MTuUg^tJLBD>v2E|-JcZWnpI
z9<;eB5b#nk7@!2~lSz?MsYz-_oY=?+h2n9Fq3j6+s2ydG*GnVg<0PVQ0!ii0wswLHGzE
zehjhW7`KSnNyN6nk3AScjxqAUhZnX%5pxAw59T125GjeACe(ZaWc38e<_(b@b~cU;
zJG&=<9Fye57~2se!ec|6F^t3H@E|ATk{Cg*0dgT$5cya=>?1;P%)J*m+8r)xkHjc}
zIT*t;vDqDXwqrDbu~NB%%8ec@Pd;z5U>S^dngmaP$H8OZ5pWVLf)n64I0kM9hrs;m
z1;Ab~`*VX`U!8Ul0fSuke#vMT5Qn{OM;e
zEj~!2$!p@k)qyn%zwU+kh5rA1@WwLz)BEC;%d0(nJki^GQS9rx_}PU?+C*0`i{dH=
z4^w|%@5SV$zTV#c-%M>9ym09X74LWt(?tITDD+*rc=6NzhyLzT-;Gt-(ZeM6U%rv-
z75n=y|Jx?hXT7V;3H=k7lOHF0FI>DVzCZHq&(@d|)O-K^+_fv0dO_!YAr_hYe05AT
zk$mWyIC$mawN0A_hYHbsKA+*epQh8kJ9LGHZ;03a{zF1ZPu}NKWJ6m00Hc53hWnn{&5f*wEJ2Bjd;z_(yI}iUo7ETfXi5-g`flGZ`AZBwkHk75j8^pU*18
z{f57~{eb+|Tj%dkFP4zlyJBut6j^@wY1{4e?0odiG>yFfFGE8TV5v$zUmE=jcMjP$
z%={$!<{rZxjULwet+2km>_7CT{qeut^EWPo{J=AHnk|dEv^>ATv9G4>W!qmrzsF_B
z$PXBb`yKE7*YZm>ZHM+gRby*wGn6T7`hWG{<&%|kI{iTA{CeH+
zu*=upe*Qdq*TgOibXQ#+I@jq{mI1)-sF;oM8?9l+b9qb$9SzTwW%77#^C+o!PQ`SH
z1Wb1?mgBbn3D50>aRa!3Qvjz5RD@H?DFU2gQPGSM;KPc&%x1Nc1AAKlcp(Vv5Rb(Q
z_(jJp{9Q@MDvVQ1CL{JjU>RT)n+^63djY3RPEKH7v-M6v2KQ$G$s;eb|c}0>Dry=!)2n8K=l7$8nzOSjCNV&x5^O$17f(
z@d?H*IQtl<1aV$Ra8|biC&Ym*60(9bJw_4W5eLp(3(ihA&UY8iRWHupAkJQg7r2V?
z3;Z%h363E*ocVG1VVq(DcCg^wHd<}u!Py+Zd2e&MRgA)zB!-wt*gJqN9KaPW#s-WF
z#xW1TB<<{PG7CI4j$Bj76?mlxk1z%aCdXCW!C1l$-#VVK00)@tIM;wB7)Q9_KMuc)
z6|7$13g7~JFh)kZ8~NGc*IU32CY*aV=rDG0K*s}o7mx@*X&p4&9d8{p92P67o%NA9x?Yy_%gYSBv?(S|?svp#Iub$Qd
z^}~k`N1{DjsHa>@nCAQn>%*46KU`a6dK>!QNlEsJ+y
z&?CgO2z0LnvSjb94}&kZEFmdaaEz#c(!GN~?yMY*L?c3WDJCF9F4xWaY;X_t2zlpG
z7O;uJKmK^s>*;SHRUZMm^A?f3(-?QaOx5ca
zv|&Vq-!En{Abz9oAq2p5b1(GOB%+a?FLLHsM9XZgMUD^|CS5(S;X*8iS-cDVwVL@l
z0;
zA4QnybUK&g7XUvNc6H&wu5oX*E-Wl$(sSt_RRQ!WH@9#9u^ND%Eia_Q@2qk^XWhQA
zV_`u~yKGgA?QTw|pR&WCh*o89y>;u>_J#TMg6r`^tUovJ+G8o24ve~}R$Fg{wk&7l
zGu3arIy<-hHCLV8vgg%Y4sW4OD|1Qp@+_X~?9H3ET-Aqmz4qEmKiqR@PnE?k@)frhQel&gU&v-*4FU>YhFF
z?DVv9b6x$+!j8=RttMw}^}*_WkMFPA<9+o}8LJ4CUN4^=3)^lz)8uSye8t&VTfM*P
zhr8cVQ~WahAKmI`YI~+5=&W`IgU{C1ANa1WzP|sCj^i);TaUN49j$J3`WqXa&I1SX
z7SrqGL-of&!M4`crZzuxUpV?KQaf+}3u7Adqfg_CtgU~!5*{%3>FHtx1Oid8*V-?-wpP^x#nB+ts*jJ!K^A40w%jrL$
z&IZ~j)9IOMO#8yNKd87v_LtC}U@8@g$tgb5fqK2QsL3i-2i)9Ai!dOo6S1iydeq?4D#uq7;Y=1ve
z*VpB!=0}%PdH)Ej#zC_h7lI|SQuE<%&I?(oc}^9}Ry8~P;;%`h73O|Ta=KiHxbLhr
z^TKFhuZ~)mqw9~NFkAZUA@3@VtIsYH;3^VTClk|Mg-ZVS^qmOW0504+=s(b)%?Mth
zFcJRvvOyL2MDZ)$JK&;Erv|;ivVpIfR5BK+&_=#41L4-~s+y%{(|~KXPO9t#brl&v
zeVFgN%uJmt1`W8I6E-%GY9SOEm=^`}gDvRD8))8%-W?X;x`ADFb{0V!xDm|e%3mHK
z17ty+xV;l5El8|LzroDSX2JYW0}1BJm6749PbgsnBLYMVem3$IBhG2F`9U(6jmS$U
zv;#@Gol$|h?cClCqe)u!un*;-IqQgNl)Nb5jQ_V
z=R*yZT1d1i%2I>bXfR^{8%cAI=FZAW^MD~*f|$jn3_6*JI5pD&SuiG
z-#grLYHGN$;t1{`H#Tf+&~7VBrL!qn#+}@ehOK;mS)xr2tEZK%!!5r(##f<*$=Q3>
z@0YV(!zupAGbp_#xfl7z{EwXY_dN()K)u6UuC4nl2
zPo2ssTlZc+iQCA0yZX8JL+qUMgSaA;q$Ph~Q19A%F30M7(X0&o_T(+SU3!<*SzlS&
zC$ySTX3`AztT?oud~HR
zOXt!p%`0KC_bo|-ezn=}?<$1l-AM9F(%F`l=H`<{Zd3#5>?tL+T3t0bdnyg>u2t$p
zPb^7zssb;?)r1Adw6+ZzxmGow)Bb5WJ8+PR`jC4Zi9cvj;Bf+fV)Cd2nPs={flpEnp+E9aqJ=(5bHhxwZO
zZSCH+gtOMDsQk^n?Kk(f-`v}p@M0Lj|1a-tMC!Z(9OP_zDR?{^#%bz4aAN*Ma!?fI=w)jU26A4^McmOAKv>bdG@TCU-H
zd(@k&U;M%A2WvAmzsuKsyPm77O{ahE%=c5u_gwW{x<^xIi>9ZYHF~e=A_Y>7@v)4}
gy!ijeaCr)Cqu_Ay|BtD{UaxU@Kl)ptrzpyQ1AizERsaA1
literal 0
HcmV?d00001
diff --git a/CONQUER.IDE b/CONQUER.IDE
new file mode 100644
index 0000000000000000000000000000000000000000..66bf576c8a877a8ed8b59d1c7af8204a1838ff65
GIT binary patch
literal 42942
zcmeHwdwf>aneIxyT)y0|ga9A#i4h}01VlhCO6~+h5|eOI5e-Qo!H~oxAS&JwDI(rc
zE49{IODUz+T5BCD9;f3l4s|*`9cz{GIF840JRHYytm9Z}&-1)%zu(?jd`Zvz+2614
z_j~u+Yp?ft-nG}>d+oK?T6=#LeFHshy&ciYv(Jt;4fJj7Y#)l&boX?ooAD;mJY!yX
z@r&19`Gzr{CmU0|F4dG86HYK)r|p{bcA|lNmiX&=8_nc-Tvy5Zn&3Ox-v1$sKP1bPN^D)dZfDfBGpH0asT>ChR_GUz$bnb29#+0b*LbD-xz
z=R)U0&xbC6E`%Ah
zhBiZ6psmmq(3Q|t(ACgOpqD}~gRX&I4qXeq0(vF14Z0564()(;Lf1n#K)ayb(2dY}
z(5s+5&`r=@DCL7a#zf`p(CaG6q?-1=-m5ov4w#uh=_qWXd{#5==y{R87NqW6B1GVrjgBRWeaSlyw=i!^82)RuNp+K)Z7C
zidRWQKFbEW%vKM_nm|Qx-Muj!YX}udYa8rVPSzYMVdk@M-9}AgjiQpyw4UyDwoD^Z
z(`Z_f>|f`g;#dQzSbzOnZCa_+OnzFfX&D;m?l0dw)K}Hn-`U&YaZH!m%eN~q&7GV2
zwsc0P22Jj(NaMcX9e9Y@xXDl51z?fw0_
zFRVFL!cyk#rpGID)~G5{KAl@T6~~%ZC6$~VJw2Mn8dxQZ$G_c}gy^@sbCk$-N8?vVoDksTUM1fMN2BNSl&`wy0r4#S+lWnRvklckd4fVsRJ}VlV>*=$ss=ofL=-DOBEls5}>PqSw
zDx)*1W-TnKs;-I7=qqVl(aH=VXADZc{GC-&y}C6P(PP8Ao(k%HQ`T+FDsMk{H&jVv
z`IR@wW`4?QgJpel0hUbPN)Zq_dmxwHCda;|L9{P2#eB4szUb;zbg
zeF(?93I}I(HA^AN%uC)&=nyujvn8Y=
z$8l5dX2r3Eq#|j9Lj#(|mXnID4>rfr*rHMq-1@#=#j&NO!nC@|#s-@fl@^$vmSQUE
zT9=nM8PhKN7?svpFukg-yuPv4n9IB}V9QN~+{>#QR#@&5Y0>#<8K$MRxvrtMwY;Lf
z+SGgeZ1Jg}EoCZKG`BQ18&fIiCDI!7(=$wYWo31Jb#r+uj23!%U~5r@by-bgL+b+X
zT4XCyh5Ytd8gsVipDj!kwBd|a5L!NLeX1lPeZxR!e_4B?B38d
zdZI};eb@A~Z?Su6ZV`@BY1yj{>#WMY
zUNSVUs-M-;w|StwQ@HA-_3zuAYi#9T0=vqUt*Gg>ko_B!?lW%#a;(fa@mViV5kA)(
z^{KU4&$ZFP&K^9}b#?@s5O5Wp6&>n}a%Hxj|9lEj!LxuVZ>ecm;59gBdFLmeSX6i)
z8S316-h30qCR@kw2}XtYp!OdB+T)Xr3a`D*z1^|<0iSqOc;0XB>D$nawndqDeE2k^
z!fR%8N83;vHq7|^#!@a0;V4$tL*Hn1i
zeDYG^H8<3`slTUf$ZIb}<*Ce1hwskzuHHUwv1P(1HWlW5Nb+ud^GQxc@-25CV;=K-
z^C?foOXt<>-6x~+1n8%ynnB#YhBo&b+rEs-)8Vl6zRkTI-F>k(JD(g?n76_9z5#ol
zM&*gpPYWZhv$t&kV98rVo;LmTWHZp&-Mik~XFi3hkawVsUhO{1)2Wxp_FDfy-+FX8
zcx{!aJh}SVRMXc#)ZN!Rh)ovHr&<-337Y*l!@0$#^NCl5{F}N52SMPaN91YPPfs?R
zI(s({dRRUstB`vW+U^6_atpa5@&xUthnViA;aP1M7Jl3MJ3DZSE0#~)DtuN$dMu4k
z<0>pKd-_|Sd`ef5w6<%}va$Qhr*{?RcjI7JCtB-x>bL29vR7exH=EXQ`-OK5`P8q%
z>!Le$O-7_Qz)uS?4Lub$PwXX75gh6XJuldMpu+ri^=%%s=Tt;`6^5k+t_AissPMRL
z=;>S6)-!1C@O-i-LWMrplh#XP4~2@P_4T3PZC=@Pp~7-s-*YW`J8b!|M?-}^*Y~uc
z1P5@OZ9029RCw*L@9ypBZyV~eexuS8;->?T6M693LDUJ>H+xA`SQZ@v-COkfV(*Cx
zeRp&ZqF%W+cI~rgMTN(%qq_~Kg2>kb!OcdmcV4hJMuk|kZU_4YOsp)~Q=`K2
zY9HvlM)!$5I4aB+(r_9NnqF_8*t?^`eQNLP8|d)Ln!P?MHm!Hi-b16(8|0@UPkoy<
z_2IHy?eVgwNJX$+eQldOUiKiVNPc;idpPzisR%B1PDP~0X;>QSS6dH7q$g^4T6>os
z8}?ACu>96-9vWg^ZxWwT>Af1B-qVBo4SK%pdBdJ96`nWiHh1@QboXvBUOIcdR9MFA
zY(Fe|-YlLyVk+X_yC0@XdPI85{B%5L#P&HVy=Z|CPXjKHANJs>a9`Tm+dBtQo$r?Xlt}NMpPnvl
z=9ZSS$|khy?eklS^!}+$GTL7?D;g?W>lz!%mKt-t@XVCnLEmPqxoE+fipn`_@JL<1
zqN;k0ZO#KQ;Mub$Q6%~4QJ#}i&Hez{Id26|3ljS;7dL=bI*~IZ*
zgYUq_Xvr
zChj77(`}ivr&lFTnU}Y;R5!N*@TS;DrB~RuPcwM5ym&=5GievhBM+$zK`ZCuepe3zHb9&nXp
zT$ft5S9gucTG*6qe
zJpb(BSMl;!*%-SfqSE_6EPX|Db1?rL8Bk&QRyEhHWclWL{2VndK`JtP#9)jzp+1y|H3hb!DJ@IZB~I{+8DATITQXJv>J+
zR9OBs_2n%~1J?paH&n=9v7)}--~Wh=eE8|;$Eeq17nKnaKMnhCPd)T^+jEDbB`VBY
zCC&}*+5s<}BPl8@yA=&}vS+q0t3*aw{Pc9wT8%#6@>cF%MtvrIl*lNIZ0+A?j?$<|K9)9?^Au?G^1%@t6}bk=o2siMD;Ceu9TleI3TQ5`<88^NbL2;b
z`%&IdM|zZzc_!tfl=Rf8
zsb5WfF7=nGpQL7or-f_6>%)7(&xJn;PfA;m)|+-;+6!r~r+t!^ojxtSCVgG{jp>i2
zznK1J`j_dGGAc6GX55(ZSjLMP*_pF5S7yGD`FiGOnd7r6vi4-Xne}DXr0fORE3>y`
z@6CQL`bKlPWGIxC5g1nV^
z19^M$4&}X;_hDX2{*?R+@-NN5E`M+SGx@LOf0UnHP*$+4U}M3qf~N{zDR{Rav2ar1
zg2GD+w-nx2c&PBz!uJbP#!MYkGiLpm9b=vt^YWOt$9yrSsAzUkbJ3QfuNJ*p^hMG5
zvCGC@H}=5T*T*Inmldxq-c$Ty@!Q2;7Ec|wY~03iyT%VreVN&m;ql-frxi#TL^Yo;jO*|n7mGdl)P6^|#n1JoZ94TkI%IgU>
zEm5{zvxsvvieY1IZCXguQfo~(gt3St<+QB{*|a1{`|$!3PBu+QqwNxNWfw;6O)}Ow
zM*&X*;Uw!Y95!1L=2b9m@hO6@Z8G6hbh#ZV7rw;k2gXb{sYtV9+Pu;~MQT{AHSacT
z+>4nZNlbY(=_L9MF}EOr`}cMZ*OO)a57=%@NY;Ok^+Yon>zI)sTe4+6DoZ2F1X-Se
zr7=^n{KHM(0h=*NvZYW`3uJwatVd*7EL(momj5QpAIkDMS!POVjx3+W7L<^z|5Vn;
z%KA%UISK0_b4bP#smG$q-@}MlgC(9qn@^ryw$ubSg*=B~UpDW8US)L=yDOe_&
zk8@C-zZK|La=BY`uz8v}PnL6KIZu}JWqH0V7szsRva2A0M=A{iQ
zpRDubNH9D(Fw+Z`9fl_dX6nh3b)zvnEx3PnpR?fgRl1jL46{ebN0p?^9WfvFWVAV8
zo-ns8j=vnR|1I6b{N;||FN!CwM3e9Nv$?kWt$EKw`mteL`6F^7Y
zZ_AbGz;n+n-x%1uHjJ-m1iq+YN`4@JoMjn2PGgb&cf+`fN8p-n
zm@?6vn=sB?f!?l)6}5|JFTCQ4*5>lcYJ{uGn;S@E^EeK+zZk|l-sKJB`2hFvOk-2n
zewc11z{<8C3eaA7BeV*wj2_dFG_Qhjl?~gI(YA@e){ZbH@Wg{m#1?WE%UUK%3_XYm
zxpE={
zr@yfKeHymgkx^a~&!!zIpMou}kYl@H$`-@+3-*k=z`6`mHW|GcgP93a*b%g_YFNkQ
z!r}dts29cutxz|p1*?gggfXUa!5WOp^I^YbP36?eHI*2WHwlw$d7O?sa8@8jaJ78H
zeWnQI!ML?4felA$F!;xESlTc);OnAVt
zITJPK5RHD9JJZ)*qjd=jtmEFZg*@Jz$WC_>=DVlR4M&oL
zHoG00a@cT0IcRg2V^aYejw%Oj_Bb|`u;IvZ(B^K(rV2J$j?F!eO*L#d(j4Tu*RiRA
z4M&`VHupI;wXorQ_MpxEj?Gfoa0EJN^MGSh2OEw;2W=j7Y?i@>qtQW|y^hUAu;Hk5
z(B>h>rXDsNoetVO?AR=a4M(VhHjg+q4Y1*S{h-ZP9h*kjaHKkD^QdFf1RIW62W|E_
zHW$N&qt-#2uQ@i&u;KdxL7T@Mn-
zh9lZRn(Y@Ts!
zHo%4>>%si|(6Q-)4c~bQ+C1ynbi-zrWAh`&W+QAk;vVFAE@orf*;m1)5;nGx%6nE1
zwy+KRQUceWO;}>)%DDXRLcfX3f#v5nz7?rL*nQXW$ax5tL|=_?{G8!@FXVVO;|siy
zZ{{%0w{sYB+?e47UdXXt#yN(|upD6`{BR8RufF+agyW-|A9*XnanKV(+qfj!5VauMfP674u5X&iTZZ2M0+wpYiFE!XU^<#^pboLhfBwtq^;cHop_I~Y5*
zT(ifPW35XrKK|!pi*oo=sITpHJ9Nsi-5fi%T(ifP{XBYaQzaQJBW({OHDXenbX*N|D0`%ca_X?<|K+L<Z^9Xf
zN%#L!7gMY1WAOKVg13c%r0+8(2nbe38NDV*8htR0baDN^d7R|zhDc}|zKV0q?%bJ>WT7a^q{x^WcF839gvZsdY<
zd5m*P0nd$doO2@&HjObGk3M#8G$G|;Xd4m|a2#h~$i@n}MZ#s?76$g*$VXc9h@34*
zX@#zX{YacMh0~74*nKGgCn`(`ububvxi6d(608FYVY4b`BNSNZdA=Gcmq0IOf}E2r
zo(Z`{!m}jEk@Ls;9fPzhVw?xW?;OW(5p3FGHXdor?>eNkQ<1>^JttO%{E}NFyjX(q0l$-xcC&E)9_RDhxh&@;
z&UqDu&8;yTOJ&bze!t*0q-=-Y!vq{(J-@KW3u@?iZKtW5O&=J)V|YOK!9f=bLbyZboQ1OOf_qT+Y8m8KS$=JOmZa
z02Z_TgcF;>7X`N4eK`wh563vC%Dya@uRw95^*aqVkHl=0Lci_sRir!$mGd8oyq{Vj
zw@A3&+rq$do{hA9aXEjEeIee}q#eV|culb2>P`9+#8n7&*W3M>rb;`LyLT1DsF9
zIDa{r=k^uy9SUx=e#>C<&6tf+=ohq}M9R0IO-#VK?F+d@!jk4CSk7~h_EcO>eiPx_
zNZAjSGLL#db2KBh)^c8hTwbZ05!!v33C{1tIQPoFtTP9&g&VEkS+MzT%tk3_!*4Ad
zMEE_Z)^(bWD>t?;ZT?2j?`)(U0?HWHb(f0Y4(B>OcbJV*X#Ma5pr3)>%!FZONN$mE
zyA5S&_jeA`ekguV!tW&U+bLfp<3{f9YI7cJo{iZkg?_c{M@V@NI=q~>XQkGXJHA-o
zbCLez81K82c;2j+??Z5-E$ex(c|K;NZ119+DL+B*0(7`i
zPuib!GN~zf2t6Dpl9!}(q&%N;JY{BTYwG^gW2w<_RrulX3G{1ppikp?nn`a>-=BUg
zJ(@9;aWvyZ#*)kjGM~@P%$k|CJ!^lK$u7y>n!PXkIR$>sdc;EU)_NHS;RmxwR~O@eY}dI~fZ8iuAp)1evAOeoubY>Tno%jZ?@
zXFim(!3v>cphZwVSF!Ch4$8L71ZV_05jqJv85)J22Au*u9a;iC13DFYCbSfK7IYf)
zY^Xh!XvM4zqlbvOmIGzWJ-7@PVa`HoE!z|w
zO!hD$AH$;>{1#onESxvYk6&&)3xeOM3u5vEkziT#tOQ!=lAc1m)pt=p4X?FDzw4*9I`(twbs9WSStj-ix>1$GRzPA
z0<0g_BEj$F1u>9w`4J_b%Ni~3u4X;M1mOBQvUb#Sfj-Z!*hrE;q$Zg!&>dd
z_{TH`_E#*1wcwHXnDD}3e)!yKF|0L%-=*_8c?YuTTT;HPq!AKqUrhP5)k@)}3N
zRl|-CEi8t$_$wn|s)zaUF|752-%$(r3tv2fAKoQ`-&YG_nn%F!t`hw2S`cGJpzVz-8V+STrvWNcj@4F;evUyEsj&mjdyi^0XU9?FB`!RTLHoE#|5zA
zH6viyYB)ZEpUX$Uu$AF|V~#D7n5XdC5io3(@GHeu*z$>C;;ux@n|KGIB4JCyfrPgc
z@)K(kuS-0X_-Y{vuqKwl1xy8!aZBN6BVA`b(31f|!PAZh|sN_KNpasw|P`(ercNaL1-%6>;?Z8|#c)|CQ64od9
zUS3c9>(ZU@!FL{W(e9y_3lb7wcM*Qv%C{N##sO#9a~Ay>P|kwqEO*Xg=PY&3Lgy@V
z&LZb5an1ri2g+I8v!I-XeJ+%WR5NYd$+334j>ogq)HWqxt1-2WJPut4@%;oG&s0%-R~*kafa~>1ENi3F@k0^^I-(mPn!WMdYP^|FoR`^IckEt)Uczd?jB;9LdUQ{O9
z-OQ!sOU63Y<}Wj$wkZWM&08AQOYwdP&kt9UZc#oWlrI(QRDXZNh$$aE%}+m4&U4L`
zgcu(Y%Euf~eZF+Wl#d*1z{m5}6`zj?<;%c2)$*Oh{$>XFnDd*#$7{e9e}9QkzAUU$
zEnj_NZBsU4nm>Aa5`4U#T=DscP`(_jQ!U><@Z}<=eDrh_e7rVY@%e~QzC5f`E#C?7
zp?qVoPPKfy!B>Qs
z^3l^A@Ui}J#pfeJ`Nm?MYWa?XuNX1qqo>v2V_oKo&qsvvjl(+C@=Z^wZ5of5^3l^a
z@UdQW#pfeJ`6ghUYWcQ-FM^oz(bMDLV;$>?&qsvvO~g9Y@*M-;B*c`Do_+>C*4M81
zd_*YUWUNyyUnIGcXD?T3)%6BH#
zsg`dI_(~B|K6=^;KHe)`@%e~QzO%4SwR{J`Hw`i6qo;k~<9*i^pN|OTI~(g%%Xbod
z(-BiXdU^?byobBu^AVwZGq6szeD$fdO=XBFA3ePXKDGy3@%e~QzH_inwS4=)Hxn`C
zqo;7zk#e>zT=DscP`+7Mr&_)f;G2z@^3l_D@UflZiqA)c@|}xys^wc0u5FrwnDWt6
z6ZqKXamD8&Lix_aI@R*+2H#x7l#iZd~A!k
z;`0%qeCK1GYWb$8)iy0aO!?^PCGfFb=Zeoqgz_!KI@R)R1K%RVl#ia?10UOnuK0XJ
zDBohNQ!U>y@LhnI^3hW``$#$4o38kLL@3{dSf^UPNP2D462z2`o~DD3ZC6)(J|dK_
z9P3oe*8{!^#FUSon!v|)uq!?v5z1GIb*kk%48AJFl#iZc%12L!!N<0~D?T3)%2$VVs^vQfzGa9h
zA3ePTK0Y_N;`0%qd>3JzYWYrruO2bwqo<^tBjtPsamD8&Liv_sooe~&Gi#d~5K}&S
zDg_^(XI%05h)}*ptWz!DKJYamrhN2N4?aE{x#IH?p?nu(ooe|`fUg-b<)f#5@bNjz
z6`zj?gTO!?^P0r2se%@v=I2<2OWb*kmt4Zf9#DIYyO4?aHsx#IH?
zp?s^bPPKf;!M7SQ<)f$L;N!ETD?T3)%6AFYsg`egc5Tz8h$)|V$~{uf=TcXEJ|dLw
zGOSZA-!|~A3GmTV3HbPo>x$1ugz{aEb*kk%2EMh3Y5wSGDfsxj?26Avgz{a1b*kly
zr~5k7p?sZKr&_+u+}ft~h$$aEy#YSI@R(W1m8x)l#iaGc}L3G@8OEiM}+cSg>|asI|;rX#FUSos=&uS
z6jyvcB9w0v)~S}SKCiZ^7cu3dr!MfZKgJcGj|k=K!#dUS?E_ywV#-HPyTQl49anrl
zB9!lHtWz!D3GfXdrhN4DH2Bz0Zr&_+l;M=y`w&wf^!_n?us_@t-v<%&@dSJ<5ufwLXr+$6mzL2ffdS55GU(
z_vHH^fHT*^!=MDdr|`HHR^7lbKMQuqL4^;b6k^2`3Z6iS>!A6Za+VPdt(M
zdE%nb($MbE!V|Z
z(+lPoY%AD_H>Tbwh!mC<_TY`F!-Yo+GshH_<%)v3wk2yIeTvT7wQ}lGvFNzAs
z&KbLP?89S^kNpD<5NA4t@#g*Pz{uCj7?a{SR1wB4!iH~2CCCwD5l76^?ffPvKK_Ey
zu(<&gQ;8Z6>om-Cuw!ClV`qzxOn3Zk3Hkog#a~(gb@;Iq@t0xtm|X#XkD0I6(qAw9
zW#HTQV-bch78AmFL?XM-BNn_*_VGlR3)X^It0YWez(VjOD^|TeaQJE6Xy=Oif@v
zn9BW_hWuyYD`P3R8bTOJ51}75gps`v`UCN98q|uezSF~U;vDqR$|`@9vdWjTq6ar$1*0700y)Y=(Z7o<
z9K~PZi2lZmf*La-s%Vs47RAk_9>KBZ0$}~oXzm4%;3Sm$SbUC>~2Nt{!SRBq$YSJW#@7#QqR!HDEo>p
zB;&^`#YIhYP!}ZhRkD0}g-$#LrMaBspvbBy92xvLAJg?U*)*GmILC4}d`-qT2E%e4
zbAB1;n(-YKUNd}$g6}-&{7u$Wnvdux*Ejxq3O{!iE9d%g4w{>{)12#jeX^~)d3~QI
z*Ef$huW$F-=6gYIJ;=Gnu7@e3?EMtkdwSrEJeSQqSM-RV?l9iy<(cJqdW>Oannby2fMLCq2r
zXYMLWb2-~V{W8JUOst`CrQ^psLs6Q;a~%}ZwT3=dQ1-n`ZjTuCHOE2S7w|PlP{BK%
z`Z~`+1$~_-DElt9^`&*qT+{;e5mJW~nd^z$werz8rQcLK3$KS01f9H!o){2VK65?}6@cD)5dNeR&4?oT$
zR+N^|LZ^076x+cIrFP+*V)t$_E8t&qy~y$ZY9QB(#6OwTzvg=JD5bR6yFP~BP1WB8
zj=%Jfe^f3If2`eIe;1D8??UmXSFhFKZ@b89=g2W#&s2O*89(||l$L*ugIXIn-niR4C@sO-QEGr%sR4MrwFF11
z#qr;6!>+AqN&dOERD81qp*dFHbxsMb^xC~VW9s4_ciq=z4)VjmxwcG@+G2HmUgRK|
zuIJiCaeZoT>qlvq)l0i9c&_n0GyeDJjzkN
zSdQ{Y?d9fCkjSlMXrH(*Lv$HsMZltD@MuXicxd9auk0nNA$O96x6B_
zQL9HmtsW6|$tb8xMnqjY3hL4kQJ0N^x@<($no&?|Mnqjc3hMF^QENv*tvv;$waXRG
z`H}9`F05Ivh4r*h-*HwZF_TcC!M2~Z3AM!>acxLy=naxq6$$^mp
zMQ(EZ;hmaTFWV;ZmmFvOqSryC1V$k+rhwmRaeVbTsJ+Jb)hDRbfG^Eqzk@muK=lhM
zEe`5x2lZkAb+w=};-Cf`)PV#)hXaDjih~+-P#XhfI4G!`IH(~P74m%z2`VoRYO{ly
zlH{W{3#uRvYKwzvNb*OGwg_rW9Mm-q>Xj59b&a6L#zAd$P|u|LsI7t;7YB8%gK9|g
zQP&D;LLAg}4r*Eeb)BFl#z9^0po%hlU)Kw2avan)2UQk8Z4=aKaZoons9OWa>jpuc
z9tU-!gUZkJb9kem&WMA$$w5s^nmkWyx0?iYW*pSb4(jay>SjTm6$f>TgPN4$`?^I?
zXU9R^>Yz5J`lwq4H6sq{HV3sJ%}3oPsB_|=wmYb|(|y!-LCuPTy4^u7Gqv*N3ifH;
zE~s6HSGJkQ&1PiLG5->A7=Zg-GVBQgSyK>H7E4V)pPzXK~=^<
z?Qu|L#z*ZDRCOHG-41Ge0Cl&ZYU7~paZoQO`o8WFR9zg@y$rT@(j(pM&ZR
zpzagY@;Ipb9n|_9zvS;1RAU^}0}g6|`IiPQ`3D4baU9fx4(ieX>On!Z#6j(KP&Em@
zuf2j=5eM~cQ)Wd?hBo68k2lZ~q_w|UNE{lWus)K4awXw7J
ztAe^b4(d?{^;!V+sGzQhgWBhyCMEj5_6e#j4(e+T>a|oK^)*4Y$3Z>jpl%GH9urh&
z9Msnx)cXO{*9Em94(f3S^<0*p!^Z{H9S8Ld2lZ68kNSq7u8Mc%sBa3YFAnNS2elyEenW|Oq9+A)bsW^U98^Q5kNTFN2IHWfa!}dnKI$n!
zZH|Nbwu5>t)kl3>P@F@nPdM71-|wLIB>SlSg5peB7xf(n)e!Pg-w_n&!@8&g4k|mr
zM;#CpXT7?p?>eYS#z%cuP@LQ9q7FK!ngHscpl*qS`ksS&Es(?S3F@{us6!5FL870-
zLxQ?J4(j_3YD=IDzb~je;-H>(Q1>PIzMdA;SK^?4;Gh};NBIYWx-$;y8Rt3n%M|O2
z}+QbL?F}4aIr3{Go$A_^tJFhHSq9zs$g|C_EkFzsEco;#kwU`~(=p
zF7ahWFRovZ;=ch=G|qno3dZ?wG8D!0--0M=&wmXH#`*6-!8rd#C>ZCz2?gW)SD|2>
z|1K1a^IwL7asJy-FwTD+3dZ^GL%}%z^(YwUzY+!G{CA>Yoc~f3jPu`$f^q(9Q83Pb
zFAB!_FGj&Q|IH{E=f4^SrpVye?JPw`7cPpIR6bP80Wtt
z1>^j8q+p!?k`#>d-;#oH{%cY&&VNq|#`!Nw!8rd-DH!L!Dh1>GccoyQ|FRT}^WT<&
zasKO4FwTEp3dZ>_Ou;z+jVT!CzcK~m{CB2cod41kjPu``f^q(9Q!vhdZwkiw4?PsS
z#MhmjIR6PGn9hHin14G)bubIqn9q|xP4=(l`9(*{H{qJU=Ww$5RdNE-eU$q2Z2gx<&aeSKf<21~7$ET0h!{#%D8{zUwe4=O?J`uNTGrq@(?Jpr*
zfba`1J`)qm_aOcvLT>*Y;WC63*I4_l2pNCb3p2OcFQWG%B;S(=iF@0N7hY?(FGI-f
z+Yw$sytn;xFFyA=yL|{D?G7O%-+Nv-{(2j4LfDM$PatGIP9R)|uwk3s{KL@<<123F|lI|zv%f3ue#FWljUhrRF<
zFD$*q+O0)cV^BU`{7r=9DZJIDFZ05i5pus?_2T9>YqtO)%cI|mAMnEWyl~QXTMjoP
zB<~Ae_$fl-rrmDCD-kjuPkHg<2x(um!-h>>xCRM7)^3d#-sgo!z3@vfob#}?+vtUld*SO|nE8lJukgaHUUx
z4}0M!URe65wOi|j4|w6LUTF5&^toQx>)
+
+// The following functions exhibit variable return modes.
+// That is, they may equally-usefully be called for a value
+// as called just for their effects. Accordingly we inhibit
+// Warning 534 for these functions.
+// Feel free to add to or subtract from this list.
+
+-esym(534,close,creat,fclose,fflush,fprintf,fputc)
+-esym(534,fputs,fscanf,fseek,fwrite,lseek,memcpy,memmove,memset)
+-esym(534,printf,puts,scanf,sprintf,sscanf,strcat,strcpy)
+-esym(534,strncat,strncpy,unlink,write)
+
+//------------------------------------------------------------------
+
+-width(0,0) // don't break up message lines
+
+// 32 bit integer and pointer size is four bytes.
+-si4 -sp4
+
+// Include directories
+-ic:\projects\c&czero\code\watcom\h;..\vq\include;..\gcl510\h
diff --git a/CONQUER.PRO b/CONQUER.PRO
new file mode 100644
index 0000000..5c4b2e9
--- /dev/null
+++ b/CONQUER.PRO
@@ -0,0 +1,864 @@
+
+ Page 1
+ .RTProfiler Performance Report
+
+Program: CONQUER.EXE Date: 12/13/1993
+Phase: 1 Time: 22:19:33
+Total Ticks: 2696
+
+ ---------- Logical --------------- Physical --------
+ | Total Average Pct | Total Average Pct |
+ Total | Clock No. of Total | Clock No. of Total|
+Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time |
+-------------------------------------------------------------------------------
+_Main_Program 1 | 2618 2618 97.11 | 1020 1020 37.83 |
+_Edit_Unit_Offsets | | |
+ 1 | 1021 1021 37.87 | 941 941 34.90 |
+display.c_NONPUB_Refresh_M | | |
+ap 1090 | 206 0 7.64 | 200 0 7.42 |
+_Self_Regulate 41213 | 122 0 4.53 | 122 0 4.53 |
+scenario.c_NONPUB_Do_Brief | | |
+ing 1 | 91 91 3.38 | 91 91 3.38 |
+_Fancy_Text_Print 938 | 80 0 2.97 | 80 0 2.97 |
+_main 1 | 2694 2694 99.93 | 76 76 2.82 |
+_Init_Game 1 | 241 241 8.94 | 54 54 2.00 |
+_Check_Menu 40124 | 47 0 1.74 | 47 0 1.74 |
+_Load_Map 1 | 16 16 0.59 | 16 16 0.59 |
+map.c_NONPUB_Smooth_Shadow | | |
+ 21775 | 11 0 0.41 | 10 0 0.37 |
+_Load_A_Icon_Set 3 | 9 3 0.33 | 9 3 0.33 |
+_Call_Back 41213 | 126 0 4.67 | 4 0 0.15 |
+_Coord_Cell 77858 | 4 0 0.15 | 4 0 0.15 |
+_Draw_Map 1090 | 209 0 7.75 | 3 0 0.11 |
+_Anim_Update 2179 | 2 0 0.07 | 2 0 0.07 |
+_Game_Screen 1 | 3 3 0.11 | 2 2 0.07 |
+_Load_System_Strings | | |
+ 2 | 2 1 0.07 | 2 1 0.07 |
+_Map_Cell 4355 | 13 0 0.48 | 2 0 0.07 |
+_Shape_Ptr 1685 | 2 0 0.07 | 2 0 0.07 |
+_Load_Brains 3 | 1 0 0.04 | 1 0 0.04 |
+_Process_Logic 1089 | 5 0 0.19 | 1 0 0.04 |
+_Unit_From_ID 2603 | 1 0 0.04 | 1 0 0.04 |
+_Unit_Next 8519 | 1 0 0.04 | 1 0 0.04 |
+_Unit_Revealed 12676 | 1 0 0.04 | 1 0 0.04 |
+__cleanup 1 | 1 1 0.04 | 1 1 0.04 |
+scenario.c_NONPUB_Clear_Sc | | |
+enario 1 | 1 1 0.04 | 1 1 0.04 |
+DGROUP@ 0 | 0 0 0.00 | 0 0 0.00 |
+_Adjacent_Cell 0 | 0 0 0.00 | 0 0 0.00 |
+_Adjacent_Free_Cell | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Assign_Destination | | |
+ 22 | 0 0 0.00 | 0 0 0.00 |
+_Assign_Order 47 | 0 0 0.00 | 0 0 0.00 |
+_Assign_Target 0 | 0 0 0.00 | 0 0 0.00 |
+_Attached_Building | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Attached_Unit 0 | 0 0 0.00 | 0 0 0.00 |
+_Base_Under_Attack | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Bldg_Chunk_In 0 | 0 0 0.00 | 0 0 0.00 |
+_Bldg_Chunk_Out 0 | 0 0 0.00 | 0 0 0.00 |
+_Bound_Cursor 59 | 0 0 0.00 | 0 0 0.00 |
+_Break_Contact_With | | |
+ 106 | 0 0 0.00 | 0 0 0.00 |
+-------------------------------------------------------------------------------
+
+ Page 2
+ .RTProfiler Performance Report
+
+Program: CONQUER.EXE Date: 12/13/1993
+Phase: 1 Time: 22:19:33
+Total Ticks: 2696
+
+ ---------- Logical --------------- Physical --------
+ | Total Average Pct | Total Average Pct |
+ Total | Clock No. of Total | Clock No. of Total|
+Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time |
+-------------------------------------------------------------------------------
+_Break_Radio_Contact | | |
+ 53 | 0 0 0.00 | 0 0 0.00 |
+_Bttn_Building 0 | 0 0 0.00 | 0 0 0.00 |
+_Bttn_Cancel 0 | 0 0 0.00 | 0 0 0.00 |
+_Bttn_Custom_Render | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Bttn_Doit_Render 0 | 0 0 0.00 | 0 0 0.00 |
+_Bttn_Enter 0 | 0 0 0.00 | 0 0 0.00 |
+_Bttn_Enter_Render | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Bttn_Mode 0 | 0 0 0.00 | 0 0 0.00 |
+_Bttn_OrderSelect 0 | 0 0 0.00 | 0 0 0.00 |
+_Bttn_Repair 0 | 0 0 0.00 | 0 0 0.00 |
+_Bttn_Return 0 | 0 0 0.00 | 0 0 0.00 |
+_Buildables 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_Alloc 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_Create 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_Damage 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_Delete 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_Destroy 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_First 6 | 0 0 0.00 | 0 0 0.00 |
+_Building_Free 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_From_ID 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_Logic 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_Look 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_Next 6 | 0 0 0.00 | 0 0 0.00 |
+_Building_Render 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_Scan 3 | 0 0 0.00 | 0 0 0.00 |
+_Building_Spec 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_System_Calibrate | | |
+ 1 | 0 0 0.00 | 0 0 0.00 |
+_Building_System_Shutdown | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_System_Startup | | |
+ 2 | 0 0 0.00 | 0 0 0.00 |
+_Building_Threat 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_Unlimbo 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_Untarget | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Calculated_Cell 0 | 0 0 0.00 | 0 0 0.00 |
+_Can_Unit_Enter_Building | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Can_Unit_Enter_Cell | | |
+ 49 | 0 0 0.00 | 0 0 0.00 |
+_Can_Upgrade 0 | 0 0 0.00 | 0 0 0.00 |
+_Cardinal_To_Fixed | | |
+ 20 | 0 0 0.00 | 0 0 0.00 |
+_CellXY_Coord 463 | 0 0 0.00 | 0 0 0.00 |
+_Cell_Building 4700 | 0 0 0.00 | 0 0 0.00 |
+_Cell_Coord 199 | 0 0 0.00 | 0 0 0.00 |
+-------------------------------------------------------------------------------
+
+ Page 3
+ .RTProfiler Performance Report
+
+Program: CONQUER.EXE Date: 12/13/1993
+Phase: 1 Time: 22:19:33
+Total Ticks: 2696
+
+ ---------- Logical --------------- Physical --------
+ | Total Average Pct | Total Average Pct |
+ Total | Clock No. of Total | Clock No. of Total|
+Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time |
+-------------------------------------------------------------------------------
+_Cell_Distance 28 | 0 0 0.00 | 0 0 0.00 |
+_Cell_Facing 16 | 0 0 0.00 | 0 0 0.00 |
+_Cell_Object 15 | 0 0 0.00 | 0 0 0.00 |
+_Cell_Unit 5201 | 0 0 0.00 | 0 0 0.00 |
+_Cell_X 0 | 0 0 0.00 | 0 0 0.00 |
+_Cell_Y 0 | 0 0 0.00 | 0 0 0.00 |
+_Center_Map 0 | 0 0 0.00 | 0 0 0.00 |
+_Change_State 30 | 3 0 0.11 | 0 0 0.00 |
+_Class_From_Name 2 | 0 0 0.00 | 0 0 0.00 |
+_Common_Attached 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Available_Building | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Class_Count | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Dialog 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Display_Message | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Distance 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Is_Clear 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Is_Enemy 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Is_Friendly | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Mono_Print | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Nearest_Spice | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Pause 181 | 0 0 0.00 | 0 0 0.00 |
+_Common_Pause_Random | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Play_Sound | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Random 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Scenario 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Target_Distance | | |
+ 283 | 0 0 0.00 | 0 0 0.00 |
+_Common_Target_Facing | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Target_Kind | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Target_Type | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Tutor_Message | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Coord_Add 208 | 0 0 0.00 | 0 0 0.00 |
+_Coord_Cell_Distance | | |
+ 454 | 0 0 0.00 | 0 0 0.00 |
+_Coord_Distance 1429 | 0 0 0.00 | 0 0 0.00 |
+_Coord_Facing16 5 | 0 0 0.00 | 0 0 0.00 |
+_Coord_Facing256 1373 | 0 0 0.00 | 0 0 0.00 |
+_Coord_Facing8 0 | 0 0 0.00 | 0 0 0.00 |
+-------------------------------------------------------------------------------
+
+ Page 4
+ .RTProfiler Performance Report
+
+Program: CONQUER.EXE Date: 12/13/1993
+Phase: 1 Time: 22:19:33
+Total Ticks: 2696
+
+ ---------- Logical --------------- Physical --------
+ | Total Average Pct | Total Average Pct |
+ Total | Clock No. of Total | Clock No. of Total|
+Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time |
+-------------------------------------------------------------------------------
+_Coord_Mid 0 | 0 0 0.00 | 0 0 0.00 |
+_Coord_Move 472 | 0 0 0.00 | 0 0 0.00 |
+_Coord_Scatter 0 | 0 0 0.00 | 0 0 0.00 |
+_Coord_Snap 712 | 0 0 0.00 | 0 0 0.00 |
+_Coord_Spillage_Number | | |
+ 1100 | 0 0 0.00 | 0 0 0.00 |
+_Coord_Sub 454 | 0 0 0.00 | 0 0 0.00 |
+_Coord_XCell 0 | 0 0 0.00 | 0 0 0.00 |
+_Coord_XPixel 0 | 0 0 0.00 | 0 0 0.00 |
+_Coord_YCell 0 | 0 0 0.00 | 0 0 0.00 |
+_Coord_YPixel 0 | 0 0 0.00 | 0 0 0.00 |
+_Coordinates_In_Region | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Cursor 1175 | 0 0 0.00 | 0 0 0.00 |
+_Cursor_Move 57 | 0 0 0.00 | 0 0 0.00 |
+_Cursor_Size 0 | 0 0 0.00 | 0 0 0.00 |
+_Debug_Callback 0 | 0 0 0.00 | 0 0 0.00 |
+_Debug_Key 96 | 1021 11 37.87 | 0 0 0.00 |
+_Desired_Facing256 | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Destroy_Wall 0 | 0 0 0.00 | 0 0 0.00 |
+_Dialog_Message 0 | 0 0 0.00 | 0 0 0.00 |
+_Display_Selected_Terrain | | |
+ 9 | 0 0 0.00 | 0 0 0.00 |
+_Display_Status 1089 | 0 0 0.00 | 0 0 0.00 |
+_Do_Button_Setup 0 | 0 0 0.00 | 0 0 0.00 |
+_Do_Explosion 20 | 0 0 0.00 | 0 0 0.00 |
+_Do_Gas 0 | 0 0 0.00 | 0 0 0.00 |
+_Do_Lose 0 | 0 0 0.00 | 0 0 0.00 |
+_Do_Win 0 | 0 0 0.00 | 0 0 0.00 |
+_Draw_Box 0 | 0 0 0.00 | 0 0 0.00 |
+_Draw_Radar 1 | 0 0 0.00 | 0 0 0.00 |
+_End_Game 1089 | 0 0 0.00 | 0 0 0.00 |
+_Establish_Contact_With | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Establish_Origin 20 | 0 0 0.00 | 0 0 0.00 |
+_Establish_Radio_Contact | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Fetch_Text_String | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Fill_In_Data 1 | 13 13 0.48 | 0 0 0.00 |
+_Find_Brain_Size 0 | 0 0 0.00 | 0 0 0.00 |
+_Find_Path 5 | 0 0 0.00 | 0 0 0.00 |
+_Find_Waypoint 0 | 0 0 0.00 | 0 0 0.00 |
+_Fire_Bullet 0 | 0 0 0.00 | 0 0 0.00 |
+_Fire_Weapon 107 | 1 0 0.04 | 0 0 0.00 |
+_Fixed_To_Cardinal | | |
+ 48 | 0 0 0.00 | 0 0 0.00 |
+_Format_Window_String | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+-------------------------------------------------------------------------------
+
+ Page 5
+ .RTProfiler Performance Report
+
+Program: CONQUER.EXE Date: 12/13/1993
+Phase: 1 Time: 22:19:33
+Total Ticks: 2696
+
+ ---------- Logical --------------- Physical --------
+ | Total Average Pct | Total Average Pct |
+ Total | Clock No. of Total | Clock No. of Total|
+Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time |
+-------------------------------------------------------------------------------
+_Free_Brains 0 | 0 0 0.00 | 0 0 0.00 |
+_GetPrivateProfileInt | | |
+ 16 | 0 0 0.00 | 0 0 0.00 |
+_GetPrivateProfileString | | |
+ 41 | 0 0 0.00 | 0 0 0.00 |
+_Get_Connection_Info | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Get_Connection_Num | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Get_Internet_Address | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Get_Local_Connection_Numb | | |
+er 0 | 0 0 0.00 | 0 0 0.00 |
+_Greatest_Building_Threat | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Greatest_Threat 8 | 0 0 0.00 | 0 0 0.00 |
+_Greatest_Unit_Noise | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Greatest_Unit_Threat | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Harvester_Check 0 | 0 0 0.00 | 0 0 0.00 |
+_House_From_Name 10 | 0 0 0.00 | 0 0 0.00 |
+_IPX_Cancel_Event 0 | 0 0 0.00 | 0 0 0.00 |
+_IPX_Close_Socket 0 | 0 0 0.00 | 0 0 0.00 |
+_IPX_Get_Local_Target | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_IPX_Listen_For_Packet | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_IPX_Open_Socket 0 | 0 0 0.00 | 0 0 0.00 |
+_IPX_Relinquish_Control | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_IPX_Schedule_IPX_Event | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_IPX_Send_Packet 0 | 0 0 0.00 | 0 0 0.00 |
+_Icon_Install 0 | 0 0 0.00 | 0 0 0.00 |
+_Icon_Remove 0 | 0 0 0.00 | 0 0 0.00 |
+_Icon_Update 1090 | 0 0 0.00 | 0 0 0.00 |
+_In_Radar 75 | 0 0 0.00 | 0 0 0.00 |
+_In_Radio_Contact 0 | 0 0 0.00 | 0 0 0.00 |
+_In_View 3 | 0 0 0.00 | 0 0 0.00 |
+_Info_Chunk_In 0 | 0 0 0.00 | 0 0 0.00 |
+_Info_Chunk_Out 0 | 0 0 0.00 | 0 0 0.00 |
+_Init_Shapes 1 | 0 0 0.00 | 0 0 0.00 |
+_Init_Thread 109 | 0 0 0.00 | 0 0 0.00 |
+_Interrupt_Thread 0 | 0 0 0.00 | 0 0 0.00 |
+_Is_Allied 1224 | 0 0 0.00 | 0 0 0.00 |
+_Is_Lined_Up 668 | 0 0 0.00 | 0 0 0.00 |
+_Is_Mapped 3 | 0 0 0.00 | 0 0 0.00 |
+_Language_Name 3 | 0 0 0.00 | 0 0 0.00 |
+_Launch_GoldenBB 0 | 0 0 0.00 | 0 0 0.00 |
+-------------------------------------------------------------------------------
+
+ Page 6
+ .RTProfiler Performance Report
+
+Program: CONQUER.EXE Date: 12/13/1993
+Phase: 1 Time: 22:19:33
+Total Ticks: 2696
+
+ ---------- Logical --------------- Physical --------
+ | Total Average Pct | Total Average Pct |
+ Total | Clock No. of Total | Clock No. of Total|
+Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time |
+-------------------------------------------------------------------------------
+_Launch_Special 0 | 0 0 0.00 | 0 0 0.00 |
+_Legal_Placement 0 | 0 0 0.00 | 0 0 0.00 |
+_Load_Samples 2 | 0 0 0.00 | 0 0 0.00 |
+_MO_From_Name 0 | 0 0 0.00 | 0 0 0.00 |
+_Make_Button 11 | 0 0 0.00 | 0 0 0.00 |
+_Map_Chunk_In 0 | 0 0 0.00 | 0 0 0.00 |
+_Map_Chunk_Out 0 | 0 0 0.00 | 0 0 0.00 |
+_Map_Edit 0 | 0 0 0.00 | 0 0 0.00 |
+_Mouse_Coord 0 | 0 0 0.00 | 0 0 0.00 |
+_Nearest_Spice 0 | 0 0 0.00 | 0 0 0.00 |
+_Novell_Detect 0 | 0 0 0.00 | 0 0 0.00 |
+_Order_From_Name 2 | 0 0 0.00 | 0 0 0.00 |
+_Phrase_CallBack 0 | 0 0 0.00 | 0 0 0.00 |
+_Pixel_Coordinate 2348 | 0 0 0.00 | 0 0 0.00 |
+_Play_Theme 0 | 0 0 0.00 | 0 0 0.00 |
+_Player_Alloc 3 | 0 0 0.00 | 0 0 0.00 |
+_Player_First 3 | 0 0 0.00 | 0 0 0.00 |
+_Player_Free 0 | 0 0 0.00 | 0 0 0.00 |
+_Player_From_ID 2428 | 0 0 0.00 | 0 0 0.00 |
+_Player_Logic 0 | 0 0 0.00 | 0 0 0.00 |
+_Player_Next 12 | 0 0 0.00 | 0 0 0.00 |
+_Player_System_Shutdown | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Player_System_Startup | | |
+ 2 | 0 0 0.00 | 0 0 0.00 |
+_Plot_Radar_Pixel 0 | 0 0 0.00 | 0 0 0.00 |
+_Plyr_Chunk_In 0 | 0 0 0.00 | 0 0 0.00 |
+_Plyr_Chunk_Out 0 | 0 0 0.00 | 0 0 0.00 |
+_Power_Effects 0 | 0 0 0.00 | 0 0 0.00 |
+_Production_Needs 0 | 0 0 0.00 | 0 0 0.00 |
+_Production_Start 0 | 0 0 0.00 | 0 0 0.00 |
+_Radar_Checkup 0 | 0 0 0.00 | 0 0 0.00 |
+_Radar_Cursor 1090 | 0 0 0.00 | 0 0 0.00 |
+_Radar_Pixel 4965 | 0 0 0.00 | 0 0 0.00 |
+_Radio_Contact 97 | 0 0 0.00 | 0 0 0.00 |
+_Read_Scenario 1 | 34 34 1.26 | 0 0 0.00 |
+_Read_Scenario_Ini | | |
+ 1 | 20 20 0.74 | 0 0 0.00 |
+_Read_Scenario_Raw | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Recalc_Storage 3 | 0 0 0.00 | 0 0 0.00 |
+_Redistribute_Power | | |
+ 3 | 0 0 0.00 | 0 0 0.00 |
+_Redraw_Objects 0 | 0 0 0.00 | 0 0 0.00 |
+_Refresh_Cell 5007 | 0 0 0.00 | 0 0 0.00 |
+_Repair_On 0 | 0 0 0.00 | 0 0 0.00 |
+_Request_Transport | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Run_Thread 9080 | 0 0 0.00 | 0 0 0.00 |
+_Sample_Effect 0 | 0 0 0.00 | 0 0 0.00 |
+-------------------------------------------------------------------------------
+
+ Page 7
+ .RTProfiler Performance Report
+
+Program: CONQUER.EXE Date: 12/13/1993
+Phase: 1 Time: 22:19:33
+Total Ticks: 2696
+
+ ---------- Logical --------------- Physical --------
+ | Total Average Pct | Total Average Pct |
+ Total | Clock No. of Total | Clock No. of Total|
+Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time |
+-------------------------------------------------------------------------------
+_Say_Phrase 2 | 0 0 0.00 | 0 0 0.00 |
+_Scroll_Tactical 48 | 0 0 0.00 | 0 0 0.00 |
+_Select_House 0 | 0 0 0.00 | 0 0 0.00 |
+_Select_Next 1 | 0 0 0.00 | 0 0 0.00 |
+_Send_Message 0 | 0 0 0.00 | 0 0 0.00 |
+_Set_Unit_Movement | | |
+ 42 | 0 0 0.00 | 0 0 0.00 |
+_Set_Unit_Rotation | | |
+ 1187 | 0 0 0.00 | 0 0 0.00 |
+_Setup_Menu 1 | 0 0 0.00 | 0 0 0.00 |
+_Sight_From 14 | 1 0 0.04 | 0 0 0.00 |
+_Sound_Effect 31 | 0 0 0.00 | 0 0 0.00 |
+_Special_Discovery | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Speed_Adjust 35 | 0 0 0.00 | 0 0 0.00 |
+_Speed_From_Name 0 | 0 0 0.00 | 0 0 0.00 |
+_Spew_Spice 0 | 0 0 0.00 | 0 0 0.00 |
+_Spice_Adjust 0 | 0 0 0.00 | 0 0 0.00 |
+_Spice_Discovery 0 | 0 0 0.00 | 0 0 0.00 |
+_Start_Scenario 1 | 125 125 4.64 | 0 0 0.00 |
+_Start_Thread 38 | 0 0 0.00 | 0 0 0.00 |
+_Struct_From_Name 1 | 0 0 0.00 | 0 0 0.00 |
+_System_Error 0 | 0 0 0.00 | 0 0 0.00 |
+_Target_Build 277 | 0 0 0.00 | 0 0 0.00 |
+_Target_Building 22 | 0 0 0.00 | 0 0 0.00 |
+_Target_Cell 27 | 0 0 0.00 | 0 0 0.00 |
+_Target_Coord 1222 | 1 0 0.04 | 0 0 0.00 |
+_Target_Distance 331 | 0 0 0.00 | 0 0 0.00 |
+_Target_Kind 2070 | 0 0 0.00 | 0 0 0.00 |
+_Target_Legal 437 | 0 0 0.00 | 0 0 0.00 |
+_Target_Object 53 | 0 0 0.00 | 0 0 0.00 |
+_Target_Unit 91 | 0 0 0.00 | 0 0 0.00 |
+_Target_Value 22 | 0 0 0.00 | 0 0 0.00 |
+_Team_Alloc 0 | 0 0 0.00 | 0 0 0.00 |
+_Team_Chunk_In 0 | 0 0 0.00 | 0 0 0.00 |
+_Team_Chunk_Out 0 | 0 0 0.00 | 0 0 0.00 |
+_Team_Create 0 | 0 0 0.00 | 0 0 0.00 |
+_Team_First 31 | 0 0 0.00 | 0 0 0.00 |
+_Team_Free 0 | 0 0 0.00 | 0 0 0.00 |
+_Team_From_ID 0 | 0 0 0.00 | 0 0 0.00 |
+_Team_Logic 0 | 0 0 0.00 | 0 0 0.00 |
+_Team_Next 31 | 0 0 0.00 | 0 0 0.00 |
+_Team_System_Calibrate | | |
+ 1 | 0 0 0.00 | 0 0 0.00 |
+_Team_System_Shutdown | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Team_System_Startup | | |
+ 2 | 0 0 0.00 | 0 0 0.00 |
+_Terrain_Cost 32 | 0 0 0.00 | 0 0 0.00 |
+_Terrain_Type 65 | 0 0 0.00 | 0 0 0.00 |
+-------------------------------------------------------------------------------
+
+ Page 8
+ .RTProfiler Performance Report
+
+Program: CONQUER.EXE Date: 12/13/1993
+Phase: 1 Time: 22:19:33
+Total Ticks: 2696
+
+ ---------- Logical --------------- Physical --------
+ | Total Average Pct | Total Average Pct |
+ Total | Clock No. of Total | Clock No. of Total|
+Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time |
+-------------------------------------------------------------------------------
+_Text_Input 0 | 0 0 0.00 | 0 0 0.00 |
+_Text_String 70 | 0 0 0.00 | 0 0 0.00 |
+_Track_Func 1089 | 0 0 0.00 | 0 0 0.00 |
+_Tutor_Message 2 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Alloc 22 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Available 0 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Brainwash 0 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Check 0 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Chunk_In 0 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Chunk_Out 0 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Create 0 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Damage 38 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Delete 20 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Enters_Building | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Unit_First 3316 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Free 20 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Hidden 31 | 0 0 0.00 | 0 0 0.00 |
+_Unit_House 1379 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Joins_Team 0 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Leaves_Team 32 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Limbo 0 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Loaner 0 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Look 29 | 1 0 0.04 | 0 0 0.00 |
+_Unit_Mark 1144 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Physics 2295 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Rally 18 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Select 29 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Sort 1090 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Stun 1 | 0 0 0.00 | 0 0 0.00 |
+_Unit_System_Calibrate | | |
+ 1 | 0 0 0.00 | 0 0 0.00 |
+_Unit_System_Shutdown | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Unit_System_Startup | | |
+ 2 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Threat 0 | 0 0 0.00 | 0 0 0.00 |
+_Unit_UnTarget 31 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Unlimbo 11 | 0 0 0.00 | 0 0 0.00 |
+_Units_Team 8 | 0 0 0.00 | 0 0 0.00 |
+_Upgrade_On 0 | 0 0 0.00 | 0 0 0.00 |
+_Valid_Thread 1918 | 0 0 0.00 | 0 0 0.00 |
+_Wall_Flags 0 | 0 0 0.00 | 0 0 0.00 |
+_Window_Box 2 | 0 0 0.00 | 0 0 0.00 |
+_WritePrivateProfileInt | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_WritePrivateProfileString | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Write_Scenario 0 | 0 0 0.00 | 0 0 0.00 |
+_Write_Scenario_Ini | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+-------------------------------------------------------------------------------
+
+ Page 9
+ .RTProfiler Performance Report
+
+Program: CONQUER.EXE Date: 12/13/1993
+Phase: 1 Time: 22:19:33
+Total Ticks: 2696
+
+ ---------- Logical --------------- Physical --------
+ | Total Average Pct | Total Average Pct |
+ Total | Clock No. of Total | Clock No. of Total|
+Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time |
+-------------------------------------------------------------------------------
+_Write_Scenario_Raw | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_XYPixel_Coord 20 | 0 0 0.00 | 0 0 0.00 |
+_XY_Cell 0 | 0 0 0.00 | 0 0 0.00 |
+__MMODEL 0 | 0 0 0.00 | 0 0 0.00 |
+__checknull 1 | 0 0 0.00 | 0 0 0.00 |
+__restorezero 1 | 0 0 0.00 | 0 0 0.00 |
+__terminate 1 | 0 0 0.00 | 0 0 0.00 |
+_abort 0 | 0 0 0.00 | 0 0 0.00 |
+_strtrim 33 | 0 0 0.00 | 0 0 0.00 |
+anim.c_NONPUB_Anim_Remove | | |
+ 20 | 0 0 0.00 | 0 0 0.00 |
+anim.c_NONPUB_Animation_In | | |
+stall 20 | 0 0 0.00 | 0 0 0.00 |
+anim.c_NONPUB_Cmd_Crash | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+anim.c_NONPUB_Cmd_Crater | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+anim.c_NONPUB_Cmd_Illegal | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+anim.c_NONPUB_Cmd_Normal | | |
+ 640 | 0 0 0.00 | 0 0 0.00 |
+anim.c_NONPUB_Cmd_RTimer | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+anim.c_NONPUB_Cmd_Repeat | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+anim.c_NONPUB_Cmd_Reverse | | |
+ 1280 | 0 0 0.00 | 0 0 0.00 |
+anim.c_NONPUB_Cmd_Shake | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+anim.c_NONPUB_Cmd_ShiftX | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+anim.c_NONPUB_Cmd_ShiftY | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+anim.c_NONPUB_Cmd_Sound | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+anim.c_NONPUB_Cmd_Stop | | |
+ 640 | 0 0 0.00 | 0 0 0.00 |
+anim.c_NONPUB_Cmd_Timer | | |
+ 1920 | 0 0 0.00 | 0 0 0.00 |
+anim.c_NONPUB_Cmd_Trigger_ | | |
+Bloom 0 | 0 0 0.00 | 0 0 0.00 |
+audio.c_NONPUB_Dune_Load_S | | |
+ample 0 | 0 0 0.00 | 0 0 0.00 |
+blogic.c_NONPUB_Cmd_Abort_ | | |
+Transport 0 | 0 0 0.00 | 0 0 0.00 |
+blogic.c_NONPUB_Cmd_Busy_S | | |
+tate 0 | 0 0 0.00 | 0 0 0.00 |
+blogic.c_NONPUB_Cmd_Change | | |
+_Facing_One 0 | 0 0 0.00 | 0 0 0.00 |
+-------------------------------------------------------------------------------
+
+ Page 10
+ .RTProfiler Performance Report
+
+Program: CONQUER.EXE Date: 12/13/1993
+Phase: 1 Time: 22:19:33
+Total Ticks: 2696
+
+ ---------- Logical --------------- Physical --------
+ | Total Average Pct | Total Average Pct |
+ Total | Clock No. of Total | Clock No. of Total|
+Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time |
+-------------------------------------------------------------------------------
+blogic.c_NONPUB_Cmd_Check_ | | |
+Up 0 | 0 0 0.00 | 0 0 0.00 |
+blogic.c_NONPUB_Cmd_Direct | | |
+ion 0 | 0 0 0.00 | 0 0 0.00 |
+blogic.c_NONPUB_Cmd_Dummy | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+blogic.c_NONPUB_Cmd_Fire_A | | |
+t 0 | 0 0 0.00 | 0 0 0.00 |
+blogic.c_NONPUB_Cmd_Image | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+blogic.c_NONPUB_Cmd_Launch | | |
+_Unit 0 | 0 0 0.00 | 0 0 0.00 |
+blogic.c_NONPUB_Cmd_Look | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+blogic.c_NONPUB_Cmd_Neares | | |
+t_Enemy 0 | 0 0 0.00 | 0 0 0.00 |
+blogic.c_NONPUB_Cmd_Play_S | | |
+ound 0 | 0 0 0.00 | 0 0 0.00 |
+blogic.c_NONPUB_Cmd_Reques | | |
+t_Transport 0 | 0 0 0.00 | 0 0 0.00 |
+blogic.c_NONPUB_Cmd_Self_E | | |
+xplode 0 | 0 0 0.00 | 0 0 0.00 |
+blogic.c_NONPUB_Cmd_Termin | | |
+ate_Self 0 | 0 0 0.00 | 0 0 0.00 |
+blogic.c_NONPUB_Cmd_Unload | | |
+_Harvester 0 | 0 0 0.00 | 0 0 0.00 |
+conquer.c_NONPUB_Keyboard_ | | |
+Process 97 | 1021 11 37.87 | 0 0 0.00 |
+conquer.c_NONPUB_MT32_Init | | |
+ 1 | 0 0 0.00 | 0 0 0.00 |
+cursor.c_NONPUB_Cursor_Mar | | |
+k 324 | 0 0 0.00 | 0 0 0.00 |
+findpath.c_NONPUB_Follow_E | | |
+dge 0 | 0 0 0.00 | 0 0 0.00 |
+findpath.c_NONPUB_Optimize | | |
+_Moves 5 | 0 0 0.00 | 0 0 0.00 |
+icon.c_NONPUB_Cmd_IconSet | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+icon.c_NONPUB_Cmd_Illegal | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+icon.c_NONPUB_Cmd_Loop | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+icon.c_NONPUB_Cmd_Map | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+icon.c_NONPUB_Cmd_Normal | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+icon.c_NONPUB_Cmd_Repeat | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+icon.c_NONPUB_Cmd_Sound | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+-------------------------------------------------------------------------------
+
+ Page 11
+ .RTProfiler Performance Report
+
+Program: CONQUER.EXE Date: 12/13/1993
+Phase: 1 Time: 22:19:33
+Total Ticks: 2696
+
+ ---------- Logical --------------- Physical --------
+ | Total Average Pct | Total Average Pct |
+ Total | Clock No. of Total | Clock No. of Total|
+Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time |
+-------------------------------------------------------------------------------
+icon.c_NONPUB_Cmd_Stop | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+icon.c_NONPUB_Cmd_Term | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+icon.c_NONPUB_Cmd_Timer | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+init.c_NONPUB_Init_Globals | | |
+ 1 | 0 0 0.00 | 0 0 0.00 |
+init.c_NONPUB_Load_A_Brain | | |
+ 3 | 1 0 0.04 | 0 0 0.00 |
+loadgame.c_NONPUB_BYTE_In | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+loadgame.c_NONPUB_LONG_In | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+loadgame.c_NONPUB_Struct_I | | |
+n 0 | 0 0 0.00 | 0 0 0.00 |
+loadgame.c_NONPUB_Thread_G | | |
+ameize 0 | 0 0 0.00 | 0 0 0.00 |
+loadgame.c_NONPUB_WORD_In | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+physics.c_NONPUB_End_Of_Mo | | |
+ve 14 | 0 0 0.00 | 0 0 0.00 |
+physics.c_NONPUB_Momentum_ | | |
+Physics 938 | 0 0 0.00 | 0 0 0.00 |
+physics.c_NONPUB_Rotation_ | | |
+Physics 237 | 0 0 0.00 | 0 0 0.00 |
+physics.c_NONPUB_Start_Of_ | | |
+Move 64 | 0 0 0.00 | 0 0 0.00 |
+physics.c_NONPUB_While_Mov | | |
+ing 930 | 0 0 0.00 | 0 0 0.00 |
+plogic.c_NONPUB_Cmd_Dummy | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+plogic.c_NONPUB_Cmd_Harves | | |
+ter_Check 0 | 0 0 0.00 | 0 0 0.00 |
+plogic.c_NONPUB_Cmd_Sound | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+plogic.c_NONPUB_Cmd_Unit_C | | |
+reate 0 | 0 0 0.00 | 0 0 0.00 |
+savegame.c_NONPUB_BYTE_Out | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+savegame.c_NONPUB_LONG_Out | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+savegame.c_NONPUB_Struct_O | | |
+ut 0 | 0 0 0.00 | 0 0 0.00 |
+savegame.c_NONPUB_Thread_N | | |
+ormalize 0 | 0 0 0.00 | 0 0 0.00 |
+savegame.c_NONPUB_WORD_Out | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+scenario.c_NONPUB_Typical_ | | |
+Briefing 0 | 0 0 0.00 | 0 0 0.00 |
+-------------------------------------------------------------------------------
+
+ Page 12
+ .RTProfiler Performance Report
+
+Program: CONQUER.EXE Date: 12/13/1993
+Phase: 1 Time: 22:19:33
+Total Ticks: 2696
+
+ ---------- Logical --------------- Physical --------
+ | Total Average Pct | Total Average Pct |
+ Total | Clock No. of Total | Clock No. of Total|
+Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time |
+-------------------------------------------------------------------------------
+strinput.c_NONPUB_Input_Cu | | |
+rsor 0 | 0 0 0.00 | 0 0 0.00 |
+tlogic.c_NONPUB_Cmd_Attack | | |
+_Team_Target 0 | 0 0 0.00 | 0 0 0.00 |
+tlogic.c_NONPUB_Cmd_Change | | |
+_MO 0 | 0 0 0.00 | 0 0 0.00 |
+tlogic.c_NONPUB_Cmd_Displa | | |
+y_Message 0 | 0 0 0.00 | 0 0 0.00 |
+tlogic.c_NONPUB_Cmd_Dummy | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+tlogic.c_NONPUB_Cmd_Find_T | | |
+eam_Target 0 | 0 0 0.00 | 0 0 0.00 |
+tlogic.c_NONPUB_Cmd_Recrui | | |
+t 0 | 0 0 0.00 | 0 0 0.00 |
+tlogic.c_NONPUB_Cmd_Regrou | | |
+p 0 | 0 0 0.00 | 0 0 0.00 |
+tlogic.c_NONPUB_Cmd_Restor | | |
+e_MO 0 | 0 0 0.00 | 0 0 0.00 |
+tlogic.c_NONPUB_Cmd_Team_C | | |
+ount 0 | 0 0 0.00 | 0 0 0.00 |
+tlogic.c_NONPUB_Cmd_Team_M | | |
+in 0 | 0 0 0.00 | 0 0 0.00 |
+tlogic.c_NONPUB_Cmd_Team_S | | |
+pread 0 | 0 0 0.00 | 0 0 0.00 |
+tlogic.c_NONPUB_Cmd_Team_T | | |
+arget 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Abort_ | | |
+Transport 14 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_All_St | | |
+op 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Altern | | |
+ate_Target 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Animat | | |
+e 3 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Call_F | | |
+or_Landing 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Cargo | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Change | | |
+_Facing 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Change | | |
+_Order 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Death_ | | |
+Message 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Deploy | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Dock | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Dummy | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+-------------------------------------------------------------------------------
+
+ Page 13
+ .RTProfiler Performance Report
+
+Program: CONQUER.EXE Date: 12/13/1993
+Phase: 1 Time: 22:19:33
+Total Ticks: 2696
+
+ ---------- Logical --------------- Physical --------
+ | Total Average Pct | Total Average Pct |
+ Total | Clock No. of Total | Clock No. of Total|
+Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time |
+-------------------------------------------------------------------------------
+ulogic.c_NONPUB_Cmd_Eject | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Explos | | |
+ion 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Face_M | | |
+ove_Toward 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Face_T | | |
+arCom 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Flash | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Greate | | |
+st_Threat 8 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Hot_LZ | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Idle | | |
+ 6 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Inq_Un | | |
+it 862 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Is_Clo | | |
+se_To_NavCom 181 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Is_Clo | | |
+se_To_TarCom 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Load | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Loaded | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Lock_O | | |
+n_To_Target 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Look | | |
+ 27 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Mine_S | | |
+pice 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_MultiE | | |
+xplosion 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Noisie | | |
+st_Target 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Reques | | |
+t_Transport 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Rest | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Set_Hu | | |
+lk 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Set_Na | | |
+vCom 8 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Set_Sp | | |
+eed 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Set_Ta | | |
+rCom 16 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Spew_B | | |
+odies 0 | 0 0 0.00 | 0 0 0.00 |
+-------------------------------------------------------------------------------
+
+ Page 14
+ .RTProfiler Performance Report
+
+Program: CONQUER.EXE Date: 12/13/1993
+Phase: 1 Time: 22:19:33
+Total Ticks: 2696
+
+ ---------- Logical --------------- Physical --------
+ | Total Average Pct | Total Average Pct |
+ Total | Clock No. of Total | Clock No. of Total|
+Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time |
+-------------------------------------------------------------------------------
+ulogic.c_NONPUB_Cmd_Target | | |
+_Dir 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Termin | | |
+ate_Self 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Threat | | |
+_Value 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Travel | | |
+_To 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Unload | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Unload | | |
+ed_Struct 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Verify | | |
+_Contact 0 | 0 0 0.00 | 0 0 0.00 |
+
+Total Logical Percentage : 362.39
+Total Physical Percentage : 99.93
diff --git a/CONQUER.REP b/CONQUER.REP
new file mode 100644
index 0000000..9361013
--- /dev/null
+++ b/CONQUER.REP
@@ -0,0 +1,880 @@
+
+ Page 1
+ .RTProfiler Performance Report
+
+Program: CONQUER.EXE Date: 01/20/1994
+Phase: 1 Time: 17:04:46
+Total Ticks: 2081
+
+ ---------- Logical --------------- Physical --------
+ | Total Average Pct | Total Average Pct |
+ Total | Clock No. of Total | Clock No. of Total|
+Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time |
+-------------------------------------------------------------------------------
+display.c_NONPUB_Refresh_M | | |
+ap 790 | 630 1 30.27 | 620 1 29.79 |
+_Call_Back 450547 | 626 0 30.08 | 541 0 26.00 |
+_Main_Program 0 | 2071 0 99.52 | 521 0 25.04 |
+_Self_Regulate 790 | 156 0 7.50 | 156 0 7.50 |
+_Sound_Callback 450547 | 85 0 4.08 | 85 0 4.08 |
+_Draw_Map 790 | 667 1 32.05 | 37 0 1.78 |
+_Process_Logic 790 | 100 0 4.81 | 21 0 1.01 |
+_Icon_Update 790 | 18 0 0.86 | 18 0 0.86 |
+_Refresh_Cell 5464 | 9 0 0.43 | 9 0 0.43 |
+_Anim_Update 790 | 14 0 0.67 | 7 0 0.34 |
+_Unit_Mark 1319 | 15 0 0.72 | 7 0 0.34 |
+physics.c_NONPUB_Momentum_ | | |
+Physics 570 | 26 0 1.25 | 7 0 0.34 |
+_Unit_Physics 1636 | 36 0 1.73 | 5 0 0.24 |
+_Coord_Cell 55104 | 4 0 0.19 | 4 0 0.19 |
+_Draw_Vehicle 493 | 4 0 0.19 | 4 0 0.19 |
+physics.c_NONPUB_While_Mov | | |
+ing 553 | 4 0 0.19 | 4 0 0.19 |
+anim.c_NONPUB_Cmd_Normal | | |
+ 256 | 3 0 0.14 | 3 0 0.14 |
+$$VMGETPAGE 1 | 2 2 0.10 | 2 2 0.10 |
+_Map_Cell 168 | 2 0 0.10 | 2 0 0.10 |
+_Unit_First 2054 | 2 0 0.10 | 2 0 0.10 |
+_main 1 | 2073 2073 99.62 | 2 2 0.10 |
+anim.c_NONPUB_Anim_Remove | | |
+ 8 | 2 0 0.10 | 2 0 0.10 |
+_Can_Unit_Enter_Cell | | |
+ 540 | 1 0 0.05 | 1 0 0.05 |
+_Coord_Distance 1376 | 1 0 0.05 | 1 0 0.05 |
+_Coord_Facing256 822 | 1 0 0.05 | 1 0 0.05 |
+_Coord_Spillage_Number | | |
+ 1291 | 1 0 0.05 | 1 0 0.05 |
+_Greatest_Threat 8 | 1 0 0.05 | 1 0 0.05 |
+_Radio_Contact 45 | 1 0 0.05 | 1 0 0.05 |
+_Run_Thread 2430 | 4 0 0.19 | 1 0 0.05 |
+_Shape_Ptr 689 | 1 0 0.05 | 1 0 0.05 |
+_Target_Coord 773 | 1 0 0.05 | 1 0 0.05 |
+_Target_Legal 298 | 1 0 0.05 | 1 0 0.05 |
+_Unit_Damage 13 | 1 0 0.05 | 1 0 0.05 |
+_Unit_From_ID 1424 | 1 0 0.05 | 1 0 0.05 |
+__cleanup 1 | 1 1 0.05 | 1 1 0.05 |
+anim.c_NONPUB_Cmd_Reverse | | |
+ 512 | 2 0 0.10 | 1 0 0.05 |
+DGROUP@ 0 | 0 0 0.00 | 0 0 0.00 |
+_Adjacent_Cell 0 | 0 0 0.00 | 0 0 0.00 |
+_Adjacent_Free_Cell | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Assign_Destination | | |
+ 5 | 0 0 0.00 | 0 0 0.00 |
+-------------------------------------------------------------------------------
+
+ Page 2
+ .RTProfiler Performance Report
+
+Program: CONQUER.EXE Date: 01/20/1994
+Phase: 1 Time: 17:04:46
+Total Ticks: 2081
+
+ ---------- Logical --------------- Physical --------
+ | Total Average Pct | Total Average Pct |
+ Total | Clock No. of Total | Clock No. of Total|
+Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time |
+-------------------------------------------------------------------------------
+_Assign_Order 15 | 0 0 0.00 | 0 0 0.00 |
+_Assign_Target 2 | 0 0 0.00 | 0 0 0.00 |
+_Attached_Building | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Attached_Unit 0 | 0 0 0.00 | 0 0 0.00 |
+_Base_Under_Attack | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Bldg_Chunk_In 0 | 0 0 0.00 | 0 0 0.00 |
+_Bldg_Chunk_Out 0 | 0 0 0.00 | 0 0 0.00 |
+_Bound_Cursor 0 | 0 0 0.00 | 0 0 0.00 |
+_Break_Contact_With | | |
+ 40 | 0 0 0.00 | 0 0 0.00 |
+_Break_Radio_Contact | | |
+ 20 | 0 0 0.00 | 0 0 0.00 |
+_Bttn_Building 0 | 0 0 0.00 | 0 0 0.00 |
+_Bttn_Cancel 0 | 0 0 0.00 | 0 0 0.00 |
+_Bttn_Custom_Render | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Bttn_Doit_Render 0 | 0 0 0.00 | 0 0 0.00 |
+_Bttn_Enter 0 | 0 0 0.00 | 0 0 0.00 |
+_Bttn_Enter_Render | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Bttn_Mode 0 | 0 0 0.00 | 0 0 0.00 |
+_Bttn_OrderSelect 0 | 0 0 0.00 | 0 0 0.00 |
+_Bttn_Repair 0 | 0 0 0.00 | 0 0 0.00 |
+_Bttn_Return 0 | 0 0 0.00 | 0 0 0.00 |
+_Buildables 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_Alloc 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_Create 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_Damage 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_Delete 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_Destroy 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_First 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_Free 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_From_ID 369 | 0 0 0.00 | 0 0 0.00 |
+_Building_Logic 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_Look 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_Next 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_Render 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_Scan 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_Spec 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_System_Calibrate | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_System_Shutdown | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_System_Startup | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_Threat 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_Unlimbo 0 | 0 0 0.00 | 0 0 0.00 |
+_Building_Untarget | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+-------------------------------------------------------------------------------
+
+ Page 3
+ .RTProfiler Performance Report
+
+Program: CONQUER.EXE Date: 01/20/1994
+Phase: 1 Time: 17:04:46
+Total Ticks: 2081
+
+ ---------- Logical --------------- Physical --------
+ | Total Average Pct | Total Average Pct |
+ Total | Clock No. of Total | Clock No. of Total|
+Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time |
+-------------------------------------------------------------------------------
+_Calculated_Cell 0 | 0 0 0.00 | 0 0 0.00 |
+_Can_Unit_Enter_Building | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Can_Upgrade 0 | 0 0 0.00 | 0 0 0.00 |
+_Cardinal_To_Fixed | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_CellXY_Coord 281 | 0 0 0.00 | 0 0 0.00 |
+_Cell_Building 28 | 0 0 0.00 | 0 0 0.00 |
+_Cell_Coord 46 | 0 0 0.00 | 0 0 0.00 |
+_Cell_Distance 3 | 0 0 0.00 | 0 0 0.00 |
+_Cell_Facing 28 | 0 0 0.00 | 0 0 0.00 |
+_Cell_Object 0 | 0 0 0.00 | 0 0 0.00 |
+_Cell_Unit 1104 | 0 0 0.00 | 0 0 0.00 |
+_Cell_X 0 | 0 0 0.00 | 0 0 0.00 |
+_Cell_Y 0 | 0 0 0.00 | 0 0 0.00 |
+_Center_Map 0 | 0 0 0.00 | 0 0 0.00 |
+_Change_State 10 | 0 0 0.00 | 0 0 0.00 |
+_Check_Menu 0 | 0 0 0.00 | 0 0 0.00 |
+_Class_From_Name 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Attached 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Available_Building | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Class_Count | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Dialog 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Display_Message | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Distance 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Is_Clear 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Is_Enemy 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Is_Friendly | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Mono_Print | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Nearest_Spice | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Pause 128 | 0 0 0.00 | 0 0 0.00 |
+_Common_Pause_Random | | |
+ 6 | 0 0 0.00 | 0 0 0.00 |
+_Common_Play_Sound | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Random 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Scenario 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Target_Distance | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Target_Facing | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Target_Kind | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Common_Target_Type | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+-------------------------------------------------------------------------------
+
+ Page 4
+ .RTProfiler Performance Report
+
+Program: CONQUER.EXE Date: 01/20/1994
+Phase: 1 Time: 17:04:46
+Total Ticks: 2081
+
+ ---------- Logical --------------- Physical --------
+ | Total Average Pct | Total Average Pct |
+ Total | Clock No. of Total | Clock No. of Total|
+Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time |
+-------------------------------------------------------------------------------
+_Common_Tutor_Message | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Coord_Add 18 | 0 0 0.00 | 0 0 0.00 |
+_Coord_Cell_Distance | | |
+ 274 | 0 0 0.00 | 0 0 0.00 |
+_Coord_Facing16 0 | 0 0 0.00 | 0 0 0.00 |
+_Coord_Facing8 0 | 0 0 0.00 | 0 0 0.00 |
+_Coord_Mid 0 | 0 0 0.00 | 0 0 0.00 |
+_Coord_Move 570 | 0 0 0.00 | 0 0 0.00 |
+_Coord_Scatter 0 | 0 0 0.00 | 0 0 0.00 |
+_Coord_Snap 0 | 0 0 0.00 | 0 0 0.00 |
+_Coord_Sub 730 | 0 0 0.00 | 0 0 0.00 |
+_Coord_XCell 0 | 0 0 0.00 | 0 0 0.00 |
+_Coord_XPixel 0 | 0 0 0.00 | 0 0 0.00 |
+_Coord_YCell 0 | 0 0 0.00 | 0 0 0.00 |
+_Coord_YPixel 0 | 0 0 0.00 | 0 0 0.00 |
+_Coordinates_In_Region | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Cursor 239 | 0 0 0.00 | 0 0 0.00 |
+_Cursor_Move 0 | 0 0 0.00 | 0 0 0.00 |
+_Cursor_Size 0 | 0 0 0.00 | 0 0 0.00 |
+_Debug_Key 40 | 0 0 0.00 | 0 0 0.00 |
+_Desired_Facing256 | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Destroy_Wall 0 | 0 0 0.00 | 0 0 0.00 |
+_Dialog_Message 0 | 0 0 0.00 | 0 0 0.00 |
+_Display_Selected_Terrain | | |
+ 10 | 0 0 0.00 | 0 0 0.00 |
+_Display_Status 790 | 0 0 0.00 | 0 0 0.00 |
+_Do_Button_Setup 0 | 0 0 0.00 | 0 0 0.00 |
+_Do_Explosion 8 | 3 0 0.14 | 0 0 0.00 |
+_Do_Gas 0 | 0 0 0.00 | 0 0 0.00 |
+_Do_Lose 0 | 0 0 0.00 | 0 0 0.00 |
+_Do_Win 0 | 0 0 0.00 | 0 0 0.00 |
+_Draw_Box 0 | 0 0 0.00 | 0 0 0.00 |
+_Edit_Unit_Offsets | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_End_Game 790 | 0 0 0.00 | 0 0 0.00 |
+_Establish_Contact_With | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Establish_Origin 9 | 0 0 0.00 | 0 0 0.00 |
+_Establish_Radio_Contact | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Fancy_Text_Print 0 | 0 0 0.00 | 0 0 0.00 |
+_Fetch_Text_String | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_File_Stream_Sample | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Fill_In_Data 0 | 0 0 0.00 | 0 0 0.00 |
+_Find_Brain_Size 0 | 0 0 0.00 | 0 0 0.00 |
+-------------------------------------------------------------------------------
+
+ Page 5
+ .RTProfiler Performance Report
+
+Program: CONQUER.EXE Date: 01/20/1994
+Phase: 1 Time: 17:04:46
+Total Ticks: 2081
+
+ ---------- Logical --------------- Physical --------
+ | Total Average Pct | Total Average Pct |
+ Total | Clock No. of Total | Clock No. of Total|
+Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time |
+-------------------------------------------------------------------------------
+_Find_Path 5 | 1 0 0.05 | 0 0 0.00 |
+_Find_Waypoint 0 | 0 0 0.00 | 0 0 0.00 |
+_Fire_Bullet 0 | 0 0 0.00 | 0 0 0.00 |
+_Fire_Weapon 267 | 3 0 0.14 | 0 0 0.00 |
+_Fixed_To_Cardinal | | |
+ 20 | 0 0 0.00 | 0 0 0.00 |
+_Format_Window_String | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Free_Brains 0 | 0 0 0.00 | 0 0 0.00 |
+_Free_Sample 0 | 0 0 0.00 | 0 0 0.00 |
+_Game_Screen 0 | 0 0 0.00 | 0 0 0.00 |
+_GetPrivateProfileInt | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_GetPrivateProfileString | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Get_Connection_Info | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Get_Connection_Num | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Get_Internet_Address | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Get_Local_Connection_Numb | | |
+er 0 | 0 0 0.00 | 0 0 0.00 |
+_Greatest_Building_Threat | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Greatest_Unit_Noise | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Greatest_Unit_Threat | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Harvester_Check 0 | 0 0 0.00 | 0 0 0.00 |
+_House_From_Name 0 | 0 0 0.00 | 0 0 0.00 |
+_IPX_Cancel_Event 0 | 0 0 0.00 | 0 0 0.00 |
+_IPX_Close_Socket 0 | 0 0 0.00 | 0 0 0.00 |
+_IPX_Get_Local_Target | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_IPX_Listen_For_Packet | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_IPX_Open_Socket 0 | 0 0 0.00 | 0 0 0.00 |
+_IPX_Relinquish_Control | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_IPX_Schedule_IPX_Event | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_IPX_Send_Packet 0 | 0 0 0.00 | 0 0 0.00 |
+_Icon_Install 0 | 0 0 0.00 | 0 0 0.00 |
+_Icon_Remove 0 | 0 0 0.00 | 0 0 0.00 |
+_In_Radar 547 | 0 0 0.00 | 0 0 0.00 |
+_In_Radio_Contact 0 | 0 0 0.00 | 0 0 0.00 |
+_In_View 0 | 0 0 0.00 | 0 0 0.00 |
+_Info_Chunk_In 0 | 0 0 0.00 | 0 0 0.00 |
+_Info_Chunk_Out 0 | 0 0 0.00 | 0 0 0.00 |
+-------------------------------------------------------------------------------
+
+ Page 6
+ .RTProfiler Performance Report
+
+Program: CONQUER.EXE Date: 01/20/1994
+Phase: 1 Time: 17:04:46
+Total Ticks: 2081
+
+ ---------- Logical --------------- Physical --------
+ | Total Average Pct | Total Average Pct |
+ Total | Clock No. of Total | Clock No. of Total|
+Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time |
+-------------------------------------------------------------------------------
+_Init_Game 0 | 0 0 0.00 | 0 0 0.00 |
+_Init_Shapes 0 | 0 0 0.00 | 0 0 0.00 |
+_Init_Thread 46 | 0 0 0.00 | 0 0 0.00 |
+_Interrupt_Thread 0 | 0 0 0.00 | 0 0 0.00 |
+_Is_Allied 860 | 0 0 0.00 | 0 0 0.00 |
+_Is_Lined_Up 273 | 0 0 0.00 | 0 0 0.00 |
+_Is_Mapped 11 | 0 0 0.00 | 0 0 0.00 |
+_Language_Name 0 | 0 0 0.00 | 0 0 0.00 |
+_Launch_GoldenBB 0 | 0 0 0.00 | 0 0 0.00 |
+_Launch_Special 0 | 0 0 0.00 | 0 0 0.00 |
+_Legal_Placement 0 | 0 0 0.00 | 0 0 0.00 |
+_Load_Brains 0 | 0 0 0.00 | 0 0 0.00 |
+_Load_Map 0 | 0 0 0.00 | 0 0 0.00 |
+_Load_Sample 9 | 0 0 0.00 | 0 0 0.00 |
+_Load_Sample_Into_Buffer | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Load_Samples 0 | 0 0 0.00 | 0 0 0.00 |
+_Load_System_Strings | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Load_Terrain_Icons | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_MO_From_Name 0 | 0 0 0.00 | 0 0 0.00 |
+_Make_Button 0 | 0 0 0.00 | 0 0 0.00 |
+_Map_Chunk_In 0 | 0 0 0.00 | 0 0 0.00 |
+_Map_Chunk_Out 0 | 0 0 0.00 | 0 0 0.00 |
+_Map_Edit 0 | 0 0 0.00 | 0 0 0.00 |
+_Mouse_Coord 0 | 0 0 0.00 | 0 0 0.00 |
+_Nearest_Spice 0 | 0 0 0.00 | 0 0 0.00 |
+_Novell_Detect 0 | 0 0 0.00 | 0 0 0.00 |
+_Order_From_Name 0 | 0 0 0.00 | 0 0 0.00 |
+_Phrase_CallBack 0 | 0 0 0.00 | 0 0 0.00 |
+_Pixel_Coordinate 1389 | 0 0 0.00 | 0 0 0.00 |
+_Play_Sample 9 | 0 0 0.00 | 0 0 0.00 |
+_Play_Sample_Vol 9 | 0 0 0.00 | 0 0 0.00 |
+_Play_Theme 0 | 0 0 0.00 | 0 0 0.00 |
+_Player_Alloc 0 | 0 0 0.00 | 0 0 0.00 |
+_Player_First 0 | 0 0 0.00 | 0 0 0.00 |
+_Player_Free 0 | 0 0 0.00 | 0 0 0.00 |
+_Player_From_ID 1663 | 0 0 0.00 | 0 0 0.00 |
+_Player_Logic 0 | 0 0 0.00 | 0 0 0.00 |
+_Player_Next 0 | 0 0 0.00 | 0 0 0.00 |
+_Player_System_Shutdown | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Player_System_Startup | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Plyr_Chunk_In 0 | 0 0 0.00 | 0 0 0.00 |
+_Plyr_Chunk_Out 0 | 0 0 0.00 | 0 0 0.00 |
+_Power_Effects 0 | 0 0 0.00 | 0 0 0.00 |
+_Production_Needs 0 | 0 0 0.00 | 0 0 0.00 |
+_Production_Start 0 | 0 0 0.00 | 0 0 0.00 |
+-------------------------------------------------------------------------------
+
+ Page 7
+ .RTProfiler Performance Report
+
+Program: CONQUER.EXE Date: 01/20/1994
+Phase: 1 Time: 17:04:46
+Total Ticks: 2081
+
+ ---------- Logical --------------- Physical --------
+ | Total Average Pct | Total Average Pct |
+ Total | Clock No. of Total | Clock No. of Total|
+Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time |
+-------------------------------------------------------------------------------
+_Radar_Checkup 0 | 0 0 0.00 | 0 0 0.00 |
+_Radar_Cursor 790 | 0 0 0.00 | 0 0 0.00 |
+_Radar_Pixel 1053 | 0 0 0.00 | 0 0 0.00 |
+_Read_Scenario 0 | 0 0 0.00 | 0 0 0.00 |
+_Read_Scenario_Ini | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Read_Scenario_Raw | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Recalc_Storage 0 | 0 0 0.00 | 0 0 0.00 |
+_Redistribute_Power | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Redraw_Objects 0 | 0 0 0.00 | 0 0 0.00 |
+_Repair_On 0 | 0 0 0.00 | 0 0 0.00 |
+_Request_Transport | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Sample_Effect 0 | 0 0 0.00 | 0 0 0.00 |
+_Sample_Read 0 | 0 0 0.00 | 0 0 0.00 |
+_Sample_Status 0 | 0 0 0.00 | 0 0 0.00 |
+_Say_Phrase 0 | 0 0 0.00 | 0 0 0.00 |
+_Scroll_Tactical 128 | 0 0 0.00 | 0 0 0.00 |
+_Select_House 0 | 0 0 0.00 | 0 0 0.00 |
+_Select_Next 0 | 0 0 0.00 | 0 0 0.00 |
+_Send_Message 0 | 0 0 0.00 | 0 0 0.00 |
+_Set_Unit_Movement | | |
+ 18 | 0 0 0.00 | 0 0 0.00 |
+_Set_Unit_Rotation | | |
+ 532 | 0 0 0.00 | 0 0 0.00 |
+_Setup_Menu 0 | 0 0 0.00 | 0 0 0.00 |
+_Sight_From 7 | 2 0 0.10 | 0 0 0.00 |
+_Sound_Effect 7 | 0 0 0.00 | 0 0 0.00 |
+_Sound_End 1 | 0 0 0.00 | 0 0 0.00 |
+_Sound_Init 0 | 0 0 0.00 | 0 0 0.00 |
+_Special_Discovery | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Speed_Adjust 16 | 0 0 0.00 | 0 0 0.00 |
+_Speed_From_Name 0 | 0 0 0.00 | 0 0 0.00 |
+_Spew_Spice 0 | 0 0 0.00 | 0 0 0.00 |
+_Spice_Adjust 0 | 0 0 0.00 | 0 0 0.00 |
+_Spice_Discovery 0 | 0 0 0.00 | 0 0 0.00 |
+_Start_Scenario 0 | 0 0 0.00 | 0 0 0.00 |
+_Start_Thread 14 | 0 0 0.00 | 0 0 0.00 |
+_Stop_Sample 0 | 0 0 0.00 | 0 0 0.00 |
+_Stream_Sample 0 | 0 0 0.00 | 0 0 0.00 |
+_Struct_From_Name 0 | 0 0 0.00 | 0 0 0.00 |
+_Target_Build 92 | 0 0 0.00 | 0 0 0.00 |
+_Target_Building 5 | 0 0 0.00 | 0 0 0.00 |
+_Target_Cell 25 | 0 0 0.00 | 0 0 0.00 |
+_Target_Distance 151 | 1 0 0.05 | 0 0 0.00 |
+_Target_Kind 1265 | 0 0 0.00 | 0 0 0.00 |
+_Target_Object 20 | 0 0 0.00 | 0 0 0.00 |
+-------------------------------------------------------------------------------
+
+ Page 8
+ .RTProfiler Performance Report
+
+Program: CONQUER.EXE Date: 01/20/1994
+Phase: 1 Time: 17:04:46
+Total Ticks: 2081
+
+ ---------- Logical --------------- Physical --------
+ | Total Average Pct | Total Average Pct |
+ Total | Clock No. of Total | Clock No. of Total|
+Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time |
+-------------------------------------------------------------------------------
+_Target_Unit 24 | 0 0 0.00 | 0 0 0.00 |
+_Target_Value 299 | 0 0 0.00 | 0 0 0.00 |
+_Team_Alloc 0 | 0 0 0.00 | 0 0 0.00 |
+_Team_Chunk_In 0 | 0 0 0.00 | 0 0 0.00 |
+_Team_Chunk_Out 0 | 0 0 0.00 | 0 0 0.00 |
+_Team_Create 0 | 0 0 0.00 | 0 0 0.00 |
+_Team_First 15 | 0 0 0.00 | 0 0 0.00 |
+_Team_Free 0 | 0 0 0.00 | 0 0 0.00 |
+_Team_From_ID 0 | 0 0 0.00 | 0 0 0.00 |
+_Team_Logic 0 | 0 0 0.00 | 0 0 0.00 |
+_Team_Next 15 | 0 0 0.00 | 0 0 0.00 |
+_Team_System_Calibrate | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Team_System_Shutdown | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Team_System_Startup | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Terrain_Cost 516 | 1 0 0.05 | 0 0 0.00 |
+_Terrain_Type 356 | 0 0 0.00 | 0 0 0.00 |
+_Text_Input 0 | 0 0 0.00 | 0 0 0.00 |
+_Text_String 51 | 0 0 0.00 | 0 0 0.00 |
+_Track_Func 790 | 0 0 0.00 | 0 0 0.00 |
+_Tutor_Message 3 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Alloc 9 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Available 0 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Brainwash 0 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Check 0 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Chunk_In 0 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Chunk_Out 0 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Create 0 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Delete 8 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Enters_Building | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Free 8 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Hidden 15 | 0 0 0.00 | 0 0 0.00 |
+_Unit_House 91 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Joins_Team 0 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Leaves_Team 15 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Limbo 0 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Loaner 0 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Look 7 | 2 0 0.10 | 0 0 0.00 |
+_Unit_Next 5013 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Rally 5 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Revealed 168 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Select 10 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Sort 790 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Stun 0 | 0 0 0.00 | 0 0 0.00 |
+_Unit_System_Calibrate | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Unit_System_Shutdown | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+-------------------------------------------------------------------------------
+
+ Page 9
+ .RTProfiler Performance Report
+
+Program: CONQUER.EXE Date: 01/20/1994
+Phase: 1 Time: 17:04:46
+Total Ticks: 2081
+
+ ---------- Logical --------------- Physical --------
+ | Total Average Pct | Total Average Pct |
+ Total | Clock No. of Total | Clock No. of Total|
+Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time |
+-------------------------------------------------------------------------------
+_Unit_System_Startup | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Unit_Threat 0 | 0 0 0.00 | 0 0 0.00 |
+_Unit_UnTarget 15 | 1 0 0.05 | 0 0 0.00 |
+_Unit_Unlimbo 7 | 0 0 0.00 | 0 0 0.00 |
+_Units_Team 2 | 0 0 0.00 | 0 0 0.00 |
+_Upgrade_On 0 | 0 0 0.00 | 0 0 0.00 |
+_Valid_Thread 882 | 0 0 0.00 | 0 0 0.00 |
+_Vehicle_Edit 0 | 0 0 0.00 | 0 0 0.00 |
+_Window_Box 0 | 0 0 0.00 | 0 0 0.00 |
+_WritePrivateProfileInt | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_WritePrivateProfileString | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Write_Scenario 0 | 0 0 0.00 | 0 0 0.00 |
+_Write_Scenario_Ini | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_Write_Scenario_Raw | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+_XYPixel_Coord 9 | 0 0 0.00 | 0 0 0.00 |
+_XY_Cell 0 | 0 0 0.00 | 0 0 0.00 |
+__MMODEL 0 | 0 0 0.00 | 0 0 0.00 |
+__checknull 1 | 0 0 0.00 | 0 0 0.00 |
+__restorezero 1 | 0 0 0.00 | 0 0 0.00 |
+__terminate 1 | 0 0 0.00 | 0 0 0.00 |
+_abort 0 | 0 0 0.00 | 0 0 0.00 |
+_strtrim 0 | 0 0 0.00 | 0 0 0.00 |
+anim.c_NONPUB_Animation_In | | |
+stall 8 | 2 0 0.10 | 0 0 0.00 |
+anim.c_NONPUB_Cmd_Crash | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+anim.c_NONPUB_Cmd_Crater | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+anim.c_NONPUB_Cmd_Illegal | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+anim.c_NONPUB_Cmd_RTimer | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+anim.c_NONPUB_Cmd_Repeat | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+anim.c_NONPUB_Cmd_Shake | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+anim.c_NONPUB_Cmd_ShiftX | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+anim.c_NONPUB_Cmd_ShiftY | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+anim.c_NONPUB_Cmd_Sound | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+anim.c_NONPUB_Cmd_Stop | | |
+ 256 | 2 0 0.10 | 0 0 0.00 |
+anim.c_NONPUB_Cmd_Timer | | |
+ 768 | 0 0 0.00 | 0 0 0.00 |
+-------------------------------------------------------------------------------
+
+ Page 10
+ .RTProfiler Performance Report
+
+Program: CONQUER.EXE Date: 01/20/1994
+Phase: 1 Time: 17:04:46
+Total Ticks: 2081
+
+ ---------- Logical --------------- Physical --------
+ | Total Average Pct | Total Average Pct |
+ Total | Clock No. of Total | Clock No. of Total|
+Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time |
+-------------------------------------------------------------------------------
+anim.c_NONPUB_Cmd_Trigger_ | | |
+Bloom 0 | 0 0 0.00 | 0 0 0.00 |
+audio.c_NONPUB_Dune_Load_S | | |
+ample 0 | 0 0 0.00 | 0 0 0.00 |
+blogic.c_NONPUB_Cmd_Abort_ | | |
+Transport 0 | 0 0 0.00 | 0 0 0.00 |
+blogic.c_NONPUB_Cmd_Busy_S | | |
+tate 0 | 0 0 0.00 | 0 0 0.00 |
+blogic.c_NONPUB_Cmd_Change | | |
+_Facing_One 0 | 0 0 0.00 | 0 0 0.00 |
+blogic.c_NONPUB_Cmd_Check_ | | |
+Up 0 | 0 0 0.00 | 0 0 0.00 |
+blogic.c_NONPUB_Cmd_Direct | | |
+ion 0 | 0 0 0.00 | 0 0 0.00 |
+blogic.c_NONPUB_Cmd_Dummy | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+blogic.c_NONPUB_Cmd_Fire_A | | |
+t 0 | 0 0 0.00 | 0 0 0.00 |
+blogic.c_NONPUB_Cmd_Image | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+blogic.c_NONPUB_Cmd_Launch | | |
+_Unit 0 | 0 0 0.00 | 0 0 0.00 |
+blogic.c_NONPUB_Cmd_Look | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+blogic.c_NONPUB_Cmd_Neares | | |
+t_Enemy 0 | 0 0 0.00 | 0 0 0.00 |
+blogic.c_NONPUB_Cmd_Play_S | | |
+ound 0 | 0 0 0.00 | 0 0 0.00 |
+blogic.c_NONPUB_Cmd_Reques | | |
+t_Transport 0 | 0 0 0.00 | 0 0 0.00 |
+blogic.c_NONPUB_Cmd_Self_E | | |
+xplode 0 | 0 0 0.00 | 0 0 0.00 |
+blogic.c_NONPUB_Cmd_Termin | | |
+ate_Self 0 | 0 0 0.00 | 0 0 0.00 |
+blogic.c_NONPUB_Cmd_Unload | | |
+_Harvester 0 | 0 0 0.00 | 0 0 0.00 |
+conquer.c_NONPUB_Keyboard_ | | |
+Process 40 | 0 0 0.00 | 0 0 0.00 |
+cursor.c_NONPUB_Cursor_Mar | | |
+k 0 | 0 0 0.00 | 0 0 0.00 |
+findpath.c_NONPUB_Follow_E | | |
+dge 10 | 1 0 0.05 | 0 0 0.00 |
+findpath.c_NONPUB_Optimize | | |
+_Moves 14 | 0 0 0.00 | 0 0 0.00 |
+icon.c_NONPUB_Cmd_IconSet | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+icon.c_NONPUB_Cmd_Illegal | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+icon.c_NONPUB_Cmd_Loop | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+-------------------------------------------------------------------------------
+
+ Page 11
+ .RTProfiler Performance Report
+
+Program: CONQUER.EXE Date: 01/20/1994
+Phase: 1 Time: 17:04:46
+Total Ticks: 2081
+
+ ---------- Logical --------------- Physical --------
+ | Total Average Pct | Total Average Pct |
+ Total | Clock No. of Total | Clock No. of Total|
+Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time |
+-------------------------------------------------------------------------------
+icon.c_NONPUB_Cmd_Map | | |
+ 130 | 0 0 0.00 | 0 0 0.00 |
+icon.c_NONPUB_Cmd_Normal | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+icon.c_NONPUB_Cmd_Repeat | | |
+ 16 | 0 0 0.00 | 0 0 0.00 |
+icon.c_NONPUB_Cmd_Sound | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+icon.c_NONPUB_Cmd_Stop | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+icon.c_NONPUB_Cmd_Term | | |
+ 1 | 0 0 0.00 | 0 0 0.00 |
+icon.c_NONPUB_Cmd_Timer | | |
+ 129 | 0 0 0.00 | 0 0 0.00 |
+init.c_NONPUB_Init_Globals | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+init.c_NONPUB_Load_A_Brain | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+loadgame.c_NONPUB_BYTE_In | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+loadgame.c_NONPUB_LONG_In | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+loadgame.c_NONPUB_Struct_I | | |
+n 0 | 0 0 0.00 | 0 0 0.00 |
+loadgame.c_NONPUB_Thread_G | | |
+ameize 0 | 0 0 0.00 | 0 0 0.00 |
+loadgame.c_NONPUB_WORD_In | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+map.c_NONPUB_Smooth_Shadow | | |
+ 840 | 0 0 0.00 | 0 0 0.00 |
+physics.c_NONPUB_End_Of_Mo | | |
+ve 17 | 0 0 0.00 | 0 0 0.00 |
+physics.c_NONPUB_Rotation_ | | |
+Physics 318 | 0 0 0.00 | 0 0 0.00 |
+physics.c_NONPUB_Start_Of_ | | |
+Move 78 | 1 0 0.05 | 0 0 0.00 |
+plogic.c_NONPUB_Cmd_Dummy | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+plogic.c_NONPUB_Cmd_Harves | | |
+ter_Check 0 | 0 0 0.00 | 0 0 0.00 |
+plogic.c_NONPUB_Cmd_Sound | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+plogic.c_NONPUB_Cmd_Unit_C | | |
+reate 0 | 0 0 0.00 | 0 0 0.00 |
+savegame.c_NONPUB_BYTE_Out | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+savegame.c_NONPUB_LONG_Out | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+savegame.c_NONPUB_Struct_O | | |
+ut 0 | 0 0 0.00 | 0 0 0.00 |
+-------------------------------------------------------------------------------
+
+ Page 12
+ .RTProfiler Performance Report
+
+Program: CONQUER.EXE Date: 01/20/1994
+Phase: 1 Time: 17:04:46
+Total Ticks: 2081
+
+ ---------- Logical --------------- Physical --------
+ | Total Average Pct | Total Average Pct |
+ Total | Clock No. of Total | Clock No. of Total|
+Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time |
+-------------------------------------------------------------------------------
+savegame.c_NONPUB_Thread_N | | |
+ormalize 0 | 0 0 0.00 | 0 0 0.00 |
+savegame.c_NONPUB_WORD_Out | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+scenario.c_NONPUB_Clear_Sc | | |
+enario 0 | 0 0 0.00 | 0 0 0.00 |
+scenario.c_NONPUB_Do_Brief | | |
+ing 0 | 0 0 0.00 | 0 0 0.00 |
+scenario.c_NONPUB_Typical_ | | |
+Briefing 0 | 0 0 0.00 | 0 0 0.00 |
+soundio.c_NONPUB_DigiCallb | | |
+ack 0 | 0 0 0.00 | 0 0 0.00 |
+soundio.c_NONPUB_File_Call | | |
+back 0 | 0 0 0.00 | 0 0 0.00 |
+strinput.c_NONPUB_Input_Cu | | |
+rsor 0 | 0 0 0.00 | 0 0 0.00 |
+tlogic.c_NONPUB_Cmd_Attack | | |
+_Team_Target 0 | 0 0 0.00 | 0 0 0.00 |
+tlogic.c_NONPUB_Cmd_Change | | |
+_MO 0 | 0 0 0.00 | 0 0 0.00 |
+tlogic.c_NONPUB_Cmd_Displa | | |
+y_Message 0 | 0 0 0.00 | 0 0 0.00 |
+tlogic.c_NONPUB_Cmd_Dummy | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+tlogic.c_NONPUB_Cmd_Find_T | | |
+eam_Target 0 | 0 0 0.00 | 0 0 0.00 |
+tlogic.c_NONPUB_Cmd_Recrui | | |
+t 0 | 0 0 0.00 | 0 0 0.00 |
+tlogic.c_NONPUB_Cmd_Regrou | | |
+p 0 | 0 0 0.00 | 0 0 0.00 |
+tlogic.c_NONPUB_Cmd_Restor | | |
+e_MO 0 | 0 0 0.00 | 0 0 0.00 |
+tlogic.c_NONPUB_Cmd_Team_C | | |
+ount 0 | 0 0 0.00 | 0 0 0.00 |
+tlogic.c_NONPUB_Cmd_Team_M | | |
+in 0 | 0 0 0.00 | 0 0 0.00 |
+tlogic.c_NONPUB_Cmd_Team_S | | |
+pread 0 | 0 0 0.00 | 0 0 0.00 |
+tlogic.c_NONPUB_Cmd_Team_T | | |
+arget 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Abort_ | | |
+Transport 3 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_All_St | | |
+op 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Altern | | |
+ate_Target 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Animat | | |
+e 8 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Call_F | | |
+or_Landing 0 | 0 0 0.00 | 0 0 0.00 |
+-------------------------------------------------------------------------------
+
+ Page 13
+ .RTProfiler Performance Report
+
+Program: CONQUER.EXE Date: 01/20/1994
+Phase: 1 Time: 17:04:46
+Total Ticks: 2081
+
+ ---------- Logical --------------- Physical --------
+ | Total Average Pct | Total Average Pct |
+ Total | Clock No. of Total | Clock No. of Total|
+Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time |
+-------------------------------------------------------------------------------
+ulogic.c_NONPUB_Cmd_Cargo | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Change | | |
+_Facing 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Change | | |
+_Order 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Death_ | | |
+Message 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Deploy | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Dock | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Dummy | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Eject | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Explos | | |
+ion 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Face_M | | |
+ove_Toward 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Face_T | | |
+arCom 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Flash | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Greate | | |
+st_Threat 8 | 1 0 0.05 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Hot_LZ | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Idle | | |
+ 2 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Inq_Un | | |
+it 127 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Is_Clo | | |
+se_To_NavCom 21 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Is_Clo | | |
+se_To_TarCom 96 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Load | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Loaded | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Lock_O | | |
+n_To_Target 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Look | | |
+ 7 | 2 0 0.10 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Mine_S | | |
+pice 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_MultiE | | |
+xplosion 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Noisie | | |
+st_Target 0 | 0 0 0.00 | 0 0 0.00 |
+-------------------------------------------------------------------------------
+
+ Page 14
+ .RTProfiler Performance Report
+
+Program: CONQUER.EXE Date: 01/20/1994
+Phase: 1 Time: 17:04:46
+Total Ticks: 2081
+
+ ---------- Logical --------------- Physical --------
+ | Total Average Pct | Total Average Pct |
+ Total | Clock No. of Total | Clock No. of Total|
+Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time |
+-------------------------------------------------------------------------------
+ulogic.c_NONPUB_Cmd_Reques | | |
+t_Transport 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Rest | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Set_Hu | | |
+lk 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Set_Na | | |
+vCom 87 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Set_Sp | | |
+eed 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Set_Ta | | |
+rCom 10 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Spew_B | | |
+odies 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Target | | |
+_Dir 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Termin | | |
+ate_Self 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Threat | | |
+_Value 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Travel | | |
+_To 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Unload | | |
+ 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Unload | | |
+ed_Struct 0 | 0 0 0.00 | 0 0 0.00 |
+ulogic.c_NONPUB_Cmd_Verify | | |
+_Contact 0 | 0 0 0.00 | 0 0 0.00 |
+
+Total Logical Percentage : 316.67
+Total Physical Percentage : 99.62
diff --git a/CONQUER.TXT b/CONQUER.TXT
new file mode 100644
index 0000000..5a6567d
--- /dev/null
+++ b/CONQUER.TXT
@@ -0,0 +1,1417 @@
+# TXT_NONE
+%3d.%02d
+# TXT_CREDIT_FORMAT
+Upgrade
+# TXT_BUTTON_UPGRADE
+Upgrade Structure
+# TXT_UPGRADE
+Upgrade
+# TXT_UPGRADE_BUTTON
+Sell
+# TXT_BUTTON_SELL
+Sell Structure
+# TXT_SELL
+Demolish Structure
+# TXT_DEMOLISH
+Repair
+# TXT_BUTTON_REPAIR
+Repair Structure
+# TXT_REPAIR
+Repair
+# TXT_REPAIR_BUTTON
+You:
+# TXT_YOU
+Enemy:
+# TXT_ENEMY
+Buildings Destroyed By
+# TXT_BUILD_DEST
+Units Destroyed By
+# TXT_UNIT_DEST
+Tiberium Harvested By
+# TXT_TIB_HARV
+Score: %d
+# TXT_SCORE_1
+You have attained the rank of
+# TXT_RANK_OF
+Yes
+# TXT_YES
+No
+# TXT_NO
+Ready
+# TXT_READY
+Holding
+# TXT_HOLDING
+Accomplished
+# TXT_SCENARIO_WON
+Failed
+# TXT_SCENARIO_LOST
+Choose Your Side
+# TXT_CHOOSE_SIDE
+Start New Game
+# TXT_START_NEW_GAME
+Replay Introduction
+# TXT_INTRO
+Cancel
+# TXT_CANCEL
+Rock
+# TXT_ROCK
+Resume Game
+# TXT_CHOAM_RESUME
+Build This
+# TXT_CHOAM_BUILD_THIS
+Thank you for playing Command & Conquer.\r
+# TXT_THANK_YOU
+Hall of Fame
+# TXT_FAME
+Global Defense Initiative
+# TXT_GDI
+Brotherhood of Nod
+# TXT_NOD
+Civilian
+# TXT_CIVILIAN
+Containment Team
+# TXT_JP
+OK
+# TXT_OK
+Tree
+# TXT_TREE
+\x011
+# TXT_LEFT
+\x010
+# TXT_RIGHT
+\x01E
+# TXT_UP
+\x01F
+# TXT_DOWN
+Clear the map
+# TXT_CLEAR_MAP
+Inherit previous map
+# TXT_INHERIT_MAP
+Clear
+# TXT_CLEAR
+Water
+# TXT_WATER
+Road
+# TXT_ROAD
+Tile Object
+# TXT_TILE
+Slope
+# TXT_SLOPE
+Brush
+# TXT_BRUSH
+Patch
+# TXT_PATCH
+River
+# TXT_RIVER
+Load Mission
+# TXT_LOAD_MISSION
+Save Mission
+# TXT_SAVE_MISSION
+Delete Mission
+# TXT_DELETE_MISSION
+Load
+# TXT_LOAD_BUTTON
+Save
+# TXT_SAVE_BUTTON
+Delete
+# TXT_DELETE_BUTTON
+Game Controls
+# TXT_GAME_CONTROLS
+Sound Controls
+# TXT_SOUND_CONTROLS
+Resume Mission
+# TXT_RESUME_MISSION
+Visual Controls
+# TXT_VISUAL_CONTROLS
+Abort Mission
+# TXT_QUIT_MISSION
+Exit Game
+# TXT_EXIT_GAME
+Options
+# TXT_OPTIONS
+Tiberium
+# TXT_TIBERIUM
+Tiberium On
+# TXT_TIBERIUM_ON
+Tiberium Off
+# TXT_TIBERIUM_OFF
+Squish mark
+# TXT_SQUISH
+Crater
+# TXT_CRATER
+Scorch Mark
+# TXT_SCORCH
+BRIGHTNESS:
+# TXT_BRIGHTNESS
+MUSIC VOLUME
+# TXT_MUSIC
+SOUND VOLUME
+# TXT_VOLUME
+TINT:
+# TXT_TINT
+CONTRAST:
+# TXT_CONTRAST
+GAME SPEED:
+# TXT_SPEED
+SCROLL RATE:
+# TXT_SCROLLRATE
+COLOR:
+# TXT_COLOR
+Return to game
+# TXT_RETURN_TO_GAME
+Enemy Soldier
+# TXT_ENEMY_SOLDIER
+Enemy Vehicle
+# TXT_ENEMY_VEHICLE
+Enemy Structure
+# TXT_ENEMY_STRUCTURE
+Flame Tank
+# TXT_FTANK
+Stealth Tank
+# TXT_STANK
+Light Tank
+# TXT_LTANK
+Med. Tank
+# TXT_MTANK
+Mammoth Tank
+# TXT_HTANK
+Nod Buggy
+# TXT_DUNE_BUGGY
+SAM Site
+# TXT_SAM
+Advanced Com. Center
+# TXT_EYE
+Rocket Launcher
+# TXT_MLRS
+Mobile HQ
+# TXT_MHQ
+Hum-vee
+# TXT_JEEP
+Transport Helicopter
+# TXT_TRANS
+A10
+# TXT_A10
+C17
+# TXT_C17
+Harvester
+# TXT_HARVESTER
+Artillery
+# TXT_ARTY
+S.S.M. Launcher
+# TXT_MSAM
+Minigunner
+# TXT_E1
+Grenadier
+# TXT_E2
+Bazooka
+# TXT_E3
+Flamethrower
+# TXT_E4
+Chem-warrior
+# TXT_E5
+Commando
+#TXT_RAMBO
+Hovercraft
+# TXT_HOVER
+Attack Helicopter
+# TXT_HELI
+Orca
+# TXT_ORCA
+APC
+# TXT_APC
+Guard Tower
+# TXT_GUARD_TOWER
+Communications Center
+# TXT_COMMAND
+Helicopter Pad
+# TXT_HELIPAD
+Airstrip
+# TXT_AIRSTRIP
+Tiberium Silo
+# TXT_STORAGE
+Construction Yard
+# TXT_CONST_YARD
+Tiberium Refinery
+# TXT_REFINERY
+Church
+# TXT_CIV1
+Han's and Gretel's
+# TXT_CIV2
+Hewitt's Manor
+# TXT_CIV3
+Ricktor's House
+# TXT_CIV4
+Gretchin's House
+# TXT_CIV5
+The Barn
+# TXT_CIV6
+Damon's pub
+# TXT_CIV7
+Fran's House
+# TXT_CIV8
+Music Factory
+# TXT_CIV9
+Toymaker's
+# TXT_CIV10
+Ludwig's House
+# TXT_CIV11
+Haystacks
+# TXT_CIV12
+Haystack
+# TXT_CIV13
+Wheat Field
+# TXT_CIV14
+Fallow Field
+# TXT_CIV15
+Corn Field
+# TXT_CIV16
+Celery Field
+# TXT_CIV17
+Potato Field
+# TXT_CIV18
+Sala's House
+# TXT_CIV20
+Abdul's House
+# TXT_CIV21
+Pablo's Wicked Pub
+# TXT_CIV22
+Village Well
+# TXT_CIV23
+Camel Trader
+# TXT_CIV24
+Church
+# TXT_CIV25
+Ali's House
+# TXT_CIV26
+Trader Ted's
+# TXT_CIV27
+Menelik's House
+# TXT_CIV28
+Prestor John's House
+# TXT_CIV29
+Village Well
+# TXT_CIV30
+Witch Doctor's Hut
+# TXT_CIV31
+Rikitikitembo's Hut
+# TXT_CIV32
+Roarke's Hut
+# TXT_CIV33
+Mubasa's Hut
+# TXT_CIV34
+Aksum's Hut
+# TXT_CIV35
+Mambo's Hut
+# TXT_CIV36
+The Studio
+# TXT_CIV37
+Technology Center
+# TXT_CIVMISS
+Gun Turret
+# TXT_TURRET
+Gun Boat
+# TXT_GUNBOAT
+Mobile Construction Yard
+# TXT_MCV
+Recon Bike
+# TXT_BIKE
+Power Plant
+# TXT_POWER
+Advanced Power Plant
+# TXT_ADVANCED_POWER
+Hospital
+# TXT_HOSPITAL
+Barracks
+# TXT_BARRACKS
+Concrete
+# TXT_CONCRETE
+Oil Pump
+# TXT_PUMP
+Oil Tanker
+# TXT_TANKER
+Sandbag Wall
+# TXT_SANDBAG_WALL
+Chain Link Fence
+# TXT_CYCLONE_WALL
+Concrete Wall
+# TXT_BRICK_WALL
+Barbwire Fence
+# TXT_BARBWIRE_WALL
+Wood Fence
+# TXT_WOOD_WALL
+Weapons Factory
+# TXT_WEAPON_FACTORY
+Advanced Guard Tower
+# TXT_AGUARD_TOWER
+Obelisk Guard Tower
+# TXT_OBELISK
+Bio-Research Laboritory
+# TXT_BIO_LAB
+Hand of Nod
+# TXT_HAND
+Temple of Nod
+# TXT_TEMPLE
+Repair Bay
+# TXT_FIX_IT
+Sidebar
+# TXT_TAB_SIDEBAR
+Options
+# TXT_TAB_BUTTON_CONTROLS
+Database
+# TXT_TAB_BUTTON_DATABASE
+Unrevealed Terrain
+# TXT_SHADOW
+Options Menu
+# TXT_OPTIONS_MENU
+STOP
+# TXT_STOP
+PLAY
+# TXT_PLAY
+SHUFFLE
+# TXT_SHUFFLE
+REPEAT
+# TXT_REPEAT
+Music volume:
+# TXT_MUSIC_VOLUME
+Sound volume:
+# TXT_SOUND_VOLUME
+On
+# TXT_ON
+Off
+# TXT_OFF
+Act On Instinct
+# TXT_THEME_AOI
+Looks Like Trouble
+# TXT_THEME_TROUBLE
+Industrial
+# TXT_THEME_IND
+Reaching Out
+# TXT_THEME_ROUT
+On The Prowl
+# TXT_THEME_OTP
+Prepare For Battle
+# TXT_THEME_PRP
+Just Do It!
+# TXT_THEME_JUSTDOIT
+In The Line Of Fire
+# TXT_THEME_LINEFIRE
+March To Doom
+# TXT_THEME_MARCH
+Deception
+# TXT_THEME_STOPTHEM
+C&C Thang
+# TXT_THEME_CCTHANG
+Enemies To Be Feared
+# TXT_THEME_BEFEARED
+Warfare
+# TXT_THEME_WARFARE
+Fight, Win, Prevail
+# TXT_THEME_FWP
+Die!!
+# TXT_THEME_DIE
+No Mercy
+# TXT_THEME_NOMERCY
+Mechanical Man
+# TXT_THEME_TARGET
+I Am
+# TXT_THEME_IAM
+Great Shot!
+# TXT_THEME_WIN1
+Multiplayer Game
+# TXT_MULTIPLAYER_GAME
+No files available
+# TXT_NO_FILES
+Do you want to delete this file?
+# TXT_DELETE_SINGLE_FILE
+Do you want to delete %d files?
+# TXT_DELETE_MULTIPLE_FILES
+Reset Values
+# TXT_RESET_MENU
+Confirmation
+# TXT_CONFIRMATION
+Do you want to abort the mission?
+# TXT_CONFIRM_EXIT
+Mission Description
+# TXT_MISSION_DESCRIPTION
+Joe
+# TXT_C1
+Bill
+# TXT_C2
+Shelly
+# TXT_C3
+Maria
+# TXT_C4
+Eydie
+# TXT_C5
+Dave
+# TXT_C6
+Phil
+# TXT_C7
+Dwight
+# TXT_C8
+Erik
+# TXT_C9
+Dr. Moebius
+# TXT_MOEBIUS
+Road Bib
+# TXT_BIB
+Faster
+# TXT_FASTER
+Slower
+# TXT_SLOWER
+Ion Cannon
+# TXT_ION_CANNON
+Nuclear Strike
+# TXT_NUKE_STRIKE
+Air Strike
+# TXT_AIR_STRIKE
+Tyrannosaurus Rex
+# TXT_TREX
+Triceratops
+# TXT_TRIC
+Velociraptor
+# TXT_RAPT
+Stegasaurus
+# TXT_STEG
+Steel Crate
+# TXT_STEEL_CRATE
+Wood Crate
+# TXT_WOOD_CRATE
+Flag Location
+# TXT_FLAG_SPOT
+GDI
+# TXT_G_D_I
+NOD
+# TXT_N_O_D
+Unable to read scenario!
+# TXT_UNABLE_READ_SCENARIO
+Error loading game!
+# TXT_ERROR_LOADING_GAME
+Obsolete saved game.
+# TXT_OBSOLETE_SAVEGAME
+You must enter a description!
+# TXT_MUSTENTER_DESCRIPTION
+Error saving game!
+# TXT_ERROR_SAVING_GAME
+Delete this file?
+# TXT_DELETE_FILE_QUERY
+[EMPTY SLOT]
+# TXT_EMPTY_SLOT
+Select Multiplayer Game
+# TXT_SELECT_MPLAYER_GAME
+Modem/Serial
+# TXT_MODEM_SERIAL
+Network
+# TXT_NETWORK
+Unable to initialize network!
+# TXT_INIT_NET_ERROR
+Join Network Game
+# TXT_JOIN_NETWORK_GAME
+New
+# TXT_NEW
+Join
+# TXT_JOIN
+Send Message
+# TXT_SEND_MESSAGE
+Your Name:
+# TXT_YOUR_NAME
+Side:
+# TXT_SIDE_COLON
+Color:
+# TXT_COLOR_COLON
+Games
+# TXT_GAMES
+Players
+# TXT_PLAYERS
+Scenario:
+# TXT_SCENARIO_COLON
+>> NOT FOUND <<
+# TXT_NOT_FOUND
+Starting Credits:
+# TXT_START_CREDITS_COLON
+Bases:
+# TXT_BASES_COLON
+Tiberium:
+# TXT_TIBERIUM_COLON
+Crates:
+# TXT_CRATES_COLON
+AI Players:
+# TXT_AI_PLAYERS_COLON
+Request denied.
+# TXT_REQUEST_DENIED
+Unable to play; scenario not found.
+# TXT_UNABLE_PLAY_WAAUGH
+Nothing to join!
+# TXT_NOTHING_TO_JOIN
+You must enter a name!
+# TXT_NAME_ERROR
+Duplicate names are not allowed.
+# TXT_DUPENAMES_NOTALLOWED
+Your game version is outdated.
+# TXT_YOURGAME_OUTDATED
+Destination game version is outdated.
+# TXT_DESTGAME_OUTDATED
+%s's Game
+# TXT_THATGUYS_GAME
+[%s's Game]
+# TXT_THATGUYS_GAME_BRACKET
+Network Game Setup
+# TXT_NETGAME_SETUP
+Reject
+# TXT_REJECT
+You can't reject yourself! You might develop serious self-esteem problems.
+# TXT_CANT_REJECT_SELF
+You must select a player to reject.
+# TXT_SELECT_PLAYER_REJECT
+Bases On
+# TXT_BASES_ON
+Bases Off
+# TXT_BASES_OFF
+Crates On
+# TXT_CRATES_ON
+Crates Off
+# TXT_CRATES_OFF
+AI Players On
+# TXT_AI_PLAYERS_ON
+AI Players Off
+# TXT_AI_PLAYERS_OFF
+Scenarios
+# TXT_SCENARIOS
+Starting Credits
+# TXT_START_CREDITS
+Only one player?
+# TXT_ONLY_ONE
+Oops!
+# TXT_OOPS
+To %s:
+# TXT_TO
+To All:
+# TXT_TO_ALL
+Message:
+# TXT_MESSAGE
+Connection to %s lost!
+# TXT_CONNECTION_LOST
+%s has left the game.
+# TXT_LEFT_GAME
+%s has been defeated!
+# TXT_PLAYER_DEFEATED
+Waiting to Connect...
+# TXT_WAITING_CONNECT
+Connection error!\rCheck your cables.\rAttempting to Reconnect...
+# TXT_NULL_CONNERR_CHECK_CABLES
+Connection error!\rRedialing...
+# TXT_MODEM_CONNERR_REDIALING
+Connection error!\rWaiting for Call...
+# TXT_MODEM_CONNERR_WAITING
+Select Serial Game
+# TXT_SELECT_SERIAL_GAME
+Dial Modem
+# TXT_DIAL_MODEM
+Answer Modem
+# TXT_ANSWER_MODEM
+Null Modem
+# TXT_NULL_MODEM
+Settings
+# TXT_SETTINGS
+Port:
+# TXT_PORT_COLON
+IRQ:
+# TXT_IRQ_COLON
+Baud:
+# TXT_BAUD_COLON
+Init String:
+# TXT_INIT_STRING
+Call Waiting String:
+# TXT_CWAIT_STRING
+Tone Dialing
+# TXT_TONE_BUTTON
+Pulse Dialing
+# TXT_PULSE_BUTTON
+Host Serial Game
+# TXT_HOST_SERIAL_GAME
+Opponent:
+# TXT_OPPONENT_COLON
+User signed off!
+# TXT_USER_SIGNED_OFF
+Join Serial Game
+# TXT_JOIN_SERIAL_GAME
+Phone List
+# TXT_PHONE_LIST
+Add
+# TXT_ADD
+Edit
+# TXT_EDIT
+Dial
+# TXT_DIAL
+Default
+# TXT_DEFAULT
+Default Settings
+# TXT_DEFAULT_SETTINGS
+Custom Settings
+# TXT_CUSTOM_SETTINGS
+Phone Listing
+# TXT_PHONE_LISTING
+Name:
+# TXT_NAME_COLON
+Number:
+# TXT_NUMBER_COLON
+Unable to find modem.\rCheck power and cables.
+# TXT_UNABLE_FIND_MODEM
+No carrier.
+# TXT_NO_CARRIER
+Line busy.
+# TXT_LINE_BUSY
+Number invalid.
+# TXT_NUMBER_INVALID
+Other system not responding!
+# TXT_SYSTEM_NOT_RESPONDING
+Games are out of sync!
+# TXT_OUT_OF_SYNC
+Packet received too late!
+# TXT_PACKET_TOO_LATE
+Other player has left the game.
+# TXT_PLAYER_LEFT_GAME
+From %s:%s
+# TXT_FROM
+2,728,000
+# TXT_MAP_P01
+38,385,000
+# TXT_MAP_P02
+10,373,000
+# TXT_MAP_P03
+51,994,000
+# TXT_MAP_P04
+80,387,000
+# TXT_MAP_P05
+10,400,000
+# TXT_MAP_P06
+5,300,000
+# TXT_MAP_P07
+7,867,000
+# TXT_MAP_P08
+10,333,000
+# TXT_MAP_P09
+1,974,000
+# TXT_MAP_P10
+23,169,000
+# TXT_MAP_P11
+10,064,000
+# TXT_MAP_P12
+3,285,000
+# TXT_MAP_P13
+8,868,000
+# TXT_MAP_P14
+10,337,000
+# TXT_MAP_P15
+4,365,000
+# TXT_MAP_P16
+1,607,000
+# TXT_MAP_P17
+4,485,000
+# TXT_MAP_P18
+56,386,000
+# TXT_MAP_P19
+28,305,000
+# TXT_MAP_P20
+5,238,000
+# TXT_MAP_P21
+2,059,000
+# TXT_MAP_P22
+13,497,000
+# TXT_MAP_P23
+4,997,000
+# TXT_MAP_P24
+88,500,000
+# TXT_MAP_P25
+1,106,000
+# TXT_MAP_P26
+12,658,000
+# TXT_MAP_P27
+3,029,000
+# TXT_MAP_P28
+39,084,000
+# TXT_MAP_P29
+23,154,000
+# TXT_MAP_P30
+8,902,000
+# TXT_MAP_P31
+27,791,000
+# TXT_MAP_P32
+1,574,000
+# TXT_MAP_P33
+15,469,000
+# TXT_MAP_P34
+1,300,000
+# TXT_MAP_P35
+41,688,000
+# TXT_MAP_P36
+24,900 SQ. MI.
+# TXT_MAP_A00
+120,727 SQ. MI.
+# TXT_MAP_A01
+80,134 SQ. MI.
+# TXT_MAP_A02
+233,100 SQ. MI.
+# TXT_MAP_A03
+137,838 SQ. MI.
+# TXT_MAP_A04
+30,449 SQ. MI.
+# TXT_MAP_A05
+18,932 SQ. MI.
+# TXT_MAP_A06
+32,377 SQ. MI.
+# TXT_MAP_A07
+35,919 SQ. MI.
+# TXT_MAP_A08
+7,819 SQ. MI.
+# TXT_MAP_A09
+91,699 SQ. MI.
+# TXT_MAP_A10
+51,146 SQ. MI.
+# TXT_MAP_A11
+11,100 SQ. MI.
+# TXT_MAP_A12
+44,365 SQ. MI.
+# TXT_MAP_A13
+39,449 SQ. MI.
+# TXT_MAP_A14
+19,741 SQ. MI.
+# TXT_MAP_A15
+17,413 SQ. MI.
+# TXT_MAP_A16
+RIGA
+# TXT_MAP_C00
+WARSAW
+# TXT_MAP_C01
+MINSK
+# TXT_MAP_C02
+KIEV
+# TXT_MAP_C03
+BERLIN
+# TXT_MAP_C04
+PRAGUE
+# TXT_MAP_C05
+BRATISLAVA
+# TXT_MAP_C06
+VIENNA
+# TXT_MAP_C07
+BUDAPEST
+# TXT_MAP_C08
+LJUBLJANA
+# TXT_MAP_C09
+BUCHAREST
+# TXT_MAP_C10
+ATHENS
+# TXT_MAP_C11
+TIRANA
+# TXT_MAP_C12
+SOFIA
+# TXT_MAP_C13
+BELGRADE
+# TXT_MAP_C14
+SARAJEVO
+# TXT_MAP_C15
+TALLINN
+# TXT_MAP_C16
+TRIPOLI
+# TXT_MAP_C17
+CAIRO
+# TXT_MAP_C18
+KHARTOUM
+# TXT_MAP_C19
+N'DJAMENA
+# TXT_MAP_C20
+NOUAKCHOTT
+# TXT_MAP_C21
+YAMOUSSOUKRO
+# TXT_MAP_C22
+PORTO-NOVO
+# TXT_MAP_C23
+ABUJA
+# TXT_MAP_C24
+LIBREVILLE
+# TXT_MAP_C25
+YAOUNDE
+# TXT_MAP_C26
+BANGUI
+# TXT_MAP_C27
+KINSHASA
+# TXT_MAP_C28
+CAIRO
+# TXT_MAP_C29
+LUANDA
+# TXT_MAP_C30
+DAR-ES-SALAAM
+# TXT_MAP_C31
+WINDHOEK
+# TXT_MAP_C32
+MAPUTO
+# TXT_MAP_C33
+GABARONE
+# TXT_MAP_C34
+CAPE TOWN
+# TXT_MAP_C35
+NEGLIGIBLE
+# TXT_MAP_GDP00
+$162.7 BLN
+# TXT_MAP_GDP01
+$47.6 BLN
+# TXT_MAP_GDP02
+$1,131 BLN
+# TXT_MAP_GDP03
+$120 BLN
+# TXT_MAP_GDP04
+$164 BLN
+# TXT_MAP_GDP05
+$60.1 BLN
+# TXT_MAP_GDP06
+$21 BLN
+# TXT_MAP_GDP07
+$71.9 BLN
+# TXT_MAP_GDP08
+$77 BLN
+# TXT_MAP_GDP09
+$4.0 BLN
+# TXT_MAP_GDP10
+$47.3 BLN
+# TXT_MAP_GDP11
+$120.1 BLN
+# TXT_MAP_GDP12
+$14.0 BLN
+# TXT_MAP_GDP13
+$28.9 BLN
+# TXT_MAP_GDP14
+$39.2 BLN
+# TXT_MAP_GDP15
+$12.1 BLN
+# TXT_MAP_GDP16
+$1.0 BLN
+# TXT_MAP_GDP17
+$10.0 BLN
+# TXT_MAP_GDP18
+$1.7 BLN
+# TXT_MAP_GDP19
+$28.0 BLN
+# TXT_MAP_GDP20
+$5.3 BLN
+# TXT_MAP_GDP21
+$11.6 BLN
+# TXT_MAP_GDP22
+$1.3 BLN
+# TXT_MAP_GDP23
+$6.6 BLN
+# TXT_MAP_GDP24
+$8.3 BLN
+# TXT_MAP_GDP25
+$6.9 BLN
+# TXT_MAP_GDP26
+$2.0 BLN
+# TXT_MAP_GDP27
+$3.1 BLN
+# TXT_MAP_GDP28
+$104.0 BLN
+# TXT_MAP_GDP29
+JELGAVA
+# TXT_MAP_PC00
+GDANSK
+# TXT_MAP_PC01
+BYELISTOK
+# TXT_MAP_PC02
+BOBYRUSK
+# TXT_MAP_PC03
+IVANO-FRANKOVSK
+# TXT_MAP_PC04
+HANOVER
+# TXT_MAP_PC05
+DRESDEN
+# TXT_MAP_PC06
+OSTRAVA
+# TXT_MAP_PC07
+BRATISLAVA
+# TXT_MAP_PC08
+SALZBURG
+# TXT_MAP_PC09
+BUDAPEST
+# TXT_MAP_PC10
+TRIESTE
+# TXT_MAP_PC11
+ARAD
+# TXT_MAP_PC12
+CORINTH
+# TXT_MAP_PC13
+SHKODER
+# TXT_MAP_PC14
+SOFIA
+# TXT_MAP_PC15
+NIS
+# TXT_MAP_PC16
+BELGRADE
+# TXT_MAP_PC17
+?
+# TXT_MAP_PC18
+PARNU
+# TXT_MAP_PC19
+TMASSAH
+# TXT_MAP_PC20
+AL-ALAMYN
+# TXT_MAP_PC21
+AL-KHARIJAH
+# TXT_MAP_PC22
+AL-UBAYYID
+# TXT_MAP_PC23
+KAFIA-KINGI
+# TXT_MAP_PC24
+OUM HADJER
+# TXT_MAP_PC25
+MAO
+# TXT_MAP_PC26
+TIDJIKDJA
+# TXT_MAP_PC27
+ABIDJAN
+# TXT_MAP_PC28
+PORTO-NOVO
+# TXT_MAP_PC29
+ABUJA
+# TXT_MAP_PC30
+KOULA-MOUTOU
+# TXT_MAP_PC31
+BERTOUA
+# TXT_MAP_PC32
+BANGASSOU
+# TXT_MAP_PC33
+LODJA
+# TXT_MAP_PC34
+KINSHASA
+# TXT_MAP_PC35
+LUXOR
+# TXT_MAP_PC36
+CAIUNDO
+# TXT_MAP_PC37
+MZUZU
+# TXT_MAP_PC38
+KEETMANSHOOP
+# TXT_MAP_PC39
+XAI-XAI
+# TXT_MAP_PC40
+GHANZI
+# TXT_MAP_PC41
+CAPE TOWN
+# TXT_MAP_PC42
+GDI PROGRESSION
+# TXT_MAP_GDI
+NOD PROGRESSION
+# TXT_MAP_NOD
+LOCATING COORDINATES
+# TXT_MAP_LOCATE
+OF NEXT MISSION
+# TXT_MAP_NEXT_MISSION
+SELECT TERRITORY
+# TXT_MAP_SELECT
+TO ATTACK
+# TXT_MAP_TO_ATTACK
+POPULATION:
+# TXT_MAP_GDISTAT0
+GEOGRAPHIC AREA:
+# TXT_MAP_GDISTAT1
+CAPITAL:
+# TXT_MAP_GDISTAT2
+GOVERNMENT:
+# TXT_MAP_GDISTAT3
+GROSS DOMESTIC PRODUCT:
+# TXT_MAP_GDISTAT4
+POINT OF CONFLICT:
+# TXT_MAP_GDISTAT5
+MILITARY POWER:
+# TXT_MAP_GDISTAT6
+EXPENDABILITY:
+# TXT_MAP_NODSTAT0
+GOVT CORRUPTABILITY:
+# TXT_MAP_NODSTAT1
+NET WORTH:
+# TXT_MAP_NODSTAT2
+MILITARY STRENGTH:
+# TXT_MAP_NODSTAT3
+MILITARY RESISTANCE:
+# TXT_MAP_NODSTAT4
+LATVIA
+# TXT_MAP_COUNTRYNAME0
+POLAND
+# TXT_MAP_COUNTRYNAME1
+BELARUS
+# TXT_MAP_COUNTRYNAME2
+UKRAINE
+# TXT_MAP_COUNTRYNAME3
+GERMANY
+# TXT_MAP_COUNTRYNAME4
+CZECH REPUBLIC
+# TXT_MAP_COUNTRYNAME5
+SLOVAKIA
+# TXT_MAP_COUNTRYNAME6
+AUSTRIA
+# TXT_MAP_COUNTRYNAME7
+HUNGARY
+# TXT_MAP_COUNTRYNAME8
+SLOVENIA
+# TXT_MAP_COUNTRYNAME9
+ROMANIA
+# TXT_MAP_COUNTRYNAME10
+GREECE
+# TXT_MAP_COUNTRYNAME11
+ALBANIA
+# TXT_MAP_COUNTRYNAME12
+BULGARIA
+# TXT_MAP_COUNTRYNAME13
+YUGOSLAVIA
+# TXT_MAP_COUNTRYNAME14
+BOSNIA/HERZOGOVINA
+# TXT_MAP_COUNTRYNAME15
+LIBYA
+# TXT_MAP_COUNTRYNAME16
+EGYPT
+# TXT_MAP_COUNTRYNAME17
+SUDAN
+# TXT_MAP_COUNTRYNAME18
+CHAD
+# TXT_MAP_COUNTRYNAME19
+MAURITANIA
+# TXT_MAP_COUNTRYNAME20
+IVORY COAST
+# TXT_MAP_COUNTRYNAME21
+BENIN
+# TXT_MAP_COUNTRYNAME22
+NIGERIA
+# TXT_MAP_COUNTRYNAME23
+GABON
+# TXT_MAP_COUNTRYNAME24
+CAMEROON
+# TXT_MAP_COUNTRYNAME25
+CENTRAL AFRICAN REPUBLIC
+# TXT_MAP_COUNTRYNAME26
+ZAIRE
+# TXT_MAP_COUNTRYNAME27
+ANGOLA
+# TXT_MAP_COUNTRYNAME28
+TANZANIA
+# TXT_MAP_COUNTRYNAME29
+NAMIBIA
+# TXT_MAP_COUNTRYNAME30
+MOZAMBIQUE
+# TXT_MAP_COUNTRYNAME31
+BOTSWANA
+# TXT_MAP_COUNTRYNAME32
+SOUTH AFRICA
+# TXT_MAP_COUNTRYNAME33
+ESTONIA
+# TXT_MAP_COUNTRYNAME34
+REPUBLIC
+# TXT_MAP_GOVT0
+DEMOCRATIC STATE
+# TXT_MAP_GOVT1
+FEDERAL REPUBLIC
+# TXT_MAP_GOVT2
+CONST. REPUBLIC
+# TXT_MAP_GOVT3
+PARL. DEMOCRACY
+# TXT_MAP_GOVT4
+PRES. PARL. REPUBLIC
+# TXT_MAP_GOVT5
+DEMOCRACY
+# TXT_MAP_GOVT6
+IN TRANSITION
+# TXT_MAP_GOVT7
+ISLAMIC SOCIALIST
+# TXT_MAP_GOVT8
+MILITARY
+# TXT_MAP_GOVT9
+ISLAMIC REPUBLIC
+# TXT_MAP_GOVT10
+PARL. REPUBLIC
+# TXT_MAP_GOVT11
+LOCAL MILITIA
+# TXT_MAP_ARMY0
+STATE MILITIA
+# TXT_MAP_ARMY1
+NATIONAL GUARD
+# TXT_MAP_ARMY2
+FREE STANDING ARMY
+# TXT_MAP_ARMY3
+?
+# TXT_MAP_ARMY4
+NATIONAL POWER
+# TXT_MAP_ARMY5
+RESPECTABLE
+# TXT_MAP_MILITARY0
+FORMIDABLE
+# TXT_MAP_MILITARY1
+LAUGHABLE
+# TXT_MAP_MILITARY2
+REASONABLE
+# TXT_MAP_MILITARY3
+INSIGNIFICANT
+# TXT_MAP_MILITARY4
+CLICK TO CONTINUE
+# TXT_MAP_CLICK2
+LOW
+# TXT_MAP_LMH0
+MEDIUM
+# TXT_MAP_LMH1
+HIGH
+# TXT_MAP_LMH2
+TIME:
+# TXT_SCORE_TIME
+LEADERSHIP:
+# TXT_SCORE_LEAD
+EFFICIENCY:
+# TXT_SCORE_EFFI
+TOTAL SCORE:
+# TXT_SCORE_TOTA
+CASUALTIES:
+# TXT_SCORE_CASU
+NEUTRAL:
+# TXT_SCORE_NEUT
+GDI:
+# TXT_SCORE_GDI
+BUILDINGS LOST
+# TXT_SCORE_BUIL
+BUILDINGS
+# TXT_SCORE_BUIL1
+LOST:
+# TXT_SCORE_BUIL2
+TOP SCORES
+# TXT_SCORE_TOP
+ENDING CREDITS:
+# TXT_SCORE_ENDCRED
+%dh %dm
+# TXT_SCORE_TIMEFORMAT1
+%dm
+# TXT_SCORE_TIMEFORMAT2
+NOD:
+# TXT_SCORE_NOD
+Dialing...
+# TXT_DIALING
+Dialing Canceled
+# TXT_DIALING_CANCELED
+Waiting for Call...
+# TXT_WAITING_FOR_CALL
+Answering Canceled
+# TXT_ANSWERING_CANCELED
+Engineer
+# TXT_E7
+Special Options
+# TXT_SPECIAL_OPTIONS
+Targeting flash visible to all.
+# TXT_VISIBLE_TARGET
+Allow targeting of trees.
+# TXT_TREE_TARGET
+Allow undeploy of construction yard.
+# TXT_MCV_DEPLOY
+Employ smarter self defense logic.
+# TXT_SMART_DEFENCE
+Moderate production speed.
+# TXT_SLOW_BUILD
+Use three point turn logic.
+# TXT_THREE_POINT
+Tiberium will grow.
+# TXT_TIBERIUM_GROWTH
+Tiberium will spread.
+# TXT_TIBERIUM_SPREAD
+Disable building "bib" pieces.
+# TXT_ROAD_PIECES
+Allow running from immediate threats.
+# TXT_SCATTER
+Not a Null Modem Cable Attached!\rIt is a modem or loopback cable.
+# TXT_MODEM_OR_LOOPBACK
+Map
+# TXT_MAP
+From Computer:
+# TXT_FROM_COMPUTER
+Prepare to die!
+# TXT_COMP_MSG1
+How about a bullet sandwich?!
+# TXT_COMP_MSG2
+Incoming!
+# TXT_COMP_MSG3
+I see you!
+# TXT_COMP_MSG4
+Hey, I'm over here!
+# TXT_COMP_MSG5
+Come get some!
+# TXT_COMP_MSG6
+I got you!
+# TXT_COMP_MSG7
+You humans are never a challenge!
+# TXT_COMP_MSG8
+Abort, Retry, Ignore? (Ha ha!)
+# TXT_COMP_MSG9
+Format another? (Just kidding!)
+# TXT_COMP_MSG10
+Beat me and I'll reboot!
+# TXT_COMP_MSG11
+You're artificial intelligence!
+# TXT_COMP_MSG12
+My AI is better than your AI.
+# TXT_COMP_MSG13
+Air Strike
+# TXT_THEME_AIRSTRIKE
+Demolition
+# TXT_THEME_HEAVYG
+Untamed Land
+# TXT_THEME_J1
+Take 'em Out
+# TXT_THEME_JDI_V2
+Radio
+# TXT_THEME_RADIO
+Rain In The Night
+# TXT_THEME_RAIN
+Canyon Chase
+# TXT_THEME_IND2
+Heartbreak
+# TXT_THEME_HEART
+Blossom Tree
+# TXT_BLOSSOM_TREE
+Restate
+# TXT_RESTATE_MISSION
+Computer
+# TXT_COMPUTER
+Unit Count:
+# TXT_COUNT
+Tech Level:
+# TXT_LEVEL
+Opponent
+# TXT_OPPONENT
+Kills:
+# TXT_KILLS_COLON
+Video
+# TXT_VIDEO
+Nikoomba
+# TXT_C10
+Capture The Flag
+# TXT_CAPTURE_THE_FLAG
+Ride of the Valkyries
+# TXT_THEME_VALK
+Mission Objective
+# TXT_OBJECTIVE
+Mission
+# TXT_MISSION
+No saved games available.
+# TXT_NO_SAVES
+Civilian Building
+# TXT_CIVILIAN_BUILDING
+Technician
+# TXT_TECHNICIAN
+Visceroid
+# TXT_VISCEROID
+Save game options are not allowed during a multiplayer session.
+# TXT_NO_SAVELOAD
+Defender has the advantage.
+# TXT_DEFENDER_ADVANTAGE
+Show true object names.
+# TXT_SHOW_NAMES
+Agent Delphi
+# TXT_DELPHI
+Would you like to replay this mission?
+# TXT_TO_REPLAY
+Reconnecting to %s.
+# TXT_RECONN_TO
+Please wait %02d seconds.
+# TXT_PLEASE_WAIT
+Do you wish\rto surrender?
+# TXT_SURRENDER
+GLOBAL DEFENSE INITIATIVE
+# TXT_GDI_NAME
+BROTHERHOOD OF NOD
+# TXT_NOD_NAME
+SELECT TRANSMISSION
+# TXT_SEL_TRANS
+Your game name must be unique.
+# TXT_GAMENAME_MUSTBE_UNIQUE
+Game is closed.
+# TXT_GAME_IS_CLOSED
+Your name must be unique.
+# TXT_NAME_MUSTBE_UNIQUE
+Reconnecting to %s
+# TXT_RECONNECTING_TO
+Waiting for connections...
+# TXT_WAITING_FOR_CONNECTIONS
+Time allowed: %02d seconds
+# TXT_TIME_ALLOWED
+Press ESC to cancel.
+# TXT_PRESS_ESC
+From Computer: It's just you and me now!
+# TXT_JUST_YOU_AND_ME
+Capture the Flag:
+# TXT_CAPTURE_THE_FLAG_COLON
+Dr. Chan
+# TXT_CHAN
+%s has allied with %s
+# TXT_HAS_ALLIED
+%s declares war on %s
+# TXT_AT_WAR
+Select a target
+# TXT_SEL_TARGET
+Allow separate helipad purchase
+# TXT_SEPARATE_HELIPAD
+Resign Game
+# TXT_RESIGN
+Tiberium grows quickly.
+# TXT_TIBERIUM_FAST
+Answering...
+# TXT_ANSWERING
+Initializing Modem...
+# TXT_INITIALIZING_MODEM
+Scenarios don't match.
+# TXT_SCENARIOS_DO_NOT_MATCH
+Power Output
+# TXT_POWER_OUTPUT
+Power Output (low)
+# TXT_POWER_OUTPUT_LOW
+Continue
+# TXT_CONTINUE
+Data Queue Overflow
+# TXT_QUEUE_FULL
+%s changed game options!
+# TXT_SPECIAL_WARNING
+Please insert a Command & Conquer CD into the CD-ROM drive.
+# TXT_CD_DIALOG_1
+Please insert CD %d (%s) into the CD-ROM drive.
+# TXT_CD_DIALOG_2
+Command & Conquer is unable to detect your CD ROM drive.
+# TXT_CD_ERROR1
+No Sound Card Detected
+# TXT_NO_SOUND_CARD
+UNKNOWN
+# TXT_UNKNOWN
+(old)
+# TXT_OLD_GAME
+Insufficient Disk Space to run Command & Conquer.
+# TXT_NO_SPACE
+You must have %d megabytes of free disk space.
+# TXT_MUST_HAVE_SPACE
+Run SETUP program first.
+# TXT_RUN_SETUP
+Waiting for Opponent
+# TXT_WAITING_FOR_OPPONENT
+Please select 'Settings' to setup default configuration
+# TXT_SELECT_SETTINGS
+Prison
+# TXT_PRISON
+Game Saved
+# TXT_GAME_WAS_SAVED
+Insufficient disk space to save a game. Please delete a previous save to free up some disk space and try again.
+# TXT_SPACE_CANT_SAVE
+Invalid Port/Address.\rCOM 1-4 OR ADDRESS
+# TXT_INVALID_PORT_ADDRESS
+Invalid Port and/or IRQ settings
+# TXT_INVALID_SETTINGS
+IRQ already in use
+# TXT_IRQ_ALREADY_IN_USE
+Abort
+# TXT_ABORT
+Restart
+# TXT_RESTART
+Mission is restarting.\rPlease wait...
+# TXT_RESTARTING
+Mission is loading.\rPlease wait...
+# TXT_LOADING
+Error in the InitString
+# TXT_ERROR_IN_INITSTRING
diff --git a/CONST.CPP b/CONST.CPP
new file mode 100644
index 0000000..654e761
--- /dev/null
+++ b/CONST.CPP
@@ -0,0 +1,425 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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
+};
+
+
+/***************************************************************************
+** 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,{0xFF, 0x80, 0x90, 0x40, 0x40}}, // WARHEAD_SA Small arms -- good against infantry.
+ { 6,true,true,true,{0xE0, 0xC0, 0x90, 0x40, 0xFF}}, // WARHEAD_HE High explosive -- good against buildings & infantry.
+ { 6,true,true,false,{0x40, 0xC0, 0xC0, 0xFF, 0x80}}, // WARHEAD_AP Armor piercing -- good against armor.
+ { 8,false,true,true,{0xE0, 0xFF, 0xB0, 0x40, 0x80}}, // WARHEAD_FIRE Incendiary -- Good against flammables.
+ { 4,false,false,false,{0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, // WARHEAD_LASER Light Amplification of Stimulated Emission by Radiation.
+ { 7,true,true,true,{0xFF, 0xFF, 0xC0, 0xC0, 0xC0}}, // WARHEAD_PB Particle beam (neutron beam).
+ { 4,false,false,false,{0xFF, 0x20, 0x20, 0x10, 0x10}}, // WARHEAD_FIST Punching in hand-to-hand combat.
+ { 4,false,false,false,{0xFF, 0x20, 0x20, 0x10, 0x10}}, // WARHEAD_FOOT Kicking in hand-to-hand combat.
+ { 4,false,false,false,{0xFF, 0x08, 0x08, 0x08, 0x08}}, // WARHEAD_HOLLOW_POINT Sniper bullet type.
+ {255,false,false,false,{0xFF, 0x01, 0x01, 0x01, 0x01}}, // WARHEAD_SPORE
+ { 1, true,true,false,{0xFF, 0xC0, 0x80, 0x20, 0x08}}, // WARHEAD_HEADBUTT
+ { 1, true,true,false,{0xFF, 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 RemapYellow[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 RemapBlueGreen[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 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
+ 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
+};
diff --git a/CONTROL.CPP b/CONTROL.CPP
new file mode 100644
index 0000000..f5e8468
--- /dev/null
+++ b/CONTROL.CPP
@@ -0,0 +1,202 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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/CONTROL.H b/CONTROL.H
new file mode 100644
index 0000000..174e734
--- /dev/null
+++ b/CONTROL.H
@@ -0,0 +1,89 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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/COORD.CPP b/COORD.CPP
new file mode 100644
index 0000000..369b276
--- /dev/null
+++ b/COORD.CPP
@@ -0,0 +1,545 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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);
+}
+
+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" \
+
+void Move_Point(short &x, short &y, register DirType dir, unsigned short distance)
+{
+// static char const CosTable[256] = {
+ 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] = {
+ 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.
+
+#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
+ x += calcx(CosTable[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
+ y += calcy(SinTable[dir], distance);
+#endif
+// asm add [word ptr start+2],ax
+
+}
diff --git a/COORDA.ASM b/COORDA.ASM
new file mode 100644
index 0000000..3d31d1e
--- /dev/null
+++ b/COORDA.ASM
@@ -0,0 +1,134 @@
+;
+; Command & Conquer(tm)
+; Copyright 2025 Electronic Arts Inc.
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation, either version 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 .
+;
+
+;***************************************************************************
+;** C O N 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/CREDITS.CPP b/CREDITS.CPP
new file mode 100644
index 0000000..774afc2
--- /dev/null
+++ b/CREDITS.CPP
@@ -0,0 +1,176 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 03/13/1995 JLB : Created. *
+ *=============================================================================================*/
+void CreditClass::AI(bool forced)
+{
+ Credits = PlayerPtr->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;
+ Map.Flag_To_Redraw(false);
+}
diff --git a/CREDITS.H b/CREDITS.H
new file mode 100644
index 0000000..b08c46c
--- /dev/null
+++ b/CREDITS.H
@@ -0,0 +1,72 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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
+
+/****************************************************************************
+** 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);
+
+ 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/CREW.CPP b/CREW.CPP
new file mode 100644
index 0000000..18bf6fc
--- /dev/null
+++ b/CREW.CPP
@@ -0,0 +1,38 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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/CREW.H b/CREW.H
new file mode 100644
index 0000000..0b9bac8
--- /dev/null
+++ b/CREW.H
@@ -0,0 +1,65 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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/CWSTUB.C b/CWSTUB.C
new file mode 100644
index 0000000..0834d8d
--- /dev/null
+++ b/CWSTUB.C
@@ -0,0 +1,71 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+#include
+#include
+#include
+#include
+#include
+
+char *dos4g_path()
+{
+ static char *paths_to_check[] = {
+ "DOS4GPATH",
+ "PATH"
+ };
+ static char fullpath[80];
+ char *dos4gpath;
+ int i;
+
+ /* If DOS4GPATH points to an executable file name, don't bother
+ searching any paths for DOS4GW.EXE.
+ */
+ if (dos4gpath = getenv("DOS4GPATH")) {
+ strlwr(strcpy(fullpath, dos4gpath));
+ if (strstr(fullpath, ".exe")) {
+ return(fullpath);
+ }
+ }
+ for( i = 0; i < sizeof(paths_to_check) / sizeof(paths_to_check[0]); i++ ) {
+ _searchenv("dos4gw.exe", paths_to_check[i], fullpath);
+ if (fullpath[0]) {
+ return( &fullpath );
+ }
+ }
+ return("dos4gw.exe");
+}
+
+main( int argc, char *argv[] )
+{
+ char *av[4];
+ auto char cmdline[128];
+
+ av[0] = dos4g_path(); /* Locate the DOS/4GW loader */
+ av[1] = argv[0]; /* name of executable to run */
+ av[2] = getcmd(cmdline); /* command line */
+ av[3] = NULL; /* end of list */
+#ifdef VMM
+ putenv("DOS4GVM=MINMEM#2000 MAXMEM#16000 SWAPMIN#4096 SWAPINC#1024 VIRTUALSIZE#10000 SWAPFILE#CONQUER.SWP DELETESWAP @CONQUER.VMC");
+#endif
+#ifdef QUIET
+ putenv("DOS4G=QUIET"); /* disables DOS/4GW Copyright banner */
+#endif
+ execvp(av[0], av);
+ perror(av[0]);
+ exit(1); /* indicate error */
+}
diff --git a/DDE.CPP b/DDE.CPP
new file mode 100644
index 0000000..5fbdebe
--- /dev/null
+++ b/DDE.CPP
@@ -0,0 +1,450 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/***************************************************************************
+ ** C O N F I 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 *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#define WIN32
+#include
+#include "dde.h"
+
+/***************************************************************************
+ * These are static members of Instance_Class
+ *=========================================================================*/
+
+static DWORD Instance_Class::id_inst; // instance identifier set by DdeInitialize
+static BOOL Instance_Class::process_pokes; // controls response to pokes
+static char Instance_Class::ascii_name[32]; // name of server
+
+static BOOL CALLBACK (*Instance_Class::callback) (
+ LPBYTE pointer, // pointer to received data
+ long length // length of received data or advisory flag
+ ) = NULL;
+
+/***************************************************************************
+ * 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 )
+{
+
+ 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
+
+}
+
+/***************************************************************************
+ * 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. *
+ *=========================================================================*/
+
+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 );
+ }
+}
+
+/***************************************************************************
+ * 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
+ )
+{
+
+
+ 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;
+ }
+}
diff --git a/DDE.H b/DDE.H
new file mode 100644
index 0000000..88d2e24
--- /dev/null
+++ b/DDE.H
@@ -0,0 +1,175 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/***************************************************************************
+ ** C O N F I 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/DEBUG.CPP b/DEBUG.CPP
new file mode 100644
index 0000000..6515925
--- /dev/null
+++ b/DEBUG.CPP
@@ -0,0 +1,690 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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:
+ /*
+ ** 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();
+ }
+ 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/DEBUG.H b/DEBUG.H
new file mode 100644
index 0000000..3d379b7
--- /dev/null
+++ b/DEBUG.H
@@ -0,0 +1,42 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+#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/DEFINES.H b/DEFINES.H
new file mode 100644
index 0000000..6c725be
--- /dev/null
+++ b/DEFINES.H
@@ -0,0 +1,2771 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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
+
+
+/**********************************************************************
+** 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
+
+
+/**********************************************************************
+** 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
+
+
+/**********************************************************************
+** These enumerations are used to implement RTTI.
+*/
+typedef enum RTTIType {
+ 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;
+
+
+/**********************************************************************
+** 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 40000L
+
+// 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<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_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 {
+ 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 {
+ 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{
+ 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
+{
+ 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 {
+ 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
+{
+ 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
+{
+ 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
+{
+ 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 {
+ 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 {
+ 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
+
+ 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 {
+ 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 {
+ 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{
+ 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{
+ 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
+
+ UNIT_COUNT,
+ UNIT_FIRST=0
+} UnitType;
+
+inline UnitType operator++(UnitType &, int);
+
+#define UNITF_HTANK (1L<id)
+
+
+#endif
diff --git a/DESCDLG.CPP b/DESCDLG.CPP
new file mode 100644
index 0000000..7a08ef5
--- /dev/null
+++ b/DESCDLG.CPP
@@ -0,0 +1,184 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $Header: F:\projects\c&c\vcs\code\descdlg.cpv 2.17 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 : 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_6PT_GRAD,
+ 0,
+ BUTTON_Y);
+
+ TextButtonClass cancelbtn(
+ BUTTON_CANCEL,
+ TXT_CANCEL,
+ TPF_6PT_GRAD,
+ 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, SeenBuff.Get_Width(), SeenBuff.Get_Height(), GadgetClass::LEFTPRESS|GadgetClass::RIGHTPRESS);
+ optionsbtn.Add_Tail(background);
+
+ /*
+ ------------------- 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;
+ }
+
+ /*
+ -------------- Invoke game callback -------------
+ */
+ Call_Back();
+
+ /*
+ -------------- Refresh display if needed --------------
+ */
+ if (display) {
+
+ Window_Hide_Mouse(WINDOW_EDITOR);
+
+ /*
+ --------- Draw the background -----------
+ */
+ Window_Box (WINDOW_EDITOR, BOXSTYLE_GREEN_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 BUTTON_OPTIONS|KN_BUTTON:
+ strtrim(string);
+ process = false;
+ break;
+
+ case KN_ESC:
+ case BUTTON_CANCEL|KN_BUTTON:
+ string[0]= NULL;
+ strtrim(string);
+ process = false;
+ break;
+
+ case BUTTON_EDIT|KN_BUTTON:
+ break;
+ }
+ }
+}
+
diff --git a/DESCDLG.H b/DESCDLG.H
new file mode 100644
index 0000000..985505b
--- /dev/null
+++ b/DESCDLG.H
@@ -0,0 +1,69 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $Header: F:\projects\c&c\vcs\code\descdlg.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 : 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/DIAL8.CPP b/DIAL8.CPP
new file mode 100644
index 0000000..ea068ad
--- /dev/null
+++ b/DIAL8.CPP
@@ -0,0 +1,317 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $Header: F:\projects\c&c\vcs\code\dial8.cpv 2.18 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 : DIAL8.CPP *
+ * *
+ * Programmer : Bill Randolph *
+ * *
+ * Start Date : February 6, 1995 *
+ * *
+ * Last Update : February 6, 1995 [BR] *
+ * *
+ *-------------------------------------------------------------------------*
+ * 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 substitues
+ ** '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)
+{
+ /*
+ ** 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_GREEN_DOWN, true);
+ for (int i=0; i<8; i++) {
+ Draw_Box(FacePoint[i][0] - 1, FacePoint[i][1] -1, 3, 3, BOXSTYLE_GREEN_RAISED, false);
+ }
+
+ /*
+ ** Draw the hand & its shadow.
+ */
+ LogicPage->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/DIAL8.H b/DIAL8.H
new file mode 100644
index 0000000..b9b5d43
--- /dev/null
+++ b/DIAL8.H
@@ -0,0 +1,76 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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/DIALOG.CPP b/DIALOG.CPP
new file mode 100644
index 0000000..da9e371
--- /dev/null
+++ b/DIALOG.CPP
@@ -0,0 +1,790 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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>1;
+ break;
+
+ case TPF_RIGHT:
+ x -= String_Pixel_Width(tempstr);
+ break;
+
+ default:
+ break;
+ }
+
+ if ((unsigned)x < SeenBuff.Get_Width() && (unsigned)y < 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 > *tabs) {
+ tabs++;
+ }
+ offset = *tabs;
+ } else {
+ offset = ((offset+1 / 50) + 1) * 50;
+ }
+ source = ptr+1;
+ } else {
+ break;
+ }
+ }
+ }
+}
diff --git a/DISPLAY.CPP b/DISPLAY.CPP
new file mode 100644
index 0000000..a531ef4
--- /dev/null
+++ b/DISPLAY.CPP
@@ -0,0 +1,3753 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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;
+
+/*
+** 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;
+
+/***********************************************************************************************
+ * 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 (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);
+}
+
+
+/***********************************************************************************************
+ * 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)
+{
+ 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);
+}
+
+
+/***********************************************************************************************
+ * 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);
+
+ if ((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::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);
+
+ /*
+ ** 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. *
+ *=============================================================================================*/
+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);
+}
+
+
+/***********************************************************************************************
+ * 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. *
+ *=============================================================================================*/
+bool DisplayClass::Map_Cell(CELL cell, HouseClass * house)
+{
+ if (house != PlayerPtr || cell >= (CELL)Size) return(false);
+
+ /*
+ ** Don't bother remapping this cell if it is already mapped.
+ */
+ if ((*this)[cell].IsMapped) {
+ return(false);
+ }
+
+ /*
+ ** Mark the cell as being mapped.
+ */
+ (*this)[cell].IsMapped = true;
+ (*this)[cell].IsVisible = true;
+ (*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.
+ */
+ for (FacingType dir = FACING_FIRST; dir < FACING_COUNT; dir++) {
+ int shadow;
+ CELL c;
+
+ c = Adjacent_Cell(cell, dir);
+
+ if (c != cell && !(*this)[c].IsMapped) {
+ shadow = Cell_Shadow(c);
+
+ /*
+ ** 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);
+ } else {
+ if (shadow != -2) {
+ (*this)[c].IsVisible = true;
+ (*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)
+{
+ 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);
+}
+
+
+/***********************************************************************************************
+ * 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) {
+ for (CELL 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->IsVisible || Debug_Unshroud) {
+ 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->IsMapped && !Debug_Unshroud) {
+ 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->IsMapped) {
+ if (cellptr->IsVisible) {
+ int shadow = Cell_Shadow(cell);
+ 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->IsMapped) {
+ if (!cellptr->IsVisible) {
+ 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 < 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)->IsDiscoveredByPlayer && 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)->IsDiscoveredByPlayer && 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.
+ */
+ if ((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. *
+ * *
+ * 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 < 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. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 01/19/1995 JLB : Created. *
+ * 04/25/1995 JLB : Limited to non-building type. *
+ *=============================================================================================*/
+void DisplayClass::Select_These(COORDINATE coord1, COORDINATE coord2)
+{
+ 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.
+ */
+ 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->Owner() == PlayerPtr->Class->House &&
+ obj->Class_Of().IsSelectable &&
+ obj->What_Am_I() != RTTI_BUILDING &&
+ x >= x1 && x <= x2 && y >= y1 && y <= y2) {
+
+ if (obj->Select()) {
+ AllowVoice = false;
+ }
+ }
+ }
+ 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].IsVisible && !Debug_Unshroud);
+ 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 = CurrentObject[0]->What_Action(object);
+ } else {
+ action = CurrentObject[0]->What_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::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() && CurrentObject[0]->What_Am_I() == RTTI_AIRCRAFT) {
+ Set_Default_Mouse(MOUSE_NO_MOVE, 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;
+
+ default:
+ Set_Default_Mouse(MOUSE_NORMAL, wwsmall);
+ break;
+ }
+ }
+
+ /*
+ ** 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);
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * 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() || CurrentObject[0]->Owner() != PlayerPtr->Class->House) {
+ action = ACTION_SELECT;
+ } else {
+ if (object->IsSelected) {
+ object->Unselect();
+ } else {
+ object->Select();
+ }
+ }
+ }
+
+ /*
+ ** Selection of other object action.
+ */
+ if (action == ACTION_SELECT || (action == ACTION_NONE && object && object->Class_Of().IsSelectable && !object->IsSelected)) {
+ Unselect_All();
+ object->Select();
+ 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) {
+
+ /*
+ ** 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);
+ }
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * 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 = Coord_X(coord) - Cell_To_Lepton(MapCellX);
+ int yy = Coord_Y(coord) - Cell_To_Lepton(MapCellY);
+
+ 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: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 02/28/1995 JLB : Commented. *
+ * 06/26/1995 JLB : Fixed building loop. *
+ *=============================================================================================*/
+void DisplayClass::Compute_Start_Pos(void)
+{
+ /*
+ ** 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.
+ */
+ long x = 0;
+ long y = 0;
+ long num = 0;
+ for (int 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;
+ }
+
+ /*
+ ** Since the TacticalCell (starting cell) represents the screen's upper-left
+ ** corner, adjust for that.
+ */
+ x -= (Lepton_To_Cell(TacLeptonWidth) / 2);
+ y -= (Lepton_To_Cell(TacLeptonHeight) / 2);
+
+ /*
+ ** Clip the computed x,y cell coords to the map's size.
+ */
+ if (x < MapCellX) {
+ x = MapCellX;
+ }
+ if (x + Lepton_To_Cell(TacLeptonWidth) > MapCellX + MapCellWidth) {
+ x = MapCellX + MapCellWidth - Lepton_To_Cell(TacLeptonWidth);
+ }
+
+ if (y < MapCellY) {
+ y = MapCellY;
+ }
+ if (y + Lepton_To_Cell(TacLeptonHeight) > MapCellY + MapCellHeight) {
+ y = MapCellY + MapCellHeight - Lepton_To_Cell(TacLeptonHeight);
+ }
+
+ /*
+ ** Set our TacticalCell
+ */
+ Set_Tactical_Position(Cell_Coord(XY_Cell(x, y)));
+ for (int index = 0; index < sizeof(Views)/sizeof(Views[0]); index++) {
+ Views[index] = Coord_Cell(TacticalCoord);
+ }
+}
+
+
+/***********************************************************************************************
+ * 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 ((unsigned)(Coord_X(coord) - Coord_X(tcoord)) > TacLeptonWidth+255) return(false);
+ if ((unsigned)(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
+{
+ 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: none *
+ * *
+ * WARNINGS: The map position changes by this routine. *
+ * *
+ * HISTORY: *
+ * 08/22/1995 JLB : Created. *
+ *=============================================================================================*/
+void 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)));
+ }
+}
diff --git a/DISPLAY.H b/DISPLAY.H
new file mode 100644
index 0000000..be410c5
--- /dev/null
+++ b/DISPLAY.H
@@ -0,0 +1,320 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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
+{
+ 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;
+ 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.
+ */
+ void Center_Map(void);
+ virtual bool Map_Cell(CELL cell, HouseClass *house);
+ 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);
+ 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);
+ 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);
+ 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(void);
+
+ /*
+ ** 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) {};
+
+ 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.
+ */
+ 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;
+};
+
+
+#endif
diff --git a/DOOR.CPP b/DOOR.CPP
new file mode 100644
index 0000000..d94c491
--- /dev/null
+++ b/DOOR.CPP
@@ -0,0 +1,202 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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/DOOR.H b/DOOR.H
new file mode 100644
index 0000000..7f16bb9
--- /dev/null
+++ b/DOOR.H
@@ -0,0 +1,94 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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/DPMI.CPP b/DPMI.CPP
new file mode 100644
index 0000000..d7fa607
--- /dev/null
+++ b/DPMI.CPP
@@ -0,0 +1,169 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#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
diff --git a/DPMI.H b/DPMI.H
new file mode 100644
index 0000000..a65a0be
--- /dev/null
+++ b/DPMI.H
@@ -0,0 +1,175 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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/DRIVE.CPP b/DRIVE.CPP
new file mode 100644
index 0000000..1c7d5ec
--- /dev/null
+++ b/DRIVE.CPP
@@ -0,0 +1,2213 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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) {};
+
+
+/***********************************************************************************************
+ * 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);
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * 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 ((Special.IsDifficult || Special.IsScatter || Scenario > 8) && !Special.IsEasy) {
+ 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.
+ */
+ if (((UnitClass *)this)->Flagged != HOUSE_NONE) {
+ actual = SpeedAccum + Fixed_To_Cardinal(Class->MaxSpeed/2, Speed);
+ } else {
+ actual = SpeedAccum + Fixed_To_Cardinal(Class->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);
+ }
+ }
+
+ /*
+ ** 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(Class->ROT)) {
+ 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);
+ }
+ return(UnitTypeClass::FULL_LOAD_CREDITS+(UnitTypeClass::FULL_LOAD_CREDITS/3)/UnitTypeClass::STEP_COUNT);
+ }
+ 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.
+};
+
diff --git a/DRIVE.H b/DRIVE.H
new file mode 100644
index 0000000..4a8225b
--- /dev/null
+++ b/DRIVE.H
@@ -0,0 +1,215 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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;
+
+ /*
+ ** 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 {
+ 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];
+};
+
+inline DriveClass::TrackControlType operator |(DriveClass::TrackControlType, DriveClass::TrackControlType);
+inline DriveClass::TrackControlType operator &(DriveClass::TrackControlType, DriveClass::TrackControlType);
+inline DriveClass::TrackControlType operator ~(DriveClass::TrackControlType);
+
+
+#endif
diff --git a/EDIT.CPP b/EDIT.CPP
new file mode 100644
index 0000000..1c099af
--- /dev/null
+++ b/EDIT.CPP
@@ -0,0 +1,471 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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() && 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);
+ }
+ } else {
+ Conquer_Clip_Text_Print(text, X+1, Y+1, Has_Focus() ? BLUE : WHITE, TBLACK, TextFlags, 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,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 ((String_Pixel_Width(String) + 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/EDIT.H b/EDIT.H
new file mode 100644
index 0000000..fae3928
--- /dev/null
+++ b/EDIT.H
@@ -0,0 +1,106 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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);
+
+#endif
diff --git a/ENDING.CPP b/ENDING.CPP
new file mode 100644
index 0000000..2484ed5
--- /dev/null
+++ b/ENDING.CPP
@@ -0,0 +1,261 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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);
+ HidPage.Blit(SeenBuff);
+ 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 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
diff --git a/ENDING.H b/ENDING.H
new file mode 100644
index 0000000..65a28e4
--- /dev/null
+++ b/ENDING.H
@@ -0,0 +1,42 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/***********************************************************************************************
+ *** C O N F I D E N T 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/EVENT.CPP b/EVENT.CPP
new file mode 100644
index 0000000..3f05482
--- /dev/null
+++ b/EVENT.CPP
@@ -0,0 +1,760 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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.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",
+ "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) {
+ /*
+ ** 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) {
+ if (Data.Anim.Owner != HOUSE_NONE && PlayerPtr->Class->House != Data.Anim.Owner && !Special.IsVisibleTarget) {
+ anim->Make_Invisible();
+ }
+ }
+ 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) {
+ if (PlayerPtr->Is_Ally(techno) || Special.IsVisibleTarget) {
+ object->Clicked_As_Target();
+ }
+ }
+ 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)) {
+
+ ((FootClass *)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) {
+
+ ((FootClass *)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/EVENT.H b/EVENT.H
new file mode 100644
index 0000000..f85f98e
--- /dev/null
+++ b/EVENT.H
@@ -0,0 +1,226 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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 {
+ 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
+ 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/EXPAND.CPP b/EXPAND.CPP
new file mode 100644
index 0000000..1465495
--- /dev/null
+++ b/EXPAND.CPP
@@ -0,0 +1,422 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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;
+ for (int 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);
+ HidPage.Blit(SeenBuff);
+
+ 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
+ };
+
+
+ for (int 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);
+ HidPage.Blit(SeenBuff);
+
+ 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
diff --git a/EXTERNS.H b/EXTERNS.H
new file mode 100644
index 0000000..bf2edce
--- /dev/null
+++ b/EXTERNS.H
@@ -0,0 +1,401 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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 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 LoseMovie[_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;
+
+/*
+** 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;
+
+extern DynamicVectorClass 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 bool 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 RemapYellow[256];
+extern unsigned char const RemapRed[256];
+extern unsigned char const RemapBlueGreen[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;
+
+#endif
diff --git a/FACING.CPP b/FACING.CPP
new file mode 100644
index 0000000..62401ae
--- /dev/null
+++ b/FACING.CPP
@@ -0,0 +1,183 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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/FACING.H b/FACING.H
new file mode 100644
index 0000000..d5cf79e
--- /dev/null
+++ b/FACING.H
@@ -0,0 +1,79 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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/FACTORY.CPP b/FACTORY.CPP
new file mode 100644
index 0000000..ce75b00
--- /dev/null
+++ b/FACTORY.CPP
@@ -0,0 +1,750 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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;
+ 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::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();
+ 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->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;
+ }
+
+ 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.
+ */
+ House->Refund_Money(Object->Class_Of().Cost_Of() - 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/FACTORY.H b/FACTORY.H
new file mode 100644
index 0000000..9c01035
--- /dev/null
+++ b/FACTORY.H
@@ -0,0 +1,144 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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
+{
+ 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);};
+
+ /*
+ ** Dee-buggin' support.
+ */
+ int Validate(void) const;
+
+ /*
+ ** 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;
+
+ /*
+ ** 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;
+};
+
+#endif
diff --git a/FIELD.CPP b/FIELD.CPP
new file mode 100644
index 0000000..ad77fba
--- /dev/null
+++ b/FIELD.CPP
@@ -0,0 +1,214 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/***************************************************************************
+ * *
+ * 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/FIELD.H b/FIELD.H
new file mode 100644
index 0000000..9335437
--- /dev/null
+++ b/FIELD.H
@@ -0,0 +1,85 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/***************************************************************************
+ * *
+ * 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/FINDPATH.CPP b/FINDPATH.CPP
new file mode 100644
index 0000000..ec7a10c
--- /dev/null
+++ b/FINDPATH.CPP
@@ -0,0 +1,1546 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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;
+ int threat_stage;
+
+ /*
+ ** 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 = IsSelected && Special.IsShowPath;
+ } else {
+ DrawPath = IsSelected;
+ }
+ 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/FLASHER.CPP b/FLASHER.CPP
new file mode 100644
index 0000000..a155aee
--- /dev/null
+++ b/FLASHER.CPP
@@ -0,0 +1,97 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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)
+{
+ if (FlashCount) {
+ FlashCount--;
+ IsBlushing = false;
+
+ if (FlashCount & 0x01) {
+ IsBlushing = true;
+ }
+ return(true);
+ }
+ return(false);
+}
+
+
diff --git a/FLASHER.H b/FLASHER.H
new file mode 100644
index 0000000..be89421
--- /dev/null
+++ b/FLASHER.H
@@ -0,0 +1,71 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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;
+
+ FlasherClass(void) {FlashCount = 0; IsBlushing = false;};
+ #ifdef CHEAT_KEYS
+ void Debug_Dump(MonoClass *mono) const;
+ #endif
+ bool Process(void);
+
+ /*
+ ** File I/O.
+ */
+ void Code_Pointers(void);
+ void Decode_Pointers(void);
+
+};
+
+#endif
diff --git a/FLY.CPP b/FLY.CPP
new file mode 100644
index 0000000..b53eed0
--- /dev/null
+++ b/FLY.CPP
@@ -0,0 +1,133 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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/FLY.H b/FLY.H
new file mode 100644
index 0000000..962755e
--- /dev/null
+++ b/FLY.H
@@ -0,0 +1,83 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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 {
+ 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
diff --git a/FOOT.CPP b/FOOT.CPP
new file mode 100644
index 0000000..64e5e2f
--- /dev/null
+++ b/FOOT.CPP
@@ -0,0 +1,2015 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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)
+{
+ ArchiveTarget = TARGET_NONE;
+ 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)
+{
+ ArchiveTarget = TARGET_NONE;
+ 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;
+
+ 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:
+ if (What_Am_I() != RTTI_AIRCRAFT || Map[cell].IsVisible) {
+ Player_Assign_Mission(MISSION_MOVE, TARGET_NONE, ::As_Target(cell));
+ }
+ break;
+
+ case ACTION_ATTACK:
+ Player_Assign_Mission(MISSION_ATTACK, ::As_Target(cell));
+ break;
+ }
+}
+
+
+/***********************************************************************************************
+ * 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.
+ */
+ 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) {
+ inrange = In_Range(((FootClass const *)techno)->Likely_Coord());
+ }
+
+ 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);
+ }
+ }
+ 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) {
+ ArchiveTarget = TARGET_NONE;
+ Enter_Idle_Mode();
+ } else {
+ 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
+{
+ 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);
+ } else {
+ if (House != PlayerPtr && GameToPlay != GAME_NORMAL) {
+ if (Options.IsDeathAnnounce) Speak(VOX_ENEMY_UNIT);
+ } else {
+ if (House == PlayerPtr || Options.IsDeathAnnounce) {
+ if (!Options.IsDeathAnnounce) {
+ Speak(VOX_UNIT_LOST);
+ } else {
+ switch (House->ActLike) {
+ case HOUSE_GOOD:
+ Speak(VOX_DEAD_GDI);
+ break;
+
+ case HOUSE_BAD:
+ Speak(VOX_DEAD_NOD);
+ 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());
+}
diff --git a/FOOT.H b/FOOT.H
new file mode 100644
index 0000000..9069df3
--- /dev/null
+++ b/FOOT.H
@@ -0,0 +1,311 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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;
+
+ /*
+ ** 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 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;
+};
+
+#endif
diff --git a/FTIMER.H b/FTIMER.H
new file mode 100644
index 0000000..641a457
--- /dev/null
+++ b/FTIMER.H
@@ -0,0 +1,91 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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/FUNCTION.H b/FUNCTION.H
new file mode 100644
index 0000000..9ec6791
--- /dev/null
+++ b/FUNCTION.H
@@ -0,0 +1,926 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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.
+*/
+#ifndef TRUE_FALSE_DEFINED
+enum {false=0,true=1};
+typedef int bool;
+#define TRUE_FALSE_DEFINED
+#endif //TRUE_FALSE_DEFINED
+
+
+#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
+#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));}
+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 "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"
+/****************************************************************************
+** 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, 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
+*/
+
+/*
+** 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);
+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 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, 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);
+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);
+inline bool Target_Legal(TARGET target) {return(target != TARGET_NONE);};
+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_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);
+#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));
+}
+#if(0)
+#ifndef NOMEMCHECK
+#define NO_INTERCEPT
+#include "memcheck.h"
+#endif
+#endif
+
+void WWDOS_Shutdown(void);
+
+#endif
diff --git a/FUNCTION.I b/FUNCTION.I
new file mode 100644
index 0000000..872a43c
--- /dev/null
+++ b/FUNCTION.I
@@ -0,0 +1,19 @@
+;***************************************************************************
+;** C O N F I D E N T 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.I *
+;* *
+;* Programmer : Joe L. Bostic *
+;* *
+;* Start Date : November 10, 1993 *
+;* *
+;* Last Update : November 10, 1993 [JLB] *
+;* *
+;*-------------------------------------------------------------------------*
+;* Functions: *
+;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *
+
+
diff --git a/FUSE.CPP b/FUSE.CPP
new file mode 100644
index 0000000..5762148
--- /dev/null
+++ b/FUSE.CPP
@@ -0,0 +1,196 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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/FUSE.H b/FUSE.H
new file mode 100644
index 0000000..cd48083
--- /dev/null
+++ b/FUSE.H
@@ -0,0 +1,95 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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/GADGET.CPP b/GADGET.CPP
new file mode 100644
index 0000000..5dd5cb6
--- /dev/null
+++ b/GADGET.CPP
@@ -0,0 +1,804 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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 && (unsigned)(mousex - X) < Width && (unsigned)(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/GADGET.H b/GADGET.H
new file mode 100644
index 0000000..f6f650b
--- /dev/null
+++ b/GADGET.H
@@ -0,0 +1,239 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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:
+ 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);
+
+
+#endif
diff --git a/GAMEDLG.CPP b/GAMEDLG.CPP
new file mode 100644
index 0000000..4924b3c
--- /dev/null
+++ b/GAMEDLG.CPP
@@ -0,0 +1,422 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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/GAMEDLG.H b/GAMEDLG.H
new file mode 100644
index 0000000..e166eb0
--- /dev/null
+++ b/GAMEDLG.H
@@ -0,0 +1,52 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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/GAUGE.CPP b/GAUGE.CPP
new file mode 100644
index 0000000..d323cd0
--- /dev/null
+++ b/GAUGE.CPP
@@ -0,0 +1,539 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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/GAUGE.H b/GAUGE.H
new file mode 100644
index 0000000..76f191f
--- /dev/null
+++ b/GAUGE.H
@@ -0,0 +1,111 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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/GDI.BAT b/GDI.BAT
new file mode 100644
index 0000000..de54db5
--- /dev/null
+++ b/GDI.BAT
@@ -0,0 +1,5 @@
+@echo off
+pushd
+cd ..\run
+conquer -CD..\cd\aud1;..\cd;..\cd\install %1 %2 %3 %4 %5
+popd
diff --git a/GLOBALS.CPP b/GLOBALS.CPP
new file mode 100644
index 0000000..ebff031
--- /dev/null
+++ b/GLOBALS.CPP
@@ -0,0 +1,1035 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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 .
+*/
+
+/* $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;
+
+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 is a list of all selected objects (for this map). The support functions
+** are used to control access to this list. Do not modify it directly.
+*/
+DynamicVectorClass 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.
+*/
+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 LoseMovie[_MAX_FNAME+_MAX_EXT];
+char ActionMovie[_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;
+
+/***************************************************************************
+** 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;
+
+
+/***************************************************************************
+** 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.
+bool 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.
+*/
+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 - "
+};
+
+
+/***************************************************************************
+** 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},
+
+};
+
+
+/* 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,640,480);
+GraphicBufferClass ModeXBuff;
+GraphicViewPortClass HidPage(&HiddenPage, 0,0,640,480);
+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=640;
+int ScreenHeight=400;
+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;
+
diff --git a/GOPTIONS.CPP b/GOPTIONS.CPP
new file mode 100644
index 0000000..bf1e0ae
--- /dev/null
+++ b/GOPTIONS.CPP
@@ -0,0 +1,587 @@
+/*
+** Command & Conquer(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 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