diff --git a/CODE/2KEYFBUF.ASM b/CODE/2KEYFBUF.ASM
new file mode 100644
index 0000000..2719ac9
--- /dev/null
+++ b/CODE/2KEYFBUF.ASM
@@ -0,0 +1,4848 @@
+;
+; Command & Conquer Red Alert(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 A S S O C I A T E S **
+;***************************************************************************
+;* *
+;* Project Name : Command & Conquer *
+;* *
+;* File Name : KEYFBUFF.ASM *
+;* *
+;* Programmer : David R. Dettmer *
+;* *
+;* Start Date : March 3, 1995 *
+;* *
+;* Last Update : *
+;* *
+;*-------------------------------------------------------------------------*
+;* Functions: *
+;* Buffer_Frame_To_Page -- Copies a linear buffer to a virtual viewport *
+;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *
+
+;********************** Model & Processor Directives ***********************
+IDEAL
+P386
+MODEL USE32 FLAT
+jumps
+
+;******************************** Includes *********************************
+INCLUDE "gbuffer.inc"
+include "profile.inc"
+
+
+;******************************** Equates **********************************
+
+TRUE equ 1 ; Boolean 'true' value
+FALSE equ 0 ; Boolean 'false' value
+
+;*=========================================================================*/
+;* The following are defines used to control what functions are linked *
+;* in for Buffer_Frame_To_Page. *
+;*=========================================================================*/
+;USE_NORMAL EQU TRUE
+;USE_HORZ_REV EQU TRUE
+;USE_VERT_REV EQU TRUE
+;USE_SCALING EQU TRUE
+
+
+FLAG_NORMAL EQU 0
+FLAG_TRANS EQU 1
+FLAG_GHOST EQU 2
+FLAG_FADING EQU 4
+FLAG_PREDATOR EQU 8
+
+FLAG_MASK EQU 0Fh
+
+
+SHAPE_NORMAL EQU 0000h ; Standard shape
+;SHAPE_HORZ_REV EQU 0001h ; Flipped horizontally
+;SHAPE_VERT_REV EQU 0002h ; Flipped vertically
+;SHAPE_SCALING EQU 0004h ; Scaled (WORD scale_x, WORD scale_y)
+;SHAPE_VIEWPORT_REL EQU 0010h ; Coords are window-relative
+;SHAPE_WIN_REL EQU 0010h ; Coordinates are window relative instead of absolute.
+SHAPE_CENTER EQU 0020h ; Coords are based on shape's center pt
+SHAPE_TRANS EQU 0040h ; has transparency
+
+SHAPE_FADING EQU 0100h ; Fading effect (VOID * fading_table,
+ ; WORD fading_num)
+SHAPE_PREDATOR EQU 0200h ; Transparent warping effect
+;SHAPE_COMPACT EQU 0400h ; Never use this bit
+;SHAPE_PRIORITY EQU 0800h ; Use priority system when drawing
+SHAPE_GHOST EQU 1000h ; Shape is drawn ghosted
+;SHAPE_SHADOW EQU 2000h
+SHAPE_PARTIAL EQU 4000h
+;SHAPE_COLOR EQU 8000h ; Remap the shape's colors
+ ; (VOID * color_table)
+
+
+;
+;.......................... Shadow Effect ..................................
+;
+SHADOW_COL EQU 00FFh ; magic number for shadows
+
+;......................... Priority System .................................
+;
+CLEAR_UNUSED_BITS EQU 0007h ; and with 0000-0111 to clear
+ ; non-walkable high bit and
+ ; scaling id bits
+NON_WALKABLE_BIT EQU 0080h ; and with 1000-0000 to clear all
+ ; but non-walkable bit
+;
+;......................... Predator Effect .................................
+;
+;PRED_MASK EQU 0007h ; mask used for predator pixel puts
+PRED_MASK EQU 000Eh ; mask used for predator pixel puts
+
+
+;---------------------------------------------------------------------------
+;
+; Use a macro to make code a little cleaner.
+; The parameter varname is optional.
+; Syntax to use macro is :
+; WANT equ expression
+; USE func [,varname]
+; If the 'varname' is defined, a table declaration is created like:
+; GLOBAL TableName:DWORD
+; Then, the table entry is created:
+; If WANT is true, the table entry is created for the given function:
+; varname DD func
+; If WANT is not TRUE, a Not_Supported entry is put in the table:
+; varname DD Not_Supported
+; The resulting tables look like:
+;
+; GLOBAL ExampTable:DWORD
+; ExampTable DD routine1
+; DD routine2
+; DD routine3
+; ...
+; Thus, each table is an array of function pointers.
+;
+;---------------------------------------------------------------------------
+MACRO USE func, varname
+ IF WANT
+ varname DD func
+ ELSE
+ varname DD Not_Supported
+ ENDIF
+ENDM
+
+; IFNB
+; GLOBAL varname:DWORD
+; ENDIF
+
+;---------------------------------------------------------------------------
+
+
+DATASEG
+
+;---------------------------------------------------------------------------
+; Predator effect variables
+;---------------------------------------------------------------------------
+; make table for negative offset and use the used space for other variables
+
+BFPredNegTable DW -1, -3, -2, -5, -2, -4, -3, -1
+ ; 8 words below calculated
+ DW 0, 0, 0, 0, 0, 0, 0, 0 ; index ffffff00
+ DD 0, 0, 0, 0 ; index ffffff10
+BFPredOffset DD 0, 0, 0, 0 ; index ffffff20
+ DD 0, 0, 0, 0 ; index ffffff30
+ ; partially faded predator effect value
+BFPartialPred DD 0, 0, 0, 0 ; index ffffff40
+BFPartialCount DD 0, 0, 0, 0 ; index ffffff50
+ DD 0, 0, 0, 0 ; index ffffff60
+ DD 0, 0, 0, 0 ; index ffffff70
+ DD 0, 0, 0, 0 ; index ffffff80
+ DD 0, 0, 0, 0 ; index ffffff90
+ DD 0, 0, 0, 0 ; index ffffffa0
+ DD 0, 0, 0, 0 ; index ffffffb0
+ DD 0, 0, 0, 0 ; index ffffffc0
+ DD 0, 0, 0, 0 ; index ffffffd0
+ DD 0, 0, 0, 0 ; index ffffffe0
+ DD 0, 0, 0, 0 ; index fffffff0
+BFPredTable DW 1, 3, 2, 5, 2, 3, 4, 1
+;BFPredTable DB 1, 3, 2, 5, 4, 3, 2, 1
+
+
+
+
+
+
+ global C BigShapeBufferStart:dword
+ global C UseBigShapeBuffer:dword
+ global C UseOldShapeDraw:dword
+ global C TheaterShapeBufferStart:dword
+ global C IsTheaterShape:dword
+ global C Single_Line_Trans_Entry:near
+ global C Next_Line:near
+ global C MMX_Done:near
+ global C MMXAvailable:dword
+ global EndNewShapeJumpTable:byte
+ global NewShapeJumpTable:dword
+
+
+;**********************************************************************************
+;
+; Jump tables for new line header system
+;
+; Each line of shape data now has a header byte which describes the data on the line.
+;
+
+;
+; Header byte control bits
+;
+BLIT_TRANSPARENT =1
+BLIT_GHOST =2
+BLIT_FADING =4
+BLIT_PREDATOR =8
+BLIT_SKIP =16
+BLIT_ALL =BLIT_TRANSPARENT or BLIT_GHOST or BLIT_FADING or BLIT_PREDATOR or BLIT_SKIP
+
+
+ struc ShapeHeaderType
+
+ draw_flags dd ?
+ shape_data dd ?
+ shape_buffer dd ?
+
+ ends
+
+;
+; Global definitions for routines that draw a single line of a shape
+;
+ global Short_Single_Line_Copy:near
+ global Single_Line_Trans:near
+ global Single_Line_Ghost:near
+ global Single_Line_Ghost_Trans:near
+ global Single_Line_Fading:near
+ global Single_Line_Fading_Trans:near
+ global Single_Line_Ghost_Fading:near
+ global Single_Line_Ghost_Fading_Trans:near
+ global Single_Line_Predator:near
+ global Single_Line_Predator_Trans:near
+ global Single_Line_Predator_Ghost:near
+ global Single_Line_Predator_Ghost_Trans:near
+ global Single_Line_Predator_Fading:near
+ global Single_Line_Predator_Fading_Trans:near
+ global Single_Line_Predator_Ghost_Fading:near
+ global Single_Line_Predator_Ghost_Fading_Trans:near
+ global Single_Line_Skip:near
+
+ global Single_Line_Single_Fade:near
+ global Single_Line_Single_Fade_Trans:near
+
+
+
+label NewShapeJumpTable dword
+
+;
+; Jumptable for shape line drawing with no flags set
+;
+
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+label CriticalFadeRedirections dword
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+
+
+;
+; Jumptable for shape line drawing routines with transparent flags set
+;
+ dd Short_Single_Line_Copy
+ dd Single_Line_Trans
+ dd Short_Single_Line_Copy
+ dd Single_Line_Trans
+ dd Short_Single_Line_Copy
+ dd Single_Line_Trans
+ dd Short_Single_Line_Copy
+ dd Single_Line_Trans
+ dd Short_Single_Line_Copy
+ dd Single_Line_Trans
+ dd Short_Single_Line_Copy
+ dd Single_Line_Trans
+ dd Short_Single_Line_Copy
+ dd Single_Line_Trans
+ dd Short_Single_Line_Copy
+ dd Single_Line_Trans
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+
+
+;
+; Jumptable for shape line drawing routines with ghost flags set
+;
+
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Single_Line_Ghost
+ dd Single_Line_Ghost
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Single_Line_Ghost
+ dd Single_Line_Ghost
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Single_Line_Ghost
+ dd Single_Line_Ghost
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Single_Line_Ghost
+ dd Single_Line_Ghost
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+
+
+;
+; Jumptable for shape line drawing routines with ghost and transparent flags set
+;
+
+ dd Short_Single_Line_Copy
+ dd Single_Line_Trans
+ dd Single_Line_Ghost
+ dd Single_Line_Ghost_Trans
+ dd Short_Single_Line_Copy
+ dd Single_Line_Trans
+ dd Single_Line_Ghost
+ dd Single_Line_Ghost_Trans
+ dd Short_Single_Line_Copy
+ dd Single_Line_Trans
+ dd Single_Line_Ghost
+ dd Single_Line_Ghost_Trans
+ dd Short_Single_Line_Copy
+ dd Single_Line_Trans
+ dd Single_Line_Ghost
+ dd Single_Line_Ghost_Trans
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+
+
+
+
+
+;
+; Jumptable for shape line drawing routines with fading flag set
+;
+
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Single_Line_Single_Fade
+ dd Single_Line_Single_Fade
+ dd Single_Line_Fading
+ dd Single_Line_Fading
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Single_Line_Fading
+ dd Single_Line_Fading
+ dd Single_Line_Fading
+ dd Single_Line_Fading
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+
+
+;
+; Jumptable for shape line drawing routines with fading and transparent flags set
+;
+
+ dd Short_Single_Line_Copy
+ dd Single_Line_Trans
+ dd Short_Single_Line_Copy
+ dd Single_Line_Trans
+ dd Single_Line_Single_Fade
+ dd Single_Line_Single_Fade_Trans
+ dd Single_Line_Fading
+ dd Single_Line_Fading_Trans
+ dd Short_Single_Line_Copy
+ dd Single_Line_Trans
+ dd Short_Single_Line_Copy
+ dd Single_Line_Trans
+ dd Single_Line_Fading
+ dd Single_Line_Fading_Trans
+ dd Single_Line_Fading
+ dd Single_Line_Fading_Trans
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+
+
+;
+; Jumptable for shape line drawing routines with fading and ghost flags set
+;
+
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Single_Line_Ghost
+ dd Single_Line_Ghost
+ dd Single_Line_Single_Fade
+ dd Single_Line_Single_Fade
+ dd Single_Line_Ghost_Fading
+ dd Single_Line_Ghost_Fading
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Single_Line_Ghost
+ dd Single_Line_Ghost
+ dd Single_Line_Fading
+ dd Single_Line_Fading
+ dd Single_Line_Ghost_Fading
+ dd Single_Line_Ghost_Fading
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+
+
+
+;
+; Jumptable for shape line drawing routines with fading, transparent and ghost flags set
+;
+
+ dd Short_Single_Line_Copy
+ dd Single_Line_Trans
+ dd Single_Line_Ghost
+ dd Single_Line_Ghost_Trans
+ dd Single_Line_Single_Fade
+ dd Single_Line_Single_Fade_Trans
+ dd Single_Line_Ghost_Fading
+ dd Single_Line_Ghost_Fading_Trans
+ dd Short_Single_Line_Copy
+ dd Single_Line_Trans
+ dd Single_Line_Ghost
+ dd Single_Line_Ghost_Trans
+ dd Single_Line_Fading
+ dd Single_Line_Fading_Trans
+ dd Single_Line_Ghost_Fading
+ dd Single_Line_Ghost_Fading_Trans
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+
+
+
+
+
+
+;
+; Jumptable for shape line drawing with predator flag set
+;
+
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Single_Line_Predator
+ dd Single_Line_Predator
+ dd Single_Line_Predator
+ dd Single_Line_Predator
+ dd Single_Line_Predator
+ dd Single_Line_Predator
+ dd Single_Line_Predator
+ dd Single_Line_Predator
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+
+
+;
+; Jumptable for shape line drawing routines with transparent and predator flags set
+;
+ dd Short_Single_Line_Copy
+ dd Single_Line_Trans
+ dd Short_Single_Line_Copy
+ dd Single_Line_Trans
+ dd Short_Single_Line_Copy
+ dd Single_Line_Trans
+ dd Short_Single_Line_Copy
+ dd Single_Line_Trans
+ dd Single_Line_Predator
+ dd Single_Line_Predator_Trans
+ dd Single_Line_Predator
+ dd Single_Line_Predator_Trans
+ dd Single_Line_Predator
+ dd Single_Line_Predator_Trans
+ dd Single_Line_Predator
+ dd Single_Line_Predator_Trans
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+
+
+;
+; Jumptable for shape line drawing routines with ghost and predator flags set
+;
+
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Single_Line_Ghost
+ dd Single_Line_Ghost
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Single_Line_Ghost
+ dd Single_Line_Ghost
+ dd Single_Line_Predator
+ dd Single_Line_Predator
+ dd Single_Line_Predator_Ghost
+ dd Single_Line_Predator_Ghost
+ dd Single_Line_Predator
+ dd Single_Line_Predator
+ dd Single_Line_Predator_Ghost
+ dd Single_Line_Predator_Ghost
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+
+
+;
+; Jumptable for shape line drawing routines with ghost and transparent and predator flags set
+;
+
+ dd Short_Single_Line_Copy
+ dd Single_Line_Trans
+ dd Single_Line_Ghost
+ dd Single_Line_Ghost_Trans
+ dd Short_Single_Line_Copy
+ dd Single_Line_Trans
+ dd Single_Line_Ghost
+ dd Single_Line_Ghost_Trans
+ dd Single_Line_Predator
+ dd Single_Line_Predator_Trans
+ dd Single_Line_Predator_Ghost
+ dd Single_Line_Predator_Ghost_Trans
+ dd Single_Line_Predator
+ dd Single_Line_Predator_Trans
+ dd Single_Line_Predator_Ghost
+ dd Single_Line_Predator_Ghost_Trans
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+
+
+
+
+
+;
+; Jumptable for shape line drawing routines with fading and predator flags set
+;
+
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Single_Line_Single_Fade
+ dd Single_Line_Single_Fade
+ dd Single_Line_Fading
+ dd Single_Line_Fading
+ dd Single_Line_Predator
+ dd Single_Line_Predator
+ dd Single_Line_Predator
+ dd Single_Line_Predator
+ dd Single_Line_Predator_Fading
+ dd Single_Line_Predator_Fading
+ dd Single_Line_Predator_Fading
+ dd Single_Line_Predator_Fading
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+
+
+;
+; Jumptable for shape line drawing routines with fading and transparent and predator flags set
+;
+
+ dd Short_Single_Line_Copy
+ dd Single_Line_Trans
+ dd Short_Single_Line_Copy
+ dd Single_Line_Trans
+ dd Single_Line_Single_Fade
+ dd Single_Line_Single_Fade_Trans
+ dd Single_Line_Fading
+ dd Single_Line_Fading_Trans
+ dd Single_Line_Predator
+ dd Single_Line_Predator_Trans
+ dd Single_Line_Predator
+ dd Single_Line_Predator_Trans
+ dd Single_Line_Predator_Fading
+ dd Single_Line_Predator_Fading_Trans
+ dd Single_Line_Predator_Fading
+ dd Single_Line_Predator_Fading_Trans
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+
+
+;
+; Jumptable for shape line drawing routines with fading and ghost and predator flags set
+;
+
+ dd Short_Single_Line_Copy
+ dd Short_Single_Line_Copy
+ dd Single_Line_Ghost
+ dd Single_Line_Ghost
+ dd Single_Line_Single_Fade
+ dd Single_Line_Single_Fade
+ dd Single_Line_Ghost_Fading
+ dd Single_Line_Ghost_Fading
+ dd Single_Line_Predator
+ dd Single_Line_Predator
+ dd Single_Line_Predator_Ghost
+ dd Single_Line_Predator_Ghost
+ dd Single_Line_Predator_Fading
+ dd Single_Line_Predator_Fading
+ dd Single_Line_Predator_Ghost_Fading
+ dd Single_Line_Predator_Ghost_Fading
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+
+
+
+
+
+
+
+;
+; Jumptable for shape line drawing routines with all flags set
+;
+
+label AllFlagsJumpTable dword
+ dd Short_Single_Line_Copy
+ dd Single_Line_Trans
+ dd Single_Line_Ghost
+ dd Single_Line_Ghost_Trans
+ dd Single_Line_Single_Fade
+ dd Single_Line_Single_Fade_Trans
+ dd Single_Line_Ghost_Fading
+ dd Single_Line_Ghost_Fading_Trans
+ dd Single_Line_Predator
+ dd Single_Line_Predator_Trans
+ dd Single_Line_Predator_Ghost
+ dd Single_Line_Predator_Ghost_Trans
+ dd Single_Line_Predator_Fading
+ dd Single_Line_Predator_Fading_Trans
+ dd Single_Line_Predator_Ghost_Fading
+ dd Single_Line_Predator_Ghost_Fading_Trans
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+ dd Single_Line_Skip
+
+
+
+label EndNewShapeJumpTable byte
+
+CODESEG
+
+;---------------------------------------------------------------------------
+; Code Segment Tables:
+; This code uses the USE macro to set up tables of function addresses.
+; The tables have the following format:
+; Tables defined are:
+; BufferFrameTable
+;---------------------------------------------------------------------------
+
+WANT equ
+USE BF_Copy, BufferFrameTable
+
+WANT equ
+USE BF_Trans
+
+WANT equ
+USE BF_Ghost
+
+WANT equ
+USE BF_Ghost_Trans
+
+WANT equ
+USE BF_Fading
+
+WANT equ
+USE BF_Fading_Trans
+
+WANT equ
+USE BF_Ghost_Fading
+
+WANT equ
+USE BF_Ghost_Fading_Trans
+
+WANT equ
+USE BF_Predator
+
+WANT equ
+USE BF_Predator_Trans
+
+WANT equ
+USE BF_Predator_Ghost
+
+WANT equ
+USE BF_Predator_Ghost_Trans
+
+WANT equ
+USE BF_Predator_Fading
+
+WANT equ
+USE BF_Predator_Fading_Trans
+
+WANT equ
+USE BF_Predator_Ghost_Fading
+
+WANT equ
+USE BF_Predator_Ghost_Fading_Trans
+
+
+
+
+
+;---------------------------------------------------------------------------
+
+
+
+
+;*********************************************************************************************
+;* Set_Shape_Header -- create the line header bytes for a shape *
+;* *
+;* INPUT: Shape width *
+;* Shape height *
+;* ptr to raw shape data *
+;* ptr to shape headers *
+;* shape flags *
+;* ptr to translucency table *
+;* IsTranslucent *
+;* *
+;* OUTPUT: none *
+;* *
+;* Warnings: *
+;* *
+;* HISTORY: *
+;* 11/29/95 10:09AM ST : Created. *
+;*===========================================================================================*
+
+ proc Setup_Shape_Header C near
+
+ ARG pixel_width :DWORD ; width of rectangle to blit
+ ARG pixel_height :DWORD ; height of rectangle to blit
+ ARG src :DWORD ; this is a member function
+ ARG headers :DWORD
+ ARG flags :DWORD
+ ARG Translucent :DWORD
+ ARG IsTranslucent :DWORD
+ LOCAL trans_count :DWORD
+
+ pushad
+
+
+ mov esi,[src] ;ptr to raw shape data
+ mov edi,[headers] ;ptr to headers we are going to set up
+ mov eax,[flags]
+ and eax,SHAPE_TRANS or SHAPE_FADING or SHAPE_PREDATOR or SHAPE_GHOST
+ mov [(ShapeHeaderType edi).draw_flags],eax ;save old flags in header
+ add edi,size ShapeHeaderType
+ mov edx,[pixel_height] ;number of shape lines to scan
+
+??outer_loop: mov ecx,[pixel_width] ;number of pixels in shape line
+ xor bl,bl ;flag the we dont know anything about this line yet
+ mov [trans_count],0 ;we havnt scanned any transparent pixels yet
+
+;
+; Scan one shape line to see what kind of data it contains
+;
+??inner_loop: xor eax,eax
+ mov al,[esi]
+ inc esi
+
+;
+; Check for transparent pixel
+;
+ test al,al
+ jnz ??not_transp
+ test [flags],SHAPE_TRANS
+ jz ??not_transp
+ or bl,BLIT_TRANSPARENT ;flag that pixel is transparent
+ inc [trans_count] ;keep count of the number of transparent pixels on the line
+ jmp ??end_lp
+
+;
+; Check for predator effect on this line
+;
+??not_transp: test [flags],SHAPE_PREDATOR
+ jz ??not_pred
+ or bl,BLIT_PREDATOR
+
+;
+; Check for ghost effects
+;
+??not_pred: test [flags],SHAPE_GHOST
+ jz ??not_ghost
+ push edi
+ mov edi,[IsTranslucent]
+ cmp [byte edi+eax],-1
+ pop edi
+ jz ??not_ghost
+ or bl,BLIT_GHOST
+
+;
+; Check if fading is required
+;
+??not_ghost: test [flags],SHAPE_FADING
+ jz ??end_lp
+ or bl,BLIT_FADING
+
+??end_lp: dec ecx
+ jnz ??inner_loop
+
+
+;
+; Interpret the info we have collected and decide which routine will be
+; used to draw this line
+;
+ xor bh,bh
+
+ test bl,BLIT_TRANSPARENT
+ jz ??no_transparencies
+ or bh,BLIT_TRANSPARENT
+ mov ecx,[pixel_width]
+ cmp ecx,[trans_count]
+ jnz ??not_all_trans
+
+; all pixels in the line were transparent so we dont need to draw it at all
+ mov bh,BLIT_SKIP
+ jmp ??got_line_type
+
+??not_all_trans:
+??no_transparencies:
+ mov al,bl
+ and al,BLIT_PREDATOR
+ or bh,al
+ mov al,bl
+ and al,BLIT_GHOST
+ or bh,al
+ mov al,bl
+ and al,BLIT_FADING
+ or bh,al
+
+;
+; Save the line header and do the next line
+;
+??got_line_type:mov [edi],bh
+ inc edi
+
+ dec edx
+ jnz ??outer_loop
+
+
+ popad
+ ret
+
+ endp Setup_Shape_Header
+
+
+
+
+;**************************************************************
+;
+; Macro to fetch the header of the next line and jump to the appropriate routine
+;
+ macro next_line
+
+ add edi , [ dest_adjust_width ] ;add in dest modulo
+ dec edx ;line counter
+ jz ??real_out ;return
+ mov ecx,[save_ecx] ;ecx is pixel count
+ mov eax,[header_pointer] ;ptr to next header byte
+ mov al,[eax]
+ inc [header_pointer]
+ and eax,BLIT_ALL ;Make sure we dont jump to some spurious address
+ ; if the header is wrong then its better to draw with the wrong
+ ; shape routine than to die
+ shl eax,2
+ add eax,[ShapeJumpTableAddress] ;get the address to jump to
+ jmp [dword eax] ;do the jump
+
+ endm
+
+
+
+
+
+
+;***************************************************************************
+;* VVC::TOPAGE -- Copies a linear buffer to a virtual viewport *
+;* *
+;* INPUT: WORD x_pixel - x pixel on viewport to copy from *
+;* WORD y_pixel - y pixel on viewport to copy from *
+;* WORD pixel_width - the width of copy region *
+;* WORD pixel_height - the height of copy region *
+;* BYTE * src - buffer to copy from *
+;* VVPC * dest - virtual viewport to copy to *
+;* *
+;* OUTPUT: none *
+;* *
+;* WARNINGS: Coordinates and dimensions will be adjusted if they exceed *
+;* the boundaries. In the event that no adjustment is *
+;* possible this routine will abort. If the size of the *
+;* region to copy exceeds the size passed in for the buffer *
+;* the routine will automatically abort. *
+;* *
+;* HISTORY: *
+;* 06/15/1994 PWG : Created. *
+;*=========================================================================*
+ GLOBAL C Buffer_Frame_To_Page:NEAR
+ PROC Buffer_Frame_To_Page C near
+ USES eax,ebx,ecx,edx,esi,edi
+
+ ;*===================================================================
+ ;* define the arguements that our function takes.
+ ;*===================================================================
+ ARG x_pixel :DWORD ; x pixel position in source
+ ARG y_pixel :DWORD ; y pixel position in source
+ ARG pixel_width :DWORD ; width of rectangle to blit
+ ARG pixel_height:DWORD ; height of rectangle to blit
+ ARG src :DWORD ; this is a member function
+ ARG dest :DWORD ; what are we blitting to
+
+ ARG flags :DWORD ; flags passed
+
+ ;*===================================================================
+ ; Define some locals so that we can handle things quickly
+ ;*===================================================================
+ LOCAL IsTranslucent :DWORD ; ptr to the is_translucent table
+ LOCAL Translucent :DWORD ; ptr to the actual translucent table
+ LOCAL FadingTable :DWORD ; extracted fading table pointer
+ LOCAL FadingNum :DWORD ; get the number of times to fade
+
+ LOCAL StashECX :DWORD ; temp variable for ECX register
+
+ LOCAL jflags :DWORD ; flags used to goto correct buff frame routine
+ LOCAL BufferFrameRout :DWORD ; ptr to the buffer frame routine
+
+ LOCAL jmp_loc :DWORD ; calculated jump location
+ LOCAL loop_cnt :DWORD
+
+ LOCAL x1_pixel :DWORD
+ LOCAL y1_pixel :DWORD
+ LOCAL scr_x :DWORD
+ LOCAL scr_y :DWORD
+ LOCAL dest_adjust_width :DWORD
+ LOCAL scr_adjust_width :DWORD
+ LOCAL header_pointer :DWORD
+ LOCAL use_old_draw :DWORD
+ LOCAL save_ecx :DWORD
+ LOCAL ShapeJumpTableAddress :DWORD
+ LOCAL shape_buffer_start :DWORD
+
+
+ prologue
+ cmp [ src ] , 0
+ jz ??real_out
+
+ cmp [UseOldShapeDraw],0
+ jz ??new_system
+
+ mov [use_old_draw],1
+ jmp ??do_args
+
+??new_system:
+ ;
+ ; Save the line attributes pointers and
+ ; Modify the src pointer to point to the actual image
+ ;
+ cmp [UseBigShapeBuffer],0
+ jz ??do_args ;just use the old shape drawing system
+ mov edi,[src]
+ mov [header_pointer],edi
+
+ mov eax,[BigShapeBufferStart]
+ cmp [(ShapeHeaderType edi).shape_buffer],0
+ jz ??is_ordinary_shape
+ mov eax,[TheaterShapeBufferStart]
+??is_ordinary_shape:
+ mov [shape_buffer_start],eax
+
+ mov edi,[(ShapeHeaderType edi).shape_data]
+ add edi,[shape_buffer_start]
+ mov [src],edi
+ mov [use_old_draw],0
+
+
+ ;====================================================================
+ ; Pull off optional arguments:
+ ; EDI is used as an offset from the 'flags' parameter, to point
+ ; to the optional argument currently being processed.
+ ;====================================================================
+??do_args:
+ mov edi , 4 ; optional params start past flags
+ mov [ jflags ] , 0 ; clear jump flags
+
+??check_centering:
+ ;-------------------------------------------------------------------
+ ; See if we need to center the frame
+ ;-------------------------------------------------------------------
+ test [ flags ] , SHAPE_CENTER ; does this need to be centered?
+ je ??check_trans ; if not the skip over this stuff
+
+ mov eax , [ pixel_width ]
+ mov ebx , [ pixel_height ]
+ sar eax , 1
+ sar ebx , 1
+ sub [ x_pixel ] , eax
+ sub [ y_pixel ] , ebx
+
+??check_trans:
+ test [ flags ] , SHAPE_TRANS
+ jz ??check_ghost
+
+ or [ jflags ] , FLAG_TRANS
+
+ ;--------------------------------------------------------------------
+ ; SHAPE_GHOST: DWORD is_translucent tbl
+ ;--------------------------------------------------------------------
+??check_ghost:
+ test [ flags ] , SHAPE_GHOST ; are we ghosting this shape
+ jz ??check_fading
+
+ mov eax , [ flags + edi ]
+ or [ jflags ] , FLAG_GHOST
+ mov [ IsTranslucent ] , eax ; save ptr to is_trans. tbl
+ add eax , 0100h ; add 256 for first table
+ add edi , 4 ; next argument
+ mov [ Translucent ] , eax ; save ptr to translucent tbl
+
+
+
+??check_fading:
+ ;______________________________________________________________________
+ ; If this is the first time through for this shape then
+ ; set up the shape header
+ ;______________________________________________________________________
+ pushad
+
+ cmp [UseBigShapeBuffer],0 ;no big shape buffer so use old system
+ jz ??new_shape
+ cmp [UseOldShapeDraw],0 ;use old shape system flag
+ jnz ??new_shape
+
+ mov edi,[header_pointer]
+ cmp [(ShapeHeaderType edi).draw_flags],-1
+ jz ??setup_headers
+ mov eax,[flags] ;Redo the shape headers if this shape was
+ and eax,SHAPE_TRANS or SHAPE_FADING or SHAPE_PREDATOR or SHAPE_GHOST ;initially set up with different flags
+ cmp eax,[(ShapeHeaderType edi).draw_flags]
+ jz ??no_header_setup
+??new_shape:
+ mov [use_old_draw],1
+ jmp ??no_header_setup
+
+??setup_headers:
+ push [IsTranslucent]
+ push [Translucent]
+ push [flags]
+ push [header_pointer]
+ push [src]
+ push [pixel_height]
+ push [pixel_width]
+ call Setup_Shape_Header
+ add esp,7*4
+ mov [ShapeJumpTableAddress],offset AllFlagsJumpTable
+ jmp ??headers_set
+??no_header_setup:
+
+ xor eax,eax
+ test [flags],SHAPE_PREDATOR
+ jz ??not_shape_predator
+ or al,BLIT_PREDATOR
+
+??not_shape_predator:
+ test [flags],SHAPE_FADING
+ jz ??not_shape_fading
+ or al,BLIT_FADING
+
+??not_shape_fading:
+
+ test [flags],SHAPE_TRANS
+ jz ??not_shape_transparent
+ or al,BLIT_TRANSPARENT
+
+??not_shape_transparent:
+
+ test [flags],SHAPE_GHOST
+ jz ??not_shape_ghost
+ or al,BLIT_GHOST
+
+??not_shape_ghost:
+
+ shl eax,7
+ add eax,offset NewShapeJumpTable
+ mov [ShapeJumpTableAddress],eax
+
+
+??headers_set:
+ popad
+
+ ;--------------------------------------------------------------------
+ ; SHAPE_FADING: DWORD fade_table[256], DWORD fade_count
+ ;--------------------------------------------------------------------
+ test [ flags ] , SHAPE_FADING ; are we fading this shape
+ jz ??check_predator
+
+ mov eax , [ flags + edi ]
+ mov [ FadingTable ] , eax ; save address of fading tbl
+ mov eax , [ flags + edi + 4 ] ; get fade num
+ or [ jflags ] , FLAG_FADING
+ and eax , 03fh ; no need for more than 63
+ add edi , 8 ; next argument
+ cmp eax , 0 ; check if it's 0
+ jnz ??set_fading ; if not, store fade num
+
+ and [ flags ] , NOT SHAPE_FADING ; otherwise, don't fade
+
+??set_fading:
+ mov [ FadingNum ] , eax
+
+ mov ebx,[ShapeJumpTableAddress]
+ mov [dword ebx+CriticalFadeRedirections-NewShapeJumpTable],offset Single_Line_Single_Fade
+ mov [dword ebx+CriticalFadeRedirections-NewShapeJumpTable+4],offset Single_Line_Single_Fade_Trans
+ cmp eax,1
+ jz ??single_fade
+ mov [dword ebx+CriticalFadeRedirections-NewShapeJumpTable],offset Single_Line_Fading
+ mov [dword ebx+CriticalFadeRedirections-NewShapeJumpTable+4],offset Single_Line_Fading_Trans
+
+??single_fade:
+
+ ;--------------------------------------------------------------------
+ ; SHAPE_PREDATOR: DWORD init_pred_lookup_offset (0-7)
+ ;--------------------------------------------------------------------
+??check_predator:
+ test [ flags ] , SHAPE_PREDATOR ; is predator effect on
+ jz ??check_partial
+
+ mov eax , [ flags + edi ] ; pull the partial value
+ or [ jflags ] , FLAG_PREDATOR
+ shl eax , 1
+ cmp eax , 0
+ jge ??check_range
+
+ neg eax
+ mov ebx , -1
+ and eax , PRED_MASK ; keep entries within bounds
+ mov bl , al
+ mov eax , ebx ; will be ffffff00-ffffff07
+ jmp ??pred_cont
+
+??check_range:
+ and eax , PRED_MASK ; keep entries within bounds
+
+??pred_cont:
+ add edi , 4 ; next argument
+ mov [ BFPredOffset ] , eax
+ mov [ BFPartialCount ] , 0 ; clear the partial count
+ mov [ BFPartialPred ] , 100h ; init partial to off
+
+??pred_neg_init:
+ mov esi , [ dest ] ; get ptr to dest
+ mov ebx, 7 * 2
+
+??pred_loop:
+ movzx eax , [ WORD PTR BFPredNegTable + ebx ]
+ add eax , [ (GraphicViewPort esi) . GVPWidth ] ; add width
+ add eax , [ (GraphicViewPort esi) . GVPXAdd ] ; add x add
+ add eax , [ (GraphicViewPort esi) . GVPPitch ] ; extra pitch of DD surface ST - 9/29/95 1:08PM
+ mov [ WORD PTR BFPredNegTable + 16 + ebx ] , ax
+ dec ebx
+ dec ebx
+ jge ??pred_loop
+
+ ;--------------------------------------------------------------------
+ ; SHAPE_PARTIAL: DWORD partial_pred_value (0-255)
+ ;--------------------------------------------------------------------
+??check_partial:
+ test [ flags ] , SHAPE_PARTIAL ; is this a partial pred?
+ jz ??setupfunc
+
+ mov eax , [ flags + edi ] ; pull the partial value
+ add edi , 4 ; next argument
+ and eax , 0ffh ; make sure 0-255
+ mov [ BFPartialPred ] , eax ; store it off
+
+??setupfunc:
+ mov ebx , [ jflags ] ; load flags value
+ and ebx , FLAG_MASK ; clip high bits
+ add ebx , ebx ; mult by 4 to get DWORD offset
+ add ebx , ebx
+ mov ebx , [ BufferFrameTable + ebx ] ; get table value
+ mov [ BufferFrameRout ] , ebx ; store it in the function pointer
+
+; Clip dest Rectangle against source Window boundaries.
+
+ mov [ scr_x ] , 0
+ mov [ scr_y ] , 0
+ mov esi , [ dest ] ; get ptr to dest
+ xor ecx , ecx
+ xor edx , edx
+ mov edi , [ (GraphicViewPort esi) . GVPWidth ] ; get width into register
+ mov ebx , [ x_pixel ]
+ mov eax , [ x_pixel ]
+ add ebx , [ pixel_width ]
+ shld ecx , eax , 1
+ mov [ x1_pixel ] , ebx
+ inc edi
+ shld edx , ebx , 1
+ sub eax , edi
+ sub ebx , edi
+ shld ecx , eax , 1
+ shld edx , ebx , 1
+
+ mov edi,[ ( GraphicViewPort esi) . GVPHeight ] ; get height into register
+ mov ebx , [ y_pixel ]
+ mov eax , [ y_pixel ]
+ add ebx , [ pixel_height ]
+ shld ecx , eax , 1
+ mov [ y1_pixel ] , ebx
+ inc edi
+ shld edx , ebx , 1
+ sub eax , edi
+ sub ebx , edi
+ shld ecx , eax , 1
+ shld edx , ebx , 1
+
+ xor cl , 5
+ xor dl , 5
+ mov al , cl
+ test dl , cl
+ jnz ??real_out
+
+ or al , dl
+ jz ??do_blit
+
+ mov [use_old_draw],1
+ test cl , 1000b
+ jz ??dest_left_ok
+
+ mov eax , [ x_pixel ]
+ neg eax
+ mov [ x_pixel ] , 0
+ mov [ scr_x ] , eax
+
+??dest_left_ok:
+ test cl , 0010b
+ jz ??dest_bottom_ok
+
+ mov eax , [ y_pixel ]
+ neg eax
+ mov [ y_pixel ] , 0
+ mov [ scr_y ] , eax
+
+??dest_bottom_ok:
+ test dl , 0100b
+ jz ??dest_right_ok
+
+ mov eax , [ (GraphicViewPort esi) . GVPWidth ] ; get width into register
+ mov [ x1_pixel ] , eax
+
+??dest_right_ok:
+ test dl , 0001b
+ jz ??do_blit
+
+ mov eax , [ (GraphicViewPort esi) . GVPHeight ] ; get width into register
+ mov [ y1_pixel ] , eax
+
+??do_blit:
+ cld
+ mov eax , [ (GraphicViewPort esi) . GVPXAdd ]
+ add eax , [ (GraphicViewPort esi) . GVPPitch ]
+ add eax , [ (GraphicViewPort esi) . GVPWidth ]
+ mov edi , [ (GraphicViewPort esi) . GVPOffset ]
+
+ mov ecx , eax
+ mul [ y_pixel ]
+ add edi , [ x_pixel ]
+ add edi , eax
+
+ add ecx , [ x_pixel ]
+ sub ecx , [ x1_pixel ]
+ mov [ dest_adjust_width ] , ecx
+
+ mov esi , [ src ]
+ mov eax , [ pixel_width ]
+ sub eax , [ x1_pixel ]
+ add eax , [ x_pixel ]
+ mov [ scr_adjust_width ] , eax
+
+ mov eax , [ scr_y ]
+ mul [ pixel_width ]
+ add eax , [ scr_x ]
+ add esi , eax
+
+;
+; If the shape needs to be clipped then we cant handle it with the new header systen
+; so draw it with the old shape drawer
+;
+ cmp [use_old_draw],0
+ jnz ??use_old_stuff
+
+ add [header_pointer],size ShapeHeaderType
+ mov edx,[pixel_height]
+ mov ecx,[pixel_width]
+ mov eax,[header_pointer]
+ mov al,[eax]
+ mov [save_ecx],ecx
+ inc [header_pointer]
+ and eax,BLIT_ALL
+ shl eax,2
+ add eax,[ShapeJumpTableAddress]
+ jmp [dword eax]
+
+
+??use_old_stuff:
+ mov edx , [ y1_pixel ]
+ mov eax , [ x1_pixel ]
+
+ sub edx , [ y_pixel ]
+ jle ??real_out
+
+ sub eax , [ x_pixel ]
+ jle ??real_out
+
+ jmp [ BufferFrameRout ] ; buffer frame to viewport routine
+
+??real_out:
+ epilogue
+
+ ret
+
+
+; ********************************************************************
+; Forward bitblit only
+; the inner loop is so efficient that
+; the optimal consept no longer apply because
+; the optimal byte have to by a number greather than 9 bytes
+; ********************************************************************
+global BF_Copy:near
+
+BF_Copy:
+ prologue
+ cmp eax , 10
+ jl ??forward_loop_bytes
+
+??forward_loop_dword:
+ mov ecx , edi
+ mov ebx , eax
+ neg ecx
+ and ecx , 3
+ sub ebx , ecx
+ rep movsb
+ mov ecx , ebx
+ shr ecx , 2
+ rep movsd
+ mov ecx , ebx
+ and ecx , 3
+ rep movsb
+
+ add esi , [ scr_adjust_width ]
+ add edi , [ dest_adjust_width ]
+ dec edx
+ jnz ??forward_loop_dword
+
+ ret
+
+??forward_loop_bytes:
+ mov ecx , eax
+ rep movsb
+ add esi , [ scr_adjust_width ]
+ add edi , [ dest_adjust_width ]
+ dec edx ; decrement the height
+ jnz ??forward_loop_bytes
+ epilogue
+
+ ret
+
+
+;********************************************************************
+;********************************************************************
+
+ segment code page public use32 'code' ; Need stricter segment alignment
+ ; for pentium optimisations
+
+
+;*****************************************************************************
+; Draw a single line with transparent pixels
+;
+; 11/29/95 10:21AM - ST
+;
+ align 32
+
+Single_Line_Trans:
+ prologue
+??slt_mask_map_lp: ; Pentium pipeline usage
+ ;Pipe Cycles
+ mov al,[esi] ;U 1
+ inc esi ;Vee 1
+
+ test al,al ;U 1
+ jz ??slt_skip ;Vee 1/5
+
+??slt_not_trans:mov [edi],al ;u 1
+
+ inc edi ;vee 1
+ dec ecx ;u 1
+
+ jnz ??slt_mask_map_lp ;vee (maybe) 1
+
+??slt_end_line: epilogue
+ next_line
+
+ align 32
+
+??slt_skip: inc edi
+ dec ecx
+ jz ??slt_skip_end_line
+
+ mov al,[esi]
+ inc esi
+ test al,al
+ jz ??slt_skip2
+ mov [edi],al
+ inc edi
+ dec ecx
+ jnz ??slt_mask_map_lp
+
+ epilogue
+ next_line
+
+ align 32
+
+??slt_skip2: inc edi
+ dec ecx
+ jz ??slt_end_line
+
+;
+; If we have hit two transparent pixels in a row then we go into
+; the transparent optimised bit
+;
+??slt_round_again:
+ rept 64
+ mov al,[esi] ; ;pipe 1
+ inc esi ;1 ;pipe 2
+ test al,al ; ;pipe 1
+ jnz ??slt_not_trans;pipe 2 (not pairable in 1)
+ ;2
+ inc edi ; ;pipe 1
+ dec ecx ;3 ;pipe 2
+ jz ??slt_end_line ;4 ;pipe 1 (not pairable)
+ endm ; best case is 4 cycles per iteration
+ jmp ??slt_round_again
+
+
+
+??slt_skip_end_line:
+ epilogue
+ next_line
+
+
+
+;*****************************************************************************
+; Draw a single line with no transparent pixels
+;
+; 11/29/95 10:21AM - ST
+;
+; We have to align the destination for cards that dont bankswitch correctly
+; when you write non-aligned data.
+;
+ align 32
+Long_Single_Line_Copy:
+ prologue
+
+ rept 3
+ test edi,3
+ jz ??LSLC_aligned
+ movsb
+ dec ecx
+ endm
+
+??LSLC_aligned:
+ mov ebx,ecx
+
+ shr ecx,2
+ rep movsd
+ and ebx,3
+ jz ??out
+ movsb
+ dec bl
+ jz ??out
+ movsb
+ dec bl
+ jz ??out
+ movsb
+??out: epilogue
+ next_line
+
+
+
+;*****************************************************************************
+; Draw a single short line with no transparent pixels
+;
+; 11/29/95 10:21AM - ST
+;
+ align 32
+Short_Single_Line_Copy:
+ prologue
+ cmp ecx,16
+ jge Long_Single_Line_Copy
+ mov ebx,ecx
+ rep movsb
+ mov ecx,ebx
+ epilogue
+ next_line
+
+
+;*****************************************************************************
+; Skip a line of source that is all transparent
+;
+; 11/29/95 10:21AM - ST
+;
+
+ align 32
+Single_Line_Skip:
+ prologue
+ add esi,ecx
+ add edi,ecx
+ epilogue
+ next_line
+
+
+
+;*****************************************************************************
+; Draw a single line with ghosting
+;
+; 11/29/95 10:21AM - ST
+;
+ align 32
+Single_Line_Ghost:
+
+ prologue
+ xor eax,eax
+??slg_loop: mov al,[esi]
+ mov ebx,[IsTranslucent]
+ inc esi
+ mov bh,[eax+ebx]
+ cmp bh,-1
+ jz ??slg_store_pixel
+
+ and ebx,0ff00h
+ mov al,[edi]
+ add ebx,[Translucent]
+ mov al,[eax+ebx]
+
+??slg_store_pixel:
+ mov [edi],al
+
+ inc edi
+ dec ecx
+ jnz ??slg_loop
+ epilogue
+ next_line
+
+
+
+;*****************************************************************************
+; Draw a single line with transparent pixels and ghosting
+;
+; 11/29/95 10:21AM - ST
+;
+ align 32
+Single_Line_Ghost_Trans:
+ prologue
+ xor eax,eax
+; cmp ecx,3
+; ja ??slgt4
+
+??slgt_loop: mov al,[esi]
+ inc esi
+ test al,al
+ jz ??slgt_transparent
+
+??slgt_not_transparent:
+ mov ebx,[IsTranslucent]
+ mov bh,[eax+ebx]
+ cmp bh,-1
+ jz ??slgt_store_pixel
+
+ and ebx,0ff00h
+ mov al,[edi]
+ add ebx,[Translucent]
+ mov al,[eax+ebx]
+
+??slgt_store_pixel:
+ mov [edi],al
+ inc edi
+ dec ecx
+ jnz ??slgt_loop
+ epilogue
+ next_line
+
+
+ align 32
+
+??slgt_transparent:
+ inc edi ;1
+ dec ecx ;2
+ jz ??slgt_out ;1 (not pairable)
+
+??slgt_round_again:
+ rept 64
+ mov al,[esi] ; ;pipe 1
+ inc esi ;1 ;pipe 2
+ test al,al ; ;pipe 1
+ jnz ??slgt_not_transparent ;pipe 2 (not pairable in 1)
+ ;2
+ inc edi ; ;pipe 1
+ dec ecx ;3 ;pipe 2
+ jz ??slgt_out ;4 ;pipe 1 (not pairable)
+ endm ; best case is 4 cycles per iteration
+ jmp ??slgt_round_again
+
+??slgt_out: epilogue
+ next_line
+
+
+
+;
+; Optimised video memory access version
+;
+ align 32
+
+??slgt4: push edx
+ mov edx,[edi]
+
+ rept 4
+ local slgt4_store1
+ local slgt4_trans1
+ mov al,[esi]
+ inc esi
+ test al,al
+ jz slgt4_trans1
+
+ mov ebx,[IsTranslucent]
+ mov bh,[eax+ebx]
+ cmp bh,-1
+ jz slgt4_store1
+
+ and ebx,0ff00h
+ mov al,dl
+ add ebx,[Translucent]
+ mov al,[eax+ebx]
+
+slgt4_store1: mov dl,al
+
+slgt4_trans1: ror edx,8
+ endm
+ mov [edi],edx
+ pop edx
+ lea edi,[edi+4]
+ lea ecx,[ecx+0fffffffch]
+ cmp ecx,3
+ ja ??slgt4
+ test ecx,ecx
+ jnz ??slgt_loop
+
+ epilogue
+ next_line
+
+
+
+
+
+
+
+
+
+
+;*****************************************************************************
+; Draw a single line with fading (colour remapping)
+;
+; 11/29/95 10:21AM - ST
+;
+ align 32
+
+Single_Line_Fading:
+ prologue
+ xor eax,eax
+ mov ebx,[FadingTable]
+ push ebp
+ mov ebp,[FadingNum]
+ push ebp
+
+??slf_loop: mov al,[esi]
+ inc esi
+
+ mov ebp,[esp]
+
+??slf_fade_loop:mov al,[ebx+eax]
+ dec ebp
+ jnz ??slf_fade_loop
+
+ mov [edi],al
+ inc edi
+
+ dec ecx
+ jnz ??slf_loop
+ add esp,4
+ pop ebp
+ epilogue
+ next_line
+
+
+;*****************************************************************************
+; Draw a single line with transparent pixels and fading (colour remapping)
+;
+; 11/29/95 10:21AM - ST
+;
+ align 32
+
+Single_Line_Fading_Trans:
+ prologue
+ xor eax,eax
+ mov ebx,[FadingTable]
+ push ebp
+ mov ebp,[FadingNum]
+ push ebp
+
+??slft_loop: mov al,[esi]
+ inc esi
+ test al,al
+ jz ??slft_transparent
+
+ mov ebp,[esp]
+
+??slft_fade_loop:
+ mov al,[ebx+eax]
+ dec ebp
+ jnz ??slft_fade_loop
+
+ mov [edi],al
+??slft_transparent:
+ inc edi
+
+ dec ecx
+ jnz ??slft_loop
+ add esp,4
+ pop ebp
+ epilogue
+ next_line
+
+
+
+
+
+;*****************************************************************************
+; Draw a single line with a single fade level (colour remap)
+;
+; 11/29/95 10:21AM - ST
+;
+ align 32
+
+Single_Line_Single_Fade:
+ prologue
+ xor eax,eax
+ mov ebx,[FadingTable]
+
+??slsf_loop: mov al,[esi]
+ mov al,[ebx+eax]
+ mov [edi],al
+ inc esi
+ inc edi
+
+ dec ecx
+ jnz ??slsf_loop
+ epilogue
+ next_line
+
+
+
+;*****************************************************************************
+; Draw a single line with transparent pixels and a single fade level (colour remap)
+;
+; 11/29/95 10:21AM - ST
+;
+ align 32
+
+Single_Line_Single_Fade_Trans:
+ prologue
+ xor eax,eax
+ mov ebx,[FadingTable]
+
+??slsft_loop: mov al,[esi]
+ inc esi
+ test al,al
+ jz ??slsft_transparent
+ mov al,[ebx+eax]
+ mov [edi],al
+ inc edi
+ dec ecx
+ jnz ??slsft_loop
+ epilogue
+ next_line
+
+ align 32
+
+??slsft_transparent:
+ inc edi
+
+ dec ecx
+ jz ??slsft_next_line
+ mov al,[esi]
+ inc esi
+ test al,al
+ jz ??slsft_transparent
+ mov al,[ebx+eax]
+ mov [edi],al
+ inc edi
+ dec ecx
+ jnz ??slsft_loop
+
+??slsft_next_line:
+ epilogue
+ next_line
+
+
+
+
+
+;*****************************************************************************
+; Draw a single line with ghosting and fading (colour remapping)
+;
+; 11/29/95 10:21AM - ST
+;
+ align 32
+
+Single_Line_Ghost_Fading:
+
+ prologue
+ mov [StashECX],ecx
+
+??SLGF_loop: xor eax,eax
+ mov al,[esi]
+ mov ebx,[IsTranslucent]
+ mov bh,[eax+ebx]
+ cmp bh,-1
+ jz ??slgf_do_fading
+
+ and ebx,0ff00h
+
+ mov al,[edi]
+ add ebx,[Translucent]
+ mov al,[ebx+eax]
+
+??slgf_do_fading:
+ mov ebx,[FadingTable]
+ mov ecx,[FadingNum]
+
+??slgf_fade_loop:
+ mov al,[eax+ebx]
+ dec ecx
+ jnz ??slgf_fade_loop
+
+ mov [edi],al
+ inc esi
+ inc edi
+
+ dec [StashECX]
+ jnz ??SLGF_loop
+ epilogue
+ next_line
+
+
+;*****************************************************************************
+; Draw a single line with transparent pixels, ghosting and fading
+;
+; 11/29/95 10:21AM - ST
+;
+ align 32
+
+Single_Line_Ghost_Fading_Trans:
+ prologue
+ mov [StashECX],ecx
+ xor eax,eax
+
+; cmp ecx,3
+; ja ??slgft4
+
+??SLGFT_loop: mov al,[esi]
+ inc esi
+ test al,al
+ jz ??slgft_trans_pixel
+ mov ebx,[IsTranslucent]
+ mov bh,[eax+ebx]
+ cmp bh,-1
+ jz ??slgft_do_fading
+
+ and ebx,0ff00h
+
+ mov al,[edi]
+ add ebx,[Translucent]
+ mov al,[ebx+eax]
+
+??slgft_do_fading:
+ mov ebx,[FadingTable]
+ mov ecx,[FadingNum]
+
+??slgft_fade_loop:
+ mov al,[eax+ebx]
+ dec ecx
+ jnz ??slgft_fade_loop
+
+ mov [edi],al
+??slgft_trans_pixel:
+ inc edi
+
+ dec [StashECX]
+ jnz ??SLGFT_loop
+ epilogue
+ next_line
+
+
+ align 32
+
+??slgft4: push edx
+ mov edx,[edi]
+
+ rept 4
+ local slgft4_fade
+ local slgft4_fade_lp
+ local slgft4_trans
+ mov al,[esi]
+ inc esi
+ test al,al
+ jz slgft4_trans
+ mov ebx,[IsTranslucent]
+ mov bh,[eax+ebx]
+ cmp bh,-1
+ jz slgft4_fade
+
+ and ebx,0ff00h
+
+ mov al,dl
+ add ebx,[Translucent]
+ mov al,[ebx+eax]
+
+slgft4_fade: mov ebx,[FadingTable]
+ mov ecx,[FadingNum]
+
+slgft4_fade_lp: mov al,[eax+ebx]
+ dec ecx
+ jnz slgft4_fade_lp
+
+ mov dl,al
+
+slgft4_trans: ror edx,8
+ endm
+ mov [edi],edx
+ pop edx
+ lea edi,[edi+4]
+ sub [StashECX],4
+ jz ??slgft4_out
+ cmp [StashECX],3
+ ja ??slgft4
+ jmp ??SLGFT_loop
+
+??slgft4_out: epilogue
+ next_line
+
+
+;*****************************************************************************
+; Draw a single line with predator effect
+;
+; 11/29/95 10:21AM - ST
+;
+ align 32
+
+Single_Line_Predator:
+
+ prologue
+
+??slp_loop: mov al,[esi]
+
+ mov ebx,[BFPartialCount]
+ add ebx,[BFPartialPred]
+ or bh,bh
+ jnz ??slp_get_pred
+
+ mov [BFPartialCount] , ebx
+ jmp ??slp_skip_pixel
+
+??slp_get_pred: xor bh , bh
+ mov eax,[BFPredOffset]
+ mov [BFPartialCount] , ebx
+ add [BYTE BFPredOffset],2
+ mov eax,[DWORD BFPredTable+eax]
+ and [BYTE BFPredOffset],PRED_MASK
+ and eax,0ffffh
+
+ mov al,[edi+eax]
+ mov [edi],al
+
+??slp_skip_pixel:
+ inc esi
+ inc edi
+
+ dec ecx
+ jnz ??slp_loop
+
+ epilogue
+ next_line
+
+
+
+
+;*****************************************************************************
+; Draw a single line with transparent pixels and predator effect
+;
+; 11/29/95 10:21AM - ST
+;
+ align 32
+
+Single_Line_Predator_Trans:
+
+ prologue
+
+??slpt_loop: mov al,[esi]
+ inc esi
+ test al,al
+ jz ??slpt_skip_pixel
+
+ mov ebx,[BFPartialCount]
+ add ebx,[BFPartialPred]
+ or bh,bh
+ jnz ??slpt_get_pred
+
+ mov [BFPartialCount] , ebx
+ jmp ??slpt_skip_pixel
+
+??slpt_get_pred:xor bh , bh
+ mov eax,[BFPredOffset]
+ mov [BFPartialCount] , ebx
+ add [BYTE BFPredOffset],2
+ mov eax,[DWORD BFPredTable+eax]
+ and [BYTE PTR BFPredOffset ] , PRED_MASK
+ and eax,0ffffh
+
+ mov al,[edi+eax]
+ mov [edi],al
+
+??slpt_skip_pixel:
+ inc edi
+
+ dec ecx
+ jnz ??slpt_loop
+
+ epilogue
+ next_line
+
+
+;*****************************************************************************
+; Draw a single line with predator and ghosting
+;
+; 11/29/95 10:21AM - ST
+;
+ align 32
+
+Single_Line_Predator_Ghost:
+
+ prologue
+
+??slpg_loop: mov al,[esi]
+ mov ebx,[BFPartialCount]
+ add ebx,[BFPartialPred]
+ test bh,bh
+ jnz ??slpg_get_pred ; is this a predator pixel?
+
+ mov [BFPartialCount],ebx
+ jmp ??slpg_check_ghost
+
+??slpg_get_pred:
+ xor bh,bh
+ mov eax,[BFPredOffset]
+ mov [BFPartialCount],ebx
+ add [BYTE BFPredOffset],2
+ mov eax,[DWORD BFPredTable+eax ]
+ and [BYTE BFPredOffset],PRED_MASK
+ and eax,0ffffh
+ mov al,[edi+eax]
+
+??slpg_check_ghost:
+ mov ebx,[IsTranslucent]
+ mov bh,[ebx+eax]
+ cmp bh,0ffh
+ je ??slpg_store_pixel
+
+ xor eax,eax
+ and ebx,0FF00h
+
+ mov al,[edi]
+ add ebx,[Translucent]
+
+ mov al,[ebx+eax]
+
+??slpg_store_pixel:
+ mov [edi],al
+ inc esi
+ inc edi
+
+ dec ecx
+ jnz ??slpg_loop
+
+ epilogue
+ next_line
+
+
+
+;*****************************************************************************
+; Draw a single line with transparent pixels, predator and ghosting
+;
+; 11/29/95 10:21AM - ST
+;
+ align 32
+
+Single_Line_Predator_Ghost_Trans:
+ prologue
+
+??slpgt_loop: mov al,[esi]
+ inc esi
+ test al,al
+ jz ??slpgt_transparent
+
+ mov ebx,[BFPartialCount]
+ add ebx,[BFPartialPred]
+ test bh,bh
+ jnz ??slpgt_get_pred ; is this a predator pixel?
+
+ mov [BFPartialCount],ebx
+ jmp ??slpgt_check_ghost
+
+??slpgt_get_pred:
+ xor bh,bh
+ mov eax,[BFPredOffset]
+ mov [BFPartialCount],ebx
+ add [BYTE BFPredOffset],2
+ mov eax,[DWORD BFPredTable+eax ]
+ and [BYTE BFPredOffset],PRED_MASK
+ and eax,0ffffh
+ mov al,[edi+eax]
+
+??slpgt_check_ghost:
+ mov ebx,[IsTranslucent]
+ mov bh,[ebx+eax]
+ cmp bh,0ffh
+ je ??slpgt_store_pixel
+
+ xor eax,eax
+ and ebx,0FF00h
+
+ mov al,[edi]
+ add ebx,[Translucent]
+
+ mov al,[ebx+eax]
+
+??slpgt_store_pixel:
+ mov [edi],al
+??slpgt_transparent:
+ inc edi
+
+ dec ecx
+ jnz ??slpgt_loop
+
+ pop ecx
+ epilogue
+ next_line
+
+
+;*****************************************************************************
+; Draw a single line with predator and fading
+;
+; 11/29/95 10:21AM - ST
+;
+ align 32
+
+Single_Line_Predator_Fading:
+
+ prologue
+ mov [StashECX],ecx
+
+??slpf_loop: mov al,[esi]
+ mov ebx,[BFPartialCount]
+ inc esi
+ add ebx,[BFPartialPred]
+ test bh,bh
+ jnz ??slpf_get_pred
+
+ mov [BFPartialCount],ebx
+ jmp ??slpf_do_fading
+
+??slpf_get_pred:xor bh,bh
+ mov eax,[BFPredOffset]
+ mov [BFPartialCount],ebx
+ and [BYTE BFPredOffset],2
+ mov eax,[DWORD BFPredTable+eax]
+ and [BYTE BFPredOffset],PRED_MASK
+
+ and eax,0ffffh
+ mov al,[eax+edi]
+
+??slpf_do_fading:
+ and eax,255
+ mov ebx,[FadingTable]
+ mov ecx,[FadingNum]
+
+??slpf_fade_loop:
+ mov al,[eax+ebx]
+ dec ecx
+ jnz ??slpf_fade_loop
+
+ mov [edi],al
+ inc edi
+
+ dec [StashECX]
+ jnz ??slpf_loop
+
+ epilogue
+ next_line
+
+
+
+;*****************************************************************************
+; Draw a single line with transparent pixels, fading and predator
+;
+; 11/29/95 10:21AM - ST
+;
+ align 32
+
+Single_Line_Predator_Fading_Trans:
+ prologue
+ mov [StashECX],ecx
+
+??slpft_loop: mov al,[esi]
+ inc esi
+ test al,al
+ jz ??slpft_transparent
+ mov ebx,[BFPartialCount]
+ add ebx,[BFPartialPred]
+ test bh,bh
+ jnz ??slpft_get_pred
+
+ mov [BFPartialCount],ebx
+ jmp ??slpft_do_fading
+
+??slpft_get_pred:
+ xor bh,bh
+ mov eax,[BFPredOffset]
+ mov [BFPartialCount],ebx
+ and [BYTE BFPredOffset],2
+ mov eax,[DWORD BFPredTable+eax]
+ and [BYTE BFPredOffset],PRED_MASK
+
+ and eax,0ffffh
+ mov al,[eax+edi]
+
+??slpft_do_fading:
+ and eax,255
+ mov ebx,[FadingTable]
+ mov ecx,[FadingNum]
+
+??slpft_fade_loop:
+ mov al,[eax+ebx]
+ dec ecx
+ jnz ??slpft_fade_loop
+
+ mov [edi],al
+??slpft_transparent:
+ inc edi
+
+ dec [StashECX]
+ jnz ??slpft_loop
+
+ epilogue
+ next_line
+
+
+
+;*****************************************************************************
+; Draw a single line with predator, ghosting and fading
+;
+; 11/29/95 10:21AM - ST
+;
+ align 32
+
+Single_Line_Predator_Ghost_Fading:
+
+ prologue
+ mov [StashECX],ecx
+
+??slpgf_loop: mov al,[esi]
+ mov ebx,[BFPartialCount]
+ inc esi
+ add ebx,[BFPartialPred]
+ test bh , bh
+ jnz ??slpgf_get_pred ; is this a predator pixel?
+
+ mov [BFPartialCount],ebx
+ jmp ??slpgf_check_ghost
+
+??slpgf_get_pred:
+ xor bh,bh
+ mov eax,[BFPredOffset]
+ mov [BFPartialCount],ebx
+ add [BYTE BFPredOffset],2
+ mov eax,[DWORD BFPredTable+eax]
+ and [BYTE BFPredOffset],PRED_MASK
+ and eax,0ffffh
+
+ mov al,[edi+eax]
+
+??slpgf_check_ghost:
+ and eax,255
+ mov ebx,[IsTranslucent]
+ mov bh,[ebx+eax]
+ cmp bh,0ffh
+ je ??slpgf_do_fading
+
+ and ebx , 0FF00h
+
+ mov al,[edi]
+ add ebx,[Translucent]
+
+ mov al,[ebx+eax]
+
+??slpgf_do_fading:
+ xor eax,eax
+ mov ebx,[FadingTable]
+ mov ecx,[FadingNum]
+
+??slpgf_fade_loop:
+ mov al,[ebx+eax]
+ dec ecx
+ jnz ??slpgf_fade_loop
+
+??slpgf_store_pixel:
+ mov [edi],al
+ inc edi
+
+ dec [StashECX]
+ jnz ??slpgf_loop
+
+ epilogue
+ next_line
+
+
+
+;*****************************************************************************
+; Draw a single line with transparent pixels, predator, ghosting and fading
+;
+; 11/29/95 10:21AM - ST
+;
+ align 32
+
+Single_Line_Predator_Ghost_Fading_Trans:
+
+ prologue
+ mov [StashECX],ecx
+
+??slpgft_loop: mov al,[esi]
+ inc esi
+ test al,al
+ jz ??slpgft_transparent
+
+ mov ebx,[BFPartialCount]
+ add ebx,[BFPartialPred]
+ test bh , bh
+ jnz ??slpgft_get_pred ; is this a predator pixel?
+
+ mov [BFPartialCount],ebx
+ jmp ??slpgft_check_ghost
+
+??slpgft_get_pred:
+ xor bh,bh
+ mov eax,[BFPredOffset]
+ mov [BFPartialCount],ebx
+ add [BYTE BFPredOffset],2
+ mov eax,[DWORD BFPredTable+eax]
+ and [BYTE BFPredOffset],PRED_MASK
+ and eax,0ffffh
+
+ mov al,[edi+eax]
+
+??slpgft_check_ghost:
+ and eax,255
+ mov ebx,[IsTranslucent]
+ mov bh,[ebx+eax]
+ cmp bh,0ffh
+ je ??slpgft_do_fading
+
+ and ebx , 0FF00h
+
+ mov al,[edi]
+ add ebx,[Translucent]
+
+ mov al,[ebx+eax]
+
+??slpgft_do_fading:
+ xor eax,eax
+ mov ebx,[FadingTable]
+ mov ecx,[FadingNum]
+
+??slpgft_fade_loop:
+ mov al,[ebx+eax]
+ dec ecx
+ jnz ??slpgft_fade_loop
+
+??slpgft_store_pixel:
+ mov [edi],al
+??slpgft_transparent:
+ inc edi
+
+ dec [StashECX]
+ jnz ??slpgft_loop
+
+ epilogue
+ next_line
+
+
+
+
+ ends ;end of strict alignment segment
+
+ codeseg
+
+
+
+global BF_Trans:near
+
+BF_Trans:
+
+ prologue
+; calc the code location to skip to 10 bytes per REPT below!!!!
+ mov ecx , eax
+ and ecx , 01fh
+ lea ecx , [ ecx + ecx * 4 ] ; quick multiply by 5
+ neg ecx
+ shr eax , 5
+ lea ecx , [ ??trans_reference + ecx * 2 ] ; next multiply by 2
+ mov [ loop_cnt ] , eax
+ mov [ jmp_loc ] , ecx
+
+??trans_loop:
+ mov ecx , [ loop_cnt ]
+ jmp [ jmp_loc ]
+
+; the following code should NOT be changed without changing the calculation
+; above!!!!!!
+
+??trans_line:
+
+ REPT 32
+ local trans_pixel
+ mov bl , [ esi ]
+ inc esi
+ test bl , bl
+ jz trans_pixel
+
+ mov [ edi ] , bl
+
+ trans_pixel:
+ inc edi
+ ENDM
+
+??trans_reference:
+ dec ecx
+ jge ??trans_line
+
+ add esi , [ scr_adjust_width ]
+ add edi , [ dest_adjust_width ]
+ dec edx
+ jnz ??trans_loop
+ epilogue
+
+ ret
+
+;********************************************************************
+;********************************************************************
+
+global BF_Ghost:near
+BF_Ghost:
+
+ prologue
+ mov ebx , eax ; width
+
+ ; NOTE: the below calculation assumes a group of instructions is
+ ; less than 256 bytes
+
+ ; get length of the 32 groups of instructions
+
+ lea ecx , [ ??ghost_reference - ??ghost_line ]
+
+ shr ebx , 5 ; width / 32
+ shr ecx , 5 ; length of instructions / 32
+ and eax , 01fh ; mod of width / 32
+ mul cl ; calc offset to start of group
+ neg eax ; inverse of width
+ mov [ loop_cnt ] , ebx ; save width / 32
+ lea ecx , [ ??ghost_reference + eax ]
+ mov eax , 0
+ mov [ jmp_loc ] , ecx
+
+??ghost_loop:
+ mov ecx , [ loop_cnt ]
+ jmp [ jmp_loc ]
+
+??ghost_line:
+
+ REPT 32
+ local store_pixel
+ mov al , [ esi ]
+ inc esi
+ mov ebx , [ IsTranslucent ] ; is it a translucent color?
+ mov bh , [ BYTE PTR ebx + eax ]
+ cmp bh , 0ffh
+ je store_pixel
+
+ and ebx , 0FF00h ; clear all of ebx except bh
+ ; we have the index to the translation table
+ ; ((trans_colour * 256) + dest colour)
+ mov al , [ edi ] ; mov pixel at destination to al
+ add ebx , [ Translucent ] ; get the ptr to it!
+ ; Add the (trans_color * 256) of the translation equ.
+ mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al
+
+ store_pixel:
+ mov [ edi ] , al
+ inc edi
+
+ ENDM
+
+??ghost_reference:
+ dec ecx
+ jge ??ghost_line
+
+ add esi , [ scr_adjust_width ]
+ add edi , [ dest_adjust_width ]
+ dec edx
+ jnz ??ghost_loop
+
+ epilogue
+ ret
+
+
+;********************************************************************
+;********************************************************************
+
+global BF_Ghost_Trans:near
+BF_Ghost_Trans:
+
+ prologue
+ mov ebx , eax ; width
+
+ ; NOTE: the below calculation assumes a group of instructions is
+ ; less than 256 bytes
+
+ ; get length of the 32 groups of instructions
+
+ lea ecx , [ ??ghost_t_reference - ??ghost_t_line ]
+
+ shr ebx , 5 ; width / 32
+ shr ecx , 5 ; length of instructions / 32
+ and eax , 01fh ; mod of width / 32
+ mul cl ; calc offset to start of group
+ neg eax ; inverse of width
+ mov [ loop_cnt ] , ebx ; save width / 32
+ lea ecx , [ ??ghost_t_reference + eax ]
+ mov eax , 0
+ mov [ jmp_loc ] , ecx
+
+??ghost_t_loop:
+ mov ecx , [ loop_cnt ]
+ jmp [ jmp_loc ]
+
+??ghost_t_line:
+
+ REPT 32
+ local transp_pixel
+ local store_pixel
+ mov al , [ esi ]
+ inc esi
+ test al , al
+ jz transp_pixel
+
+ mov ebx , [ IsTranslucent ] ; is it a translucent color?
+ mov bh , [ BYTE PTR ebx + eax ]
+ cmp bh , 0ffh
+ je store_pixel
+
+ and ebx , 0FF00h ; clear all of ebx except bh
+ ; we have the index to the translation table
+ ; ((trans_colour * 256) + dest colour)
+ mov al , [ edi ] ; mov pixel at destination to al
+ add ebx , [ Translucent ] ; get the ptr to it!
+ ; Add the (trans_color * 256) of the translation equ.
+ mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al
+
+ store_pixel:
+ mov [ edi ] , al
+
+ transp_pixel:
+ inc edi
+
+ ENDM
+
+??ghost_t_reference:
+ dec ecx
+ jge ??ghost_t_line
+
+ add esi , [ scr_adjust_width ]
+ add edi , [ dest_adjust_width ]
+ dec edx
+ jnz ??ghost_t_loop
+
+ epilogue
+ ret
+
+
+;********************************************************************
+;********************************************************************
+
+global BF_Fading:near
+BF_Fading:
+
+ prologue
+ mov ebx , eax ; width
+
+ ; NOTE: the below calculation assumes a group of instructions is
+ ; less than 256 bytes
+
+ ; get length of the 32 groups of instructions
+
+ lea ecx , [ ??fading_reference - ??fading_line ]
+
+ shr ebx , 5 ; width / 32
+ shr ecx , 5 ; length of instructions / 32
+ and eax , 01fh ; mod of width / 32
+ mul cl ; calc offset to start of group
+ neg eax ; inverse of width
+ mov [ loop_cnt ] , ebx ; save width / 32
+ lea ecx , [ ??fading_reference + eax ]
+ mov eax , 0
+ mov [ jmp_loc ] , ecx
+
+??fading_loop:
+ mov ecx , [ loop_cnt ]
+ mov [ StashECX ] , ecx ; preserve ecx for later
+ mov ebx , [ FadingTable ] ; run color through fading table
+ jmp [ jmp_loc ]
+
+??fading_line_begin:
+ mov [ StashECX ] , ecx ; preserve ecx for later
+
+??fading_line:
+
+ REPT 32
+ local fade_loop
+ mov al , [ esi ]
+ inc esi
+ mov ecx , [ FadingNum ]
+
+ fade_loop:
+ mov al, [BYTE PTR ebx + eax]
+ dec ecx
+ jnz fade_loop
+
+ mov [ edi ] , al
+ inc edi
+
+ ENDM
+
+??fading_reference:
+ mov ecx , [ StashECX ] ; restore ecx for main draw loop
+ dec ecx
+ jge ??fading_line_begin
+
+ add esi , [ scr_adjust_width ]
+ add edi , [ dest_adjust_width ]
+ dec edx
+ jnz ??fading_loop
+
+ epilogue
+ ret
+
+
+;********************************************************************
+;********************************************************************
+
+global BF_Fading_Trans:near
+BF_Fading_Trans:
+
+ prologue
+ mov ebx , eax ; width
+
+ ; NOTE: the below calculation assumes a group of instructions is
+ ; less than 256 bytes
+
+ ; get length of the 32 groups of instructions
+
+ lea ecx , [ ??fading_t_reference - ??fading_t_line ]
+
+ shr ebx , 5 ; width / 32
+ shr ecx , 5 ; length of instructions / 32
+ and eax , 01fh ; mod of width / 32
+ mul cl ; calc offset to start of group
+ neg eax ; inverse of width
+ mov [ loop_cnt ] , ebx ; save width / 32
+ lea ecx , [ ??fading_t_reference + eax ]
+ mov eax , 0
+ mov [ jmp_loc ] , ecx
+
+??fading_t_loop:
+ mov ecx , [ loop_cnt ]
+ mov [ StashECX ] , ecx ; preserve ecx for later
+ mov ebx , [ FadingTable ] ; run color through fading table
+ jmp [ jmp_loc ]
+
+??fading_t_line_begin:
+ mov [ StashECX ] , ecx ; preserve ecx for later
+
+??fading_t_line:
+
+ REPT 32
+ local transp_pixel
+ local fade_loop
+ mov al , [ esi ]
+ inc esi
+ test al , al
+ jz transp_pixel
+
+ mov ecx , [ FadingNum ]
+
+ fade_loop:
+ mov al, [BYTE PTR ebx + eax]
+ dec ecx
+ jnz fade_loop
+
+ mov [ edi ] , al
+
+ transp_pixel:
+ inc edi
+
+ ENDM
+
+??fading_t_reference:
+ mov ecx , [ StashECX ] ; restore ecx for main draw loop
+ dec ecx
+ jge ??fading_t_line_begin
+
+ add esi , [ scr_adjust_width ]
+ add edi , [ dest_adjust_width ]
+ dec edx
+ jnz ??fading_t_loop
+
+ epilogue
+ ret
+
+
+;********************************************************************
+;********************************************************************
+
+global BF_Ghost_Fading:near
+BF_Ghost_Fading:
+
+ prologue
+ mov ebx , eax ; width
+
+ ; NOTE: the below calculation assumes a group of instructions is
+ ; less than 256 bytes
+
+ ; get length of the 32 groups of instructions
+
+ lea ecx , [ ??ghost_f_reference - ??ghost_f_line ]
+
+ shr ebx , 5 ; width / 32
+ shr ecx , 5 ; length of instructions / 32
+ and eax , 01fh ; mod of width / 32
+ mul cl ; calc offset to start of group
+ neg eax ; inverse of width
+ mov [ loop_cnt ] , ebx ; save width / 32
+ lea ecx , [ ??ghost_f_reference + eax ]
+ mov eax , 0
+ mov [ jmp_loc ] , ecx
+
+??ghost_f_loop:
+ mov ecx , [ loop_cnt ]
+ mov [ StashECX ] , ecx ; preserve ecx for later
+ jmp [ jmp_loc ]
+
+??ghost_f_line_begin:
+ mov [ StashECX ] , ecx ; preserve ecx for later
+
+??ghost_f_line:
+
+ REPT 32
+ local store_pixel
+ local do_fading
+ local fade_loop
+ mov al , [ esi ]
+ inc esi
+ mov ebx , [ IsTranslucent ] ; is it a lucent color?
+ mov bh , [ BYTE PTR ebx + eax ]
+ cmp bh , 0ffh
+ je do_fading
+
+ and ebx , 0FF00h ; clear all of ebx except bh
+ ; we have the index to the lation table
+ ; ((_colour * 256) + dest colour)
+ mov al , [ edi ] ; mov pixel at destination to al
+ add ebx , [ Translucent ] ; get the ptr to it!
+ ; Add the (_color * 256) of the lation equ.
+ mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al
+; DRD jmp store_pixel
+
+ do_fading:
+ mov ebx , [ FadingTable ] ; run color through fading table
+ mov ecx , [ FadingNum ]
+
+ fade_loop:
+ mov al, [BYTE PTR ebx + eax]
+ dec ecx
+ jnz fade_loop
+
+ store_pixel:
+ mov [ edi ] , al
+ inc edi
+
+ ENDM
+
+??ghost_f_reference:
+ mov ecx , [ StashECX ] ; restore ecx for main draw loop
+ dec ecx
+ jge ??ghost_f_line_begin
+
+ add esi , [ scr_adjust_width ]
+ add edi , [ dest_adjust_width ]
+ dec edx
+ jnz ??ghost_f_loop
+
+ epilogue
+ ret
+
+
+;********************************************************************
+;********************************************************************
+
+global BF_Ghost_Fading_Trans:near
+BF_Ghost_Fading_Trans:
+
+ prologue
+ mov ebx , eax ; width
+
+ ; NOTE: the below calculation assumes a group of instructions is
+ ; less than 256 bytes
+
+ ; get length of the 32 groups of instructions
+
+ lea ecx , [ ??ghost_f_t_reference - ??ghost_f_t_line ]
+
+ shr ebx , 5 ; width / 32
+ shr ecx , 5 ; length of instructions / 32
+ and eax , 01fh ; mod of width / 32
+ mul cl ; calc offset to start of group
+ neg eax ; inverse of width
+ mov [ loop_cnt ] , ebx ; save width / 32
+ lea ecx , [ ??ghost_f_t_reference + eax ]
+ mov eax , 0
+ mov [ jmp_loc ] , ecx
+
+??ghost_f_t_loop:
+ mov ecx , [ loop_cnt ]
+ mov [ StashECX ] , ecx ; preserve ecx for later
+ jmp [ jmp_loc ]
+
+??ghost_f_t_line_begin:
+ mov [ StashECX ] , ecx ; preserve ecx for later
+
+??ghost_f_t_line:
+
+ REPT 32
+ local transp_pixel
+ local store_pixel
+ local do_fading
+ local fade_loop
+ mov al , [ esi ]
+ inc esi
+ test al , al
+ jz transp_pixel
+
+ mov ebx , [ IsTranslucent ] ; is it a translucent color?
+ mov bh , [ BYTE PTR ebx + eax ]
+ cmp bh , 0ffh
+ je do_fading
+
+ and ebx , 0FF00h ; clear all of ebx except bh
+ ; we have the index to the translation table
+ ; ((trans_colour * 256) + dest colour)
+ mov al , [ edi ] ; mov pixel at destination to al
+ add ebx , [ Translucent ] ; get the ptr to it!
+ ; Add the (trans_color * 256) of the translation equ.
+ mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al
+; DRD jmp store_pixel
+
+ do_fading:
+ mov ebx , [ FadingTable ] ; run color through fading table
+ mov ecx , [ FadingNum ]
+
+ fade_loop:
+ mov al, [BYTE PTR ebx + eax]
+ dec ecx
+ jnz fade_loop
+
+ store_pixel:
+ mov [ edi ] , al
+
+ transp_pixel:
+ inc edi
+
+ ENDM
+
+??ghost_f_t_reference:
+ mov ecx , [ StashECX ] ; restore ecx for main draw loop
+ dec ecx
+ jge ??ghost_f_t_line_begin
+
+ add esi , [ scr_adjust_width ]
+ add edi , [ dest_adjust_width ]
+ dec edx
+ jnz ??ghost_f_t_loop
+
+ epilogue
+ ret
+
+
+;********************************************************************
+;********************************************************************
+
+global BF_Predator:near
+BF_Predator:
+
+ prologue
+ mov ebx , eax ; width
+
+ ; NOTE: the below calculation assumes a group of instructions is
+ ; less than 256 bytes
+
+ ; get length of the 32 groups of instructions
+
+ lea ecx , [ ??predator_reference - ??predator_line ]
+
+ shr ebx , 5 ; width / 32
+ shr ecx , 5 ; length of instructions / 32
+ and eax , 01fh ; mod of width / 32
+ mul cl ; calc offset to start of group
+ neg eax ; inverse of width
+ mov [ loop_cnt ] , ebx ; save width / 32
+ lea ecx , [ ??predator_reference + eax ]
+ mov eax , 0
+ mov [ jmp_loc ] , ecx
+
+??predator_loop:
+ mov ecx , [ loop_cnt ]
+ jmp [ jmp_loc ]
+
+??predator_line:
+
+ REPT 32
+ local get_pred
+ local skip_pixel
+ mov al , [ esi ]
+ inc esi
+ mov ebx , [ BFPartialCount ]
+ add ebx , [ BFPartialPred ]
+ or bh , bh
+ jnz get_pred ; is this a predator pixel?
+
+ mov [ BFPartialCount ] , ebx
+ jmp skip_pixel
+
+ get_pred:
+ xor bh , bh
+ mov eax, [ BFPredOffset ]
+ mov [ BFPartialCount ] , ebx
+ add [ BYTE PTR BFPredOffset ] , 2
+ movzx eax , [ WORD PTR BFPredTable + eax ]
+ and [ BYTE PTR BFPredOffset ] , PRED_MASK
+ ; pick up a color offset a pseudo-
+ ; random amount from the current
+ movzx eax , [ BYTE PTR edi + eax ] ; viewport address
+
+; xor bh , bh
+; mov eax , [ BFPredValue ] ; pick up a color offset a pseudo-
+; ; random amount from the current
+; mov [ BFPartialCount ] , ebx
+; mov al , [ edi + eax ] ; viewport address
+
+ mov [ edi ] , al
+
+ skip_pixel:
+ inc edi
+
+ ENDM
+
+??predator_reference:
+ dec ecx
+ jge ??predator_line
+
+ add esi , [ scr_adjust_width ]
+ add edi , [ dest_adjust_width ]
+ dec edx
+ jnz ??predator_loop
+
+ epilogue
+ ret
+
+
+;********************************************************************
+;********************************************************************
+
+global BF_Predator_Trans:near
+BF_Predator_Trans:
+
+ prologue
+ mov ebx , eax ; width
+
+ ; NOTE: the below calculation assumes a group of instructions is
+ ; less than 256 bytes
+
+ ; get length of the 32 groups of instructions
+
+ lea ecx , [ ??predator_t_reference - ??predator_t_line ]
+
+ shr ebx , 5 ; width / 32
+ shr ecx , 5 ; length of instructions / 32
+ and eax , 01fh ; mod of width / 32
+ mul cl ; calc offset to start of group
+ neg eax ; inverse of width
+ mov [ loop_cnt ] , ebx ; save width / 32
+ lea ecx , [ ??predator_t_reference + eax ]
+ mov eax , 0
+ mov [ jmp_loc ] , ecx
+
+??predator_t_loop:
+ mov ecx , [ loop_cnt ]
+ jmp [ jmp_loc ]
+
+??predator_t_line:
+
+ REPT 32
+ local trans_pixel
+ local get_pred
+ local store_pixel
+ mov al , [ esi ]
+ inc esi
+ test al , al
+ jz trans_pixel
+
+ mov ebx , [ BFPartialCount ]
+ add ebx , [ BFPartialPred ]
+ or bh , bh
+ jnz get_pred ; is this a predator pixel?
+
+ mov [ BFPartialCount ] , ebx
+ jmp store_pixel
+
+ get_pred:
+ xor bh , bh
+ mov eax, [ BFPredOffset ]
+ mov [ BFPartialCount ] , ebx
+ add [ BYTE PTR BFPredOffset ] , 2
+ movzx eax , [ WORD PTR BFPredTable + eax ]
+ and [ BYTE PTR BFPredOffset ] , PRED_MASK
+ ; pick up a color offset a pseudo-
+ ; random amount from the current
+ movzx eax , [ BYTE PTR edi + eax ] ; viewport address
+
+ store_pixel:
+ mov [ edi ] , al
+
+ trans_pixel:
+ inc edi
+
+ ENDM
+
+??predator_t_reference:
+ dec ecx
+ jge ??predator_t_line
+
+ add esi , [ scr_adjust_width ]
+ add edi , [ dest_adjust_width ]
+ dec edx
+ jnz ??predator_t_loop
+
+ epilogue
+ ret
+
+
+;********************************************************************
+;********************************************************************
+
+global BF_Predator_Ghost:near
+BF_Predator_Ghost:
+
+ prologue
+ mov ebx , eax ; width
+
+ ; NOTE: the below calculation assumes a group of instructions is
+ ; less than 256 bytes
+
+ ; get length of the 32 groups of instructions
+
+ lea ecx , [ ??predator_g_reference - ??predator_g_line ]
+
+ shr ebx , 5 ; width / 32
+ shr ecx , 5 ; length of instructions / 32
+ and eax , 01fh ; mod of width / 32
+ mul cl ; calc offset to start of group
+ neg eax ; inverse of width
+ mov [ loop_cnt ] , ebx ; save width / 32
+ lea ecx , [ ??predator_g_reference + eax ]
+ mov eax , 0
+ mov [ jmp_loc ] , ecx
+
+??predator_g_loop:
+ mov ecx , [ loop_cnt ]
+ jmp [ jmp_loc ]
+
+??predator_g_line:
+
+ REPT 32
+ local get_pred
+ local check_ghost
+ local store_pixel
+ mov al , [ esi ]
+ mov ebx , [ BFPartialCount ]
+ inc esi
+ add ebx , [ BFPartialPred ]
+ or bh , bh
+ jnz get_pred ; is this a predator pixel?
+
+ mov [ BFPartialCount ] , ebx
+ jmp check_ghost
+
+ get_pred:
+ xor bh , bh
+ mov eax, [ BFPredOffset ]
+ mov [ BFPartialCount ] , ebx
+ add [ BYTE PTR BFPredOffset ] , 2
+ movzx eax , [ WORD PTR BFPredTable + eax ]
+ and [ BYTE PTR BFPredOffset ] , PRED_MASK
+ ; pick up a color offset a pseudo-
+ ; random amount from the current
+ movzx eax , [ BYTE PTR edi + eax ] ; viewport address
+
+ check_ghost:
+ mov ebx , [ IsTranslucent ] ; is it a translucent color?
+ mov bh , [ BYTE PTR ebx + eax ]
+ cmp bh , 0ffh
+ je store_pixel
+
+ and ebx , 0FF00h ; clear all of ebx except bh
+ ; we have the index to the translation table
+ ; ((trans_colour * 256) + dest colour)
+ mov al , [ edi ] ; mov pixel at destination to al
+ add ebx , [ Translucent ] ; get the ptr to it!
+ ; Add the (trans_color * 256) of the translation equ.
+ mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al
+
+ store_pixel:
+ mov [ edi ] , al
+ inc edi
+
+ ENDM
+
+??predator_g_reference:
+ dec ecx
+ jge ??predator_g_line
+
+ add esi , [ scr_adjust_width ]
+ add edi , [ dest_adjust_width ]
+ dec edx
+ jnz ??predator_g_loop
+
+ epilogue
+ ret
+
+
+;********************************************************************
+;********************************************************************
+
+global BF_Predator_Ghost_Trans:near
+BF_Predator_Ghost_Trans:
+
+ prologue
+ mov ebx , eax ; width
+
+ ; NOTE: the below calculation assumes a group of instructions is
+ ; less than 256 bytes
+
+ ; get length of the 32 groups of instructions
+
+ lea ecx , [ ??predator_g_t_reference - ??predator_g_t_line ]
+
+ shr ebx , 5 ; width / 32
+ shr ecx , 5 ; length of instructions / 32
+ and eax , 01fh ; mod of width / 32
+ mul cl ; calc offset to start of group
+ neg eax ; inverse of width
+ mov [ loop_cnt ] , ebx ; save width / 32
+ lea ecx , [ ??predator_g_t_reference + eax ]
+ mov eax , 0
+ mov [ jmp_loc ] , ecx
+
+??predator_g_t_loop:
+ mov ecx , [ loop_cnt ]
+ jmp [ jmp_loc ]
+
+??predator_g_t_line:
+
+ REPT 32
+ local trans_pixel
+ local get_pred
+ local check_ghost
+ local store_pixel
+ mov al , [ esi ]
+ inc esi
+ test al , al
+ jz trans_pixel
+
+ mov ebx , [ BFPartialCount ]
+ add ebx , [ BFPartialPred ]
+ or bh , bh
+ jnz get_pred ; is this a predator pixel?
+
+ mov [ BFPartialCount ] , ebx
+ jmp check_ghost
+
+ get_pred:
+ xor bh , bh
+ mov eax, [ BFPredOffset ]
+ mov [ BFPartialCount ] , ebx
+ add [ BYTE PTR BFPredOffset ] , 2
+ movzx eax , [ WORD PTR BFPredTable + eax ]
+ and [ BYTE PTR BFPredOffset ] , PRED_MASK
+ ; pick up a color offset a pseudo-
+ ; random amount from the current
+ movzx eax , [ BYTE PTR edi + eax ] ; viewport address
+
+ check_ghost:
+ mov ebx , [ IsTranslucent ] ; is it a translucent color?
+ mov bh , [ BYTE PTR ebx + eax ]
+ cmp bh , 0ffh
+ je store_pixel
+
+ and ebx , 0FF00h ; clear all of ebx except bh
+ ; we have the index to the translation table
+ ; ((trans_colour * 256) + dest colour)
+ mov al , [ edi ] ; mov pixel at destination to al
+ add ebx , [ Translucent ] ; get the ptr to it!
+ ; Add the (trans_color * 256) of the translation equ.
+ mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al
+
+ store_pixel:
+ mov [ edi ] , al
+
+ trans_pixel:
+ inc edi
+
+ ENDM
+
+??predator_g_t_reference:
+ dec ecx
+ jge ??predator_g_t_line
+
+ add esi , [ scr_adjust_width ]
+ add edi , [ dest_adjust_width ]
+ dec edx
+ jnz ??predator_g_t_loop
+
+ epilogue
+ ret
+
+
+;********************************************************************
+;********************************************************************
+
+global BF_Predator_Fading:near
+BF_Predator_Fading:
+
+ prologue
+ mov ebx , eax ; width
+
+ ; NOTE: the below calculation assumes a group of instructions is
+ ; less than 256 bytes
+
+ ; get length of the 32 groups of instructions
+
+ lea ecx , [ ??predator_f_reference - ??predator_f_line ]
+
+ shr ebx , 5 ; width / 32
+ shr ecx , 5 ; length of instructions / 32
+ and eax , 01fh ; mod of width / 32
+ mul cl ; calc offset to start of group
+ neg eax ; inverse of width
+ mov [ loop_cnt ] , ebx ; save width / 32
+ lea ecx , [ ??predator_f_reference + eax ]
+ mov eax , 0
+ mov [ jmp_loc ] , ecx
+
+??predator_f_loop:
+ mov ecx , [ loop_cnt ]
+ mov [ StashECX ] , ecx ; preserve ecx for later
+ jmp [ jmp_loc ]
+
+??predator_f_line_begin:
+ mov [ StashECX ] , ecx ; preserve ecx for later
+
+??predator_f_line:
+
+ REPT 32
+ local get_pred
+ local do_fading
+ local fade_loop
+ mov al , [ esi ]
+ mov ebx , [ BFPartialCount ]
+ inc esi
+ add ebx , [ BFPartialPred ]
+ or bh , bh
+ jnz get_pred ; is this a predator pixel?
+
+ mov [ BFPartialCount ] , ebx
+ jmp do_fading
+
+ get_pred:
+ xor bh , bh
+ mov eax, [ BFPredOffset ]
+ mov [ BFPartialCount ] , ebx
+ add [ BYTE PTR BFPredOffset ] , 2
+ movzx eax , [ WORD PTR BFPredTable + eax ]
+ and [ BYTE PTR BFPredOffset ] , PRED_MASK
+ ; pick up a color offset a pseudo-
+ ; random amount from the current
+ movzx eax , [ BYTE PTR edi + eax ] ; viewport address
+
+ do_fading:
+ mov ebx , [ FadingTable ] ; run color through fading table
+ mov ecx , [ FadingNum ]
+
+ fade_loop:
+ mov al, [BYTE PTR ebx + eax]
+ dec ecx
+ jnz fade_loop
+
+ mov [ edi ] , al
+ inc edi
+
+ ENDM
+
+??predator_f_reference:
+ mov ecx , [ StashECX ] ; restore ecx for main draw loop
+ dec ecx
+ jge ??predator_f_line_begin
+
+ add esi , [ scr_adjust_width ]
+ add edi , [ dest_adjust_width ]
+ dec edx
+ jnz ??predator_f_loop
+
+ epilogue
+ ret
+
+
+;********************************************************************
+;********************************************************************
+
+global BF_Predator_Fading_Trans:near
+BF_Predator_Fading_Trans:
+
+ prologue
+ mov ebx , eax ; width
+
+ ; NOTE: the below calculation assumes a group of instructions is
+ ; less than 256 bytes
+
+ ; get length of the 32 groups of instructions
+
+ lea ecx , [ ??predator_f_t_reference - ??predator_f_t_line ]
+
+ shr ebx , 5 ; width / 32
+ shr ecx , 5 ; length of instructions / 32
+ and eax , 01fh ; mod of width / 32
+ mul cl ; calc offset to start of group
+ neg eax ; inverse of width
+ mov [ loop_cnt ] , ebx ; save width / 32
+ lea ecx , [ ??predator_f_t_reference + eax ]
+ mov eax , 0
+ mov [ jmp_loc ] , ecx
+
+??predator_f_t_loop:
+ mov ecx , [ loop_cnt ]
+ mov [ StashECX ] , ecx ; preserve ecx for later
+ jmp [ jmp_loc ]
+
+??predator_f_t_line_begin:
+ mov [ StashECX ] , ecx ; preserve ecx for later
+
+??predator_f_t_line:
+
+ REPT 32
+ local trans_pixel
+ local get_pred
+ local do_fading
+ local fade_loop
+ mov al , [ esi ]
+ inc esi
+ test al , al
+ jz trans_pixel
+
+ mov ebx , [ BFPartialCount ]
+ add ebx , [ BFPartialPred ]
+ or bh , bh
+ jnz get_pred ; is this a predator pixel?
+
+ mov [ BFPartialCount ] , ebx
+ jmp do_fading
+
+ get_pred:
+ xor bh , bh
+ mov eax, [ BFPredOffset ]
+ mov [ BFPartialCount ] , ebx
+ add [ BYTE PTR BFPredOffset ] , 2
+ movzx eax , [ WORD PTR BFPredTable + eax ]
+ and [ BYTE PTR BFPredOffset ] , PRED_MASK
+ ; pick up a color offset a pseudo-
+ ; random amount from the current
+ movzx eax , [ BYTE PTR edi + eax ] ; viewport address
+
+ do_fading:
+ mov ebx , [ FadingTable ] ; run color through fading table
+ mov ecx , [ FadingNum ]
+
+ fade_loop:
+ mov al, [BYTE PTR ebx + eax]
+ dec ecx
+ jnz fade_loop
+
+ mov [ edi ] , al
+
+ trans_pixel:
+ inc edi
+
+ ENDM
+
+??predator_f_t_reference:
+ mov ecx , [ StashECX ] ; restore ecx for main draw loop
+ dec ecx
+ jge ??predator_f_t_line_begin
+
+ add esi , [ scr_adjust_width ]
+ add edi , [ dest_adjust_width ]
+ dec edx
+ jnz ??predator_f_t_loop
+
+ epilogue
+ ret
+
+
+;********************************************************************
+;********************************************************************
+
+global BF_Predator_Ghost_Fading:near
+BF_Predator_Ghost_Fading:
+
+ prologue
+ mov ebx , eax ; width
+
+ ; NOTE: the below calculation assumes a group of instructions is
+ ; less than 256 bytes
+
+ ; get length of the 32 groups of instructions
+
+ lea ecx , [ ??predator_g_f_reference - ??predator_g_f_line ]
+
+ shr ebx , 5 ; width / 32
+ shr ecx , 5 ; length of instructions / 32
+ and eax , 01fh ; mod of width / 32
+ mul cl ; calc offset to start of group
+ neg eax ; inverse of width
+ mov [ loop_cnt ] , ebx ; save width / 32
+ lea ecx , [ ??predator_g_f_reference + eax ]
+ mov eax , 0
+ mov [ jmp_loc ] , ecx
+
+??predator_g_f_loop:
+ mov ecx , [ loop_cnt ]
+ mov [ StashECX ] , ecx ; preserve ecx for later
+ jmp [ jmp_loc ]
+
+??predator_g_f_line_begin:
+ mov [ StashECX ] , ecx ; preserve ecx for later
+
+??predator_g_f_line:
+
+ REPT 32
+ local get_pred
+ local check_ghost
+ local store_pixel
+ local do_fading
+ local fade_loop
+ mov al , [ esi ]
+ mov ebx , [ BFPartialCount ]
+ inc esi
+ add ebx , [ BFPartialPred ]
+ or bh , bh
+ jnz get_pred ; is this a predator pixel?
+
+ mov [ BFPartialCount ] , ebx
+ jmp check_ghost
+
+ get_pred:
+ xor bh , bh
+ mov eax, [ BFPredOffset ]
+ mov [ BFPartialCount ] , ebx
+ add [ BYTE PTR BFPredOffset ] , 2
+ movzx eax , [ WORD PTR BFPredTable + eax ]
+ and [ BYTE PTR BFPredOffset ] , PRED_MASK
+ ; pick up a color offset a pseudo-
+ ; random amount from the current
+ movzx eax , [ BYTE PTR edi + eax ] ; viewport address
+
+ check_ghost:
+ mov ebx , [ IsTranslucent ] ; is it a translucent color?
+ mov bh , [ BYTE PTR ebx + eax ]
+ cmp bh , 0ffh
+ je do_fading
+
+ and ebx , 0FF00h ; clear all of ebx except bh
+ ; we have the index to the translation table
+ ; ((trans_colour * 256) + dest colour)
+ mov al , [ edi ] ; mov pixel at destination to al
+ add ebx , [ Translucent ] ; get the ptr to it!
+ ; Add the (trans_color * 256) of the translation equ.
+ mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al
+; DRD jmp store_pixel
+
+ do_fading:
+ mov ebx , [ FadingTable ] ; run color through fading table
+ mov ecx , [ FadingNum ]
+
+ fade_loop:
+ mov al, [BYTE PTR ebx + eax]
+ dec ecx
+ jnz fade_loop
+
+ store_pixel:
+ mov [ edi ] , al
+ inc edi
+
+ ENDM
+
+??predator_g_f_reference:
+ mov ecx , [ StashECX ] ; restore ecx for main draw loop
+ dec ecx
+ jge ??predator_g_f_line_begin
+
+ add esi , [ scr_adjust_width ]
+ add edi , [ dest_adjust_width ]
+ dec edx
+ jnz ??predator_g_f_loop
+
+ epilogue
+ ret
+
+
+;********************************************************************
+;********************************************************************
+
+global BF_Predator_Ghost_Fading_Trans:near
+BF_Predator_Ghost_Fading_Trans:
+
+ prologue
+ mov ebx , eax ; width
+
+ ; NOTE: the below calculation assumes a group of instructions is
+ ; less than 256 bytes
+
+ ; get length of the 32 groups of instructions
+
+ lea ecx , [ ??predator_g_f_t_reference - ??predator_g_f_t_line ]
+
+ shr ebx , 5 ; width / 32
+ shr ecx , 5 ; length of instructions / 32
+ and eax , 01fh ; mod of width / 32
+ mul cl ; calc offset to start of group
+ neg eax ; inverse of width
+ mov [ loop_cnt ] , ebx ; save width / 32
+ lea ecx , [ ??predator_g_f_t_reference + eax ]
+ mov eax , 0
+ mov [ jmp_loc ] , ecx
+
+??predator_g_f_t_loop:
+ mov ecx , [ loop_cnt ]
+ mov [ StashECX ] , ecx ; preserve ecx for later
+ jmp [ jmp_loc ]
+
+??predator_g_f_t_line_begin:
+ mov [ StashECX ] , ecx ; preserve ecx for later
+
+??predator_g_f_t_line:
+
+ REPT 32
+ local trans_pixel
+ local get_pred
+ local check_ghost
+ local store_pixel
+ local do_fading
+ local fade_loop
+ mov al , [ esi ]
+ inc esi
+ test al , al
+ jz trans_pixel
+
+ mov ebx , [ BFPartialCount ]
+ add ebx , [ BFPartialPred ]
+ or bh , bh
+ jnz get_pred ; is this a predator pixel?
+
+ mov [ BFPartialCount ] , ebx
+ jmp check_ghost
+
+ get_pred:
+ xor bh , bh
+ mov eax, [ BFPredOffset ]
+ mov [ BFPartialCount ] , ebx
+ add [ BYTE PTR BFPredOffset ] , 2
+ movzx eax , [ WORD PTR BFPredTable + eax ]
+ and [ BYTE PTR BFPredOffset ] , PRED_MASK
+ ; pick up a color offset a pseudo-
+ ; random amount from the current
+ movzx eax , [ BYTE PTR edi + eax ] ; viewport address
+
+ check_ghost:
+ mov ebx , [ IsTranslucent ] ; is it a translucent color?
+ mov bh , [ BYTE PTR ebx + eax ]
+ cmp bh , 0ffh
+ je do_fading
+
+ and ebx , 0FF00h ; clear all of ebx except bh
+ ; we have the index to the translation table
+ ; ((trans_colour * 256) + dest colour)
+ mov al , [ edi ] ; mov pixel at destination to al
+ add ebx , [ Translucent ] ; get the ptr to it!
+ ; Add the (trans_color * 256) of the translation equ.
+ mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al
+; DRD jmp store_pixel
+
+ do_fading:
+ mov ebx , [ FadingTable ] ; run color through fading table
+ mov ecx , [ FadingNum ]
+
+ fade_loop:
+ mov al, [BYTE PTR ebx + eax]
+ dec ecx
+ jnz fade_loop
+
+ store_pixel:
+ mov [ edi ] , al
+
+ trans_pixel:
+ inc edi
+
+ ENDM
+
+??predator_g_f_t_reference:
+ mov ecx , [ StashECX ] ; restore ecx for main draw loop
+ dec ecx
+ jge ??predator_g_f_t_line_begin
+
+ add esi , [ scr_adjust_width ]
+ add edi , [ dest_adjust_width ]
+ dec edx
+ jnz ??predator_g_f_t_loop
+
+ epilogue
+ ret
+
+
+;********************************************************************
+;********************************************************************
+
+Not_Supported:
+ ret
+
+ ENDP Buffer_Frame_To_Page
+END
+
+;***************************************************************************
+;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S **
+;***************************************************************************
+;* *
+;* Project Name : Westwood Library *
+;* *
+;* File Name : KEYFBUFF.ASM *
+;* *
+;* Programmer : Phil W. Gorrow *
+;* *
+;* Start Date : July 16, 1992 *
+;* *
+;* Last Update : October 2, 1994 [JLB] *
+;* *
+;*-------------------------------------------------------------------------*
+;* Functions: *
+;* BUFFER_FRAME_TO_LOGICPAGE -- *
+;* Normal_Draw -- Function that writes a normal pixel line *
+;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *
+
+ IDEAL
+ P386
+IDEAL_MODE EQU 1
+ INCLUDE "wwlib.i"
+
+ ;-------------------------------------------------------------------
+ ; Extern all the library variables that this module requires
+ ;-------------------------------------------------------------------
+
+ EXTRN C MaskPage:WORD
+ EXTRN C BackGroundPage:WORD
+
+ ;-------------------------------------------------------------------
+ ; Define all the equates that this module requires
+ ;-------------------------------------------------------------------
+
+WIN_X EQU 0 ; offset for the x coordinate
+WIN_Y EQU 2 ; offset for the y coordinate
+WIN_WIDTH EQU 4 ; offset for the window width
+WIN_HEIGHT EQU 6 ; offset for the window height
+BYTESPERROW EQU 320 ; number of bytes per row
+
+FLAG_NORMAL EQU 0 ; flag for normal draw
+
+FLAG_GHOST EQU 1 ; This flag enables the ghost
+FLAG_PRIORITY_TRANS EQU 2 ; flag for priority and transparent
+FLAG_TRANS EQU 4 ; flag for transparent draw
+FLAG_PRIORITY EQU 8 ; flag for priority draw
+
+ ; fx on the above flags
+
+FLAG_MASK EQU 15 ; used to and of uneeded bits
+
+SHAPE_NORMAL EQU 0000h ; Standard shape.
+;SHAPE_HORZ_REV EQU 0001h ; Flipped horizontally.
+;SHAPE_VERT_REV EQU 0002h ; Flipped vertically.
+;SHAPE_SCALING EQU 0004h ; Scaled (WORD scale_x, WORD scale_y)
+
+SHAPE_WIN_REL EQU 0010h ; Coordinates are window relative instead of absolute.
+SHAPE_CENTER EQU 0020h ; Coordinates are based on shape's center point.
+SHAPE_TRANS EQU 0040h ; has transparency
+
+
+;SHAPE_FADING EQU 0100h ; Fading effect active (VOID * fading_table, WORD fading_num).
+;SHAPE_PREDATOR EQU 0200h ; Transparent warping effect.
+;SHAPE_COMPACT EQU 0400h ; Never use this bit.
+SHAPE_PRIORITY EQU 0800h ; Use priority system when drawing.
+
+SHAPE_GHOST EQU 1000h ; Transluscent table process.
+;SHAPE_SHADOW EQU 2000h ; ????
+;SHAPE_PARTIAL EQU 4000h ; ????
+;SHAPE_COLOR EQU 8000h ; Remap the shape's colors (VOID * color_table).
+
+
+; MBL MOD 12.1.92
+
+CLEAR_NON_WALK_BIT_AND_SCALE_BITS EQU 7 ; Makes it one AND per pixel in Priority_Trans display
+CLEAR_NON_WALK_BIT EQU 7fh ; and with 0111-1111 to clear non-walkable high bit
+CLEAR_SCALE_BITS EQU 87h ; and with 1000-0111 to clear scaling id bits
+NON_WALKABLE_BIT EQU 80h ; and with 1000-0000 to clear all but non-walkable bit
+
+; END MBL MOD
+
+
+ CODESEG
+
+ ; 1 = GHOST (all odd entrys are prefixed with Ghost_)
+ ; 2 = BLAAAH
+ ; 4 = Trans (prfx)
+ ; 8 = Prior (prfx)
+
+
+;---------------------------------------------------------------------------
+; Define the table of different line draw types
+;---------------------------------------------------------------------------
+
+LineTable DW WSA_Normal_Draw ;0
+ DW Ghost_Normal_Draw ;1
+ DW 0 ;2
+ DW 0 ;3
+
+ DW Transparent_Draw ;4
+ DW Ghost_Transparent_Draw ;5
+ DW 0 ;6
+ DW 0 ;7
+
+ DW Priority_Draw ;8
+ DW Ghost_Priority_Draw ;9
+ DW 0 ;10
+ DW 0 ;11
+
+ DW Priority_Transparent_Draw ;12
+ DW Ghost_Priority_Transparent_Draw ;13
+ DW 0 ;14
+ DW 0 ;15
+
+
+
+;***************************************************************************
+;* BUFFER_FRAME_TO_LOGICPAGE -- *
+;* *
+;* *
+;* *
+;* INPUT: *
+;* *
+;* OUTPUT: *
+;* *
+;* WARNINGS: *
+;* *
+;* HISTORY: *
+;* 07/16/1992 PWG : Created. *
+;*=========================================================================*
+ PUBLIC C Buffer_Frame_To_LogicPage
+ PROC C Buffer_Frame_To_LogicPage FAR USES ax bx ecx dx ds esi es edi
+
+ ;-------------------------------------------------------------------
+ ; Define the arguements that our program takes.
+ ;-------------------------------------------------------------------
+
+ ARG x_pixel:WORD ; x pixel position to draw at
+ ARG y_pixel:WORD ; y pixel position to draw at
+ ARG pixel_w:WORD ; pixel width of draw region
+ ARG pixel_h:WORD ; pixel height of draw region
+ ARG win:WORD ; window to clip around
+ ARG flags:WORD ; flags that this routine will take
+ ARG buffer:DWORD ; pointer to the buffer with data
+ ARG args:WORD
+
+ ;-------------------------------------------------------------------
+ ; Define the local variables that our program uses
+ ;-------------------------------------------------------------------
+
+ LOCAL IsTranslucent:DWORD ; ptr to the is_translucent table
+ LOCAL Translucent:DWORD ; ptr to the actual translucent table
+
+ LOCAL win_x1:WORD ; clip window left x pixel position
+ LOCAL win_x2:WORD ; clip window right x pixel position
+ LOCAL win_y1:WORD ; clip window top y pixel position
+ LOCAL win_y2:WORD ; clip window bottom y pixel position
+ LOCAL clipleft:WORD ; number of pixels to clip on left
+ LOCAL clipright:WORD ; number of pixels to clip on right
+ LOCAL nextline:WORD ; offset to the next line
+ LOCAL putmiddle:WORD ; routine to call to put the middle
+ LOCAL maskpage:WORD ; location of the depth masks
+ LOCAL background:WORD ; location of the background data
+ LOCAL jflags:WORD ; location of the background data
+
+ LOCAL priority:BYTE ; the priority level of the back
+
+ push fs
+
+ xor ecx,ecx
+
+ ;--------------------------------------------------------------------
+ ; Check to see if we have supplied any GHOST tables.
+ ;--------------------------------------------------------------------
+ push di
+
+ mov di,6
+ mov [jflags],0
+
+??ghost:
+ test [flags],SHAPE_GHOST ; are we ghosting this shape
+ jz short ??no_ghost ; if not then skip and do more
+
+ or [jflags],FLAG_GHOST
+
+ les ax,[DWORD PTR buffer + di]
+
+ ; get the "are we really translucent?" table
+ mov [WORD PTR IsTranslucent],ax
+ mov [WORD PTR IsTranslucent + 2],es
+ add ax,0100h ; add to offset for tables
+
+ ; get the "ok we are translucent!!" table
+ mov [WORD PTR Translucent],ax
+ mov [WORD PTR Translucent + 2],es
+
+ add di,4
+
+??no_ghost:
+
+ pop di
+
+ ;-------------------------------------------------------------------
+ ; See if we need to center the frame
+ ;-------------------------------------------------------------------
+ test [flags],SHAPE_CENTER ; does this need to be centered?
+ je short ??no_centering ; if not the skip over this stuff
+
+ mov ax,[pixel_w]
+ mov bx,[pixel_h]
+ sar ax,1
+ sar bx,1
+ sub [x_pixel],ax
+ sub [y_pixel],bx
+
+??no_centering:
+ mov ax,[flags]
+ and ax,SHAPE_PRIORITY+SHAPE_TRANS
+ cmp ax,SHAPE_PRIORITY+SHAPE_TRANS
+ jne short ??test_trans
+
+ or [jflags],FLAG_PRIORITY_TRANS
+ jmp short ??priority
+
+ ;-------------------------------------------------------------------
+ ; Get the trans information if we need to get it
+ ;-------------------------------------------------------------------
+??test_trans:
+ test [flags],SHAPE_TRANS ; does this draw use transparencies?
+ je short ??test_priority ; if not the skip over this junk
+
+ or [jflags],FLAG_TRANS
+
+??test_priority:
+ ;-------------------------------------------------------------------
+ ; Get the priority information if we need to get it
+ ;-------------------------------------------------------------------
+ test [flags],SHAPE_PRIORITY ; does this draw use priorities?
+ je short ??no_priority ; if not the skip over this junk
+
+ or [jflags],FLAG_PRIORITY
+
+??priority:
+ mov ax,[BackGroundPage] ; get the background page from ds
+ mov [background],ax ; and store it on the stack
+ mov ax,[MaskPage] ; get the mask page from ds
+ mov [maskpage],ax ; and store it on the stack
+ mov ax,[WORD PTR buffer + 4]; get the priority level from args
+ mov [priority],al ; and store it in a local
+
+ ;-------------------------------------------------------------------
+ ; Get the draw routine that we are going to draw with
+ ;-------------------------------------------------------------------
+??no_priority:
+; mov bx,[flags] ; load in the current flags byte
+; and bx,FLAG_MASK ; prevent lockup on bad value
+ mov bx,[jflags] ; load in the jump table flags
+ shl bx,1
+ mov ax,[WORD PTR LineTable + bx] ; get the offset of the skip table
+ mov [putmiddle],ax ; store off the new offset
+
+ ;-------------------------------------------------------------------
+ ; Get a pointer to the logic page to where we will draw our buffer
+ ;-------------------------------------------------------------------
+ push [LogicPage] ; push the current logic page
+ call FAR PTR Get_Page ; get the physical page address
+ add sp,2 ; pull the parameter from the stack
+ mov es,dx ; store the address in the dest
+
+ ;--------------------------------------------------------------------
+ ; Point DI to the beginning of the window that we need to look at.
+ ; that way we can access all of the info through di.
+ ;--------------------------------------------------------------------
+ mov si,OFFSET WindowList ; get the offset of the window list
+ mov cl,4 ; shift 3 times = multiply by 16
+ mov ax,[win] ; get the window number we are using
+ shl ax,cl ; each window is 8 words long
+ add si,ax ; add that into the offset of window
+
+ ;--------------------------------------------------------------------
+ ; Place all the clipping values on the stack so our function will
+ ; be truly re-entrant and will not need to shadow these values.
+ ;--------------------------------------------------------------------
+ mov cl,3 ; to convert x to pixel mult by 8
+ mov ax,[si + WIN_X] ; get the left clip position
+ shl ax,cl ; convert to a pixel x position
+ mov [win_x1],ax ; store the left edge of window
+ mov [win_x2],ax
+
+ mov ax,[si + WIN_WIDTH] ; get the width of the window
+ shl ax,cl ; convert to a pixel width
+ add [win_x2],ax ; add to get the right window edge
+
+ mov ax,[si + WIN_Y] ; get the win y coordinate to clip
+ mov [win_y1],ax ; and save it onto the stack
+
+ add ax,[si + WIN_HEIGHT] ; calculate the bottom win y coord
+ mov [win_y2],ax ; and save it onto the stack
+
+ test [flags],SHAPE_WIN_REL ; is this window relative?
+ je short ??get_buffer ; if not the skip over
+
+ mov ax,[win_x1] ; get left edge of window
+ add [x_pixel],ax ; add to x pixel position
+ mov ax,[win_y1] ; get top edge of window
+ add [y_pixel],ax ; add to y pixel position
+
+ ;--------------------------------------------------------------------
+ ; Get a pointer to the source buffer so we can handle the clipping
+ ;--------------------------------------------------------------------
+??get_buffer:
+ lds si,[buffer] ; get a pointer to the buffer
+
+ ;--------------------------------------------------------------------
+ ; Check the top of our shape and clip any lines that are necessary
+ ;--------------------------------------------------------------------
+ mov ax,[y_pixel] ; get the y_pixel draw position
+ sub ax,[win_y1] ; subtract out the window y top
+ jns short ??check_bottom ; skip if y below window top
+ add ax,[pixel_h] ; add in the height of the region
+ jg short ??clip_top ; if positive then clip top lines
+
+??jump_exit:
+ jmp ??exit ; otherwise completely clipped
+
+??clip_top:
+ xchg [pixel_h],ax
+ sub ax,[pixel_h]
+ add [y_pixel],ax
+ mul [pixel_w] ; convert to number of bytes to skip
+ add si,ax ; skip past the necessary bytes
+
+ ;--------------------------------------------------------------------
+ ; Check the bottom of our shape and clip it if necessary
+ ;--------------------------------------------------------------------
+??check_bottom:
+ mov ax,[win_y2] ; get the bottom y of the window
+ sub ax,[y_pixel] ; subtract of the y to draw at
+ js ??jump_exit ; if its signed then nothing to draw
+ jz ??jump_exit ; if its zero then nothing to draw
+
+ cmp ax,[pixel_h] ; if more room to draw then height
+ jae short ??clip_x_left ; then go check the left clip
+ mov [pixel_h],ax ; clip all but amount that will fit
+
+??clip_x_left:
+ mov [clipleft],0 ; clear clip on left of region
+ mov ax,[x_pixel] ; get the pixel x of draw region
+ sub ax,[win_x1] ; pull out the window coordinate
+ jns short ??clip_x_right
+ neg ax ; negate to get amnt to skip in buf
+ mov [clipleft],ax ; store it in the left clip info
+ add [x_pixel],ax ; move to the edge of the window
+ sub [pixel_w],ax ; pull it out of the pixel width
+
+??clip_x_right:
+ mov [clipright],0 ; clear clip on right of region
+ mov ax,[win_x2] ; get the window x of clip region
+ sub ax,[x_pixel] ; subtract the draw edge of region
+ js ??jump_exit ; if its negative then get out
+ jz ??jump_exit ; if its zero then get out
+
+ cmp ax,[pixel_w] ; is space available larger than w
+ jae short ??draw_prep ; if so then go get drawing
+
+
+ xchg [pixel_w],ax ; amt to draw in pixel_w (wid in ax)
+ sub ax,[pixel_w] ; pull out the amount to draw
+ mov [clipright],ax ; this is the amount to clip on right
+
+??draw_prep:
+ push si ; save off source pos in buffer
+ push ds ; both offset and segment
+ mov ax,@data
+ mov ds,ax
+ mov bx,[y_pixel]
+ shl bx,1 ; shift left by 1 for word table look
+ lds si,[YTable] ; get the address of the ytable
+ mov di,[ds:si+bx] ; look up the multiplied value
+ pop ds ; restore source pos in buffer
+ pop si ; both offset and segment
+
+ add di,[x_pixel] ; add in the x pixel position
+ mov [nextline],di ; save it off in the next line
+
+ ;--------------------------------------------------------------------
+ ; Now determine the type of the shape and process it in the proper
+ ; way.
+ ;--------------------------------------------------------------------
+ mov dx,[pixel_h]
+
+ ; Check to see if the WSA is the screen width and there is no
+ ; clipping. In this case, then a special single call to the
+ ; line processing routine is possible.
+ mov ax,[clipleft]
+ add ax,[clipright]
+ jne short ??top_of_loop
+ cmp [pixel_w],BYTESPERROW
+ jne short ??top_of_loop
+
+ ;------------------------------------
+ ; The width of the WSA is the screen width, so just process as
+ ; one large WSA line.
+ mov ax,BYTESPERROW
+ imul dx
+ mov cx,ax
+ call [putmiddle]
+ jmp short ??exit
+
+ ;------------------------------------
+ ; Process line by line.
+??top_of_loop:
+ add si,[clipleft] ; skip whats necessary on left edge
+ mov cx,[pixel_w] ; get the width we need to draw
+
+ ; Copy the source to the destination as appropriate. This routine can
+ ; trash AX, BX, CX, and DI. It must properly modify SI to point one byte past
+ ; the end of the data.
+ call [putmiddle]
+
+ add si,[clipright] ; skip past the left clip
+ add [nextline],BYTESPERROW
+ mov di,[nextline]
+
+ dec dx
+ jnz ??top_of_loop
+
+??exit:
+ pop fs
+ ret
+ ENDP
+
+
+;***************************************************************************
+;* NORMAL_DRAW -- Function that writes a normal pixel line *
+;* *
+;* INPUT: cx - number of pixels to write *
+;* ds:si - buffer which holds the pixels to write *
+;* es:di - place to put the pixels we are writing *
+;* *
+;* OUTPUT: ds:si - points to next pixel past last pixel read *
+;* es:di - points to next pixel past last pixel written *
+;* *
+;* WARNINGS: none *
+;* *
+;* HISTORY: *
+;* 07/17/1992 PWG : Created. *
+;*=========================================================================*
+
+ PROC NOLANGUAGE WSA_Normal_Draw NEAR
+
+ IF 1
+ ; This version is marginally faster than the later version.
+ mov ax,cx
+ shr cx,2
+ rep movsd
+ and ax,011b
+ mov cx,ax
+ shr cx,1
+ rep movsw
+ adc cx,cx
+ rep movsb
+ ret
+
+ ELSE
+
+ shr cx,1 ; convert to words (odd pix in carry)
+ rep movsw ; write out the needed words
+ adc cx,0 ; add the carry into cx
+ rep movsb ; write out the odd byte if any
+ ret
+ ENDIF
+
+ ENDP
+
+
+;***************************************************************************
+;* TRANSPARENT_DRAW -- Function that writes a transparent pixel line *
+;* *
+;* INPUT: cx - number of pixels to write *
+;* ds:si - buffer which holds the pixels to write *
+;* es:di - place to put the pixels we are writing *
+;* *
+;* OUTPUT: ds:si - points to next pixel past last pixel read *
+;* es:di - points to next pixel past last pixel written *
+;* *
+;* WARNINGS: none *
+;* *
+;* HISTORY: *
+;* 07/17/1992 PWG : Created. *
+;* 10/02/1994 JLB : Optimized for 250% speed improvement. *
+;*=========================================================================*
+ PROC NOLANGUAGE Transparent_Draw NEAR
+
+ IF 1
+ ; Preserve DX since it is used as a scratch register.
+ push dx
+
+??loop:
+ ; Swap DS:SI and ES:DI back in preparation for the REP SCASB
+ ; instruction.
+ xchg di,si
+ mov dx,es
+ mov ax,ds
+ mov ds,dx
+ mov es,ax
+
+ ; Remember the bytes remaining in order to calculate the position
+ ; of the scan when it stops.
+ mov bx,cx
+
+ ; Scan looking for a non-zero value in the source buffer.
+ xor al,al
+ repe scasb
+
+ ; When the loop ends, if the EQ flag is set then the scanning is
+ ; complete. Jump to the end of the routine in order to fixup the
+ ; pointers.
+ je short ??fini
+
+ ; Advance the destination pointer by the amount necessary to match
+ ; the source movement. DS:SI points to where data should be written.
+ add si,bx
+ inc cx ; SCASB leaves CX one too low, fix it.
+ dec di ; SCASB leaves DI one byte too far, fix it.
+ sub si,cx
+
+ ; Scan for the duration of non-zero pixels. This yields a count which
+ ; is used to copy the source data to the destination. Preserve DI.
+ mov dx,di
+ mov bx,cx
+ repne scasb
+ mov di,dx
+
+ ; Set BX to equal the number of bytes to copy from source to dest.
+ inc cx ; SCASB leaves CX one too low, fix it.
+ sub bx,cx
+
+ ; Move the data from ES:DI to DS:SI for BX bytes.
+ xchg cx,bx ; Make CX=bytes to move, BX=bytes remaining.
+
+ ; Swap DS:SI and ES:DI in preparation for the REP MOV instruction.
+ xchg di,si
+ mov dx,es
+ mov ax,ds
+ mov ds,dx
+ mov es,ax
+
+ ; Move the data from source to dest. First try to move double
+ ; words. Then copy the remainder bytes (if any). Putting jumps in
+ ; this section doesn't result in any savings -- oh well.
+ mov ax,cx
+ shr cx,2
+ rep movsd
+ and ax,0011b
+ mov cx,ax
+ shr cx,1
+ rep movsw
+ adc cx,cx
+ rep movsb
+
+ ; Restore CX with the remaining bytes to process.
+ mov cx,bx
+
+ ; If there are more bytes to process, then loop back.
+ or cx,cx
+ jne short ??loop
+
+??fini:
+ ; Swap ES:DI and DS:SI back to original orientation.
+ mov ax,ds
+ mov bx,es
+ mov es,ax
+ mov ds,bx
+ xchg di,si
+
+ ; Restore DX and return.
+ pop dx
+ ret
+
+ ELSE
+
+??loop_top:
+ lodsb
+ or al,al
+ jz short ??skip
+
+ mov [es:di],al ; store the pixel to the screen
+??skip:
+ inc di
+ loop ??loop_top
+ ret
+
+ ENDIF
+
+ ENDP
+
+
+;***************************************************************************
+;* PRIORITY_DRAW -- Function that writes a pixels if they are in front of *
+;* the given plate. *
+;* *
+;* INPUT: cx - number of pixels to write *
+;* ds:si - buffer which holds the pixels to write *
+;* es:di - place to put the pixels we are writing *
+;* *
+;* OUTPUT: ds:si - points to next pixel past last pixel read *
+;* es:di - points to next pixel past last pixel written *
+;* *
+;* WARNINGS: none *
+;* *
+;* HISTORY: *
+;* 07/17/1992 PWG : Created. *
+;* 12/01/1992 MBL : Updated to work with latest mask data encoding. *
+;* 17/01/1993 MCC : Updated for 386, and optimized *
+;*=========================================================================*
+
+ PROC NOLANGUAGE Priority_Draw NEAR
+
+ mov fs,[background] ; get the SEG of the background page
+ mov gs,[maskpage] ; get the SEG of the mask info
+ mov ah,[priority] ; keep a copy of priority varible for faster cmp
+
+
+??loop_top:
+ lodsb ; get the pixel to draw on the screen
+
+ ; get the mask byte for our pixel
+ mov bl,[ds:di]
+ ; get rid of non-walkable bit and
+ ; get rid of scaling id bits
+ and bl,CLEAR_NON_WALK_BIT_AND_SCALE_BITS
+
+ cmp ah,bl ; are we more toward the front?
+ jge short ??out_pixel ; if so then write the pixel
+
+ mov al,[fs:di] ; get the pixel to write
+??out_pixel:
+ stosb ; write the pixel and inc the DI
+ loop ??loop_top
+ ret
+
+ ENDP
+
+
+;***************************************************************************
+;* PRIORITY_TRANSPARENT_DRAW -- Function that writes a pixels if they are *
+;* in front of the given plate. It also deals with *
+;* transparent pixels. *
+;* *
+;* INPUT: cx - number of pixels to write *
+;* ds:si - buffer which holds the pixels to write *
+;* es:di - place to put the pixels we are writing *
+;* *
+;* OUTPUT: ds:si - points to next pixel past last pixel read *
+;* es:di - points to next pixel past last pixel written *
+;* *
+;* WARNINGS: none *
+;* *
+;* HISTORY: *
+;* 07/17/1992 PWG : Created. *
+;* 12/01/1992 MBL : Updated to work with latest mask data encoding. *
+;* 17/01/1993 MCC : Updated for 386, and optimized *
+;*=========================================================================*
+
+ PROC NOLANGUAGE Priority_Transparent_Draw NEAR
+
+ mov fs,[background] ; get the SEG of the background page
+ mov gs,[maskpage] ; get the SEG of the mask info
+ mov ah,[priority] ; keep a copy of priority varible for faster cmp
+
+??loop_top:
+ lodsb ; get the pixel on the screen
+ or al,al ; check to see if al is transparent
+ je short ??write_back ; if it is go write background
+
+ mov bl,[gs:di] ; get the mask byte for our pixel
+
+ ; get rid of non-walkable bit and
+ ; get rid of scaling id bits
+ and bl,CLEAR_NON_WALK_BIT_AND_SCALE_BITS
+
+ cmp ah,bl ; are we more toward the front?
+ jge short ??out_pixel ; if so then write the pixel
+
+??write_back:
+ mov al,[fs:di] ; get the pixel to write
+??out_pixel:
+ stosb ; write the pixel
+ loop ??loop_top
+ ret
+
+ ENDP
+
+
+;***************************************************************************
+;* GHOST_NORMAL_DRAW -- Function that writes a normal pixel line *
+;* *
+;* INPUT: cx - number of pixels to write *
+;* ds:si - buffer which holds the pixels to write *
+;* es:di - place to put the pixels we are writing *
+;* *
+;* OUTPUT: ds:si - points to next pixel past last pixel read *
+;* es:di - points to next pixel past last pixel written *
+;* *
+;* WARNINGS: none *
+;* *
+;* HISTORY: *
+;* 05/27/1993 MCC : Created. *
+;*=========================================================================*
+
+ PROC NOLANGUAGE Ghost_Normal_Draw NEAR
+
+??loop_top:
+ lodsb
+
+;---
+ ; Ok, find out if the colour is a Translucent colour
+ push ax
+ push ds
+
+ lds bx,[IsTranslucent]
+ mov ah,al ; preserve real pixel
+ xlat ; get new al (transluecent pixel
+ xchg ah,al ; get real pixel back into AL just in case
+ cmp ah,255
+ je short ??normal_pixel ; is it a translucent ?
+ ; if we get passed here value in
+ ; AH should be 0-15
+
+ ; yes, it is a translucent colour so goto our translucent translation
+ ; table and set up a ptr to the correct table
+
+ mov al,[es:di]
+ ; mov pixel at destination to al and we have
+ ; the index to the translation table
+ ; ((trans_colour * 256) + dest colour)
+ lds bx,[Translucent] ; get the ptr to it!
+ add bh,ah ; Add the (trans_color * 256) of the translation equ.
+ ; XLAT only uses AL so no need to clear AH
+ xlat ; get new pixel in AL
+
+??normal_pixel:
+ pop ds
+ pop bx
+ mov ah,bh
+;---
+
+ mov [es:di],al ; store the pixel to the screen
+
+??skip:
+ inc di
+ loop ??loop_top
+
+ ret
+
+ ENDP
+
+
+;***************************************************************************
+;* GHOST_TRANSPARENT_DRAW -- Function that writes a transparent pixel line *
+;* *
+;* INPUT: cx - number of pixels to write *
+;* ds:si - buffer which holds the pixels to write *
+;* es:di - place to put the pixels we are writing *
+;* *
+;* OUTPUT: ds:si - points to next pixel past last pixel read *
+;* es:di - points to next pixel past last pixel written *
+;* *
+;* WARNINGS: none *
+;* *
+;* HISTORY: *
+;* 05/27/1993 MCC : Created. *
+;*=========================================================================*
+ PROC NOLANGUAGE Ghost_Transparent_Draw NEAR
+
+??loop_top:
+ lodsb
+ or al,al
+ jz short ??skip
+
+;---
+ ; Ok, find out if the colour is a Translucent colour
+ push ax
+ push ds
+
+ lds bx,[IsTranslucent]
+ mov ah,al ; preserve real pixel
+ xlat ; get new al (transluecent pixel
+ xchg ah,al ; get real pixel back into AL just in case
+ cmp ah,255
+ je short ??normal_pixel ; is it a translucent ?
+ ; if we get passed here value in
+ ; AH should be 0-15
+
+ ; yes, it is a translucent colour so goto our translucent translation
+ ; table and set up a ptr to the correct table
+
+ mov al,[es:di]
+ ; mov pixel at destination to al and we have
+ ; the index to the translation table
+ ; ((trans_colour * 256) + dest colour)
+ lds bx,[Translucent] ; get the ptr to it!
+ add bh,ah ; Add the (trans_color * 256) of the translation equ.
+ ; XLAT only uses AL so no need to clear AH
+ xlat ; get new pixel in AL
+
+??normal_pixel:
+ pop ds
+ pop bx
+ mov ah,bh
+;---
+
+ mov [es:di],al ; store the pixel to the screen
+
+??skip:
+ inc di
+ loop ??loop_top
+ ret
+
+ ENDP
+
+
+;***************************************************************************
+;* GHOST_PRIORITY_DRAW -- Function that writes a pixels if they are in fron*
+;* the given plate. *
+;* *
+;* INPUT: cx - number of pixels to write *
+;* ds:si - buffer which holds the pixels to write *
+;* es:di - place to put the pixels we are writing *
+;* *
+;* OUTPUT: ds:si - points to next pixel past last pixel read *
+;* es:di - points to next pixel past last pixel written *
+;* *
+;* WARNINGS: none *
+;* *
+;* HISTORY: *
+;* 07/17/1992 PWG : Created. *
+;* 12/01/1992 MBL : Updated to work with latest mask data encoding. *
+;* 05/27/1993 MCC : Updated to use the new Ghosting fx *
+;* 17/01/1993 MCC : Updated for 386, and optimized *
+;*=========================================================================*
+ PROC NOLANGUAGE Ghost_Priority_Draw NEAR
+
+ mov fs,[background] ; get the SEG of the background page
+ mov gs,[maskpage] ; get the SEG of the mask info
+ mov ah,[priority] ; keep a copy of priority varible for faster cmp
+
+
+??loop_top:
+ lodsb ; get the pixel to draw on the screen
+ ; get the mask byte for our pixel
+ mov bl,[ds:di]
+ ; get rid of non-walkable bit and
+ ; get rid of scaling id bits
+ and bl,CLEAR_NON_WALK_BIT_AND_SCALE_BITS
+ cmp ah,bl ; are we more toward the front?
+ jge short ??out_pixel ; if so then write the pixel
+
+ mov al,[fs:di] ; get the pixel to write
+??out_pixel:
+ stosb ; write the pixel and inc the DI
+ loop ??loop_top
+
+ ret
+
+ ENDP
+
+
+;***************************************************************************
+;* GHOST_PRIORITY_TRANSPARENT_DRAW -- Function that writes a pixels if they*
+;* in front of the given plate. It also deals with *
+;* transparent pixels. *
+;* *
+;* INPUT: cx - number of pixels to write *
+;* ds:si - buffer which holds the pixels to write *
+;* es:di - place to put the pixels we are writing *
+;* *
+;* OUTPUT: ds:si - points to next pixel past last pixel read *
+;* es:di - points to next pixel past last pixel written *
+;* *
+;* WARNINGS: none *
+;* *
+;* HISTORY: *
+;* 07/17/1992 PWG : Created. *
+;* 12/01/1992 MBL : Updated to work with latest mask data encoding. *
+;* 05/27/1993 MCC : Updated to use the new Ghosting fx *
+;* 17/01/1993 MCC : Updated for 386, and optimized *
+;*=========================================================================*
+ PROC NOLANGUAGE Ghost_Priority_Transparent_Draw NEAR
+
+ mov fs,[background] ; get the SEG of the background page
+ mov gs,[maskpage] ; get the SEG of the mask info
+ mov ah,[priority] ; keep a copy of priority varible for faster cmp
+
+??loop_top:
+ lodsb ; get the pixel on the screen
+ or al,al ; check to see if al is transparent
+ je short ??write_back ; if it is go write background
+ mov bl,[gs:di] ; get the mask byte for our pixel
+ ; get rid of non-walkable bit and
+ ; get rid of scaling id bits
+ and bl,CLEAR_NON_WALK_BIT_AND_SCALE_BITS
+ cmp ah,bl ; are we more toward the front?
+ jge short ??out_pixel ; if so then write the pixel
+??write_back:
+ mov al,[fs:di] ; get the pixel to write
+??out_pixel:
+ stosb ; write the pixel
+ loop ??loop_top
+
+ ret
+
+ ENDP
+
+ END
diff --git a/CODE/2KEYFRAM.CPP b/CODE/2KEYFRAM.CPP
new file mode 100644
index 0000000..9a02ab6
--- /dev/null
+++ b/CODE/2KEYFRAM.CPP
@@ -0,0 +1,583 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/2KEYFRAM.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : KEYFRAME.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 06/25/95 *
+ * *
+ * Last Update : June 25, 1995 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * Get_Build_Frame_Count -- Fetches the number of frames in data block. *
+ * Get_Build_Frame_Width -- Fetches the width of the shape image. *
+ * Get_Build_Frame_Height -- Fetches the height of the shape image. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#include "function.h"
+
+#define SUBFRAMEOFFS 7 // 3 1/2 frame offsets loaded (2 offsets/frame)
+
+
+#define Apply_Delta(buffer, delta) Apply_XOR_Delta((char*)(buffer), (char*)(delta))
+
+typedef struct {
+ unsigned short frames;
+ unsigned short x;
+ unsigned short y;
+ unsigned short width;
+ unsigned short height;
+ unsigned short largest_frame_size;
+ short flags;
+} KeyFrameHeaderType;
+
+#define INITIAL_BIG_SHAPE_BUFFER_SIZE 8000000
+#define THEATER_BIG_SHAPE_BUFFER_SIZE 4000000
+#define UNCOMPRESS_MAGIC_NUMBER 56789
+
+unsigned BigShapeBufferLength = INITIAL_BIG_SHAPE_BUFFER_SIZE;
+unsigned TheaterShapeBufferLength = THEATER_BIG_SHAPE_BUFFER_SIZE;
+extern "C"{
+ char *BigShapeBufferStart = NULL;
+ char *TheaterShapeBufferStart = NULL;
+ BOOL UseBigShapeBuffer = FALSE;
+ bool IsTheaterShape = false;
+}
+#ifdef FIXIT_SCORE_CRASH
+/*
+** Global required to fix the score screen crash bug by allowing disabling of uncompressed shapes.
+*/
+bool OriginalUseBigShapeBuffer = false;
+#endif //FIXIT
+char *BigShapeBufferPtr = NULL;
+int TotalBigShapes=0;
+BOOL ReallocShapeBufferFlag = FALSE;
+
+char *TheaterShapeBufferPtr = NULL;
+int TotalTheaterShapes = 0;
+
+
+#define MAX_SLOTS 1500
+#define THEATER_SLOT_START 1000
+
+char **KeyFrameSlots [MAX_SLOTS];
+int TotalSlotsUsed=0;
+int TheaterSlotsUsed = THEATER_SLOT_START;
+
+
+
+typedef struct tShapeHeaderType{
+ unsigned draw_flags;
+ char *shape_data;
+ int shape_buffer; //1 if shape is in theater buffer
+} ShapeHeaderType;
+
+static int Length;
+
+void *Get_Shape_Header_Data(void *ptr)
+{
+ if (UseBigShapeBuffer) {
+
+ ShapeHeaderType *header = (ShapeHeaderType*) ptr;
+ return ((void*) (header->shape_data + (long)(header->shape_buffer ? TheaterShapeBufferStart : BigShapeBufferStart) ) );
+
+ } else {
+ return (ptr);
+ }
+}
+
+int Get_Last_Frame_Length(void)
+{
+ return(Length);
+}
+
+
+void Reset_Theater_Shapes (void)
+{
+ /*
+ ** Delete any previously allocated slots
+ */
+ for (int i=THEATER_SLOT_START ; i 16*1024*1024) ? TRUE : FALSE;
+#ifdef FIXIT_SCORE_CRASH
+ /*
+ ** Keep track of our original decision about whether to use cached shapes.
+ ** This is needed for the score screen crash fix.
+ */
+ OriginalUseBigShapeBuffer = UseBigShapeBuffer;
+#endif //FIXIT
+}
+
+
+
+
+
+unsigned long Build_Frame(void const *dataptr, unsigned short framenumber, void *buffptr)
+{
+#ifdef FIXIT_SCORE_CRASH
+ char *ptr;
+ unsigned long offcurr, offdiff;
+#else
+ char *ptr, *lockptr;//, *uncomp_ptr;
+ unsigned long offcurr, off16, offdiff;
+#endif
+ unsigned long offset[SUBFRAMEOFFS];
+ KeyFrameHeaderType *keyfr;
+ unsigned short buffsize, currframe, subframe;
+ unsigned long length = 0;
+ char frameflags;
+ unsigned long return_value;
+ char *temp_shape_ptr;
+
+ //
+ // valid pointer??
+ //
+ Length = 0;
+ if ( !dataptr || !buffptr ) {
+ return(0);
+ }
+
+ //
+ // look at header then check that frame to build is not greater
+ // than total frames
+ //
+ keyfr = (KeyFrameHeaderType *) dataptr;
+
+ if ( framenumber >= keyfr->frames ) {
+ return(0);
+ }
+
+
+ if (UseBigShapeBuffer) {
+ /*
+ ** If we havnt yet allocated memory for uncompressed shapes then do so now.
+ **
+ */
+ if (!BigShapeBufferStart) {
+ BigShapeBufferStart = (char*)Alloc(BigShapeBufferLength, MEM_NORMAL);
+ BigShapeBufferPtr = BigShapeBufferStart;
+
+ /*
+ ** Allocate memory for theater specific uncompressed shapes
+ */
+ TheaterShapeBufferStart = (char*) Alloc (TheaterShapeBufferLength, MEM_NORMAL);
+ TheaterShapeBufferPtr = TheaterShapeBufferStart;
+ }
+
+ /*
+ ** If we are running out of memory (<10k left) for uncompressed shapes
+ ** then allocate some more.
+ */
+ if (( (unsigned)BigShapeBufferStart + BigShapeBufferLength) - (unsigned)BigShapeBufferPtr < 128000) {
+ ReallocShapeBufferFlag = TRUE;
+ }
+
+ /*
+ ** If this animation was not previously uncompressed then
+ ** allocate memory to keep the pointers to the uncompressed data
+ ** for these animation frames
+ */
+ if (keyfr->x != UNCOMPRESS_MAGIC_NUMBER) {
+ keyfr->x = UNCOMPRESS_MAGIC_NUMBER;
+ if (IsTheaterShape) {
+ keyfr->y = TheaterSlotsUsed;
+ TheaterSlotsUsed++;
+ } else {
+ keyfr->y = TotalSlotsUsed;
+ TotalSlotsUsed++;
+ }
+ /*
+ ** Allocate and clear the memory for the shape info
+ */
+ KeyFrameSlots[keyfr->y]= new char *[keyfr->frames];
+ memset (KeyFrameSlots[keyfr->y] , 0 , keyfr->frames*4);
+ }
+
+ /*
+ ** If this frame was previously uncompressed then just return
+ ** a pointer to the raw data
+ */
+ if (*(KeyFrameSlots[keyfr->y]+framenumber)) {
+ if (IsTheaterShape) {
+ return ((unsigned long)TheaterShapeBufferStart + (unsigned long)*(KeyFrameSlots[keyfr->y]+framenumber));
+ } else {
+ return ((unsigned long)BigShapeBufferStart + (unsigned long)*(KeyFrameSlots[keyfr->y]+framenumber));
+ }
+ }
+ }
+
+ // calc buff size
+ buffsize = keyfr->width * keyfr->height;
+
+ // get offset into data
+ ptr = (char *)Add_Long_To_Pointer( dataptr, (((unsigned long)framenumber << 3) + sizeof(KeyFrameHeaderType)) );
+ Mem_Copy( ptr, &offset[0], 12L );
+ frameflags = (char)(offset[0] >> 24);
+
+
+ if ( (frameflags & KF_KEYFRAME) ) {
+
+ ptr = (char *)Add_Long_To_Pointer( dataptr, (offset[0] & 0x00FFFFFFL) );
+
+ if (keyfr->flags & 1 ) {
+ ptr = (char *)Add_Long_To_Pointer( ptr, 768L );
+ }
+ length = LCW_Uncompress( ptr, buffptr, buffsize );
+ } else { // key delta or delta
+
+ if ( (frameflags & KF_DELTA) ) {
+ currframe = (unsigned short)offset[1];
+
+ ptr = (char *)Add_Long_To_Pointer( dataptr, (((unsigned long)currframe << 3) + sizeof(KeyFrameHeaderType)) );
+ Mem_Copy( ptr, &offset[0], (long)(SUBFRAMEOFFS * sizeof(unsigned long)) );
+ }
+
+ // key frame
+ offcurr = offset[1] & 0x00FFFFFFL;
+
+ // key delta
+ offdiff = (offset[0] & 0x00FFFFFFL) - offcurr;
+
+ ptr = (char *)Add_Long_To_Pointer( dataptr, offcurr );
+
+ if (keyfr->flags & 1 ) {
+ ptr = (char *)Add_Long_To_Pointer( ptr, 768L );
+ }
+
+#ifndef FIXIT_SCORE_CRASH
+ off16 = (unsigned long)lockptr & 0x00003FFFL;
+#endif
+
+ length = LCW_Uncompress( ptr, buffptr, buffsize );
+
+ if (length > buffsize) {
+ return(0);
+ }
+
+#ifndef FIXIT_SCORE_CRASH
+ if ( ((offset[2] & 0x00FFFFFFL) - offcurr) >= (0x00010000L - off16) ) {
+
+ ptr = (char *)Add_Long_To_Pointer( ptr, offdiff );
+ off16 = (unsigned long)ptr & 0x00003FFFL;
+
+ offcurr += offdiff;
+ offdiff = 0;
+ }
+#endif
+
+ length = buffsize;
+ Apply_Delta(buffptr, Add_Long_To_Pointer(ptr, offdiff));
+
+ if ( (frameflags & KF_DELTA) ) {
+ // adjust to delta after the keydelta
+
+ currframe++;
+ subframe = 2;
+
+ while (currframe <= framenumber) {
+ offdiff = (offset[subframe] & 0x00FFFFFFL) - offcurr;
+
+#ifndef FIXIT_SCORE_CRASH
+ if ( ((offset[subframe+2] & 0x00FFFFFFL) - offcurr) >= (0x00010000L - off16) ) {
+
+ ptr = (char *)Add_Long_To_Pointer( ptr, offdiff );
+ off16 = (unsigned long)lockptr & 0x00003FFFL;
+
+ offcurr += offdiff;
+ offdiff = 0;
+ }
+#endif
+
+ length = buffsize;
+ Apply_Delta(buffptr, Add_Long_To_Pointer(ptr, offdiff));
+
+ currframe++;
+ subframe += 2;
+
+ if ( subframe >= (SUBFRAMEOFFS - 1) &&
+ currframe <= framenumber ) {
+ Mem_Copy( Add_Long_To_Pointer( dataptr,
+ (((unsigned long)currframe << 3) +
+ sizeof(KeyFrameHeaderType)) ),
+ &offset[0], (long)(SUBFRAMEOFFS * sizeof(unsigned long)) );
+ subframe = 0;
+ }
+ }
+ }
+ }
+
+
+ if (UseBigShapeBuffer) {
+ /*
+ ** Save the uncompressed shape data so we dont have to uncompress it
+ ** again next time its drawn.
+ ** We keep a space free before the raw shape data so we can add line
+ ** header info before the shape is drawn for the first time
+ */
+
+ if (IsTheaterShape) {
+ /*
+ ** Shape is a theater specific shape
+ */
+ return_value = (unsigned long) TheaterShapeBufferPtr;
+ temp_shape_ptr = TheaterShapeBufferPtr + keyfr->height+sizeof(ShapeHeaderType);
+ /*
+ ** align the actual shape data
+ */
+ if (3 & (unsigned)temp_shape_ptr) {
+ temp_shape_ptr = (char *) ((unsigned)(temp_shape_ptr + 3) & 0xfffffffc);
+ }
+
+ memcpy (temp_shape_ptr , buffptr , length);
+ ((ShapeHeaderType *)TheaterShapeBufferPtr)->draw_flags = -1; //Flag that headers need to be generated
+ ((ShapeHeaderType *)TheaterShapeBufferPtr)->shape_data = temp_shape_ptr - (unsigned)TheaterShapeBufferStart; //pointer to old raw shape data
+ ((ShapeHeaderType *)TheaterShapeBufferPtr)->shape_buffer = 1; //Theater buffer
+ *(KeyFrameSlots[keyfr->y]+framenumber) = TheaterShapeBufferPtr - (unsigned)TheaterShapeBufferStart;
+ TheaterShapeBufferPtr = (char*)(length + (unsigned)temp_shape_ptr);
+ /*
+ ** Align the next shape
+ */
+ if (3 & (unsigned)TheaterShapeBufferPtr) {
+ TheaterShapeBufferPtr = (char *)((unsigned)(TheaterShapeBufferPtr + 3) & 0xfffffffc);
+ }
+ Length = length;
+ return (return_value);
+
+ } else {
+
+
+ return_value=(unsigned long)BigShapeBufferPtr;
+ temp_shape_ptr = BigShapeBufferPtr + keyfr->height+sizeof(ShapeHeaderType);
+ /*
+ ** align the actual shape data
+ */
+ if (3 & (unsigned)temp_shape_ptr) {
+ temp_shape_ptr = (char *) ((unsigned)(temp_shape_ptr + 3) & 0xfffffffc);
+ }
+ memcpy (temp_shape_ptr , buffptr , length);
+ ((ShapeHeaderType *)BigShapeBufferPtr)->draw_flags = -1; //Flag that headers need to be generated
+ ((ShapeHeaderType *)BigShapeBufferPtr)->shape_data = temp_shape_ptr - (unsigned)BigShapeBufferStart; //pointer to old raw shape data
+ ((ShapeHeaderType *)BigShapeBufferPtr)->shape_buffer = 0; //Normal Big Shape Buffer
+ *(KeyFrameSlots[keyfr->y]+framenumber) = BigShapeBufferPtr - (unsigned)BigShapeBufferStart;
+ BigShapeBufferPtr = (char*)(length + (unsigned)temp_shape_ptr);
+ // Align the next shape
+ if (3 & (unsigned)BigShapeBufferPtr) {
+ BigShapeBufferPtr = (char *)((unsigned)(BigShapeBufferPtr + 3) & 0xfffffffc);
+ }
+ Length = length;
+ return (return_value);
+ }
+
+ } else {
+ return ((unsigned long)buffptr);
+ }
+}
+
+
+/***********************************************************************************************
+ * Get_Build_Frame_Count -- Fetches the number of frames in data block. *
+ * *
+ * Use this routine to determine the number of shapes within the data block. *
+ * *
+ * INPUT: dataptr -- Pointer to the keyframe shape data block. *
+ * *
+ * OUTPUT: Returns with the number of shapes in the data block. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/25/1995 JLB : Commented. *
+ *=============================================================================================*/
+unsigned short Get_Build_Frame_Count(void const *dataptr)
+{
+ if (dataptr) {
+ return(((KeyFrameHeaderType const *)dataptr)->frames);
+ }
+ return(0);
+}
+
+
+unsigned short Get_Build_Frame_X(void const *dataptr)
+{
+ if (dataptr) {
+ return(((KeyFrameHeaderType const *)dataptr)->x);
+ }
+ return(0);
+}
+
+
+unsigned short Get_Build_Frame_Y(void const *dataptr)
+{
+ if (dataptr) {
+ return(((KeyFrameHeaderType const *)dataptr)->y);
+ }
+ return(0);
+}
+
+
+/***********************************************************************************************
+ * Get_Build_Frame_Width -- Fetches the width of the shape image. *
+ * *
+ * Use this routine to fetch the width of the shapes within the keyframe shape data block. *
+ * All shapes within the block have the same width. *
+ * *
+ * INPUT: dataptr -- Pointer to the keyframe shape data block. *
+ * *
+ * OUTPUT: Returns with the width of the shapes in the block -- expressed in pixels. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/25/1995 JLB : Commented *
+ *=============================================================================================*/
+unsigned short Get_Build_Frame_Width(void const *dataptr)
+{
+ if (dataptr) {
+ return(((KeyFrameHeaderType const *)dataptr)->width);
+ }
+ return(0);
+}
+
+
+/***********************************************************************************************
+ * Get_Build_Frame_Height -- Fetches the height of the shape image. *
+ * *
+ * Use this routine to fetch the height of the shapes within the keyframe shape data block. *
+ * All shapes within the block have the same height. *
+ * *
+ * INPUT: dataptr -- Pointer to the keyframe shape data block. *
+ * *
+ * OUTPUT: Returns with the height of the shapes in the block -- expressed in pixels. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/25/1995 JLB : Commented *
+ *=============================================================================================*/
+unsigned short Get_Build_Frame_Height(void const *dataptr)
+{
+ if (dataptr) {
+ return(((KeyFrameHeaderType const *)dataptr)->height);
+ }
+ return(0);
+}
+
+
+bool Get_Build_Frame_Palette(void const * dataptr, void * palette)
+{
+ if (dataptr && (((KeyFrameHeaderType const *)dataptr)->flags & 1)) {
+ char const * ptr = (char const *)Add_Long_To_Pointer( dataptr,
+ ( (( (long)sizeof(unsigned long) << 1) *
+ ((KeyFrameHeaderType *) dataptr)->frames ) +
+ 16 + sizeof(KeyFrameHeaderType) ) );
+
+ memcpy(palette, ptr, 768L);
+ return(true);
+ }
+ return(false);
+}
diff --git a/CODE/2SUPPORT.ASM b/CODE/2SUPPORT.ASM
new file mode 100644
index 0000000..4b1499d
--- /dev/null
+++ b/CODE/2SUPPORT.ASM
@@ -0,0 +1,564 @@
+;
+; Command & Conquer Red Alert(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&c0\vcs\code\2support.asv 5.0 11 Nov 1996 09:40:36 JOE_BOSTIC $
+;***************************************************************************
+;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S **
+;***************************************************************************
+;* *
+;* Project Name : Command & Conquer *
+;* *
+;* File Name : SUPPORT.ASM *
+;* *
+;* Programmer : Joe L. Bostic *
+;* *
+;* Start Date : September 23, 1993 *
+;* *
+;* Last Update : May 10, 1994 [JLB] *
+;* *
+;*-------------------------------------------------------------------------*
+;* Functions: *
+;* strtrim -- Remove the trailing white space from a string. *
+;* Fat_Put_Pixel -- Draws a fat pixel. *
+;* Conquer_Build_Fading_Table -- Builds custom shadow/light fading table.*
+;* Remove_From_List -- Removes a pointer from a list of pointers. *
+;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *
+
+IDEAL
+P386
+MODEL USE32 FLAT
+
+INCLUDE "gbuffer.inc"
+ DISPLAY "Command & Conquer assembly support routines."
+
+ CODESEG
+
+
+;***************************************************************************
+;* Fat_Put_Pixel -- Draws a fat pixel. *
+;* *
+;* Use this routine to draw a "pixel" that is bigger than 1 pixel *
+;* across. This routine is faster than drawing a similar small shape *
+;* and faster than calling Fill_Rect. *
+;* *
+;* INPUT: x,y -- Screen coordinates to draw the pixel's upper *
+;* left corner. *
+;* *
+;* color -- The color to render the pixel in. *
+;* *
+;* size -- The number of pixels width of the big "pixel". *
+;* *
+;* page -- The pointer to a GraphicBuffer class or something *
+;* *
+;* OUTPUT: none *
+;* *
+;* WARNINGS: none *
+;* *
+;* HISTORY: *
+;* 03/17/1994 JLB : Created. *
+;*=========================================================================*
+; VOID cdecl Fat_Put_Pixel(long x, long y, long color, long size, void *page)
+ GLOBAL C Fat_Put_Pixel:NEAR
+ PROC Fat_Put_Pixel C near
+ USES eax, ebx, ecx, edx, edi, esi
+
+ ARG x:DWORD ; X coordinate of upper left pixel corner.
+ ARG y:DWORD ; Y coordinate of upper left pixel corner.
+ ARG color:DWORD ; Color to use for the "pixel".
+ ARG siz:DWORD ; Size of "pixel" to plot (square).
+ ARG gpage:DWORD ; graphic page address to plot onto
+
+ cmp [siz],0
+ je short ??exit
+
+ ; Set EDI to point to start of logical page memory.
+ ;*===================================================================
+ ; Get the viewport information and put bytes per row in ecx
+ ;*===================================================================
+ mov ebx,[gpage] ; get a pointer to viewport
+ mov edi,[(GraphicViewPort ebx).GVPOffset] ; get the correct offset
+
+ ; Verify the the Y pixel offset is legal.
+ mov eax,[y]
+ cmp eax,[(GraphicViewPort ebx).GVPHeight] ;YPIXEL_MAX
+ jae short ??exit
+ mov ecx,[(GraphicViewPort ebx).GVPWidth]
+ add ecx,[(GraphicViewPort ebx).GVPXAdd]
+ add ecx,[(GraphicViewPort ebx).GVPPitch]
+ mul ecx
+ add edi,eax
+
+ ; Verify the the X pixel offset is legal.
+
+ mov edx,[(GraphicViewPort ebx).GVPWidth]
+ cmp edx,[x]
+ mov edx,ecx
+ jbe short ??exit
+ add edi,[x]
+
+ ; Write the pixel to the screen.
+ mov ebx,[siz] ; Copy of pixel size.
+ sub edx,ebx ; Modulo to reach start of next row.
+ mov eax,[color]
+??again:
+ mov ecx,ebx
+ rep stosb
+ add edi,edx ; EDI points to start of next row.
+ dec [siz]
+ jnz short ??again
+
+??exit:
+ ret
+
+ ENDP Fat_Put_Pixel
+
+
+if 0
+
+;***************************************************************************
+;* strtrim -- Remove the trailing white space from a string. *
+;* *
+;* Use this routine to remove white space characters from the beginning *
+;* and end of the string. The string is modified in place by *
+;* this routine. *
+;* *
+;* INPUT: buffer -- Pointer to the string to modify. *
+;* *
+;* OUTPUT: none *
+;* *
+;* WARNINGS: none *
+;* *
+;* HISTORY: *
+;* 10/07/1992 JLB : Created. *
+;*=========================================================================*
+; VOID cdecl strtrim(BYTE *buffer);
+ GLOBAL C strtrim :NEAR
+ PROC strtrim C near
+ USES ax, edi, esi
+
+ ARG buffer:DWORD ; Pointer to string to modify.
+
+ cmp [buffer],0
+ je short ??fini
+
+ ; Prepare for string scanning by loading pointers.
+ cld
+ mov esi,[buffer]
+ mov edi,esi
+
+ ; Strip white space from the start of the string.
+??looper:
+ lodsb
+ cmp al,20h ; Space
+ je short ??looper
+ cmp al,9 ; TAB
+ je short ??looper
+ stosb
+
+ ; Copy the rest of the string.
+??gruntloop:
+ lodsb
+ stosb
+ or al,al
+ jnz short ??gruntloop
+ dec edi
+ ; Strip the white space from the end of the string.
+??looper2:
+ mov [edi],al
+ dec edi
+ mov ah,[edi]
+ cmp ah,20h
+ je short ??looper2
+ cmp ah,9
+ je short ??looper2
+
+??fini:
+ ret
+
+ ENDP strtrim
+
+
+;***************************************************************************
+;* Conquer_Build_Fading_Table -- Builds custom shadow/light fading table. *
+;* *
+;* This routine is used to build a special fading table for C&C. There *
+;* are certain colors that get faded to and cannot be faded again. *
+;* With this rule, it is possible to draw a shadow multiple times and *
+;* not have it get any lighter or darker. *
+;* *
+;* INPUT: palette -- Pointer to the 768 byte IBM palette to build from. *
+;* *
+;* dest -- Pointer to the 256 byte remap table. *
+;* *
+;* color -- Color index of the color to "fade to". *
+;* *
+;* frac -- The fraction to fade to the specified color *
+;* *
+;* OUTPUT: Returns with pointer to the remap table. *
+;* *
+;* WARNINGS: none *
+;* *
+;* HISTORY: *
+;* 10/07/1992 JLB : Created. *
+;*=========================================================================*/
+;VOID * cdecl Conquer_Build_Fading_Table(VOID *palette, VOID *dest, long color, long frac);
+ GLOBAL C Conquer_Build_Fading_Table : NEAR
+ PROC Conquer_Build_Fading_Table C near
+ USES ebx, ecx, edi, esi
+
+ ARG palette:DWORD
+ ARG dest:DWORD
+ ARG color:DWORD
+ ARG frac:DWORD
+
+ LOCAL matchvalue:DWORD ; Last recorded match value.
+ LOCAL targetred:BYTE ; Target gun red.
+ LOCAL targetgreen:BYTE ; Target gun green.
+ LOCAL targetblue:BYTE ; Target gun blue.
+ LOCAL idealred:BYTE
+ LOCAL idealgreen:BYTE
+ LOCAL idealblue:BYTE
+ LOCAL matchcolor:BYTE ; Tentative match color.
+
+ALLOWED_COUNT EQU 16
+ALLOWED_START EQU 256-ALLOWED_COUNT
+
+ cld
+
+ ; If the source palette is NULL, then just return with current fading table pointer.
+ cmp [palette],0
+ je ??fini1
+ cmp [dest],0
+ je ??fini1
+
+ ; Fractions above 255 become 255.
+ mov eax,[frac]
+ cmp eax,0100h
+ jb short ??ok
+ mov [frac],0FFh
+??ok:
+
+ ; Record the target gun values.
+ mov esi,[palette]
+ mov ebx,[color]
+ add esi,ebx
+ add esi,ebx
+ add esi,ebx
+ lodsb
+ mov [targetred],al
+ lodsb
+ mov [targetgreen],al
+ lodsb
+ mov [targetblue],al
+
+ ; Main loop.
+ xor ebx,ebx ; Remap table index.
+
+ ; Transparent black never gets remapped.
+ mov edi,[dest]
+ mov [edi],bl
+ inc edi
+
+ ; EBX = source palette logical number (1..255).
+ ; EDI = running pointer into dest remap table.
+??mainloop:
+ inc ebx
+ mov esi,[palette]
+ add esi,ebx
+ add esi,ebx
+ add esi,ebx
+
+ mov edx,[frac]
+ shr edx,1
+ ; new = orig - ((orig-target) * fraction);
+
+ lodsb ; orig
+ mov dh,al ; preserve it for later.
+ sub al,[targetred] ; al = (orig-target)
+ imul dl ; ax = (orig-target)*fraction
+ shl eax,1
+ sub dh,ah ; dh = orig - ((orig-target) * fraction)
+ mov [idealred],dh ; preserve ideal color gun value.
+
+ lodsb ; orig
+ mov dh,al ; preserve it for later.
+ sub al,[targetgreen] ; al = (orig-target)
+ imul dl ; ax = (orig-target)*fraction
+ shl eax,1
+ sub dh,ah ; dh = orig - ((orig-target) * fraction)
+ mov [idealgreen],dh ; preserve ideal color gun value.
+
+ lodsb ; orig
+ mov dh,al ; preserve it for later.
+ sub al,[targetblue] ; al = (orig-target)
+ imul dl ; ax = (orig-target)*fraction
+ shl eax,1
+ sub dh,ah ; dh = orig - ((orig-target) * fraction)
+ mov [idealblue],dh ; preserve ideal color gun value.
+
+ ; Sweep through a limited set of existing colors to find the closest
+ ; matching color.
+
+ mov eax,[color]
+ mov [matchcolor],al ; Default color (self).
+ mov [matchvalue],-1 ; Ridiculous match value init.
+ mov ecx,ALLOWED_COUNT
+
+ mov esi,[palette] ; Pointer to original palette.
+ add esi,(ALLOWED_START)*3
+
+ ; BH = color index.
+ mov bh,ALLOWED_START
+??innerloop:
+
+ xor edx,edx ; Comparison value starts null.
+
+ ; Build the comparison value based on the sum of the differences of the color
+ ; guns squared.
+ lodsb
+ sub al,[idealred]
+ mov ah,al
+ imul ah
+ add edx,eax
+
+ lodsb
+ sub al,[idealgreen]
+ mov ah,al
+ imul ah
+ add edx,eax
+
+ lodsb
+ sub al,[idealblue]
+ mov ah,al
+ imul ah
+ add edx,eax
+ jz short ??perfect ; If perfect match found then quit early.
+
+ cmp edx,[matchvalue]
+ jae short ??notclose
+ mov [matchvalue],edx ; Record new possible color.
+ mov [matchcolor],bh
+??notclose:
+ inc bh ; Checking color index.
+ loop ??innerloop
+ mov bh,[matchcolor]
+??perfect:
+ mov [matchcolor],bh
+ xor bh,bh ; Make BX valid main index again.
+
+ ; When the loop exits, we have found the closest match.
+ mov al,[matchcolor]
+ stosb
+ cmp ebx,ALLOWED_START-1
+ jne ??mainloop
+
+ ; Fill the remainder of the remap table with values
+ ; that will remap the color to itself.
+ mov ecx,ALLOWED_COUNT
+??fillerloop:
+ inc bl
+ mov al,bl
+ stosb
+ loop ??fillerloop
+
+??fini1:
+ mov esi,[dest]
+ mov eax,esi
+ ret
+
+ ENDP Conquer_Build_Fading_Table
+
+
+;***************************************************************************
+;* Remove_From_List -- Removes a pointer from a list of pointers. *
+;* *
+;* This low level routine is used to remove a pointer from a list of *
+;* pointers. The trailing pointers are moved downward to fill the *
+;* hole. *
+;* *
+;* INPUT: list -- Pointer to list of pointer. *
+;* *
+;* index -- Pointer to length of pointer list. *
+;* *
+;* ptr -- The pointer value to search for and remove. *
+;* *
+;* OUTPUT: none *
+;* *
+;* WARNINGS: none *
+;* *
+;* HISTORY: *
+;* 04/11/1994 JLB : Created. *
+;* 04/22/1994 JLB : Convert to assembly language. *
+;* 05/10/1994 JLB : Short pointers now. *
+;*=========================================================================*/
+;VOID cdecl Remove_From_List(VOID **list, long *index, long ptr);
+ GLOBAL C Remove_From_List:NEAR
+ PROC Remove_From_List C near
+ USES edi, esi, ecx, eax
+ ARG list:DWORD ; Pointer to list.
+ ARG index:DWORD ; Pointer to count.
+ ARG element:DWORD ; Element to remove.
+
+ ; Fetch the number of elements in the list. If there are no
+ ; elements, then just exit quickly.
+ mov edi,[index]
+ mov ecx,[edi]
+ jcxz short ??fini2
+
+ ; Fetch pointer to list.
+ cmp [list],0
+ je short ??fini2
+ mov edi,[list]
+
+ ; Loop through all elements searching for a match.
+ mov eax,[element]
+ repne scasd
+ jne short ??fini2 ; No match found.
+
+ ; Copy all remaining elements down. If this is the
+ ; last element in the list then nothing needs to be
+ ; copied -- just decrement the list size.
+ jcxz short ??nocopy ; No copy necessary.
+ mov esi,edi
+ sub edi,4
+ rep movsd
+
+ ; Reduce the list count by one.
+??nocopy:
+ mov edi,[index]
+ dec [DWORD PTR edi]
+
+??fini2:
+ ret
+
+ ENDP Remove_From_List
+
+
+; long cdecl Get_EAX();
+ GLOBAL C Get_EAX :NEAR
+ PROC Get_EAX C near
+ ret
+
+ ENDP Get_EAX
+endif
+
+
+ DATASEG
+
+TabA DD 6949350
+ DD 4913933
+ DD 3474675
+ DD 2456966
+ DD 1737338
+ DD 1228483
+ DD 868669
+ DD 614242
+ DD 434334
+ DD 307121
+ DD 217167
+ DD 153560
+ DD 108584
+ DD 76780
+ DD 54292
+ DD 38390
+ DD 27146
+ DD 19195
+ DD 13573
+ DD 9598
+ DD 6786
+ DD 4799
+ DD 3393
+ DD 2399
+ DD 1697
+ DD 1200
+ DD 848
+ DD 600
+ DD 424
+ DD 300
+ DD 212
+ DD 150
+ DD 106
+
+TabB DD 154
+ DD 218
+ DD 309
+ DD 437
+ DD 618
+ DD 874
+ DD 1236
+ DD 1748
+ DD 2472
+ DD 3496
+ DD 4944
+ DD 6992
+ DD 9888
+ DD 13983
+ DD 19775
+ DD 27967
+ DD 39551
+ DD 55933
+ DD 79101
+ DD 111866
+ DD 158203
+ DD 223732
+ DD 316405
+ DD 447465
+ DD 632811
+ DD 894929
+ DD 1265621
+ DD 1789859
+ DD 2531243
+ DD 3579718
+ DD 5062486
+ DD 7159436
+ DD 10124971
+
+ CODESEG
+
+;***********************************************************************************************
+;* Square_Root -- Finds the square root of the fixed pointer parameter. *
+;* *
+;* INPUT: val -- The fixed point (16:16) value to find the square root of. *
+;* *
+;* OUTPUT: Returns with the square root of the fixed pointer parameter. *
+;* *
+;* WARNINGS: none *
+;* *
+;* HISTORY: *
+;* 10/04/1995 JLB : Adapted. *
+;*=============================================================================================*/
+;unsigned Square_Root(unsigned val);
+ GLOBAL C Square_Root :NEAR
+ PROC Square_Root C near
+ USES ebx,edx
+
+ bsr ebx,eax
+ jz ??zero
+
+ mul [DWORD 4*ebx + OFFSET TabA]
+ shrd eax,edx,10h
+ add eax, [4*ebx + OFFSET TabB]
+??zero:
+ ret
+
+ ENDP Square_Root
+
+;----------------------------------------------------------------------------
+
+ END
+
diff --git a/CODE/2TXTPRNT.ASM b/CODE/2TXTPRNT.ASM
new file mode 100644
index 0000000..af53028
--- /dev/null
+++ b/CODE/2TXTPRNT.ASM
@@ -0,0 +1,507 @@
+;
+; Command & Conquer Red Alert(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 : Westwood 32 bit Library *
+;* *
+;* File Name : TXTPRNT.ASM *
+;* *
+;* Programmer : Phil W. Gorrow *
+;* *
+;* Start Date : January 17, 1995 *
+;* *
+;* Last Update : January 17, 1995 [PWG] *
+;* *
+;*-------------------------------------------------------------------------*
+;* Functions: *
+;* MCGA_Print -- Assembly MCGA text print routine *
+;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *
+
+IDEAL
+P386
+MODEL USE32 FLAT
+
+;INCLUDE "mcgaprim.inc"
+;INCLUDE ".\gbuffer.inc"
+
+GLOBAL C Buffer_Print : NEAR
+
+STRUC GraphicViewPort
+GVPOffset DD ? ; offset to virtual viewport
+GVPWidth DD ? ; width of virtual viewport
+GVPHeight DD ? ; height of virtual viewport
+GVPXAdd DD ? ; x mod to get to next line
+GVPXPos DD ? ; x pos relative to Graphic Buff
+GVPYPos DD ? ; y pos relative to Graphic Buff
+GVPPitch dd ? ; modulo of graphic view port
+GVPBuffPtr DD ? ; ptr to associated Graphic Buff
+ENDS
+
+
+
+;*=========================================================================*
+;* Extern the font pointer which is defined by the font class *
+;*=========================================================================*
+GLOBAL C FontPtr:DWORD
+GLOBAL C FontXSpacing:DWORD
+GLOBAL C FontYSpacing:DWORD
+GLOBAL C ColorXlat:BYTE
+
+;*=========================================================================*
+;* Define the necessary equates for structures and bounds checking *
+;*=========================================================================*
+; The header of the font file looks like this:
+; UWORD FontLength; 0
+; BYTE FontCompress; 2
+; BYTE FontDataBlocks; 3
+; UWORD InfoBlockOffset; 4
+; UWORD OffsetBlockOffset; 6
+; UWORD WidthBlockOffset; 8
+; UWORD DataBlockOffset; 10
+; UWORD HeightOffset; 12
+; For this reason the following equates have these values:
+FONTINFOBLOCK EQU 4
+FONTOFFSETBLOCK EQU 6
+FONTWIDTHBLOCK EQU 8
+FONTDATABLOCK EQU 10
+FONTHEIGHTBLOCK EQU 12
+
+FONTINFOMAXHEIGHT EQU 4
+FONTINFOMAXWIDTH EQU 5
+
+
+LOCALS ??
+;*=========================================================================*
+;* Define the color xlate table in the data segment *
+;*=========================================================================*
+ DATASEG
+ColorXlat DB 000H,001H,002H,003H,004H,005H,006H,007H
+ DB 008H,009H,00AH,00BH,00CH,00DH,00EH,00FH
+
+ DB 001H,000H,000H,000H,000H,000H,000H,000H
+ DB 000H,000H,000H,000H,000H,000H,000H,000H
+
+ DB 002H,000H,000H,000H,000H,000H,000H,000H
+ DB 000H,000H,000H,000H,000H,000H,000H,000H
+
+ DB 003H,000H,000H,000H,000H,000H,000H,000H
+ DB 000H,000H,000H,000H,000H,000H,000H,000H
+
+ DB 004H,000H,000H,000H,000H,000H,000H,000H
+ DB 000H,000H,000H,000H,000H,000H,000H,000H
+
+ DB 005H,000H,000H,000H,000H,000H,000H,000H
+ DB 000H,000H,000H,000H,000H,000H,000H,000H
+
+ DB 006H,000H,000H,000H,000H,000H,000H,000H
+ DB 000H,000H,000H,000H,000H,000H,000H,000H
+
+ DB 007H,000H,000H,000H,000H,000H,000H,000H
+ DB 000H,000H,000H,000H,000H,000H,000H,000H
+
+ DB 008H,000H,000H,000H,000H,000H,000H,000H
+ DB 000H,000H,000H,000H,000H,000H,000H,000H
+
+ DB 009H,000H,000H,000H,000H,000H,000H,000H
+ DB 000H,000H,000H,000H,000H,000H,000H,000H
+
+ DB 00AH,000H,000H,000H,000H,000H,000H,000H
+ DB 000H,000H,000H,000H,000H,000H,000H,000H
+
+ DB 00BH,000H,000H,000H,000H,000H,000H,000H
+ DB 000H,000H,000H,000H,000H,000H,000H,000H
+
+ DB 00CH,000H,000H,000H,000H,000H,000H,000H
+ DB 000H,000H,000H,000H,000H,000H,000H,000H
+
+ DB 00DH,000H,000H,000H,000H,000H,000H,000H
+ DB 000H,000H,000H,000H,000H,000H,000H,000H
+
+ DB 00EH,000H,000H,000H,000H,000H,000H,000H
+ DB 000H,000H,000H,000H,000H,000H,000H,000H
+
+ DB 00FH
+ CODESEG
+
+
+;***************************************************************************
+;* MCGA_PRINT -- Assembly MCGA text print routine *
+;* *
+;* *
+;* *
+;* INPUT: *
+;* *
+;* OUTPUT: *
+;* *
+;* PROTO: *
+;* *
+;* WARNINGS: *
+;* *
+;* HISTORY: *
+;* 01/17/1995 PWG : Created. *
+;*=========================================================================*
+ PROC Buffer_Print C near
+ USES ebx,ecx,edx,esi,edi
+
+ ARG this:DWORD
+ ARG string:DWORD
+ ARG x_pixel:DWORD
+ ARG y_pixel:DWORD
+ ARG fcolor:DWORD
+ ARG bcolor:DWORD
+
+ LOCAL infoblock:DWORD ; pointer to info block
+ LOCAL offsetblock:DWORD ; pointer to offset block (UWORD *)
+ LOCAL widthblock:DWORD ; pointer to width block (BYTE *)
+ LOCAL heightblock:DWORD ; pointer to height block (UWORD *)
+
+ LOCAL curline:DWORD ; pointer to first column of current row.
+ LOCAL bufferwidth:DWORD ; width of buffer (vpwidth + Xadd)
+ LOCAL nextdraw:DWORD ; bufferwidth - width of cur character.
+ LOCAL startdraw:DWORD ; where next character will start being drawn.
+
+ LOCAL char:DWORD ; current character value.
+
+ LOCAL maxheight:BYTE ; max height of characters in font.
+ LOCAL bottomblank:BYTE ; amount of empty space below current character.
+ LOCAL charheight:BYTE ; true height of current character.
+ LOCAL vpwidth:DWORD
+ LOCAL vpheight:DWORD
+ LOCAL original_x:DWORD ; Starting X position.
+
+
+;-------------------------------- Where to draw -----------------------------------------------
+ ; Set up memory location to start drawing.
+ mov ebx,[this] ; get a pointer to dest
+ mov eax,[(GraphicViewPort ebx).GVPHeight] ; get height of viewport
+ mov [vpheight],eax ; save off height of viewport
+ mov eax,[(GraphicViewPort ebx).GVPWidth] ; get width of viewport
+ mov [vpwidth],eax ; save it off for later
+ add eax,[(GraphicViewPort ebx).GVPXAdd] ; add in xadd for bytes_per_line
+ add eax,[(GraphicViewPort ebx).GVPPitch] ; add in pitch of direct draw surface
+ mov [bufferwidth],eax ; save it off for later use.
+
+ mul [y_pixel] ; multiply rowsize * y_pixel start.
+ mov edi,[(GraphicViewPort ebx).GVPOffset] ; get start of the viewport
+ add edi,eax ; add y position to start of vp
+ mov [curline],edi ; save 0,y address for line feed stuff.
+ add edi,[x_pixel] ; add to get starting column in starting row.
+ mov [startdraw],edi ; save it off.
+
+ mov eax,[x_pixel]
+ mov [original_x],eax
+
+;-------------------------------- Create block pointers ----------------------------------------
+ ; Get the pointer to the font.
+ ; We could check for NULL but why waste the time.
+ ; It is up to programmer to make sure it is set.
+ mov esi,[FontPtr] ; Get the font pointer
+ or esi,esi
+ jz ??overflow
+
+ ; Set up some pointers to the different memory blocks.
+ ; esi (FontPtr) is added to each to get the true address of each block.
+ ; Many registers are used for P5 optimizations.
+ ; ebx is used for InfoBlock which is then used in the next section.
+ movzx eax,[WORD PTR esi+FONTOFFSETBLOCK] ; get offset to offset block
+ movzx ebx,[WORD PTR esi+FONTINFOBLOCK] ; get offset to info block (must be ebx for height test)
+ movzx ecx,[WORD PTR esi+FONTWIDTHBLOCK] ; get offset to width block
+ movzx edx,[WORD PTR esi+FONTHEIGHTBLOCK] ; get offset to height block
+
+ add eax,esi ; add offset of FontPtr to offset block
+ add ebx,esi ; add offset of FontPtr to info block
+ add ecx,esi ; add offset of FontPtr to width block
+ add edx,esi ; add offset of FontPtr to height block
+
+ mov [offsetblock],eax ; save offset to offset block
+ mov [infoblock],ebx ; save offset to info block
+ mov [widthblock],ecx ; save offset to width block
+ mov [heightblock],edx ; save offset to height block
+
+;------------------------------------------ Test for fit ----------------------------------------------
+ ; Test to make sure the height of the max character will fit on this line
+ ; and and not fall out of the viewport.
+ ; remember we set ebx to FONTINFOBLOCK above.
+ movzx eax,[BYTE PTR ebx + FONTINFOMAXHEIGHT]; get the max height in font.
+ mov [maxheight],al ; save it for later use.
+ add eax,[y_pixel] ; add current y_value.
+ cmp eax,[vpheight] ; are we over the edge?
+ jg ??overflow ; if so, we're outa here.
+
+ mov [y_pixel],eax ; save for next line feed. y value for next line.
+
+ cld ; Make sure we are always forward copying.
+
+;------------------------ Set palette foreground and background ----------------------------------
+ mov eax,[fcolor] ; foreground color
+ mov [ColorXlat+1],al
+ mov [ColorXlat+16],al
+
+ mov eax,[bcolor] ; background color
+ mov [ColorXlat],al
+
+;-------------------------------------------------------------------------------------------------
+;----------------------------------------- Main loop ----------------------------------------------
+ ; Now we go into the main loop of reading each character in the string and doing
+ ; something with it.
+??next_char:
+ ; while (*string++)
+ xor eax,eax ; zero out since we will just load al.
+ mov esi,[string] ; get address on next character.
+ lodsb ; load the character into al.
+ test eax,0FFH ; test to see if character is a NULL
+ jz ??done ; character is NULL, get outa here.
+
+ mov edi,[startdraw] ; Load the starting address.
+
+ mov [string],esi ; save index into string. (incremented by lodsb)
+
+ cmp al,10 ; is the character a line feed?
+ je ??line_feed ; if so, go to special case.
+
+ cmp al,13 ; is the character a line feed?
+ je ??line_feed ; if so, go to special case.
+
+ mov [char],eax ; save the character off for later reference.
+ mov ebx,eax ; save it in ebx for later use also.
+
+ add eax,[widthblock] ; figure address of width of character.
+ mov ecx,[x_pixel] ; get current x_pixel.
+ movzx edx,[BYTE PTR eax] ; get the width of the character in dl.
+ add ecx,edx ; add width of char to current x_pixel.
+ mov eax,[FontXSpacing]
+ add ecx,eax
+ add [startdraw],edx ; save start draw for next character.
+ add [startdraw],eax ; adjust for the font spacing value
+
+ cmp ecx,[vpwidth] ; is the pixel greater then the vp width?
+ jg ??force_line_feed ; if so, force a line feed.
+
+ mov [x_pixel],ecx ; save value of start of next character.
+ mov ecx,[bufferwidth] ; get amount to next y same x (one row down)
+ sub ecx,edx ; take the current width off.
+ mov [nextdraw],ecx ; save it to add to edi when done with a row.
+
+ ; At this point we got the character. It is now time to find out specifics
+ ; about drawing the darn thing.
+ ; ebx = char so they can be used as an indexes.
+ ; edx = width of character for loop later.
+
+ ; get offset of data for character into esi.
+ shl ebx,1 ; mult by 2 to later use as a WORD index.
+ mov esi,[offsetblock] ; get pointer to begining of offset block.
+ add esi,ebx ; index into offset block.
+ movzx esi,[WORD PTR esi] ; get true offset into data block from FontPtr.
+ add esi,[FontPtr] ; Now add FontPtr address to get true address.
+
+ ; Get top and bottom blank sizes and the true height of the character.
+ add ebx,[heightblock] ; point ebx to element in height array.
+ mov al,[ebx+1] ; load the data height into dl.
+ mov cl,[ebx] ; load the first data row into cl.
+ mov bl,[maxheight] ; get the max height of characters.
+ mov [charheight],al ; get number of rows with data.
+ add al,cl ; add the two heights.
+ sub bl,al ; subract topblank + char height from maxheight.
+ mov [bottomblank],bl ; save off the number of blank rows on the bottom.
+ ; leaving this section:
+ ; dl is still the width of the character.
+ ; cl is the height of the top blank area.
+
+ mov ebx,OFFSET ColorXlat ; setup ebx for xlat commands.
+ mov dh,dl ; save the width of the character to restore each loop.
+
+ cmp cl,0 ; is there any blank rows on top?
+ jz ??draw_char ; if not go and draw the real character.
+
+ xor eax,eax ; get color 0 for background.
+ xlat [ebx] ; get translated color into al
+ test al,al ; is it transparent black
+ jnz ??loop_top ; if not go and write the color
+
+;----------------------------------------- skip Top blank area ----------------------------------------
+ ; this case, the top is transparrent, but we need to increase our dest pointer to correct row.
+ movzx eax,cl ; get number of rows into eax;
+ mov ecx,edx ; save width since edx will be destroyed by mul.
+ mul [bufferwidth] ; multiply that by the width of the buffer.
+ mov edx,ecx ; restore the width
+ add edi,eax ; update the pointer.
+ jmp short ??draw_char ; now go draw the character.
+
+;----------------------------------------- fill Top blank area ----------------------------------------
+ ; edi was set a long time ago.
+ ; al is the translated color
+??loop_top:
+ stosb ; store the value
+ dec dh ; decrement our width.
+ jnz ??loop_top ; if more width, continue on.
+
+ add edi,[nextdraw] ; add amount for entire row.
+
+ dec cl ; decrement or row count
+ mov dh,dl ; restore width in dh for loop.
+ jz ??draw_char ; we are done here, go draw the character.
+ jmp short ??loop_top ; go back to top of loop.
+
+
+;----------------------------------------- Draw character ----------------------------------------------
+??draw_char:
+ movzx ecx,[charheight] ; get the height of character to count down rows.
+ test ecx,ecx ; is there any data? (blank would not have any)
+ jz ??next_char ; if no data, go on to next character.
+
+??while_data:
+ lodsb ; get byte value from font data
+ mov ah,al ; save hinibble
+ and eax,0F00FH ; mask of low nibble in al hi nibble in ah.
+ xlat [ebx] ; get new color
+
+ test al,al ; is it a transparent?
+ jz short ??skiplo ; skip over write
+ mov [es:edi],al ; write it out
+??skiplo:
+ inc edi
+ dec dh ; decrement our width.
+ jz short ??nextrow ; check if done with width of char
+
+ mov al,ah ; restore to get
+ ; test the time difference between looking up in a large table when shr al,4 is not done as
+ ; compared to using only a 16 byte table when using the shr al,4
+ ;shr al,4 ; shift the hi nibble down to low nibble
+ xlat [ebx] ; get new color
+
+ test al,al ; is it a transparent?
+ jz short ??skiphi ; skip over write
+ mov [es:edi],al ; write it out
+??skiphi:
+
+ inc edi
+ dec dh ; decrement our width.
+ jnz short ??while_data ; check if done with width of char
+
+??nextrow:
+ add edi,[nextdraw] ; go to next line.
+ dec ecx ; decrement the number of rows to go
+ mov dh,dl ; restore our column count for row.
+ jnz ??while_data ; more data for character.
+
+ ; Now it is time to setup for clearing out the bottom of the character.
+ movzx ecx,[bottomblank] ; get amount on bottom that is blank
+ cmp ecx,0 ; if there is no blank bottom...
+ jz ??next_char ; then skip to go to next character
+
+ xor eax,eax ; get color 0 for background.
+ xlat [ebx] ; get translated color into al
+ test al,al ; is it transparent black
+ jz ??next_char ; skip the top black section to let the background through
+
+ mov dh,dl ; restore width in dh for loop.
+
+;----------------------------------------- Blank below character -----------------------------------
+??loop_bottom:
+ stosb ; store the value
+ dec dh ; decrement our width.
+ jnz ??loop_bottom ; if more width, continue on.
+
+ add edi,[nextdraw] ; add amount for entire row.
+
+ mov dh,dl ; restore width in dh for loop.
+ dec cl ; decrement or row count
+ jz ??next_char ; we are done here, go to the next character.
+ jmp short ??loop_bottom ; go back to top of loop.
+
+;----------------------------------- end of next_char (main) loop ------------------------------------
+;-------------------------------------------------------------------------------------------------
+
+
+;----------------------------------- special case line feeds ----------------------------------------
+
+??force_line_feed:
+ ; decrement pointer *string so that it will be back at same character
+ ; when it goes through the loop.
+ mov eax,[string] ; get string pointer.
+ dec eax ; decrement it to point to previos char
+ mov [string],eax ; save it back off.
+ xor eax,eax
+ ; Now go into the line feed code.....
+
+??line_feed:
+ mov bl,al
+ mov edx,[y_pixel] ; get the current y pixel value.
+ movzx ecx,[maxheight] ; get max height for later use.
+ add ecx,[FontYSpacing]
+ add edx,ecx ; add max height to y_pixel
+ cmp edx,[vpheight] ; are we over the edge?
+ jg ??overflow ; if so, we are outa here.
+
+ mov eax,[bufferwidth] ; get bytes to next line.
+ mov edi,[curline] ; get start of current line.
+ mul ecx ; mult max height * next line.
+
+ add edi,eax ; add adjustment to current line.
+ add [y_pixel],ecx ; increment to our next y position.
+;;; DRD
+ mov [curline],edi ; save it off for next line_feed.
+
+ ; Move the cursor to either the left edge of the screen
+ ; or the left margin of the print position depending
+ ; on whether or was specified. = left margin
+ ; = left edge of screen
+ xor eax,eax
+ cmp bl,10
+ je ??lfeed
+ mov eax,[original_x]
+??lfeed:
+ mov [x_pixel],eax ; zero out x_pixel
+
+ add edi,eax
+;;; DRD mov [curline],edi ; save it off for next line_feed.
+ mov [startdraw],edi ; save it off so we know where to draw next char.w
+
+ jmp ??next_char
+
+??overflow:
+ mov [startdraw],0 ; Indicate that there is no valid next pos.
+??done:
+ mov eax,[startdraw] ; return this so calling routine
+ ret ; can figure out where to draw next.
+
+ ENDP Buffer_Print
+;***************************************************************************
+;* GET_FONT_PALETTE_PTR -- Returns a pointer to the 256 byte font palette *
+;* *
+;* INPUT: none *
+;* *
+;* OUTPUT: none *
+;* *
+;* PROTO: void *Get_Font_Palette_Ptr(void); *
+;* *
+;* HISTORY: *
+;* 08/18/1995 PWG : Created. *
+;*=========================================================================*
+
+ GLOBAL C Get_Font_Palette_Ptr:NEAR
+ PROC Get_Font_Palette_Ptr C near
+ mov eax, OFFSET ColorXlat
+ ret
+ ENDP Get_Font_Palette_Ptr
+
+
+END
diff --git a/CODE/AADATA.CPP b/CODE/AADATA.CPP
new file mode 100644
index 0000000..222406d
--- /dev/null
+++ b/CODE/AADATA.CPP
@@ -0,0 +1,655 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/AADATA.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : AADATA.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : July 22, 1994 *
+ * *
+ * Last Update : July 9, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * AircraftTypeClass::AircraftTypeClass -- Constructor for aircraft objects. *
+ * AircraftTypeClass::As_Reference -- Given an aircraft type, find the matching type object. *
+ * AircraftTypeClass::Create_And_Place -- Creates and places aircraft using normal game syste*
+ * AircraftTypeClass::Create_One_Of -- Creates an aircraft object of the appropriate type. *
+ * AircraftTypeClass::Dimensions -- Fetches the graphic dimensions of the aircraft type. *
+ * AircraftTypeClass::Display -- Displays a generic version of the aircraft type. *
+ * AircraftTypeClass::From_Name -- Converts an ASCII name into an aircraft type number. *
+ * AircraftTypeClass::Init_Heap -- Initialize the aircraft type class heap. *
+ * AircraftTypeClass::Max_Pips -- Fetches the maximum number of pips allowed. *
+ * AircraftTypeClass::Occupy_List -- Returns with occupation list for landed aircraft. *
+ * AircraftTypeClass::One_Time -- Performs one time initialization of the aircraft type class*
+ * AircraftTypeClass::Overlap_List -- Determines the overlap list for a landed aircraft. *
+ * AircraftTypeClass::Prep_For_Add -- Prepares the scenario editor for adding an aircraft obj*
+ * AircraftTypeClass::operator delete -- Returns aircraft type to special memory pool. *
+ * AircraftTypeClass::operator new -- Allocates an aircraft type object from special pool. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+
+
+void const * AircraftTypeClass::LRotorData = NULL;
+void const * AircraftTypeClass::RRotorData = NULL;
+
+// Badger bomber
+static AircraftTypeClass const BadgerPlane(
+ AIRCRAFT_BADGER, // What kind of aircraft is this.
+ TXT_BADGER, // Translated text number for aircraft.
+ "BADR", // INI name of aircraft.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ true, // Fixed wing aircraft?
+ false, // Equipped with a rotor?
+ false, // Custom rotor sets for each facing?
+ false, // Can this aircraft land on clear terrain?
+ true, // Is it invisible on radar?
+ false, // Can the player select it so as to give it orders?
+ true, // Can it be assigned as a target for attack.
+ false, // Is it insignificant (won't be announced)?
+ false, // Is it immune to normal combat damage?
+ STRUCT_NONE, // Preferred landing building.
+ 0xFF, // Landing speed
+ 16, // Number of rotation stages.
+ MISSION_HUNT // Default mission for aircraft.
+);
+
+// Photo recon plane.
+static AircraftTypeClass const U2Plane(
+ AIRCRAFT_U2, // What kind of aircraft is this.
+ TXT_U2, // Translated text number for aircraft.
+ "U2", // INI name of aircraft.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ true, // Fixed wing aircraft?
+ false, // Equipped with a rotor?
+ false, // Custom rotor sets for each facing?
+ false, // Can this aircraft land on clear terrain?
+ true, // Is it invisible on radar?
+ false, // Can the player select it so as to give it orders?
+ true, // Can it be assigned as a target for attack.
+ false, // Is it insignificant (won't be announced)?
+ false, // Is it immune to normal combat damage?
+ STRUCT_NONE, // Preferred landing building.
+ 0xFF, // Landing speed
+ 16, // Number of rotation stages.
+ MISSION_HUNT // Default mission for aircraft.
+);
+
+// Mig attack aircraft.
+static AircraftTypeClass const MigPlane(
+ AIRCRAFT_MIG, // What kind of aircraft is this.
+ TXT_MIG, // Translated text number for aircraft.
+ "MIG", // INI name of aircraft.
+ 0x0000, // Vertical offset.
+ 0x0020, // Primary weapon offset along turret centerline.
+ 0x0020, // Primary weapon lateral offset along turret centerline.
+ true, // Fixed wing aircraft?
+ false, // Equipped with a rotor?
+ false, // Custom rotor sets for each facing?
+ false, // Can this aircraft land on clear terrain?
+ true, // Is it invisible on radar?
+ true, // Can the player select it so as to give it orders?
+ true, // Can it be assigned as a target for attack.
+ false, // Is it insignificant (won't be announced)?
+ false, // Is it immune to normal combat damage?
+ STRUCT_AIRSTRIP, // Preferred landing building.
+ 0xC0, // Landing speed
+ 16, // Number of rotation stages.
+ MISSION_HUNT // Default mission for aircraft.
+);
+
+// Yak attack aircraft.
+static AircraftTypeClass const YakPlane(
+ AIRCRAFT_YAK, // What kind of aircraft is this.
+ TXT_YAK, // Translated text number for aircraft.
+ "YAK", // INI name of aircraft.
+ 0x0000, // Vertical offset.
+ 0x0020, // Primary weapon offset along turret centerline.
+ 0x0020, // Primary weapon lateral offset along turret centerline.
+ true, // Fixed wing aircraft?
+ false, // Equipped with a rotor?
+ false, // Custom rotor sets for each facing?
+ false, // Can this aircraft land on clear terrain?
+ true, // Is it invisible on radar?
+ true, // Can the player select it so as to give it orders?
+ true, // Can it be assigned as a target for attack.
+ false, // Is it insignificant (won't be announced)?
+ false, // Is it immune to normal combat damage?
+ STRUCT_AIRSTRIP, // Preferred landing building.
+ 0xFF, // Landing speed
+ 16, // Number of rotation stages.
+ MISSION_HUNT // Default mission for aircraft.
+);
+
+// Transport helicopter.
+static AircraftTypeClass const TransportHeli(
+ AIRCRAFT_TRANSPORT, // What kind of aircraft is this.
+ TXT_TRANS, // Translated text number for aircraft.
+ "TRAN", // INI name of aircraft.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Fixed wing aircraft?
+ true, // Equipped with a rotor?
+ true, // Custom rotor sets for each facing?
+ true, // Can this aircraft land on clear terrain?
+ true, // Is it invisible on radar?
+ true, // Can the player select it so as to give it orders?
+ true, // Can it be assigned as a target for attack.
+ false, // Is it insignificant (won't be announced)?
+ false, // Is it immune to normal combat damage?
+ STRUCT_NONE, // Preferred landing building.
+ 0xFF, // Landing speed
+ 32, // Number of rotation stages.
+ MISSION_HUNT // Default mission for aircraft.
+);
+
+// Longbow attack helicopter
+static AircraftTypeClass const AttackHeli(
+ AIRCRAFT_LONGBOW, // What kind of aircraft is this.
+ TXT_HELI, // Translated text number for aircraft.
+ "HELI", // INI name of aircraft.
+ 0x0000, // Vertical offset.
+ 0x0040, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Fixed wing aircraft?
+ true, // Equipped with a rotor?
+ false, // Custom rotor sets for each facing?
+ false, // Can this aircraft land on clear terrain?
+ true, // Is it invisible on radar?
+ true, // Can the player select it so as to give it orders?
+ true, // Can it be assigned as a target for attack.
+ false, // Is it insignificant (won't be announced)?
+ false, // Is it immune to normal combat damage?
+ STRUCT_HELIPAD, // Preferred landing building.
+ 0xFF, // Landing speed
+ 32, // Number of rotation stages.
+ MISSION_HUNT // Default mission for aircraft.
+);
+
+
+// Hind
+static AircraftTypeClass const OrcaHeli(
+ AIRCRAFT_HIND, // What kind of aircraft is this.
+ TXT_ORCA, // Translated text number for aircraft.
+ "HIND", // INI name of aircraft.
+ 0x0000, // Vertical offset.
+ 0x0040, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Fixed wing aircraft?
+ true, // Equipped with a rotor?
+ false, // Custom rotor sets for each facing?
+ false, // Can this aircraft land on clear terrain?
+ true, // Is it invisible on radar?
+ true, // Can the player select it so as to give it orders?
+ true, // Can it be assigned as a target for attack.
+ false, // Is it insignificant (won't be announced)?
+ false, // Is it immune to normal combat damage?
+ STRUCT_HELIPAD, // Preferred landing building.
+ 0xFF, // Landing speed
+ 32, // Number of rotation stages.
+ MISSION_HUNT // Default mission for aircraft.
+);
+
+
+/***********************************************************************************************
+ * AircraftTypeClass::AircraftTypeClass -- Constructor for aircraft objects. *
+ * *
+ * This is the constructor for the aircraft object. *
+ * *
+ * INPUT: see below... *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/26/1994 JLB : Created. *
+ *=============================================================================================*/
+AircraftTypeClass::AircraftTypeClass(
+ AircraftType airtype,
+ int name,
+ char const * ininame,
+ int verticaloffset,
+ int primaryoffset,
+ int primarylateral,
+ bool is_fixedwing,
+ bool is_rotorequipped,
+ bool is_rotorcustom,
+ bool is_landable,
+ bool is_stealthy,
+ bool is_selectable,
+ bool is_legal_target,
+ bool is_insignificant,
+ bool is_immune,
+ StructType building,
+ int landingspeed,
+ int rotation,
+ MissionType deforder
+ ) :
+ TechnoTypeClass(RTTI_AIRCRAFTTYPE,
+ int(airtype),
+ name,
+ ininame,
+ REMAP_NORMAL,
+ verticaloffset,
+ primaryoffset,
+ primarylateral,
+ primaryoffset,
+ primarylateral,
+ false,
+ is_stealthy,
+ is_selectable,
+ is_legal_target,
+ is_insignificant,
+ is_immune,
+ false,
+ false,
+ true,
+ true,
+ rotation,
+ SPEED_WINGED),
+ IsFixedWing(is_fixedwing),
+ IsLandable(is_landable),
+ IsRotorEquipped(is_rotorequipped),
+ IsRotorCustom(is_rotorcustom),
+ Type(airtype),
+ Mission(deforder),
+ Building(building),
+ LandingSpeed(landingspeed)
+{
+ /*
+ ** Forced aircraft overrides from the default.
+ */
+ Speed = SPEED_WINGED;
+}
+
+
+/***********************************************************************************************
+ * AircraftTypeClass::operator new -- Allocates an aircraft type object from special pool. *
+ * *
+ * This will allocate an aircraft type class object from the memory pool of that purpose. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with a pointer to the newly allocated aircraft type class object. If there *
+ * was insufficient memory to fulfill the request, then NULL is returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/09/1996 JLB : Created. *
+ *=============================================================================================*/
+void * AircraftTypeClass::operator new(size_t)
+{
+ return(AircraftTypes.Alloc());
+}
+
+
+/***********************************************************************************************
+ * AircraftTypeClass::operator delete -- Returns aircraft type to special memory pool. *
+ * *
+ * This will return the aircraft type class object back to the special memory pool that *
+ * it was allocated from. *
+ * *
+ * INPUT: pointer -- Pointer to the aircraft type class object to delete. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/09/1996 JLB : Created. *
+ *=============================================================================================*/
+void AircraftTypeClass::operator delete(void * pointer)
+{
+ AircraftTypes.Free((AircraftTypeClass *)pointer);
+}
+
+
+/***********************************************************************************************
+ * AircraftTypeClass::Init_Heap -- Initialize the aircraft type class heap. *
+ * *
+ * This will initialize the aircraft type class heap by pre-allocating all known aircraft *
+ * types. It should be called once and before the rules.ini file is processed. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/09/1996 JLB : Created. *
+ *=============================================================================================*/
+void AircraftTypeClass::Init_Heap(void)
+{
+ /*
+ ** These aircraft type class objects must be allocated in the exact order that they
+ ** are specified in the AircraftSmen enumeration. This is necessary because the heap
+ ** allocation block index serves double duty as the type number index.
+ */
+ new AircraftTypeClass(TransportHeli);
+ new AircraftTypeClass(BadgerPlane);
+ new AircraftTypeClass(U2Plane);
+ new AircraftTypeClass(MigPlane);
+ new AircraftTypeClass(YakPlane);
+ new AircraftTypeClass(AttackHeli);
+ new AircraftTypeClass(OrcaHeli);
+}
+
+
+/***********************************************************************************************
+ * AircraftTypeClass::From_Name -- Converts an ASCII name into an aircraft type number. *
+ * *
+ * This routine is used to convert an ASCII representation of an aircraft into the *
+ * matching aircraft type number. This is used by the scenario INI reader code. *
+ * *
+ * INPUT: name -- Pointer to ASCII name to translate. *
+ * *
+ * OUTPUT: Returns the aircraft type number that matches the ASCII name provided. If no *
+ * match could be found, then AIRCRAFT_NONE is returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/26/1994 JLB : Created. *
+ *=============================================================================================*/
+AircraftType AircraftTypeClass::From_Name(char const * name)
+{
+ if (name != NULL) {
+ for (AircraftType classid = AIRCRAFT_FIRST; classid < AIRCRAFT_COUNT; classid++) {
+ if (stricmp(As_Reference(classid).IniName, name) == 0) {
+ return(classid);
+ }
+ }
+ }
+ return(AIRCRAFT_NONE);
+}
+
+
+/***********************************************************************************************
+ * AircraftTypeClass::One_Time -- Performs one time initialization of the aircraft type class. *
+ * *
+ * This routine is used to perform the onetime initialization of the aircraft type. This *
+ * includes primarily the shape and other graphic data loading. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: This goes to disk and also must only be called ONCE. *
+ * *
+ * HISTORY: *
+ * 07/26/1994 JLB : Created. *
+ *=============================================================================================*/
+void AircraftTypeClass::One_Time(void)
+{
+ for (AircraftType index = AIRCRAFT_FIRST; index < AIRCRAFT_COUNT; index++) {
+ char fullname[_MAX_FNAME+_MAX_EXT];
+ AircraftTypeClass const & uclass = As_Reference(index);
+
+ /*
+ ** Fetch the supporting data files for the unit.
+ */
+ char buffer[_MAX_FNAME];
+ sprintf(buffer, "%sICON", uclass.Graphic_Name());
+ _makepath(fullname, NULL, NULL, buffer, ".SHP");
+ ((void const *&)uclass.CameoData) = MFCD::Retrieve(fullname);
+
+ /*
+ ** Generic shape for all houses load method.
+ */
+ _makepath(fullname, NULL, NULL, uclass.Graphic_Name(), ".SHP");
+ ((void const *&)uclass.ImageData) = MFCD::Retrieve(fullname);
+ }
+
+ LRotorData = MFCD::Retrieve("LROTOR.SHP");
+ RRotorData = MFCD::Retrieve("RROTOR.SHP");
+}
+
+
+/***********************************************************************************************
+ * AircraftTypeClass::Create_One_Of -- Creates an aircraft object of the appropriate type. *
+ * *
+ * This routine is used to create an aircraft object that matches the aircraft type. It *
+ * serves as a shortcut to creating an object using the "new" operator and "if" checks. *
+ * *
+ * INPUT: house -- The house owner of the aircraft that is to be created. *
+ * *
+ * OUTPUT: Returns with a pointer to the aircraft created. If the aircraft could not be *
+ * created, then a NULL is returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/26/1994 JLB : Created. *
+ *=============================================================================================*/
+ObjectClass * AircraftTypeClass::Create_One_Of(HouseClass * house) const
+{
+ return(new AircraftClass(Type, house->Class->House));
+}
+
+
+#ifdef SCENARIO_EDITOR
+/***********************************************************************************************
+ * AircraftTypeClass::Prep_For_Add -- Prepares the scenario editor for adding an aircraft objec*
+ * *
+ * This routine is used by the scenario editor to prepare for the adding operation. It *
+ * builds a list of pointers to object types that can be added. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/26/1994 JLB : Created. *
+ *=============================================================================================*/
+void AircraftTypeClass::Prep_For_Add(void)
+{
+ for (AircraftType index = AIRCRAFT_FIRST; index < AIRCRAFT_COUNT; index++) {
+ if (As_Reference(index).Get_Image_Data()) {
+ Map.Add_To_List(&As_Reference(index));
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * AircraftTypeClass::Display -- Displays a generic version of the aircraft type. *
+ * *
+ * This routine is used by the scenario editor to display a generic version of the object *
+ * type. This is displayed in the object selection dialog box. *
+ * *
+ * INPUT: x,y -- The coordinates to draw the aircraft at (centered). *
+ * *
+ * window -- The window to base the coordinates upon. *
+ * *
+ * house -- The owner of this generic aircraft. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/26/1994 JLB : Created. *
+ *=============================================================================================*/
+void AircraftTypeClass::Display(int x, int y, WindowNumberType window, HousesType ) const
+{
+ int shape = 0;
+ void const * ptr = Get_Cameo_Data();
+ if (ptr == NULL) {
+ ptr = Get_Image_Data();
+ shape = 5;
+ }
+ CC_Draw_Shape(ptr, shape, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL);
+}
+#endif
+
+
+/***********************************************************************************************
+ * AircraftTypeClass::Occupy_List -- Returns with occupation list for landed aircraft. *
+ * *
+ * This determines the occupation list for the aircraft (if it was landed). *
+ * *
+ * INPUT: placement -- Is this for placement legality checking only? The normal condition *
+ * is for marking occupation flags. *
+ * *
+ * OUTPUT: Returns with a pointer to a cell offset occupation list for the aircraft. *
+ * *
+ * WARNINGS: This occupation list is only valid if the aircraft is landed. *
+ * *
+ * HISTORY: *
+ * 07/26/1994 JLB : Created. *
+ *=============================================================================================*/
+short const * AircraftTypeClass::Occupy_List(bool) const
+{
+ static short const _list[] = {0, REFRESH_EOL};
+ return(_list);
+}
+
+
+/***********************************************************************************************
+ * AircraftTypeClass::Overlap_List -- Determines the overlap list for a landed aircraft. *
+ * *
+ * This routine figures out the overlap list for the aircraft as if it were landed. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the cell offset overlap list for the aircraft. *
+ * *
+ * WARNINGS: This overlap list is only valid when the aircraft is landed. *
+ * *
+ * HISTORY: *
+ * 07/26/1994 JLB : Created. *
+ *=============================================================================================*/
+short const * AircraftTypeClass::Overlap_List(void) const
+{
+ static short const _list[] = {-(MAP_CELL_W-1), -MAP_CELL_W, -(MAP_CELL_W+1), -1, 1, (MAP_CELL_W-1), MAP_CELL_W, (MAP_CELL_W+1), REFRESH_EOL};
+ return(_list);
+}
+
+
+/***********************************************************************************************
+ * AircraftTypeClass::Max_Pips -- Fetches the maximum number of pips allowed. *
+ * *
+ * Use this routine to retrieve the maximum pip count allowed for this aircraft. This is *
+ * the maximum number of passengers. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the maximum number of pips for this aircraft. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/26/1995 JLB : Created. *
+ *=============================================================================================*/
+int AircraftTypeClass::Max_Pips(void) const
+{
+ if (PrimaryWeapon != NULL) {
+ return(5);
+ }
+ return(Max_Passengers());
+}
+
+
+/***********************************************************************************************
+ * AircraftTypeClass::Create_And_Place -- Creates and places aircraft using normal game system *
+ * *
+ * This routine is used to create and place an aircraft through the normal game system. *
+ * Since creation of aircraft in this fashion is prohibited, this routine does nothing. *
+ * *
+ * INPUT: na *
+ * *
+ * OUTPUT: Always returns a failure code (false). *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/07/1995 JLB : Created. *
+ *=============================================================================================*/
+bool AircraftTypeClass::Create_And_Place(CELL, HousesType) const
+{
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * AircraftTypeClass::Dimensions -- Fetches the graphic dimensions of the aircraft type. *
+ * *
+ * This routine will fetch the pixel dimensions of this aircraft type. These dimensions *
+ * are used to control map refresh and select box rendering. *
+ * *
+ * INPUT: width -- Reference to variable that will be filled in with aircraft width. *
+ * *
+ * height -- Reference to variable that will be filled in with aircraft height. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/07/1995 JLB : Created. *
+ *=============================================================================================*/
+void AircraftTypeClass::Dimensions(int &width, int &height) const
+{
+ if (Type == AIRCRAFT_BADGER) {
+ width = 56;
+ height = 56;
+ } else {
+ width = 21;
+ height = 20;
+ }
+}
+
+
+/***********************************************************************************************
+ * AircraftTypeClass::As_Reference -- Given an aircraft type, find the matching type object. *
+ * *
+ * This routine is used to fetch a reference to the aircraft type class object that matches *
+ * the aircraft type specified. *
+ * *
+ * INPUT: aircraft -- The aircraft type to fetch a reference to the type class object of. *
+ * *
+ * OUTPUT: Returns with a reference to the type class object of this aircraft type. *
+ * *
+ * WARNINGS: Be sure that the aircraft type specified is legal. Illegal values will result *
+ * in undefined behavior. *
+ * *
+ * HISTORY: *
+ * 07/09/1996 JLB : Created. *
+ *=============================================================================================*/
+AircraftTypeClass & AircraftTypeClass::As_Reference(AircraftType aircraft)
+{
+ return(*AircraftTypes.Ptr(aircraft));
+}
diff --git a/CODE/ABSTRACT.CPP b/CODE/ABSTRACT.CPP
new file mode 100644
index 0000000..3744722
--- /dev/null
+++ b/CODE/ABSTRACT.CPP
@@ -0,0 +1,216 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/ABSTRACT.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : ABSTRACT.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 01/26/95 *
+ * *
+ * Last Update : July 10, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * AbstractClass::Debug_Dump -- Display debug information to mono screen. *
+ * AbstractClass::Distance -- Determines distance to target. *
+ * AbstractTypeClass::AbstractTypeClass -- Constructor for abstract type objects. *
+ * AbstractTypeClass::Coord_Fixup -- Performs custom adjustments to location coordinate. *
+ * AbstractTypeClass::Full_Name -- Returns the full name (number) of this object type. *
+ * AbstractTypeClass::Get_Ownable -- Fetch the ownable bits for this object. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+
+
+/***********************************************************************************************
+ * AbstractClass::Debug_Dump -- Display debug information to mono screen. *
+ * *
+ * This debug only routine will display various information about this abstract class *
+ * object to the monochrome screen specified. *
+ * *
+ * INPUT: mono -- Pointer to the monochrome screen to display the debug information to. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/10/1996 JLB : Created. *
+ *=============================================================================================*/
+#ifdef CHEAT_KEYS
+void AbstractClass::Debug_Dump(MonoClass * mono) const
+{
+ assert(IsActive);
+
+ mono->Set_Cursor(11, 5);mono->Printf("%08X", As_Target());
+ mono->Set_Cursor(20, 1);mono->Printf("%08X", Coord);
+ mono->Set_Cursor(29, 1);mono->Printf("%3d", Height);
+ if (Owner() != HOUSE_NONE) {
+ mono->Set_Cursor(1, 3);
+ mono->Printf("%-18s", Text_String(HouseTypeClass::As_Reference(Owner()).FullName));
+ }
+}
+#endif
+
+
+/***********************************************************************************************
+ * AbstractClass::Distance -- Determines distance to target. *
+ * *
+ * This will determine the distance (direct line) to the target. The distance is in *
+ * 'leptons'. This routine is typically used for weapon range checks. *
+ * *
+ * INPUT: target -- The target to determine range to. *
+ * *
+ * OUTPUT: Returns with the range to the specified target (in leptons). *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/17/1994 JLB : Created. *
+ *=============================================================================================*/
+int AbstractClass::Distance(TARGET target) const
+{
+ /*
+ ** Should subtract a fudge-factor distance for building targets.
+ */
+ BuildingClass * obj = As_Building(target);
+ int dist = Distance(As_Coord(target));
+
+ /*
+ ** If the object is a building the adjust it by the average radius
+ ** of the object.
+ */
+ if (obj) {
+ dist -= ((obj->Class->Width() + obj->Class->Height()) * (0x100 / 4));
+ if (dist < 0) dist = 0;
+ }
+
+ /*
+ ** Return the distance to the target
+ */
+ return(dist);
+}
+
+
+/***********************************************************************************************
+ * AbstractTypeClass::AbstractTypeClass -- Constructor for abstract type objects. *
+ * *
+ * This is the constructor for AbstractTypeClass objects. It initializes the INI name and *
+ * the text name for this object type. *
+ * *
+ * INPUT: name -- Text number for the full name of the object. *
+ * *
+ * ini -- The ini name for this object type. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/22/1995 JLB : Created. *
+ *=============================================================================================*/
+AbstractTypeClass::AbstractTypeClass(RTTIType rtti, int id, int name, char const * ini) :
+ RTTI(rtti),
+ ID(id),
+ FullName(name)
+{
+ strncpy((char *)IniName, ini, sizeof(IniName));
+ ((char &)IniName[sizeof(IniName)-1]) = '\0';
+}
+
+
+/***********************************************************************************************
+ * AbstractTypeClass::Coord_Fixup -- Performs custom adjustments to location coordinate. *
+ * *
+ * This routine is called when the placement coordinate should be fixed up according *
+ * to any special rules specific to this object type. At this level, no transformation *
+ * occurs. Derived classes will transform the coordinate as necessary. *
+ * *
+ * INPUT: coord -- The proposed coordinate that this object type will be placed down at. *
+ * *
+ * OUTPUT: Returns with the adjusted coordinate that the object should be placed down at. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 09/21/1995 JLB : Created. *
+ *=============================================================================================*/
+COORDINATE AbstractTypeClass::Coord_Fixup(COORDINATE coord) const
+{
+ return(coord);
+}
+
+
+/***********************************************************************************************
+ * AbstractTypeClass::Full_Name -- Returns the full name (number) of this object type. *
+ * *
+ * This routine is used to fetch the full name of this object type. The name value *
+ * returned is actually the index number into the text array. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the full name index number for this object type. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 09/21/1995 JLB : Created. *
+ *=============================================================================================*/
+int AbstractTypeClass::Full_Name(void) const
+{
+#ifdef FIXIT_NAME_OVERRIDE
+ for (int index = 0; index < ARRAY_SIZE(NameOverride); index++) {
+ if (NameIDOverride[index] == ((RTTI+1) * 100) + ID) {
+ return(-(index+1));
+ }
+ }
+#endif
+ return(FullName);
+}
+
+
+/***********************************************************************************************
+ * AbstractTypeClass::Get_Ownable -- Fetch the ownable bits for this object. *
+ * *
+ * This returns a bit flag that indicates which houses are allowed to own this object *
+ * type. At this level, all houses have ownership rights. This routine will be overridden *
+ * by object types that restrict ownership. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with a bit flag indicating which houses have ownership rights. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 09/21/1995 JLB : Created. *
+ *=============================================================================================*/
+int AbstractTypeClass::Get_Ownable(void) const
+{
+ return(HOUSEF_ALLIES | HOUSEF_SOVIET | HOUSEF_OTHERS);
+}
+
+
+
diff --git a/CODE/ABSTRACT.H b/CODE/ABSTRACT.H
new file mode 100644
index 0000000..0189a32
--- /dev/null
+++ b/CODE/ABSTRACT.H
@@ -0,0 +1,135 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/ABSTRACT.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : ABSTRACT.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 01/26/95 *
+ * *
+ * Last Update : January 26, 1995 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifndef ABSTRACT_H
+#define ABSTRACT_H
+
+DirType Direction(CELL cell1, CELL cell2);
+DirType Direction(COORDINATE coord1, COORDINATE coord2);
+int Distance(COORDINATE coord1, COORDINATE coord2);
+COORDINATE As_Coord(TARGET target);
+
+class AbstractTypeClass;
+
+/*
+** This class is the base class for all game objects that have an existence on the
+** battlefield.
+*/
+class AbstractClass
+{
+ public:
+
+ /*
+ ** This specifies the type of object and the unique ID number
+ ** associated with it. The ID number happens to match the index into
+ ** the object heap appropriate for this object type.
+ */
+ RTTIType RTTI;
+ int ID;
+
+ /*
+ ** The coordinate location of the unit. For vehicles, this is the center
+ ** point. For buildings, it is the upper left corner.
+ */
+ COORDINATE Coord;
+
+ /*
+ ** This is the height of the object above ground (expressed in leptons).
+ */
+ int Height;
+
+ /*
+ ** The actual object ram-space is located in arrays in the data segment. This flag
+ ** is used to indicate which objects are free to be reused and which are currently
+ ** in use by the game.
+ */
+ unsigned IsActive:1;
+
+ /*-----------------------------------------------------------------------------------
+ ** Constructor & destructors.
+ */
+ AbstractClass(RTTIType rtti, int id) : RTTI(rtti), ID(id), Coord(0xFFFFFFFFL), Height(0) {};
+ AbstractClass(NoInitClass const & x) {x();};
+ virtual ~AbstractClass(void) {};
+
+ /*
+ ** Query functions.
+ */
+ virtual char const * Name(void) const {return("");}
+ virtual HousesType Owner(void) const {return HOUSE_NONE;};
+ TARGET As_Target(void) const {return(Build_Target(RTTI, ID));};
+ RTTIType What_Am_I(void) const {return(RTTI);};
+
+ /*
+ ** Scenario and debug support.
+ */
+ #ifdef CHEAT_KEYS
+ virtual void Debug_Dump(MonoClass * mono) const;
+ #endif
+
+ /*
+ ** Coordinate query support functions.
+ */
+ virtual COORDINATE Center_Coord(void) const {return Coord;};
+ virtual COORDINATE Target_Coord(void) const {return Coord;};
+
+ /*
+ ** Coordinate inquiry functions. These are used for both display and
+ ** combat purposes.
+ */
+ DirType Direction(AbstractClass const * object) const {return ::Direction(Center_Coord(), object->Target_Coord());};
+ DirType Direction(COORDINATE coord) const {return ::Direction(Center_Coord(), coord);};
+ DirType Direction(TARGET target) const {return ::Direction(Center_Coord(), As_Coord(target));};
+ DirType Direction(CELL cell) const {return ::Direction(Coord_Cell(Center_Coord()), cell);};
+ int Distance(TARGET target) const;
+ int Distance(COORDINATE coord) const {return ::Distance(Center_Coord(), coord);};
+ int Distance(AbstractClass const * object) const {return ::Distance(Center_Coord(), object->Target_Coord());};
+
+ /*
+ ** Object entry and exit from the game system.
+ */
+ virtual MoveType Can_Enter_Cell(CELL , FacingType = FACING_NONE) const {return MOVE_OK;};
+
+ /*
+ ** AI.
+ */
+ virtual void AI(void) {};
+
+};
+
+
+#endif
diff --git a/CODE/ADAMTEMP.MAK b/CODE/ADAMTEMP.MAK
new file mode 100644
index 0000000..a4065c6
--- /dev/null
+++ b/CODE/ADAMTEMP.MAK
@@ -0,0 +1,1847 @@
+#
+# Command & Conquer Red Alert(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&c0\vcs\code\bfile.mav 5.0 11 Nov 1996 09:40: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 : MAKEFILE *
+#* *
+#* Programmer : Joe L. Bostic *
+#* *
+#* Start Date : March 25, 1993 *
+#* *
+#* Last Update : March 25, 1993 [JLB] *
+#* *
+#*-------------------------------------------------------------------------*
+
+
+# Comment out the following line to disable "include file autodependency".
+.AUTODEPEND
+#.SWAP
+
+!include "rules.mak"
+
+
+##########################################################################
+
+MAPFILES = \
+
+CACHEMAP = \
+ BRIEFING.AUD \
+
+LOCALFILES = \
+ PROLOG.CPS \
+ MAP.AUD \
+ TITLE.CPS \
+ PALETTE.CPS \
+ INTRO.AUD \
+ EGOPAL.PAL \
+ RULES.INI \
+ CREDITS.TXT \
+ ALIPAPER.CPS \
+ 3POINT.FNT \
+ 8POINT.FNT \
+ EDITFNT.FNT \
+ CONQUER.ENG \
+ DEBUG.ENG \
+ LED.FNT \
+ SNOW.PAL \
+ TEMPERAT.PAL \
+ INTERIOR.PAL \
+ VCR.FNT \
+ HOLE0000.LUT \
+ HOLE0001.LUT \
+ HOLE0002.LUT \
+ HOLE0003.LUT \
+ HOLE0004.LUT \
+ HOLE0005.LUT \
+ HOLE0006.LUT \
+ HOLE0007.LUT \
+ HOLE0008.LUT \
+ HOLE0009.LUT \
+ HOLE0010.LUT \
+ HOLE0011.LUT \
+ HOLE0012.LUT \
+ HOLE0013.LUT \
+ HOLE0014.LUT \
+ HOLE0015.LUT \
+ HOLE0016.LUT \
+ HOLE0017.LUT \
+ HOLE0018.LUT \
+ HOLE0019.LUT \
+ HOLE0020.LUT \
+ HOLE0021.LUT \
+ HOLE0022.LUT \
+ HOLE0023.LUT \
+ HOLE0024.LUT \
+ HOLE0025.LUT \
+ HOLE0026.LUT \
+ HOLE0027.LUT \
+ HOLE0028.LUT \
+ HOLE0029.LUT \
+ HOLE0030.LUT \
+ HOLE0031.LUT \
+ HOLE0032.LUT \
+ HOLE0033.LUT \
+ HOLE0034.LUT \
+ HOLE0035.LUT \
+ HOLE0036.LUT \
+ HOLE0037.LUT \
+ HOLE0038.LUT \
+ HOLE0039.LUT \
+ HOLE0040.LUT \
+ HOLE0041.LUT \
+ HOLE0042.LUT \
+ HOLE0043.LUT \
+ HOLE0044.LUT \
+ HOLE0045.LUT \
+ HOLE0046.LUT \
+ HOLE0047.LUT \
+# TEMPSCOR.FNT \
+# 6POINT.FNT \
+# GRAD6FNT.FNT \
+# SCOREFNT.FNT \
+
+
+# Files that have counterparts in both high and low resolutions.
+# These files will be built into the HIRES.MIX and LORES.MIX files.
+HILORES = \
+ TRANICON.SHP \
+ PIPS.SHP \
+ PULSE.SHP \
+ ATOMICON.SHP \
+ WARPICON.SHP \
+ C1.SHP \
+ C2.SHP \
+ CHAN.SHP \
+ DELPHI.SHP \
+ E1.SHP \
+ E2.SHP \
+ E3.SHP \
+ E4.SHP \
+ E5.SHP \
+ E6.SHP \
+ E7.SHP \
+ EINSTEIN.SHP \
+ GNRL.SHP \
+ MEDI.SHP \
+ SPY.SHP \
+ THF.SHP \
+ DD-BKGND.SHP \
+ DD-BOTM.SHP \
+ DD-CRNR.SHP \
+ DD-EDGE.SHP \
+ DD-LEFT.SHP \
+ DD-RIGHT.SHP \
+ DD-TOP.SHP \
+ 12METFNT.FNT \
+ GRAD6FNT.FNT \
+ HELP.FNT \
+ 6POINT.FNT \
+ TYPE.FNT \
+ SCOREFNT.FNT \
+ 1TNKICON.SHP \
+ 2TNKICON.SHP \
+ 3TNKICON.SHP \
+ 4TNKICON.SHP \
+ AFLDICON.SHP \
+ AGUNICON.SHP \
+ APCICON.SHP \
+ APWRICON.SHP \
+ ARTYICON.SHP \
+ ATEKICON.SHP \
+ BADRICON.SHP \
+ BARRICON.SHP \
+ BRIKICON.SHP \
+ BTN-DN.SHP \
+ BTN-PL.SHP \
+ BTN-ST.SHP \
+ BTN-UP.SHP \
+ CAICON.SHP \
+ CAMICON.SHP \
+ CLOCK.SHP \
+ DDICON.SHP \
+ DOGICON.SHP \
+ DOMEICON.SHP \
+ DOMFICON.SHP \
+ E1ICON.SHP \
+ E2ICON.SHP \
+ E3ICON.SHP \
+ E4ICON.SHP \
+ E6ICON.SHP \
+ E7ICON.SHP \
+ FACFICON.SHP \
+ FACTICON.SHP \
+ FENCICON.SHP \
+ FIXICON.SHP \
+ FTURICON.SHP \
+ GAPICON.SHP \
+ GPSSICON.SHP \
+ GUNICON.SHP \
+ HARVICON.SHP \
+ HBOXICON.SHP \
+ HELIICON.SHP \
+ HINDICON.SHP \
+ HPADICON.SHP \
+ INFXICON.SHP \
+ IRONICON.SHP \
+ JEEPICON.SHP \
+ KENNICON.SHP \
+ LSTICON.SHP \
+ MAP.SHP \
+ MCVICON.SHP \
+ MEDIICON.SHP \
+ MGGICON.SHP \
+ MIGICON.SHP \
+ MNLYICON.SHP \
+ MOUSE.SHP \
+ MRJICON.SHP \
+ MSLOICON.SHP \
+ NATORADR.SHP \
+ PBMBICON.SHP \
+ PBOXICON.SHP \
+ PDOXICON.SHP \
+ PINFICON.SHP \
+ POWER.SHP \
+ POWERBAR.SHP \
+ POWRICON.SHP \
+ PROCICON.SHP \
+ PTICON.SHP \
+ REPAIR.SHP \
+ SAMICON.SHP \
+ SBAGICON.SHP \
+ SELL.SHP \
+ SIDEBAR.SHP \
+ SILOICON.SHP \
+ SMIGICON.SHP \
+ SONRICON.SHP \
+ SOVPAPER.CPS \
+ SPEFICON.SHP \
+ SPENICON.SHP \
+ SPYICON.SHP \
+ SSICON.SHP \
+ STEKICON.SHP \
+ STRIP.SHP \
+ STRIPDN.SHP \
+ STRIPUP.SHP \
+ SYRDICON.SHP \
+ SYRFICON.SHP \
+ TABS.SHP \
+ TENTICON.SHP \
+ THFICON.SHP \
+ TRUKICON.SHP \
+ TSLAICON.SHP \
+ U2ICON.SHP \
+ USSRRADR.SHP \
+ V2RLICON.SHP \
+ WEAFICON.SHP \
+ WEAPICON.SHP \
+ YAKICON.SHP \
+ NRADRFRM.SHP \
+ URADRFRM.SHP \
+ SIDE1NA.SHP \
+ SIDE1US.SHP \
+ SIDE2NA.SHP \
+ SIDE2US.SHP \
+ SIDE3NA.SHP \
+ SIDE3US.SHP \
+ STRIPNA.SHP \
+ STRIPUS.SHP \
+
+# MOEBICON.SHP \
+
+HILORES1 = \
+ MECH.SHP \
+ SHOK.SHP \
+ CARRICON.SHP \
+ CTNKICON.SHP \
+ DTRKICON.SHP \
+ MECHICON.SHP \
+ MSUBICON.SHP \
+ QTNKICON.SHP \
+ SHOKICON.SHP \
+ STNKICON.SHP \
+ TTNKICON.SHP \
+
+# These helper macros substitute the extension so that
+# the appropriate art build rule will be invoked.
+xLOHILORES = $(HILORES:.SHP=.LOW)
+LOHILORES = $(xLOHILORES:.FNT=.LNT)
+xHIHILORES = $(HILORES:.SHP=.HI)
+HIHILORES = $(xHIHILORES:.FNT=.HNT)
+
+xLOHILORES1 = $(HILORES1:.SHP=.LOW)
+LOHILORES1 = $(xLOHILORES1:.FNT=.LNT)
+xHIHILORES1 = $(HILORES1:.SHP=.HI)
+HIHILORES1 = $(xHIHILORES1:.FNT=.HNT)
+
+#
+# Files required for hires/Win95 version only
+#
+# This mix file is cached
+#
+HIRESFILES = \
+ ALIPAPER.PCX \
+ PROLOG.PCX \
+ SOVPAPER.PCX \
+ AFTR_HI.PCX \
+ ALY1.PCX \
+ APC_HI.PCX \
+ APHI0049.PCX \
+ BNHI0020.PCX \
+ DCHI0040.PCX \
+ FRHI0166.PCX \
+ LAB.PCX \
+ LANDSBRG.PCX \
+ MAHI0107.PCX \
+ MIG_HI.PCX \
+ MTFACTHI.PCX \
+ NEEDLE.PCX \
+ SOV2.PCX \
+ SPY.PCX \
+ STALIN.PCX \
+ TENT.PCX \
+# ENG_HI.PCX \
+
+
+CONQUERFILES = \
+ PARABOMB.SHP \
+
+GENERALMAPFILES = \
+ MISSIONS.PKT \
+ CSTRIKE.PKT \
+ TUTORIAL.INI \
+ SCG01EA.INI \
+ SCG40EA.INI \
+ SCG41EA.INI \
+ SCG42EA.INI \
+ SCG43EA.INI \
+ SCG44EA.INI \
+ SCG45EA.INI \
+ SCG46EA.INI \
+ SCG47EA.INI \
+ SCG48EA.INI \
+ SCU40EA.INI \
+ SCU41EA.INI \
+ SCU42EA.INI \
+ SCU43EA.INI \
+ SCU44EA.INI \
+ SCU45EA.INI \
+ SCU46EA.INI \
+ SCU47EA.INI \
+ SCU48EA.INI \
+ SCU01EA.INI \
+ SCM01EA.INI \
+ SCM02EA.INI \
+ SCM03EA.INI \
+ SCM04EA.INI \
+ SCM05EA.INI \
+ SCM06EA.INI \
+ SCM07EA.INI \
+ SCM08EA.INI \
+ SCM09EA.INI \
+ SCM10EA.INI \
+ SCM11EA.INI \
+ SCM12EA.INI \
+ SCM13EA.INI \
+ SCM14EA.INI \
+ SCM15EA.INI \
+ SCM16EA.INI \
+ SCM17EA.INI \
+ SCM18EA.INI \
+ SCM19EA.INI \
+ SCM20EA.INI \
+ SCM21EA.INI \
+ SCM22EA.INI \
+ SCM23EA.INI \
+ SCM24EA.INI \
+ SCMD0EA.INI \
+ SCMD1EA.INI \
+ SCMD2EA.INI \
+ SCMD3EA.INI \
+ SCMD4EA.INI \
+ SCMD5EA.INI \
+ SCMD6EA.INI \
+ SCMD7EA.INI \
+ SCMD8EA.INI \
+ SCMD9EA.INI \
+ SCME0EA.INI \
+ SCME1EA.INI \
+ SCME2EA.INI \
+ SCME3EA.INI \
+ SCME4EA.INI \
+ SCME5EA.INI \
+ SCME6EA.INI \
+ SCME7EA.INI \
+ SCME8EA.INI \
+ SCME9EA.INI \
+ SCMF0EA.INI \
+ SCMF1EA.INI \
+ SCMF2EA.INI \
+ SCMF3EA.INI \
+ SCMF4EA.INI \
+ SCMF5EA.INI \
+ SCMF6EA.INI \
+ SCMF7EA.INI \
+ SCMF8EA.INI \
+ SCMF9EA.INI \
+ SCMG0EA.INI \
+ SCMG1EA.INI \
+ SCMG2EA.INI \
+ SCMG3EA.INI \
+ SCMG4EA.INI \
+ SCMG5EA.INI \
+ SCMG6EA.INI \
+ SCMG7EA.INI \
+ SCMG8EA.INI \
+ SCMG9EA.INI \
+ SCMH0EA.INI \
+ SCMH1EA.INI \
+ SCMH2EA.INI \
+ SCMH3EA.INI \
+ SCMH4EA.INI \
+ SCMH5EA.INI \
+ SCMH6EA.INI \
+ SCMH7EA.INI \
+ SCMH8EA.INI \
+ SCMH9EA.INI \
+ SCMI0EA.INI \
+ SCMI1EA.INI \
+ SCMI2EA.INI \
+ SCMI3EA.INI \
+ SCMI4EA.INI \
+ SCMI5EA.INI \
+ SCMI6EA.INI \
+ SCMI7EA.INI \
+ SCMI8EA.INI \
+ SCMI9EA.INI \
+ SCMJ0EA.INI \
+ SCMJ1EA.INI \
+ SCMJ2EA.INI \
+ SCMJ3EA.INI \
+ SCMJ4EA.INI \
+ SCMJ5EA.INI \
+ SCMJ6EA.INI \
+ SCMJ7EA.INI \
+ SCMJ8EA.INI \
+ SCMJ9EA.INI \
+ SCMK0EA.INI \
+ SCMK1EA.INI \
+ SCMK2EA.INI \
+ SCMK3EA.INI \
+ SCMK4EA.INI \
+ SCMK5EA.INI \
+ SCMK6EA.INI \
+ SCMK7EA.INI \
+ SCMK8EA.INI \
+ SCMK9EA.INI \
+ SCML0EA.INI \
+ SCML1EA.INI \
+ SCML2EA.INI \
+ SCML3EA.INI \
+ SCML4EA.INI \
+ SCML5EA.INI \
+ SCML6EA.INI \
+ SCML7EA.INI \
+ SCML8EA.INI \
+ SCML9EA.INI \
+ SCMM0EA.INI \
+ SCMM1EA.INI \
+ SCMM2EA.INI \
+ SCMM3EA.INI \
+ SCMM4EA.INI \
+ SCMM5EA.INI \
+ SCMM6EA.INI \
+ SCMM7EA.INI \
+ SCMM8EA.INI \
+ SCMM9EA.INI \
+ SCM25EA.INI \
+ SCM26EA.INI \
+ SCM27EA.INI \
+ SCM28EA.INI \
+ SCM29EA.INI \
+ SCM30EA.INI \
+ SCM31EA.INI \
+ SCM32EA.INI \
+ SCM33EA.INI \
+ SCM34EA.INI \
+ SCM35EA.INI \
+ SCM36EA.INI \
+ SCM37EA.INI \
+ SCM38EA.INI \
+ SCM39EA.INI \
+ SCM40EA.INI \
+ SCM41EA.INI \
+ SCM42EA.INI \
+ SCM43EA.INI \
+ SCM44EA.INI \
+ SCM45EA.INI \
+ SCM46EA.INI \
+ SCM47EA.INI \
+ SCM48EA.INI \
+ SCM49EA.INI \
+ SCM50EA.INI \
+ SCM51EA.INI \
+ SCM52EA.INI \
+ SCM53EA.INI \
+ SCM54EA.INI \
+ SCM55EA.INI \
+ SCM56EA.INI \
+ SCM57EA.INI \
+ SCM58EA.INI \
+ SCM59EA.INI \
+ SCM60EA.INI \
+ SCM61EA.INI \
+ SCM62EA.INI \
+ SCM63EA.INI \
+ SCM64EA.INI \
+ SCM65EA.INI \
+ SCM66EA.INI \
+ SCM67EA.INI \
+ SCM68EA.INI \
+ SCM69EA.INI \
+ SCM70EA.INI \
+ SCM71EA.INI \
+ SCM72EA.INI \
+ SCM73EA.INI \
+ SCM74EA.INI \
+ SCM75EA.INI \
+ SCM76EA.INI \
+ SCM77EA.INI \
+ SCM78EA.INI \
+ SCM79EA.INI \
+ SCM80EA.INI \
+ SCM81EA.INI \
+ SCM82EA.INI \
+ SCM83EA.INI \
+ SCM84EA.INI \
+ SCM85EA.INI \
+ SCM86EA.INI \
+ SCM87EA.INI \
+ SCM88EA.INI \
+ SCM89EA.INI \
+ SCM90EA.INI \
+ SCM91EA.INI \
+ SCM92EA.INI \
+ SCM93EA.INI \
+ SCM94EA.INI \
+ SCM95EA.INI \
+ SCM96EA.INI \
+ SCM97EA.INI \
+ SCM98EA.INI \
+ SCM99EA.INI \
+ SCM100EA.INI \
+ SCM101EA.INI \
+ SCM102EA.INI \
+ SCM103EA.INI \
+ SCM104EA.INI \
+ SCM105EA.INI \
+ SCM106EA.INI \
+ SCM107EA.INI \
+ SCM108EA.INI \
+ SCM109EA.INI \
+ SCM110EA.INI \
+ SCM111EA.INI \
+ SCM112EA.INI \
+ SCM113EA.INI \
+ SCM114EA.INI \
+ SCM115EA.INI \
+ SCM116EA.INI \
+ SCM117EA.INI \
+ SCM118EA.INI \
+ SCM119EA.INI \
+ SCM120EA.INI \
+ SCM121EA.INI \
+ SCM122EA.INI \
+ SCM123EA.INI \
+ SCM124EA.INI \
+ SCM125EA.INI \
+ SCM126EA.INI \
+ SCM127EA.INI \
+ SCM128EA.INI \
+ SCM129EA.INI \
+ SCM130EA.INI \
+
+NETMAPFILES = \
+
+# Files that aren't cached.
+GENERALFILES = \
+ AFTR_LO.CPS \
+ ALY1-LO.CPS \
+ APC_LO.CPS \
+ APLO0049.CPS \
+ BNLO0020.CPS \
+ DCLO0040.CPS \
+ FRLO0166.CPS \
+ LAB-LO.CPS \
+ LANDS-LO.CPS \
+ MALO0107.CPS \
+ MIG_LO.CPS \
+ MTFACTLO.CPS \
+ NEEDL-LO.CPS \
+ SOV2-LO.CPS \
+ SPY-LO.CPS \
+ STALN-LO.CPS \
+ TENT-LO.CPS \
+ TITLE.CPS \
+ PPAPER.CPS \
+ MSAA.WSA \
+ MSAB.WSA \
+ MSAC.WSA \
+ MSAD.WSA \
+ MSAE.WSA \
+ MSAF.WSA \
+ MSAG.WSA \
+ MSAH.WSA \
+ MSAI.WSA \
+ MSAJ.WSA \
+ MSAK.WSA \
+ MSAL.WSA \
+ MSAM.WSA \
+ MSAN.WSA \
+ MSSA.WSA \
+ MSSB.WSA \
+ MSSC.WSA \
+ MSSD.WSA \
+ MSSE.WSA \
+ MSSF.WSA \
+ MSSG.WSA \
+ MSSH.WSA \
+ MSSI.WSA \
+ MSSJ.WSA \
+ MSSK.WSA \
+ MSSL.WSA \
+ MSSM.WSA \
+ MSSN.WSA \
+
+INTERIORFILES = \
+ BOXES01.INT \
+ BOXES02.INT \
+ BOXES03.INT \
+ BOXES04.INT \
+ BOXES05.INT \
+ BOXES06.INT \
+ BOXES07.INT \
+ BOXES08.INT \
+ BOXES09.INT \
+ XTRA0001.INT \
+ XTRA0002.INT \
+ XTRA0003.INT \
+ XTRA0004.INT \
+ XTRA0005.INT \
+ XTRA0006.INT \
+ XTRA0007.INT \
+ XTRA0008.INT \
+ XTRA0009.INT \
+ XTRA0010.INT \
+ XTRA0011.INT \
+ XTRA0012.INT \
+ XTRA0013.INT \
+ XTRA0014.INT \
+ XTRA0015.INT \
+ XTRA0016.INT \
+ CLEAR1.INT \
+ MOVEFLSH.INT \
+ ARRO0001.INT \
+ ARRO0002.INT \
+ ARRO0003.INT \
+ ARRO0004.INT \
+ ARRO0005.INT \
+ ARRO0006.INT \
+ ARRO0007.INT \
+ ARRO0008.INT \
+ ARRO0009.INT \
+ ARRO0010.INT \
+ ARRO0011.INT \
+ ARRO0012.INT \
+ ARRO0013.INT \
+ ARRO0014.INT \
+ ARRO0015.INT \
+ FLOR0001.INT \
+ FLOR0002.INT \
+ FLOR0003.INT \
+ FLOR0004.INT \
+ FLOR0005.INT \
+ FLOR0006.INT \
+ FLOR0007.INT \
+ GFLR0001.INT \
+ GFLR0002.INT \
+ GFLR0003.INT \
+ GFLR0004.INT \
+ GFLR0005.INT \
+ GSTR0001.INT \
+ GSTR0002.INT \
+ GSTR0003.INT \
+ GSTR0004.INT \
+ GSTR0005.INT \
+ GSTR0006.INT \
+ GSTR0007.INT \
+ GSTR0008.INT \
+ GSTR0009.INT \
+ GSTR0010.INT \
+ GSTR0011.INT \
+ LWAL0001.INT \
+ LWAL0002.INT \
+ LWAL0003.INT \
+ LWAL0004.INT \
+ LWAL0005.INT \
+ LWAL0006.INT \
+ LWAL0007.INT \
+ LWAL0008.INT \
+ LWAL0009.INT \
+ LWAL0010.INT \
+ LWAL0011.INT \
+ LWAL0012.INT \
+ LWAL0013.INT \
+ LWAL0014.INT \
+ LWAL0015.INT \
+ LWAL0016.INT \
+ LWAL0017.INT \
+ LWAL0018.INT \
+ LWAL0019.INT \
+ LWAL0020.INT \
+ LWAL0021.INT \
+ LWAL0022.INT \
+ LWAL0023.INT \
+ LWAL0024.INT \
+ LWAL0025.INT \
+ LWAL0026.INT \
+ LWAL0027.INT \
+ STRP0001.INT \
+ STRP0002.INT \
+ STRP0003.INT \
+ STRP0004.INT \
+ STRP0005.INT \
+ STRP0006.INT \
+ STRP0007.INT \
+ STRP0008.INT \
+ STRP0009.INT \
+ STRP0010.INT \
+ STRP0011.INT \
+ WALL0001.INT \
+ WALL0002.INT \
+ WALL0003.INT \
+ WALL0004.INT \
+ WALL0005.INT \
+ WALL0006.INT \
+ WALL0007.INT \
+ WALL0008.INT \
+ WALL0009.INT \
+ WALL0010.INT \
+ WALL0011.INT \
+ WALL0012.INT \
+ WALL0013.INT \
+ WALL0014.INT \
+ WALL0015.INT \
+ WALL0016.INT \
+ WALL0017.INT \
+ WALL0018.INT \
+ WALL0019.INT \
+ WALL0020.INT \
+ WALL0021.INT \
+ WALL0022.INT \
+ WALL0023.INT \
+ WALL0024.INT \
+ WALL0025.INT \
+ WALL0026.INT \
+ WALL0027.INT \
+ WALL0028.INT \
+ WALL0029.INT \
+ WALL0030.INT \
+ WALL0031.INT \
+ WALL0032.INT \
+ WALL0033.INT \
+ WALL0034.INT \
+ WALL0035.INT \
+ WALL0036.INT \
+ WALL0037.INT \
+ WALL0038.INT \
+ WALL0039.INT \
+ WALL0040.INT \
+ WALL0041.INT \
+ WALL0042.INT \
+ WALL0043.INT \
+ WALL0044.INT \
+ WALL0045.INT \
+ WALL0046.INT \
+ WALL0047.INT \
+ WALL0048.INT \
+ WALL0049.INT \
+
+# Both the temperate and snow sets have identical template entries.
+TEMPERATEFILES = \
+ MINE.TEM \
+ ICE01.TEM \
+ ICE02.TEM \
+ ICE03.TEM \
+ ICE04.TEM \
+ ICE05.TEM \
+ MOVEFLSH.TEM \
+ BR1X.TEM \
+ BR2X.TEM \
+ BRIDGE1X.TEM \
+ BRIDGE2X.TEM \
+ BRIDGE1H.TEM \
+ BRIDGE2H.TEM \
+ F01.TEM \
+ F02.TEM \
+ F03.TEM \
+ F04.TEM \
+ F05.TEM \
+ F06.TEM \
+ ELECTRO.TEM \
+ B1.TEM \
+ B2.TEM \
+ B3.TEM \
+ BIB1.TEM \
+ BIB2.TEM \
+ BIB3.TEM \
+ BR1A.TEM \
+ BR1B.TEM \
+ BR1C.TEM \
+ BR2A.TEM \
+ BR2B.TEM \
+ BR2C.TEM \
+ BR3A.TEM \
+ BR3B.TEM \
+ BR3C.TEM \
+ BR3D.TEM \
+ BR3E.TEM \
+ BR3F.TEM \
+ BRIDGE1.TEM \
+ BRIDGE1D.TEM \
+ BRIDGE2.TEM \
+ BRIDGE2D.TEM \
+ CLEAR1.TEM \
+ CORPSE1.TEM \
+ CORPSE2.TEM \
+ CORPSE3.TEM \
+ CR1.TEM \
+ CR2.TEM \
+ CR3.TEM \
+ CR4.TEM \
+ CR5.TEM \
+ CR6.TEM \
+ D01.TEM \
+ D02.TEM \
+ D03.TEM \
+ D04.TEM \
+ D05.TEM \
+ D06.TEM \
+ D07.TEM \
+ D08.TEM \
+ D09.TEM \
+ D10.TEM \
+ D11.TEM \
+ D12.TEM \
+ D13.TEM \
+ D14.TEM \
+ D15.TEM \
+ D16.TEM \
+ D17.TEM \
+ D18.TEM \
+ D19.TEM \
+ D20.TEM \
+ D21.TEM \
+ D22.TEM \
+ D23.TEM \
+ D24.TEM \
+ D25.TEM \
+ D26.TEM \
+ D27.TEM \
+ D28.TEM \
+ D29.TEM \
+ D30.TEM \
+ D31.TEM \
+ D32.TEM \
+ D33.TEM \
+ D34.TEM \
+ D35.TEM \
+ D36.TEM \
+ D37.TEM \
+ D38.TEM \
+ D39.TEM \
+ D40.TEM \
+ D41.TEM \
+ D42.TEM \
+ D43.TEM \
+ D44.TEM \
+ D45.TEM \
+ FALLS1.TEM \
+ FALLS1A.TEM \
+ FALLS2.TEM \
+ FALLS2A.TEM \
+ FORD1.TEM \
+ FORD2.TEM \
+ GEM01.TEM \
+ GEM02.TEM \
+ GEM03.TEM \
+ GEM04.TEM \
+ GOLD01.TEM \
+ GOLD02.TEM \
+ GOLD03.TEM \
+ GOLD04.TEM \
+ HBOX.TEM \
+ MSLOMAKE.TEM \
+ HBOXMAKE.TEM \
+ MSLO.TEM \
+ P01.TEM \
+ P02.TEM \
+ P03.TEM \
+ P04.TEM \
+ P07.TEM \
+ P08.TEM \
+ P13.TEM \
+ P14.TEM \
+ RC01.TEM \
+ RC02.TEM \
+ RC03.TEM \
+ RC04.TEM \
+ RF01.TEM \
+ RF02.TEM \
+ RF03.TEM \
+ RF04.TEM \
+ RF05.TEM \
+ RF06.TEM \
+ RF07.TEM \
+ RF08.TEM \
+ RF09.TEM \
+ RF10.TEM \
+ RF11.TEM \
+ RV01.TEM \
+ RV02.TEM \
+ RV03.TEM \
+ RV04.TEM \
+ RV05.TEM \
+ RV06.TEM \
+ RV07.TEM \
+ RV08.TEM \
+ RV09.TEM \
+ RV10.TEM \
+ RV11.TEM \
+ RV12.TEM \
+ RV13.TEM \
+ RV14.TEM \
+ RV15.TEM \
+ S01.TEM \
+ S02.TEM \
+ S03.TEM \
+ S04.TEM \
+ S05.TEM \
+ S06.TEM \
+ S07.TEM \
+ S08.TEM \
+ S09.TEM \
+ S10.TEM \
+ S11.TEM \
+ S12.TEM \
+ S13.TEM \
+ S14.TEM \
+ S15.TEM \
+ S16.TEM \
+ S17.TEM \
+ S18.TEM \
+ S19.TEM \
+ S20.TEM \
+ S21.TEM \
+ S22.TEM \
+ S23.TEM \
+ S24.TEM \
+ S25.TEM \
+ S26.TEM \
+ S27.TEM \
+ S28.TEM \
+ S29.TEM \
+ S30.TEM \
+ S31.TEM \
+ S32.TEM \
+ S33.TEM \
+ S34.TEM \
+ S35.TEM \
+ S36.TEM \
+ S37.TEM \
+ S38.TEM \
+ SC1.TEM \
+ SC2.TEM \
+ SC3.TEM \
+ SC4.TEM \
+ SC5.TEM \
+ SC6.TEM \
+ SH01.TEM \
+ SH02.TEM \
+ SH03.TEM \
+ SH04.TEM \
+ SH05.TEM \
+ SH06.TEM \
+ SH07.TEM \
+ SH08.TEM \
+ SH09.TEM \
+ SH10.TEM \
+ SH11.TEM \
+ SH12.TEM \
+ SH13.TEM \
+ SH14.TEM \
+ SH15.TEM \
+ SH16.TEM \
+ SH17.TEM \
+ SH18.TEM \
+ SH19.TEM \
+ SH20.TEM \
+ SH21.TEM \
+ SH22.TEM \
+ SH23.TEM \
+ SH24.TEM \
+ SH25.TEM \
+ SH26.TEM \
+ SH27.TEM \
+ SH28.TEM \
+ SH29.TEM \
+ SH30.TEM \
+ SH31.TEM \
+ SH32.TEM \
+ SH33.TEM \
+ SH34.TEM \
+ SH35.TEM \
+ SH36.TEM \
+ SH37.TEM \
+ SH38.TEM \
+ SH39.TEM \
+ SH40.TEM \
+ SH41.TEM \
+ SH42.TEM \
+ SH43.TEM \
+ SH44.TEM \
+ SH45.TEM \
+ SH46.TEM \
+ SH47.TEM \
+ SH48.TEM \
+ SH49.TEM \
+ SH50.TEM \
+ SH51.TEM \
+ SH52.TEM \
+ SH53.TEM \
+ SH54.TEM \
+ SH55.TEM \
+ SH56.TEM \
+ T01.TEM \
+ T02.TEM \
+ T03.TEM \
+ T05.TEM \
+ T06.TEM \
+ T07.TEM \
+ T08.TEM \
+ T10.TEM \
+ T11.TEM \
+ T12.TEM \
+ T13.TEM \
+ T14.TEM \
+ T15.TEM \
+ T16.TEM \
+ T17.TEM \
+ TC01.TEM \
+ TC02.TEM \
+ TC03.TEM \
+ TC04.TEM \
+ TC05.TEM \
+ V01.TEM \
+ V02.TEM \
+ V03.TEM \
+ V04.TEM \
+ V05.TEM \
+ V06.TEM \
+ V07.TEM \
+ V08.TEM \
+ V09.TEM \
+ V10.TEM \
+ V11.TEM \
+ V12.TEM \
+ V13.TEM \
+ V14.TEM \
+ V15.TEM \
+ V16.TEM \
+ V17.TEM \
+ V18.TEM \
+ W1.TEM \
+ W2.TEM \
+ WC01.TEM \
+ WC02.TEM \
+ WC03.TEM \
+ WC04.TEM \
+ WC05.TEM \
+ WC06.TEM \
+ WC07.TEM \
+ WC08.TEM \
+ WC09.TEM \
+ WC10.TEM \
+ WC11.TEM \
+ WC12.TEM \
+ WC13.TEM \
+ WC14.TEM \
+ WC15.TEM \
+ WC16.TEM \
+ WC17.TEM \
+ WC18.TEM \
+ WC19.TEM \
+ WC20.TEM \
+ WC21.TEM \
+ WC22.TEM \
+ WC23.TEM \
+ WC24.TEM \
+ WC25.TEM \
+ WC26.TEM \
+ WC27.TEM \
+ WC28.TEM \
+ WC29.TEM \
+ WC30.TEM \
+ WC31.TEM \
+ WC32.TEM \
+ WC33.TEM \
+ WC34.TEM \
+ WC35.TEM \
+ WC36.TEM \
+ WC37.TEM \
+ WC38.TEM \
+
+# Every temperate theater terrain file has a snow theater counterpart.
+SNOWFILES = $(TEMPERATEFILES:.TEM=.SNO)
+
+# Sound effects (Juvenile or Adult)
+SFX = \
+
+# Generic wave files (never changes).
+WAVFILES = \
+ AACANON3.AUD \
+ BEEPSLCT.AUD \
+ BLEEP11.AUD \
+ BLEEP12.AUD \
+ BLEEP13.AUD \
+ BLEEP17.AUD \
+ BLEEP5.AUD \
+ BLEEP6.AUD \
+ BLEEP9.AUD \
+ BOMBIT1.AUD \
+ BUILD5.AUD \
+ BUZZY1.AUD \
+ CANNON1.AUD \
+ CANNON2.AUD \
+ CASHDN1.AUD \
+ CASHTURN.AUD \
+ CASHUP1.AUD \
+ CHRONO2.AUD \
+ CHROTNK1.AUD \
+ CHUTE1.AUD \
+ CMON1.AUD \
+ CRMBLE2.AUD \
+ DEDMAN1.AUD \
+ DEDMAN10.AUD \
+ DEDMAN2.AUD \
+ DEDMAN3.AUD \
+ DEDMAN4.AUD \
+ DEDMAN5.AUD \
+ DEDMAN6.AUD \
+ DEDMAN7.AUD \
+ DEDMAN8.AUD \
+ DOGG5P.AUD \
+ DOGW3PX.AUD \
+ DOGW5.AUD \
+ DOGW6.AUD \
+ DOGW7.AUD \
+ DOGY1.AUD \
+ EAFFIRM1.AUD \
+ EENGIN1.AUD \
+ EINAH1.AUD \
+ EINOK1.AUD \
+ EINYES1.AUD \
+ EMOVOUT1.AUD \
+ EYESSIR1.AUD \
+ FIREBL3.AUD \
+ FIRETRT1.AUD \
+ FIXIT1.AUD \
+ GIRLOKAY.AUD \
+ GIRLYEAH.AUD \
+ GOTIT1.AUD \
+ GRENADE1.AUD \
+ GUN11.AUD \
+ GUN13.AUD \
+ GUN27.AUD \
+ GUN5.AUD \
+ GUYOKAY1.AUD \
+ GUYYEAH1.AUD \
+ H2OBOMB2.AUD \
+ HEAL2.AUD \
+ HYDROD1.AUD \
+ INVUL2.AUD \
+ IRONCUR9.AUD \
+ JBURN1.AUD \
+ JCHRGE1.AUD \
+ JCRISP1.AUD \
+ JDANCE1.AUD \
+ JJUICE1.AUD \
+ JJUMP1.AUD \
+ JLIGHT1.AUD \
+ JPOWER1.AUD \
+ JSHOCK1.AUD \
+ JYES1.AUD \
+ KABOOM1.AUD \
+ KABOOM12.AUD \
+ KABOOM15.AUD \
+ KABOOM22.AUD \
+ KABOOM25.AUD \
+ KABOOM30.AUD \
+ KEEPEM1.AUD \
+ LAUGH1.AUD \
+ LEFTY1.AUD \
+ MADCHRG2.AUD \
+ MADEXPLO.AUD \
+ MAFFIRM1.AUD \
+ MBOSS1.AUD \
+ MHEAR1.AUD \
+ MHOTDIG1.AUD \
+ MHOWDY1.AUD \
+ MHUH1.AUD \
+ MGUNINF1.AUD \
+ MINE1.AUD \
+ MINEBLO1.AUD \
+ MINELAY1.AUD \
+ MISSILE1.AUD \
+ MISSILE6.AUD \
+ MISSILE7.AUD \
+ MLAFF1.AUD \
+ MMOVOUT1.AUD \
+ MRESPON1.AUD \
+ MRISE1.AUD \
+ MWRENCH1.AUD \
+ MYEEHAW1.AUD \
+ MYES1.AUD \
+ MYESSIR1.AUD \
+ ONIT1.AUD \
+ PILLBOX1.AUD \
+ PLACBLDG.AUD \
+ RABEEP1.AUD \
+ RADARDN1.AUD \
+ RADARON2.AUD \
+ RAMENU1.AUD \
+ ROKROLL1.AUD \
+ SAFFIRM1.AUD \
+ SANDBAG2.AUD \
+ SCOLDY1.AUD \
+ SCOMND1.AUD \
+ SHKTROP1.AUD \
+ SILENCER.AUD \
+ SINDEED1.AUD \
+ SKING1.AUD \
+ SMOUT1.AUD \
+ SOKAY1.AUD \
+ SONPULSE.AUD \
+ SONWAY1.AUD \
+ SPLASH9.AUD \
+ SQUISHY2.AUD \
+ SUBSHOW1.AUD \
+ SWHAT1.AUD \
+ SYEAH1.AUD \
+ SYESSIR1.AUD \
+ TANDETH1.AUD \
+ TANK5.AUD \
+ TANK6.AUD \
+ TESLA1.AUD \
+ TORPEDO1.AUD \
+ TSLACHG2.AUD \
+ TUFFGUY1.AUD \
+ TURRET1.AUD \
+ WALLKIL2.AUD \
+ YEAH1.AUD \
+ YES1.AUD \
+ YO1.AUD \
+
+# Vehicle responses
+RESPONSE1 = \
+ ACKNO.AUD \
+ AFFIRM1.AUD \
+ AWAIT1.AUD \
+ REPORT1.AUD \
+ VEHIC1.AUD \
+ YESSIR1.AUD \
+
+# Infantry responses
+RESPONSE2 = \
+ ACKNO.AUD \
+ AFFIRM1.AUD \
+ AWAIT1.AUD \
+ NOPROB.AUD \
+ OVEROUT.AUD \
+ READY.AUD \
+ REPORT1.AUD \
+ RITAWAY.AUD \
+ ROGER.AUD \
+ UGOTIT.AUD \
+ YESSIR1.AUD \
+
+#TSCOREFILES = \
+# cps\record.bin \
+# WIN1.AUD \
+# MAP1.AUD \
+
+VARFILES = \
+
+SCOREFILES = \
+ CREDITS.AUD \
+ AWAIT.AUD \
+ BIGF226M.AUD \
+ CRUS226M.AUD \
+ DENSE_R.AUD \
+ FAC1226M.AUD \
+ FAC2226M.AUD \
+ FOGGER1A.AUD \
+ HELL226M.AUD \
+ MUD1A.AUD \
+ RADIO2.AUD \
+ ROLLOUT.AUD \
+ RUN1226M.AUD \
+ SCORE.AUD \
+ SMSH226M.AUD \
+ SNAKE.AUD \
+ TERMINAT.AUD \
+ TREN226M.AUD \
+ TWIN.AUD \
+ VECTOR1A.AUD \
+ WORK226M.AUD \
+ 2ND_HAND.AUD \
+ ARAZIOD.AUD \
+ BACKSTAB.AUD \
+ CHAOS2.AUD \
+ SHUT_IT.AUD \
+ TWINMIX1.AUD \
+ UNDER3.AUD \
+ VR2.AUD \
+ BOG.AUD \
+ FLOAT_V2.AUD \
+ GLOOM.AUD \
+ GRNDWIRE.AUD \
+ RPT.AUD \
+ SEARCH.AUD \
+ TRACTION.AUD \
+ WASTELND.AUD \
+
+SPEECHFILES = \
+ STRCKIL1.AUD \
+ NOPOWR1.AUD \
+ SAVE1.AUD \
+ LOAD1.AUD \
+ 10MINR.AUD \
+ 1MINR.AUD \
+ 1OBJMET1.AUD \
+ 20MINR.AUD \
+ 2MINR.AUD \
+ 2OBJMET1.AUD \
+ 30MINR.AUD \
+ 3MINR.AUD \
+ 3OBJMET1.AUD \
+ 40MINR.AUD \
+ 4MINR.AUD \
+ 5MINR.AUD \
+ AAPPRO1.AUD \
+ AARIVE1.AUD \
+ AARIVE1.AUD \
+ AARRIVE1.AUD \
+ AARRIVN1.AUD \
+ AARRIVS1.AUD \
+ AARRIVW1.AUD \
+ AAVAIL1.AUD \
+ ABLDGIN1.AUD \
+ AFALLEN1.AUD \
+ ALAUNCH1.AUD \
+ APREP1.AUD \
+ AREADY1.AUD \
+ ARMORUP1.AUD \
+ ASELECT1.AUD \
+ ATLNCH1.AUD \
+ ATPREP1.AUD \
+ AUNITL1.AUD \
+ BASEATK1.AUD \
+ BCT1.AUD \
+ BLDGINF1.AUD \
+ BLDGPRG1.AUD \
+ CANCLD1.AUD \
+ CHROCHR1.AUD \
+ CHRORDY1.AUD \
+ CHROYES1.AUD \
+ CMDCNTR1.AUD \
+ CNTLDED1.AUD \
+ COMNDOF1.AUD \
+ COMNDOR1.AUD \
+ CONSCMP1.AUD \
+ CONVLST1.AUD \
+ CONVYAP1.AUD \
+ CREDIT1.AUD \
+ ENMYAPP1.AUD \
+ FIREPO1.AUD \
+ FLARE1.AUD \
+ FLAREE1.AUD \
+ FLAREN1.AUD \
+ FLARES1.AUD \
+ FLAREW1.AUD \
+ IRONCHG1.AUD \
+ IRONRDY1.AUD \
+ KOSYFRE1.AUD \
+ KOSYRES1.AUD \
+ LOPOWER1.AUD \
+ MERCF1.AUD \
+ MERCR1.AUD \
+ MISNLST1.AUD \
+ MISNWON1.AUD \
+ MTIMEIN1.AUD \
+ NAVYLST1.AUD \
+ NEWOPT1.AUD \
+ NOBUILD1.AUD \
+ NODEPLY1.AUD \
+ NOFUNDS1.AUD \
+ NOFUNDS1.AUD \
+ OBJMET1.AUD \
+ OBJNMET1.AUD \
+ OBJNRCH1.AUD \
+ OBJRCH1.AUD \
+ ONHOLD1.AUD \
+ OPTERM1.AUD \
+ PRIBLDG1.AUD \
+ PROGRES1.AUD \
+ PULSE1.AUD \
+ REINFOR1.AUD \
+ REPAIR1.AUD \
+ REPAIR1.AUD \
+ SATLNCH1.AUD \
+ SILOND1.AUD \
+ SLCTTGT1.AUD \
+ SOVEFAL1.AUD \
+ SOVEMP1.AUD \
+ SOVFAPP1.AUD \
+ SOVFORC1.AUD \
+ SOVREIN1.AUD \
+ SPYPLN1.AUD \
+ STRUCAP1.AUD \
+ STRUSLD1.AUD \
+ TANYAF1.AUD \
+ TANYAR1.AUD \
+ TARGFRE1.AUD \
+ TARGRES1.AUD \
+ TIMERGO1.AUD \
+ TIMERNO1.AUD \
+ TRAIN1.AUD \
+ UNITFUL1.AUD \
+ UNITLST1.AUD \
+ UNITRDY1.AUD \
+ UNITREP1.AUD \
+ UNITSLD1.AUD \
+ UNITSPD1.AUD \
+ XPLOPLC1.AUD \
+# ABLDGC1.AUD \
+# SOVBLDG1.AUD \
+# SOVSTRC1.AUD \
+# SOVUNTD1.AUD \
+# AUNITD1.AUD \
+# ASTRUCD1.AUD \
+
+#ALLIESVQ = \
+DUMMYVQ = \
+ AAGUN.VQA \
+ AFTRMATH.VQA \
+ ALLY1.VQA \
+ ALLY10.VQA \
+ ALLY10B.VQA \
+ ALLY11.VQA \
+ ALLY12.VQA \
+ ALLY14.VQA \
+ ALLY2.VQA \
+ ALLY4.VQA \
+ ALLY5.VQA \
+ ALLY6.VQA \
+ ALLY8.VQA \
+ ALLY9.VQA \
+ ALLYEND.VQA \
+ ALLYMORF.VQA \
+ APCESCPE.VQA \
+ ASSESS.VQA \
+ BATTLE.VQA \
+ 1BINOC.VQA \
+ BMAP.VQA \
+ BRDGTILT.VQA \
+ CRONTEST.VQA \
+ CRONFAIL.VQA \
+ DESTROYR.VQA \
+ DUD.VQA \
+ ELEVATOR.VQA \
+ FLARE.VQA \
+ FROZEN.VQA \
+ GRVESTNE.VQA \
+ LANDING.VQA \
+ MASASSLT.VQA \
+ MCV.VQA \
+ MCV_LAND.VQA \
+ MONTPASS.VQA \
+ OILDRUM.VQA \
+ OVERRUN.VQA \
+ PROLOG.VQA \
+ REDINTRO.VQA \
+ SHIPSINK.VQA \
+ SHORBOM1.VQA \
+ SHORBOM2.VQA \
+ SHORBOMB.VQA \
+ SNOWBOMB.VQA \
+ SOVIET1.VQA \
+ SOVTSTAR.VQA \
+ SPY.VQA \
+ TANYA1.VQA \
+ TANYA2.VQA \
+ TOOFAR.VQA \
+ TRINITY.VQA \
+# TRAILER.VQA \
+
+SOVIETVQ = \
+ AAGUN.VQA \
+ CRONFAIL.VQA \
+ AIRFIELD.VQA \
+ ALLY1.VQA \
+ ALLYMORF.VQA \
+ AVERTED.VQA \
+ BEACHEAD.VQA \
+ BMAP.VQA \
+ BOMBRUN.VQA \
+ COUNTDWN.VQA \
+ DOUBLE.VQA \
+ DPTHCHRG.VQA \
+ EXECUTE.VQA \
+ FLARE.VQA \
+ LANDING.VQA \
+ MCVBRDGE.VQA \
+ MIG.VQA \
+ MOVINGIN.VQA \
+ MTNKFACT.VQA \
+ NUKESTOK.VQA \
+ ONTHPRWL.VQA \
+ PERISCOP.VQA \
+ PROLOG.VQA \
+ RADRRAID.VQA \
+ REDINTRO.VQA \
+ SEARCH.VQA \
+ SFROZEN.VQA \
+ SITDUCK.VQA \
+ SLNTSRVC.VQA \
+ SNOWBOMB.VQA \
+ SNSTRAFE.VQA \
+ SOVBATL.VQA \
+ SOVCEMET.VQA \
+ SOVFINAL.VQA \
+ SOVIET1.VQA \
+ SOVIET10.VQA \
+ SOVIET11.VQA \
+ SOVIET12.VQA \
+ SOVIET13.VQA \
+ SOVIET14.VQA \
+ SOVIET2.VQA \
+ SOVIET3.VQA \
+ SOVIET4.VQA \
+ SOVIET5.VQA \
+ SOVIET6.VQA \
+ SOVIET7.VQA \
+ SOVIET8.VQA \
+ SOVIET9.VQA \
+ SOVMCV.VQA \
+ SOVTSTAR.VQA \
+ SPOTTER.VQA \
+ STRAFE.VQA \
+ TAKE_OFF.VQA \
+ TESLA.VQA \
+ V2ROCKET.VQA \
+# TRAILER.VQA \
+
+ALLIESVQ = \
+ AFTRMATH.VQA \
+ ALLY1.VQA \
+ ALLYMORF.VQA \
+ APCESCPE.VQA \
+ BATTLE.VQA \
+ BMAP.VQA \
+ CRONFAIL.VQA \
+ DPTHCHRG.VQA \
+ EXECUTE.VQA \
+ FLARE.VQA \
+ FROZEN.VQA \
+ GRVESTNE.VQA \
+ LANDING.VQA \
+ MASASSLT.VQA \
+ NUKESTOK.VQA \
+ ONTHPRWL.VQA \
+ OVERRUN.VQA \
+ PROLOG.VQA \
+ REDINTRO.VQA \
+ SFROZEN.VQA \
+ SLNTSRVC.VQA \
+ SNOWBOMB.VQA \
+ SNOWBASE.VQA \
+ SOVMCV.VQA \
+ SNSTRAFE.VQA \
+ SOVBATL.VQA \
+ SOVCEMET.VQA \
+ SOVIET1.VQA \
+ SOVTSTAR.VQA \
+ SPY.VQA \
+ STRAFE.VQA \
+ TESLA.VQA \
+ TOOFAR.VQA \
+ TRINITY.VQA \
+ V2ROCKET.VQA \
+# ANTEND.VQA \
+# ANTINTRO.VQA \
+
+
+
+# Files required for hires/Win95 version only
+#
+# This mix file is not cached
+#
+NOCACHEHIRESFILES= \
+ ENGLISH.VQA \
+ $(ALLIESVQ:.VQA=.VQP) \
+ $(SOVIETVQ:.VQA=.VQP) \
+
+
+LINTOBJECTS1 = $(OBJECTS:,=)
+LINTOBJECTS = $(LINTOBJECTS1:.OBJ=.LOB)
+
+# Mixfiles that should reside on the CD-ROM drive.
+CD1MIXFILES = \
+ CONQUER.MIX \
+
+
+
+# Mixfiles that should reside on the hard drive.
+LOCALMIXFILES = \
+ EDITOR.MIX \
+ HIRES.MIX \
+ LOCAL.MIX \
+ LORES.MIX \
+ NCHIRES.MIX \
+ SPEECH.MIX \
+
+
+# Mixfiles as they appear on the CD and hard drive.
+
+
+
+# Ant assets SOME ASSETS ARE HERE FOR OVERRIDING
+EXPANDFILES= \
+ ANT1.SHP \
+ ANT2.SHP \
+ ANT3.SHP \
+ QUEE.SHP \
+ CREDITS.ENG \
+ HILL01.TEM \
+ ANTBITE.AUD \
+ ANTDIE.AUD \
+ ANTDIE.SHP \
+ LAR1.SHP \
+ LAR2.SHP \
+ TITLE.PCX \
+ MISSION.INI \
+ BUZZY1.AUD \
+ STAVCMDR.AUD \
+ STAVCRSE.AUD \
+ STAVYES.AUD \
+ STAVMOV.AUD \
+ CONQUER.ENG \
+ RAMBO1.AUD \
+ RAMBO2.AUD \
+ RAMBO3.AUD \
+ TITLE.CPS \
+ TUTORIAL.INI \
+ BMAP.VQP \
+ ANTEND.VQP \
+ ANTINTRO.VQP \
+
+# Aftermath expansion files
+EXPAND2FILES= \
+ CARR.SHP \
+
+
+
+#############################################################
+# Rebuilds all the mixfiles.
+packfiles: always $(PACKFILES)
+
+always:
+ copy f:\projects\c&c0\editor\english\*.mix $(.path.mix) /u
+
+
+####################################################################
+# All mixfiles that exist on the CD-ROM are embedded within this mega-mixfile.
+$(.path.cd1)MAIN.MIX: $(CD1MIXFILES)
+ UTILS\MIXFILE -k -I$(.path.mix) &&!
+ $**
+! $(.path.cd1)$&.mix
+
+
+
+
+####################################################################
+# These are the various sub-mixfiles.
+CONQUER.MIX: $(CONQUERFILES) $(CACHEMAP) .\key.ini
+ UTILS\MIXFILE -k -h -I$(.path.cps) &&!
+ $(CONQUERFILES) $(CACHEMAP)
+! $(.path.mix)$&.mix
+
+TEMPERAT.MIX: $(TEMPERATEFILES) .\key.ini
+ UTILS\MIXFILE -h -k -I$(.path.cps) &&!
+ $(TEMPERATEFILES)
+! $(.path.mix)$&.mix
+
+SNOW.MIX: $(SNOWFILES) .\key.ini
+ UTILS\MIXFILE -h -k -I$(.path.cps) &&!
+ $(SNOWFILES)
+! $(.path.mix)$&.mix
+
+INTERIOR.MIX: $(INTERIORFILES) .\key.ini
+ UTILS\MIXFILE -h -k -I$(.path.cps) &&!
+ $(INTERIORFILES)
+! $(.path.mix)$&.mix
+
+GENERAL.MIX: $(GENERALFILES) $(GENERALMAPFILES) $(NETMAPFILES) $(MAPFILES) .\key.ini
+ UTILS\MIXFILE -k -I$(.path.cps) -I$(.path.ini) &&!
+ $(GENERALFILES) $(GENERALMAPFILES) $(NETMAPFILES) $(MAPFILES)
+! $(.path.mix)$&.mix
+
+SCORES.MIX: $(SCOREFILES)
+ UTILS\MIXFILE -k -I$(.path.cps) -I$(.path.ini) &&!
+ $**
+! $(.path.mix)$&.mix
+
+SOUNDS.MIX: $(WAVFILES) $(SFX)
+ UTILS\MIXFILE -h -k -EA60=V00 -EA61=V01 -EA62=V02 -EA63=V03 -I$(.path.aud) &&!
+ $**
+! $(.path.mix)$&.mix
+
+RUSSIAN.MIX: $(RESPONSE1:.AUD=.R00) $(RESPONSE2:.AUD=.R01) $(RESPONSE1:.AUD=.R02) $(RESPONSE2:.AUD=.R03)
+ UTILS\MIXFILE -h -k -I$(.path.aud) &&!
+ $**
+! $(.path.mix)$&.mix
+
+LIMITED.MIX: BLEEP11.AUD
+ UTILS\MIXFILE -h -k -I$(.path.aud) &&!
+ $**
+! $(.path.mix)$&.mix
+
+ALLIES.MIX: $(RESPONSE1:.AUD=.V00) $(RESPONSE2:.AUD=.V01) $(RESPONSE1:.AUD=.V02) $(RESPONSE2:.AUD=.V03)
+ UTILS\MIXFILE -h -k -I$(.path.aud) &&!
+ $**
+! $(.path.mix)$&.mix
+
+MOVIES1.MIX: $(ALLIESVQ)
+ UTILS\MIXFILE -k -I$(.path.vqa) &&!
+ $**
+! $(.path.mix)$&.mix
+
+
+
+
+
+NCHIRES.MIX: $(NOCACHEHIRESFILES:.SHP=.HI)
+ UTILS\MIXFILE -k -I$(.path.vqp) -I$(.path.cps) &&!
+ $(NOCACHEHIRESFILES)
+! $(.path.mix)$&.mix
+
+LOCAL.MIX: $(LOCALFILES) .\key.ini
+ UTILS\MIXFILE -h -k -E.A6=.AUD -I$(.path.ini) -I$(.path.txt) -I$(.path.cps) &&!
+ $(LOCALFILES)
+! $(.path.mix)$&.mix
+
+LORES.MIX: $(LOHILORES) .\key.ini
+ UTILS\MIXFILE -h -k -E.LOW=.SHP -E.LNT=.FNT -I$(.path.cps) &&!
+ $(LOHILORES)
+! $(.path.mix)$&.mix
+
+HIRES.MIX: $(HIRESFILES:.SHP=.HI) $(HIHILORES) .\key.ini
+ UTILS\MIXFILE -h -k -E.HI=.SHP -E.HNT=.FNT -I$(.path.cps) &&!
+ $(HIRESFILES:.SHP=.HI) $(HIHILORES)
+! $(.path.mix)$&.mix
+
+LORES1.MIX: $(LOHILORES1) .\key.ini
+ UTILS\MIXFILE -h -k -E.LOW=.SHP -E.LNT=.FNT -I$(.path.cps) &&!
+ $(LOHILORES1)
+! $(.path.mix)$&.mix
+
+HIRES1.MIX: $(HIHILORES1) .\key.ini
+ UTILS\MIXFILE -h -k -E.HI=.SHP -E.HNT=.FNT -I$(.path.cps) &&!
+ $(HIHILORES1)
+! $(.path.mix)$&.mix
+
+SPEECH.MIX: $(SPEECHFILES)
+ UTILS\MIXFILE -k -I$(.path.aud) &&!
+ $**
+! $(.path.mix)$&.mix
+
+
+EXPAND.MIX: $(EXPANDFILES)
+ UTILS\MIXFILE -k -I$(.path.mix) &&!
+ $**
+! $(.path.mix)$&.mix
+
+EXPAND2.MIX: $(EXPAND2FILES)
+ UTILS\MIXFILE -k -I$(.path.mix) &&!
+ $**
+! $(.path.mix)$&.mix
+
+#############################################################
+# Special rule to create the mouse shape (which must be a shape file)
+mouse.hi: $(.path.anm)hires\mouse.anm
+ -utils\makeshps $(.path.lbm)palettes\temperat.lbm &&!
+ &$(.path.anm)hires\mouse.anm;
+ end;
+! $(.path.hi)$&.hi $(SHAPEBUFFSIZE)
+
+# Special rule to create the mouse shape (which must be a shape file)
+mouse.low: $(.path.anm)lores\mouse.anm
+ -utils\makeshps $(.path.lbm)palettes\temperat.lbm &&!
+ &$(.path.anm)lores\mouse.anm;
+ end;
+! $(.path.low)$&.low $(SHAPEBUFFSIZE)
+
+
+#############################################################
+# Special build rule for radar animations so that they won't.
+#
+NATORADR.HI: $(.path.anm)hires\NATORADR.ANM
+ utils\newkeyf $** $(.path.hi)$&.hi -l -k
+
+USSRRADR.HI: $(.path.anm)hires\USSRRADR.ANM
+ utils\newkeyf $** $(.path.hi)$&.hi -l -k
+
+NATORADR.LOW: $(.path.anm)lores\NATORADR.ANM
+ utils\newkeyf $** $(.path.low)$&.low -l -k
+
+USSRRADR.LOW: $(.path.anm)lores\USSRRADR.ANM
+ utils\newkeyf $** $(.path.low)$&.low -l -k
+
+
+#############################################################
+# Debug text file creation.
+debug.eng: debug.txt
+ utils\textmake -b1000 eng\$&.txt $(.path.eng)$&.eng $&.h
+
+
diff --git a/CODE/ADATA.CPP b/CODE/ADATA.CPP
new file mode 100644
index 0000000..8b95fb0
--- /dev/null
+++ b/CODE/ADATA.CPP
@@ -0,0 +1,2361 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/ADATA.CPP 3 3/07/97 4:27p Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : ADATA.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : May 30, 1994 *
+ * *
+ * Last Update : July 9, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * AnimTypeClass::AnimTypeClass -- Constructor for animation types. *
+ * AnimTypeClass::One_Time -- Performs one time action for animation types. *
+ * AnimTypeClass::Init -- Load any animation artwork that is theater specific. *
+ * Anim_Name -- Fetches the ASCII name of the animation type specified. *
+ * AnimTypeClass::As_Reference -- Fetch a reference to the animation type specified. *
+ * AnimTypeClass::Init_Heap -- Initialize the animation type system. *
+ * AnimTypeClass::operator new -- Allocate an animation type object from private pool. *
+ * AnimTypeClass::operator delete -- Returns an anim type class object back to the pool. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+
+static AnimTypeClass const AtomBomb(
+ ANIM_ATOM_BLAST, // Animation number.
+ "ATOMSFX", // Data name of animation.
+ 72, // Maximum dimension of animation.
+ 19, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ true, // Scorches the ground?
+ true, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 0, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+
+static AnimTypeClass const SputDoor(
+ ANIM_SPUTDOOR, // Animation number.
+ "SPUTDOOR", // Data name of animation.
+ 42, // Maximum dimension of animation.
+ 1, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ -1, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 1, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+
+// Electrocution death anim from Tesla coil
+static AnimTypeClass const ElectricDie(
+ ANIM_ELECT_DIE, // Animation number.
+ "ELECTRO", // Data name of animation.
+ 16, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ true, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ true, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ true, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ 3, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 5, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_FIRE_MED
+);
+
+// Electrocution death anim from Tesla coil for dog
+static AnimTypeClass const DogElectricDie(
+ ANIM_DOG_ELECT_DIE, // Animation number.
+ "ELECTDOG", // Data name of animation.
+ 17, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ true, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ true, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ 3, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 5, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_FIRE_MED
+);
+
+static AnimTypeClass const SAMN(
+ ANIM_SAM_N, // Animation number.
+ "SAMFIRE", // Data name of animation.
+ 55, // Maximum dimension of animation.
+ 4, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 18*0, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Ending frame of loop back.
+ 18, // Number of animation stages.
+ 0, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+static AnimTypeClass const SAMNW(
+ ANIM_SAM_NW, // Animation number.
+ "SAMFIRE", // Data name of animation.
+ 55, // Maximum dimension of animation.
+ 22, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 18*1, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Ending frame of loop back.
+ 18, // Number of animation stages.
+ 0, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+static AnimTypeClass const SAMW(
+ ANIM_SAM_W, // Animation number.
+ "SAMFIRE", // Data name of animation.
+ 55, // Maximum dimension of animation.
+ 40, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 18*2, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Ending frame of loop back.
+ 18, // Number of animation stages.
+ 0, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+static AnimTypeClass const SAMSW(
+ ANIM_SAM_SW, // Animation number.
+ "SAMFIRE", // Data name of animation.
+ 55, // Maximum dimension of animation.
+ 58, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 18*3, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Ending frame of loop back.
+ 18, // Number of animation stages.
+ 0, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+static AnimTypeClass const SAMS(
+ ANIM_SAM_S, // Animation number.
+ "SAMFIRE", // Data name of animation.
+ 55, // Maximum dimension of animation.
+ 76, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 18*4, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Ending frame of loop back.
+ 18, // Number of animation stages.
+ 0, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+static AnimTypeClass const SAMSE(
+ ANIM_SAM_SE, // Animation number.
+ "SAMFIRE", // Data name of animation.
+ 55, // Maximum dimension of animation.
+ 94, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 18*5, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Ending frame of loop back.
+ 18, // Number of animation stages.
+ 0, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+static AnimTypeClass const SAME(
+ ANIM_SAM_E, // Animation number.
+ "SAMFIRE", // Data name of animation.
+ 55, // Maximum dimension of animation.
+ 112, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 18*6, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Ending frame of loop back.
+ 18, // Number of animation stages.
+ 0, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+static AnimTypeClass const SAMNE(
+ ANIM_SAM_NE, // Animation number.
+ "SAMFIRE", // Data name of animation.
+ 55, // Maximum dimension of animation.
+ 130, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 18*7, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Ending frame of loop back.
+ 18, // Number of animation stages.
+ 0, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+
+static AnimTypeClass const LZSmoke(
+ ANIM_LZ_SMOKE, // Animation number.
+ "SMOKLAND", // Data name of animation.
+ 32, // Maximum dimension of animation.
+ 72, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ true, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 2, // Delay between frames.
+ 0, // Starting frame number.
+ 72, // Loop start frame number.
+ 91, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 255, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+
+/*
+** Flammable object burning animations. Primarily used on trees and buildings.
+*/
+static AnimTypeClass const BurnSmall(
+ ANIM_BURN_SMALL, // Animation number.
+ "BURN-S", // Data name of animation.
+ 11, // Maximum dimension of animation.
+ 13, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ true, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ fixed(1, 32), // Damage to apply per tick (fixed point).
+ 2, // Delay between frames.
+ 0, // Starting frame number.
+ 30, // Loop start frame number.
+ 62, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 4, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+static AnimTypeClass const BurnMed(
+ ANIM_BURN_MED, // Animation number.
+ "BURN-M", // Data name of animation.
+ 14, // Maximum dimension of animation.
+ 13, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ true, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ fixed(1, 16), // Damage to apply per tick (fixed point).
+ 2, // Delay between frames.
+ 0, // Starting frame number.
+ 30, // Loop start frame number.
+ 62, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 4, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+static AnimTypeClass const BurnBig(
+ ANIM_BURN_BIG, // Animation number.
+ "BURN-L", // Data name of animation.
+ 23, // Maximum dimension of animation.
+ 13, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ true, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ true, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ fixed(1, 10), // Damage to apply per tick (fixed point).
+ 2, // Delay between frames.
+ 0, // Starting frame number.
+ 30, // Loop start frame number.
+ 62, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 4, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+
+/*
+** Flammable object burning animations that trail into smoke. Used for
+** buildings and the gunboat.
+*/
+static AnimTypeClass const OnFireSmall(
+ ANIM_ON_FIRE_SMALL, // Animation number.
+ "BURN-S", // Data name of animation.
+ 11, // Maximum dimension of animation.
+ 13, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ true, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ fixed(1, 32), // Damage to apply per tick (fixed point).
+ 2, // Delay between frames.
+ 0, // Starting frame number.
+ 30, // Loop start frame number.
+ 62, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 4, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_SMOKE_M
+);
+static AnimTypeClass const OnFireMed(
+ ANIM_ON_FIRE_MED, // Animation number.
+ "BURN-M", // Data name of animation.
+ 14, // Maximum dimension of animation.
+ 13, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ true, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ fixed(1, 16), // Damage to apply per tick (fixed point).
+ 2, // Delay between frames.
+ 0, // Starting frame number.
+ 30, // Loop start frame number.
+ 62, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 4, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_ON_FIRE_SMALL
+);
+static AnimTypeClass const OnFireBig(
+ ANIM_ON_FIRE_BIG, // Animation number.
+ "BURN-L", // Data name of animation.
+ 23, // Maximum dimension of animation.
+ 13, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ true, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ true, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ fixed(1, 10), // Damage to apply per tick (fixed point).
+ 2, // Delay between frames.
+ 0, // Starting frame number.
+ 30, // Loop start frame number.
+ 62, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 4, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_ON_FIRE_MED
+);
+static AnimTypeClass const Parachute(
+ ANIM_PARACHUTE, // Animation number.
+ "PARACH", // Data name of animation.
+ 32, // Maximum dimension of animation.
+ 15, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 4, // Delay between frames.
+ 0, // Starting frame number.
+ 7, // Loop start frame number.
+ -1, // Loopback frame number.
+ -1, // Number of animation stages.
+ 15, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+static AnimTypeClass const ParaBomb(
+ ANIM_PARA_BOMB, // Animation number.
+ "PARABOMB", // Data name of animation.
+ 32, // Maximum dimension of animation.
+ 8, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 4, // Delay between frames.
+ 0, // Starting frame number.
+ 7, // Loop start frame number.
+ -1, // Loopback frame number.
+ -1, // Number of animation stages.
+ 15, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+
+static AnimTypeClass const FBall1(
+ ANIM_FBALL1, // Animation number.
+ "FBALL1", // Data name of animation.
+ 67, // Maximum dimension of animation.
+ 6, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ true, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ -1, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 1, // Number of times the animation loops.
+ VOC_KABOOM25, // Sound effect to play.
+ ANIM_NONE
+);
+
+static AnimTypeClass const Frag1(
+ ANIM_FRAG1, // Animation number.
+ "FRAG1", // Data name of animation.
+ 45, // Maximum dimension of animation.
+ 3, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ true, // Forms a crater?
+ true, // Sticks to unit in square?
+ true, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ -1, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 1, // Number of times the animation loops.
+ VOC_KABOOM30, // Sound effect to play.
+ ANIM_NONE
+);
+
+static AnimTypeClass const VehHit1(
+ ANIM_VEH_HIT1, // Animation number.
+ "VEH-HIT1", // Data name of animation.
+ 30, // Maximum dimension of animation.
+ 4, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ true, // Forms a crater?
+ true, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ -1, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 1, // Number of times the animation loops.
+ VOC_KABOOM25, // Sound effect to play.
+ ANIM_NONE
+);
+
+static AnimTypeClass const VehHit2(
+ ANIM_VEH_HIT2, // Animation number.
+ "VEH-HIT2", // Data name of animation.
+ 21, // Maximum dimension of animation.
+ 1, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ true, // Forms a crater?
+ true, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ -1, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 1, // Number of times the animation loops.
+ VOC_KABOOM12, // Sound effect to play.
+ ANIM_NONE
+);
+
+static AnimTypeClass const VehHit3(
+ ANIM_VEH_HIT3, // Animation number.
+ "VEH-HIT3", // Data name of animation.
+ 19, // Maximum dimension of animation.
+ 3, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ true, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ -1, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 1, // Number of times the animation loops.
+ VOC_KABOOM12, // Sound effect to play.
+ ANIM_NONE
+);
+
+static AnimTypeClass const ArtExp1(
+ ANIM_ART_EXP1, // Animation number.
+ "ART-EXP1", // Data name of animation.
+ 41, // Maximum dimension of animation.
+ 1, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ true, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ -1, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 1, // Number of times the animation loops.
+ VOC_KABOOM22, // Sound effect to play.
+ ANIM_NONE
+);
+
+static AnimTypeClass const Napalm1(
+ ANIM_NAPALM1, // Animation number.
+ "NAPALM1", // Data name of animation.
+ 21, // Maximum dimension of animation.
+ 5, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ true, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ -1, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 1, // Number of times the animation loops.
+ VOC_FIRE_EXPLODE, // Sound effect to play.
+ ANIM_NONE
+);
+
+static AnimTypeClass const Napalm2(
+ ANIM_NAPALM2, // Animation number.
+ "NAPALM2", // Data name of animation.
+ 41, // Maximum dimension of animation.
+ 5, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ true, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ -1, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 1, // Number of times the animation loops.
+ VOC_FIRE_EXPLODE, // Sound effect to play.
+ ANIM_NONE
+);
+
+static AnimTypeClass const Napalm3(
+ ANIM_NAPALM3, // Animation number.
+ "NAPALM3", // Data name of animation.
+ 78, // Maximum dimension of animation.
+ 5, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ true, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ -1, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 1, // Number of times the animation loops.
+ VOC_FIRE_LAUNCH, // Sound effect to play.
+ ANIM_NONE
+);
+
+static AnimTypeClass const SmokePuff(
+ ANIM_SMOKE_PUFF, // Animation number.
+ "SMOKEY", // Data name of animation.
+ 24, // Maximum dimension of animation.
+ 2, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ true, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ -1, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 1, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+
+static AnimTypeClass const FireBallFade(
+ ANIM_FBALL_FADE, // Animation number.
+ "FB2", // Data name of animation.
+ 24, // Maximum dimension of animation.
+ 1, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ -1, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 1, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+
+static AnimTypeClass const Piff(
+ ANIM_PIFF, // Animation number.
+ "PIFF", // Data name of animation.
+ 13, // Maximum dimension of animation.
+ 1, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ -1, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 1, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+
+static AnimTypeClass const PiffPiff(
+ ANIM_PIFFPIFF, // Animation number.
+ "PIFFPIFF", // Data name of animation.
+ 20, // Maximum dimension of animation.
+ 2, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ -1, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 1, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+
+static AnimTypeClass const Fire3(
+ ANIM_FIRE_SMALL, // Animation number.
+ "FIRE3", // Data name of animation.
+ 23, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ true, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ fixed(1, 32), // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ -1, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 2, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+
+static AnimTypeClass const Fire1(
+ ANIM_FIRE_MED2, // Animation number.
+ "FIRE1", // Data name of animation.
+ 23, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ true, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ true, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ fixed(1, 16), // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ -1, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 3, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+
+static AnimTypeClass const Fire4(
+ ANIM_FIRE_TINY, // Animation number.
+ "FIRE4", // Data name of animation.
+ 7, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ true, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ fixed(1, 32), // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ -1, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 3, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+
+static AnimTypeClass const Fire2(
+ ANIM_FIRE_MED, // Animation number.
+ "FIRE2", // Data name of animation.
+ 23, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ true, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ true, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ fixed(1, 16), // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ -1, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 3, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+
+static AnimTypeClass const OilFieldBurn(
+ ANIM_OILFIELD_BURN, // Animation number.
+ "FLMSPT", // Data name of animation.
+ 42, // Maximum dimension of animation.
+ 58, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ true, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 33, // Loop start frame number.
+ 99, // Ending frame of loop back.
+ 66, // Number of animation stages.
+ 65535, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+
+static AnimTypeClass const Gunfire(
+ ANIM_MUZZLE_FLASH, // Animation number.
+ "GUNFIRE", // Data name of animation.
+ 16, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ true, // Ground level animation?
+ true, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Number of times the animation loops.
+ 1, // Number of animation stages.
+ 1, // Ending frame of loop back.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+
+static AnimTypeClass const SmokeM(
+ ANIM_SMOKE_M, // Animation number.
+ "SMOKE_M", // Data name of animation.
+ 28, // Maximum dimension of animation.
+ 30, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ true, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 67, // Loop start frame number.
+ -1, // Loopback frame number.
+ -1, // Number of animation stages.
+ 6, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+
+/*
+** Mini-gun fire effect -- used by guard towers.
+*/
+static AnimTypeClass const GUNN(
+ ANIM_GUN_N, // Animation number.
+ "MINIGUN", // Data name of animation.
+ 18, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Number of times the animation loops.
+ 6, // Number of animation stages.
+ 0, // Ending frame of loop back.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+static AnimTypeClass const GUNNW(
+ ANIM_GUN_NW, // Animation number.
+ "MINIGUN", // Data name of animation.
+ 18, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 6, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Number of times the animation loops.
+ 6, // Number of animation stages.
+ 0, // Ending frame of loop back.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+static AnimTypeClass const GUNW(
+ ANIM_GUN_W, // Animation number.
+ "MINIGUN", // Data name of animation.
+ 18, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 12, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Number of times the animation loops.
+ 6, // Number of animation stages.
+ 0, // Ending frame of loop back.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+static AnimTypeClass const GUNSW(
+ ANIM_GUN_SW, // Animation number.
+ "MINIGUN", // Data name of animation.
+ 18, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 18, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Number of times the animation loops.
+ 6, // Number of animation stages.
+ 0, // Ending frame of loop back.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+static AnimTypeClass const GUNS(
+ ANIM_GUN_S, // Animation number.
+ "MINIGUN", // Data name of animation.
+ 18, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 24, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Number of times the animation loops.
+ 6, // Number of animation stages.
+ 0, // Ending frame of loop back.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+static AnimTypeClass const GUNSE(
+ ANIM_GUN_SE, // Animation number.
+ "MINIGUN", // Data name of animation.
+ 18, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 30, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Number of times the animation loops.
+ 6, // Number of animation stages.
+ 0, // Ending frame of loop back.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+static AnimTypeClass const GUNE(
+ ANIM_GUN_E, // Animation number.
+ "MINIGUN", // Data name of animation.
+ 18, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 36, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Number of times the animation loops.
+ 6, // Number of animation stages.
+ 0, // Ending frame of loop back.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+static AnimTypeClass const GUNNE(
+ ANIM_GUN_NE, // Animation number.
+ "MINIGUN", // Data name of animation.
+ 18, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ false, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 42, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Number of times the animation loops.
+ 6, // Number of animation stages.
+ 0, // Ending frame of loop back.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+static AnimTypeClass const CDeviator(
+ ANIM_CRATE_DEVIATOR, // Animation number.
+ "DEVIATOR", // Data name of animation.
+ 48, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 2, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 0, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE // Follow up animation.
+);
+
+static AnimTypeClass const CrateArmor(
+ ANIM_CRATE_ARMOR, // Animation number.
+ "ARMOR", // Data name of animation.
+ 48, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 2, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 0, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE // Follow up animation.
+);
+static AnimTypeClass const CrateSpeed(
+ ANIM_CRATE_SPEED, // Animation number.
+ "SPEED", // Data name of animation.
+ 48, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 2, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 0, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE // Follow up animation.
+);
+
+static AnimTypeClass const CrateFPower(
+ ANIM_CRATE_FPOWER, // Animation number.
+ "FPOWER", // Data name of animation.
+ 48, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 2, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 0, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE // Follow up animation.
+);
+static AnimTypeClass const CrateTQuake(
+ ANIM_CRATE_TQUAKE, // Animation number.
+ "TQUAKE", // Data name of animation.
+ 48, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 2, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 0, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE // Follow up animation.
+);
+
+static AnimTypeClass const CDollar(
+ ANIM_CRATE_DOLLAR, // Animation number.
+ "DOLLAR", // Data name of animation.
+ 48, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 2, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 0, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE // Follow up animation.
+);
+static AnimTypeClass const CEarth(
+ ANIM_CRATE_EARTH, // Animation number.
+ "EARTH", // Data name of animation.
+ 48, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 2, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 0, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE // Follow up animation.
+);
+static AnimTypeClass const CEmpulse(
+ ANIM_CRATE_EMPULSE, // Animation number.
+ "EMPULSE", // Data name of animation.
+ 48, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 2, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 0, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE // Follow up animation.
+);
+static AnimTypeClass const CInvun(
+ ANIM_CRATE_INVUN, // Animation number.
+ "INVUN", // Data name of animation.
+ 48, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 2, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 0, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE // Follow up animation.
+);
+static AnimTypeClass const CMine(
+ ANIM_CRATE_MINE, // Animation number.
+ "MINE", // Data name of animation.
+ 48, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 2, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 0, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE // Follow up animation.
+);
+static AnimTypeClass const CRapid(
+ ANIM_CRATE_RAPID, // Animation number.
+ "RAPID", // Data name of animation.
+ 48, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 2, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 0, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE // Follow up animation.
+);
+static AnimTypeClass const CStealth(
+ ANIM_CRATE_STEALTH, // Animation number.
+ "STEALTH2", // Data name of animation.
+ 48, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 2, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 0, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE // Follow up animation.
+);
+static AnimTypeClass const ChronoBox(
+ ANIM_CHRONO_BOX, // Animation number.
+ "CHRONBOX", // Data name of animation.
+ 48, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 2, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 0, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE // Follow up animation.
+);
+static AnimTypeClass const GPSBox(
+ ANIM_GPS_BOX, // Animation number.
+ "GPSBOX", // Data name of animation.
+ 48, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 2, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 0, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE // Follow up animation.
+);
+static AnimTypeClass const InvulBox(
+ ANIM_INVUL_BOX, // Animation number.
+ "INVULBOX", // Data name of animation.
+ 48, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 2, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 0, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE // Follow up animation.
+);
+static AnimTypeClass const ParaBox(
+ ANIM_PARA_BOX, // Animation number.
+ "PARABOX", // Data name of animation.
+ 48, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 2, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 0, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE // Follow up animation.
+);
+static AnimTypeClass const SonarBox(
+ ANIM_SONAR_BOX, // Animation number.
+ "SONARBOX", // Data name of animation.
+ 48, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 2, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 0, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE // Follow up animation.
+);
+
+static AnimTypeClass const CMissile(
+ ANIM_CRATE_MISSILE, // Animation number.
+ "MISSILE2", // Data name of animation.
+ 48, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 2, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 0, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE // Follow up animation.
+);
+
+static AnimTypeClass const MoveFlash(
+ ANIM_MOVE_FLASH, // Animation number.
+ "MOVEFLSH", // Data name of animation.
+ 24, // Maximum dimension of animation.
+ 0, // Biggest animation stage.
+ true, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ true, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ true, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 0, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE // Follow up animation.
+);
+
+static AnimTypeClass const Corpse1(
+ ANIM_CORPSE1, // Animation number.
+ "CORPSE1", // Data name of animation.
+ 24, // Maximum dimension of animation.
+ 1, // Biggest animation stage.
+ true, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ true, // Ground level animation?
+ true, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 15, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 0, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+
+static AnimTypeClass const Corpse2(
+ ANIM_CORPSE2, // Animation number.
+ "CORPSE2", // Data name of animation.
+ 24, // Maximum dimension of animation.
+ 1, // Biggest animation stage.
+ true, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ true, // Ground level animation?
+ true, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 15, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 0, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+
+static AnimTypeClass const Corpse3(
+ ANIM_CORPSE3, // Animation number.
+ "CORPSE3", // Data name of animation.
+ 24, // Maximum dimension of animation.
+ 1, // Biggest animation stage.
+ true, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ true, // Ground level animation?
+ true, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 15, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ 0, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 0, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+
+static AnimTypeClass const Twinkle1(
+ ANIM_TWINKLE1, // Animation number.
+ "TWINKLE1", // Data name of animation.
+ 8, // Maximum dimension of animation.
+ 1, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ -1, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 1, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+static AnimTypeClass const Twinkle2(
+ ANIM_TWINKLE2, // Animation number.
+ "TWINKLE2", // Data name of animation.
+ 8, // Maximum dimension of animation.
+ 1, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ -1, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 1, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+static AnimTypeClass const Twinkle3(
+ ANIM_TWINKLE3, // Animation number.
+ "TWINKLE3", // Data name of animation.
+ 8, // Maximum dimension of animation.
+ 1, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ -1, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 1, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+static AnimTypeClass const Flak(
+ ANIM_FLAK, // Animation number.
+ "FLAK", // Data name of animation.
+ 8, // Maximum dimension of animation.
+ 7, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ -1, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 1, // Number of times the animation loops.
+ VOC_NONE, // Sound effect to play.
+ ANIM_NONE
+);
+static AnimTypeClass const WaterExp1(
+ ANIM_WATER_EXP1, // Animation number.
+ "H2O_EXP1", // Data name of animation.
+ 64, // Maximum dimension of animation.
+ 3, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ true, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ -1, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 1, // Number of times the animation loops.
+ VOC_SPLASH, // Sound effect to play.
+ ANIM_NONE
+);
+static AnimTypeClass const WaterExp2(
+ ANIM_WATER_EXP2, // Animation number.
+ "H2O_EXP2", // Data name of animation.
+ 40, // Maximum dimension of animation.
+ 3, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ true, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ -1, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 1, // Number of times the animation loops.
+ VOC_SPLASH, // Sound effect to play.
+ ANIM_NONE
+);
+static AnimTypeClass const WaterExp3(
+ ANIM_WATER_EXP3, // Animation number.
+ "H2O_EXP3", // Data name of animation.
+ 32, // Maximum dimension of animation.
+ 3, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ true, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ -1, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 1, // Number of times the animation loops.
+ VOC_SPLASH, // Sound effect to play.
+ ANIM_NONE
+);
+
+
+static AnimTypeClass const MineExp1(
+ ANIM_MINE_EXP1, // Animation number.
+ "VEH-HIT2", // Data name of animation.
+ 21, // Maximum dimension of animation.
+ 1, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ true, // Forms a crater?
+ false, // Sticks to unit in square?
+ false, // Ground level animation?
+ false, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 1, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ -1, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 1, // Number of times the animation loops.
+ VOC_MINEBLOW, // Sound effect to play.
+ ANIM_NONE
+);
+
+#ifdef FIXIT_ANTS
+static AnimTypeClass const AntDeath(
+ ANIM_ANT_DEATH, // Animation number.
+ "ANTDIE", // Data name of animation.
+ 28, // Maximum dimension of animation.
+ 1, // Biggest animation stage.
+ false, // Theater specific art imagery?
+ true, // Normalized animation rate?
+ false, // Uses white translucent table?
+ false, // Scorches the ground?
+ false, // Forms a crater?
+ false, // Sticks to unit in square?
+ true, // Ground level animation?
+ true, // Translucent colors in this animation?
+ false, // Is this a flame thrower animation?
+ 0, // Damage to apply per tick (fixed point).
+ 4, // Delay between frames.
+ 0, // Starting frame number.
+ 0, // Loop start frame number.
+ -1, // Ending frame of loop back.
+ -1, // Number of animation stages.
+ 1, // Number of times the animation loops.
+ VOC_ANTDIE, // Sound effect to play.
+ ANIM_NONE
+);
+#endif
+
+/***********************************************************************************************
+ * AnimTypeClass::AnimTypeClass -- Constructor for animation types. *
+ * *
+ * This is the constructor for static objects that elaborate the various animation types *
+ * allowed in the game. Each animation in the game is of one of these types. *
+ * *
+ * INPUT: see below... *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/23/1994 JLB : Created. *
+ *=============================================================================================*/
+AnimTypeClass::AnimTypeClass(
+ AnimType anim,
+ char const * name,
+ int size,
+ int biggest,
+ bool istheater,
+ bool isnormal,
+ bool iswhitetrans,
+ bool isscorcher,
+ bool iscrater,
+ bool issticky,
+ bool ground,
+ bool istrans,
+ bool isflame,
+ fixed damage,
+ int delaytime,
+ int start,
+ int loopstart,
+ int loopend,
+ int stages,
+ int loops,
+ VocType soundid,
+ AnimType chainto) :
+ ObjectTypeClass(RTTI_ANIMTYPE,
+ int(anim),
+ true,
+ true,
+ false,
+ false,
+ true,
+ true,
+ false,
+ TXT_NONE,
+ name
+ ),
+ IsNormalized(isnormal),
+ IsGroundLayer(ground),
+ IsTranslucent(istrans),
+ IsWhiteTrans(iswhitetrans),
+ IsFlameThrower(isflame),
+ IsScorcher(isscorcher),
+ IsCraterForming(iscrater),
+ IsSticky(issticky),
+ IsTheater(istheater),
+ Type(anim),
+ Size(size),
+ Biggest(biggest),
+ Damage(damage),
+ Delay(delaytime),
+ Start(start),
+ LoopStart(loopstart),
+ LoopEnd(loopend),
+ Stages(stages),
+ Loops(loops),
+ Sound(soundid),
+ ChainTo(chainto)
+{
+}
+
+
+/***********************************************************************************************
+ * AnimTypeClass::operator new -- Allocate an animation type object from private pool. *
+ * *
+ * This routine will allocate an animation type class object. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with a pointer to the newly allocated anim type object. If no anim type *
+ * could be allocated, then NULL is returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/09/1996 JLB : Created. *
+ *=============================================================================================*/
+void * AnimTypeClass::operator new(size_t)
+{
+ return(AnimTypes.Alloc());
+}
+
+
+/***********************************************************************************************
+ * AnimTypeClass::operator delete -- Returns an anim type class object back to the pool. *
+ * *
+ * This will return the anim type class object back to the memory pool from whence it was *
+ * previously allocated. *
+ * *
+ * INPUT: pointer -- Pointer to the anim type class object to return to the memory pool. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/09/1996 JLB : Created. *
+ *=============================================================================================*/
+void AnimTypeClass::operator delete(void * pointer)
+{
+ AnimTypes.Free((AnimTypeClass *)pointer);
+}
+
+
+/***********************************************************************************************
+ * AnimTypeClass::Init_Heap -- Initialize the animation type system. *
+ * *
+ * This routine is called to initialize the animation type class heap. It allocates all *
+ * known animation types. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/09/1996 JLB : Created. *
+ *=============================================================================================*/
+void AnimTypeClass::Init_Heap(void)
+{
+ /*
+ ** These anim type class objects must be allocated in the exact order that they
+ ** are specified in the AnimType enumeration. This is necessary because the heap
+ ** allocation block index serves double duty as the type number index.
+ */
+ new AnimTypeClass(FBall1);
+ new AnimTypeClass(FireBallFade);
+ new AnimTypeClass(Frag1);
+ new AnimTypeClass(VehHit1);
+ new AnimTypeClass(VehHit2);
+ new AnimTypeClass(VehHit3);
+ new AnimTypeClass(ArtExp1);
+ new AnimTypeClass(Napalm1);
+ new AnimTypeClass(Napalm2);
+ new AnimTypeClass(Napalm3);
+ new AnimTypeClass(SmokePuff);
+ new AnimTypeClass(Piff);
+ new AnimTypeClass(PiffPiff);
+ new AnimTypeClass(Fire3);
+ new AnimTypeClass(Fire2);
+ new AnimTypeClass(Fire1);
+ new AnimTypeClass(Fire4);
+ new AnimTypeClass(Gunfire);
+ new AnimTypeClass(SmokeM);
+ new AnimTypeClass(BurnSmall);
+ new AnimTypeClass(BurnMed);
+ new AnimTypeClass(BurnBig);
+ new AnimTypeClass(OnFireSmall);
+ new AnimTypeClass(OnFireMed);
+ new AnimTypeClass(OnFireBig);
+ new AnimTypeClass(SAMN);
+ new AnimTypeClass(SAMNE);
+ new AnimTypeClass(SAME);
+ new AnimTypeClass(SAMSE);
+ new AnimTypeClass(SAMS);
+ new AnimTypeClass(SAMSW);
+ new AnimTypeClass(SAMW);
+ new AnimTypeClass(SAMNW);
+ new AnimTypeClass(GUNN);
+ new AnimTypeClass(GUNNE);
+ new AnimTypeClass(GUNE);
+ new AnimTypeClass(GUNSE);
+ new AnimTypeClass(GUNS);
+ new AnimTypeClass(GUNSW);
+ new AnimTypeClass(GUNW);
+ new AnimTypeClass(GUNNW);
+ new AnimTypeClass(LZSmoke);
+ new AnimTypeClass(CDeviator);
+ new AnimTypeClass(CDollar);
+ new AnimTypeClass(CEarth);
+ new AnimTypeClass(CEmpulse);
+ new AnimTypeClass(CInvun);
+ new AnimTypeClass(CMine);
+ new AnimTypeClass(CRapid);
+ new AnimTypeClass(CStealth);
+ new AnimTypeClass(CMissile);
+ new AnimTypeClass(MoveFlash);
+ new AnimTypeClass(OilFieldBurn);
+ new AnimTypeClass(ElectricDie);
+ new AnimTypeClass(Parachute);
+ new AnimTypeClass(DogElectricDie);
+ new AnimTypeClass(Corpse1);
+ new AnimTypeClass(Corpse2);
+ new AnimTypeClass(Corpse3);
+ new AnimTypeClass(SputDoor);
+ new AnimTypeClass(AtomBomb);
+ new AnimTypeClass(ChronoBox);
+ new AnimTypeClass(GPSBox);
+ new AnimTypeClass(InvulBox);
+ new AnimTypeClass(ParaBox);
+ new AnimTypeClass(SonarBox);
+ new AnimTypeClass(Twinkle1);
+ new AnimTypeClass(Twinkle2);
+ new AnimTypeClass(Twinkle3);
+ new AnimTypeClass(Flak);
+ new AnimTypeClass(WaterExp1);
+ new AnimTypeClass(WaterExp2);
+ new AnimTypeClass(WaterExp3);
+ new AnimTypeClass(CrateArmor);
+ new AnimTypeClass(CrateSpeed);
+ new AnimTypeClass(CrateFPower);
+ new AnimTypeClass(CrateTQuake);
+ new AnimTypeClass(ParaBomb);
+ new AnimTypeClass(MineExp1);
+#ifdef FIXIT_ANTS
+ new AnimTypeClass(AntDeath);
+#endif
+}
+
+/***********************************************************************************************
+ * AnimTypeClass::One_Time -- Performs one time action for animation types. *
+ * *
+ * This will load the animation shape data. It is called by the game initialization *
+ * process. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: This routine should be called ONLY once. *
+ * *
+ * HISTORY: *
+ * 06/02/1994 JLB : Created. *
+ *=============================================================================================*/
+void AnimTypeClass::One_Time(void)
+{
+ for (AnimType index = ANIM_FIRST; index < ANIM_COUNT; index++) {
+ char fullname[_MAX_FNAME+_MAX_EXT];
+
+ AnimTypeClass const & anim = As_Reference(index);
+
+ if (!anim.IsTheater) {
+
+ _makepath(fullname, NULL, NULL, As_Reference(index).IniName, ".SHP");
+
+ #ifndef NDEBUG
+ RawFileClass file(fullname);
+ if (file.Is_Available()) {
+ ((void const *&)As_Reference(index).ImageData) = Load_Alloc_Data(file);
+ } else {
+ ((void const *&)As_Reference(index).ImageData) = MFCD::Retrieve(fullname);
+ }
+ #else
+ ((void const *&)As_Reference(index).ImageData) = MFCD::Retrieve(fullname);
+ #endif
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * AnimTypeClass::Init -- Load any animation artwork that is theater specific. *
+ * *
+ * This routine will examine all the animation types and for any that are theater *
+ * specific, it will fetch a pointer to the artwork appropriate for the theater specified. *
+ * *
+ * INPUT: theater -- The theater to align the animation artwork with. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: Call this routine when the theater changes. *
+ * *
+ * HISTORY: *
+ * 07/06/1996 JLB : Created. *
+ *=============================================================================================*/
+void AnimTypeClass::Init(TheaterType theater)
+{
+ if (theater != LastTheater) {
+ for (AnimType index = ANIM_FIRST; index < ANIM_COUNT; index++) {
+ AnimTypeClass const & anim = As_Reference(index);
+
+ if (anim.IsTheater) {
+ char fullname[_MAX_FNAME+_MAX_EXT]; // Fully constructed iconset name.
+ _makepath(fullname, NULL, NULL, anim.IniName, Theaters[theater].Suffix);
+ ((void const *&)anim.ImageData) = MFCD::Retrieve(fullname);
+ }
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * Anim_Name -- Fetches the ASCII name of the animation type specified. *
+ * *
+ * This will convert the animation type specified into a text name. This name can be used *
+ * for uniquely identifying the animation. *
+ * *
+ * INPUT: anim -- The anim type to convert to a text string. *
+ * *
+ * OUTPUT: Returns with a pointer to the ASCII string that identifies this animation. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/06/1996 JLB : Created. *
+ *=============================================================================================*/
+char const * Anim_Name(AnimType anim)
+{
+ if (anim == ANIM_NONE) return("");
+
+ return(AnimTypeClass::As_Reference(anim).IniName);
+}
+
+
+/***********************************************************************************************
+ * AnimTypeClass::As_Reference -- Fetch a reference to the animation type specified. *
+ * *
+ * This routine will convert the animation type specified into a reference to the *
+ * animation type class object. *
+ * *
+ * INPUT: type -- The animation type to convert into a reference. *
+ * *
+ * OUTPUT: Returns with a reference to the animation type class object. *
+ * *
+ * WARNINGS: Be sure that the animation type specified is legal. If it isn't then the *
+ * results of this routine are undefined. *
+ * *
+ * HISTORY: *
+ * 07/06/1996 JLB : Created. *
+ *=============================================================================================*/
+AnimTypeClass & AnimTypeClass::As_Reference(AnimType type)
+{
+ return(* AnimTypes.Ptr(type));
+}
+
diff --git a/CODE/ADPCM.CPP b/CODE/ADPCM.CPP
new file mode 100644
index 0000000..1c87935
--- /dev/null
+++ b/CODE/ADPCM.CPP
@@ -0,0 +1,83 @@
+/*
+** Command & Conquer Red Alert(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 "function.h"
+
+extern "C" {
+#include "soscomp.h"
+#include "itable.cpp"
+#include "dtable.cpp"
+
+
+void sosCODECInitStream(_SOS_COMPRESS_INFO* info)
+{
+ info->dwSampleIndex = 0;
+ info->dwPredicted = 0;
+}
+
+
+unsigned long sosCODECDecompressData(_SOS_COMPRESS_INFO* info, unsigned long numbytes)
+{
+ unsigned long token;
+ long sample;
+ unsigned int fastindex;
+ unsigned char *inbuff;
+ unsigned short *outbuff;
+
+ inbuff = (unsigned char *)info->lpSource;
+ outbuff = (unsigned short *)info->lpDest;
+
+ // Preload variables before the big loop
+ fastindex = (unsigned int)info->dwSampleIndex;
+ sample = info->dwPredicted;
+
+ if (!numbytes)
+ goto SkipLoop;
+
+ do {
+ // First nibble
+ token = *inbuff++;
+ fastindex += token & 0x0f;
+ sample += DiffTable[fastindex];
+ fastindex = IndexTable[fastindex];
+ if (sample > 32767L)
+ sample = 32767L;
+ if (sample < -32768L)
+ sample = -32768L;
+ *outbuff++ = (unsigned short)sample;
+
+ // Second nibble
+ fastindex += token >> 4;
+ sample += DiffTable[fastindex];
+ fastindex = IndexTable[fastindex];
+ if (sample > 32767L)
+ sample = 32767L;
+ if (sample < -32768L)
+ sample = -32768L;
+ *outbuff++ = (unsigned short)sample;
+ } while(--numbytes);
+
+SkipLoop:
+
+ // Put local vars back
+ info->dwSampleIndex = (unsigned long)fastindex;
+ info->dwPredicted = sample;
+ return(numbytes << 2);
+}
+
+}
diff --git a/CODE/AIRCRAFT.CPP b/CODE/AIRCRAFT.CPP
new file mode 100644
index 0000000..c9a99e6
--- /dev/null
+++ b/CODE/AIRCRAFT.CPP
@@ -0,0 +1,4375 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/AIRCRAFT.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : AIRCRAFT.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : July 22, 1994 *
+ * *
+ * Last Update : November 2, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * AircraftClass::AI -- Processes the normal non-graphic AI for the aircraft. *
+ * AircraftClass::Active_Click_With -- Handles clicking over specified cell. *
+ * AircraftClass::Active_Click_With -- Handles clicking over specified object. *
+ * AircraftClass::AircraftClass -- The constructor for aircraft objects. *
+ * AircraftClass::Can_Enter_Cell -- Determines if the aircraft can land at this location. *
+ * AircraftClass::Can_Fire -- Checks to see if the aircraft can fire. *
+ * AircraftClass::Cell_Seems_Ok -- Checks to see if a cell is good to enter. *
+ * AircraftClass::Desired_Load_Dir -- Determines where passengers should line up. *
+ * AircraftClass::Draw_It -- Renders an aircraft object at the location specified. *
+ * AircraftClass::Draw_Rotors -- Draw rotor blades on the aircraft. *
+ * AircraftClass::Edge_Of_World_AI -- Detect if aircraft has exited the map. *
+ * AircraftClass::Enter_Idle_Mode -- Gives the aircraft an appropriate mission. *
+ * AircraftClass::Exit_Object -- Unloads passenger from aircraft. *
+ * AircraftClass::Fire_At -- Handles firing a projectile from an aircraft. *
+ * AircraftClass::Fire_Direction -- Determines the direction of fire. *
+ * AircraftClass::Good_Fire_Location -- Searches for and finds a good spot to fire from. *
+ * AircraftClass::Good_LZ -- Locates a good spot ot land. *
+ * AircraftClass::In_Which_Layer -- Calculates the display layer of the aircraft. *
+ * AircraftClass::Init -- Initialize the aircraft system to an empty state. *
+ * AircraftClass::Is_LZ_Clear -- Determines if landing zone is free for landing. *
+ * AircraftClass::Landing_Takeoff_AI -- Handle aircraft take off and landing processing. *
+ * AircraftClass::Look -- Aircraft will look if they are on the ground always. *
+ * AircraftClass::Mission_Attack -- Handles the attack mission for aircraft. *
+ * AircraftClass::Mission_Enter -- Control aircraft to fly to the helipad or repair center. *
+ * AircraftClass::Mission_Guard -- Handles aircraft in guard mode. *
+ * AircraftClass::Mission_Guard_Area -- Handles the aircraft guard area logic. *
+ * AircraftClass::Mission_Hunt -- Maintains hunt AI for the aircraft. *
+ * AircraftClass::Mission_Move -- Handles movement mission. *
+ * AircraftClass::Mission_Retreat -- Handles the aircraft logic for leaving the battlefield. *
+ * AircraftClass::Mission_Unload -- Handles unloading cargo. *
+ * AircraftClass::Movement_AI -- Handles aircraft physical movement logic. *
+ * AircraftClass::New_LZ -- Find a good landing zone. *
+ * AircraftClass::Overlap_List -- Returns with list of cells the aircraft overlaps. *
+ * AircraftClass::Paradrop_Cargo -- Drop a passenger by parachute. *
+ * AircraftClass::Per_Cell_Process -- Handle the aircraft per cell process. *
+ * AircraftClass::Pip_Count -- Returns the number of "objects" in aircraft. *
+ * AircraftClass::Player_Assign_Mission -- Handles player input to assign a mission. *
+ * AircraftClass::Pose_Dir -- Fetches the natural landing facing. *
+ * AircraftClass::Process_Fly_To -- Handles state machine for flying to destination. *
+ * AircraftClass::Process_Landing -- Landing process state machine handler. *
+ * AircraftClass::Process_Take_Off -- State machine support for taking off. *
+ * AircraftClass::Read_INI -- Reads aircraft object data from an INI file. *
+ * AircraftClass::Receive_Message -- Handles receipt of radio messages. *
+ * AircraftClass::Response_Attack -- Gives audio response to attack order. *
+ * AircraftClass::Response_Move -- Gives audio response to move request. *
+ * AircraftClass::Response_Select -- Gives audio response when selected. *
+ * AircraftClass::Rotation_AI -- Handle aircraft body and flight rotation. *
+ * AircraftClass::Scatter -- Causes the aircraft to move away a bit. *
+ * AircraftClass::Set_Speed -- Sets the speed for the aircraft. *
+ * AircraftClass::Shape_Number -- Fetch the shape number to use for the aircraft. *
+ * AircraftClass::Sort_Y -- Figures the sorting coordinate. *
+ * AircraftClass::Take_Damage -- Applies damage to the aircraft. *
+ * AircraftClass::Unlimbo -- Removes an aircraft from the limbo state. *
+ * AircraftClass::What_Action -- Determines what action to perform. *
+ * AircraftClass::What_Action -- Determines what action to perform. *
+ * AircraftClass::operator delete -- Deletes the aircraft object. *
+ * AircraftClass::operator new -- Allocates a new aircraft object from the pool *
+ * AircraftClass::~AircraftClass -- Destructor for aircraft object. *
+ * _Counts_As_Civ_Evac -- Is the specified object a candidate for civilian evac logic? *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+
+
+/***********************************************************************************************
+ * _Counts_As_Civ_Evac -- Is the specified object a candidate for civilian evac logic? *
+ * *
+ * Examines the specified object to see if it qualifies to be a civilian evacuation. This *
+ * can only occur if it is a civilian (or Tanya) and the special evacuation flag has been *
+ * set in the scenario control structure. *
+ * *
+ * INPUT: candidate -- Candidate object to examine for civilian evacuation legality. *
+ * *
+ * OUTPUT: bool; Is the specified object considered a civilian that must be auto-evacuated? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/24/1996 JLB : Created. *
+ *=============================================================================================*/
+static bool _Counts_As_Civ_Evac(ObjectClass const * candidate)
+{
+ /*
+ ** If the candidate pointer is missing, then return with failure code.
+ */
+ if (candidate == NULL) return(false);
+
+ /*
+ ** Only infantry objects can be considered for civilian evacuation action.
+ */
+ if (candidate->What_Am_I() != RTTI_INFANTRY) return(false);
+
+ /*
+ ** Working infantry object pointer.
+ */
+ InfantryClass const * inf = (InfantryClass const *)candidate;
+
+ /*
+ ** Certain infantry types will always be considered a civilian evacuation candidate. These
+ ** include the special one-time infantry that appear in some missions.
+ */
+ if (*inf == INFANTRY_EINSTEIN || *inf == INFANTRY_GENERAL || *inf == INFANTRY_DELPHI || *inf == INFANTRY_CHAN) return(true);
+
+ /*
+ ** Consider Tanya to be part of the civilian evacuation logic if the scenario is
+ ** specially flagged for this.
+ */
+ if (Scen.IsTanyaEvac && *inf == INFANTRY_TANYA) return(true);
+
+ /*
+ ** If the infantry is not a civilian, then it isn't allowed to be a civilian evacuation.
+ */
+ if (!inf->Class->IsCivilian) return(false);
+
+ /*
+ ** Technicians look like civilians, but are not considered a legal evacuation candidate.
+ */
+ if (inf->IsTechnician) return(false);
+
+ /*
+ ** All tests pass, so return the success of the infantry as a civilian evacuation candidate.
+ */
+ return(true);
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::operator new -- Allocates a new aircraft object from the pool *
+ * *
+ * This routine will allocate an aircraft object from the free aircraft object pool. If *
+ * there are no free object available, then this routine will fail (return NULL). *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with a pointer to the allocate aircraft object or NULL if none were *
+ * available. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/26/1994 JLB : Created. *
+ *=============================================================================================*/
+void * AircraftClass::operator new(size_t)
+{
+ void * ptr = Aircraft.Allocate();
+ if (ptr) {
+ ((AircraftClass *)ptr)->IsActive = true;
+ }
+ return(ptr);
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::operator delete -- Deletes the aircraft object. *
+ * *
+ * This routine will return the aircraft object back to the free aircraft object pool. *
+ * *
+ * INPUT: ptr -- Pointer to the aircraft object to delete. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/26/1994 JLB : Created. *
+ *=============================================================================================*/
+void AircraftClass::operator delete(void * ptr)
+{
+ if (ptr) {
+ ((AircraftClass *)ptr)->IsActive = false;
+ }
+ Aircraft.Free((AircraftClass *)ptr);
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::AircraftClass -- The constructor for aircraft objects. *
+ * *
+ * This routine is the constructor for aircraft objects. An aircraft object can be *
+ * created and possibly placed into the game system by this routine. *
+ * *
+ * INPUT: classid -- The type of aircraft to create. *
+ * *
+ * house -- The owner of this aircraft. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/26/1994 JLB : Created. *
+ *=============================================================================================*/
+AircraftClass::AircraftClass(AircraftType classid, HousesType house) :
+ FootClass(RTTI_AIRCRAFT, Aircraft.ID(this), house),
+ Class(AircraftTypes.Ptr((int)classid)),
+ SecondaryFacing(PrimaryFacing),
+ Passenger(false),
+ IsLanding(false),
+ IsTakingOff(false),
+ IsHovering(false),
+ Jitter(0),
+ SightTimer(0),
+ AttacksRemaining(1)
+{
+ /*
+ ** For two shooters, clear out the second shot flag -- it will be set the first time
+ ** the object fires. For non two shooters, set the flag since it will never be cleared
+ ** and the second shot flag tells the system that normal rearm times apply -- this is
+ ** what is desired for non two shooters.
+ */
+ IsSecondShot = !Class->Is_Two_Shooter();
+ House->Tracking_Add(this);
+ Ammo = Class->MaxAmmo;
+ Height = FLIGHT_LEVEL;
+ Strength = Class->MaxStrength;
+ NavCom = TARGET_NONE;
+
+ /*
+ ** Keep count of the number of units created. Dont track cargo planes as they are created
+ ** automatically, not bought.
+ */
+// if (/*classid != AIRCRAFT_CARGO && */ Session.Type == GAME_INTERNET) {
+// House->AircraftTotals->Increment_Unit_Total((int)classid);
+// }
+
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Unlimbo -- Removes an aircraft from the limbo state. *
+ * *
+ * This routine is used to transition the aircraft from the limbo to the non limbo state. *
+ * It occurs when the aircraft is placed on the map for whatever reason. When it is *
+ * unlimboed, only then will normal game processing recognize it. *
+ * *
+ * INPUT: coord -- The coordinate that the aircraft should appear at. *
+ * *
+ * dir -- The direction it should start facing. *
+ * *
+ * strength (optional) -- sets initial strength *
+ * *
+ * mission (optional) -- sets initial mission *
+ * *
+ * OUTPUT: bool; Was the aircraft unlimboed successfully? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/26/1994 JLB : Created. *
+ *=============================================================================================*/
+bool AircraftClass::Unlimbo(COORDINATE coord, DirType dir)
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ if (FootClass::Unlimbo(coord, dir)) {
+
+ if (*this == AIRCRAFT_BADGER || (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->IsCamera)) {
+ IsALoaner = true;
+ }
+
+ /*
+ ** Ensure that the owning house knows about the
+ ** new object.
+ */
+ House->AScan |= (1L << Class->Type);
+ House->ActiveAScan |= (1L << Class->Type);
+
+ /*
+ ** Hack it so that aircraft that are both passenger and cargo carrying
+ ** will carry passengers at the expense of ammo.
+ */
+ if (Is_Something_Attached()) {
+ Ammo = 0;
+ Passenger = true;
+ }
+
+ /*
+ ** Forces the body of the helicopter to face the correct direction.
+ */
+ SecondaryFacing = dir;
+
+ /*
+ ** Start rotor animation.
+ */
+ if (!Class->IsFixedWing) {
+ Set_Rate(1);
+ Set_Stage(0);
+ }
+
+ /*
+ ** When starting at flight level, then give it speed. When landed
+ ** then it must be stationary.
+ */
+ if (Height == FLIGHT_LEVEL) {
+ Set_Speed(0xFF);
+ } else {
+ Set_Speed(0);
+ }
+ return(true);
+ }
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Shape_Number -- Fetch the shape number to use for the aircraft. *
+ * *
+ * This will determine what shape number to use for the aircraft in its current state. *
+ * The shape number can be used for drawing or determine shape rectangle size. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the shape number to use for the aircraft body. *
+ * *
+ * WARNINGS: Some aircraft, particularly helicopters, require other shapes attached to it. *
+ * *
+ * HISTORY: *
+ * 07/26/1996 JLB : Created. *
+ *=============================================================================================*/
+int AircraftClass::Shape_Number(void) const
+{
+ int shapenum = 0;
+
+ switch (Class->Rotation) {
+ case 32:
+ shapenum = UnitClass::BodyShape[Dir_To_32(SecondaryFacing)];
+ break;
+
+ case 16:
+ shapenum = UnitClass::BodyShape[Dir_To_16(SecondaryFacing)*2]/2;
+ break;
+
+ case 8:
+ shapenum = UnitClass::BodyShape[Dir_To_8(SecondaryFacing)*4]/4;
+ break;
+
+ default:
+ break;
+ }
+
+ /*
+ ** If there is a door on this aircraft (Chinook), then adjust the
+ ** shape number to match the door open state.
+ */
+ if (!Is_Door_Closed()) {
+ shapenum = Class->Rotation + Door_Stage();
+ }
+
+ return(shapenum);
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Draw_It -- Renders an aircraft object at the location specified. *
+ * *
+ * This routine is used to display the aircraft object at the coordinates specified. *
+ * The tactical map display uses this routine for all aircraft rendering. *
+ * *
+ * INPUT: x,y -- The coordinates to render the aircraft at. *
+ * *
+ * window -- The window that the coordinates are based upon. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/26/1994 JLB : Created. *
+ *=============================================================================================*/
+void AircraftClass::Draw_It(int x, int y, WindowNumberType window) const
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ /*
+ ** Verify the legality of the unit class.
+ */
+ void const * shapefile = Get_Image_Data();
+ if (!shapefile) return;
+
+ int shapenum = Shape_Number();
+
+ /*
+ ** Certain aircraft use algorithmic rotation for some stages. Set the
+ ** rotation value accordingly. A rotation of DIR_N means no rotation at all.
+ */
+ DirType rotation = DIR_N;
+ if (Class->Rotation == 16) {
+ rotation = DirType(Rotation16[SecondaryFacing]);
+ }
+
+#ifdef TOFIX
+ /*
+ ** The orca attack helicopter uses a special shape set when it is travelling
+ ** forward above a certain speed.
+ */
+ if (*this == AIRCRAFT_HIND && Get_Speed() >= MPH_MEDIUM_FAST) {
+ shapenum += Class->Rotation;
+ }
+#endif
+
+ /*
+ ** Helicopters that are flying have a "bobbing" effect.
+ */
+ int jitter = 0;
+ if (Height == FLIGHT_LEVEL && Get_Speed() < 3) {
+ static int _jitter[] = {0,0,0,0,1,1,1,0,0,0,0,0,-1,-1,-1,0};
+ jitter = _jitter[::Frame % 16];
+ }
+
+ /*
+ ** Special manual shadow draw code.
+ */
+ if (Visual_Character() <= VISUAL_DARKEN) {
+ CC_Draw_Shape(shapefile, shapenum, x+1, y+2, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, DisplayClass::FadingShade, NULL);
+ }
+
+ /*
+ ** Actually draw the root body of the unit.
+ */
+ Techno_Draw_Object(shapefile, shapenum, x, y+jitter, window, rotation);
+
+ /*
+ ** If this aircraft is equipped with rotor blades, then draw them at this time.
+ */
+ if (Class->IsRotorEquipped) {
+ Draw_Rotors(x, y+jitter, window);
+ }
+
+ /*
+ ** This draws any overlay graphics on the aircraft.
+ */
+ FootClass::Draw_It(x, y-Lepton_To_Pixel(Height), window);
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Draw_Rotors -- Draw rotor blades on the aircraft. *
+ * *
+ * This routine will draw rotor blades on the aircraft. It is presumed that the aircraft *
+ * has already been drawn at the X and Y pixel coordinates specified. *
+ * *
+ * INPUT: x,y -- The X and Y pixel coordinates to draw the rotor blades. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/26/1996 JLB : Created. *
+ *=============================================================================================*/
+void AircraftClass::Draw_Rotors(int x, int y, WindowNumberType window) const
+{
+ ShapeFlags_Type flags = SHAPE_CENTER|SHAPE_WIN_REL;
+ int shapenum;
+
+ /*
+ ** The rotor shape number depends on whether the helicopter is idling
+ ** or not. A landed helicopter uses slow moving "idling" blades.
+ */
+ if (Height == 0) {
+ shapenum = (Fetch_Stage()%8)+4;
+ flags = flags | SHAPE_GHOST;
+ } else {
+ shapenum = Fetch_Stage()%4;
+ flags = flags | SHAPE_FADING|SHAPE_PREDATOR;
+ }
+
+ if (*this == AIRCRAFT_TRANSPORT) {
+ int _stretch[FACING_COUNT] = {8, 9, 10, 9, 8, 9, 10, 9};
+
+ /*
+ ** Dual rotors offset along flight axis.
+ */
+ short xx = x;
+ short yy = y-Lepton_To_Pixel(Height);
+ FacingType face = Dir_Facing(SecondaryFacing);
+ Move_Point(xx, yy, SecondaryFacing.Current(), _stretch[face]);
+ CC_Draw_Shape(AircraftTypeClass::RRotorData, shapenum, xx, yy-2, window, flags, NULL, DisplayClass::UnitShadow);
+
+ Move_Point(xx, yy, SecondaryFacing.Current()+DIR_S, _stretch[face]*2);
+ CC_Draw_Shape(AircraftTypeClass::LRotorData, shapenum, xx, yy-2, window, flags, NULL, DisplayClass::UnitShadow);
+
+ } else {
+
+ /*
+ ** Single rotor centered about shape.
+ */
+ CC_Draw_Shape(AircraftTypeClass::RRotorData, shapenum, x, ((y-Lepton_To_Pixel(Height))-2), window, flags, NULL, DisplayClass::UnitShadow);
+ }
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Read_INI -- Reads aircraft object data from an INI file. *
+ * *
+ * This routine is used to read the aircraft object data from the INI file buffer *
+ * specified. This is used by the scenario loader code to interpret the INI file and *
+ * create the specified objects therein. *
+ * *
+ * INPUT: buffer -- Pointer to the INI buffer. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/26/1994 JLB : Created. *
+ *=============================================================================================*/
+void AircraftClass::Read_INI(CCINIClass & ini)
+{
+ AircraftClass * air; // Working unit pointer.
+ HousesType inhouse; // Unit house.
+ AircraftType classid; // Unit class.
+ char buf[128];
+
+ int counter = ini.Entry_Count(INI_Name());
+ for (int index = 0; index < counter; index++) {
+ char const * entry = ini.Get_Entry(INI_Name(), index);
+
+ ini.Get_String(INI_Name(), entry, NULL, buf, sizeof(buf)-1);
+ inhouse = HouseTypeClass::From_Name(strtok(buf, ","));
+ if (inhouse != HOUSE_NONE) {
+ classid = AircraftTypeClass::From_Name(strtok(NULL, ","));
+
+ if (classid != AIRCRAFT_NONE) {
+
+ air = new AircraftClass(classid, inhouse);
+ if (air) {
+ COORDINATE coord;
+ int strength;
+ DirType dir;
+
+ /*
+ ** Read the raw data.
+ */
+ char * token = strtok(NULL, ",");
+ if (token) {
+ strength = atoi(token);
+ } else {
+ strength = 0;
+ }
+
+ token = strtok(NULL, ",");
+ if (token) {
+ coord = Cell_Coord((CELL)atoi(token));
+ } else {
+ coord = 0xFFFFFFFFL;
+ }
+
+ token = strtok(NULL, ",");
+ if (token) {
+ dir = (DirType)atoi(token);
+ } else {
+ dir = DIR_N;
+ }
+
+ if (!Map.In_Radar(Coord_Cell(coord))) {
+ delete air;
+ } else {
+
+ air->Strength = air->Class->MaxStrength * fixed(strength, 256);
+ if (air->Unlimbo(coord, dir)) {
+ air->Assign_Mission(AircraftClass::Mission_From_Name(strtok(NULL, ",\n\r")));
+ } else {
+ delete air;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Mission_Hunt -- Maintains hunt AI for the aircraft. *
+ * *
+ * Hunt AI consists of finding a target and attacking it. If there is no target assigned *
+ * and this unit doesn't automatically hunt for more targets, then it will change *
+ * mission to a more passive (land and await further orders) type. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the number of ticks before calling this routine again. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/26/1994 JLB : Created. *
+ *=============================================================================================*/
+int AircraftClass::Mission_Hunt(void)
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ if (Class->IsFixedWing) {
+
+ if (TarCom != NavCom) {
+ Assign_Destination(TarCom);
+ }
+
+ enum {
+ LOOK_FOR_TARGET,
+ TAKE_OFF,
+ FLY_TO_TARGET,
+ DROP_BOMBS,
+ REGROUP
+ };
+ switch (Status) {
+
+ /*
+ ** Acquiring target stage.
+ */
+ case LOOK_FOR_TARGET:
+ if (Target_Legal(TarCom)) {
+ Status = TAKE_OFF;
+ return(1);
+ } else {
+ if (!Team.Is_Valid()) {
+ if (Session.Type != GAME_NORMAL) {
+ Assign_Target(Greatest_Threat(THREAT_TIBERIUM));
+ }
+ if (!Target_Legal(TarCom)) {
+ Assign_Target(Greatest_Threat(THREAT_NORMAL));
+ }
+
+ /*
+ ** If there is no target, then this aircraft should just do its normal thing.
+ */
+ if (!Target_Legal(TarCom) && !Team.Is_Valid()) {
+ Enter_Idle_Mode();
+ }
+ }
+ }
+ break;
+
+ /*
+ ** Make the aircraft take off from the airstrip.
+ */
+ case TAKE_OFF:
+ /*
+ ** If the aircraft is high enough to begin its mission, then do so.
+ */
+ if (Process_Take_Off()) {
+ IsTakingOff = false;
+ Set_Speed(0xFF);
+
+ /*
+ ** After takeoff is complete, break radio contact.
+ */
+ if (In_Radio_Contact()/*KO && Map[Coord].Cell_Building() == Contact_With_Whom()*/) {
+ Transmit_Message(RADIO_OVER_OUT);
+ }
+
+ Status = FLY_TO_TARGET;
+ }
+ return(1);
+
+ /*
+ ** Homing in on target stage.
+ */
+ case FLY_TO_TARGET:
+ switch (Can_Fire(TarCom, 0)) {
+ case FIRE_FACING:
+ /*
+ ** Catch the case where it is tightly circling the target. In that
+ ** case, increase the delay so that it has a chance to fly away and
+ ** break the circle cycle.
+ */
+ if (In_Range(TarCom, 0) || Passenger) {
+ return(TICKS_PER_SECOND * 2);
+ }
+ if (!PrimaryFacing.Is_Rotating() && Target_Legal(TarCom)) {
+ PrimaryFacing.Set_Desired(Direction(TarCom));
+ }
+ break;
+
+ case FIRE_AMMO:
+ Status = REGROUP;
+ break;
+
+ case FIRE_CANT:
+ case FIRE_ILLEGAL:
+ if (Mission == MISSION_ATTACK) {
+ Status = REGROUP;
+ } else {
+ Status = LOOK_FOR_TARGET;
+ }
+ break;
+
+ case FIRE_OK:
+ Status = DROP_BOMBS;
+ return(1);
+
+ default:
+ if (!PrimaryFacing.Is_Rotating() && Target_Legal(TarCom)) {
+ PrimaryFacing.Set_Desired(Direction(TarCom));
+ }
+ break;
+ }
+ return(TICKS_PER_SECOND/2);
+
+ /*
+ ** Dropping a stream of bombs phase.
+ */
+ case DROP_BOMBS:
+ TARGET targ;
+ switch (Can_Fire(TarCom, 0)) {
+ case FIRE_OK:
+ targ = ::As_Target(Coord_Move(Center_Coord(), SecondaryFacing, Weapon_Range(0)-0x0200));
+ if (Class->PrimaryWeapon != NULL) {
+ if (Class->PrimaryWeapon->IsCamera) {
+ Status = REGROUP;
+ } else {
+ Map[::As_Cell(TarCom)].Incoming(Coord, true);
+ }
+
+ /*
+ ** Force the target to be the actual target if this aircraft is
+ ** equipped with homing projectile.
+ */
+ if (Class->PrimaryWeapon->Bullet != NULL && Class->PrimaryWeapon->Bullet->ROT > 0) {
+ targ = TarCom;
+ }
+ }
+ Fire_At(targ, 0);
+ if (Class->Is_Two_Shooter()) {
+ Fire_At(targ, 0);
+ }
+ return(Arm);
+
+ case FIRE_RANGE:
+ case FIRE_FACING:
+ Status = FLY_TO_TARGET;
+ return(TICKS_PER_SECOND*4);
+
+ case FIRE_ILLEGAL:
+ if (Mission == MISSION_ATTACK) {
+ Status = REGROUP;
+ } else {
+ Status = LOOK_FOR_TARGET;
+ }
+ break;
+
+ case FIRE_CANT:
+ Status = REGROUP;
+ break;
+
+ case FIRE_AMMO:
+ AttacksRemaining--;
+ Status = REGROUP;
+ break;
+
+ default:
+ break;
+ }
+ return(1);
+
+ /*
+ ** Pull away to regroup for possibly another attack or a retreat.
+ */
+ case REGROUP:
+ if (Ammo == 0) {
+ AttacksRemaining = 0;
+ if (Team.Is_Valid()) Team->Remove(this);
+ Enter_Idle_Mode();
+ }
+
+ if (Mission == MISSION_ATTACK || (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->IsCamera) || (!AttacksRemaining && !Is_Something_Attached())) {
+
+ if (IsALoaner) {
+ if (Team) Team->Remove(this);
+ Assign_Mission(MISSION_RETREAT);
+ Commence();
+ } else {
+ if (!Team.Is_Valid()) Enter_Idle_Mode();
+ }
+ Commence();
+ } else {
+ Status = LOOK_FOR_TARGET;
+ }
+ break;
+
+ default:
+ break;
+ }
+ } else {
+ if (!Ammo) {
+ if (Team) Team->Remove(this);
+ Enter_Idle_Mode();
+ } else {
+ if (!Target_Legal(TarCom)) {
+ if (Session.Type != GAME_NORMAL) {
+ Assign_Target(Greatest_Threat(THREAT_TIBERIUM));
+ }
+ if (!Target_Legal(TarCom)) {
+ Assign_Target(Greatest_Threat(THREAT_NORMAL));
+ }
+ if (!Target_Legal(TarCom)) {
+ Enter_Idle_Mode();
+ return(1);
+ }
+ }
+
+ Assign_Mission(MISSION_ATTACK);
+ return(1);
+ }
+ }
+ return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2));
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::AI -- Processes the normal non-graphic AI for the aircraft. *
+ * *
+ * This handles the non-graphic AI processing for the aircraft. This usually entails *
+ * maintenance and other AI functions. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/26/1994 JLB : Created. *
+ *=============================================================================================*/
+void AircraftClass::AI(void)
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ /*
+ ** A Mission change can always occur if the aircraft is landed or flying.
+ */
+ if (!IsLanding && !IsTakingOff) {
+ Commence();
+ }
+
+ /*
+ ** Perform any base class AI processing. If during this process, the aircraft was
+ ** destroyed, then detect this and bail from this AI routine early.
+ */
+ FootClass::AI();
+ if (!IsActive) {
+ return;
+ }
+
+ /*
+ ** A Mission change can always occur if the aircraft is landed or flying.
+ */
+ if (!IsLanding && !IsTakingOff) {
+ Commence();
+ }
+
+ /*
+ ** Handle any body rotation at this time. Body rotation can occur even if the
+ ** flying object is not actually moving.
+ */
+ Rotation_AI();
+
+ /*
+ ** Handle any aircraft movement at this time.
+ */
+ Movement_AI();
+
+ /*
+ ** Any aircraft that is not in the ground layer must be redrawn. This is a
+ ** performance hit, but there is no other choice. The cells under an aircraft
+ ** do not know if there is an aircraft above it. Thus, it cannot flag the
+ ** aircraft to redraw. As a consequence, all aircraft must redraw.
+ */
+ if (In_Which_Layer() != LAYER_GROUND) {
+ Mark();
+ }
+
+ /*
+ ** Perform sighting every so often as controlled by the sight timer.
+ */
+ if (IsOwnedByPlayer && Class->SightRange && SightTimer == 0) {
+ Look();
+ SightTimer = TICKS_PER_SECOND;
+ }
+
+ /*
+ ** Handle landing and taking off logic. Helicopters are prime users of this technique. The
+ ** aircraft will either gain or lose altitude as appropriate. As the aircraft transitions
+ ** between flying level and ground level, it will be moved into the appropriate render
+ ** layer.
+ */
+ if (Landing_Takeoff_AI()) {
+ return;
+ }
+
+ /*
+ ** Always flag the map draw process to occur if there is an aircraft in the view.
+ ** This ensures that it will be rendered even if there is nothing else that flagged
+ ** the map to be redrawn.
+ */
+ if (Map.In_View(Coord_Cell(Coord))) {
+ Map.Flag_To_Redraw(false);
+ Map.DisplayClass::IsToRedraw = true;
+ }
+
+ /*
+ ** When aircraft leave the edge of the map, they might get destroyed. This occurs if the
+ ** aircraft is a non-player produced unit and it has completed its mission. A transport
+ ** helicopter that has already delivered reinforcements is a good example of this.
+ */
+ if (Edge_Of_World_AI()) {
+ return;
+ }
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Overlap_List -- Returns with list of cells the aircraft overlaps. *
+ * *
+ * When aircraft are flying, they can overlap quite a number of cells. These cells can *
+ * be determined from the coordinate where the aircraft is centered and the size of the *
+ * aircraft's shape. Landed aircraft are a special case and are usually much smaller *
+ * than when flying. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with a pointer to a cell offset list that specifies all cells that *
+ * the aircraft overlaps given the aircraft's current state. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/26/1994 JLB : Created. *
+ *=============================================================================================*/
+short const * AircraftClass::Overlap_List(bool redraw) const
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ static short const _list[] = {
+ -(MAP_CELL_W-1), -MAP_CELL_W, -(MAP_CELL_W+1),
+ -1, 0, 1,
+ (MAP_CELL_W-1), MAP_CELL_W, (MAP_CELL_W+1),
+ -((MAP_CELL_W*2)-1), -(MAP_CELL_W*2), -((MAP_CELL_W*2)+1),
+ -((MAP_CELL_W*3)-1), -(MAP_CELL_W*3), -((MAP_CELL_W*3)+1),
+ REFRESH_EOL
+ };
+
+ static short const _listbadger[] = {
+ -(MAP_CELL_W-2), -(MAP_CELL_W-1), -MAP_CELL_W, -(MAP_CELL_W+1), -(MAP_CELL_W+2),
+ -2, -1, 0, 1, 2,
+ (MAP_CELL_W-2), (MAP_CELL_W-1), MAP_CELL_W, (MAP_CELL_W+1), (MAP_CELL_W+2),
+ -((MAP_CELL_W*2)-2), -((MAP_CELL_W*2)-1), -(MAP_CELL_W*2), -((MAP_CELL_W*2)+1), -((MAP_CELL_W*2)+2),
+ -((MAP_CELL_W*3)-2), -((MAP_CELL_W*3)-1), -(MAP_CELL_W*3), -((MAP_CELL_W*3)+1), -((MAP_CELL_W*3)+2),
+ REFRESH_EOL
+ };
+
+ if (redraw || Height != 0) {
+#ifdef PARTIAL
+ Rect rect;
+ if (!IsSelected && Class->DimensionData != NULL && Class->IsFixedWing) {
+ int shapenum = min(Shape_Number(), Get_Build_Frame_Count(Class->Get_Image_Data())-1);
+ if (!Class->DimensionData[shapenum].Is_Valid()) {
+ Class->DimensionData[shapenum] = Shape_Dimensions(Class->Get_Image_Data(), shapenum);
+ }
+ rect = Class->DimensionData[shapenum];
+
+ /*
+ ** Increase the rectangle for the aircraft since the aircraft could
+ ** have its shape algorithmically rotated.
+ */
+ rect.X -= 5;
+ rect.Y -= 5;
+ rect.Width += 10;
+ rect.Height += 10;
+
+ Rect hrect = rect;
+
+ hrect.Y -= Lepton_To_Pixel(Height);
+
+ return(Coord_Spillage_List(Coord, Union(rect, hrect), true));
+ }
+#endif
+
+ if (*this == AIRCRAFT_BADGER) {
+ return(_listbadger);
+ } else {
+ return(_list);
+ }
+ }
+ return(Class->Overlap_List());
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Init -- Initialize the aircraft system to an empty state. *
+ * *
+ * This routine is used to clear out the aircraft allocation system. It is called in *
+ * preparation for a scenario load or save game load. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 09/24/1994 JLB : Created. *
+ *=============================================================================================*/
+void AircraftClass::Init(void)
+{
+ Aircraft.Free_All();
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Mission_Unload -- Handles unloading cargo. *
+ * *
+ * This function is used to handle finding, heading toward, landing, and unloading the *
+ * cargo from the aircraft. Once unloading of cargo has occurred, then the aircraft follows *
+ * a different mission. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns the number of game ticks to delay before calling this function again. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 10/31/94 JLB : Created. *
+ *=============================================================================================*/
+int AircraftClass::Mission_Unload(void)
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ if (Class->IsFixedWing) {
+
+ Assign_Target(NavCom);
+ return(Mission_Hunt());
+
+ } else {
+ enum {
+ SEARCH_FOR_LZ,
+ FLY_TO_LZ,
+ LAND_ON_LZ,
+ UNLOAD_PASSENGERS,
+ TAKE_OFF
+ };
+
+ switch (Status) {
+
+ /*
+ ** Search for an appropriate destination spot if one isn't already assigned.
+ */
+ case SEARCH_FOR_LZ:
+ if (Height == 0 && (Target_Legal(NavCom) || Coord == As_Coord(NavCom))) {
+ Status = UNLOAD_PASSENGERS;
+ } else {
+ if (!Is_LZ_Clear(NavCom)) {
+
+ FootClass * foot = Attached_Object();
+ if (foot != NULL && foot->Team && foot->Team->Class->Origin != -1) {
+ Assign_Destination(::As_Target(Scen.Waypoint[foot->Team->Class->Origin]));
+ } else {
+ Assign_Destination(New_LZ(::As_Target(Scen.Waypoint[WAYPT_REINF])));
+ if (Team.Is_Valid()) {
+ Team->Assign_Mission_Target(NavCom);
+ }
+ }
+ } else {
+ if (Height == FLIGHT_LEVEL) {
+ Status = FLY_TO_LZ;
+ } else {
+ Status = TAKE_OFF;
+ }
+ }
+ }
+ break;
+
+ /*
+ ** Fly to destination.
+ */
+ case FLY_TO_LZ:
+ if (Is_LZ_Clear(NavCom)) {
+ int distance = Process_Fly_To(true, NavCom);
+
+ if (distance < 0x0100) {
+ SecondaryFacing.Set_Desired(Pose_Dir());
+
+ if (distance < 0x0010) {
+ Status = LAND_ON_LZ;
+ }
+ return(1);
+ } else {
+ SecondaryFacing.Set_Desired(PrimaryFacing.Desired());
+ return(5);
+ }
+ } else {
+ Status = SEARCH_FOR_LZ;
+ }
+ break;
+
+ /*
+ ** Landing phase. Just delay until landing is complete. At that time,
+ ** transition to the unloading phase.
+ */
+ case LAND_ON_LZ:
+ if (IsTakingOff) {
+ Status = TAKE_OFF;
+ } else {
+ if (Process_Landing()) {
+ Status = UNLOAD_PASSENGERS;
+ }
+ }
+ return(1);
+
+ /*
+ ** Hold while unloading passengers. When passengers are unloaded the order for this
+ ** transport gets changed to MISSION_RETREAT.
+ */
+ case UNLOAD_PASSENGERS:
+ if (!IsTethered) {
+ if (Is_Something_Attached()) {
+ FootClass * unit = (FootClass *)Detach_Object();
+
+ /*
+ ** First thing is to lift the transport off of the map so that the unlimbo
+ ** process for the passengers is more likely to succeed.
+ */
+ Map.Pick_Up(Coord_Cell(Coord), this);
+
+ if (!Exit_Object(unit)) {
+ delete unit;
+ }
+
+ /*
+ ** Restore the transport back down on the map.
+ */
+ Map.Place_Down(Coord_Cell(Coord), this);
+
+ if (!Is_Something_Attached()) {
+ Enter_Idle_Mode();
+ }
+
+ } else {
+
+ Enter_Idle_Mode();
+ }
+ }
+ break;
+
+ /*
+ ** Aircraft is now taking off. Once the aircraft reaches flying altitude then it
+ ** will either take off or look for another landing spot to try again.
+ */
+ case TAKE_OFF: {
+ if (Process_Take_Off()) {
+ if (Is_Something_Attached()) {
+ Status = SEARCH_FOR_LZ;
+
+ /*
+ ** Break off radio contact with the helipad it is taking off from.
+ */
+ if (In_Radio_Contact() && Map[Coord].Cell_Building() == Contact_With_Whom()) {
+ Transmit_Message(RADIO_OVER_OUT);
+ }
+ } else {
+ Enter_Idle_Mode();
+ }
+ }
+ return(1);
+ }
+
+ default:
+ break;
+ }
+ }
+ return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2));
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Is_LZ_Clear -- Determines if landing zone is free for landing. *
+ * *
+ * This routine examines the landing zone (as specified by the target parameter) in order *
+ * to determine if it is free to be landed upon. Call this routine when it is necessary *
+ * to double check this. Typically this occurs right before a helicopter lands and also *
+ * when determining the landing zone in the first place. *
+ * *
+ * INPUT: target -- The target that is the "landing zone". *
+ * *
+ * OUTPUT: bool; Is the landing zone clear for landing? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 10/31/94 JLB : Created. *
+ *=============================================================================================*/
+bool AircraftClass::Is_LZ_Clear(TARGET target) const
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ if (!Target_Legal(target)) return(false);
+ CELL cell = ::As_Cell(target);
+ if (!Map.In_Radar(cell)) return(false);
+
+ /*
+ ** If the requested landing location is occupied, then only consider that location
+ ** legal if the occupying object is in radio contact with the aircraft. This presumes that
+ ** the two objects know what they are doing.
+ */
+ ObjectClass * object = Map[cell].Cell_Object();
+ if (object) {
+ if (object == this) return(true);
+
+ if (In_Radio_Contact() && Contact_With_Whom() == object) {
+ return(true);
+ }
+ return(false);
+ }
+
+ if (!Map[cell].Is_Clear_To_Move(SPEED_TRACK, false, false)) return(false);
+
+ return(true);
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Sort_Y -- Figures the sorting coordinate. *
+ * *
+ * This routine is used to determine the coordinate to use for sorting the aircraft. This *
+ * sorting value is used when the aircraft is on the ground. At that time the aircraft *
+ * must be rendered in proper relationship to the other ground objects. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the coordinate to use when sorting the aircraft with other ground *
+ * objects. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 11/02/1994 JLB : Created. *
+ *=============================================================================================*/
+COORDINATE AircraftClass::Sort_Y(void) const
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ return(Coord_Add(Coord, 0x00800000L));
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Mission_Retreat -- Handles the aircraft logic for leaving the battlefield. *
+ * *
+ * This mission will be followed when the aircraft decides that it is time to leave the *
+ * battle. Typically, this occurs when a loaner transport has dropped off its load or when *
+ * an attack air vehicle has expended its ordinance. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the number of game ticks to delay before calling this routine again. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 03/19/1995 JLB : Created. *
+ * 08/13/1995 JLB : Handles aircraft altitude gain after takeoff logic. *
+ *=============================================================================================*/
+int AircraftClass::Mission_Retreat(void)
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ if (Class->IsFixedWing) {
+ if (Class->IsFixedWing && Height < FLIGHT_LEVEL) {
+ Height += 1;
+ return(3);
+ }
+ return(TICKS_PER_SECOND*10);
+ }
+
+ enum {
+ TAKE_OFF,
+ FACE_MAP_EDGE,
+ KEEP_FLYING
+ };
+ switch (Status) {
+
+ /*
+ ** Take off if landed.
+ */
+ case TAKE_OFF:
+ if (Process_Take_Off()) {
+ Status = FACE_MAP_EDGE;
+ }
+ return(1);
+
+ /*
+ ** Set facing and speed toward the friendly map edge.
+ */
+ case FACE_MAP_EDGE:
+ Set_Speed(0xFF);
+
+ /*
+ ** Take advantage of the fact that the source map edge enumerations happen to
+ ** occur in a clockwise order and are the first four enumerations of the map
+ ** edge default for the house. If this value is masked and then shifted, a
+ ** normalized direction value results. Use this value to head the aircraft
+ ** toward the "friendly" map edge.
+ */
+ PrimaryFacing.Set_Desired((DirType)((House->Control.Edge & 0x03) << 6));
+ SecondaryFacing.Set_Desired(PrimaryFacing.Desired());
+ Status = KEEP_FLYING;
+ break;
+
+ /*
+ ** Just do nothing since we are headed toward the map edge. When the edge is
+ ** reached, the aircraft should be automatically eliminated.
+ */
+ case KEEP_FLYING:
+ break;
+
+ default:
+ break;
+ }
+ return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2));
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Exit_Object -- Unloads passenger from aircraft. *
+ * *
+ * This routine is called when the aircraft is to unload a passenger. The passenger must *
+ * be able to move under its own power. Typical situation is when a transport helicopter *
+ * is to unload an infantry unit. *
+ * *
+ * INPUT: unit -- Pointer to the unit that is to be unloaded from this aircraft. *
+ * *
+ * OUTPUT: bool; Was the unit unloaded successfully? *
+ * *
+ * WARNINGS: The unload process is merely started by this routine. Radio contact is *
+ * established with the unloading unit and when the unit is clear of the aircraft *
+ * the radio contact will be broken and then the aircraft is free to pursue *
+ * other. *
+ * *
+ * HISTORY: *
+ * 01/10/1995 JLB : Created. *
+ *=============================================================================================*/
+int AircraftClass::Exit_Object(TechnoClass * unit)
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ static FacingType _toface[FACING_COUNT] = {FACING_S, FACING_SW, FACING_SE, FACING_NW, FACING_NE, FACING_N, FACING_W, FACING_E};
+ CELL cell;
+
+ /*
+ ** Find a free cell to drop the unit off at.
+ */
+ for (FacingType face = FACING_N; face < FACING_COUNT; face++) {
+ cell = Adjacent_Cell(Coord_Cell(Coord), _toface[face]);
+ if (unit->Can_Enter_Cell(cell) == MOVE_OK) break;
+ }
+
+ // Should perform a check here to see if no cell could be found.
+
+ /*
+ ** If the passenger can be placed on the map, then start it moving toward the
+ ** destination cell and establish radio contact with the transport. This is used
+ ** to make sure that the transport waits until the passenger is clear before
+ ** unloading the next passenger or taking off.
+ */
+ if (unit->Unlimbo(Coord, Facing_Dir(_toface[face]))) {
+ unit->Assign_Mission(MISSION_MOVE);
+ unit->Assign_Destination(::As_Target(cell));
+ if (Transmit_Message(RADIO_HELLO, unit) == RADIO_ROGER) {
+ Transmit_Message(RADIO_UNLOAD);
+ }
+ return(true);
+ }
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Paradrop_Cargo -- Drop a passenger by parachute. *
+ * *
+ * Call this routine when a passenger needs to be dropped off by parachute. One passenger *
+ * is offloaded by a call to this routine. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the delay time that it is safe to wait before processing any further *
+ * paradrop actions. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/26/1996 JLB : Created. *
+ *=============================================================================================*/
+int AircraftClass::Paradrop_Cargo(void)
+{
+ FootClass * passenger = Detach_Object();
+ if (passenger) {
+ if (!passenger->Paradrop(Center_Coord())) {
+ Attach(passenger);
+ } else {
+
+ /*
+ ** Play a sound effect of the parachute opening.
+ */
+ Sound_Effect(VOC_CHUTE1, Coord);
+
+ if (Team.Is_Valid()) {
+ Team->Remove(passenger);
+ if (passenger->House->IsHuman) {
+ Assign_Mission(MISSION_GUARD);
+ } else {
+ Assign_Mission(MISSION_HUNT);
+ }
+ }
+// Arm = Rearm_Delay(IsSecondShot);
+ Arm = 0;
+ }
+ }
+ return(Arm);
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Fire_At -- Handles firing a projectile from an aircraft. *
+ * *
+ * Sometimes, aircraft firing needs special handling. Example: for napalm bombs, the *
+ * bomb travels forward at nearly the speed of the delivery aircraft, not necessarily the *
+ * default speed defined in the BulletTypeClass structure. *
+ * *
+ * INPUT: target -- The target that the projectile is heading for. *
+ * *
+ * which -- Which weapon to use in the attack. 0=primary, 1=secondary. *
+ * *
+ * OUTPUT: Returns with a pointer to the bullet that was created as a result of this attack. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 03/19/1995 JLB : Created. *
+ *=============================================================================================*/
+BulletClass * AircraftClass::Fire_At(TARGET target, int which)
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ /*
+ ** Passenger aircraft will actually paradrop their cargo instead of
+ ** firing their weapon.
+ */
+ if (Is_Something_Attached()) {
+ Paradrop_Cargo();
+ return(0);
+ }
+
+ /*
+ ** If the weapon is actually a camera, then perform the "snapshot" of the
+ ** ground instead of normal weapon fire.
+ */
+ if (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->IsCamera) {
+ if (House->Is_Ally(PlayerPtr)) {
+ Map.Sight_From(Coord_Cell(Center_Coord()), 9, House, false);
+ }
+ Ammo = 0;
+ Arm = Rearm_Delay(IsSecondShot);
+ return(0);
+ }
+
+
+ BulletClass * bullet = FootClass::Fire_At(target, which);
+
+ if (bullet) {
+
+ /*
+ ** Falling bullets move at a speed proportionate to the delivery craft.
+ */
+ if (bullet->Class->IsDropping) {
+ bullet->Fly_Speed(40, MPH_MEDIUM_SLOW); // TCTC To fix.
+ }
+ }
+ return(bullet);
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Take_Damage -- Applies damage to the aircraft. *
+ * *
+ * This routine is used to apply damage to the specified aircraft. This is where any *
+ * special crash animation will be initiated. *
+ * *
+ * INPUT: damage -- Reference to the damage that will be applied to the aircraft. *
+ * This value will be filled in with the actual damage that was *
+ * applied. *
+ * *
+ * distance -- Distance from the source of the explosion to this aircraft. *
+ * *
+ * warhead -- The warhead type that the damage occurs from. *
+ * *
+ * source -- Pointer to the originator of the damage. This can be used so that *
+ * proper "thank you" can be delivered. *
+ * *
+ * OUTPUT: Returns with the result of the damage as it affects this aircraft. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/26/1995 JLB : Created. *
+ *=============================================================================================*/
+ResultType AircraftClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, int forced)
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ ResultType res = RESULT_NONE;
+
+ /*
+ ** Flying aircraft take half damage.
+ */
+ if (Height) {
+ damage /= 2;
+ }
+
+ /*
+ ** Apply the damage to the aircraft.
+ */
+ res = FootClass::Take_Damage(damage, distance, warhead, source, forced);
+
+ /*
+ ** Special action is performed if the aircraft is killed -- the cargo is destroyed
+ ** as well.
+ */
+ switch (res) {
+ case RESULT_DESTROYED:
+ Kill_Cargo(source);
+ Death_Announcement();
+ new AnimClass(ANIM_FBALL1, Target_Coord());
+
+ /*
+ ** Parachute a survivor if possible.
+ */
+ if (Class->IsCrew && Percent_Chance(90) && Map[Center_Coord()].Is_Clear_To_Move(SPEED_FOOT, true, false)) {
+ InfantryClass * infantry = new InfantryClass(INFANTRY_E1, House->Class->House);
+ if (infantry != NULL) {
+ if (!infantry->Paradrop(Center_Coord())) {
+ delete infantry;
+ }
+ }
+ }
+
+ delete this;
+ break;
+
+ default:
+ case RESULT_HALF:
+ break;
+ }
+
+ return(res);
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Mission_Move -- Handles movement mission. *
+ * *
+ * This state machine routine is used when an aircraft (usually helicopter) is to move *
+ * from one location to another. It will handle any necessary take off and landing this *
+ * may require. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the number of game frames that should elapse before this routine *
+ * is called again. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/19/1995 JLB : Created. *
+ *=============================================================================================*/
+int AircraftClass::Mission_Move(void)
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ if (Class->IsFixedWing) {
+
+ enum {
+ TAKE_OFF,
+ FLY_TOWARD_TARGET
+ };
+
+ switch (Status) {
+ int distance;
+
+ case TAKE_OFF:
+
+ /*
+ ** If the aircraft is high enough to begin its mission, then do so.
+ */
+ if (Process_Take_Off()) {
+ IsTakingOff = false;
+ Set_Speed(0xFF);
+
+ /*
+ ** After takeoff is complete, break radio contact.
+ */
+ if (In_Radio_Contact() && Map[Coord].Cell_Building() == Contact_With_Whom()) {
+ Transmit_Message(RADIO_OVER_OUT);
+ }
+
+ Status = FLY_TOWARD_TARGET;
+ }
+ return(1);
+
+ case FLY_TOWARD_TARGET:
+ PrimaryFacing.Set_Desired(Direction(NavCom));
+ distance = Distance(NavCom);
+
+ if (distance < 0x00C0) {
+ MissionType mission = MISSION_GUARD;
+
+ if (!IsALoaner) {
+ /*
+ ** Normal aircraft try to find a good landing spot to rest.
+ */
+ BuildingClass * building = Find_Docking_Bay(Class->Building, false);
+#ifdef FIXIT_CARRIER // checked - ajw 9/28/98
+ if (!Class->IsFixedWing) {
+ int dist = 0x7FFFFFFF;
+ if (building) dist=Distance(building);
+ for (int index = 0; index < Vessels.Count(); index++) {
+ VesselClass *ship = Vessels.Ptr(index);
+ if (ship != NULL && *ship == VESSEL_CARRIER && !ship->IsInLimbo && ship->IsActive && ship->House == House && ship->How_Many() < ship->Class->Max_Passengers() ) {
+ if (Distance(ship) < dist || !building) {
+ building = (BuildingClass *)ship;
+ dist = Distance(ship);
+ }
+// break;
+ }
+ }
+ }
+#endif
+ Assign_Destination(TARGET_NONE);
+#ifdef FIXIT_CARRIER // checked - ajw 9/28/98
+ if (building && (Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER || building->What_Am_I() == RTTI_VESSEL) ) {
+#else
+ if (building && Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER) {
+#endif
+ mission = MISSION_ENTER;
+#ifdef FIXIT_CARRIER // checked - ajw 9/28/98
+ if (building->What_Am_I() == RTTI_VESSEL) {
+ Assign_Destination(building->As_Target());
+ }
+#endif
+ } else {
+ Assign_Destination(Good_LZ());
+
+ /*
+ ** If this aircraft has nowhere else to go, meaning that
+ ** there is no airfield available, then it has to crash.
+ */
+ if (Is_Target_Cell(NavCom)) {
+
+ if (Process_Landing()) {
+ Strength = 1;
+ int damage = Strength;
+ Take_Damage(damage, 0, WARHEAD_AP, 0, true);
+ return(1);
+ }
+ return(500);
+ }
+ mission = MISSION_MOVE;
+ }
+ Assign_Mission(mission);
+ Commence();
+ } else {
+ if (!Team.Is_Valid()) {
+ Enter_Idle_Mode();
+ }
+ }
+ return(1);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return(5);
+ }
+
+ enum {
+ VALIDATE_LZ,
+ TAKE_OFF,
+ FLY_TO_LZ,
+ LAND
+ };
+ switch (Status) {
+
+ /*
+ ** Double check and change LZ if necessary.
+ */
+ case VALIDATE_LZ:
+ if (!Target_Legal(NavCom)) {
+ Enter_Idle_Mode();
+ } else {
+ if (!Is_LZ_Clear(NavCom) || !Cell_Seems_Ok(As_Cell(NavCom))) {
+ Assign_Destination(New_LZ(NavCom));
+ if (Team.Is_Valid()) {
+ Team->Assign_Mission_Target(NavCom);
+ }
+ } else {
+ Status = TAKE_OFF;
+ }
+ }
+ break;
+
+ /*
+ ** Take off if necessary.
+ */
+ case TAKE_OFF:
+ if (!Target_Legal(NavCom)) {
+ Status = VALIDATE_LZ;
+ } else {
+ if (Process_Take_Off()) {
+
+ /*
+ ** After takeoff is complete, break radio contact with any helipad that this
+ ** helicopter is taking off from.
+ */
+ if (In_Radio_Contact() && Map[Coord].Cell_Building() == Contact_With_Whom()) {
+ Transmit_Message(RADIO_OVER_OUT);
+ }
+
+ Status = FLY_TO_LZ;
+ }
+ return(1);
+ }
+ break;
+
+ /*
+ ** Fly toward target.
+ */
+ case FLY_TO_LZ:
+ if (Is_LZ_Clear(NavCom)) {
+ int distance = Process_Fly_To(true, NavCom);
+
+ if (distance < 0x0080) {
+ if (Target_Legal(TarCom)) {
+ SecondaryFacing.Set_Desired(Direction(TarCom));
+ } else {
+ SecondaryFacing.Set_Desired(Pose_Dir());
+ }
+
+ if (distance < 0x0010) {
+ Status = LAND;
+ }
+ return(1);
+ }
+
+// SecondaryFacing.Set_Desired(::Direction(Fire_Coord(0), As_Coord(NavCom)));
+ SecondaryFacing.Set_Desired(Direction(NavCom));
+
+ } else {
+ Assign_Destination(New_LZ(NavCom));
+ if (Team.Is_Valid()) {
+ Team->Assign_Mission_Target(NavCom);
+ }
+ if (!Target_Legal(NavCom)) {
+ Status = LAND;
+ }
+ }
+ return(1);
+
+ /*
+ ** Land on target.
+ */
+ case LAND:
+ if (IsTakingOff) {
+ Assign_Destination(New_LZ(NavCom));
+ if (Team.Is_Valid()) {
+ Team->Assign_Mission_Target(NavCom);
+ }
+ Status = TAKE_OFF;
+ }
+ if (Process_Landing()) {
+ if (MissionQueue == MISSION_NONE) {
+ Enter_Idle_Mode();
+ }
+ }
+ return(1);
+
+ default:
+ break;
+ }
+
+ return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2));
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Enter_Idle_Mode -- Gives the aircraft an appropriate mission. *
+ * *
+ * Use this routine when the mission for the aircraft is in doubt. This routine will find *
+ * an appropriate mission for the aircraft and dispatch it. *
+ * *
+ * INPUT: initial -- Is this called when the unit just leaves a factory or is initially *
+ * or is initially placed on the map? *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/05/1995 JLB : Created. *
+ *=============================================================================================*/
+void AircraftClass::Enter_Idle_Mode(bool )
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ MissionType mission = MISSION_GUARD;
+ if (Class->IsFixedWing) {
+ if (In_Which_Layer() == LAYER_GROUND) {
+ if (IsALoaner) {
+ mission = MISSION_RETREAT;
+ } else {
+ Assign_Destination(TARGET_NONE);
+ Assign_Target(TARGET_NONE);
+ mission = MISSION_GUARD;
+ }
+ } else {
+
+ /*
+ ** If this transport is a loaner and part of a team, then remove it from
+ ** the team it is attached to.
+ */
+ if ((IsALoaner && House->IsHuman) || (!House->IsHuman && !Ammo)) {
+ if (Team.Is_Valid() && Team->Has_Entered_Map()) {
+ Team->Remove(this);
+ }
+ }
+ if (Team.Is_Valid()) return;
+
+ /*
+ ** Weapon equipped helicopters that run out of ammo and were
+ ** brought in as reinforcements will leave the map.
+ */
+ if (Mission != MISSION_ATTACK && IsALoaner && Ammo == 0 && Class->PrimaryWeapon != NULL) {
+ mission = MISSION_HUNT;
+ } else {
+
+ if (!IsALoaner) {
+ /*
+ ** Normal aircraft try to find a good landing spot to rest.
+ */
+ BuildingClass * building = Find_Docking_Bay(Class->Building, false);
+ Assign_Destination(TARGET_NONE);
+ if (building != NULL && Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER) {
+ if (Class->IsFixedWing) {
+ Status = 0; //BG - reset the mission status to avoid landing on the ground next to the airstrip
+ if (IsLanding) {
+ Process_Take_Off();
+ }
+ }
+ mission = MISSION_ENTER;
+ } else {
+ mission = MISSION_RETREAT;
+ }
+ }
+ }
+ }
+
+ } else {
+
+ if (In_Which_Layer() == LAYER_GROUND) {
+ if (IsALoaner) {
+ if (Is_Something_Attached()) {
+
+ /*
+ ** In the case of a computer controlled helicopter that hold passengers,
+ ** don't unload when landing. Wait for specific instructions from the
+ ** controlling team.
+ */
+ if (Team.Is_Valid()) {
+// if (Team.Is_Valid() && !House->IsHuman) {
+ mission = MISSION_GUARD;
+ } else {
+ mission = MISSION_UNLOAD;
+ }
+ } else {
+ mission = MISSION_RETREAT;
+ }
+ } else {
+ Assign_Destination(TARGET_NONE);
+ Assign_Target(TARGET_NONE);
+ mission = MISSION_GUARD;
+ }
+ } else {
+ if (Is_Something_Attached()) {
+ if (IsALoaner) {
+ if (Team) {
+ mission = MISSION_GUARD;
+ } else {
+ mission = MISSION_UNLOAD;
+ Assign_Destination(Good_LZ());
+ }
+ } else {
+ Assign_Destination(Good_LZ());
+ mission = MISSION_MOVE;
+ }
+ } else {
+
+ /*
+ ** If this transport is a loaner and part of a team, then remove it from
+ ** the team it is attached to.
+ */
+ if ((IsALoaner && House->IsHuman) || (!House->IsHuman && !Ammo)) {
+ if (Team.Is_Valid() && Team->Has_Entered_Map()) {
+ Team->Remove(this);
+ }
+ }
+
+ if (Class->PrimaryWeapon != NULL) {
+
+ /*
+ ** Weapon equipped helicopters that run out of ammo and were
+ ** brought in as reinforcements will leave the map.
+ */
+ if (IsALoaner) {
+
+ /*
+ ** If it has no ammo, then break off of the team and leave the map.
+ ** If it can fight, then give it fighting orders.
+ */
+ if (Ammo == 0) {
+ if (Team.Is_Valid()) Team->Remove(this);
+ mission = MISSION_RETREAT;
+ } else {
+ if (!Team.Is_Valid()) {
+ mission = MISSION_HUNT;
+ }
+ }
+
+ } else {
+
+ /*
+ ** Normal aircraft try to find a good landing spot to rest.
+ */
+ BuildingClass * building = Find_Docking_Bay(Class->Building, false);
+#ifdef FIXIT_CARRIER // checked - ajw 9/28/98
+ if (!Class->IsFixedWing) {
+ int dist = 0x7FFFFFFF;
+ if (building) dist=Distance(building);
+ for (int index = 0; index < Vessels.Count(); index++) {
+ VesselClass *ship = Vessels.Ptr(index);
+ if (ship != NULL && *ship == VESSEL_CARRIER && !ship->IsInLimbo && ship->IsActive && ship->House == House && ship->How_Many() < ship->Class->Max_Passengers()/* && !ship->In_Radio_Contact()*/) {
+ if (Distance(ship) < dist || !building) {
+ building = (BuildingClass *)ship;
+ dist = Distance(ship);
+ }
+// break;
+ }
+ }
+ }
+#endif
+ Assign_Destination(TARGET_NONE);
+#ifdef FIXIT_CARRIER // checked - ajw 9/28/98
+ if (building && (Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER || building->What_Am_I() == RTTI_VESSEL) ) {
+#else
+ if (building && Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER) {
+#endif
+ mission = MISSION_ENTER;
+#ifdef FIXIT_CARRIER // checked - ajw 9/28/98
+ if (building->What_Am_I() == RTTI_VESSEL) {
+ Assign_Destination(building->As_Target());
+ }
+#endif
+ } else {
+ Assign_Destination(Good_LZ());
+ mission = MISSION_MOVE;
+ }
+ }
+ } else {
+ if (Team) return;
+
+ Assign_Destination(Good_LZ());
+ mission = MISSION_MOVE;
+ }
+ }
+ }
+ }
+ Assign_Mission(mission);
+ Commence();
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Process_Fly_To -- Handles state machine for flying to destination. *
+ * *
+ * This support routine is used when the helicopter is to fly to the destination. It can *
+ * optionally slow the helicopter down as it approaches the destination. *
+ * *
+ * INPUT: slowdown -- Should the aircraft be slowed down when it approaches the dest? *
+ * *
+ * OUTPUT: Returns with the distance remaining between the aircraft and the destination. *
+ * *
+ * WARNINGS: Because the aircraft can move at a fast speed, the distance to target value *
+ * will probably never be zero. The likely case will be that the aircraft *
+ * overshoots the target. *
+ * *
+ * HISTORY: *
+ * 06/14/1995 JLB : Created. *
+ * 03/05/1996 JLB : Specifies destination target value. *
+ *=============================================================================================*/
+int AircraftClass::Process_Fly_To(bool slowdown, TARGET dest)
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ if (Class->IsFixedWing) slowdown = false;
+
+ COORDINATE coord;
+ if (Is_Target_Building(dest)) {
+ coord = As_Building(dest)->Docking_Coord();
+ } else {
+ coord = As_Coord(dest);
+ }
+ int distance = Distance(coord);
+
+ PrimaryFacing.Set_Desired(Direction(coord));
+
+ if (slowdown) {
+ int speed = min(distance, 0x0300);
+ speed = Bound(speed/3, 0x0020, 0x00FF);
+ if (Speed != speed) {
+ Set_Speed(speed);
+ }
+ }
+
+ if (distance < 0x0010) {
+ if (slowdown) {
+ Set_Speed(0);
+ }
+ distance = 0;
+ }
+ return(distance);
+}
+
+
+#ifdef CHEAT_KEYS
+/***********************************************************************************************
+ * AircraftClass::Debug_Dump -- Displays the status of the aircraft to the mono monitor. *
+ * *
+ * This displays the current status of the aircraft class to the mono monitor. By this *
+ * display bugs may be tracked down or prevented. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/02/1994 JLB : Created. *
+ *=============================================================================================*/
+void AircraftClass::Debug_Dump(MonoClass * mono) const
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ mono->Set_Cursor(0, 0);
+ mono->Print(Text_String(TXT_DEBUG_AIRCRAFT));
+ mono->Set_Cursor(1, 11);mono->Printf("%3d", AttacksRemaining);
+
+ FootClass::Debug_Dump(mono);
+}
+#endif
+
+
+/***********************************************************************************************
+ * AircraftClass::Active_Click_With -- Handles clicking over specified object. *
+ * *
+ * This routine is used when the player clicks over the speicifed object. It will assign *
+ * the appropriate mission to the aircraft. *
+ * *
+ * INPUT: action -- The action that was nominally determined by the What_Action function. *
+ * *
+ * object -- The object over which the mouse was clicked. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: This routine will alter the game sequence and causes an event packet to be *
+ * propagated to all connected machines. *
+ * *
+ * HISTORY: *
+ * 06/19/1995 JLB : Created. *
+ *=============================================================================================*/
+void AircraftClass::Active_Click_With(ActionType action, ObjectClass * object)
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ action = What_Action(object);
+
+ switch (action) {
+ case ACTION_NOMOVE:
+ return;
+
+ case ACTION_ENTER:
+ Player_Assign_Mission(MISSION_ENTER, TARGET_NONE, object->As_Target());
+ break;
+
+ case ACTION_SELF:
+ Player_Assign_Mission(MISSION_UNLOAD, TARGET_NONE, TARGET_NONE);
+ break;
+
+ default:
+ break;
+ }
+ FootClass::Active_Click_With(action, object);
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Active_Click_With -- Handles clicking over specified cell. *
+ * *
+ * This routine is used when the player clicks the mouse of the specified cell. It will *
+ * assign the appropriate mission to the aircraft. *
+ * *
+ * INPUT: action -- The action nominally determined by What_Action(). *
+ * *
+ * cell -- The cell over which the mouse was clicked. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: This routine will affect the game sequence and causes an event object to be *
+ * propagated to all connected machines. *
+ * *
+ * HISTORY: *
+ * 06/19/1995 JLB : Created. *
+ *=============================================================================================*/
+void AircraftClass::Active_Click_With(ActionType action, CELL cell)
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ FootClass::Active_Click_With(action, cell);
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Player_Assign_Mission -- Handles player input to assign a mission. *
+ * *
+ * This routine is called as a result of player input with the intent to change the *
+ * mission of the aircraft. *
+ * *
+ * INPUT: mission -- The mission requested of the aircraft. *
+ * *
+ * target -- The value to assign to the aircraft's targeting computer. *
+ * *
+ * dest. -- The value to assign to the aircraft's navigation computer. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: The mission specified will be executed at an indeterminate future game frame. *
+ * This is controlled by net/modem propagation delay. *
+ * *
+ * HISTORY: *
+ * 06/19/1995 JLB : Created. *
+ *=============================================================================================*/
+void AircraftClass::Player_Assign_Mission(MissionType mission, TARGET target, TARGET destination)
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ if (AllowVoice) {
+ if (mission == MISSION_ATTACK) {
+ Response_Attack();
+ } else {
+ Response_Move();
+ }
+ }
+ Queue_Mission(TargetClass(this), mission, target, destination);
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::What_Action -- Determines what action to perform. *
+ * *
+ * This routine is used to determine what action will likely be performed if the mouse *
+ * were clicked over the object specified. The display system calls this routine to *
+ * control the mouse shape. *
+ * *
+ * INPUT: target -- Pointer to the object that the mouse is currently over. *
+ * *
+ * OUTPUT: Returns with the action that will occur if the mouse were clicked over the *
+ * object specified. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/19/1995 JLB : Created. *
+ *=============================================================================================*/
+ActionType AircraftClass::What_Action(ObjectClass const * target) const
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ ActionType action = FootClass::What_Action(target);
+
+ if (action == ACTION_SELF && !How_Many()) {
+ action = ACTION_NONE;
+ }
+
+ if (action == ACTION_ATTACK && Class->PrimaryWeapon == NULL) {
+ action = ACTION_NONE;
+ }
+
+ if (House->IsPlayerControl && House->Is_Ally(target) && target->What_Am_I() == RTTI_BUILDING && ((AircraftClass *)this)->Transmit_Message(RADIO_CAN_LOAD, (TechnoClass*)target) == RADIO_ROGER) {
+ action = ACTION_ENTER;
+ }
+#ifdef FIXIT_CARRIER // checked - ajw 9/28/98
+ if (!Class->IsFixedWing && House->IsPlayerControl && House->Is_Ally(target) && target->What_Am_I() == RTTI_VESSEL && *(VesselClass *)target == VESSEL_CARRIER && ((AircraftClass *)this)->Transmit_Message(RADIO_CAN_LOAD, (TechnoClass*)target) == RADIO_ROGER) {
+ action = ACTION_ENTER;
+ }
+#endif
+
+ if (Class->IsFixedWing && action == ACTION_MOVE) {
+ action = ACTION_NOMOVE;
+ }
+
+ if (action == ACTION_NONE) {
+ action = What_Action(Coord_Cell(target->Center_Coord()));
+ }
+
+ /*
+ ** Special return to friendly repair factory action.
+ */
+ if (House->IsPlayerControl && action == ACTION_SELECT && target->What_Am_I() == RTTI_BUILDING) {
+ BuildingClass * building = (BuildingClass *)target;
+ if (building->Class->Type == STRUCT_REPAIR && !building->In_Radio_Contact() && !building->Is_Something_Attached()) {
+ action = ACTION_ENTER;
+ }
+ }
+
+ return(action);
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::What_Action -- Determines what action to perform. *
+ * *
+ * This routine will determine what action would occur if the mouse were clicked over the *
+ * cell specified. The display system calls this routine to determine what mouse shape *
+ * to use. *
+ * *
+ * INPUT: cell -- The cell over which the mouse is currently positioned. *
+ * *
+ * OUTPUT: Returns with the action that will be performed if the mouse were clicked at the *
+ * specified cell location. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/19/1995 JLB : Created. *
+ *=============================================================================================*/
+ActionType AircraftClass::What_Action(CELL cell) const
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ ActionType action = FootClass::What_Action(cell);
+
+ if (action == ACTION_MOVE && Session.Type == GAME_NORMAL && !Map[cell].IsVisible) {
+ action = ACTION_NOMOVE;
+ }
+
+ if (action == ACTION_ATTACK && Class->PrimaryWeapon == NULL) {
+ action = ACTION_NONE;
+ }
+
+ if (Class->IsFixedWing && action == ACTION_MOVE) {
+ action = ACTION_NOMOVE;
+ }
+
+ return(action);
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Pose_Dir -- Fetches the natural landing facing. *
+ * *
+ * Use this routine to get the desired facing the aircraft should assume when landing. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the normal default facing the aircraft should have when landed. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/19/1995 JLB : Created. *
+ * 03/04/1996 JLB : Fixed wing aircraft always face down the runway. *
+ *=============================================================================================*/
+DirType AircraftClass::Pose_Dir(void) const
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ if (*this == AIRCRAFT_TRANSPORT) {
+ return(DIR_N);
+ }
+ if (Class->IsFixedWing) {
+ return(DIR_E);
+ }
+ return(DIR_NE);
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Mission_Attack -- Handles the attack mission for aircraft. *
+ * *
+ * This routine is the state machine that handles the attack mission for aircraft. It will *
+ * handling homing in on and firing on the target in the aircraft's targeting computer. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the number of game ticks to pass before this routine must be called *
+ * again. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/19/1995 JLB : Created. *
+ * 09/22/1995 JLB : Fixes brain dead helicopter for Nod scen #7. *
+ *=============================================================================================*/
+int AircraftClass::Mission_Attack(void)
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ if (Class->IsFixedWing) {
+ return(Mission_Hunt());
+ }
+
+ enum {
+ VALIDATE_AZ,
+ PICK_ATTACK_LOCATION,
+ TAKE_OFF,
+ FLY_TO_POSITION,
+ FIRE_AT_TARGET,
+ FIRE_AT_TARGET2,
+ RETURN_TO_BASE
+ };
+ switch (Status) {
+
+ /*
+ ** Double check target and validate the attack zone.
+ */
+ case VALIDATE_AZ:
+ if (!Target_Legal(TarCom)) {
+ Status = RETURN_TO_BASE;
+ } else {
+ Status = PICK_ATTACK_LOCATION;
+ }
+ break;
+
+ /*
+ ** Pick a good location to attack from.
+ */
+ case PICK_ATTACK_LOCATION:
+ if (!Target_Legal(TarCom)) {
+ Status = RETURN_TO_BASE;
+ } else {
+ Assign_Destination(Good_Fire_Location(TarCom));
+ if (Target_Legal(NavCom)) {
+ Status = TAKE_OFF;
+ } else {
+ Status = RETURN_TO_BASE;
+ }
+ }
+ break;
+
+ /*
+ ** Take off (if necessary).
+ */
+ case TAKE_OFF:
+ if (!Target_Legal(TarCom)) {
+ Status = RETURN_TO_BASE;
+ } else {
+ if (Process_Take_Off()) {
+ Status = FLY_TO_POSITION;
+
+ /*
+ ** Break off radio contact with the helipad it is taking off from.
+ */
+ if (In_Radio_Contact() && Map[Coord].Cell_Building() == Contact_With_Whom()) {
+ Transmit_Message(RADIO_OVER_OUT);
+ }
+
+ /*
+ ** Start flying toward the destination by skewing at first.
+ ** As the flight progresses, the body will rotate to face
+ ** the direction of travel.
+ */
+ int diff = SecondaryFacing.Difference(Direction(NavCom));
+ diff = Bound(diff, -128, 128);
+ PrimaryFacing = DirType((int)SecondaryFacing.Current()+diff);
+ }
+ return(1);
+ }
+ break;
+
+ /*
+ ** Fly to attack location.
+ */
+ case FLY_TO_POSITION:
+ if (Target_Legal(TarCom)) {
+
+ /*
+ ** If the navcom was cleared mysteriously, then try to pick
+ ** a new attack location. This is a likely event if the player
+ ** clicks on a new target while in flight to an existing target.
+ */
+ if (!Target_Legal(NavCom)) {
+ Status = PICK_ATTACK_LOCATION;
+ return(1);
+ }
+
+ int distance = Process_Fly_To(true, NavCom);
+
+ if (distance < 0x0200) {
+ SecondaryFacing.Set_Desired(Direction(TarCom));
+
+ if (distance < 0x0010) {
+ Status = FIRE_AT_TARGET;
+ Assign_Destination(TARGET_NONE);
+ }
+ } else {
+ SecondaryFacing.Set_Desired(::Direction(Fire_Coord(0), As_Coord(NavCom)));
+ return(1);
+ }
+ } else {
+ Status = RETURN_TO_BASE;
+ }
+ return(1);
+
+ /*
+ ** Fire at the target.
+ */
+ case FIRE_AT_TARGET:
+ if (!Target_Legal(TarCom)) {
+ Status = RETURN_TO_BASE;
+ return(1);
+ }
+
+ PrimaryFacing.Set_Desired(Direction(TarCom));
+ SecondaryFacing.Set_Desired(Direction(TarCom));
+ switch (Can_Fire(TarCom, 0)) {
+ case FIRE_CLOAKED:
+ Do_Uncloak();
+ break;
+
+ case FIRE_OK:
+ Fire_At(TarCom, 0);
+ Map[::As_Cell(TarCom)].Incoming(Coord, true);
+ Status = FIRE_AT_TARGET2;
+ break;
+
+ case FIRE_REARM:
+ case FIRE_FACING:
+ break;
+
+ default:
+ if (!Ammo) {
+ Status = RETURN_TO_BASE;
+ } else {
+ Status = FIRE_AT_TARGET2;
+ }
+ break;
+ }
+ return(1);
+
+ /*
+ ** Fire at the target.
+ */
+ case FIRE_AT_TARGET2:
+ if (!Target_Legal(TarCom)) {
+ Status = RETURN_TO_BASE;
+ return(1);
+ }
+
+ PrimaryFacing.Set_Desired(Direction(TarCom));
+ SecondaryFacing.Set_Desired(Direction(TarCom));
+ switch (Can_Fire(TarCom, 0)) {
+ case FIRE_CLOAKED:
+ Do_Uncloak();
+ break;
+
+ case FIRE_REARM:
+ break;
+
+ case FIRE_OK:
+ Fire_At(TarCom, 0);
+ Map[::As_Cell(TarCom)].Incoming(Coord, true);
+
+ if (Ammo) {
+ Status = Rule.IsCurleyShuffle ? PICK_ATTACK_LOCATION : FIRE_AT_TARGET;
+ } else {
+ Status = RETURN_TO_BASE;
+ }
+ break;
+
+ default:
+ if (!Ammo) {
+ Status = RETURN_TO_BASE;
+ } else {
+ if (!In_Range(TarCom)) {
+ Status = PICK_ATTACK_LOCATION;
+ } else {
+ Status = Rule.IsCurleyShuffle ? PICK_ATTACK_LOCATION : FIRE_AT_TARGET;
+ }
+ }
+ break;
+ }
+ break;
+
+ /*
+ ** Fly back to landing spot.
+ */
+ case RETURN_TO_BASE:
+ /*
+ ** Break off of firing at the target if there is no more
+ ** point in attacking it this mission. The player will
+ ** reassign a target for the next mission.
+ */
+ if (!Ammo && (IsALoaner || House->IsHuman)) {
+ Assign_Target(TARGET_NONE);
+ }
+ Assign_Destination(TARGET_NONE);
+ Enter_Idle_Mode();
+ break;
+
+ default:
+ break;
+ }
+
+ return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2));
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::New_LZ -- Find a good landing zone. *
+ * *
+ * Use this routine to locate a good landing zone that is nearby the location specified. *
+ * By using this routine it is possible to assign the same landing zone to several *
+ * aircraft and they will land nearby without conflict. *
+ * *
+ * INPUT: oldlz -- Target value of desired landing zone (usually a cell target value). *
+ * *
+ * OUTPUT: Returns with the new good landing zone. It might be the same value passed in. *
+ * *
+ * WARNINGS: The landing zone might be a goodly distance away from the ideal if there is *
+ * extensive blocking terrain in the vicinity. *
+ * *
+ * HISTORY: *
+ * 06/19/1995 JLB : Created. *
+ *=============================================================================================*/
+TARGET AircraftClass::New_LZ(TARGET oldlz) const
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ if (Target_Legal(oldlz) && (!Is_LZ_Clear(oldlz) || !Cell_Seems_Ok(As_Cell(oldlz)))) {
+ COORDINATE coord = As_Coord(oldlz);
+
+ /*
+ ** Scan outward in a series of concentric rings up to certain distance
+ ** in cells.
+ */
+ for (int radius = 0; radius < Rule.LZScanRadius / CELL_LEPTON_W; radius++) {
+ FacingType modifier = Random_Pick(FACING_N, FACING_NW);
+ CELL lastcell = -1;
+
+ /*
+ ** Perform a radius scan out from the original center location. Try to
+ ** find a cell that is allowed to be a legal LZ.
+ */
+ for (FacingType facing = FACING_N; facing < FACING_COUNT; facing++) {
+ CELL newcell = Coord_Cell(Coord_Move(coord, Facing_Dir(facing+modifier), radius * ICON_LEPTON_W));
+ if (Map.In_Radar(newcell)) {
+ TARGET newtarget = ::As_Target(newcell);
+
+ if (newcell != lastcell && Is_LZ_Clear(newtarget) && Cell_Seems_Ok(newcell)) {
+ return(newtarget);
+ }
+ lastcell = newcell;
+ }
+ }
+ }
+ }
+ return(oldlz);
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Receive_Message -- Handles receipt of radio messages. *
+ * *
+ * This routine receives all radio messages directed at this aircraft. It is used to handle *
+ * all inter-object coordination. Typically, this would be for transport helicopters and *
+ * other complex landing operations required of helicopters. *
+ * *
+ * INPUT: from -- The source of this radio message. *
+ * *
+ * message -- The message itself. *
+ * *
+ * param -- An optional parameter that may be used to transfer additional *
+ * data. *
+ * *
+ * OUTPUT: Returns with the radio response from the aircraft. *
+ * *
+ * WARNINGS: Some radio messages are handled by the base classes. *
+ * *
+ * HISTORY: *
+ * 06/19/1995 JLB : Created. *
+ *=============================================================================================*/
+RadioMessageType AircraftClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param)
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ switch (message) {
+
+ case RADIO_PREPARED:
+ if (Target_Legal(TarCom)) return(RADIO_NEGATIVE);
+ if ((Height == 0 && Ammo == Class->MaxAmmo) || (Height > 0 && Ammo > 0)) return(RADIO_ROGER);
+ return(RADIO_NEGATIVE);
+
+ /*
+ ** Something disastrous has happened to the object in contact with. Fall back
+ ** and regroup. This means that any landing process is immediately aborted.
+ */
+ case RADIO_RUN_AWAY:
+ if (IsLanding) {
+ IsLanding = false;
+ IsTakingOff = true;
+ }
+ if (Class->IsFixedWing) {
+ Assign_Destination(Good_LZ());
+ if (!Target_Legal(NavCom)) {
+ Assign_Mission(MISSION_RETREAT);
+ } else {
+ Assign_Mission(MISSION_MOVE);
+ }
+ } else {
+ Scatter(0, true);
+ }
+ break;
+
+ /*
+ ** The ground control requests that this specified landing spot be used.
+ */
+ case RADIO_MOVE_HERE:
+ FootClass::Receive_Message(from, message, param);
+ if (Is_Target_Building(param)) {
+ if (Transmit_Message(RADIO_CAN_LOAD, As_Techno(param)) != RADIO_ROGER) {
+ return(RADIO_NEGATIVE);
+ }
+ Assign_Mission(MISSION_ENTER);
+ Assign_Destination((TARGET)param);
+ } else {
+ Assign_Mission(MISSION_MOVE);
+ Assign_Destination((TARGET)param);
+ }
+ Commence();
+ return(RADIO_ROGER);
+
+ /*
+ ** Ground control is requesting if the aircraft requires navigation direction.
+ */
+ case RADIO_NEED_TO_MOVE:
+ FootClass::Receive_Message(from, message, param);
+ if (!Target_Legal(NavCom) && !IsTakingOff && !IsLanding) {
+ return(RADIO_ROGER);
+ }
+ return(RADIO_NEGATIVE);
+
+ /*
+ ** This message is sent by the passenger when it determines that it has
+ ** entered the transport.
+ */
+ case RADIO_IM_IN:
+ if (How_Many() == Class->Max_Passengers()) {
+ Close_Door(5, 4);
+ }
+
+ /*
+ ** If a civilian has entered the transport, then the transport will immediately
+ ** fly off the map.
+ */
+ if (_Counts_As_Civ_Evac(from)) {
+ Assign_Mission(MISSION_RETREAT);
+ }
+ return(RADIO_ATTACH);
+
+ /*
+ ** Docking maintenance message received. Check to see if new orders should be given
+ ** to the impatient unit.
+ */
+ case RADIO_DOCKING:
+ if (Class->Max_Passengers() > 0 && How_Many() < Class->Max_Passengers()) {
+ FootClass::Receive_Message(from, message, param);
+
+ if (!IsTethered && !IsLanding && !IsTakingOff && Height == 0) {
+
+ Open_Door(5, 4);
+
+ /*
+ ** If the potential passenger needs someplace to go, then figure out a good
+ ** spot and tell it to go.
+ */
+ if (Transmit_Message(RADIO_NEED_TO_MOVE, from) == RADIO_ROGER) {
+ CELL cell;
+ /*DirType dir =*/ Desired_Load_Dir(from, cell);
+
+ /*
+ ** If no adjacent free cells are detected, then passenger loading
+ ** cannot occur. Break radio contact.
+ */
+ if (cell == 0) {
+ Transmit_Message(RADIO_OVER_OUT, from);
+ } else {
+ param = (long)::As_Target(cell);
+
+ /*
+ ** Tell the potential passenger where it should go. If the passenger is
+ ** already at the staging location, then tell it to move onto the transport
+ ** directly.
+ */
+ if (Transmit_Message(RADIO_MOVE_HERE, param, from) == RADIO_YEA_NOW_WHAT) {
+ param = (long)As_Target();
+ Transmit_Message(RADIO_TETHER);
+ if (Transmit_Message(RADIO_MOVE_HERE, param, from) != RADIO_ROGER) {
+ Transmit_Message(RADIO_OVER_OUT, from);
+ } else {
+ Contact_With_Whom()->Unselect();
+ }
+ }
+ }
+ }
+ }
+ return(RADIO_ROGER);
+ }
+ break;
+
+ /*
+ ** Asks if the passenger can load on this transport.
+ */
+ case RADIO_CAN_LOAD:
+ if (Class->Max_Passengers() == 0 || from == NULL || !House->Is_Ally(from->Owner())) return(RADIO_STATIC);
+ if (/*!In_Radio_Contact() &&*/ How_Many() < Class->Max_Passengers()) {
+ return(RADIO_ROGER);
+ }
+ return(RADIO_NEGATIVE);
+
+ default:
+ break;
+ }
+
+ /*
+ ** Let the base class take over processing this message.
+ */
+ return(FootClass::Receive_Message(from, message, param));
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Desired_Load_Dir -- Determines where passengers should line up. *
+ * *
+ * This routine is used by the transport helicopter to determine the location where the *
+ * infantry passengers should line up before loading. *
+ * *
+ * INPUT: object -- The object that is trying to load up on this transport. *
+ * *
+ * -- Reference to the cell that the passengers should move to before the *
+ * actual load process may begin. *
+ * *
+ * OUTPUT: Returns with the direction that the helicopter should face for the load operation. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/12/1995 JLB : Created. *
+ * 07/30/1995 JLB : Revamped to scan all adjacent cells. *
+ *=============================================================================================*/
+DirType AircraftClass::Desired_Load_Dir(ObjectClass * object, CELL & moveto) const
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ CELL center = Coord_Cell(Center_Coord());
+ for (int sweep = FACING_N; sweep < FACING_S; sweep++) {
+ moveto = Adjacent_Cell(center, FacingType(FACING_S+sweep));
+ if (Map.In_Radar(moveto) && (Coord_Cell(object->Center_Coord()) == moveto || Map[moveto].Is_Clear_To_Move(SPEED_FOOT, false, false))) return(DIR_N);
+
+ moveto = Adjacent_Cell(center, FacingType(FACING_S-sweep));
+ if (Map.In_Radar(moveto) && (Coord_Cell(object->Center_Coord()) == moveto || Map[moveto].Is_Clear_To_Move(SPEED_FOOT, false, false))) return(DIR_N);
+ }
+ return(DIR_N);
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Process_Take_Off -- State machine support for taking off. *
+ * *
+ * This routine is used by the main game state machine processor. This utility routine *
+ * handles a helicopter as it transitions from landed to flying state. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Has the helicopter reached flight level now? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/12/1995 JLB : Created. *
+ *=============================================================================================*/
+bool AircraftClass::Process_Take_Off(void)
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ IsLanding = false;
+ IsTakingOff = true;
+
+ if (Class->IsFixedWing) {
+ Set_Speed(0xFF);
+ if (Height == FLIGHT_LEVEL) {
+ return(true);
+ }
+
+ } else {
+
+ switch (Height) {
+ case 0:
+ Close_Door(5, 4);
+ PrimaryFacing = SecondaryFacing;
+ break;
+
+ case FLIGHT_LEVEL/2:
+ PrimaryFacing.Set_Desired(Direction(NavCom));
+ break;
+
+ case FLIGHT_LEVEL-(FLIGHT_LEVEL/3):
+ SecondaryFacing.Set_Desired(PrimaryFacing.Desired());
+ Set_Speed(0x20);
+ break;
+
+ case FLIGHT_LEVEL-(FLIGHT_LEVEL/5):
+ Set_Speed(0x40);
+ break;
+
+ case FLIGHT_LEVEL:
+ Set_Speed(0xFF);
+ IsTakingOff = false;
+ return(true);
+
+ default:
+ break;
+ }
+ }
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Process_Landing -- Landing process state machine handler. *
+ * *
+ * This is a support routine that is called by the main state machine routines. This *
+ * routine is responsible for handling the helicopter as it transitions from flight to *
+ * landing. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Has the helicopter completely landed now? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/12/1995 JLB : Created. *
+ * 03/04/1996 JLB : Handles fixed wing aircraft. *
+ *=============================================================================================*/
+bool AircraftClass::Process_Landing(void)
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ IsTakingOff = false;
+ IsLanding = true;
+
+ if (Class->IsFixedWing) {
+ int distance = Distance(NavCom);
+
+ if (distance > 0x0100) {
+ SecondaryFacing.Set_Desired(::Direction(Fire_Coord(0), As_Coord(NavCom)));
+ }
+
+ switch (Height) {
+ case 0:
+ Set_Speed(0);
+ IsLanding = false;
+ return(true);
+
+ default:
+// if (distance*2 > Class->LandingSpeed) {
+// Set_Speed(Class->LandingSpeed);
+// } else {
+// Set_Speed(distance/2);
+// }
+
+ Set_Speed(Class->LandingSpeed / House->AirspeedBias);
+ break;
+ }
+
+ } else {
+ switch (Height) {
+ case 0:
+ IsLanding = false;
+ return(true);
+
+ case FLIGHT_LEVEL/2:
+ Set_Speed(0);
+ break;
+
+ case FLIGHT_LEVEL:
+ break;
+
+ default:
+ break;
+ }
+ }
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Can_Enter_Cell -- Determines if the aircraft can land at this location. *
+ * *
+ * This routine is used when the passability of a cell needs to be determined. This is *
+ * necessary when scanning for a location that the aircraft can land. *
+ * *
+ * INPUT: cell -- The cell location to check for landing. *
+ * *
+ * OUTPUT: Returns a value indicating if the cell is a legal landing spot or not. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/12/1995 JLB : Created. *
+ *=============================================================================================*/
+MoveType AircraftClass::Can_Enter_Cell(CELL cell, FacingType ) const
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ if (!Map.In_Radar(cell)) return(MOVE_NO);
+
+ CellClass * cellptr = &Map[cell];
+
+ ObjectClass const * occupier = cellptr->Cell_Occupier();
+
+ if (occupier == NULL ||
+ !occupier->Is_Techno() ||
+ ((TechnoClass *)occupier)->House->Is_Ally(House) ||
+ (((TechnoClass *)occupier)->Cloak != CLOAKED &&
+ (ScenarioInit == 0 && (occupier->What_Am_I() != RTTI_BUILDING || !((BuildingClass*)occupier)->Class->IsInvisible)) )
+ ) {
+
+ if (!cellptr->Is_Clear_To_Move(SPEED_TRACK, false, false)) return(MOVE_NO);
+ }
+
+ if (Session.Type == GAME_NORMAL && IsOwnedByPlayer && !cellptr->IsMapped) {
+ return(MOVE_NO);
+ }
+
+ return(MOVE_OK);
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Good_Fire_Location -- Searches for and finds a good spot to fire from. *
+ * *
+ * Given the specified target, this routine will locate a good spot for the aircraft to *
+ * fire at the target. *
+ * *
+ * INPUT: target -- The target that is desired to be attacked. *
+ * *
+ * OUTPUT: Returns with the target location of the place that firing should be made from. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/12/1995 JLB : Created. *
+ * 06/14/1995 JLB : Finer resolution on ring scan. *
+ * 11/02/1996 JLB : Bias fire position to get closer to moving objects. *
+ *=============================================================================================*/
+TARGET AircraftClass::Good_Fire_Location(TARGET target) const
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ if (Target_Legal(target)) {
+ int range = Weapon_Range(0);
+ COORDINATE tcoord = As_Coord(target);
+ CELL bestcell = 0;
+ CELL best2cell = 0;
+ int bestval = -1;
+ int best2val = -1;
+
+ /*
+ ** Try to get closer to a target that is moving.
+ */
+ COORDINATE altcoord = 0;
+ if (Is_Target_Object(target) && As_Object(target)->Is_Foot()) {
+ TARGET alttarg = ((FootClass *)As_Object(target))->NavCom;
+ if (Target_Legal(alttarg)) {
+ altcoord = As_Coord(alttarg);
+ }
+ }
+
+ for (int r = range-0x0100; r > 0x0100; r -= 0x0100) {
+ for (int face = 0; face < 255; face += 16) {
+ COORDINATE newcoord = Coord_Move(tcoord, (DirType)face, r);
+ CELL newcell = Coord_Cell(newcoord);
+
+ if (Map.In_Radar(newcell) && (Session.Type != GAME_NORMAL || Map[newcell].IsVisible) && Cell_Seems_Ok(newcell, true)) {
+ int dist;
+ if (altcoord != 0) {
+ dist = ::Distance(newcoord, altcoord);
+ } else {
+ dist = Distance(newcoord);
+ }
+ if (bestval == -1 || dist < bestval) {
+ best2val = bestval;
+ best2cell = bestcell;
+ bestval = dist;
+ bestcell = newcell;
+ }
+ }
+ }
+ if (bestval != -1) break;
+ }
+
+ if (best2val == -1) {
+ best2cell = bestcell;
+ }
+
+ /*
+ ** If it found a good firing location, then return this location as
+ ** a target value.
+ */
+ if (bestval != -1) {
+ if (Percent_Chance(50)) {
+ return(::As_Target(bestcell));
+ } else {
+ return(::As_Target(best2cell));
+ }
+ }
+ }
+ return(TARGET_NONE);
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Cell_Seems_Ok -- Checks to see if a cell is good to enter. *
+ * *
+ * This routine examines the navigation computers of other aircraft in order to see if the *
+ * specified cell is safe to fly to. The intent of this routine is to avoid unnecessary *
+ * mid-air collisions. *
+ * *
+ * INPUT: cell -- The cell to examine for clear airspace. *
+ * *
+ * strict -- Should the scan consider the aircraft, that is making this check, a *
+ * blocking aircraft. Typically, the aircraft itself is not considered *
+ * a blockage -- an aircraft can always exist where it is currently *
+ * located. A strict check is useful for helicopters that need to move *
+ * around at the slightest provocation. *
+ * *
+ * OUTPUT: Is the specified cell free from airspace conflicts? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/19/1995 JLB : Created. *
+ *=============================================================================================*/
+bool AircraftClass::Cell_Seems_Ok(CELL cell, bool strict) const
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ /*
+ ** Make sure that no other aircraft are heading to the selected location. If they
+ ** are, then don't consider the location as valid.
+ */
+ TARGET astarget = ::As_Target(cell);
+ for (int index = 0; index < Aircraft.Count(); index++) {
+ AircraftClass * air = Aircraft.Ptr(index);
+ if (air && (strict || air != this) && !air->IsInLimbo) {
+ if (Coord_Cell(air->Coord) == cell || air->NavCom == astarget) {
+ return(false);
+ }
+ }
+ }
+ return(true);
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Pip_Count -- Returns the number of "objects" in aircraft. *
+ * *
+ * This routine is used by the render logic to draw the little container "pips". This *
+ * corresponds to the number of passengers for a transport helicopter or the number of *
+ * shots remaining for an attack helicopter. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the number of "pips" to render on the aircraft. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/11/1995 JLB : Created. *
+ *=============================================================================================*/
+int AircraftClass::Pip_Count(void) const
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ int retval = 0;
+
+ if (Class->Max_Passengers() > 0) {
+ retval = How_Many();
+ } else {
+ if (Ammo) {
+ retval = Class->Max_Pips() * fixed(Ammo, Class->MaxAmmo);
+ if (!retval) retval = 1;
+ }
+ }
+ return(retval);
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Mission_Enter -- Control aircraft to fly to the helipad or repair center. *
+ * *
+ * This routine is used when the aircraft needs to fly for either rearming or repairing. *
+ * It tries to establish contact with the support building. Once contact is established *
+ * the ground controller takes care of commanding the aircraft. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the delay before this routine should be called again. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/12/1995 JLB : Created. *
+ * 07/04/1995 JLB : Ground controller gives orders. *
+ *=============================================================================================*/
+int AircraftClass::Mission_Enter(void)
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ enum {
+ INITIAL,
+ TAKEOFF,
+ ALTITUDE,
+ STACK,
+ DOWNWIND,
+ CROSSWIND,
+ TRAVEL,
+ LANDING
+ };
+
+ /*
+ ** Verify that it has a valid NavCom. If it doesn't then request one from the
+ ** building this building is trying to land upon. If that fails, then enter
+ ** idle mode.
+ */
+ if (!Target_Legal(NavCom) && In_Which_Layer() != LAYER_GROUND) {
+ if (Transmit_Message(RADIO_DOCKING) != RADIO_ROGER) {
+ Enter_Idle_Mode();
+ return(1);
+ }
+ }
+
+ switch (Status) {
+ case INITIAL:
+ if (Height < FLIGHT_LEVEL || IsLanding) {
+ Status = TAKEOFF;
+ } else {
+ Status = ALTITUDE;
+ }
+ break;
+
+ case TAKEOFF:
+ if (Process_Take_Off()) {
+ /*
+ ** After takeoff is complete, break radio contact with any helipad that this
+ ** helicopter is taking off from.
+ */
+ if (In_Radio_Contact() && Map[Coord].Cell_Building() == Contact_With_Whom()) {
+ Transmit_Message(RADIO_OVER_OUT);
+ }
+ Status = ALTITUDE;
+ }
+ break;
+
+ case ALTITUDE:
+ /*
+ ** Establish radio contact with the building this helicopter is trying
+ ** to land at.
+ */
+ if (In_Radio_Contact()) {
+ if (!Target_Legal(NavCom)) {
+ Transmit_Message(RADIO_DOCKING);
+ if (!Target_Legal(NavCom)) {
+ Enter_Idle_Mode();
+ return(1);
+ }
+ }
+ Status = STACK;
+ } else {
+ TechnoClass * tech = As_Techno(NavCom);
+ if (tech && Transmit_Message(RADIO_CAN_LOAD, tech) == RADIO_ROGER) {
+ Transmit_Message(RADIO_HELLO, tech);
+ Transmit_Message(RADIO_DOCKING);
+ Status = STACK;
+ } else {
+#ifdef FIXIT_CARRIER // checked - ajw 9/28/98
+ if (tech->What_Am_I() != RTTI_VESSEL) {
+ Assign_Destination(TARGET_NONE);
+ Enter_Idle_Mode();
+ }
+#else
+ Assign_Destination(TARGET_NONE);
+ Enter_Idle_Mode();
+#endif
+ }
+ }
+ break;
+
+ case STACK:
+ if (Class->IsFixedWing) {
+ int distance;
+ TARGET togo;
+
+ BuildingClass const * building = As_Building(NavCom);
+ if (building) {
+ togo = ::As_Target(building->Check_Point(CHECK_STACK));
+ } else {
+ togo = NavCom;
+ }
+
+ distance = Process_Fly_To(true, togo);
+ if (distance < 0x0080) {
+ Status = DOWNWIND;
+ }
+ } else {
+ Status = DOWNWIND;
+ }
+ break;
+
+ case DOWNWIND:
+ if (Class->IsFixedWing) {
+ int distance;
+ TARGET togo;
+
+ Set_Speed(200);
+ BuildingClass const * building = As_Building(NavCom);
+ if (building) {
+ togo = ::As_Target(building->Check_Point(CHECK_DOWNWIND));
+ } else {
+ togo = NavCom;
+ }
+
+ distance = Process_Fly_To(true, togo);
+ if (distance < 0x0080) {
+ Status = CROSSWIND;
+ }
+ } else {
+ Status = CROSSWIND;
+ }
+ break;
+
+ case CROSSWIND:
+ if (Class->IsFixedWing) {
+ int distance;
+ TARGET togo;
+
+ Set_Speed(140);
+ BuildingClass const * building = As_Building(NavCom);
+ if (building) {
+ togo = ::As_Target(building->Check_Point(CHECK_CROSSWIND));
+ } else {
+ togo = NavCom;
+ }
+
+ distance = Process_Fly_To(true, togo);
+ if (distance < 0x0080) {
+ Status = TRAVEL;
+ }
+ } else {
+ Status = TRAVEL;
+ }
+ break;
+
+ case TRAVEL:
+ Transmit_Message(RADIO_DOCKING);
+#ifdef FIXIT_CARRIER // checked - ajw 9/28/98
+ if (!In_Radio_Contact() && !Is_Target_Vessel(NavCom)) {
+#else
+ if (!In_Radio_Contact()) {
+#endif
+ Assign_Destination(TARGET_NONE);
+ Enter_Idle_Mode();
+ } else {
+ int distance = Process_Fly_To(true, NavCom);
+
+ if (Class->IsFixedWing) {
+
+ if (distance < 0x0400) {
+ Status = LANDING;
+ }
+ return(1);
+
+ } else {
+ if (distance < 0x0080) {
+ if (Target_Legal(TarCom)) {
+ SecondaryFacing.Set_Desired(Direction(TarCom));
+ } else {
+ SecondaryFacing.Set_Desired(Pose_Dir());
+ }
+
+#ifdef FIXIT_CARRIER // checked - ajw 9/28/98
+ if (Is_Target_Vessel(NavCom) && !In_Radio_Contact()) {
+ Enter_Idle_Mode();
+ break;
+ }
+#endif
+ if (distance < 0x0010) {
+ Status = LANDING;
+#ifdef FIXIT_CARRIER // checked - ajw 9/28/98
+ if(Is_Target_Vessel(NavCom) && As_Vessel(NavCom)->NavCom) {
+ Status = TRAVEL;
+ }
+#endif
+
+ }
+ break;
+ } else {
+ SecondaryFacing.Set_Desired(Direction(NavCom));
+// SecondaryFacing.Set_Desired(::Direction(Fire_Coord(0), As_Coord(NavCom)));
+ }
+ }
+ return(3);
+ }
+ break;
+
+ case LANDING:
+ if (IsTakingOff && !Class->IsFixedWing) {
+ Assign_Destination(TARGET_NONE);
+ Enter_Idle_Mode();
+ }
+#ifdef FIXIT_CARRIER // checked - ajw 9/28/98
+// If we were trying to land on a carrier and it moved, take off again
+ if ( As_Vessel(NavCom) && !In_Radio_Contact()) {
+ Status = INITIAL;
+ break;
+ }
+#endif
+ if (Process_Landing()) {
+ switch (Transmit_Message(RADIO_IM_IN)) {
+ case RADIO_ROGER:
+ Assign_Mission(MISSION_GUARD);
+ break;
+
+ case RADIO_ATTACH:
+#ifdef FIXIT_CARRIER // checked - ajw 9/28/98
+ if(Contact_With_Whom()->What_Am_I() != RTTI_VESSEL) Limbo();
+#else
+ Limbo();
+#endif
+ Contact_With_Whom()->Attach(this);
+ break;
+
+ default:
+ Enter_Idle_Mode();
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ return(1);
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Good_LZ -- Locates a good spot to land. *
+ * *
+ * This routine is used when helicopters need a place to land, but there are no obvious *
+ * spots (i.e., helipad) available. It will try to land near a friendly helipad or friendly *
+ * building if there are no helipads anywhere. In the event that there are no friendly *
+ * buildings anywhere on the map, then just land right where it is flying. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the target location where this aircraft should land. This value may *
+ * not be a clear cell, but the normal landing logic will resolve that problem. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/12/1995 JLB : Created. *
+ *=============================================================================================*/
+TARGET AircraftClass::Good_LZ(void) const
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ /*
+ ** Scan through all of the buildings and try to land near
+ ** the helipad (if there is one) or the nearest friendly building.
+ */
+ CELL bestcell = 0;
+ int bestdist = -1;
+ for (int index = 0; index < Buildings.Count(); index++) {
+ BuildingClass * building = Buildings.Ptr(index);
+
+ if (building && !building->IsInLimbo && building->House == House) {
+ int dist = Distance(building);
+ if (*building == Class->Building) {
+ dist /= 4;
+ }
+ if (bestdist == -1 || dist < bestdist) {
+ bestdist = dist;
+ bestcell = Coord_Cell(building->Center_Coord());
+ }
+ }
+ }
+
+ /*
+ ** Return with the suitable location if one was found.
+ */
+ if (bestdist != -1) {
+ return(::As_Target(bestcell));
+ }
+
+ /*
+ ** No good location was found. Just try to land here.
+ */
+ return(::As_Target(Coord_Cell(Coord)));
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Set_Speed -- Sets the speed for the aircraft. *
+ * *
+ * This routine will set the speed for the aircraft. The speed is specified as a fraction *
+ * of full speed. *
+ * *
+ * INPUT: speed -- The fixed point fractional speed setting. 0x00 is stopped, 0xFF is full *
+ * speed. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/19/1995 JLB : Created. *
+ *=============================================================================================*/
+void AircraftClass::Set_Speed(int speed)
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ FootClass::Set_Speed(speed);
+
+ MPHType sp = MPHType(min(Class->MaxSpeed * SpeedBias * House->AirspeedBias, MPH_LIGHT_SPEED));
+ Fly_Speed(speed, sp);
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Fire_Direction -- Determines the direction of fire. *
+ * *
+ * This routine will determine what direction a projectile would take if it were fired *
+ * from the aircraft. This is the direction that the aircraft's body is facing. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the direction of projectile fire. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/19/1995 JLB : Created. *
+ *=============================================================================================*/
+DirType AircraftClass::Fire_Direction(void) const
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ return(SecondaryFacing.Current());
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::~AircraftClass -- Destructor for aircraft object. *
+ * *
+ * This is the destructor for aircraft. It will limbo the aircraft if it isn't already *
+ * and also removes the aircraft from any team it may be attached to. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/24/1995 JLB : Created. *
+ *=============================================================================================*/
+AircraftClass::~AircraftClass(void)
+{
+ if (GameActive && Class) {
+
+ /*
+ ** Remove this member from any team it may be associated with. This must occur at the
+ ** top most level of the inheritance hierarchy because it may call virtual functions.
+ */
+ if (Team) {
+ Team->Remove(this);
+ Team = NULL;
+ }
+
+ House->Tracking_Remove(this);
+
+ /*
+ ** If there are any cargo members, delete them.
+ */
+ while (Is_Something_Attached()) {
+ delete Detach_Object();
+ }
+
+ AircraftClass::Limbo();
+ Class = 0;
+ }
+ ID = -1;
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Scatter -- Causes the aircraft to move away a bit. *
+ * *
+ * This routine will cause the aircraft to move away from its current location and then *
+ * enter some idle mode. Typically this is called when the aircraft is attacked while on *
+ * the ground. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/08/1995 JLB : Created. *
+ *=============================================================================================*/
+void AircraftClass::Scatter(COORDINATE , bool, bool )
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ /*
+ ** Certain missions prevent scattering regardless of whether it would be
+ ** a good idea or not.
+ */
+ if (!MissionControl[Mission].IsScatter) return;
+
+ /*
+ ** Fixed wing aircraft never scatter.
+ */
+ if (Class->IsFixedWing) return;
+
+ if (IsLanding || Height == 0) {
+ IsLanding = false;
+ IsTakingOff = true;
+ }
+ Enter_Idle_Mode();
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Mission_Guard -- Handles aircraft in guard mode. *
+ * *
+ * Aircraft don't like to be in guard mode if in flight. If this situation is detected, *
+ * then figure out what the aircraft should be doing and go do it. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the number of game frames to delay before calling this routine again. *
+ * *
+ * WARNINGS: This routine typically calls the normal guard logic for ground units. *
+ * *
+ * HISTORY: *
+ * 07/18/1995 JLB : Created. *
+ * 10/10/1995 JLB : Hunts for harvesters that are unescorted. *
+ *=============================================================================================*/
+int AircraftClass::Mission_Guard(void)
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ if (Height == FLIGHT_LEVEL) {
+
+ /*
+ ** If part of a team, then do nothing, since the team
+ ** handler will take care of giving this aircraft a
+ ** mission.
+ */
+ if (Team) {
+ if (Target_Legal(NavCom)) {
+ Assign_Mission(MISSION_MOVE);
+ }
+ return(MissionControl[Mission].Normal_Delay());
+ }
+
+ if (Class->PrimaryWeapon == NULL) {
+ Assign_Destination(::As_Target(Coord_Cell(Coord)));
+ Assign_Mission(MISSION_MOVE);
+ } else {
+ if (!Team.Is_Valid()) Enter_Idle_Mode();
+ }
+ return(1);
+ }
+ if (House->IsHuman) return(MissionControl[Mission].Normal_Delay());
+
+ /*
+ ** If the aircraft is very badly damaged, then it will search for a
+ ** repair bay first.
+ */
+ if (House->Available_Money() >= 100 && Health_Ratio() <= Rule.ConditionYellow) {
+ if (!In_Radio_Contact() ||
+ (Height == 0 &&
+ (Contact_With_Whom()->What_Am_I() != RTTI_BUILDING || *((BuildingClass *)Contact_With_Whom()) != STRUCT_REPAIR))) {
+
+
+ BuildingClass * building = Find_Docking_Bay(STRUCT_REPAIR, true);
+ if (building != NULL) {
+ Assign_Destination(building->As_Target());
+ Assign_Target(TARGET_NONE);
+ Assign_Mission(MISSION_ENTER);
+ return(1);
+ }
+ }
+ }
+
+ /*
+ ** If the aircraft cannot attack anything because of lack of ammo,
+ ** abort any normal guard logic in order to look for a helipad
+ ** to rearm.
+ */
+ if (Ammo == 0 && Is_Weapon_Equipped()) {
+ if (!In_Radio_Contact()) {
+ BuildingClass * building = Find_Docking_Bay(STRUCT_HELIPAD, false);
+#ifdef FIXIT_CARRIER // checked - ajw 9/28/98
+ if (!Class->IsFixedWing) {
+ int dist = 0x7FFFFFFF;
+ if (building) dist=Distance(building);
+ for (int index = 0; index < Vessels.Count(); index++) {
+ VesselClass *ship = Vessels.Ptr(index);
+ if (ship != NULL && *ship == VESSEL_CARRIER && !ship->IsInLimbo && ship->IsActive && ship->House == House && ship->How_Many() < ship->Class->Max_Passengers()) {
+ if (Distance(ship) < dist || !building) {
+ building = (BuildingClass *)ship;
+ dist = Distance(ship);
+ }
+// break;
+ }
+ }
+ }
+#endif
+ if (building != NULL) {
+ Assign_Destination(building->As_Target());
+ Assign_Target(TARGET_NONE);
+ Assign_Mission(MISSION_ENTER);
+ return(1);
+ }
+ }
+ }
+
+ /*
+ ** If the aircraft already has a target, then attack it if possible.
+ */
+ if (Target_Legal(TarCom)) {
+ Assign_Mission(MISSION_ATTACK);
+ return(1);
+ }
+
+ /*
+ ** Transport helicopters don't really do anything but just sit there.
+ */
+ if (!Is_Weapon_Equipped()) {
+ return(TICKS_PER_SECOND*3);
+ }
+
+ /*
+ ** Computer controlled helicopters will defend themselves by bouncing around
+ ** and looking for a free helipad.
+ */
+ if (Height == 0 && !In_Radio_Contact()) {
+ Scatter(0, true);
+ return(TICKS_PER_SECOND*3);
+ }
+
+ /*
+ ** Perform a special check to hunt for harvesters that are outside of the protective
+ ** shield of their base.
+ */
+ if (House->State != STATE_ATTACKED) {
+ TARGET target = House->Find_Juicy_Target(Coord);
+
+ if (Target_Legal(target)) {
+ Assign_Target(target);
+ Assign_Mission(MISSION_ATTACK);
+ }
+ }
+
+ return(FootClass::Mission_Guard());
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Mission_Guard_Area -- Handles the aircraft guard area logic. *
+ * *
+ * This routine handles area guard logic for aircraft. Aircraft require special handling *
+ * for this mode since they are to guard area only if they are in a position to do so. *
+ * Otherwise they just defend themselves. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the number of game frames to delay before calling this routine *
+ * again. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/10/1995 JLB : Created. *
+ *=============================================================================================*/
+int AircraftClass::Mission_Guard_Area(void)
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ if (Height == FLIGHT_LEVEL) {
+ if (!Team.Is_Valid()) Enter_Idle_Mode();
+ return(1);
+ }
+ if (House->IsHuman) return(TICKS_PER_SECOND);
+
+ if (Height == 0 && !In_Radio_Contact()) {
+ Scatter(0, true);
+ return(TICKS_PER_SECOND*3);
+ }
+
+ if (Target_Legal(TarCom)) {
+ Assign_Mission(MISSION_ATTACK);
+ return(1);
+ }
+ return(FootClass::Mission_Guard_Area());
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Response_Attack -- Gives audio response to attack order. *
+ * *
+ * This routine is used to give an audio response to an attack order. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/10/1995 JLB : Created. *
+ *=============================================================================================*/
+void AircraftClass::Response_Attack(void)
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ static VocType _response[] = {
+ VOC_AFFIRM,
+ VOC_ACKNOWL
+ };
+ VocType response = _response[Sim_Random_Pick(0, ARRAY_SIZE(_response)-1)];
+ if (AllowVoice) {
+ Sound_Effect(response, fixed(1), -(ID+1));
+ }
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Response_Move -- Gives audio response to move request. *
+ * *
+ * This routine is used to give an audio response to movement orders. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/10/1995 JLB : Created. *
+ *=============================================================================================*/
+void AircraftClass::Response_Move(void)
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ static VocType _response[] = {
+ VOC_ACKNOWL,
+ VOC_AFFIRM
+ };
+ VocType response = _response[Sim_Random_Pick(0, ARRAY_SIZE(_response)-1)];
+ if (AllowVoice) {
+ Sound_Effect(response, fixed(1), -(ID+1));
+ }
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Response_Select -- Gives audio response when selected. *
+ * *
+ * This routine is called when an audio response for selection is desired. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/10/1995 JLB : Created. *
+ *=============================================================================================*/
+void AircraftClass::Response_Select(void)
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ static VocType _response[] = {
+ VOC_VEHIC,
+ VOC_REPORT,
+ VOC_YESSIR,
+ VOC_YESSIR,
+ VOC_YESSIR,
+ VOC_AWAIT
+ };
+ VocType response = _response[Sim_Random_Pick(0, ARRAY_SIZE(_response)-1)];
+ if (AllowVoice) {
+ Sound_Effect(response, fixed(1), -(ID+1));
+ }
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Can_Fire -- Checks to see if the aircraft can fire. *
+ * *
+ * This routine is used to determine if the aircraft can fire its weapon at the target *
+ * specified. If it cannot, then the reason why is returned. *
+ * *
+ * INPUT: target -- The target that the aircraft might fire upon. *
+ * *
+ * which -- The weapon that will be used to fire. *
+ * *
+ * OUTPUT: Returns with the reason why it can't fire or with FIRE_OK. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 03/05/1996 JLB : Created. *
+ * 07/11/1996 JLB : Fixed for camera carrying aircraft. *
+ *=============================================================================================*/
+FireErrorType AircraftClass::Can_Fire(TARGET target, int which) const
+{
+ assert(Aircraft.ID(this) == ID);
+ assert(IsActive);
+
+ if (Passenger && !Is_Something_Attached()) {
+ return(FIRE_AMMO);
+ }
+
+ bool camera = (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->IsCamera);
+ bool fudge = (Passenger || (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->Bullet != NULL && Class->PrimaryWeapon->Bullet->IsParachuted));
+
+ if (fudge && !camera && !Ammo && !Passenger) {
+ return(FIRE_AMMO);
+ }
+
+ /*
+ ** Passenger aircraft that wish to 'fire' actually are requesting to
+ ** paradrop or 'throw out' the cargo. This is always allowed if the terrain under the
+ ** aircraft is generally clear.
+ */
+ if (camera || (fudge && Passenger && Is_Something_Attached())) {
+ if (Arm != 0) return(FIRE_REARM);
+
+ if (Distance(target) < (camera ? 0x0380 : 0x0200) && Map.In_Radar(Coord_Cell(Center_Coord()))) {
+// if (Distance(target) < (camera ? 0x0380 : 0x0280) && Map.In_Radar(Coord_Cell(Center_Coord()))) {
+ return(FIRE_OK);
+ }
+ return(FIRE_RANGE);
+ }
+
+ FireErrorType canfire = FootClass::Can_Fire(target, which);
+
+ if (canfire == FIRE_OK) {
+
+ /*
+ ** Double check to make sure that the facing is roughly toward
+ ** the target. If the difference is too great, then firing is
+ ** temporarily postponed.
+ */
+ if (Class->IsFixedWing) {
+
+ int diff = PrimaryFacing.Difference(Direction(TarCom));
+ if (ABS(diff) > (fudge ? 16 : 8)) {
+ return(FIRE_FACING);
+ }
+ }
+ }
+ return(canfire);
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Landing_Takeoff_AI -- Handle aircraft take off and landing processing. *
+ * *
+ * This routine handles the tricky maneuver of taking off and landing. The process of *
+ * landing is not entirely safe and thus the aircraft may be destroyed as a consequence. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: bool; Was the aircraft destroyed by this process? *
+ * *
+ * WARNINGS: Only call this routine once per aircraft per game logic loop. Be sure to *
+ * examine the return value and if true, abort all further processing of this *
+ * aircraft since it is now dead. *
+ * *
+ * HISTORY: *
+ * 07/29/1996 JLB : Created. *
+ *=============================================================================================*/
+bool AircraftClass::Landing_Takeoff_AI(void)
+{
+ /*
+ ** Handle landing and taking off logic. Helicopters are prime users of this technique. The
+ ** aircraft will either gain or lose altitude as appropriate. As the aircraft transitions
+ ** between flying level and ground level, it will be moved into the appropriate render
+ ** layer.
+ */
+ if (Is_Door_Closed() && (IsLanding || IsTakingOff)) {
+ LayerType layer = In_Which_Layer();
+
+ if (IsLanding) {
+ Mark(MARK_UP);
+ if (Height) Height -= Pixel_To_Lepton(1);
+ if (Height <= 0) {
+ Height = 0;
+ IsLanding = false;
+ Set_Speed(0);
+
+ /*
+ ** If the NavCom now equals the destination, then clear out the NavCom.
+ */
+ if (Coord_Cell(Center_Coord()) == As_Cell(NavCom)) {
+ Assign_Destination(TARGET_NONE);
+ }
+
+ /*
+ ** If a fixed-wing aircraft just landed on the ground, blow him up
+ */
+ if (Class->IsFixedWing && Mission != MISSION_ENTER) {
+ Strength = 1;
+
+ int damage = Strength;
+ Map.Remove(this, layer);
+ Take_Damage(damage, 0, WARHEAD_AP, 0, true);
+ return(true);
+ }
+
+ if (Target_Legal(NavCom) && As_Techno(NavCom) == Contact_With_Whom()) {
+ if (In_Radio_Contact() && Transmit_Message(RADIO_IM_IN) != RADIO_ROGER) {
+ Scatter(0, true);
+ }
+ }
+ }
+ Mark(MARK_DOWN);
+ }
+ if (IsTakingOff) {
+ Mark(MARK_UP);
+// Map.Remove(this, layer);
+ Height += Pixel_To_Lepton(1);
+ if (Height >= FLIGHT_LEVEL) {
+ Height = FLIGHT_LEVEL;
+ IsTakingOff = false;
+ }
+// Map.Submit(this, In_Which_Layer());
+ Mark(MARK_DOWN);
+ }
+
+ /*
+ ** Make adjustments for altitude by moving from one layer to another as
+ ** necessary.
+ */
+ if (layer != In_Which_Layer()) {
+
+ /*
+ ** When the aircraft is about to enter the ground layer, perform on last
+ ** check to see if it is legal to enter that location. If not, then
+ ** start the take off process. Let the normal logic handle this
+ ** change of plans.
+ */
+ bool ok = true;
+ if (In_Which_Layer() == LAYER_GROUND && !IsTakingOff && !Class->IsFixedWing) {
+ if (!Is_LZ_Clear(::As_Target(Coord_Cell(Coord)))) {
+ IsTakingOff = true;
+ Mark(MARK_UP);
+ Height += Pixel_To_Lepton(1);
+ Mark(MARK_DOWN);
+ ok = false;
+ }
+ }
+
+ if (ok) {
+
+ Map.Remove(this, layer);
+ Map.Submit(this, In_Which_Layer());
+
+ /*
+ ** When the aircraft is close to the ground, it should exist as a ground object.
+ ** This aspect is controlled by the Place_Down and Pick_Up functions.
+ */
+ if (In_Which_Layer() == LAYER_GROUND) {
+ Assign_Destination(TARGET_NONE); // Clear the navcom.
+ Transmit_Message(RADIO_TETHER);
+ Look();
+// Map.Sight_From(Coord_Cell(Coord), 1, House, false);
+ } else {
+ Transmit_Message(RADIO_UNTETHER);
+
+ /*
+ ** If the navigation computer is not attached to the object this
+ ** aircraft is in radio contact with, then assume that radio
+ ** contact is now superfluous. Break radio contact.
+ */
+ if (In_Radio_Contact() && Target_Legal(NavCom) && NavCom != Contact_With_Whom()->As_Target()) {
+ Transmit_Message(RADIO_OVER_OUT);
+ }
+ }
+ }
+ }
+ }
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Edge_Of_World_AI -- Detect if aircraft has exited the map. *
+ * *
+ * Certain aircraft will be eliminated when they leave the edge of the world presumably *
+ * after completing their mission. An exception is for aircraft that have been newly *
+ * created as reinforcements and have not yet completed their mission. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: bool; Was the aircraft deleted by this routine? *
+ * *
+ * WARNINGS: Be sure to call this routine only once per aircraft per game logic loop. If *
+ * the return value is true, then abort any further processing of this aircraft *
+ * since it has been eliminated. *
+ * *
+ * HISTORY: *
+ * 07/29/1996 JLB : Created. *
+ *=============================================================================================*/
+bool AircraftClass::Edge_Of_World_AI(void)
+{
+ if (!Map.In_Radar(Coord_Cell(Coord))) {
+ if (Mission == MISSION_RETREAT /*|| (*this == AIRCRAFT_CARGO && !Is_Something_Attached())*/) {
+
+ /*
+ ** Check to see if there are any civilians aboard. If so, then flag the house
+ ** that the civilian evacuation trigger event has been fulfilled.
+ */
+ while (Is_Something_Attached()) {
+ FootClass * obj = Detach_Object();
+
+ /*
+ ** Flag the owning house that civ evacuation has occurred.
+ */
+ if (_Counts_As_Civ_Evac(obj)) {
+ obj->House->IsCivEvacuated = true;
+ }
+
+ if (obj->Team.Is_Valid()) obj->Team->IsLeaveMap = true;
+
+#ifdef OLD
+ /*
+ ** Transport planes that leave can only be because they carry purchased
+ ** equipment and must be have their cost refunded.
+ */
+ if (*this == AIRCRAFT_CARGO) {
+ House->Refund_Money(obj->Class_Of().Cost_Of());
+ }
+#endif
+ delete obj;
+ }
+ if (Team.Is_Valid()) {
+ Team->IsLeaveMap = true;
+ }
+ Stun();
+ delete this;
+ return(true);
+ }
+ } else {
+ IsLocked = true;
+ }
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Movement_AI -- Handles aircraft physical movement logic. *
+ * *
+ * This routine manages the aircraft movement across the map. If any movement occurred, the *
+ * aircraft will be flagged to be redrawn. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: Only call this routine once per aircraft per game logic loop. *
+ * *
+ * HISTORY: *
+ * 07/29/1996 JLB : Created. *
+ *=============================================================================================*/
+void AircraftClass::Movement_AI(void)
+{
+ /*
+ ** If for some strange reason, there is a valid NavCom, but this aircraft is not
+ ** in a movement order, then give it a movement order.
+ */
+ if (Target_Legal(NavCom) && Mission == MISSION_GUARD && MissionQueue == MISSION_NONE) {
+ Assign_Mission(MISSION_MOVE);
+ }
+
+ if (Speed != 0) {
+ if (In_Which_Layer() == LAYER_GROUND) {
+ Mark(MARK_UP);
+ Physics(Coord, PrimaryFacing);
+ Mark(MARK_DOWN);
+ } else {
+ Mark(MARK_CHANGE_REDRAW);
+ if (Physics(Coord, PrimaryFacing) != RESULT_NONE) {
+ Mark(MARK_CHANGE_REDRAW);
+ }
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Rotation_AI -- Handle aircraft body and flight rotation. *
+ * *
+ * This will process the aircraft visible body and flight model rotation operations. If *
+ * any rotation occurred, the aircraft will be flagged to be redrawn. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: Only call this routine once per aircraft per game logic loop. *
+ * *
+ * HISTORY: *
+ * 07/29/1996 JLB : Created. *
+ *=============================================================================================*/
+void AircraftClass::Rotation_AI(void)
+{
+ if (PrimaryFacing.Is_Rotating()) {
+ Mark(MARK_CHANGE_REDRAW);
+ if (PrimaryFacing.Rotation_Adjust(Class->ROT)) {
+ Mark(MARK_CHANGE_REDRAW);
+ }
+ }
+ if (Class->IsFixedWing) {
+ SecondaryFacing = PrimaryFacing;
+ }
+ if (SecondaryFacing.Is_Rotating()) {
+ Mark(MARK_CHANGE_REDRAW);
+ if (SecondaryFacing.Rotation_Adjust(Class->ROT)) {
+ Mark(MARK_CHANGE_REDRAW);
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Per_Cell_Process -- Handle the aircraft per cell process. *
+ * *
+ * This is a seldom used function since its only purpose is to be called when an aircraft *
+ * lands on the ground. *
+ * *
+ * INPUT: why -- Why was this per cell process function called. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 09/15/1996 JLB : Created. *
+ *=============================================================================================*/
+void AircraftClass::Per_Cell_Process(PCPType why)
+{
+ BStart(BENCH_PCP);
+ FootClass::Per_Cell_Process(why);
+ BEnd(BENCH_PCP);
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Assign_Destination -- Assigns movement destination to the object. *
+ * *
+ * This routine is called when the object needs to have a new movement destination *
+ * assigned. Aircraft have their own version of this routine because a fixed-wing plane *
+ * trying to land will behave poorly if given a new destinatio while it's landing. * *
+ * *
+ * INPUT: destination -- The destination to assign to this object. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/24/1995 JLB : Created. *
+ *=============================================================================================*/
+void AircraftClass::Assign_Destination(TARGET dest)
+{
+ assert(IsActive);
+ if (dest == NavCom) return;
+
+ if (Target_Legal(dest) && Class->IsFixedWing && (IsLanding || (Target_Legal(NavCom) && dest != NavCom))) {
+// if (Target_Legal(dest) /*&& Class->IsFixedWing*/ && (IsLanding || (Target_Legal(NavCom) && dest != NavCom))) {
+
+// if (Class->IsFixedWing || As_Cell(dest) != Coord_Cell(Center_Coord())) {
+ Process_Take_Off();
+ Status = 0;
+// }
+ }
+ FootClass::Assign_Destination(dest);
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::In_Which_Layer -- Calculates the display layer of the aircraft. *
+ * *
+ * This examines the aircraft to determine what display layer it should be located *
+ * in. Fixed wing aircraft must always be in the top layer if they are flying even though *
+ * they may be low to the ground. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the layer that this aircraft resides in. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 10/20/1996 JLB : Created. *
+ *=============================================================================================*/
+LayerType AircraftClass::In_Which_Layer(void) const
+{
+ if (Class->IsFixedWing && Height > 0) {
+ return(LAYER_TOP);
+ }
+ return(FootClass::In_Which_Layer());
+}
+
+
+/***********************************************************************************************
+ * AircraftClass::Look -- Aircraft will look if they are on the ground always. *
+ * *
+ * Aircraft perform a look operation according to their sight range. If the aircraft is *
+ * on the ground, then it will look a distance of one cell regardless of what its *
+ * specified sight range is. *
+ * *
+ * INPUT: incremental -- Is this an incremental look? *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 10/23/1996 JLB : Created. *
+ *=============================================================================================*/
+void AircraftClass::Look(bool incremental)
+{
+ assert(IsActive);
+ assert(!IsInLimbo);
+
+ int sight_range = Techno_Type_Class()->SightRange;
+ if (Height == 0) {
+ sight_range = 1;
+ }
+
+ if (sight_range) {
+ Map.Sight_From(Coord_Cell(Coord), sight_range, House, incremental);
+ }
+}
diff --git a/CODE/AIRCRAFT.H b/CODE/AIRCRAFT.H
new file mode 100644
index 0000000..46d3e88
--- /dev/null
+++ b/CODE/AIRCRAFT.H
@@ -0,0 +1,248 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/AIRCRAFT.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : AIRCRAFT.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : July 22, 1994 *
+ * *
+ * Last Update : November 28, 1994 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifndef AIRCRAFT_H
+#define AIRCRAFT_H
+
+#include "radio.h"
+#include "fly.h"
+#include "target.h"
+
+
+/*
+** This aircraft class is used for all flying sentient objects. This includes fixed wing
+** aircraft as well as helicopters. It excludes bullets even though some bullets might
+** be considered to be "flying" in a loose interpretatin of the word.
+*/
+class AircraftClass : public FootClass, public FlyClass
+{
+ public:
+ /*
+ ** This is a pointer to the class control structure for the aircraft.
+ */
+ CCPtr Class;
+
+ //-----------------------------------------------------------------------------
+ static void * operator new(size_t);
+ static void * operator new(size_t, void * ptr) {return(ptr);};
+ static void operator delete(void *);
+ operator AircraftType(void) const {return Class->Type;};
+ AircraftClass(AircraftType classid, HousesType house);
+ AircraftClass(NoInitClass const & x) : FootClass(x), FlyClass(x), Class(x), SecondaryFacing(x), SightTimer(x) {};
+ virtual ~AircraftClass(void);
+
+ static void Init(void);
+
+ virtual int Mission_Attack(void);
+ virtual int Mission_Unload(void);
+ virtual int Mission_Hunt(void);
+ virtual int Mission_Retreat(void);
+ virtual int Mission_Move(void);
+ virtual int Mission_Enter(void);
+ virtual int Mission_Guard(void);
+ virtual int Mission_Guard_Area(void);
+
+ virtual void Assign_Destination(TARGET target);
+ /*
+ ** State machine support routines.
+ */
+ bool Process_Take_Off(void);
+ bool Process_Landing(void);
+ int Process_Fly_To(bool slowdown, TARGET dest);
+
+ /*
+ ** Query functions.
+ */
+ virtual LayerType In_Which_Layer(void) const;
+ virtual DirType Turret_Facing(void) const {return(SecondaryFacing.Current());}
+ int Shape_Number(void) const;
+ virtual MoveType Can_Enter_Cell(CELL cell, FacingType facing=FACING_NONE) const;
+ virtual ObjectTypeClass const & Class_Of(void) const {return *Class;};
+ virtual ActionType What_Action(ObjectClass const * target) const;
+ virtual ActionType What_Action(CELL cell) const;
+ virtual DirType Desired_Load_Dir(ObjectClass * passenger, CELL & moveto) const;
+ virtual int Pip_Count(void) const;
+ TARGET Good_Fire_Location(TARGET target) const;
+ bool Cell_Seems_Ok(CELL cell, bool landing=false) const;
+ DirType Pose_Dir(void) const;
+ TARGET Good_LZ(void) const;
+ virtual DirType Fire_Direction(void) const;
+ virtual FireErrorType Can_Fire(TARGET target, int which) const;
+
+ /*
+ ** Landing zone support functionality.
+ */
+ virtual void Per_Cell_Process(PCPType why);
+ bool Is_LZ_Clear(TARGET target) const;
+ TARGET New_LZ(TARGET oldlz) const;
+
+ /*
+ ** Coordinate inquiry functions. These are used for both display and
+ ** combat purposes.
+ */
+ virtual COORDINATE Sort_Y(void) const;
+
+ /*
+ ** Object entry and exit from the game system.
+ */
+ virtual bool Unlimbo(COORDINATE , DirType facing = DIR_N);
+
+ /*
+ ** Display and rendering support functionality. Supports imagery and how
+ ** object interacts with the map and thus indirectly controls rendering.
+ */
+ virtual void Look(bool incremental=false);
+ void Draw_Rotors(int x, int y, WindowNumberType window) const;
+ virtual int Exit_Object(TechnoClass *);
+ virtual short const * Overlap_List(bool redraw=false) const;
+ virtual void Draw_It(int x, int y, WindowNumberType window) const;
+ virtual void Set_Speed(int speed);
+
+ /*
+ ** User I/O.
+ */
+ virtual void Active_Click_With(ActionType action, ObjectClass * object);
+ virtual void Active_Click_With(ActionType action, CELL cell);
+ virtual void Player_Assign_Mission(MissionType mission, TARGET target=TARGET_NONE, TARGET destination=TARGET_NONE);
+ virtual void Response_Select(void);
+ virtual void Response_Move(void);
+ virtual void Response_Attack(void);
+
+ /*
+ ** Combat related.
+ */
+ virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced=false);
+ virtual BulletClass * Fire_At(TARGET target, int which);
+
+ /*
+ ** AI.
+ */
+ bool Landing_Takeoff_AI(void);
+ bool Edge_Of_World_AI(void);
+ void Movement_AI(void);
+ void Rotation_AI(void);
+ int Paradrop_Cargo(void);
+ virtual void AI(void);
+ virtual void Enter_Idle_Mode(bool initial = false);
+ virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param);
+ virtual void Scatter(COORDINATE threat, bool forced=false, bool nokidding=false);
+
+ /*
+ ** Scenario and debug support.
+ */
+ #ifdef CHEAT_KEYS
+ virtual void Debug_Dump(MonoClass *mono) const;
+ #endif
+
+ /*
+ ** File I/O.
+ */
+ static void Read_INI(CCINIClass & ini);
+ static char * INI_Name(void) {return "AIRCRAFT";};
+ bool Load(Straw & file);
+ bool Save(Pipe & file) const;
+
+ public:
+
+ /*
+ ** This is the facing used for the body of the aircraft. Typically, this is the same
+ ** as the PrimaryFacing, but in the case of helicopters, it can be different.
+ */
+ FacingClass SecondaryFacing;
+
+ /*
+ ** If this is a passenger carrying aircraft then this flag will be set. This is
+ ** necessary because once the passengers are unloaded, the fact that it was a
+ ** passenger carrier must still be known.
+ */
+ bool Passenger;
+
+ private:
+
+ /*
+ ** Aircraft can be in either state of landing, taking off, or in steady altitude.
+ ** These flags are used to control transition between flying and landing. It is
+ ** necessary to handle the transition in this manner so that it occurs smoothly
+ ** during the graphic processing section.
+ */
+ unsigned IsLanding:1;
+ unsigned IsTakingOff:1;
+
+ /*
+ ** It is very common for aircraft to be homing in on a target. When this flag is
+ ** true, the aircraft will constantly adjust its facing toward the TarCom. When the
+ ** target is very close (one cell away or less), then this flag is automatically cleared.
+ ** This is because the homing algorithm is designed to get the aircraft to the destination
+ ** but no more. Checking when this flag is cleared is a way of flagging transition into
+ ** a new mode. Example: Transport helicopters go into a hovering into correct position
+ ** mode when the target is reached.
+ */
+ unsigned IsHoming:1;
+
+ /*
+ ** Helicopters that are about to land must hover into a position exactly above the landing
+ ** zone. When this flag is true, the aircraft will be adjusted so that it is exactly over
+ ** the TarCom. The facing of the aircraft is not altered by this movement. The affect
+ ** like the helicopter is hovering and shifting sideways to position over the landing
+ ** zone. When the position is over the landing zone, then this flag is set to false.
+ */
+ unsigned IsHovering:1;
+
+ /*
+ ** This is the jitter tracker to be used when the aircraft is a helicopter and
+ ** is flying. It is most noticeable when the helicopter is hovering.
+ */
+ unsigned char Jitter;
+
+ private:
+
+ /*
+ ** This timer controls when the aircraft will reveal the terrain around itself.
+ ** When this timer expires and this aircraft has a sight range, then the
+ ** look around process will occur.
+ */
+ CDTimerClass SightTimer;
+
+ /*
+ ** Most attack aircraft can make several attack runs. This value contains the
+ ** number of attack runs the aircraft has left. When this value reaches
+ ** zero then the aircraft is technically out of ammo.
+ */
+ char AttacksRemaining;
+};
+
+#endif
diff --git a/CODE/ALLOC.CPP b/CODE/ALLOC.CPP
new file mode 100644
index 0000000..665bebe
--- /dev/null
+++ b/CODE/ALLOC.CPP
@@ -0,0 +1,590 @@
+/*
+** Command & Conquer Red Alert(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 A S S O C I A T E S **
+ ***************************************************************************
+ * *
+ * Project Name : Westwood Library *
+ * *
+ * File Name : ALLOC.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : February 1, 1992 *
+ * *
+ * Last Update : March 9, 1995 [JLB] *
+ * *
+ *-------------------------------------------------------------------------*
+ * Functions: *
+ * Alloc -- Allocates system RAM. *
+ * Ram_Free -- Determines the largest free chunk of RAM. *
+ * Free -- Free an Alloc'ed block of RAM. *
+ * Resize_Alloc -- Change the size of an allocated block. *
+ * Heap_Size -- Size of the heap we have. *
+ * Total_Ram_Free -- Total amount of free RAM. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+#ifndef WWMEM_H
+#include "wwmem.h"
+#endif
+
+
+extern "C" unsigned long Largest_Mem_Block ( void ) ;
+
+//
+// use double-word alignment for allocs
+//
+#define LONG_ALIGNMENT 1
+
+/*
+** Define the equates necessary to call a DPMI interrupt.
+*/
+#define DPMI_INT 0x0031
+#define DPMI_LOCK_MEM 0x0600
+#define DPMI_UNLOCK_MEM 0x0601
+#define LOGGING FALSE
+/*=========================================================================*/
+/* The following PRIVATE functions are in this file: */
+/*=========================================================================*/
+
+
+/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
+
+unsigned long MinRam=0L; // Record of least memory at worst case.
+unsigned long MaxRam=0L; // Record of total allocated at worst case.
+static unsigned long TotalRam = 0L;
+static unsigned long Memory_Calls = 0L;
+static unsigned long RequestedSystemRam = 8*1024*1024;
+static unsigned long LargestRamBlock = 0L;
+
+void (*Memory_Error)(void) = NULL;
+void (*Memory_Error_Exit)(char *string) = NULL;
+
+/***************************************************************************
+ * DPMI_LOCK -- handles locking a block of DPMI memory *
+ * *
+ * INPUT: *
+ * *
+ * OUTPUT: *
+ * *
+ * WARNINGS: *
+ * *
+ * HISTORY: *
+ * 06/23/1995 PWG : Created. *
+ *=========================================================================*/
+#include"mono.h"
+void DPMI_Lock(VOID const *ptr, long const size)
+{
+ union REGS regs;
+ struct SREGS sregs;
+
+ /*
+ ** Lock memory
+ ** AX = 0x600
+ ** BX:CX = starting linear address of memory to lock
+ ** SI:DI = size of region to lock (in bytes)
+ ** - If Failure, carry flag is set.
+ */
+ memset (®s, 0 ,sizeof(regs));
+ segread (&sregs);
+ regs.x.eax = DPMI_LOCK_MEM;
+ regs.x.ebx = ((long)ptr & 0xffff0000) >> 16;
+ regs.x.ecx = ((long)ptr & 0x0000ffff);
+ regs.x.esi = ((long)size & 0xffff0000) >> 16;
+ regs.x.edi = ((long)size & 0x0000ffff);
+ int386x (DPMI_INT, ®s, ®s, &sregs); // call DPMI
+// if (regs.x.cflag) {
+// }
+#if(0)
+ char *temp = (char *)ptr;
+ char hold;
+ for (int lp = 0; lp < size; lp += 2048) {
+ hold = *temp;
+ temp += 2048;
+ }
+#endif
+
+}
+
+/***************************************************************************
+ * DPMI_UNLOCK -- Handles unlocking a locked block of DPMI *
+ * *
+ * INPUT: *
+ * *
+ * OUTPUT: *
+ * *
+ * WARNINGS: *
+ * *
+ * HISTORY: *
+ * 06/23/1995 PWG : Created. *
+ *=========================================================================*/
+void DPMI_Unlock(void const *ptr, long const size)
+{
+ union REGS regs;
+ struct SREGS sregs;
+
+ /*
+ ** Unlock the memory
+ */
+ memset (®s, 0 ,sizeof(regs));
+ segread (&sregs);
+ regs.x.eax = DPMI_UNLOCK_MEM; // DPMI function to call
+ regs.x.ebx = ((long)ptr & 0xffff0000) >> 16;
+ regs.x.ecx = ((long)ptr & 0x0000ffff);
+ regs.x.esi = ((long)size & 0xffff0000) >> 16;
+ regs.x.edi = ((long)size & 0x0000ffff);
+ int386x (DPMI_INT, ®s, ®s, &sregs); // call DPMI
+// if (regs.x.cflag) {
+// }
+
+}
+
+/***************************************************************************
+ * Alloc -- Allocates system RAM. *
+ * *
+ * This is the basic RAM allocation function. It is used for all *
+ * memory allocations needed by the system or the main program. *
+ * *
+ * INPUT: bytes_to_alloc -- LONG value of the number of bytes to alloc. *
+ * *
+ * flags -- Memory allocation control flags. *
+ * MEM_NORMAL: No special flags. *
+ * MEM_CLEAR: Zero out memory block. *
+ * MEM_NEW: Called by a new. *
+ * *
+ * OUTPUT: Returns with pointer to allocated block. If NULL was returned *
+ * it indicates a failure to allocate. Note: NULL will never be *
+ * returned if the standard library allocation error routine is *
+ * used. *
+ * *
+ * WARNINGS: If you replace the standard memory allocation error routine *
+ * and make it so that Alloc CAN return with a NULL, be sure *
+ * and check for this in your code. *
+ * *
+ * HISTORY: *
+ * 09/03/1991 JLB : Documented. *
+ * 08/09/1993 JLB : Updated with EMS memory support. *
+ * 04/28/1994 JAW : Updated to 32bit Protected mode. *
+ * 03/09/1995 JLB : Fixed *
+ *=========================================================================*/
+void *Alloc(unsigned long bytes_to_alloc, MemoryFlagType flags)
+{
+ union REGS regs ;
+ struct SREGS sregs ;
+ unsigned char *retval=NULL; // Pointer to allocated block.
+ unsigned long original_size; // Original allocation size.
+ unsigned long bytesfree; // Number of free bytes.
+ long *longptr=NULL; // Pointer used to store selector
+ static unsigned char _allocinit=0;
+
+
+ //
+ // Init memory system by finding largest block to alloc
+ // then allocate it to get one large heap and free it.
+ // There may be more memory available from DPMI but we only are
+ // for now allocating and freeing the first largest block.
+ //
+ if ( !_allocinit ) {
+ unsigned long largestblock = Largest_Mem_Block();
+ largestblock -= 1024; // subtract for heap header and misc
+ largestblock &= 0xffff0000; // forcing to 64K boundary
+
+ if ( largestblock ) {
+ LargestRamBlock = MIN( largestblock, RequestedSystemRam );
+ unsigned char *lptr = (unsigned char *)malloc( LargestRamBlock );
+ if ( lptr ) {
+ free( (void *)lptr );
+ }
+ }
+
+ /*
+ ** Initialize the total ram available value.
+ */
+ TotalRam = Total_Ram_Free(MEM_NORMAL);
+
+ _allocinit = 1;
+ }
+
+ /*
+ ** Save the original allocated space size so that we can clear the
+ ** exact amount of RAM if they specified MEM_CLEAR.
+ */
+ original_size = bytes_to_alloc;
+
+ /*
+ ** Reserve one byte for the header of the memory we allocated.
+ ** We will store the flags variable there for later use.
+ */
+#if (LONG_ALIGNMENT)
+ bytes_to_alloc += (flags & MEM_LOCK) ? 8 : 4;
+#else
+ bytes_to_alloc += (flags & MEM_LOCK) ? 5 : 1;
+#endif
+
+
+ // Try to allocate the memory out of the protected mode memory
+ // chain if we did not require a real mode allocation. If this
+ // fails we will have to try to allocate it out of real mode memory.
+ // Real mode memory is a last resort because some types of applications
+ // require real mode memory.
+ if (!(flags & MEM_REAL)) {
+ retval = (unsigned char*)malloc(bytes_to_alloc);
+ }
+
+ // Try to allocate the memory out of the real mode memory using DPMI
+ // service 0x100. Note that retval will be null if we are requesting
+ // real mode memory so that we do not have to explicitly check for the
+ // real mode flag. Remember we need to reserve room for the dos
+ // selector value at the beginning of our allocated block so rather than
+ // adding fifteen and rounding, we need to add 19 and round.
+ if (!retval) {
+ flags = (MemoryFlagType)(flags | MEM_REAL);
+ regs.x.eax = 0x100;
+ regs.x.ebx = (bytes_to_alloc + 19) >> 4;
+ if (regs.x.ebx & 0xFFFF0000) {
+ retval = NULL;
+ } else {
+ segread ( & sregs ) ;
+ int386x ( 0x31 , & regs, & regs , & sregs ) ;
+ if (regs.x.cflag)
+ retval = NULL;
+ else {
+#if (LONG_ALIGNMENT)
+ longptr = (long *)(((regs.x.eax & 0xFFFF) << 4)+ 4);
+#else
+ longptr = (long *)(((regs.x.eax & 0xFFFF) << 4)+ 1);
+#endif
+ *longptr++ = regs.x.edx & 0xFFFF;
+ retval = (unsigned char *)longptr;
+ }
+ }
+ }
+
+ // If the alloc failed then we need to signify a memory error.
+ if (retval == NULL) {
+ if (Memory_Error != NULL)
+ Memory_Error();
+ return NULL;
+ }
+
+ // If the memory needs to be DPMI locked then we should store the
+ // original size in the header before we store the flags.
+ if (flags & MEM_LOCK) {
+ longptr = (long *)retval;
+ *longptr++ = original_size;
+ retval = (unsigned char *)longptr;
+ }
+
+
+ // Now that we know the alloc was sucessful (and for an extra byte
+ // more than the user wanted) we need to stick in the memory flags.
+#if (LONG_ALIGNMENT)
+ if ( !(flags & (MEM_LOCK|MEM_REAL)) ) {
+ //
+ // WARNING!!!!!!!!!!
+ // USE this only with the WATCOM malloc ALLOCATION!!!!!!!!!
+ // it reads the actual block size before the ptr returned.
+ // then eors and uses the upper word for a validation later on free.
+ //
+ longptr = (long *)retval;
+ *longptr = ((*(longptr - 1)) ^ 0xffffffff) & 0xffff0000;
+ *retval++ = flags;
+ *retval++ = (unsigned char)(flags ^ 0xff);
+ retval += 2;
+ }
+ else {
+ *retval++ = flags;
+ *retval++ = (unsigned char)(flags ^ 0xff);
+ *retval++ = 0;
+ *retval++ = 0;
+ }
+#else
+ *retval++ = (unsigned char)(flags | (((flags ^ 0x07) & 0x07) << 5));
+#endif
+
+ // If the memory needed to be DPMI locked then set it up so it
+ // is locked.
+ if (flags & MEM_LOCK) {
+ DPMI_Lock(retval, original_size);
+ }
+
+ /* Clear the space if they wanted it clear */
+
+ if (flags & MEM_CLEAR) {
+ unsigned char *ptr; // Working memory block pointer.
+
+ ptr = retval;
+ memset(ptr, '\0', original_size);
+ }
+
+ bytesfree = Total_Ram_Free(MEM_NORMAL);
+ if (bytesfree < MinRam) {
+ MinRam = bytesfree;
+ }
+ if (TotalRam-bytesfree > MaxRam) {
+ MaxRam = TotalRam-bytesfree;
+ }
+
+ Memory_Calls++;
+
+#if(LOGGING)
+ int val = _heapchk();
+
+ FILE *file = fopen("mem.txt","at");
+ fprintf(file, "%P Alloc size = %d, Actual Size = %d, flags = %d, heap = %d\n",
+ retval,
+ original_size,
+ bytes_to_alloc,
+ flags,
+ val);
+ fclose(file);
+#endif
+
+ return(retval);
+}
+
+
+/***************************************************************************
+ * Free -- Free an Alloc'ed block of RAM. *
+ * *
+ * FUNCTION: *
+ * *
+ * INPUT: A pointer to a block of RAM from Alloc. *
+ * *
+ * OUTPUT: None. *
+ * *
+ * WARNINGS: Don't use this for an Alloc_Block'ed RAM block. *
+ * *
+ * HISTORY: *
+ * 05/25/1990 : Created. *
+ ***************************************************************************/
+void Free(void const *pointer)
+{
+ union REGS regs ;
+ struct SREGS sregs ;
+
+// void const *original = pointer;
+ char string[80];
+
+ if (pointer) {
+ /*
+ ** Get a pointer to the flags that we stored off.
+ */
+#if (LONG_ALIGNMENT)
+ unsigned char *byteptr = ((unsigned char *)pointer) - 4;
+
+ //
+ // validate the flags with and eor of the flags
+ //
+ if ( *byteptr != ((*(byteptr + 1)) ^ 0xff) ) {
+ if (Memory_Error_Exit != NULL) {
+ sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer );
+ Memory_Error_Exit( string );
+ }
+ }
+ else {
+ if ( !(*byteptr & (MEM_LOCK|MEM_REAL)) ) {
+ unsigned short *wordptr = (unsigned short *)(byteptr - 2);
+
+ //
+ // WARNING!!!!!!!!!!
+ // USE this only with the WATCOM malloc ALLOCATION!!!!!!!!!
+ // it reads the actual block size before the ptr to be freed.
+ // then compares with the EOR to the value stored during allocation.
+ //
+ if ( *wordptr != ((*(wordptr + 2)) ^ 0xffff) ) {
+ if (Memory_Error_Exit != NULL) {
+ sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer );
+ Memory_Error_Exit( string );
+ }
+ }
+ }
+ else if ( *(byteptr + 2) || *(byteptr + 3) ) {
+ if (Memory_Error_Exit != NULL) {
+ sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer );
+ Memory_Error_Exit( string );
+ }
+ }
+ }
+// if ( *byteptr != (*(byteptr + 1) ^ 0xff) ||
+// *(byteptr + 2) || *(byteptr + 3) ) {
+// if (Memory_Error_Exit != NULL) {
+// sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer );
+// Memory_Error_Exit( string );
+// }
+// }
+#else
+ unsigned char *byteptr = ((unsigned char *)pointer) - 1;
+
+ if ( (*byteptr & 0xe0) != (((*byteptr ^ 0x07) & 0x07) << 5) ) {
+ if (Memory_Error_Exit != NULL) {
+ sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer );
+ Memory_Error_Exit( string );
+ }
+ }
+#endif
+
+ /*
+ ** Check to see if this was locked me and if it was unlock it.
+ */
+ if (*byteptr & MEM_LOCK) {
+ long *longptr = ((long *)byteptr) - 1;
+ DPMI_Unlock(pointer, *longptr);
+ pointer = (void *)longptr;
+ } else
+ pointer = (void *)byteptr;
+
+#if(LOGGING)
+ int val = _heapchk();
+ FILE *file = fopen("mem.txt","at");
+ fprintf(file, "%P Free flags = %d, Heap = %d\n",
+ original,
+ *byteptr,
+ val);
+ fclose(file);
+#endif
+
+ // If the pointer is a real mode pointer than it will point to the
+ // first megabyte of system memory. If it does than we need to
+ // use DPMI to free it.
+ if (*byteptr & MEM_REAL) {
+ regs.x.eax = 0x101;
+ regs.x.edx = *(((long *)pointer) - 1);
+ segread ( & sregs ) ;
+ int386x(0x31, ®s, ®s, &sregs);
+ } else {
+ free((void *)pointer);
+ }
+ Memory_Calls--;
+ }
+}
+
+
+/***************************************************************************
+ * Resize_Alloc -- Change the size of an allocated block. *
+ * *
+ * This routine will take a previously allocated block and change its *
+ * size without unnecessarily altering its contents. *
+ * *
+ * INPUT: pointer -- Pointer to the original memory allocation. *
+ * *
+ * new_size -- Size in bytes that it will be converted to. *
+ * *
+ * OUTPUT: Returns with a pointer to the new allocation. *
+ * *
+ * WARNINGS: ??? *
+ * *
+ * HISTORY: *
+ * 02/01/1992 JLB : Commented. *
+ *=========================================================================*/
+void *Resize_Alloc(void *original_ptr, unsigned long new_size_in_bytes)
+{
+
+ unsigned long *temp;
+// unsigned long diff, flags;
+
+ temp = (unsigned long*)original_ptr;
+
+ /* ReAlloc the space */
+ temp = (unsigned long *)realloc(temp, new_size_in_bytes);
+ if (temp == NULL) {
+ if (Memory_Error != NULL)
+ Memory_Error();
+ return NULL;
+ }
+
+ return(temp);
+}
+
+
+/***************************************************************************
+ * Ram_Free -- Determines the largest free chunk of RAM. *
+ * *
+ * Use this routine to determine the largest free chunk of available *
+ * RAM for allocation. It also performs a check of the memory chain. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the size of the largest free chunk of RAM. *
+ * *
+ * WARNINGS: This does not return the TOTAL memory free, only the *
+ * largest free chunk. *
+ * *
+ * HISTORY: *
+ * 09/03/1991 JLB : Commented. *
+ *=========================================================================*/
+long Ram_Free(MemoryFlagType)
+{
+ return(_memmax());
+// return Largest_Mem_Block();
+}
+
+
+/***************************************************************************
+ * Heap_Size -- Size of the heap we have. *
+ * *
+ * *
+ * *
+ * INPUT: *
+ * *
+ * OUTPUT: *
+ * *
+ * WARNINGS: *
+ * *
+ * HISTORY: *
+ * 06/21/1994 SKB : Created. *
+ *=========================================================================*/
+long Heap_Size(MemoryFlagType )
+{
+ if (!TotalRam) {
+ TotalRam = Total_Ram_Free(MEM_NORMAL);
+ }
+ return(TotalRam);
+}
+
+
+/***************************************************************************
+ * Total_Ram_Free -- Total amount of free RAM. *
+ * *
+ * *
+ * *
+ * INPUT: *
+ * *
+ * OUTPUT: *
+ * *
+ * WARNINGS: *
+ * *
+ * HISTORY: *
+ * 06/21/1994 SKB : Created. *
+ * 03/09/1995 JLB : Uses prerecorded heap size maximum. *
+ *=========================================================================*/
+long Total_Ram_Free(MemoryFlagType )
+{
+ return(_memavl());
+// return Largest_Mem_Block () ;
+}
+
diff --git a/CODE/ANIM.CPP b/CODE/ANIM.CPP
new file mode 100644
index 0000000..fba36b1
--- /dev/null
+++ b/CODE/ANIM.CPP
@@ -0,0 +1,1108 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/ANIM.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Dune *
+ * *
+ * File Name : ANIM.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : June 3, 1991 *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * AnimClass::AI -- This is the low level anim processor. *
+ * AnimClass::AnimClass -- The constructor for animation objects. *
+ * AnimClass::Attach_To -- Attaches animation to object specified. *
+ * AnimClass::Center_Coord -- Determine center of animation. *
+ * AnimClass::Detach -- Remove animation if attached to target. *
+ * AnimClass::Do_Atom_Damage -- Do atom bomb damage centered around the cell specified. *
+ * AnimClass::Draw_It -- Draws the animation at the location specified. *
+ * AnimClass::In_Which_Layer -- Determines what render layer the anim should be in. *
+ * AnimClass::Init -- Performs pre-scenario initialization. *
+ * AnimClass::Mark -- Signals to map that redrawing is necessary. *
+ * AnimClass::Middle -- Processes any middle events. *
+ * AnimClass::Occupy_List -- Determines the occupy list for the animation. *
+ * AnimClass::Overlap_List -- Determines the overlap list for the animation. *
+ * AnimClass::Render -- Draws an animation object. *
+ * AnimClass::Sort_Y -- Returns with the sorting coordinate for the animation. *
+ * AnimClass::Start -- Processes initial animation side effects. *
+ * AnimClass::delete -- Returns an anim object back to the free pool. *
+ * AnimClass::new -- Allocates an anim object from the pool. *
+ * AnimClass::~AnimClass -- Destructor for anim objects. *
+ * Anim_From_Name -- Given a name, this finds the corresponding anim type. *
+ * Shorten_Attached_Anims -- Reduces attached animation durations. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+#define VIC 1
+
+/***********************************************************************************************
+ * Anim_From_Name -- Given a name, this finds the corresponding anim type. *
+ * *
+ * This routine will convert the supplied ASCII name into the animation type that it *
+ * represents. *
+ * *
+ * INPUT: name -- Pointer to the ASCII name to convert. *
+ * *
+ * OUTPUT: Returns with the animation type that matches the name specified. If no match could *
+ * be found, then ANIM_NONE is returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/06/1996 JLB : Created. *
+ *=============================================================================================*/
+AnimType Anim_From_Name(char const * name)
+{
+ #ifdef VIC
+ if (name == NULL) return(ANIM_NONE);
+
+ for (AnimType anim = ANIM_FIRST; anim < ANIM_COUNT; anim++) {
+ if (stricmp(AnimTypeClass::As_Reference(anim).IniName, name) == 0) {
+ return(anim);
+ }
+ }
+#endif
+ return(ANIM_NONE);
+}
+
+
+/***********************************************************************************************
+ * Shorten_Attached_Anims -- Reduces attached animation durations. *
+ * *
+ * This routine is used to reduce the amount of time any attached animations will process. *
+ * Typical use of this is when an object is on fire and the object should now be destroyed *
+ * but the attached animations are to run until completion before destruction can follow. *
+ * This routine will make the animation appear to run its course, but in as short of time *
+ * as possible. The shortening effect is achieved by reducing the number of times the *
+ * animation will loop. *
+ * *
+ * INPUT: obj -- Pointer to the object that all attached animations will be processed. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 12/11/1994 JLB : Created. *
+ *=============================================================================================*/
+void Shorten_Attached_Anims(ObjectClass * obj)
+{
+ if (obj != NULL) {
+ for (int index = 0; index < Anims.Count(); index++) {
+ AnimClass & anim = * Anims.Ptr(index);
+
+ if (As_Object(anim.xObject) == obj) {
+ anim.Loops = 0;
+ }
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * AnimClass::Sort_Y -- Returns with the sorting coordinate for the animation. *
+ * *
+ * This routine is used by the sorting system. Animations that are located in the ground *
+ * layer will be sorted by this the value returned from this function. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the sort coordinate to use for this animation. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 10/17/1994 JLB : Created. *
+ * 12/15/1994 JLB : Handles flat anims (infantry decay anims). *
+ *=============================================================================================*/
+COORDINATE AnimClass::Sort_Y(void) const
+{
+#ifdef VIC
+ assert(Anims.ID(this) == ID);
+ assert(IsActive);
+
+ if (xObject != TARGET_NONE) {
+ return(Coord_Add(As_Object(xObject)->Sort_Y(), 0x00010000L));
+ }
+ if (*this == ANIM_MOVE_FLASH) {
+ return(Coord_Add(Center_Coord(), XYP_COORD(0, -24)));
+ }
+ if (Class->IsGroundLayer || *this == ANIM_LZ_SMOKE) {
+ return(Coord_Add(Center_Coord(), XYP_COORD(0, 14)));
+ }
+#endif
+ return(Coord);
+}
+
+
+/***********************************************************************************************
+ * AnimClass::Center_Coord -- Determine center of animation. *
+ * *
+ * This support function will return the "center" of the animation. The actual coordinate *
+ * of the animation may be dependant on if the the animation is attached to an object. *
+ * In such a case, it must factor in the object's location. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns the coordinate of the center of the animation. The coordinate is in real *
+ * game coordinates -- taking into consideration if the animation is attached. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 09/19/1994 JLB : Created. *
+ * 02/02/1996 JLB : Coordinate based on visual center of object. *
+ *=============================================================================================*/
+COORDINATE AnimClass::Center_Coord(void) const
+{
+#ifdef VIC
+ assert(Anims.ID(this) == ID);
+ assert(IsActive);
+
+ if (xObject != TARGET_NONE) {
+ return(Coord_Add(Coord, As_Object(xObject)->Target_Coord()));
+ }
+#endif
+ return(Coord);
+}
+
+
+/***********************************************************************************************
+ * AnimClass::Render -- Draws an animation object. *
+ * *
+ * This is the working routine that renders the animation shape. It gets called once *
+ * per animation per frame. It needs to be fast. *
+ * *
+ * INPUT: bool; Should the animation be rendered in spite of render flag? *
+ * *
+ * OUTPUT: bool; Was the animation rendered? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/31/1994 JLB : Created. *
+ *=============================================================================================*/
+bool AnimClass::Render(bool forced) const
+{
+#ifdef VIC
+ assert(Anims.ID(this) == ID);
+ assert(IsActive);
+
+ if (Delay) return(false);
+ if (Map[Center_Coord()].IsVisible) {
+ IsToDisplay = true;
+ }
+#endif
+ return(ObjectClass::Render(forced));
+}
+
+
+/***********************************************************************************************
+ * AnimClass::Draw_It -- Draws the animation at the location specified. *
+ * *
+ * This routine is used to render the animation object at the location specified. This is *
+ * how the map imagery gets updated. *
+ * *
+ * INPUT: x,y -- The pixel coordinates to draw the animation at. *
+ * *
+ * window -- The to base the draw coordinates upon. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 09/24/1994 JLB : Created. *
+ * 05/19/1995 JLB : Added white translucent effect. *
+ *=============================================================================================*/
+void AnimClass::Draw_It(int x, int y, WindowNumberType window) const
+{
+#ifdef VIC
+ assert(Anims.ID(this) == ID);
+ assert(IsActive);
+
+ if (!IsInvisible) {
+ BStart(BENCH_ANIMS);
+
+ IsTheaterShape = Class->IsTheater;
+
+ void const * shapefile = Get_Image_Data();
+ if (shapefile != NULL) {
+ void const * transtable = NULL;
+ int shapenum = Class->Start + Fetch_Stage();
+ void const * remap = NULL;
+
+ /*
+ ** If the translucent table hasn't been determined yet, then check to see if it
+ ** should use the white or normal translucent tables.
+ */
+ if (transtable == NULL && Class->IsWhiteTrans) transtable = DisplayClass::WhiteTranslucentTable;
+ if (transtable == NULL && Class->IsTranslucent) transtable = DisplayClass::TranslucentTable;
+ if (Class->Type == ANIM_ATOM_BLAST) transtable = Map.UnitShadow;
+
+ /*
+ ** Set the shape flags to properly take into account any fading or ghosting
+ ** table necessary.
+ */
+ ShapeFlags_Type flags = SHAPE_CENTER|SHAPE_WIN_REL;
+ if (transtable != NULL) flags = flags | SHAPE_GHOST;
+
+ /*
+ ** Draw the animation shape.
+ */
+ CC_Draw_Shape(shapefile, shapenum, x, y, window, flags, remap, transtable);
+ }
+ IsTheaterShape = false;
+ BEnd(BENCH_ANIMS);
+ }
+#endif
+}
+
+
+/***********************************************************************************************
+ * AnimClass::Mark -- Signals to map that redrawing is necessary. *
+ * *
+ * This routine is used by the animation logic system to inform the map that the cells *
+ * under the animation must be rerendered. *
+ * *
+ * INPUT: *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/31/1994 JLB : Created. *
+ *=============================================================================================*/
+bool AnimClass::Mark(MarkType mark)
+{
+#ifdef VIC
+ assert(Anims.ID(this) == ID);
+ assert(IsActive);
+
+ if (ObjectClass::Mark(mark)) {
+ Map.Refresh_Cells(Coord_Cell(Center_Coord()), Overlap_List());
+// ObjectClass::Mark(mark);
+ return(true);
+ }
+#endif
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * AnimClass::Overlap_List -- Determines the overlap list for the animation. *
+ * *
+ * Use this routine to fetch the overlap list for the animation. This overlap list is the *
+ * cells that this animation spills over. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns a pointer to the overlap list for this particular instance of the *
+ * animation. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 03/19/1995 JLB : Created. *
+ *=============================================================================================*/
+short const * AnimClass::Overlap_List(void) const
+{
+#ifdef VIC
+ assert(Anims.ID(this) == ID);
+ assert(IsActive);
+ static short const OverlapAtom[] = {
+ (-MAP_CELL_W * 2) - 1, (-MAP_CELL_W * 2), (-MAP_CELL_W * 2) + 1,
+ (-MAP_CELL_W * 1) - 1, (-MAP_CELL_W * 1), (-MAP_CELL_W * 1) + 1,
+ (-MAP_CELL_W * 0) - 1, (-MAP_CELL_W * 0), (-MAP_CELL_W * 0) + 1,
+ ( MAP_CELL_W * 1) - 1, ( MAP_CELL_W * 1), ( MAP_CELL_W * 1) + 1,
+ ( MAP_CELL_W * 2) - 1, ( MAP_CELL_W * 2), ( MAP_CELL_W * 2) + 1,
+ REFRESH_EOL
+ };
+
+ if (IsToDelete) {
+ static short const _list[] = {REFRESH_EOL};
+ return(_list);
+ }
+
+ if (Class->Type == ANIM_ATOM_BLAST) {
+ return(OverlapAtom);
+ }
+
+#ifdef PARTIAL
+IsTheaterShape = Class->IsTheater;
+ if (Class->Get_Image_Data() != NULL) {
+ int shapenum = Class->Start + Fetch_Stage();
+ int count = Get_Build_Frame_Count(Class->Get_Image_Data());
+ shapenum = min(shapenum, count-1);
+
+ if (Class->DimensionData == NULL) {
+ Class->DimensionData = new Rect [count];
+ }
+ if (Class->DimensionData != NULL && !Class->DimensionData[shapenum].Is_Valid()) {
+ Class->DimensionData[shapenum] = Shape_Dimensions(Class->Get_Image_Data(), shapenum);
+IsTheaterShape = false;
+ return(Coord_Spillage_List(Center_Coord(), Class->DimensionData[shapenum]));
+ }
+ }
+IsTheaterShape = false;
+#endif
+#endif
+ return(Coord_Spillage_List(Center_Coord(), Class->Size));
+}
+
+
+/***********************************************************************************************
+ * AnimClass::Occupy_List -- Determines the occupy list for the animation. *
+ * *
+ * Animations always occupy only the cell that their center is located over. As such, this *
+ * routine always returns a simple (center cell) occupation list. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the occupation list for the animation. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 03/19/1995 JLB : Created. *
+ *=============================================================================================*/
+short const * AnimClass::Occupy_List(bool) const
+{
+#ifdef VIC
+ assert(Anims.ID(this) == ID);
+ assert(IsActive);
+
+ static short _simple[] = {REFRESH_EOL};
+
+#endif
+ return(_simple);
+}
+
+
+/***********************************************************************************************
+ * AnimClass::Init -- Performs pre-scenario initialization. *
+ * *
+ * This routine is used to initialize the animation system prior to a scenario being loaded *
+ * or reloaded. It effectively removes all animations from the system. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/31/1994 JLB : Created. *
+ *=============================================================================================*/
+void AnimClass::Init(void)
+{
+ Anims.Free_All();
+}
+
+
+/***********************************************************************************************
+ * AnimClass::new -- Allocates an anim object from the pool. *
+ * *
+ * This routine is used to allocate a free anim class object from the preallocated pool *
+ * in the near heap. If there are no free animation objects, then null is returned. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with a pointer to a free anim object. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/31/1994 JLB : Created. *
+ *=============================================================================================*/
+void * AnimClass::operator new(size_t)
+{
+ void * ptr = Anims.Allocate();
+ if (ptr != NULL) {
+ ((AnimClass *)ptr)->IsActive = true;
+ }
+ return(ptr);
+}
+
+
+/***********************************************************************************************
+ * AnimClass::delete -- Returns an anim object back to the free pool. *
+ * *
+ * This routine is used to return an anim object back to the pool of free anim objects. *
+ * Anim objects so returned are available to be reallocated for the next animation. *
+ * *
+ * INPUT: ptr -- Pointer to the anim object to return to the pool. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/31/1994 JLB : Created. *
+ *=============================================================================================*/
+void AnimClass::operator delete(void * ptr)
+{
+ if (ptr != NULL) {
+ ((AnimClass *)ptr)->IsActive = false;
+ }
+ Anims.Free((AnimClass *)ptr);
+}
+
+
+/***********************************************************************************************
+ * AnimClass::AnimClass -- The constructor for animation objects. *
+ * *
+ * This routine is used as the constructor of animation objects. It initializes and adds *
+ * the animation object to the display and logic systems. *
+ * *
+ * INPUT: animnum -- The animation number to start. *
+ * *
+ * coord -- The location of the animation. *
+ * *
+ * timedelay-- The delay before the animation starts. *
+ * *
+ * loop -- The number of times to loop this animation. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/31/1994 JLB : Created. *
+ * 08/03/1994 JLB : Added a delayed affect parameter. *
+ *=============================================================================================*/
+AnimClass::AnimClass(AnimType animnum, COORDINATE coord, unsigned char timedelay, unsigned char loop) :
+ ObjectClass(RTTI_ANIM, Anims.ID(this)),
+ Class(AnimTypes.Ptr((int)animnum)),
+ xObject(TARGET_NONE),
+ OwnerHouse(HOUSE_NONE),
+ Loops(1),
+ IsToDelete(false),
+ IsBrandNew(true),
+ IsInvisible(false),
+ Delay(timedelay),
+ Accum(0)
+{
+#ifdef VIC
+ if (Class->Stages == -1) {
+IsTheaterShape = Class->IsTheater;
+ ((int&)Class->Stages) = Get_Build_Frame_Count(Class->Get_Image_Data());
+IsTheaterShape = false;
+
+ }
+ if (Class->LoopEnd == -1) {
+ ((int&)Class->LoopEnd) = Class->Stages;
+ }
+ if (Class->IsNormalized) {
+ Set_Rate(Options.Normalize_Delay(Class->Delay));
+ } else {
+ Set_Rate(Class->Delay);
+ }
+ Set_Stage(0);
+
+ if (Class->IsGroundLayer) {
+ Height = FLIGHT_LEVEL;
+ }
+
+ AnimClass::Unlimbo(coord);
+
+ /*
+ ** Drop zone smoke always reveals the map around itself.
+ */
+ if (*this == ANIM_LZ_SMOKE) {
+ Map.Sight_From(Coord_Cell(coord), Rule.DropZoneRadius / CELL_LEPTON_W, PlayerPtr, false);
+ }
+
+ Loops = (unsigned char)(max(loop, 1) * Class->Loops);
+ Loops = (unsigned char)max(Loops, 1);
+
+ /*
+ ** If the animation starts immediately, then play the associated sound effect now.
+ */
+ if (!Delay) {
+ Start();
+ }
+#endif
+}
+
+
+/***********************************************************************************************
+ * AnimClass::~AnimClass -- Destructor for anim objects. *
+ * *
+ * This destructor handles removing the animation object from the system. It might require *
+ * informing any object this animation is attached to that it is no longer attached. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 11/29/1994 JLB : Created. *
+ *=============================================================================================*/
+AnimClass::~AnimClass(void)
+{
+#ifdef VIC
+ assert(Anims.ID(this) == ID);
+ assert(IsActive);
+ if (GameActive) {
+
+ /*
+ ** If this anim is attached to another object
+ ** then check to see if this is the last anim attached to it. If this
+ ** is the case, then inform the object that it is no longer attached to
+ ** an animation.
+ */
+ if (Target_Legal(xObject) && As_Object(xObject) != NULL) {
+ ObjectClass * to = As_Object(xObject);
+
+ /*
+ ** Remove the object from the appropriate display list.
+ */
+ Map.Remove(this, In_Which_Layer());
+
+ /*
+ ** Scan for any other animations that are attached to the object that
+ ** this animation is attached to. If there are no others, then inform the
+ ** attached object of this fact.
+ */
+ for (int index = 0; index < Anims.Count(); index++) {
+ if (Anims.Ptr(index) != this && Anims.Ptr(index)->xObject == xObject) break;
+ }
+
+ /*
+ ** Tell the object that it is no longer being damaged.
+ */
+ if (index == Anims.Count()) {
+ to->Fire_Out();
+ to->Mark(MARK_OVERLAP_UP);
+ to->IsAnimAttached = false;
+ to->Mark(MARK_OVERLAP_DOWN);
+ }
+ Coord = Coord_Add(to->Center_Coord(), Coord);
+ xObject = TARGET_NONE;
+ }
+
+ Limbo();
+ }
+
+ xObject = TARGET_NONE;
+ Class = 0;
+ ID = -1;
+
+#endif
+}
+
+
+/***********************************************************************************************
+ * AnimClass::AI -- This is the low level anim processor. *
+ * *
+ * This routine is called once per frame per animation. It handles transition between *
+ * animation frames and marks the map for redraw as necessary. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: Speed is of upmost importance. *
+ * *
+ * HISTORY: *
+ * 05/31/1994 JLB : Created. *
+ *=============================================================================================*/
+void AnimClass::AI(void)
+{
+#ifdef VIC
+ assert(Anims.ID(this) == ID);
+ assert(IsActive);
+
+ /*
+ ** For ground level based animations (ones that can run slowly as well as
+ ** occur behind other ground objects) always cause the cell to be redrawn.
+ */
+#ifdef PARTIAL
+ if (!Delay && Class->IsGroundLayer) {
+ Map.Refresh_Cells(Coord_Cell(Center_Coord()), Overlap_List());
+ }
+#else
+ Map.Refresh_Cells(Coord_Cell(Center_Coord()), Overlap_List());
+#endif
+
+ /*
+ ** Special case check to make sure that building on top of a smoke marker
+ ** causes the smoke marker to vanish.
+ */
+ if (Class->Type == ANIM_LZ_SMOKE && Map[Center_Coord()].Cell_Building()) {
+ IsToDelete = true;
+ }
+
+ /*
+ ** Delete this animation and bail early if the animation is flagged to be deleted
+ ** immediately.
+ */
+ if (IsToDelete) {
+ delete this;
+ return;
+ }
+
+ /*
+ ** If this is a brand new animation, then don't process it the first logic pass
+ ** since it might end up skipping the first animation frame before it has had a
+ ** chance to draw it.
+ */
+ if (IsBrandNew) {
+ IsBrandNew = false;
+ return;
+ }
+
+#ifdef FIXIT_MULTI_SAVE
+ if (Class->Stages == -1) {
+ IsTheaterShape = Class->IsTheater;
+ ((int&)Class->Stages) = Get_Build_Frame_Count(Class->Get_Image_Data());
+ IsTheaterShape = false;
+ }
+ if (Class->LoopEnd == -1) {
+ ((int&)Class->LoopEnd) = Class->Stages;
+ }
+#endif
+
+ if (Delay) {
+ Delay--;
+ if (!Delay) {
+ Start();
+ }
+ } else {
+
+#ifdef FIXIT_MULTI_SAVE
+ if (Class->Stages == -1) {
+ IsTheaterShape = Class->IsTheater;
+ ((int&)Class->Stages) = Get_Build_Frame_Count(Class->Get_Image_Data());
+ IsTheaterShape = false;
+ }
+ if (Class->LoopEnd == -1) {
+ ((int&)Class->LoopEnd) = Class->Stages;
+ }
+#endif
+
+ /*
+ ** This is necessary because there is no recording of animations on the map
+ ** and thus the animation cannot be intelligently flagged for redraw. Most
+ ** animations move fast enough that they would need to be redrawn every
+ ** game frame anyway so this isn't TOO bad.
+ */
+ Mark(MARK_CHANGE);
+
+ if (StageClass::Graphic_Logic()) {
+ int stage = Fetch_Stage();
+
+ /*
+ ** If this animation is attached to another object and it is a
+ ** damaging kind of animation, then do the damage to the other
+ ** object.
+ */
+ if (xObject != TARGET_NONE && Class->Damage > 0) {
+ Accum += Class->Damage;
+
+ if (Accum >= 1) {
+
+ /*
+ ** Administer the damage. If the object was destroyed by this anim,
+ ** then the attached damaging anim is also destroyed.
+ */
+ int damage = Accum;
+ Accum -= damage;
+ if (As_Object(xObject)->Take_Damage(damage, 0, WARHEAD_FIRE) == RESULT_DESTROYED) {
+ delete this;
+ return;
+ }
+ }
+ }
+
+ /*
+ ** During the biggest stage (covers the most ground), perform any ground altering
+ ** action required. This masks craters and scorch marks, so that they appear
+ ** naturally rather than "popping" into existence while in plain sight.
+ */
+ if (Class->Biggest && Class->Start+stage == Class->Biggest) {
+ Middle();
+ }
+
+ /*
+ ** Check to see if the last frame has been displayed. If so, then the
+ ** animation either ends or loops.
+ */
+ if ((Loops <= 1 && stage >= Class->Stages) || (Loops > 1 && stage >= Class->LoopEnd-Class->Start)) {
+
+ /*
+ ** Determine if this animation should loop another time. If so, then start the loop
+ ** but if not, then proceed into the animation termination handler.
+ */
+ if (Loops) Loops--;
+ if (Loops) {
+ Set_Stage(Class->LoopStart);
+ } else {
+
+ /*
+ ** The animation should end now, but first check to see if
+ ** it needs to chain into another animation. If so, then the
+ ** animation isn't technically over. It metamorphoses into the
+ ** new form.
+ */
+ if (Class->ChainTo != ANIM_NONE) {
+
+ Class = (AnimTypeClass *)&AnimTypeClass::As_Reference(Class->ChainTo);
+
+ if (Class->Stages == -1) {
+IsTheaterShape = Class->IsTheater;
+ ((int&)Class->Stages) = Get_Build_Frame_Count(Class->Get_Image_Data());
+IsTheaterShape = false;
+ }
+ if (Class->LoopEnd == -1) {
+ ((int&)Class->LoopEnd) = Class->Stages;
+ }
+
+ IsToDelete = false;
+ Loops = Class->Loops;
+ Accum = 0;
+ if (Class->IsNormalized) {
+ Set_Rate(Options.Normalize_Delay(Class->Delay));
+ } else {
+ Set_Rate(Class->Delay);
+ }
+ Set_Stage(Class->Start);
+ Start();
+ } else {
+ delete this;
+ }
+ }
+ }
+ }
+ }
+#endif
+}
+
+
+/***********************************************************************************************
+ * AnimClass::Attach_To -- Attaches animation to object specified. *
+ * *
+ * An animation can be "attached" to an object. In such cases, the animation is rendered *
+ * as an offset from the center of the object it is attached to. This allows affects such *
+ * as fire or smoke to be consistently placed on the vehicle it is associated with. *
+ * *
+ * INPUT: obj -- Pointer to the object to attach the animation to. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 09/19/1994 JLB : Created. *
+ *=============================================================================================*/
+void AnimClass::Attach_To(ObjectClass * obj)
+{
+#ifdef VIC
+ assert(Anims.ID(this) == ID);
+ assert(IsActive);
+
+ if (obj == NULL) return;
+ assert(obj->IsActive);
+
+ obj->Mark(MARK_OVERLAP_UP);
+ obj->IsAnimAttached = true;
+ obj->Mark(MARK_OVERLAP_DOWN);
+ Map.Remove(this, In_Which_Layer());
+ xObject = obj->As_Target();
+ Map.Submit(this, In_Which_Layer());
+ Coord = Coord_Sub(Coord, obj->Target_Coord());
+#endif
+}
+
+
+/***********************************************************************************************
+ * AnimClass::In_Which_Layer -- Determines what render layer the anim should be in. *
+ * *
+ * Use this routine to find out which display layer (ground or air) that the animation *
+ * should be in. This information is used to place the animation into the correct display *
+ * list. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the layer that the animation should exist in. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 12/25/1994 JLB : Created. *
+ *=============================================================================================*/
+LayerType AnimClass::In_Which_Layer(void) const
+{
+#ifdef VIC
+ assert(Anims.ID(this) == ID);
+ assert(IsActive);
+
+ if (Class->Type >= ANIM_CORPSE1 && Class->Type <= ANIM_CORPSE3) {
+ return(LAYER_SURFACE);
+ }
+
+ if (Target_Legal(xObject) || Class->IsGroundLayer) {
+ return(LAYER_GROUND);
+ }
+#endif
+ return(LAYER_AIR);
+}
+
+
+/***********************************************************************************************
+ * AnimClass::Start -- Processes initial animation side effects. *
+ * *
+ * This routine is called when the animation first starts. Sometimes there are side effects *
+ * associated with this animation that must occur immediately. Typically, this is the *
+ * sound effect assigned to this animation. If this animation is supposed to attach itself *
+ * to any object at its location, then do so at this time as well. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/30/1995 JLB : Created. *
+ *=============================================================================================*/
+void AnimClass::Start(void)
+{
+#ifdef VIC
+ assert(Anims.ID(this) == ID);
+ assert(IsActive);
+
+ Mark();
+
+ /*
+ ** Play the sound effect for this animation.
+ */
+ Sound_Effect(Class->Sound, Coord);
+
+ /*
+ ** If the stage where collateral effects occur is the first stage of the animation, then
+ ** perform this action now. Subsequent checks against this stage value starts with the
+ ** second frame of the animation.
+ */
+ if (!Class->Biggest) {
+ Middle();
+ }
+#endif
+}
+
+
+/***********************************************************************************************
+ * AnimClass::Middle -- Processes any middle events. *
+ * *
+ * This routine is called when the animation as reached its largest stage. Typically, this *
+ * routine is used to cause scorches or craters to appear at a cosmetically pleasing *
+ * moment. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/30/1995 JLB : Created. *
+ * 10/17/1995 JLB : Ion camera added. *
+ *=============================================================================================*/
+void AnimClass::Middle(void)
+{
+#ifdef VIC
+ assert(Anims.ID(this) == ID);
+ assert(IsActive);
+
+ CELL cell = Coord_Cell(Center_Coord());
+ CellClass * cellptr = &Map[cell];
+
+ if (Class->Type == ANIM_ATOM_BLAST) {
+ Do_Atom_Damage(OwnerHouse, cell);
+ }
+
+ /*
+ ** If this animation leaves scorch marks (e.g., napalm), then do so at this time.
+ */
+ if (Class->IsScorcher) {
+ new SmudgeClass(Random_Pick(SMUDGE_SCORCH1, SMUDGE_SCORCH6), Center_Coord());
+ }
+
+ /*
+ ** Some animations leave a crater when they occur. Artillery is a good example.
+ ** Craters always remove the Tiberium where they occur.
+ */
+ if (Class->IsCraterForming) {
+
+ /*
+ ** Craters reduce the level of Tiberium in the cell.
+ */
+ cellptr->Reduce_Tiberium(6);
+
+ /*
+ ** If there already is a crater in the cell, then just expand the
+ ** crater.
+ */
+ new SmudgeClass(SMUDGE_CRATER1, Center_Coord());
+ }
+
+ AnimClass * newanim;
+
+ /*
+ ** If this animation spawns side effects during its lifetime, then
+ ** do so now. Usually, these side effects are in the form of other
+ ** animations.
+ */
+ switch (Class->Type) {
+ case ANIM_NAPALM1:
+ case ANIM_NAPALM2:
+ case ANIM_NAPALM3:
+ new AnimClass(ANIM_FIRE_SMALL, Map.Closest_Free_Spot(Coord_Scatter(Center_Coord(), 0x0040), true), 0, Random_Pick(1, 2));
+ if (Percent_Chance(50)) {
+ new AnimClass(ANIM_FIRE_SMALL, Map.Closest_Free_Spot(Coord_Scatter(Center_Coord(), 0x00A0), true), 0, Random_Pick(1, 2));
+ }
+ if (Percent_Chance(50)) {
+ new AnimClass(ANIM_FIRE_MED, Map.Closest_Free_Spot(Coord_Scatter(Center_Coord(), 0x0070), true), 0, Random_Pick(1, 2));
+ }
+ break;
+
+ case ANIM_FIRE_MED:
+ case ANIM_FIRE_MED2:
+ newanim = new AnimClass(ANIM_FIRE_SMALL, Center_Coord(), 0, Random_Pick(1, 2));
+ if (newanim != NULL && xObject != TARGET_NONE) {
+ newanim->Attach_To(As_Object(xObject));
+ }
+ break;
+
+ default:
+ break;
+ }
+#endif
+}
+
+
+/***********************************************************************************************
+ * AnimClass::Detach -- Remove animation if attached to target. *
+ * *
+ * This routine is called when the specified target is being removed from the game. If this *
+ * animation happens to be attached to this object, then the animation must be remove as *
+ * well. *
+ * *
+ * INPUT: target -- The target that is about to be destroyed. *
+ * *
+ * all -- Is the target being destroyed RIGHT NOW? If not, then it will be *
+ * destroyed soon. In that case, the animation should continue to remain *
+ * attached for cosmetic reasons. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/30/1995 JLB : Created. *
+ * 07/02/1995 JLB : Detach is a precursor to animation destruction. *
+ *=============================================================================================*/
+void AnimClass::Detach(TARGET target, bool all)
+{
+#ifdef VIC
+ assert(Anims.ID(this) == ID);
+ assert(IsActive);
+
+ if (xObject == target && all) {
+ Map.Remove(this, In_Which_Layer());
+ xObject = TARGET_NONE;
+ IsToDelete = true;
+ Mark(MARK_UP);
+ }
+#endif
+}
+
+
+/***********************************************************************************************
+ * AnimClass::Do_Atom_Damage -- Do atom bomb damage centered around the cell specified. *
+ * *
+ * This routine will apply damage around the ground-zero cell specified. *
+ * *
+ * INPUT: ownerhouse -- The owner of this atom bomb. *
+ * *
+ * cell -- The ground zero location to apply the atom bomb damage. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/06/1996 JLB : Created. *
+ *=============================================================================================*/
+void AnimClass::Do_Atom_Damage(HousesType ownerhouse, CELL cell)
+{
+#ifdef VIC
+ /*
+ ** Find someone to blame the explosion on. This is necessary in
+ ** order to properly enact retribution and record the kill for
+ ** score purposes.
+ */
+ BuildingClass * building = NULL;
+ TechnoClass * backup = NULL;
+ if (ownerhouse != HOUSE_NONE) {
+ for (int index = 0; index < Logic.Count(); index++) {
+ ObjectClass * obj = Logic[index];
+
+ if (obj != NULL && obj->Is_Techno() && obj->Owner() == ownerhouse) {
+ backup = (TechnoClass *)obj;
+ if (obj->What_Am_I() == RTTI_BUILDING && *((BuildingClass *)obj) == STRUCT_MSLO) {
+ building = (BuildingClass *)obj;
+ break;
+ }
+ }
+ }
+
+ if (building == NULL) building = (BuildingClass *)backup;
+ }
+
+ int radius;
+ int rawdamage;
+ if (Session.Type == GAME_NORMAL) {
+ radius = 4;
+ rawdamage = Rule.AtomDamage;
+ WhitePalette.Set(FADE_PALETTE_SLOW, Call_Back);
+ } else {
+ radius = 3;
+ rawdamage = Rule.AtomDamage/5;
+ }
+
+ Wide_Area_Damage(Cell_Coord(cell), radius * CELL_LEPTON_W, rawdamage, building, WARHEAD_FIRE);
+ Shake_The_Screen(3);
+ if (Session.Type == GAME_NORMAL) {
+ GamePalette.Set(FADE_PALETTE_SLOW, Call_Back);
+ }
+#endif
+}
+
+
diff --git a/CODE/ANIM.CPP.BAK b/CODE/ANIM.CPP.BAK
new file mode 100644
index 0000000..6ef5060
--- /dev/null
+++ b/CODE/ANIM.CPP.BAK
@@ -0,0 +1,1062 @@
+/*
+** Command & Conquer Red Alert(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&c0\vcs\code\anim.cpv 4.78 03 Oct 1996 09:20: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 : Dune *
+ * *
+ * File Name : ANIM.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : June 3, 1991 *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * AnimClass::AI -- This is the low level anim processor. *
+ * AnimClass::AnimClass -- The constructor for animation objects. *
+ * AnimClass::Attach_To -- Attaches animation to object specified. *
+ * AnimClass::Center_Coord -- Determine center of animation. *
+ * AnimClass::Detach -- Remove animation if attached to target. *
+ * AnimClass::Do_Atom_Damage -- Do atom bomb damage centered around the cell specified. *
+ * AnimClass::Draw_It -- Draws the animation at the location specified. *
+ * AnimClass::In_Which_Layer -- Determines what render layer the anim should be in. *
+ * AnimClass::Init -- Performs pre-scenario initialization. *
+ * AnimClass::Mark -- Signals to map that redrawing is necessary. *
+ * AnimClass::Middle -- Processes any middle events. *
+ * AnimClass::Occupy_List -- Determines the occupy list for the animation. *
+ * AnimClass::Overlap_List -- Determines the overlap list for the animation. *
+ * AnimClass::Render -- Draws an animation object. *
+ * AnimClass::Sort_Y -- Returns with the sorting coordinate for the animation. *
+ * AnimClass::Start -- Processes initial animation side effects. *
+ * AnimClass::delete -- Returns an anim object back to the free pool. *
+ * AnimClass::new -- Allocates an anim object from the pool. *
+ * AnimClass::~AnimClass -- Destructor for anim objects. *
+ * Anim_From_Name -- Given a name, this finds the corresponding anim type. *
+ * Shorten_Attached_Anims -- Reduces attached animation durations. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+
+
+/***********************************************************************************************
+ * Anim_From_Name -- Given a name, this finds the corresponding anim type. *
+ * *
+ * This routine will convert the supplied ASCII name into the animation type that it *
+ * represents. *
+ * *
+ * INPUT: name -- Pointer to the ASCII name to convert. *
+ * *
+ * OUTPUT: Returns with the animation type that matches the name specified. If no match could *
+ * be found, then ANIM_NONE is returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/06/1996 JLB : Created. *
+ *=============================================================================================*/
+AnimType Anim_From_Name(char const * name)
+{
+ #ifdef 0
+ if (name == NULL) return(ANIM_NONE);
+
+ for (AnimType anim = ANIM_FIRST; anim < ANIM_COUNT; anim++) {
+ if (stricmp(AnimTypeClass::As_Reference(anim).IniName, name) == 0) {
+ return(anim);
+ }
+ }
+#endif
+ return(ANIM_NONE);
+}
+
+
+/***********************************************************************************************
+ * Shorten_Attached_Anims -- Reduces attached animation durations. *
+ * *
+ * This routine is used to reduce the amount of time any attached animations will process. *
+ * Typical use of this is when an object is on fire and the object should now be destroyed *
+ * but the attached animations are to run until completion before destruction can follow. *
+ * This routine will make the animation appear to run its course, but in as short of time *
+ * as possible. The shortening effect is achieved by reducing the number of times the *
+ * animation will loop. *
+ * *
+ * INPUT: obj -- Pointer to the object that all attached animations will be processed. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 12/11/1994 JLB : Created. *
+ *=============================================================================================*/
+void Shorten_Attached_Anims(ObjectClass * obj)
+{
+ if (obj != NULL) {
+ for (int index = 0; index < Anims.Count(); index++) {
+ AnimClass & anim = * Anims.Ptr(index);
+
+ if (As_Object(anim.xObject) == obj) {
+ anim.Loops = 0;
+ }
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * AnimClass::Sort_Y -- Returns with the sorting coordinate for the animation. *
+ * *
+ * This routine is used by the sorting system. Animations that are located in the ground *
+ * layer will be sorted by this the value returned from this function. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the sort coordinate to use for this animation. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 10/17/1994 JLB : Created. *
+ * 12/15/1994 JLB : Handles flat anims (infantry decay anims). *
+ *=============================================================================================*/
+COORDINATE AnimClass::Sort_Y(void) const
+{
+#ifdef 0
+ assert(Anims.ID(this) == ID);
+ assert(IsActive);
+
+ if (xObject != TARGET_NONE) {
+ return(Coord_Add(As_Object(xObject)->Sort_Y(), 0x00010000L));
+ }
+ if (*this == ANIM_MOVE_FLASH) {
+ return(Coord_Add(Center_Coord(), XYP_COORD(0, -24)));
+ }
+ if (Class->IsGroundLayer || *this == ANIM_LZ_SMOKE) {
+ return(Coord_Add(Center_Coord(), XYP_COORD(0, 14)));
+ }
+#endif
+ return(Coord);
+}
+
+
+/***********************************************************************************************
+ * AnimClass::Center_Coord -- Determine center of animation. *
+ * *
+ * This support function will return the "center" of the animation. The actual coordinate *
+ * of the animation may be dependant on if the the animation is attached to an object. *
+ * In such a case, it must factor in the object's location. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns the coordinate of the center of the animation. The coordinate is in real *
+ * game coordinates -- taking into consideration if the animation is attached. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 09/19/1994 JLB : Created. *
+ * 02/02/1996 JLB : Coordinate based on visual center of object. *
+ *=============================================================================================*/
+COORDINATE AnimClass::Center_Coord(void) const
+{
+#ifdef 0
+ assert(Anims.ID(this) == ID);
+ assert(IsActive);
+
+ if (xObject != TARGET_NONE) {
+ return(Coord_Add(Coord, As_Object(xObject)->Target_Coord()));
+ }
+#endif
+ return(Coord);
+}
+
+
+/***********************************************************************************************
+ * AnimClass::Render -- Draws an animation object. *
+ * *
+ * This is the working routine that renders the animation shape. It gets called once *
+ * per animation per frame. It needs to be fast. *
+ * *
+ * INPUT: bool; Should the animation be rendered in spite of render flag? *
+ * *
+ * OUTPUT: bool; Was the animation rendered? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/31/1994 JLB : Created. *
+ *=============================================================================================*/
+bool AnimClass::Render(bool forced) const
+{
+#ifdef 0
+ assert(Anims.ID(this) == ID);
+ assert(IsActive);
+
+ if (Delay) return(false);
+ if (Map[Center_Coord()].IsVisible) {
+ IsToDisplay = true;
+ }
+#endif
+ return(ObjectClass::Render(forced));
+}
+
+
+/***********************************************************************************************
+ * AnimClass::Draw_It -- Draws the animation at the location specified. *
+ * *
+ * This routine is used to render the animation object at the location specified. This is *
+ * how the map imagery gets updated. *
+ * *
+ * INPUT: x,y -- The pixel coordinates to draw the animation at. *
+ * *
+ * window -- The to base the draw coordinates upon. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 09/24/1994 JLB : Created. *
+ * 05/19/1995 JLB : Added white translucent effect. *
+ *=============================================================================================*/
+void AnimClass::Draw_It(int x, int y, WindowNumberType window) const
+{
+#ifdef 0
+ assert(Anims.ID(this) == ID);
+ assert(IsActive);
+
+ if (!IsInvisible) {
+ BStart(BENCH_ANIMS);
+
+ void const * shapefile = Get_Image_Data();
+ if (shapefile != NULL) {
+ void const * transtable = NULL;
+ int shapenum = Class->Start + Fetch_Stage();
+ void const * remap = NULL;
+
+ /*
+ ** If the translucent table hasn't been determined yet, then check to see if it
+ ** should use the white or normal translucent tables.
+ */
+ if (transtable == NULL && Class->IsWhiteTrans) transtable = DisplayClass::WhiteTranslucentTable;
+ if (transtable == NULL && Class->IsTranslucent) transtable = DisplayClass::TranslucentTable;
+ if (Class->Type == ANIM_ATOM_BLAST) transtable = Map.UnitShadow;
+
+ /*
+ ** Set the shape flags to properly take into account any fading or ghosting
+ ** table necessary.
+ */
+ ShapeFlags_Type flags = SHAPE_CENTER|SHAPE_WIN_REL;
+ if (transtable != NULL) flags = flags | SHAPE_GHOST;
+
+ /*
+ ** Draw the animation shape.
+ */
+ CC_Draw_Shape(shapefile, shapenum, x, y, window, flags, remap, transtable);
+ }
+ BEnd(BENCH_ANIMS);
+ }
+#endif
+}
+
+
+/***********************************************************************************************
+ * AnimClass::Mark -- Signals to map that redrawing is necessary. *
+ * *
+ * This routine is used by the animation logic system to inform the map that the cells *
+ * under the animation must be rerendered. *
+ * *
+ * INPUT: *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/31/1994 JLB : Created. *
+ *=============================================================================================*/
+bool AnimClass::Mark(MarkType mark)
+{
+#ifdef 0
+ assert(Anims.ID(this) == ID);
+ assert(IsActive);
+
+ if (ObjectClass::Mark(mark)) {
+ Map.Refresh_Cells(Coord_Cell(Center_Coord()), Overlap_List());
+// ObjectClass::Mark(mark);
+ return(true);
+ }
+#endif
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * AnimClass::Overlap_List -- Determines the overlap list for the animation. *
+ * *
+ * Use this routine to fetch the overlap list for the animation. This overlap list is the *
+ * cells that this animation spills over. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns a pointer to the overlap list for this particular instance of the *
+ * animation. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 03/19/1995 JLB : Created. *
+ *=============================================================================================*/
+short const * AnimClass::Overlap_List(void) const
+{
+#ifdef 0
+ assert(Anims.ID(this) == ID);
+ assert(IsActive);
+ static short const OverlapAtom[] = {
+ (-MAP_CELL_W * 2) - 1, (-MAP_CELL_W * 2), (-MAP_CELL_W * 2) + 1,
+ (-MAP_CELL_W * 1) - 1, (-MAP_CELL_W * 1), (-MAP_CELL_W * 1) + 1,
+ (-MAP_CELL_W * 0) - 1, (-MAP_CELL_W * 0), (-MAP_CELL_W * 0) + 1,
+ ( MAP_CELL_W * 1) - 1, ( MAP_CELL_W * 1), ( MAP_CELL_W * 1) + 1,
+ ( MAP_CELL_W * 2) - 1, ( MAP_CELL_W * 2), ( MAP_CELL_W * 2) + 1,
+ REFRESH_EOL
+ };
+
+ if (IsToDelete) {
+ static short const _list[] = {REFRESH_EOL};
+ return(_list);
+ }
+
+ if (Class->Type == ANIM_ATOM_BLAST) {
+ return(OverlapAtom);
+ }
+
+#ifdef PARTIAL
+ if (Class->Get_Image_Data() != NULL) {
+ int shapenum = Class->Start + Fetch_Stage();
+ int count = Get_Build_Frame_Count(Class->Get_Image_Data());
+ shapenum = min(shapenum, count-1);
+
+ if (Class->DimensionData == NULL) {
+ Class->DimensionData = new Rect [count];
+ }
+ if (Class->DimensionData != NULL && !Class->DimensionData[shapenum].Is_Valid()) {
+ Class->DimensionData[shapenum] = Shape_Dimensions(Class->Get_Image_Data(), shapenum);
+ return(Coord_Spillage_List(Center_Coord(), Class->DimensionData[shapenum]));
+ }
+ }
+#endif
+#endif
+ return(Coord_Spillage_List(Center_Coord(), Class->Size));
+}
+
+
+/***********************************************************************************************
+ * AnimClass::Occupy_List -- Determines the occupy list for the animation. *
+ * *
+ * Animations always occupy only the cell that their center is located over. As such, this *
+ * routine always returns a simple (center cell) occupation list. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the occupation list for the animation. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 03/19/1995 JLB : Created. *
+ *=============================================================================================*/
+short const * AnimClass::Occupy_List(bool) const
+{
+#ifdef 0
+ assert(Anims.ID(this) == ID);
+ assert(IsActive);
+
+ static short _simple[] = {REFRESH_EOL};
+
+#endif
+ return(_simple);
+}
+
+
+/***********************************************************************************************
+ * AnimClass::Init -- Performs pre-scenario initialization. *
+ * *
+ * This routine is used to initialize the animation system prior to a scenario being loaded *
+ * or reloaded. It effectively removes all animations from the system. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/31/1994 JLB : Created. *
+ *=============================================================================================*/
+void AnimClass::Init(void)
+{
+ Anims.Free_All();
+}
+
+
+/***********************************************************************************************
+ * AnimClass::new -- Allocates an anim object from the pool. *
+ * *
+ * This routine is used to allocate a free anim class object from the preallocated pool *
+ * in the near heap. If there are no free animation objects, then null is returned. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with a pointer to a free anim object. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/31/1994 JLB : Created. *
+ *=============================================================================================*/
+void * AnimClass::operator new(size_t)
+{
+ void * ptr = Anims.Allocate();
+ if (ptr != NULL) {
+ ((AnimClass *)ptr)->IsActive = true;
+ }
+ return(ptr);
+}
+
+
+/***********************************************************************************************
+ * AnimClass::delete -- Returns an anim object back to the free pool. *
+ * *
+ * This routine is used to return an anim object back to the pool of free anim objects. *
+ * Anim objects so returned are available to be reallocated for the next animation. *
+ * *
+ * INPUT: ptr -- Pointer to the anim object to return to the pool. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/31/1994 JLB : Created. *
+ *=============================================================================================*/
+void AnimClass::operator delete(void * ptr)
+{
+ if (ptr != NULL) {
+ ((AnimClass *)ptr)->IsActive = false;
+ }
+ Anims.Free((AnimClass *)ptr);
+}
+
+
+/***********************************************************************************************
+ * AnimClass::AnimClass -- The constructor for animation objects. *
+ * *
+ * This routine is used as the constructor of animation objects. It initializes and adds *
+ * the animation object to the display and logic systems. *
+ * *
+ * INPUT: animnum -- The animation number to start. *
+ * *
+ * coord -- The location of the animation. *
+ * *
+ * timedelay-- The delay before the animation starts. *
+ * *
+ * loop -- The number of times to loop this animation. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/31/1994 JLB : Created. *
+ * 08/03/1994 JLB : Added a delayed affect parameter. *
+ *=============================================================================================*/
+AnimClass::AnimClass(AnimType animnum, COORDINATE coord, unsigned char timedelay, unsigned char loop) :
+ ObjectClass(RTTI_ANIM, Anims.ID(this)),
+ Class(AnimTypes.Ptr((int)animnum)),
+ xObject(TARGET_NONE),
+ OwnerHouse(HOUSE_NONE),
+ Loops(1),
+ IsToDelete(false),
+ IsBrandNew(true),
+ IsInvisible(false),
+ Delay(timedelay),
+ Accum(0)
+{
+#ifdef 0
+ if (Class->Stages == -1) {
+ ((int&)Class->Stages) = Get_Build_Frame_Count(Class->Get_Image_Data());
+ }
+ if (Class->LoopEnd == -1) {
+ ((int&)Class->LoopEnd) = Class->Stages;
+ }
+ if (Class->IsNormalized) {
+ Set_Rate(Options.Normalize_Delay(Class->Delay));
+ } else {
+ Set_Rate(Class->Delay);
+ }
+ Set_Stage(0);
+
+ if (Class->IsGroundLayer) {
+ Height = FLIGHT_LEVEL;
+ }
+
+ AnimClass::Unlimbo(coord);
+
+ /*
+ ** Drop zone smoke always reveals the map around itself.
+ */
+ if (*this == ANIM_LZ_SMOKE) {
+ Map.Sight_From(Coord_Cell(coord), Rule.DropZoneRadius / CELL_LEPTON_W, PlayerPtr, false);
+ }
+
+ Loops = (unsigned char)(max(loop, 1) * Class->Loops);
+ Loops = (unsigned char)max(Loops, 1);
+
+ /*
+ ** If the animation starts immediately, then play the associated sound effect now.
+ */
+ if (!Delay) {
+ Start();
+ }
+#endif
+}
+
+
+/***********************************************************************************************
+ * AnimClass::~AnimClass -- Destructor for anim objects. *
+ * *
+ * This destructor handles removing the animation object from the system. It might require *
+ * informing any object this animation is attached to that it is no longer attached. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 11/29/1994 JLB : Created. *
+ *=============================================================================================*/
+AnimClass::~AnimClass(void)
+{
+#ifdef 0
+ assert(Anims.ID(this) == ID);
+ assert(IsActive);
+ if (GameActive) {
+
+ /*
+ ** If this anim is attached to another object
+ ** then check to see if this is the last anim attached to it. If this
+ ** is the case, then inform the object that it is no longer attached to
+ ** an animation.
+ */
+ if (Target_Legal(xObject) && As_Object(xObject) != NULL) {
+ ObjectClass * to = As_Object(xObject);
+
+ /*
+ ** Remove the object from the appropriate display list.
+ */
+ Map.Remove(this, In_Which_Layer());
+
+ /*
+ ** Scan for any other animations that are attached to the object that
+ ** this animation is attached to. If there are no others, then inform the
+ ** attached object of this fact.
+ */
+ for (int index = 0; index < Anims.Count(); index++) {
+ if (Anims.Ptr(index) != this && Anims.Ptr(index)->xObject == xObject) break;
+ }
+
+ /*
+ ** Tell the object that it is no longer being damaged.
+ */
+ if (index == Anims.Count()) {
+ to->Fire_Out();
+ to->Mark(MARK_OVERLAP_UP);
+ to->IsAnimAttached = false;
+ to->Mark(MARK_OVERLAP_DOWN);
+ }
+ Coord = Coord_Add(to->Center_Coord(), Coord);
+ xObject = TARGET_NONE;
+ }
+
+ Limbo();
+ }
+
+ xObject = TARGET_NONE;
+ Class = 0;
+ ID = -1;
+#endif
+}
+
+
+/***********************************************************************************************
+ * AnimClass::AI -- This is the low level anim processor. *
+ * *
+ * This routine is called once per frame per animation. It handles transition between *
+ * animation frames and marks the map for redraw as necessary. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: Speed is of upmost importance. *
+ * *
+ * HISTORY: *
+ * 05/31/1994 JLB : Created. *
+ *=============================================================================================*/
+void AnimClass::AI(void)
+{
+#ifdef 0
+ assert(Anims.ID(this) == ID);
+ assert(IsActive);
+
+ /*
+ ** For ground level based animations (ones that can run slowly as well as
+ ** occur behind other ground objects) always cause the cell to be redrawn.
+ */
+#ifdef PARTIAL
+ if (!Delay && Class->IsGroundLayer) {
+ Map.Refresh_Cells(Coord_Cell(Center_Coord()), Overlap_List());
+ }
+#else
+ Map.Refresh_Cells(Coord_Cell(Center_Coord()), Overlap_List());
+#endif
+
+ /*
+ ** Special case check to make sure that building on top of a smoke marker
+ ** causes the smoke marker to vanish.
+ */
+ if (Class->Type == ANIM_LZ_SMOKE && Map[Center_Coord()].Cell_Building()) {
+ IsToDelete = true;
+ }
+
+ /*
+ ** Delete this animation and bail early if the animation is flagged to be deleted
+ ** immediately.
+ */
+ if (IsToDelete) {
+ delete this;
+ return;
+ }
+
+ /*
+ ** If this is a brand new animation, then don't process it the first logic pass
+ ** since it might end up skipping the first animation frame before it has had a
+ ** chance to draw it.
+ */
+ if (IsBrandNew) {
+ IsBrandNew = false;
+ return;
+ }
+
+ if (Delay) {
+ Delay--;
+ if (!Delay) {
+ Start();
+ }
+ } else {
+
+ /*
+ ** This is necessary because there is no recording of animations on the map
+ ** and thus the animation cannot be intelligently flagged for redraw. Most
+ ** animations move fast enough that they would need to be redrawn every
+ ** game frame anyway so this isn't TOO bad.
+ */
+ Mark(MARK_CHANGE);
+
+ if (StageClass::Graphic_Logic()) {
+ int stage = Fetch_Stage();
+
+ /*
+ ** If this animation is attached to another object and it is a
+ ** damaging kind of animation, then do the damage to the other
+ ** object.
+ */
+ if (xObject != TARGET_NONE && Class->Damage > 0) {
+ Accum += Class->Damage;
+
+ if (Accum >= 1) {
+
+ /*
+ ** Administer the damage. If the object was destroyed by this anim,
+ ** then the attached damaging anim is also destroyed.
+ */
+ int damage = Accum;
+ Accum -= damage;
+ if (As_Object(xObject)->Take_Damage(damage, 0, WARHEAD_FIRE) == RESULT_DESTROYED) {
+ delete this;
+ return;
+ }
+ }
+ }
+
+ /*
+ ** During the biggest stage (covers the most ground), perform any ground altering
+ ** action required. This masks craters and scorch marks, so that they appear
+ ** naturally rather than "popping" into existence while in plain sight.
+ */
+ if (Class->Biggest && Class->Start+stage == Class->Biggest) {
+ Middle();
+ }
+
+ /*
+ ** Check to see if the last frame has been displayed. If so, then the
+ ** animation either ends or loops.
+ */
+ if ((Loops <= 1 && stage >= Class->Stages) || (Loops > 1 && stage >= Class->LoopEnd-Class->Start)) {
+
+ /*
+ ** Determine if this animation should loop another time. If so, then start the loop
+ ** but if not, then proceed into the animation termination handler.
+ */
+ if (Loops) Loops--;
+ if (Loops) {
+ Set_Stage(Class->LoopStart);
+ } else {
+
+ /*
+ ** The animation should end now, but first check to see if
+ ** it needs to chain into another animation. If so, then the
+ ** animation isn't technically over. It metamorphoses into the
+ ** new form.
+ */
+ if (Class->ChainTo != ANIM_NONE) {
+
+ Class = (AnimTypeClass *)&AnimTypeClass::As_Reference(Class->ChainTo);
+
+ if (Class->Stages == -1) {
+ ((int&)Class->Stages) = Get_Build_Frame_Count(Class->Get_Image_Data());
+ }
+ if (Class->LoopEnd == -1) {
+ ((int&)Class->LoopEnd) = Class->Stages;
+ }
+
+ IsToDelete = false;
+ Loops = Class->Loops;
+ Accum = 0;
+ if (Class->IsNormalized) {
+ Set_Rate(Options.Normalize_Delay(Class->Delay));
+ } else {
+ Set_Rate(Class->Delay);
+ }
+ Set_Stage(Class->Start);
+ Start();
+ } else {
+ delete this;
+ }
+ }
+ }
+ }
+ }
+#endif
+}
+
+
+/***********************************************************************************************
+ * AnimClass::Attach_To -- Attaches animation to object specified. *
+ * *
+ * An animation can be "attached" to an object. In such cases, the animation is rendered *
+ * as an offset from the center of the object it is attached to. This allows affects such *
+ * as fire or smoke to be consistently placed on the vehicle it is associated with. *
+ * *
+ * INPUT: obj -- Pointer to the object to attach the animation to. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 09/19/1994 JLB : Created. *
+ *=============================================================================================*/
+void AnimClass::Attach_To(ObjectClass * obj)
+{
+#ifdef 0
+ assert(Anims.ID(this) == ID);
+ assert(IsActive);
+
+ if (obj == NULL) return;
+ assert(obj->IsActive);
+
+ obj->Mark(MARK_OVERLAP_UP);
+ obj->IsAnimAttached = true;
+ obj->Mark(MARK_OVERLAP_DOWN);
+ Map.Remove(this, In_Which_Layer());
+ xObject = obj->As_Target();
+ Map.Submit(this, In_Which_Layer());
+ Coord = Coord_Sub(Coord, obj->Target_Coord());
+#endif
+}
+
+
+/***********************************************************************************************
+ * AnimClass::In_Which_Layer -- Determines what render layer the anim should be in. *
+ * *
+ * Use this routine to find out which display layer (ground or air) that the animation *
+ * should be in. This information is used to place the animation into the correct display *
+ * list. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the layer that the animation should exist in. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 12/25/1994 JLB : Created. *
+ *=============================================================================================*/
+LayerType AnimClass::In_Which_Layer(void) const
+{
+#ifdef 0
+ assert(Anims.ID(this) == ID);
+ assert(IsActive);
+
+ if (Target_Legal(xObject) || Class->IsGroundLayer) {
+ return(LAYER_GROUND);
+ }
+
+ return(LAYER_AIR);
+}
+
+
+/***********************************************************************************************
+ * AnimClass::Start -- Processes initial animation side effects. *
+ * *
+ * This routine is called when the animation first starts. Sometimes there are side effects *
+ * associated with this animation that must occur immediately. Typically, this is the *
+ * sound effect assigned to this animation. If this animation is supposed to attach itself *
+ * to any object at its location, then do so at this time as well. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/30/1995 JLB : Created. *
+ *=============================================================================================*/
+void AnimClass::Start(void)
+{
+ assert(Anims.ID(this) == ID);
+ assert(IsActive);
+
+ Mark();
+
+ /*
+ ** Play the sound effect for this animation.
+ */
+ Sound_Effect(Class->Sound, Coord);
+
+ /*
+ ** If the stage where collateral effects occur is the first stage of the animation, then
+ ** perform this action now. Subsequent checks against this stage value starts with the
+ ** second frame of the animation.
+ */
+ if (!Class->Biggest) {
+ Middle();
+ }
+}
+
+
+/***********************************************************************************************
+ * AnimClass::Middle -- Processes any middle events. *
+ * *
+ * This routine is called when the animation as reached its largest stage. Typically, this *
+ * routine is used to cause scorches or craters to appear at a cosmetically pleasing *
+ * moment. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/30/1995 JLB : Created. *
+ * 10/17/1995 JLB : Ion camera added. *
+ *=============================================================================================*/
+void AnimClass::Middle(void)
+{
+ assert(Anims.ID(this) == ID);
+ assert(IsActive);
+
+ CELL cell = Coord_Cell(Center_Coord());
+ CellClass * cellptr = &Map[cell];
+
+ if (Class->Type == ANIM_ATOM_BLAST) {
+ Do_Atom_Damage(OwnerHouse, cell);
+ }
+
+ /*
+ ** If this animation leaves scorch marks (e.g., napalm), then do so at this time.
+ */
+ if (Class->IsScorcher) {
+ new SmudgeClass(Random_Pick(SMUDGE_SCORCH1, SMUDGE_SCORCH6), Center_Coord());
+ }
+
+ /*
+ ** Some animations leave a crater when they occur. Artillery is a good example.
+ ** Craters always remove the Tiberium where they occur.
+ */
+ if (Class->IsCraterForming) {
+
+ /*
+ ** Craters reduce the level of Tiberium in the cell.
+ */
+ cellptr->Reduce_Tiberium(6);
+
+ /*
+ ** If there already is a crater in the cell, then just expand the
+ ** crater.
+ */
+ new SmudgeClass(SMUDGE_CRATER1, Center_Coord());
+ }
+
+ AnimClass * newanim;
+
+ /*
+ ** If this animation spawns side effects during its lifetime, then
+ ** do so now. Usually, these side effects are in the form of other
+ ** animations.
+ */
+ switch (Class->Type) {
+ case ANIM_NAPALM1:
+ case ANIM_NAPALM2:
+ case ANIM_NAPALM3:
+ new AnimClass(ANIM_FIRE_SMALL, Map.Closest_Free_Spot(Coord_Scatter(Center_Coord(), 0x0040), true), 0, Random_Pick(1, 2));
+ if (Percent_Chance(50)) {
+ new AnimClass(ANIM_FIRE_SMALL, Map.Closest_Free_Spot(Coord_Scatter(Center_Coord(), 0x00A0), true), 0, Random_Pick(1, 2));
+ }
+ if (Percent_Chance(50)) {
+ new AnimClass(ANIM_FIRE_MED, Map.Closest_Free_Spot(Coord_Scatter(Center_Coord(), 0x0070), true), 0, Random_Pick(1, 2));
+ }
+ break;
+
+ case ANIM_FIRE_MED:
+ case ANIM_FIRE_MED2:
+ newanim = new AnimClass(ANIM_FIRE_SMALL, Center_Coord(), 0, Random_Pick(1, 2));
+ if (newanim != NULL && xObject != TARGET_NONE) {
+ newanim->Attach_To(As_Object(xObject));
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+/***********************************************************************************************
+ * AnimClass::Detach -- Remove animation if attached to target. *
+ * *
+ * This routine is called when the specified target is being removed from the game. If this *
+ * animation happens to be attached to this object, then the animation must be remove as *
+ * well. *
+ * *
+ * INPUT: target -- The target that is about to be destroyed. *
+ * *
+ * all -- Is the target being destroyed RIGHT NOW? If not, then it will be *
+ * destroyed soon. In that case, the animation should continue to remain *
+ * attached for cosmetic reasons. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/30/1995 JLB : Created. *
+ * 07/02/1995 JLB : Detach is a precursor to animation destruction. *
+ *=============================================================================================*/
+void AnimClass::Detach(TARGET target, bool all)
+{
+ assert(Anims.ID(this) == ID);
+ assert(IsActive);
+
+ if (xObject == target && all) {
+ Map.Remove(this, In_Which_Layer());
+ xObject = TARGET_NONE;
+ IsToDelete = true;
+ Mark(MARK_UP);
+ }
+}
+
+
+/***********************************************************************************************
+ * AnimClass::Do_Atom_Damage -- Do atom bomb damage centered around the cell specified. *
+ * *
+ * This routine will apply damage around the ground-zero cell specified. *
+ * *
+ * INPUT: ownerhouse -- The owner of this atom bomb. *
+ * *
+ * cell -- The ground zero location to apply the atom bomb damage. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/06/1996 JLB : Created. *
+ *=============================================================================================*/
+void AnimClass::Do_Atom_Damage(HousesType ownerhouse, CELL cell)
+{
+ /*
+ ** Find someone to blame the explosion on. This is necessary in
+ ** order to properly enact retribution and record the kill for
+ ** score purposes.
+ */
+ BuildingClass * building = NULL;
+ TechnoClass * backup = NULL;
+ if (ownerhouse != HOUSE_NONE) {
+ for (int index = 0; index < Logic.Count(); index++) {
+ ObjectClass * obj = Logic[index];
+
+ if (obj != NULL && obj->Is_Techno() && obj->Owner() == ownerhouse) {
+ backup = (TechnoClass *)obj;
+ if (obj->What_Am_I() == RTTI_BUILDING && *((BuildingClass *)obj) == STRUCT_MSLO) {
+ building = (BuildingClass *)obj;
+ break;
+ }
+ }
+ }
+
+ if (building == NULL) building = (BuildingClass *)backup;
+ }
+
+ int radius;
+ int rawdamage;
+ if (Session.Type == GAME_NORMAL) {
+ radius = 4;
+ rawdamage = Rule.AtomDamage;
+ WhitePalette.Set(FADE_PALETTE_SLOW, Call_Back);
+ } else {
+ radius = 3;
+ rawdamage = Rule.AtomDamage/5;
+ }
+
+ Wide_Area_Damage(Cell_Coord(cell), radius * CELL_LEPTON_W, rawdamage, building, WARHEAD_FIRE);
+ Shake_The_Screen(3);
+ if (Session.Type == GAME_NORMAL) {
+ GamePalette.Set(FADE_PALETTE_SLOW, Call_Back);
+ }
+}
+
+
diff --git a/CODE/ANIM.H b/CODE/ANIM.H
new file mode 100644
index 0000000..a66446f
--- /dev/null
+++ b/CODE/ANIM.H
@@ -0,0 +1,156 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/ANIM.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : ANIM.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : May 30, 1994 *
+ * *
+ * Last Update : May 30, 1994 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifndef ANIM_H
+#define ANIM_H
+
+#include "type.h"
+
+
+/**********************************************************************************************
+** This is the class that controls the shape animation objects. Shape animation objects are
+** displayed over the top of the game map. Typically, they are used for explosion and fire
+** effects.
+*/
+class AnimClass : public ObjectClass, public StageClass {
+ /*
+ ** This points to the type of animation object this is.
+ */
+ CCPtr Class;
+
+ public:
+
+ AnimClass(AnimType animnum, COORDINATE coord, unsigned char timedelay=0, unsigned char loop=1);
+ AnimClass(NoInitClass const & x) : ObjectClass(x), Class(x), StageClass(x) {};
+ virtual ~AnimClass(void);
+
+ operator AnimType(void) const {return Class->Type;};
+
+ static void * operator new(size_t size);
+ static void * operator new(size_t , void * ptr) {return(ptr);};
+ static void operator delete(void *ptr);
+
+ /*---------------------------------------------------------------------
+ ** Member function prototypes.
+ */
+ static void Init(void);
+
+ void Attach_To(ObjectClass *obj);
+ void Make_Invisible(void) {IsInvisible = true;};
+ static void Do_Atom_Damage(HousesType ownerhouse, CELL cell);
+
+ virtual bool Can_Place_Here(COORDINATE ) const {return true;}
+ virtual bool Mark(MarkType mark=MARK_CHANGE);
+ virtual bool Render(bool forced) const;
+ virtual COORDINATE Center_Coord(void) const;
+ virtual COORDINATE Sort_Y(void) const;
+ virtual LayerType In_Which_Layer(void) const;
+ virtual ObjectTypeClass const & Class_Of(void) const {return *Class;};
+ virtual short const * Occupy_List(bool = false) const;
+ virtual short const * Overlap_List(void) const;
+ virtual void Draw_It(int x, int y, WindowNumberType window) const;
+ virtual void AI(void);
+ virtual void Detach(TARGET target, bool all);
+
+ /*
+ ** File I/O.
+ */
+ bool Load(Straw & file);
+ bool Save(FileClass & file);
+
+ /*
+ ** If this animation is attached to an object, then this points to that object. An
+ ** animation that is attached will follow that object as it moves. This is important
+ ** for animations such as flames and smoke.
+ */
+ TARGET xObject;
+
+ /*
+ ** If this animation has an owner, then it will be recorded here. An owner
+ ** is used when damage is caused by this animation during the middle of its
+ ** animation.
+ */
+ HousesType OwnerHouse;
+
+ /*
+ ** This counter tells how many more times the animation should loop before it
+ ** terminates.
+ */
+ unsigned char Loops;
+
+ protected:
+ void Middle(void);
+ void Start(void);
+
+ private:
+ /*
+ ** Delete this animation at the next opportunity. This is flagged when the
+ ** animation is to be prematurely ended as a result of some outside event.
+ */
+ unsigned IsToDelete:1;
+
+ /*
+ ** If the animation has just been created, then don't do any animation
+ ** processing until it has been through the render loop at least once.
+ */
+ unsigned IsBrandNew:1;
+
+ /*
+ ** If this animation is invisible, then this flag will be true. An invisible
+ ** animation is one that is created for the sole purpose of keeping all
+ ** machines synchronized. It will not be displayed.
+ */
+ unsigned IsInvisible:1;
+
+ /*
+ ** Is this animation in a temporary suspended state? If so, then it won't
+ ** be rendered until this value is zero. The flag will be set to false
+ ** after the first countdown timer reaches 0.
+ */
+ int Delay;
+
+ /*
+ ** If this is an animation that damages whatever it is attached to, then this
+ ** value holds the accumulation of fractional damage points. When the accumulated
+ ** fractions reach 256, then one damage point is applied to the attached object.
+ */
+ fixed Accum;
+};
+
+
+
+#endif
diff --git a/CODE/AUDIO.CPP b/CODE/AUDIO.CPP
new file mode 100644
index 0000000..9244e09
--- /dev/null
+++ b/CODE/AUDIO.CPP
@@ -0,0 +1,763 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/AUDIO.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : AUDIO.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : September 10, 1993 *
+ * *
+ * Last Update : November 1, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * Is_Speaking -- Checks to see if the eva voice is still playing. *
+ * Sound_Effect -- General purpose sound player. *
+ * Sound_Effect -- Plays a sound effect in the tactical map. *
+ * Speak -- Computer speaks to the player. *
+ * Speak_AI -- Handles starting the EVA voices. *
+ * Speech_Name -- Fetches the name for the voice specified. *
+ * Stop_Speaking -- Forces the EVA voice to stop talking. *
+ * Voc_From_Name -- Fetch VocType from ASCII name specified. *
+ * Voc_Name -- Fetches the name for the sound effect. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+
+
+/***************************************************************************
+** Controls what special effects may occur on the sound effect.
+*/
+typedef enum {
+ IN_NOVAR, // No variation or alterations allowed.
+ IN_VAR // Infantry variance response modification.
+} ContextType;
+
+static struct {
+ char const * Name; // Digitized voice file name.
+ int Priority; // Playback priority of this sample.
+ ContextType Where; // In what game context does this sample exist.
+} SoundEffectName[VOC_COUNT] = {
+
+ /*
+ ** Civilian voices (technicians too).
+ */
+ {"GIRLOKAY", 20, IN_NOVAR}, // VOC_GIRL_OKAY
+ {"GIRLYEAH", 20, IN_NOVAR}, // VOC_GIRL_YEAH
+ {"GUYOKAY1", 20, IN_NOVAR}, // VOC_GUY_OKAY
+ {"GUYYEAH1", 20, IN_NOVAR}, // VOC_GUY_YEAH
+
+ {"MINELAY1", 5, IN_VAR}, // VOC_MINELAY1
+
+ /*
+ ** Infantry and vehicle responses.
+ */
+ {"ACKNO", 20, IN_VAR}, // VOC_ACKNOWL "acknowledged"
+ {"AFFIRM1", 20, IN_VAR}, // VOC_AFFIRM "affirmative"
+ {"AWAIT1", 20, IN_VAR}, // VOC_AWAIT1 "awaiting orders"
+ {"EAFFIRM1", 20, IN_NOVAR}, // VOC_ENG_AFFIRM Engineer: "affirmative"
+ {"EENGIN1", 20, IN_NOVAR}, // VOC_ENG_ENG Engineer: "engineering"
+ {"NOPROB", 20, IN_VAR}, // VOC_NO_PROB "not a problem"
+ {"READY", 20, IN_VAR}, // VOC_READY "ready and waiting"
+ {"REPORT1", 20, IN_VAR}, // VOC_REPORT "reporting"
+ {"RITAWAY", 20, IN_VAR}, // VOC_RIGHT_AWAY "right away sir"
+ {"ROGER", 20, IN_VAR}, // VOC_ROGER "roger"
+ {"UGOTIT", 20, IN_VAR}, // VOC_UGOTIT "you got it"
+ {"VEHIC1", 20, IN_VAR}, // VOC_VEHIC1 "vehicle reporting"
+ {"YESSIR1", 20, IN_VAR}, // VOC_YESSIR "yes sir"
+
+ {"DEDMAN1", 10, IN_NOVAR}, // VOC_SCREAM1 short infantry scream
+ {"DEDMAN2", 10, IN_NOVAR}, // VOC_SCREAM3 short infantry scream
+ {"DEDMAN3", 10, IN_NOVAR}, // VOC_SCREAM4 short infantry scream
+ {"DEDMAN4", 10, IN_NOVAR}, // VOC_SCREAM5 short infantry scream
+ {"DEDMAN5", 10, IN_NOVAR}, // VOC_SCREAM6 short infantry scream
+ {"DEDMAN6", 10, IN_NOVAR}, // VOC_SCREAM7 short infantry scream
+ {"DEDMAN7", 10, IN_NOVAR}, // VOC_SCREAM10 short infantry scream
+ {"DEDMAN8", 10, IN_NOVAR}, // VOC_SCREAM11 short infantry scream
+ {"DEDMAN10", 10, IN_NOVAR}, // VOC_YELL1 long infantry scream
+
+ {"CHRONO2", 5, IN_NOVAR}, // VOC_CHRONO Chronosphere sound
+ {"CANNON1", 1, IN_NOVAR}, // VOC_CANNON1 Cannon sound (medium).
+ {"CANNON2", 1, IN_NOVAR}, // VOC_CANNON2 Cannon sound (short).
+ {"IRONCUR9", 10, IN_NOVAR}, // VOC_IRON1
+ {"EMOVOUT1", 20, IN_NOVAR}, // VOC_ENG_MOVEOUT Engineer: "movin' out"
+ {"SONPULSE", 10, IN_NOVAR}, // VOC_SONAR
+ {"SANDBAG2", 5, IN_NOVAR}, // VOC_SANDBAG sand bag crunch
+ {"MINEBLO1", 5, IN_NOVAR}, // VOC_MINEBLOW weird mine explosion
+ {"CHUTE1", 1, IN_NOVAR}, // VOC_CHUTE1 Wind swoosh sound.
+ {"DOGY1", 5, IN_NOVAR}, // VOC_DOG_BARK Dog bark.
+ {"DOGW5", 10, IN_NOVAR}, // VOC_DOG_WHINE Dog whine.
+ {"DOGG5P", 10, IN_NOVAR}, // VOC_DOG_GROWL2 Strong dog growl.
+ {"FIREBL3", 1, IN_NOVAR}, // VOC_FIRE_LAUNCH Fireball launch sound.
+ {"FIRETRT1", 1, IN_NOVAR}, // VOC_FIRE_EXPLODE Fireball explode sound.
+ {"GRENADE1", 1, IN_NOVAR}, // VOC_GRENADE_TOSS Grenade toss.
+ {"GUN11", 1, IN_NOVAR}, // VOC_GUN_5 5 round gun burst (slow).
+ {"GUN13", 1, IN_NOVAR}, // VOC_GUN_7 7 round gun burst (fast).
+ {"EYESSIR1", 20, IN_NOVAR}, // VOC_ENG_YES, Engineer: "yes sir"
+ {"GUN27", 1, IN_NOVAR}, // VOC_GUN_RIFLE Rifle shot.
+ {"HEAL2", 1, IN_NOVAR}, // VOC_HEAL Healing effect.
+ {"HYDROD1", 1, IN_NOVAR}, // VOC_DOOR Hyrdrolic door.
+ {"INVUL2", 1, IN_NOVAR}, // VOC_INVULNERABLE Invulnerability effect.
+ {"KABOOM1", 1, IN_NOVAR}, // VOC_KABOOM1 Long explosion (muffled).
+ {"KABOOM12", 1, IN_NOVAR}, // VOC_KABOOM12 Very long explosion (muffled).
+ {"KABOOM15", 1, IN_NOVAR}, // VOC_KABOOM15 Very long explosion (muffled).
+ {"SPLASH9", 5, IN_NOVAR}, // VOC_SPLASH water splash
+ {"KABOOM22", 1, IN_NOVAR}, // VOC_KABOOM22 Long explosion (sharp).
+ {"AACANON3", 1, IN_NOVAR},
+ {"TANDETH1", 10, IN_NOVAR},
+ {"MGUNINF1", 1, IN_NOVAR}, // VOC_GUN_5F 5 round gun burst (fast).
+ {"MISSILE1", 1, IN_NOVAR}, // VOC_MISSILE_1 Missile with high tech effect.
+ {"MISSILE6", 1, IN_NOVAR}, // VOC_MISSILE_2 Long missile launch.
+ {"MISSILE7", 1, IN_NOVAR}, // VOC_MISSILE_3 Short missile launch.
+ {"x", 1, IN_NOVAR},
+ {"PILLBOX1", 1, IN_NOVAR}, // VOC_GUN_5R 5 round gun burst (rattles).
+ {"RABEEP1", 1, IN_NOVAR}, // VOC_BEEP Generic beep sound.
+ {"RAMENU1", 1, IN_NOVAR}, // VOC_CLICK Generic click sound.
+ {"SILENCER", 1, IN_NOVAR}, // VOC_SILENCER Silencer.
+ {"TANK5", 1, IN_NOVAR}, // VOC_CANNON6 Long muffled cannon shot.
+ {"TANK6", 1, IN_NOVAR}, // VOC_CANNON7 Sharp mechanical cannon fire.
+ {"TORPEDO1", 1, IN_NOVAR}, // VOC_TORPEDO Torpedo launch.
+ {"TURRET1", 1, IN_NOVAR}, // VOC_CANNON8 Sharp cannon fire.
+ {"TSLACHG2", 10, IN_NOVAR}, // VOC_TESLA_POWER_UP Hum charge up.
+ {"TESLA1", 10, IN_NOVAR}, // VOC_TESLA_ZAP Tesla zap effect.
+ {"SQUISHY2", 10, IN_NOVAR}, // VOC_SQUISH Squish effect.
+ {"SCOLDY1", 10, IN_NOVAR}, // VOC_SCOLD Scold bleep.
+ {"RADARON2", 20, IN_NOVAR}, // VOC_RADAR_ON Powering up electronics.
+ {"RADARDN1", 10, IN_NOVAR}, // VOC_RADAR_OFF B movie power down effect.
+ {"PLACBLDG", 10, IN_NOVAR}, // VOC_PLACE_BUILDING_DOWN Building slam down sound.
+ {"KABOOM30", 1, IN_NOVAR}, // VOC_KABOOM30 Short explosion (HE).
+ {"KABOOM25", 10, IN_NOVAR}, // VOC_KABOOM25 Short growling explosion.
+ {"x", 10, IN_NOVAR},
+ {"DOGW7", 10, IN_NOVAR}, // VOC_DOG_HURT Dog whine (loud).
+ {"DOGW3PX", 10, IN_NOVAR}, // VOC_DOG_YES Dog 'yes sir'.
+ {"CRMBLE2", 10, IN_NOVAR}, // VOC_CRUMBLE Building crumble.
+ {"CASHUP1", 10, IN_NOVAR}, // VOC_MONEY_UP Rising money tick.
+ {"CASHDN1", 10, IN_NOVAR}, // VOC_MONEY_DOWN Falling money tick.
+ {"BUILD5", 10, IN_NOVAR}, // VOC_CONSTRUCTION Building construction sound.
+ {"BLEEP9", 10, IN_NOVAR}, // VOC_GAME_CLOSED Long bleep.
+ {"BLEEP6", 10, IN_NOVAR}, // VOC_INCOMING_MESSAGE Soft happy warble.
+ {"BLEEP5", 10, IN_NOVAR}, // VOC_SYS_ERROR Sharp soft warble.
+ {"BLEEP17", 10, IN_NOVAR}, // VOC_OPTIONS_CHANGED Mid range soft warble.
+ {"BLEEP13", 10, IN_NOVAR}, // VOC_GAME_FORMING Long warble.
+ {"BLEEP12", 10, IN_NOVAR}, // VOC_PLAYER_LEFT Chirp sequence.
+ {"BLEEP11", 10, IN_NOVAR}, // VOC_PLAYER_JOINED Reverse chirp sequence.
+ {"H2OBOMB2", 10, IN_NOVAR}, // VOC_DEPTH_CHARGE Distant explosion sound.
+ {"CASHTURN", 10, IN_NOVAR}, // VOC_CASHTURN Airbrake.
+ {"TUFFGUY1", 20, IN_NOVAR}, // VOC_TANYA_CHEW Tanya: "Chew on this"
+ {"ROKROLL1", 20, IN_NOVAR}, // VOC_TANYA_ROCK Tanya: "Let's rock"
+ {"LAUGH1", 20, IN_NOVAR}, // VOC_TANYA_LAUGH Tanya: "ha ha ha"
+ {"CMON1", 20, IN_NOVAR}, // VOC_TANYA_SHAKE Tanya: "Shake it baby"
+ {"BOMBIT1", 20, IN_NOVAR}, // VOC_TANYA_CHING Tanya: "Cha Ching"
+ {"GOTIT1", 20, IN_NOVAR}, // VOC_TANYA_GOT Tanya: "That's all you got"
+ {"KEEPEM1", 20, IN_NOVAR}, // VOC_TANYA_KISS Tanya: "Kiss it bye bye"
+ {"ONIT1", 20, IN_NOVAR}, // VOC_TANYA_THERE Tanya: "I'm there"
+ {"LEFTY1", 20, IN_NOVAR}, // VOC_TANYA_GIVE Tanya: "Give it to me"
+ {"YEAH1", 20, IN_NOVAR}, // VOC_TANYA_YEA Tanya: "Yea?"
+ {"YES1", 20, IN_NOVAR}, // VOC_TANYA_YES Tanya: "Yes sir?"
+ {"YO1", 20, IN_NOVAR}, // VOC_TANYA_WHATS Tanya: "What's up."
+ {"WALLKIL2", 5, IN_NOVAR}, // VOC_WALLKILL2 Crushing wall sound.
+ {"x", 10, IN_NOVAR},
+ {"GUN5", 5, IN_NOVAR}, // VOC_TRIPLE_SHOT Three quick shots in succession.
+ {"SUBSHOW1", 5, IN_NOVAR}, // VOC_SUBSHOW Submarine surface sound.
+ {"EINAH1", 20, IN_NOVAR}, // VOC_E_AH, Einstien "ah"
+ {"EINOK1", 20, IN_NOVAR}, // VOC_E_OK, Einstien "ok"
+ {"EINYES1", 20, IN_NOVAR}, // VOC_E_YES, Einstien "yes"
+ {"MINE1", 10, IN_NOVAR}, // VOC_TRIP_MINE mine explosion sound
+
+ {"SCOMND1", 20, IN_NOVAR}, // VOC_SPY_COMMANDER Spy: "commander?"
+ {"SYESSIR1", 20, IN_NOVAR}, // VOC_SPY_YESSIR Spy: "yes sir"
+ {"SINDEED1", 20, IN_NOVAR}, // VOC_SPY_INDEED Spy: "indeed"
+ {"SONWAY1", 20, IN_NOVAR}, // VOC_SPY_ONWAY Spy: "on my way"
+ {"SKING1", 20, IN_NOVAR}, // VOC_SPY_KING Spy: "for king and country"
+ {"MRESPON1", 20, IN_NOVAR}, // VOC_MED_REPORTING Medic: "reporting"
+ {"MYESSIR1", 20, IN_NOVAR}, // VOC_MED_YESSIR Medic: "yes sir"
+ {"MAFFIRM1", 20, IN_NOVAR}, // VOC_MED_AFFIRM Medic: "affirmative"
+ {"MMOVOUT1", 20, IN_NOVAR}, // VOC_MED_MOVEOUT Medic: "movin' out"
+ {"BEEPSLCT", 10, IN_NOVAR}, // VOC_BEEP_SELECT map selection beep
+
+ {"SYEAH1", 20, IN_NOVAR}, // VOC_THIEF_YEA Thief: "yea?"
+ {"ANTDIE", 20, IN_NOVAR}, // VOC_ANTDIE
+ {"ANTBITE", 20, IN_NOVAR}, // VOC_ANTBITE
+ {"SMOUT1", 20, IN_NOVAR}, // VOC_THIEF_MOVEOUT Thief: "movin' out"
+ {"SOKAY1", 20, IN_NOVAR}, // VOC_THIEF_OKAY Thief: "ok"
+ {"x", 20, IN_NOVAR},
+ {"SWHAT1", 20, IN_NOVAR}, // VOC_THIEF_WHAT Thief: "what"
+ {"SAFFIRM1", 20, IN_NOVAR}, // VOC_THIEF_AFFIRM Thief: "affirmative"
+//ADDED VG 2/24/97
+ {"STAVCMDR", 20, IN_NOVAR},
+ {"STAVCRSE", 20, IN_NOVAR},
+ {"STAVYES", 20, IN_NOVAR},
+ {"STAVMOV", 20, IN_NOVAR},
+ {"BUZZY1", 20, IN_NOVAR},
+ {"RAMBO1", 20, IN_NOVAR},
+ {"RAMBO2", 20, IN_NOVAR},
+ {"RAMBO3", 20, IN_NOVAR},
+#ifdef FIXIT_CSII // checked - ajw 9/28/98
+ {"MYES1", 20, IN_NOVAR}, // VOC_MECHYES1 Mechanic: "Yes sir!"
+ {"MHOWDY1", 20, IN_NOVAR}, // VOC_MECHHOWDY1 Mechanic: "Howdy!"
+ {"MRISE1", 20, IN_NOVAR}, // VOC_MECHRISE1 Mechanic: "Rise 'n shine!"
+ {"MHUH1", 20, IN_NOVAR}, // VOC_MECHHUH1 Mechanic: "Huh?"
+ {"MHEAR1", 20, IN_NOVAR}, // VOC_MECHHEAR1 Mechanic: "I Hear Ya!"
+ {"MLAFF1", 20, IN_NOVAR}, // VOC_MECHLAFF1 Mechanic: guffaw
+ {"MBOSS1", 20, IN_NOVAR}, // VOC_MECHBOSS1 Mechanic: "Sure Thing, Boss!"
+ {"MYEEHAW1", 20, IN_NOVAR}, // VOC_MECHYEEHAW1 Mechanic: "Yee Haw!"
+ {"MHOTDIG1", 20, IN_NOVAR}, // VOC_MECHHOTDIG1 Mechanic: "Hot Diggity Dog!"
+ {"MWRENCH1", 20, IN_NOVAR}, // VOC_MECHWRENCH1 Mechanic: "I'll get my wrench."
+
+ {"JBURN1", 20, IN_NOVAR}, // VOC_STBURN1 Shock Trooper: "Burn baby burn!"
+ {"JCHRGE1", 20, IN_NOVAR}, // VOC_STCHRGE1 Shock Trooper: "Fully charged!"
+ {"JCRISP1", 20, IN_NOVAR}, // VOC_STCRISP1 Shock Trooper: "Extra Crispy!"
+ {"JDANCE1", 20, IN_NOVAR}, // VOC_STDANCE1 Shock Trooper: "Let's Dance!"
+ {"JJUICE1", 20, IN_NOVAR}, // VOC_STJUICE1 Shock Trooper: "Got juice?"
+ {"JJUMP1", 20, IN_NOVAR}, // VOC_STJUMP1 Shock Trooper: "Need a jump?"
+ {"JLIGHT1", 20, IN_NOVAR}, // VOC_STLIGHT1 Shock Trooper: "Lights out!"
+ {"JPOWER1", 20, IN_NOVAR}, // VOC_STPOWER1 Shock Trooper: "Power on!"
+ {"JSHOCK1", 20, IN_NOVAR}, // VOC_STSHOCK1 Shock Trooper: "Shocking!"
+ {"JYES1", 20, IN_NOVAR}, // VOC_STYES1 Shock Trooper: "Yesssss!"
+
+ {"CHROTNK1", 20, IN_NOVAR}, // VOC_CHRONOTANK1 Chrono tank teleport
+ {"FIXIT1", 20, IN_NOVAR}, // VOC_MECH_FIXIT1 Mechanic fixes something
+ {"MADCHRG2", 20, IN_NOVAR}, // VOC_MAD_CHARGE MAD tank charges up
+ {"MADEXPLO", 20, IN_NOVAR}, // VOC_MAD_EXPLODE MAD tank explodes
+ {"SHKTROP1", 20, IN_NOVAR}, // VOC_SHOCK_TROOP1 Shock Trooper fires
+
+#endif
+};
+
+
+/***********************************************************************************************
+ * Voc_From_Name -- Fetch VocType from ASCII name specified. *
+ * *
+ * This will find the corresponding VocType from the ASCII string specified. It does this *
+ * by finding a root filename that matches the string. *
+ * *
+ * INPUT: name -- Pointer to the ASCII string that will be converted into a VocType. *
+ * *
+ * OUTPUT: Returns with the VocType that matches the string specified. If no match could be *
+ * found, then VOC_NONE is returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/06/1996 JLB : Created. *
+ *=============================================================================================*/
+VocType Voc_From_Name(char const * name)
+{
+ if (name == NULL) return(VOC_NONE);
+
+ for (VocType voc = VOC_FIRST; voc < VOC_COUNT; voc++) {
+ if (stricmp(name, SoundEffectName[voc].Name) == 0) {
+ return(voc);
+ }
+ }
+
+ return(VOC_NONE);
+}
+
+
+/***********************************************************************************************
+ * Voc_Name -- Fetches the name for the sound effect. *
+ * *
+ * This routine returns the descriptive name of the sound effect. Currently, this is just *
+ * the root of the file name. *
+ * *
+ * INPUT: voc -- The VocType that the corresponding name is requested. *
+ * *
+ * OUTPUT: Returns with a pointer to the text string the represents the sound effect. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/06/1996 JLB : Created. *
+ *=============================================================================================*/
+char const * Voc_Name(VocType voc)
+{
+ if (voc == VOC_NONE) return("none");
+ return(SoundEffectName[voc].Name);
+}
+
+
+/***********************************************************************************************
+ * Sound_Effect -- Plays a sound effect in the tactical map. *
+ * *
+ * This routine is used when a sound effect occurs in the game world. It handles fading *
+ * the sound according to distance. *
+ * *
+ * INPUT: voc -- The sound effect number to play. *
+ * *
+ * coord -- The world location that the sound originates from. *
+ * *
+ * variation -- This is the optional variation number to use when playing special *
+ * sound effects that have variations. For normal sound effects, this *
+ * parameter is ignored. *
+ * *
+ * house -- This specifies the optional house override value to use when playing *
+ * sound effects that have a variation. If not specified, then the current *
+ * player is examined for the house variation to use. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 11/12/1994 JLB : Created. *
+ * 01/05/1995 JLB : Reduces sound more dramatically when off screen. *
+ * 09/15/1996 JLB : Revamped volume logic. *
+ * 11/01/1996 JLB : House override control. *
+ *=============================================================================================*/
+void Sound_Effect(VocType voc, COORDINATE coord, int variation, HousesType house)
+{
+ CELL cell_pos = 0;
+ int pan_value;
+
+ if (Debug_Quiet || Options.Volume == 0 || voc == VOC_NONE || !SoundOn || SampleType == SAMPLE_NONE) {
+ return;
+ }
+ if (coord) {
+ cell_pos = Coord_Cell(coord);
+ }
+
+ fixed volume = 1;
+ pan_value = 0;
+ if (coord && !Map.In_View(cell_pos)) {
+ int distance = Distance(coord, Map.TacticalCoord) / CELL_LEPTON_W;
+ fixed dfixed = fixed(distance, 128+64);
+ dfixed.Sub_Saturate(1);
+ volume = fixed(1) - dfixed;
+
+ pan_value = Cell_X(cell_pos);
+ pan_value -= Coord_XCell(Map.TacticalCoord) + (Lepton_To_Cell(Map.TacLeptonWidth) / 2);
+ if (ABS(pan_value) > Lepton_To_Cell(Map.TacLeptonWidth / 2)) {
+ pan_value *= 0x8000;
+ pan_value /= (MAP_CELL_W >> 2);
+ pan_value = Bound(pan_value, -0x7FFF, 0x7FFF);
+ } else {
+ pan_value = 0;
+ }
+ }
+
+ Sound_Effect(voc, volume, variation, pan_value, house);
+}
+
+
+/***********************************************************************************************
+ * Sound_Effect -- General purpose sound player. *
+ * *
+ * This is used for general purpose sound effects. These are sounds that occur outside *
+ * of the game world. They do not have a corresponding game world location as their source. *
+ * *
+ * INPUT: voc -- The sound effect number to play. *
+ * *
+ * volume -- The volume to assign to this sound effect. *
+ * *
+ * variation -- This is the optional variation number to use when playing special *
+ * sound effects that have variations. For normal sound effects, this *
+ * parameter is ignored. *
+ * *
+ * house -- This specifies the optional house override value to use when playing *
+ * sound effects that have a variation. If not specified, then the current *
+ * player is examined for the house variation to use. *
+ * *
+ * OUTPUT: Returns with the sound handle (-1 if no sound was played). *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 11/12/1994 JLB : Created. *
+ * 11/12/1994 JLB : Handles cache logic. *
+ * 05/04/1995 JLB : Variation adjustments. *
+ * 11/01/1996 JLB : House override control. *
+ *=============================================================================================*/
+int Sound_Effect(VocType voc, fixed volume, int variation, signed short pan_value, HousesType house)
+{
+ char name[_MAX_FNAME+_MAX_EXT]; // Working filename of sound effect.
+
+ if (Debug_Quiet || Options.Volume == 0 || voc == VOC_NONE || !SoundOn || SampleType == SAMPLE_NONE) {
+ return(-1);
+ }
+
+ /*
+ ** Alter the volume according to the game volume setting.
+ */
+ volume = volume * Options.Volume;
+
+ /*
+ ** Fetch a pointer to the sound effect data. Modify the sound as appropriate and desired.
+ */
+ char const * ext = ".AUD";
+ if (SoundEffectName[voc].Where == IN_VAR) {
+
+ /*
+ ** If there is no forced house, then use the current player
+ ** act like house.
+ */
+ if (house == HOUSE_NONE) {
+ house = PlayerPtr->ActLike;
+ }
+
+ /*
+ ** Change the extension based on the variation and house accent requested.
+ */
+ if (((1 << house) & HOUSEF_ALLIES) != 0) {
+
+ /*
+ ** For infantry, use a variation on the response. For vehicles, always
+ ** use the vehicle response table.
+ */
+ if (variation < 0) {
+ if (ABS(variation) % 2) {
+ ext = ".V00";
+ } else {
+ ext = ".V02";
+ }
+ } else {
+ if (variation % 2) {
+ ext = ".V01";
+ } else {
+ ext = ".V03";
+ }
+ }
+ } else {
+ if (variation < 0) {
+ if (ABS(variation) % 2) {
+ ext = ".R00";
+ } else {
+ ext = ".R02";
+ }
+ } else {
+ if (variation % 2) {
+ ext = ".R01";
+ } else {
+ ext = ".R03";
+ }
+ }
+ }
+ }
+ _makepath(name, NULL, NULL, SoundEffectName[voc].Name, ext);
+ void const * ptr = MFCD::Retrieve(name);
+
+ /*
+ ** If the sound data pointer is not null, then presume that it is valid.
+ */
+ if (ptr != NULL) {
+ volume.Sub_Saturate(1);
+ return(Play_Sample(ptr, SoundEffectName[voc].Priority * volume, volume*256, pan_value));
+ }
+ return(-1);
+}
+
+
+/*
+** This elaborates all the EVA speech voices.
+*/
+static char const * Speech[VOX_COUNT] = {
+ "MISNWON1", // VOX_ACCOMPLISHED mission accomplished
+ "MISNLST1", // VOX_FAIL your mission has failed
+ "PROGRES1", // VOX_NO_FACTORY unable to comply, building in progress
+ "CONSCMP1", // VOX_CONSTRUCTION construction complete
+ "UNITRDY1", // VOX_UNIT_READY unit ready
+ "NEWOPT1", // VOX_NEW_CONSTRUCT new construction options
+ "NODEPLY1", // VOX_DEPLOY cannot deploy here
+ "STRCKIL1", // VOX_STRUCTURE_DESTROYED, structure destroyed
+ "NOPOWR1", // VOX_INSUFFICIENT_POWER, insufficient power
+ "NOFUNDS1", // VOX_NO_CASH insufficient funds
+ "BCT1", // VOX_CONTROL_EXIT battle control terminated
+ "REINFOR1", // VOX_REINFORCEMENTS reinforcements have arrived
+ "CANCLD1", // VOX_CANCELED canceled
+ "ABLDGIN1", // VOX_BUILDING building
+ "LOPOWER1", // VOX_LOW_POWER low power
+ "NOFUNDS1", // VOX_NEED_MO_MONEY insufficent funds
+ "BASEATK1", // VOX_BASE_UNDER_ATTACK our base is under attack
+ "NOBUILD1", // VOX_UNABLE_TO_BUILD unable to build more
+ "PRIBLDG1", // VOX_PRIMARY_SELECTED primary building selected
+#ifdef FIXIT_CSII // checked - ajw 9/28/98
+#ifdef ENGLISH
+ "TANK01", // VOX_MADTANK_DEPLOYED M.A.D. Tank Deployed
+#else
+ "none",
+#endif
+#else
+ "none",
+#endif
+ "none", // VOX_SOVIET_CAPTURED Allied building captured
+ "UNITLST1", // VOX_UNIT_LOST unit lost
+ "SLCTTGT1", // VOX_SELECT_TARGET select target
+ "ENMYAPP1", // VOX_PREPARE enemy approaching
+ "SILOND1", // VOX_NEED_MO_CAPACITY silos needed
+ "ONHOLD1", // VOX_SUSPENDED on hold
+ "REPAIR1", // VOX_REPAIRING repairing
+ "none",
+ "none",
+ "AUNITL1", // VOX_AIRCRAFT_LOST airborne unit lost
+ "none",
+ "AAPPRO1", // VOX_ALLIED_FORCES_APPROACHING allied forces approaching
+ "AARRIVE1", // VOX_ALLIED_APPROACHING allied reinforcements have arrived
+ "none",
+ "none",
+ "BLDGINF1", // VOX_BUILDING_INFILTRATED building infiltrated
+ "CHROCHR1", // VOX_CHRONO_CHARGING chronosphere charging
+ "CHRORDY1", // VOX_CHRONO_READY chronosphere ready
+ "CHROYES1", // VOX_CHRONO_TEST chronosphere test successful
+ "CMDCNTR1", // VOX_HQ_UNDER_ATTACK command center under attack
+ "CNTLDED1", // VOX_CENTER_DEACTIVATED control center deactivated
+ "CONVYAP1", // VOX_CONVOY_APPROACHING convoy approaching
+ "CONVLST1", // VOX_CONVOY_UNIT_LOST convoy unit lost
+ "XPLOPLC1", // VOX_EXPLOSIVE_PLACED explosive charge placed
+ "CREDIT1", // VOX_MONEY_STOLEN credits stolen
+ "NAVYLST1", // VOX_SHIP_LOST naval unit lost
+ "SATLNCH1", // VOX_SATALITE_LAUNCHED satalite launched
+ "PULSE1", // VOX_SONAR_AVAILABLE sonar pulse available
+ "none",
+ "SOVFAPP1", // VOX_SOVIET_FORCES_APPROACHING soviet forces approaching
+ "SOVREIN1", // VOX_SOVIET_REINFROCEMENTS soviet reinforcements have arrived
+ "TRAIN1", // VOX_TRAINING training
+ "AREADY1", // VOX_ABOMB_READY
+ "ALAUNCH1", // VOX_ABOMB_LAUNCH
+ "AARRIVN1", // VOX_ALLIES_N
+ "AARRIVS1", // VOX_ALLIES_S
+ "AARIVE1", // VOX_ALLIES_E
+ "AARRIVW1", // VOX_ALLIES_W
+ "1OBJMET1", // VOX_OBJECTIVE1
+ "2OBJMET1", // VOX_OBJECTIVE2
+ "3OBJMET1", // VOX_OBJECTIVE3
+ "IRONCHG1", // VOX_IRON_CHARGING
+ "IRONRDY1", // VOX_IRON_READY
+ "KOSYRES1", // VOX_RESCUED
+ "OBJNMET1", // VOX_OBJECTIVE_NOT
+ "FLAREN1", // VOX_SIGNAL_N
+ "FLARES1", // VOX_SIGNAL_S
+ "FLAREE1", // VOX_SIGNAL_E
+ "FLAREW1", // VOX_SIGNAL_W
+ "SPYPLN1", // VOX_SPY_PLANE
+ "TANYAF1", // VOX_FREED
+ "ARMORUP1", // VOX_UPGRADE_ARMOR
+ "FIREPO1", // VOX_UPGRADE_FIREPOWER
+ "UNITSPD1", // VOX_UPGRADE_SPEED
+ "MTIMEIN1", // VOX_MISSION_TIMER
+ "UNITFUL1", // VOX_UNIT_FULL
+ "UNITREP1", // VOX_UNIT_REPAIRED
+ "40MINR", // VOX_TIME_40
+ "30MINR", // VOX_TIME_30
+ "20MINR", // VOX_TIME_20
+ "10MINR", // VOX_TIME_10
+ "5MINR", // VOX_TIME_5
+ "4MINR", // VOX_TIME_4
+ "3MINR", // VOX_TIME_3
+ "2MINR", // VOX_TIME_2
+ "1MINR", // VOX_TIME_1
+ "TIMERNO1", // VOX_TIME_STOP
+ "UNITSLD1", // VOX_UNIT_SOLD
+ "TIMERGO1", // VOX_TIMER_STARTED
+ "TARGRES1", // VOX_TARGET_RESCUED
+ "TARGFRE1", // VOX_TARGET_FREED
+ "TANYAR1", // VOX_TANYA_RESCUED
+ "STRUSLD1", // VOX_STRUCTURE_SOLD
+ "SOVFORC1", // VOX_SOVIET_FORCES_FALLEN
+ "SOVEMP1", // VOX_SOVIET_SELECTED
+ "SOVEFAL1", // VOX_SOVIET_EMPIRE_FALLEN
+ "OPTERM1", // VOX_OPERATION_TERMINATED
+ "OBJRCH1", // VOX_OBJECTIVE_REACHED
+ "OBJNRCH1", // VOX_OBJECTIVE_NOT_REACHED
+ "OBJMET1", // VOX_OBJECTIVE_MET
+ "MERCR1", // VOX_MERCENARY_RESCUED
+ "MERCF1", // VOX_MERCENARY_FREED
+ "KOSYFRE1", // VOX_KOSOYGEN_FREED
+ "FLARE1", // VOX_FLARE_DETECTED
+ "COMNDOR1", // VOX_COMMANDO_RESCUED
+ "COMNDOF1", // VOX_COMMANDO_FREED
+ "BLDGPRG1", // VOX_BUILDING_IN_PROGRESS
+ "ATPREP1", // VOX_ATOM_PREPPING
+ "ASELECT1", // VOX_ALLIED_SELECTED
+ "APREP1", // VOX_ABOMB_PREPPING
+ "ATLNCH1", // VOX_ATOM_LAUNCHED
+ "AFALLEN1", // VOX_ALLIED_FORCES_FALLEN
+ "AAVAIL1", // VOX_ABOMB_AVAILABLE
+ "AARRIVE1", // VOX_ALLIED_REINFORCEMENTS
+ "SAVE1", // VOX_MISSION_SAVED
+ "LOAD1" // VOX_MISSION_LOADED
+};
+
+
+static VoxType CurrentVoice = VOX_NONE;
+
+
+/***********************************************************************************************
+ * Speech_Name -- Fetches the name for the voice specified. *
+ * *
+ * Use this routine to fetch the ASCII name of the speech id specified. Typical use of this *
+ * would be to build a displayable list of the speech types. The trigger system uses this *
+ * so that a speech type can be selected. *
+ * *
+ * INPUT: speech -- The speech type id to convert to ASCII string. *
+ * *
+ * OUTPUT: Returns with a pointer to the speech ASCII representation of the speech id type. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/01/1996 JLB : Created. *
+ *=============================================================================================*/
+char const * Speech_Name(VoxType speech)
+{
+ if (speech == VOX_NONE) return("none");
+ return(Speech[speech]);
+}
+
+
+/***********************************************************************************************
+ * Speak -- Computer speaks to the player. *
+ * *
+ * This routine is used to have the game computer (EVA) speak to the player. *
+ * *
+ * INPUT: voice -- The voice number to speak (see defines.h). *
+ * *
+ * OUTPUT: Returns with the handle of the playing speech (-1 if no voice started). *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 11/12/1994 JLB : Created. *
+ *=============================================================================================*/
+void Speak(VoxType voice)
+{
+ if (!Debug_Quiet && Options.Volume != 0 && SampleType != 0 && voice != VOX_NONE && voice != SpeakQueue && voice != CurrentVoice && SpeakQueue == VOX_NONE) {
+ SpeakQueue = voice;
+ Speak_AI();
+ }
+}
+
+
+/***********************************************************************************************
+ * Speak_AI -- Handles starting the EVA voices. *
+ * *
+ * This starts the EVA voice talking as well. If there is any speech request in the queue, *
+ * it will be started when the current voice is finished. Call this routine as often as *
+ * possible (once per game tick is sufficient). *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 12/27/1994 JLB : Created. *
+ * 10/11/1996 JLB : Handles multiple speech buffers. *
+ *=============================================================================================*/
+void Speak_AI(void)
+{
+ static int _index = 0;
+ if (Debug_Quiet || SampleType == 0) return;
+
+ if (!Is_Sample_Playing(SpeechBuffer[_index])) {
+ CurrentVoice = VOX_NONE;
+ if (SpeakQueue != VOX_NONE) {
+
+ /*
+ ** Try to find a previously loaded copy of the EVA speech in one of the
+ ** speech buffers.
+ */
+ void const * speech = NULL;
+ for (int index = 0; index < ARRAY_SIZE(SpeechRecord); index++) {
+ if (SpeechRecord[index] == SpeakQueue) break;
+ }
+
+ /*
+ ** If a previous copy could not be located, then load the requested
+ ** voice into the oldest buffer available.
+ */
+ if (speech == NULL) {
+ _index = (_index + 1) % ARRAY_SIZE(SpeechRecord);
+
+ char name[_MAX_FNAME+_MAX_EXT];
+
+ _makepath(name, NULL, NULL, Speech[SpeakQueue], ".AUD");
+ CCFileClass file(name);
+ if (file.Is_Available() && file.Read(SpeechBuffer[_index], SPEECH_BUFFER_SIZE)) {
+ speech = SpeechBuffer[_index];
+ SpeechRecord[_index] = SpeakQueue;
+ }
+ }
+
+ /*
+ ** Since the speech file was loaded, play it.
+ */
+ if (speech != NULL) {
+ Play_Sample(speech, 254, Options.Volume * 256);
+ CurrentVoice = SpeakQueue;
+ }
+
+ SpeakQueue = VOX_NONE;
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * Stop_Speaking -- Forces the EVA voice to stop talking. *
+ * *
+ * Use this routine to immediately stop the EVA voice from speaking. It also clears out *
+ * the pending voice queue. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 12/27/1994 JLB : Created. *
+ *=============================================================================================*/
+void Stop_Speaking(void)
+{
+ SpeakQueue = VOX_NONE;
+ Stop_Sample_Playing(SpeechBuffer);
+}
+
+
+/***********************************************************************************************
+ * Is_Speaking -- Checks to see if the eva voice is still playing. *
+ * *
+ * Call this routine when the EVA voice being played needs to be checked. A typical use *
+ * of this would be when some action needs to be delayed until the voice has finished -- *
+ * say the end of the game. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: bool; Is the EVA voice still playing? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 03/12/1995 JLB : Created. *
+ *=============================================================================================*/
+bool Is_Speaking(void)
+{
+ Speak_AI();
+ if (!Debug_Quiet && SampleType != 0 && (SpeakQueue != VOX_NONE || Is_Sample_Playing(SpeechBuffer))) {
+ return(true);
+ }
+ return(false);
+}
diff --git a/CODE/AUDIO.CPP.BAK b/CODE/AUDIO.CPP.BAK
new file mode 100644
index 0000000..a653458
--- /dev/null
+++ b/CODE/AUDIO.CPP.BAK
@@ -0,0 +1,656 @@
+/*
+** Command & Conquer Red Alert(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&c0\vcs\code\audio.cpv 4.78 03 Oct 1996 09:20: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 : AUDIO.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : September 10, 1993 *
+ * *
+ * Last Update : September 15, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * Is_Speaking -- Checks to see if the eva voice is still playing. *
+ * Sound_Effect -- General purpose sound player. *
+ * Sound_Effect -- Plays a sound effect in the tactical map. *
+ * Speak -- Computer speaks to the player. *
+ * Speak_AI -- Handles starting the EVA voices. *
+ * Speech_Name -- Fetches the name for the voice specified. *
+ * Stop_Speaking -- Forces the EVA voice to stop talking. *
+ * Voc_From_Name -- Fetch VocType from ASCII name specified. *
+ * Voc_Name -- Fetches the name for the sound effect. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+
+
+/***************************************************************************
+** Controls what special effects may occur on the sound effect.
+*/
+typedef enum {
+ IN_NOVAR, // No variation or alterations allowed.
+ IN_VAR // Infantry variance response modification.
+} ContextType;
+
+static struct {
+ char const * Name; // Digitized voice file name.
+ int Priority; // Playback priority of this sample.
+ ContextType Where; // In what game context does this sample exist.
+} SoundEffectName[VOC_COUNT] = {
+
+ /*
+ ** Civilian voices (technicians too).
+ */
+ {"GIRLOKAY", 20, IN_NOVAR}, // VOC_GIRL_OKAY
+ {"GIRLYEAH", 20, IN_NOVAR}, // VOC_GIRL_YEAH
+ {"GUYOKAY1", 20, IN_NOVAR}, // VOC_GUY_OKAY
+ {"GUYYEAH1", 20, IN_NOVAR}, // VOC_GUY_YEAH
+
+ {"MINELAY1", 5, IN_VAR}, // VOC_MINELAY1
+
+ /*
+ ** Infantry and vehicle responses.
+ */
+ {"ACKNO", 20, IN_VAR}, // VOC_ACKNOWL "acknowledged"
+ {"AFFIRM1", 20, IN_VAR}, // VOC_AFFIRM "affirmative"
+ {"AWAIT1", 20, IN_VAR}, // VOC_AWAIT1 "awaiting orders"
+ {"EAFFIRM1", 20, IN_NOVAR}, // VOC_ENG_AFFIRM Engineer: "affirmative"
+ {"EENGIN1", 20, IN_VAR}, // VOC_ENG_ENG Engineer: "engineering"
+ {"NOPROB", 20, IN_VAR}, // VOC_NO_PROB "not a problem"
+ {"READY", 20, IN_VAR}, // VOC_READY "ready and waiting"
+ {"REPORT1", 20, IN_VAR}, // VOC_REPORT "reporting"
+ {"RITAWAY", 20, IN_VAR}, // VOC_RIGHT_AWAY "right away sir"
+ {"ROGER", 20, IN_VAR}, // VOC_ROGER "roger"
+ {"UGOTIT", 20, IN_VAR}, // VOC_UGOTIT "you got it"
+ {"VEHIC1", 20, IN_VAR}, // VOC_VEHIC1 "vehicle reporting"
+ {"YESSIR1", 20, IN_VAR}, // VOC_YESSIR "yes sir"
+
+ {"DEDMAN1", 10, IN_NOVAR}, // VOC_SCREAM1 short infantry scream
+ {"DEDMAN2", 10, IN_NOVAR}, // VOC_SCREAM3 short infantry scream
+ {"DEDMAN3", 10, IN_NOVAR}, // VOC_SCREAM4 short infantry scream
+ {"DEDMAN4", 10, IN_NOVAR}, // VOC_SCREAM5 short infantry scream
+ {"DEDMAN5", 10, IN_NOVAR}, // VOC_SCREAM6 short infantry scream
+ {"DEDMAN6", 10, IN_NOVAR}, // VOC_SCREAM7 short infantry scream
+ {"DEDMAN7", 10, IN_NOVAR}, // VOC_SCREAM10 short infantry scream
+ {"DEDMAN8", 10, IN_NOVAR}, // VOC_SCREAM11 short infantry scream
+ {"DEDMAN10", 10, IN_NOVAR}, // VOC_YELL1 long infantry scream
+
+ {"CHRONO2", 5, IN_NOVAR}, // VOC_CHRONO Chronosphere sound
+ {"CANNON1", 1, IN_NOVAR}, // VOC_CANNON1 Cannon sound (medium).
+ {"CANNON2", 1, IN_NOVAR}, // VOC_CANNON2 Cannon sound (short).
+ {"IRONCUR1", 5, IN_NOVAR}, // VOC_IRON1
+ {"EMOVOUT1", 20, IN_NOVAR}, // VOC_ENG_MOVEOUT Engineer: "movin' out"
+ {"IRONCUR2", 5, IN_NOVAR}, // VOC_IRON2
+ {"x", 1, IN_NOVAR},
+ {"x", 1, IN_NOVAR},
+ {"CHUTE1", 1, IN_NOVAR}, // VOC_CHUTE1 Wind swoosh sound.
+ {"DOGY1", 5, IN_NOVAR}, // VOC_DOG_BARK Dog bark.
+ {"DOGW5", 10, IN_NOVAR}, // VOC_DOG_WHINE Dog whine.
+ {"DOGG5P", 10, IN_NOVAR}, // VOC_DOG_GROWL2 Strong dog growl.
+ {"FIREBL3", 1, IN_NOVAR}, // VOC_FIRE_LAUNCH Fireball launch sound.
+ {"FIRETRT1", 1, IN_NOVAR}, // VOC_FIRE_EXPLODE Fireball explode sound.
+ {"GRENADE1", 1, IN_NOVAR}, // VOC_GRENADE_TOSS Grenade toss.
+ {"GUN11", 1, IN_NOVAR}, // VOC_GUN_5 5 round gun burst (slow).
+ {"GUN13", 1, IN_NOVAR}, // VOC_GUN_7 7 round gun burst (fast).
+ {"EYESSIR1", 20, IN_NOVAR}, // VOC_ENG_YES, Engineer: "yes sir"
+ {"GUN27", 1, IN_NOVAR}, // VOC_GUN_RIFLE Rifle shot.
+ {"HEAL2", 1, IN_NOVAR}, // VOC_HEAL Healing effect.
+ {"HYDROD1", 1, IN_NOVAR}, // VOC_DOOR Hyrdrolic door.
+ {"INVUL2", 1, IN_NOVAR}, // VOC_INVULNERABLE Invulnerability effect.
+ {"KABOOM1", 1, IN_NOVAR}, // VOC_KABOOM1 Long explosion (muffled).
+ {"KABOOM12", 1, IN_NOVAR}, // VOC_KABOOM12 Very long explosion (muffled).
+ {"KABOOM15", 1, IN_NOVAR}, // VOC_KABOOM15 Very long explosion (muffled).
+ {"x", 1, IN_NOVAR},
+ {"KABOOM22", 1, IN_NOVAR}, // VOC_KABOOM22 Long explosion (sharp).
+ {"x", 1, IN_NOVAR},
+ {"x", 1, IN_NOVAR},
+ {"MGUNINF1", 1, IN_NOVAR}, // VOC_GUN_5F 5 round gun burst (fast).
+ {"MISSILE1", 1, IN_NOVAR}, // VOC_MISSILE_1 Missile with high tech effect.
+ {"MISSILE6", 1, IN_NOVAR}, // VOC_MISSILE_2 Long missile launch.
+ {"MISSILE7", 1, IN_NOVAR}, // VOC_MISSILE_3 Short missile launch.
+ {"x", 1, IN_NOVAR},
+ {"PILLBOX1", 1, IN_NOVAR}, // VOC_GUN_5R 5 round gun burst (rattles).
+ {"RABEEP1", 1, IN_NOVAR}, // VOC_BEEP Generic beep sound.
+ {"RAMENU1", 1, IN_NOVAR}, // VOC_CLICK Generic click sound.
+ {"SILENCER", 1, IN_NOVAR}, // VOC_SILENCER Silencer.
+ {"TANK5", 1, IN_NOVAR}, // VOC_CANNON6 Long muffled cannon shot.
+ {"TANK6", 1, IN_NOVAR}, // VOC_CANNON7 Sharp mechanical cannon fire.
+ {"TORPEDO1", 1, IN_NOVAR}, // VOC_TORPEDO Torpedo launch.
+ {"TURRET1", 1, IN_NOVAR}, // VOC_CANNON8 Sharp cannon fire.
+ {"TSLACHG2", 10, IN_NOVAR}, // VOC_TESLA_POWER_UP Hum charge up.
+ {"TESLA1", 10, IN_NOVAR}, // VOC_TESLA_ZAP Tesla zap effect.
+ {"SQUISHY2", 10, IN_NOVAR}, // VOC_SQUISH Squish effect.
+ {"SCOLDY1", 10, IN_NOVAR}, // VOC_SCOLD Scold bleep.
+ {"RADARON2", 20, IN_NOVAR}, // VOC_RADAR_ON Powering up electronics.
+ {"RADARDN1", 10, IN_NOVAR}, // VOC_RADAR_OFF B movie power down effect.
+ {"PLACBLDG", 10, IN_NOVAR}, // VOC_PLACE_BUILDING_DOWN Building slam down sound.
+ {"KABOOM30", 1, IN_NOVAR}, // VOC_KABOOM30 Short explosion (HE).
+ {"KABOOM25", 10, IN_NOVAR}, // VOC_KABOOM25 Short growling explosion.
+ {"x", 10, IN_NOVAR},
+ {"DOGW7", 10, IN_NOVAR}, // VOC_DOG_HURT Dog whine (loud).
+ {"DOGW3PX", 10, IN_NOVAR}, // VOC_DOG_YES Dog 'yes sir'.
+ {"CRMBLE2", 10, IN_NOVAR}, // VOC_CRUMBLE Building crumble.
+ {"CASHUP1", 10, IN_NOVAR}, // VOC_MONEY_UP Rising money tick.
+ {"CASHDN1", 10, IN_NOVAR}, // VOC_MONEY_DOWN Falling money tick.
+ {"BUILD5", 10, IN_NOVAR}, // VOC_CONSTRUCTION Building construction sound.
+ {"BLEEP9", 10, IN_NOVAR}, // VOC_GAME_CLOSED Long bleep.
+ {"BLEEP6", 10, IN_NOVAR}, // VOC_INCOMING_MESSAGE Soft happy warble.
+ {"BLEEP5", 10, IN_NOVAR}, // VOC_SYS_ERROR Sharp soft warble.
+ {"BLEEP17", 10, IN_NOVAR}, // VOC_OPTIONS_CHANGED Mid range soft warble.
+ {"BLEEP13", 10, IN_NOVAR}, // VOC_GAME_FORMING Long warble.
+ {"BLEEP12", 10, IN_NOVAR}, // VOC_PLAYER_LEFT Chirp sequence.
+ {"BLEEP11", 10, IN_NOVAR}, // VOC_PLAYER_JOINED Reverse chirp sequence.
+ {"H2OBOMB2", 10, IN_NOVAR}, // VOC_DEPTH_CHARGE Distant explosion sound.
+ {"CASHTURN", 10, IN_NOVAR}, // VOC_CASHTURN Airbrake.
+ {"TUFFGUY1", 20, IN_NOVAR}, // VOC_TANYA_CHEW Tanya: "Chew on this"
+ {"ROKROLL1", 20, IN_NOVAR}, // VOC_TANYA_ROCK Tanya: "Let's rock"
+ {"LAUGH1", 20, IN_NOVAR}, // VOC_TANYA_LAUGH Tanya: "ha ha ha"
+ {"CMON1", 20, IN_NOVAR}, // VOC_TANYA_SHAKE Tanya: "Shake it baby"
+ {"BOMBIT1", 20, IN_NOVAR}, // VOC_TANYA_CHING Tanya: "Cha Ching"
+ {"GOTIT1", 20, IN_NOVAR}, // VOC_TANYA_GOT Tanya: "That's all you got"
+ {"KEEPEM1", 20, IN_NOVAR}, // VOC_TANYA_KISS Tanya: "Kiss it bye bye"
+ {"ONIT1", 20, IN_NOVAR}, // VOC_TANYA_THERE Tanya: "I'm there"
+ {"LEFTY1", 20, IN_NOVAR}, // VOC_TANYA_GIVE Tanya: "Give it to me"
+ {"YEAH1", 20, IN_NOVAR}, // VOC_TANYA_YEA Tanya: "Yea?"
+ {"YES1", 20, IN_NOVAR}, // VOC_TANYA_YES Tanya: "Yes sir?"
+ {"YO1", 20, IN_NOVAR}, // VOC_TANYA_WHATS Tanya: "What's up."
+ {"WALLKIL2", 5, IN_NOVAR}, // VOC_WALLKILL2 Crushing wall sound.
+ {"x", 10, IN_NOVAR},
+ {"GUN5", 5, IN_NOVAR}, // VOC_TRIPLE_SHOT Three quick shots in succession.
+ {"SUBSHOW1", 5, IN_NOVAR}, // VOC_SUBSHOW Submarine surface sound.
+ {"EINAH1", 20, IN_NOVAR}, // VOC_E_AH, Einstien "ah"
+ {"EINOK1", 20, IN_NOVAR}, // VOC_E_OK, Einstien "ok"
+ {"EINYES1", 20, IN_NOVAR}, // VOC_E_YES, Einstien "yes"
+ {"MINE1", 10, IN_NOVAR}, // VOC_TRIP_MINE mine explosion sound
+
+ {"SCOMND1", 20, IN_NOVAR}, // VOC_SPY_COMMANDER Spy: "commander?"
+ {"SYESSIR1", 20, IN_NOVAR}, // VOC_SPY_YESSIR Spy: "yes sir"
+ {"SINDEED1", 20, IN_NOVAR}, // VOC_SPY_INDEED Spy: "indeed"
+ {"SONWAY1", 20, IN_NOVAR}, // VOC_SPY_ONWAY Spy: "on my way"
+ {"SKING1", 20, IN_NOVAR}, // VOC_SPY_KING Spy: "for king and country"
+ {"MRESPON1", 20, IN_NOVAR}, // VOC_MED_REPORTING Medic: "reporting"
+ {"MYESSIR1", 20, IN_NOVAR}, // VOC_MED_YESSIR Medic: "yes sir"
+ {"MAFFIRM1", 20, IN_NOVAR}, // VOC_MED_AFFIRM Medic: "affirmative"
+ {"MMOVOUT1", 20, IN_NOVAR}, // VOC_MED_MOVEOUT Medic: "movin' out"
+ {"BEEPSLCT", 10, IN_NOVAR}, // VOC_BEEP_SELECT map selection beep
+
+ {"SYEAH1", 20, IN_NOVAR}, // VOC_THIEF_YEA Thief: "yea?"
+ {"x", 20, IN_NOVAR},
+ {"x", 20, IN_NOVAR},
+ {"SMOUT1", 20, IN_NOVAR}, // VOC_THIEF_MOVEOUT Thief: "movin' out"
+ {"SOKAY1", 20, IN_NOVAR}, // VOC_THIEF_OKAY Thief: "ok"
+ {"x", 20, IN_NOVAR},
+ {"SWHAT1", 20, IN_NOVAR}, // VOC_THIEF_WHAT Thief: "what"
+ {"SAFFIRM1", 20, IN_NOVAR}, // VOC_THIEF_AFFIRM Thief: "affirmative"
+};
+
+
+/***********************************************************************************************
+ * Voc_From_Name -- Fetch VocType from ASCII name specified. *
+ * *
+ * This will find the corresponding VocType from the ASCII string specified. It does this *
+ * by finding a root filename that matches the string. *
+ * *
+ * INPUT: name -- Pointer to the ASCII string that will be converted into a VocType. *
+ * *
+ * OUTPUT: Returns with the VocType that matches the string specified. If no match could be *
+ * found, then VOC_NONE is returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/06/1996 JLB : Created. *
+ *=============================================================================================*/
+VocType Voc_From_Name(char const * name)
+{
+ #ifdef 0
+ if (name == NULL) return(VOC_NONE);
+
+ for (VocType voc = VOC_FIRST; voc < VOC_COUNT; voc++) {
+ if (stricmp(name, SoundEffectName[voc].Name) == 0) {
+ return(voc);
+ }
+ }
+
+#endif
+ return(VOC_NONE);
+
+}
+
+
+/***********************************************************************************************
+ * Voc_Name -- Fetches the name for the sound effect. *
+ * *
+ * This routine returns the descriptive name of the sound effect. Currently, this is just *
+ * the root of the file name. *
+ * *
+ * INPUT: voc -- The VocType that the corresponding name is requested. *
+ * *
+ * OUTPUT: Returns with a pointer to the text string the represents the sound effect. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/06/1996 JLB : Created. *
+ *=============================================================================================*/
+char const * Voc_Name(VocType voc)
+{
+ if (voc == VOC_NONE) return("none");
+ return(SoundEffectName[voc].Name);
+}
+
+
+/***********************************************************************************************
+ * Sound_Effect -- Plays a sound effect in the tactical map. *
+ * *
+ * This routine is used when a sound effect occurs in the game world. It handles fading *
+ * the sound according to distance. *
+ * *
+ * INPUT: voc -- The sound effect number to play. *
+ * *
+ * coord -- The world location that the sound originates from. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 11/12/1994 JLB : Created. *
+ * 01/05/1995 JLB : Reduces sound more dramatically when off screen. *
+ * 09/15/1996 JLB : Revamped volume logic. *
+ *=============================================================================================*/
+void Sound_Effect(VocType voc, COORDINATE coord, int variation)
+{
+ #ifdef 0
+ CELL cell_pos = 0;
+ int pan_value;
+
+ if (Debug_Quiet || Options.Volume == 0 || voc == VOC_NONE || !SoundOn || SampleType == SAMPLE_NONE) {
+ return;
+ }
+ if (coord) {
+ cell_pos = Coord_Cell(coord);
+ }
+
+ fixed volume = 1;
+ pan_value = 0;
+ if (coord && !Map.In_View(cell_pos)) {
+ int distance = Distance(coord, Map.TacticalCoord) / CELL_LEPTON_W;
+ fixed dfixed = fixed(distance, 128+64);
+ dfixed.Sub_Saturate(1);
+ volume = fixed(1) - dfixed;
+
+ pan_value = Cell_X(cell_pos);
+ pan_value -= Coord_XCell(Map.TacticalCoord) + (Lepton_To_Cell(Map.TacLeptonWidth) / 2);
+ if (ABS(pan_value) > Lepton_To_Cell(Map.TacLeptonWidth / 2)) {
+ pan_value *= 0x8000;
+ pan_value /= (MAP_CELL_W >> 2);
+ pan_value = Bound(pan_value, -0x7FFF, 0x7FFF);
+ } else {
+ pan_value = 0;
+ }
+ }
+
+ Sound_Effect(voc, volume, variation, pan_value);
+#endif
+}
+
+
+/***********************************************************************************************
+ * Sound_Effect -- General purpose sound player. *
+ * *
+ * This is used for general purpose sound effects. These are sounds that occur outside *
+ * of the game world. They do not have a corresponding game world location as their source. *
+ * *
+ * INPUT: voc -- The sound effect number to play. *
+ * *
+ * volume -- The volume to assign to this sound effect. *
+ * *
+ * OUTPUT: Returns with the sound handle (-1 if no sound was played). *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 11/12/1994 JLB : Created. *
+ * 11/12/1994 JLB : Handles cache logic. *
+ * 05/04/1995 JLB : Variation adjustments. *
+ *=============================================================================================*/
+int Sound_Effect(VocType voc, fixed volume, int variation, signed short pan_value)
+{
+ #ifdef 0
+ char name[_MAX_FNAME+_MAX_EXT]; // Working filename of sound effect.
+
+ if (Debug_Quiet || Options.Volume == 0 || voc == VOC_NONE || !SoundOn || SampleType == SAMPLE_NONE) {
+ return(-1);
+ }
+
+ /*
+ ** Alter the volume according to the game volume setting.
+ */
+ volume = volume * Options.Volume;
+
+ /*
+ ** Fetch a pointer to the sound effect data. Modify the sound as appropriate and desired.
+ */
+ char const * ext = ".AUD";
+ if (SoundEffectName[voc].Where == IN_VAR) {
+
+ /*
+ ** For infantry, use a variation on the response. For vehicles, always
+ ** use the vehicle response table.
+ */
+ if (variation < 0) {
+ if (ABS(variation) % 2) {
+ ext = ".V00";
+ } else {
+ ext = ".V02";
+ }
+ } else {
+ if (variation % 2) {
+ ext = ".V01";
+ } else {
+ ext = ".V03";
+ }
+ }
+ }
+ _makepath(name, NULL, NULL, SoundEffectName[voc].Name, ext);
+ void const * ptr = MFCD::Retrieve(name);
+
+ /*
+ ** If the sound data pointer is not null, then presume that it is valid.
+ */
+ if (ptr != NULL) {
+ volume.Sub_Saturate(1);
+ return(Play_Sample(ptr, SoundEffectName[voc].Priority * volume, volume*256, pan_value));
+// } else {
+// Mono_Printf("Cannot find '%s'.\n", name);
+ }
+#endif
+ return(-1);
+}
+
+
+/*
+** This elaborates all the EVA speech voices.
+*/
+static char const * Speech[VOX_COUNT] = {
+ "MISNWON1", // VOX_ACCOMPLISHED mission accomplished
+ "MISNLST1", // VOX_FAIL your mission has failed
+ "PROGRES1", // VOX_NO_FACTORY unable to comply, building in progress
+ "CONSCMP1", // VOX_CONSTRUCTION construction complete
+ "UNITRDY1", // VOX_UNIT_READY unit ready
+ "NEWOPT1", // VOX_NEW_CONSTRUCT new construction options
+ "NODEPLY1", // VOX_DEPLOY cannot deploy here
+ "STRCKIL1", // VOX_STRUCTURE_DESTROYED, structure destroyed
+ "NOPOWR1", // VOX_INSUFFICIENT_POWER, insufficient power
+ "NOFUNDS1", // VOX_NO_CASH insufficient funds
+ "BCT1", // VOX_CONTROL_EXIT battle control terminated
+ "REINFOR1", // VOX_REINFORCEMENTS reinforcements have arrived
+ "CANCLD1", // VOX_CANCELED canceled
+ "ABLDGIN1", // VOX_BUILDING building
+ "LOPOWER1", // VOX_LOW_POWER low power
+ "NOFUNDS1", // VOX_NEED_MO_MONEY insufficent funds
+ "BASEATK1", // VOX_BASE_UNDER_ATTACK our base is under attack
+ "NOBUILD1", // VOX_UNABLE_TO_BUILD unable to build more
+ "PRIBLDG1", // VOX_PRIMARY_SELECTED primary building selected
+ "none",
+ "none", // VOX_SOVIET_CAPTURED Allied building captured
+ "UNITLST1", // VOX_UNIT_LOST unit lost
+ "SLCTTGT1", // VOX_SELECT_TARGET select target
+ "ENMYAPP1", // VOX_PREPARE enemy approaching
+ "SILOND1", // VOX_NEED_MO_CAPACITY silos needed
+ "ONHOLD1", // VOX_SUSPENDED on hold
+ "REPAIR1", // VOX_REPAIRING repairing
+ "none",
+ "none",
+ "AUNITL1", // VOX_AIRCRAFT_LOST airborne unit lost
+ "none",
+ "AAPPRO1", // VOX_ALLIED_FORCES_APPROACHING allied forces approaching
+ "AARRIVE1", // VOX_ALLIED_APPROACHING allied reinforcements have arrived
+ "none",
+ "none",
+ "BLDGINF1", // VOX_BUILDING_INFILTRATED building infiltrated
+ "CHROCHR1", // VOX_CHRONO_CHARGING chronosphere charging
+ "CHRORDY1", // VOX_CHRONO_READY chronosphere ready
+ "CHROYES1", // VOX_CHRONO_TEST chronosphere test successful
+ "CMDCNTR1", // VOX_HQ_UNDER_ATTACK command center under attack
+ "CNTLDED1", // VOX_CENTER_DEACTIVATED control center deactivated
+ "CONVYAP1", // VOX_CONVOY_APPROACHING convoy approaching
+ "CONVLST1", // VOX_CONVOY_UNIT_LOST convoy unit lost
+ "XPLOPLC1", // VOX_EXPLOSIVE_PLACED explosive charge placed
+ "CREDIT1", // VOX_MONEY_STOLEN credits stolen
+ "NAVYLST1", // VOX_SHIP_LOST naval unit lost
+ "SATLNCH1", // VOX_SATALITE_LAUNCHED satalite launched
+ "PULSE1", // VOX_SONAR_AVAILABLE sonar pulse available
+ "none",
+ "SOVFAPP1", // VOX_SOVIET_FORCES_APPROACHING soviet forces approaching
+ "SOVREIN1", // VOX_SOVIET_REINFROCEMENTS soviet reinforcements have arrived
+ "TRAIN1", // VOX_TRAINING training
+ "AREADY1", // VOX_ABOMB_READY
+ "ALAUNCH1", // VOX_ABOMB_LAUNCH
+ "AARRIVN1", // VOX_ALLIES_N
+ "AARRIVS1", // VOX_ALLIES_S
+ "AARIVE1", // VOX_ALLIES_E
+ "AARRIVW1", // VOX_ALLIES_W
+ "1OBJMET1", // VOX_OBJECTIVE1
+ "2OBJMET1", // VOX_OBJECTIVE2
+ "3OBJMET1", // VOX_OBJECTIVE3
+ "IRONCHG1", // VOX_IRON_CHARGING
+ "IRONRDY1", // VOX_IRON_READY
+ "KOSYRES1", // VOX_RESCUED
+ "OBJNMET1", // VOX_OBJECTIVE_NOT
+ "FLAREN1", // VOX_SIGNAL_N
+ "FLARES1", // VOX_SIGNAL_S
+ "FLAREE1", // VOX_SIGNAL_E
+ "FLAREW1", // VOX_SIGNAL_W
+ "SPYPLN1", // VOX_SPY_PLANE
+ "TANYAF1", // VOX_FREED
+ "ARMORUP1", // VOX_UPGRADE_ARMOR
+ "FIREPO1", // VOX_UPGRADE_FIREPOWER
+ "UNITSPD1", // VOX_UPGRADE_SPEED
+ "MTIMEIN1", // VOX_MISSION_TIMER
+ "UNITFUL1", // VOX_UNIT_FULL
+ "UNITREP1", // VOX_UNIT_REPAIRED
+ "40MINR", // VOX_TIME_40
+ "30MINR", // VOX_TIME_30
+ "20MINR", // VOX_TIME_20
+ "10MINR", // VOX_TIME_10
+ "5MINR", // VOX_TIME_5
+ "4MINR", // VOX_TIME_4
+ "3MINR", // VOX_TIME_3
+ "2MINR", // VOX_TIME_2
+ "1MINR", // VOX_TIME_1
+ "TIMERNO1", // VOX_TIME_STOP
+ "UNITSLD1", // VOX_UNIT_SOLD
+ "TIMERGO1", // VOX_TIMER_STARTED
+ "TARGRES1", // VOX_TARGET_RESCUED
+ "TARGFRE1", // VOX_TARGET_FREED
+ "TANYAR1", // VOX_TANYA_RESCUED
+ "STRUSLD1", // VOX_STRUCTURE_SOLD
+ "SOVFORC1", // VOX_SOVIET_FORCES_FALLEN
+ "SOVEMP1", // VOX_SOVIET_SELECTED
+ "SOVEFAL1", // VOX_SOVIET_EMPIRE_FALLEN
+ "OPTERM1", // VOX_OPERATION_TERMINATED
+ "OBJRCH1", // VOX_OBJECTIVE_REACHED
+ "OBJNRCH1", // VOX_OBJECTIVE_NOT_REACHED
+ "OBJMET1", // VOX_OBJECTIVE_MET
+ "MERCR1", // VOX_MERCENARY_RESCUED
+ "MERCF1", // VOX_MERCENARY_FREED
+ "KOSYFRE1", // VOX_KOSOYGEN_FREED
+ "FLARE1", // VOX_FLARE_DETECTED
+ "COMNDOR1", // VOX_COMMANDO_RESCUED
+ "COMNDOF1", // VOX_COMMANDO_FREED
+ "BLDGPRG1", // VOX_BUILDING_IN_PROGRESS
+ "ATPREP1", // VOX_ATOM_PREPPING
+ "ASELECT1", // VOX_ALLIED_SELECTED
+ "APREP1", // VOX_ABOMB_PREPPING
+ "ATLNCH1", // VOX_ATOM_LAUNCHED
+ "AFALLEN1", // VOX_ALLIED_FORCES_FALLEN
+ "AAVAIL1", // VOX_ABOMB_AVAILABLE
+ "AARRIVE1", // VOX_ALLIED_REINFORCEMENTS
+ "SAVE1", // VOX_MISSION_SAVED
+ "LOAD1" // VOX_MISSION_LOADED
+};
+
+
+static VoxType CurrentVoice = VOX_NONE;
+
+
+/***********************************************************************************************
+ * Speech_Name -- Fetches the name for the voice specified. *
+ * *
+ * Use this routine to fetch the ASCII name of the speech id specified. Typical use of this *
+ * would be to build a displayable list of the speech types. The trigger system uses this *
+ * so that a speech type can be selected. *
+ * *
+ * INPUT: speech -- The speech type id to convert to ASCII string. *
+ * *
+ * OUTPUT: Returns with a pointer to the speech ASCII representation of the speech id type. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/01/1996 JLB : Created. *
+ *=============================================================================================*/
+char const * Speech_Name(VoxType speech)
+{
+ if (speech == VOX_NONE) return("none");
+ return(Speech[speech]);
+}
+
+
+/***********************************************************************************************
+ * Speak -- Computer speaks to the player. *
+ * *
+ * This routine is used to have the game computer (EVA) speak to the player. *
+ * *
+ * INPUT: voice -- The voice number to speak (see defines.h). *
+ * *
+ * OUTPUT: Returns with the handle of the playing speech (-1 if no voice started). *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 11/12/1994 JLB : Created. *
+ *=============================================================================================*/
+void Speak(VoxType voice)
+{
+ if (!Debug_Quiet && Options.Volume != 0 && SampleType != 0 && voice != VOX_NONE && voice != SpeakQueue && voice != CurrentVoice && SpeakQueue == VOX_NONE) {
+ SpeakQueue = voice;
+ Speak_AI();
+ }
+}
+
+
+/***********************************************************************************************
+ * Speak_AI -- Handles starting the EVA voices. *
+ * *
+ * This starts the EVA voice talking as well. If there is any speech request in the queue, *
+ * it will be started when the current voice is finished. Call this routine as often as *
+ * possible (once per game tick is sufficient). *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 12/27/1994 JLB : Created. *
+ *=============================================================================================*/
+void Speak_AI(void)
+{
+ static VoxType _last = VOX_NONE;
+ if (Debug_Quiet || SampleType == 0) return;
+
+ if (!Is_Sample_Playing(SpeechBuffer)) {
+ CurrentVoice = VOX_NONE;
+ if (SpeakQueue != VOX_NONE) {
+ if (SpeakQueue != _last) {
+ char name[_MAX_FNAME+_MAX_EXT];
+
+ _makepath(name, NULL, NULL, Speech[SpeakQueue], ".AUD");
+ CCFileClass file(name);
+ if (file.Is_Available() && file.Read(SpeechBuffer, SPEECH_BUFFER_SIZE)) {
+ Play_Sample(SpeechBuffer, 254, Options.Volume * 256);
+ CurrentVoice = SpeakQueue;
+ }
+ _last = SpeakQueue;
+ } else {
+ Play_Sample(SpeechBuffer, 254, Options.Volume * 256);
+ }
+ SpeakQueue = VOX_NONE;
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * Stop_Speaking -- Forces the EVA voice to stop talking. *
+ * *
+ * Use this routine to immediately stop the EVA voice from speaking. It also clears out *
+ * the pending voice queue. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 12/27/1994 JLB : Created. *
+ *=============================================================================================*/
+void Stop_Speaking(void)
+{
+ SpeakQueue = VOX_NONE;
+ Stop_Sample_Playing(SpeechBuffer);
+}
+
+
+/***********************************************************************************************
+ * Is_Speaking -- Checks to see if the eva voice is still playing. *
+ * *
+ * Call this routine when the EVA voice being played needs to be checked. A typical use *
+ * of this would be when some action needs to be delayed until the voice has finished -- *
+ * say the end of the game. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: bool; Is the EVA voice still playing? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 03/12/1995 JLB : Created. *
+ *=============================================================================================*/
+bool Is_Speaking(void)
+{
+ Speak_AI();
+ if (!Debug_Quiet && SampleType != 0 && (SpeakQueue != VOX_NONE || Is_Sample_Playing(SpeechBuffer))) {
+ return(true);
+ }
+ return(false);
+}
diff --git a/CODE/AUDIO.H b/CODE/AUDIO.H
new file mode 100644
index 0000000..7f24844
--- /dev/null
+++ b/CODE/AUDIO.H
@@ -0,0 +1,99 @@
+/*
+** Command & Conquer Red Alert(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&c0\vcs\code\audio.h_v 4.43 05 Jul 1996 17:58:10 JOE_BOSTIC $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : AUDIO.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : June 21, 1994 *
+ * *
+ * Last Update : June 21, 1994 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifndef AUDIO_H
+#define AUDIO_H
+
+#include "memory.h"
+
+class AudioClass {
+ char const * Name; // Name of audio asset.
+ void const * Data; // Loaded audio data.
+ int Handle; // Handle of asset (as it is playing).
+ MemoryClass *Mem; // Pointer to memory handler class.
+ unsigned IsMIDI:1; // Is this a midi file?
+
+ public:
+ AudioClass(void);
+ AudioClass(char const *name, MemoryClass &mem);
+ virtual ~AudioClass(void);
+
+ bool Load(char const *name = 0);
+ bool Free(void);
+ bool Play(int volume = 0xFF);
+ bool Stop(void);
+ bool Pause(void);
+ bool Resume(void);
+ bool Set_Name(char const *name);
+ bool Is_Playing(void) const;
+ bool Is_Loaded(void) const;
+ bool Is_MIDI(void) const;
+};
+
+inline AudioClass::AudioClass(void)
+{
+ Name = 0;
+ Data = 0;
+ Mem = 0;
+ Handle = -1;
+};
+
+inline AudioClass::AudioClass(char const *name, MemoryClass &mem)
+{
+ if (mem) {
+ Mem = &mem;
+ } else {
+ Mem = &::Mem; // Uses global default memory handler.
+ }
+ Name = strdup(name);
+ Data = 0;
+ Handle = -1;
+};
+
+inline AudioClass::~AudioClass(void)
+{
+ if (GameActive) {
+ if (Name) free(Name);
+ if (Data) Mem->Free(Data);
+ Name = 0;
+ Data = 0;
+ Handle = -1;
+ }
+};
+
+
+#endif
diff --git a/CODE/B64PIPE.CPP b/CODE/B64PIPE.CPP
new file mode 100644
index 0000000..353869c
--- /dev/null
+++ b/CODE/B64PIPE.CPP
@@ -0,0 +1,165 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/B64PIPE.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : B64PIPE.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 06/30/96 *
+ * *
+ * Last Update : July 3, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * Base64Pipe::Put -- Processes a block of data through the pipe. *
+ * Base64Pipe::Flush -- Flushes the final pending data through the pipe. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#include "b64pipe.h"
+#include "base64.h"
+#include
+
+
+/***********************************************************************************************
+ * Base64Pipe::Put -- Processes a block of data through the pipe. *
+ * *
+ * This will take the data submitted and either Base64 encode or decode it (as specified *
+ * in the pipe's constructor). The nature of Base64 encoding means that the data will *
+ * grow 30% in size when encoding and decrease by a like amount when decoding. *
+ * *
+ * INPUT: source -- Pointer to the data to be translated. *
+ * *
+ * length -- The number of bytes to translate. *
+ * *
+ * OUTPUT: Returns with the actual number of bytes output at the far distant final end of *
+ * the pipe chain. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+int Base64Pipe::Put(void const * source, int slen)
+{
+ if (source == NULL || slen < 1) {
+ return(Pipe::Put(source, slen));
+ }
+
+ int total = 0;
+
+ char * from;
+ int fromsize;
+ char * to;
+ int tosize;
+
+ if (Control == ENCODE) {
+ from = PBuffer;
+ fromsize = sizeof(PBuffer);
+ to = CBuffer;
+ tosize = sizeof(CBuffer);
+ } else {
+ from = CBuffer;
+ fromsize = sizeof(CBuffer);
+ to = PBuffer;
+ tosize = sizeof(PBuffer);
+ }
+
+ if (Counter > 0) {
+ int len = (slen < (fromsize-Counter)) ? slen : (fromsize-Counter);
+ memmove(&from[Counter], source, len);
+ Counter += len;
+ slen -= len;
+ source = ((char *)source) + len;
+
+ if (Counter == fromsize) {
+ int outcount;
+ if (Control == ENCODE) {
+ outcount = Base64_Encode(from, fromsize, to, tosize);
+ } else {
+ outcount = Base64_Decode(from, fromsize, to, tosize);
+ }
+ total += Pipe::Put(to, outcount);
+ Counter = 0;
+ }
+ }
+
+ while (slen >= fromsize) {
+ int outcount;
+ if (Control == ENCODE) {
+ outcount = Base64_Encode(source, fromsize, to, tosize);
+ } else {
+ outcount = Base64_Decode(source, fromsize, to, tosize);
+ }
+ source = ((char *)source) + fromsize;
+ total += Pipe::Put(to, outcount);
+ slen -= fromsize;
+ }
+
+ if (slen > 0) {
+ memmove(from, source, slen);
+ Counter = slen;
+ }
+
+ return(total);
+}
+
+
+/***********************************************************************************************
+ * Base64Pipe::Flush -- Flushes the final pending data through the pipe. *
+ * *
+ * If there is any non-processed data accumulated in the holding buffer (quite likely when *
+ * encoding), then it will be processed and flushed out the end of the pipe. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the number of bytes output at the far distant final end of the pipe *
+ * chain. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+int Base64Pipe::Flush(void)
+{
+ int len = 0;
+
+ if (Counter) {
+ if (Control == ENCODE) {
+ int chars = Base64_Encode(PBuffer, Counter, CBuffer, sizeof(CBuffer));
+ len += Pipe::Put(CBuffer, chars);
+ } else {
+ int chars = Base64_Decode(CBuffer, Counter, PBuffer, sizeof(PBuffer));
+ len += Pipe::Put(PBuffer, chars);
+ }
+ Counter = 0;
+ }
+ len += Pipe::Flush();
+ return(len);
+}
+
+
+
diff --git a/CODE/B64PIPE.H b/CODE/B64PIPE.H
new file mode 100644
index 0000000..2512d91
--- /dev/null
+++ b/CODE/B64PIPE.H
@@ -0,0 +1,92 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/B64PIPE.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : B64PIPE.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 06/30/96 *
+ * *
+ * Last Update : June 30, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#ifndef B64PIPE_H
+#define B64PIPE_H
+
+#include "pipe.h"
+
+/*
+** This class performs Base64 encoding/decoding to the data that is piped through. Note that
+** encoded data will grow in size by about 30%. The reverse occurs when decoding.
+*/
+class Base64Pipe : public Pipe
+{
+ public:
+ typedef enum CodeControl {
+ ENCODE,
+ DECODE
+ } CodeControl;
+
+ Base64Pipe(CodeControl control) : Control(control), Counter(0) {}
+
+ virtual int Flush(void);
+ virtual int Put(void const * source, int slen);
+
+ private:
+
+ /*
+ ** Indicates if this is for encoding or decoding of Base64 data.
+ */
+ CodeControl Control;
+
+ /*
+ ** The counter of the number of accumulated bytes pending for processing.
+ */
+ int Counter;
+
+ /*
+ ** Buffer that holds the Base64 coded bytes. This will be the staging buffer if
+ ** this is for a decoding process. Otherwise, it will be used as a scratch buffer.
+ */
+ char CBuffer[4];
+
+ /*
+ ** Buffer that holds the plain bytes. This will be the staging buffer if this
+ ** is for an encoding process. Otherwise, it will be used as a scratch buffer.
+ */
+ char PBuffer[3];
+
+ /*
+ ** Explicitly disable the copy constructor and the assignment operator.
+ */
+ Base64Pipe(Base64Pipe & rvalue);
+ Base64Pipe & operator = (Base64Pipe const & pipe);
+};
+
+#endif
diff --git a/CODE/B64STRAW.CPP b/CODE/B64STRAW.CPP
new file mode 100644
index 0000000..bff6cb4
--- /dev/null
+++ b/CODE/B64STRAW.CPP
@@ -0,0 +1,117 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/B64STRAW.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : B64STRAW.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 07/02/96 *
+ * *
+ * Last Update : July 3, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * Base64Straw::Get -- Fetch data and convert it to/from base 64 encoding. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#include "b64straw.h"
+#include "base64.h"
+#include
+
+
+/***********************************************************************************************
+ * Base64Straw::Get -- Fetch data and convert it to/from base 64 encoding. *
+ * *
+ * This routine will fetch the number of bytes requested and perform any conversion as *
+ * necessary upon the data. The nature of Base 64 encoding means that the data will *
+ * increase in size by 30% when encoding and decrease in like manner when decoding. *
+ * *
+ * INPUT: source -- The buffer to hold the processed data. *
+ * *
+ * length -- The number of bytes requested. *
+ * *
+ * OUTPUT: Returns with the number of bytes stored into the buffer. If the number is less *
+ * than requested, then this indicates that the data stream has been exhausted. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+int Base64Straw::Get(void * source, int slen)
+{
+ int total = 0;
+
+ char * from;
+ int fromsize;
+ char * to;
+ int tosize;
+
+ if (Control == ENCODE) {
+ from = PBuffer;
+ fromsize = sizeof(PBuffer);
+ to = CBuffer;
+ tosize = sizeof(CBuffer);
+ } else {
+ from = CBuffer;
+ fromsize = sizeof(CBuffer);
+ to = PBuffer;
+ tosize = sizeof(PBuffer);
+ }
+
+ /*
+ ** Process the byte request in code blocks until there are either
+ ** no more source bytes available or the request has been fulfilled.
+ */
+ while (slen > 0) {
+
+ /*
+ ** Transfer any processed bytes available to the request buffer.
+ */
+ if (Counter > 0) {
+ int len = (slen < Counter) ? slen : Counter;
+ memmove(source, &to[tosize-Counter], len);
+ Counter -= len;
+ slen -= len;
+ source = ((char *)source) + len;
+ total += len;
+ }
+ if (slen == 0) break;
+
+ /*
+ ** More bytes are needed, so fetch and process another base 64 block.
+ */
+ int incount = Straw::Get(from, fromsize);
+ if (Control == ENCODE) {
+ Counter = Base64_Encode(from, incount, to, tosize);
+ } else {
+ Counter = Base64_Decode(from, incount, to, tosize);
+ }
+ if (Counter == 0) break;
+ }
+
+ return(total);
+}
diff --git a/CODE/B64STRAW.H b/CODE/B64STRAW.H
new file mode 100644
index 0000000..8153eae
--- /dev/null
+++ b/CODE/B64STRAW.H
@@ -0,0 +1,91 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/B64STRAW.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : B64STRAW.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 07/02/96 *
+ * *
+ * Last Update : July 2, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#ifndef B64STRAW_H
+#define B64STRAW_H
+
+#include "straw.h"
+
+/*
+** Performs Base 64 encoding/decoding on the data that is drawn through the straw. Note that
+** encoding increases the data size by about 30%. The reverse occurs when decoding.
+*/
+class Base64Straw : public Straw
+{
+ public:
+ typedef enum CodeControl {
+ ENCODE,
+ DECODE
+ } CodeControl;
+
+ Base64Straw(CodeControl control) : Control(control), Counter(0) {}
+ virtual int Get(void * source, int slen);
+
+ private:
+
+ /*
+ ** Indicates if this is for encoding or decoding of Base64 data.
+ */
+ CodeControl Control;
+
+ /*
+ ** The counter of the number of accumulated bytes pending for processing.
+ */
+ int Counter;
+
+ /*
+ ** Buffer that holds the Base64 coded bytes. This will be the staging buffer if
+ ** this is for a decoding process. Otherwise, it will be used as a scratch buffer.
+ */
+ char CBuffer[4];
+
+ /*
+ ** Buffer that holds the plain bytes. This will be the staging buffer if this
+ ** is for an encoding process. Otherwise, it will be used as a scratch buffer.
+ */
+ char PBuffer[3];
+
+ /*
+ ** Explicitly disable the copy constructor and the assignment operator.
+ */
+ Base64Straw(Base64Straw & rvalue);
+ Base64Straw & operator = (Base64Straw const & pipe);
+};
+
+
+#endif
diff --git a/CODE/BAR.CPP b/CODE/BAR.CPP
new file mode 100644
index 0000000..16b757c
--- /dev/null
+++ b/CODE/BAR.CPP
@@ -0,0 +1,239 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/BAR.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : BAR.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 08/16/96 *
+ * *
+ * Last Update : August 16, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * ProgressBarClass::Is_Horizontal -- Determines if the bargraph is horizontal or not. *
+ * ProgressBarClass::Outline -- Draw an outline around the bargraph if supposed to. *
+ * ProgressBarClass::ProgressBarClass -- Constructor for the bargraph object. *
+ * ProgressBarClass::Redraw -- Redraw the bargraph. *
+ * ProgressBarClass::Set_Limit -- Set the logic tracking value. *
+ * ProgressBarClass::Update -- Update the value and redraw as necessary. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+#include "bar.h"
+#include "fixed.h"
+
+
+/***********************************************************************************************
+ * ProgressBarClass::ProgressBarClass -- Constructor for the bargraph object. *
+ * *
+ * This is the constructor for the bargraph object. It establishes the dimensions and *
+ * coordinate of the bargraph as well as the colors it will use when drawn. *
+ * *
+ * INPUT: w,y -- Pixel coordinate of the upper left corner of the bargraph. *
+ * *
+ * width,height -- Dimensions of the bargraph. *
+ * *
+ * forecolor -- The color to use for the filled portion of the bargraph. *
+ * *
+ * backcolor -- The color to use for the non-filled portion of the bargraph. *
+ * *
+ * bordercolor -- Optional border color. If not zero, then the bargraph will be *
+ * outlined with this color. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/16/1996 JLB : Created. *
+ *=============================================================================================*/
+ProgressBarClass::ProgressBarClass(int x, int y, int width, int height, int forecolor, int backcolor, int bordercolor) :
+ X(x),
+ Y(y),
+ Width(width),
+ Height(height),
+ BarColor(forecolor),
+ BackColor(backcolor),
+ BorderColor(bordercolor),
+ CurrentValue(0),
+ LastDisplayCurrent(0),
+ IsDrawn(false)
+{
+}
+
+
+/***********************************************************************************************
+ * ProgressBarClass::Is_Horizontal -- Determines if the bargraph is horizontal or not. *
+ * *
+ * If the bargraph is oriented horizontally, then this function will return TRUE. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: bool; Is this bargraph horizontal? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/16/1996 JLB : Created. *
+ *=============================================================================================*/
+bool ProgressBarClass::Is_Horizontal(void) const
+{
+ if (Width > Height) return(true);
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * ProgressBarClass::Update -- Update the value and redraw as necessary. *
+ * *
+ * This will update the value of the bargraph to the fill ratio specified and then *
+ * redraw it if required. Very small changes to the bargraph value might not result in a *
+ * visual change. *
+ * *
+ * INPUT: value -- The new value to assign to this bargraph. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: bool; Did this update result in a redraw? *
+ * *
+ * HISTORY: *
+ * 08/16/1996 JLB : Created. *
+ *=============================================================================================*/
+bool ProgressBarClass::Update(fixed value)
+{
+ CurrentValue = value;
+
+ if (!IsDrawn || value - LastDisplayCurrent >= fixed(1, 10)) {
+ Redraw();
+ return(true);
+ }
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * ProgressBarClass::Outline -- Draw an outline around the bargraph if supposed to. *
+ * *
+ * This routine will draw a border around the bargraph if this bargraph has a color *
+ * specified for the border. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/16/1996 JLB : Created. *
+ *=============================================================================================*/
+void ProgressBarClass::Outline(void) const
+{
+ if (Is_Outlined()) {
+ LogicPage->Draw_Line(X, Y, X+Width, Y, BorderColor);
+ LogicPage->Draw_Line(X, Y, X, Y+Height, BorderColor);
+ LogicPage->Draw_Line(X, Y+Height, X, Y+Height, BorderColor);
+ LogicPage->Draw_Line(X+Width, Y, X+Width, Y+Height, BorderColor);
+ }
+}
+
+
+/***********************************************************************************************
+ * ProgressBarClass::Redraw -- Redraw the bargraph. *
+ * *
+ * This will redraw the entire bargraph. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/16/1996 JLB : Created. *
+ *=============================================================================================*/
+void ProgressBarClass::Redraw(void) const
+{
+ Hide_Mouse();
+
+ Outline();
+
+ /*
+ ** Determine the inner dimensions of the bargraph. This will be
+ ** somewhat smaller than indicated if it has a border.
+ */
+ int x = X;
+ int y = Y;
+ int w = Width;
+ int h = Height;
+ if (Is_Outlined()) {
+ x += 1;
+ y += 1;
+ w -= 2;
+ h -= 2;
+ }
+
+ /*
+ ** The working "length" of the bargraph is dependant on whether the
+ ** bargraph is horizontal or vertical.
+ */
+ int size = Is_Horizontal() ? w : h;
+
+ /*
+ ** Determine the number of pixels to fill in the bargraph depending on the
+ ** size of the internal value. The larger the internal value the more
+ ** filled the bargraph becomes.
+ */
+ int fill = CurrentValue * size;
+
+ /*
+ ** Draw the filled portion of the bargraph if there is any pixels to draw.
+ */
+ if (fill > 0) {
+ if (Is_Horizontal()) {
+ LogicPage->Fill_Rect(x, y, x+fill, y+h, BarColor);
+ } else {
+ LogicPage->Fill_Rect(x, y+fill, x+w, y+h, BarColor);
+ }
+ }
+
+ /*
+ ** Draw the unfilled portion of the bargraph if there are any pixels to
+ ** draw of it.
+ */
+ if (w-fill > 0) {
+ if (Is_Horizontal()) {
+ LogicPage->Fill_Rect(x+fill, y, x+w, y+h, BackColor);
+ } else {
+ LogicPage->Fill_Rect(x, y, x+w, y+fill-1, BackColor);
+ }
+ }
+
+ Show_Mouse();
+
+ ProgressBarClass * me = (ProgressBarClass *)this;
+ me->LastDisplayCurrent = CurrentValue;
+ me->IsDrawn = true;
+}
diff --git a/CODE/BAR.H b/CODE/BAR.H
new file mode 100644
index 0000000..6af34ef
--- /dev/null
+++ b/CODE/BAR.H
@@ -0,0 +1,111 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/BAR.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : BAR.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 08/16/96 *
+ * *
+ * Last Update : August 16, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifndef BAR_H
+#define BAR_H
+
+/*
+** The "bool" integral type was defined by the C++ committee in
+** November of '94. Until the compiler supports this, use the following
+** definition.
+*/
+#ifndef __BORLANDC__
+#ifndef TRUE_FALSE_DEFINED
+#define TRUE_FALSE_DEFINED
+enum {false=0,true=1};
+typedef int bool;
+#endif
+#endif
+
+#include "fixed.h"
+
+
+/*
+** This is a manager for a progress (or other) bargraph. Such a graph consists of a fill
+** and a background region. The fill percentage of the bargraph is controlled by an
+** update value. The bargraph can be optionally outlined.
+*/
+class ProgressBarClass
+{
+ public:
+ ProgressBarClass(int x, int y, int width, int height, int forecolor, int backcolor, int bordercolor=0);
+
+ bool Update(fixed value);
+ void Redraw(void) const;
+
+ private:
+
+ void Outline(void) const;
+ bool Is_Horizontal(void) const;
+ bool Is_Outlined(void) const {return(BorderColor != 0);}
+
+ /*
+ ** This is the upper left coordinates of the bargraph.
+ */
+ int X,Y;
+
+ /*
+ ** This is the dimensions of the bargraph.
+ */
+ int Width, Height;
+
+ /*
+ ** These are the colors to use when drawing the progress bar.
+ */
+ int BarColor;
+ int BackColor;
+ int BorderColor;
+
+ /*
+ ** This is the current value of the bargraph.
+ */
+ fixed CurrentValue;
+
+ /*
+ ** This is the current value as of the last time the bargraph was rendered.
+ */
+ fixed LastDisplayCurrent;
+
+ /*
+ ** If the bargraph has been drawn at least once, then this flag will
+ ** be true.
+ */
+ unsigned IsDrawn:1;
+};
+
+
+#endif
diff --git a/CODE/BASE.CPP b/CODE/BASE.CPP
new file mode 100644
index 0000000..121d8e9
--- /dev/null
+++ b/CODE/BASE.CPP
@@ -0,0 +1,548 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/BASE.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : BASE.CPP *
+ * *
+ * Programmer : Bill Randolph *
+ * *
+ * Start Date : 03/27/95 *
+ * *
+ * Last Update : July 30, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * BaseClass::Get_Building -- Returns ptr to the built building for the given node *
+ * BaseClass::Get_Node -- Finds the node that matches the cell specified. *
+ * BaseClass::Get_Node -- Returns ptr to the node corresponding to given object *
+ * BaseClass::Is_Built -- Tells if given item in the list has been built yet *
+ * BaseClass::Is_Node -- Tells if the given building is part of our base list *
+ * BaseClass::Load -- loads from a saved game file *
+ * BaseClass::Next_Buildable -- returns ptr to the next node that needs to be built *
+ * BaseClass::Read_INI -- INI reading routine *
+ * BaseClass::Save -- saves to a saved game file *
+ * BaseClass::Write_INI -- Writes all the base information to the INI database. *
+ * BaseNodeClass::operator != -- inequality operator *
+ * BaseNodeClass::operator == -- equality operator *
+ * BaseNodeClass::operator > -- greater-than operator *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+
+
+/***********************************************************************************************
+ * BaseNodeClass::operator == -- equality operator *
+ * *
+ * INPUT: *
+ * node node to test against *
+ * *
+ * OUTPUT: *
+ * true = equal, false = not equal *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 03/24/1995 BRR : Created. *
+ *=============================================================================================*/
+int BaseNodeClass::operator == (BaseNodeClass const & node)
+{
+ return(Type == node.Type && Cell == node.Cell);
+}
+
+
+/***********************************************************************************************
+ * BaseNodeClass::operator != -- inequality operator *
+ * *
+ * INPUT: *
+ * node node to test against *
+ * *
+ * OUTPUT: *
+ * comparison result *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 03/24/1995 BRR : Created. *
+ *=============================================================================================*/
+int BaseNodeClass::operator !=(BaseNodeClass const & node)
+{
+ return(!(*this == node));
+}
+
+
+/***********************************************************************************************
+ * BaseNodeClass::operator > -- greater-than operator *
+ * *
+ * INPUT: *
+ * node node to test against *
+ * *
+ * OUTPUT: *
+ * comparison result *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 03/24/1995 BRR : Created. *
+ *=============================================================================================*/
+int BaseNodeClass::operator > (BaseNodeClass const & )
+{
+ return(true);
+}
+
+
+/***********************************************************************************************
+ * BaseClass::Load -- loads from a saved game file *
+ * *
+ * INPUT: *
+ * file open file *
+ * *
+ * OUTPUT: *
+ * true = success, false = failure *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 03/24/1995 BRR : Created. *
+ * 07/04/1996 JLB : Converted to demand driven data source. *
+ *=============================================================================================*/
+bool BaseClass::Load(Straw & file)
+{
+ int num_struct;
+ int i;
+ BaseNodeClass node;
+
+ /*
+ ** Read in & check the size of this class
+ */
+ if (file.Get(&i, sizeof(i)) != sizeof(i)) {
+ return(false);
+ }
+
+ if (i != sizeof(*this)) {
+ return(false);
+ }
+
+ /*
+ ** Read in the House & the number of structures in the base
+ */
+ if (file.Get(&House, sizeof(House)) != sizeof(House)) {
+ return(false);
+ }
+
+ if (file.Get(&num_struct, sizeof(num_struct)) != sizeof(num_struct)) {
+ return(false);
+ }
+
+ /*
+ ** Read each node entry & add it to the list
+ */
+ for (i = 0; i < num_struct; i++) {
+ if (file.Get(&node, sizeof(node)) != sizeof(node)) {
+ return(false);
+ }
+ Nodes.Add(node);
+ }
+
+ return(true);
+}
+
+
+/***********************************************************************************************
+ * BaseClass::Save -- saves to a saved game file *
+ * *
+ * INPUT: *
+ * file open file *
+ * *
+ * OUTPUT: *
+ * true = success, false = failure *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 03/24/1995 BRR : Created. *
+ * 07/04/1996 JLB : Converted to supply driven data output. *
+ *=============================================================================================*/
+bool BaseClass::Save(Pipe & file) const
+{
+ int num_struct;
+ int i;
+ BaseNodeClass node;
+
+ /*
+ ** Write the size of this class
+ */
+ i = sizeof(*this);
+ file.Put(&i, sizeof(i));
+
+ /*
+ ** Write the House & the number of structures in the base
+ */
+ file.Put(&House, sizeof(House));
+
+ num_struct = Nodes.Count();
+ file.Put(&num_struct, sizeof(num_struct));
+
+ /*
+ ** Write each node entry
+ */
+ for (i = 0; i < num_struct; i++) {
+ node = Nodes[i];
+ file.Put(&node, sizeof(node));
+ }
+
+ return(true);
+}
+
+
+/***********************************************************************************************
+ * BaseClass::Is_Built -- Tells if given item in the list has been built yet *
+ * *
+ * INPUT: *
+ * index index into base list *
+ * *
+ * OUTPUT: *
+ * true = yes, false = no *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 03/24/1995 BRR : Created. *
+ *=============================================================================================*/
+bool BaseClass::Is_Built(int index) const
+{
+ if (Get_Building(index) != NULL) {
+ return(true);
+ } else {
+ return(false);
+ }
+}
+
+
+/***********************************************************************************************
+ * BaseClass::Get_Building -- Returns ptr to the built building for the given node *
+ * *
+ * INPUT: *
+ * obj pointer to building to test *
+ * *
+ * OUTPUT: *
+ * ptr to already-built building, NULL if none *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 03/24/1995 BRR : Created. *
+ * 07/30/1996 JLB : Handle arbitrary overlapper list length. *
+ *=============================================================================================*/
+BuildingClass * BaseClass::Get_Building(int index) const
+{
+ ObjectClass * obj[1 + ARRAY_SIZE(Map[(CELL)0].Overlapper)];
+
+ /*
+ ** Check the location on the map where this building should be; if it's
+ ** there, return a pointer to it.
+ */
+ CELL cell = Nodes[index].Cell;
+
+ obj[0] = Map[cell].Cell_Building();
+ int count = 1;
+ for (int xindex = 0; xindex < ARRAY_SIZE(Map[cell].Overlapper); xindex++) {
+ if (Map[cell].Overlapper[xindex] != NULL) {
+ obj[count++] = Map[cell].Overlapper[xindex];
+ }
+ }
+
+ BuildingClass * bldg = NULL;
+ for (int i = 0; i < count; i++) {
+ if (obj[i] &&
+ Coord_Cell(obj[i]->Coord) == Nodes[index].Cell &&
+ obj[i]->What_Am_I() == RTTI_BUILDING &&
+ ((BuildingClass *)obj[i])->Class->Type == Nodes[index].Type) {
+
+ bldg = (BuildingClass *)obj[i];
+ break;
+ }
+ }
+
+ return(bldg);
+}
+
+
+/***********************************************************************************************
+ * BaseClass::Is_Node -- Tells if the given building is part of our base list *
+ * *
+ * INPUT: *
+ * obj pointer to building to test *
+ * *
+ * OUTPUT: *
+ * true = building is a node in the list, false = isn't *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 03/24/1995 BRR : Created. *
+ *=============================================================================================*/
+bool BaseClass::Is_Node(BuildingClass const * obj)
+{
+ if (Get_Node(obj) != NULL) {
+ return(true);
+ } else {
+ return(false);
+ }
+}
+
+
+/***********************************************************************************************
+ * BaseClass::Get_Node -- Returns ptr to the node corresponding to given object *
+ * *
+ * INPUT: *
+ * obj pointer to building to test *
+ * *
+ * OUTPUT: *
+ * ptr to node *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 03/24/1995 BRR : Created. *
+ *=============================================================================================*/
+BaseNodeClass * BaseClass::Get_Node(BuildingClass const * obj)
+{
+ for (int i = 0; i < Nodes.Count(); i++) {
+ if (obj->Class->Type == Nodes[i].Type && Coord_Cell(obj->Coord) == Nodes[i].Cell) {
+ return(&Nodes[i]);
+ }
+ }
+ return(NULL);
+}
+
+
+/***********************************************************************************************
+ * BaseClass::Get_Node -- Finds the node that matches the cell specified. *
+ * *
+ * This routine is used to find a matching node the corresponds to the cell specified. *
+ * *
+ * INPUT: cell -- The cell to use in finding a match. *
+ * *
+ * OUTPUT: Returns a pointer to the matching node if found. If not found, then NULL is *
+ * returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 03/12/1996 JLB : Created. *
+ *=============================================================================================*/
+BaseNodeClass * BaseClass::Get_Node(CELL cell)
+{
+ for (int index = 0; index < Nodes.Count(); index++) {
+ if (cell == Nodes[index].Cell) {
+ return(&Nodes[index]);
+ }
+ }
+ return(NULL);
+}
+
+
+/***********************************************************************************************
+ * BaseClass::Next_Buildable -- returns ptr to the next node that needs to be built *
+ * *
+ * If 'type' is not NONE, returns ptr to the next "hole" in the list of the given type. *
+ * Otherwise, returns ptr to the next hole in the list of any type. *
+ * *
+ * INPUT: *
+ * type type of building to check for *
+ * *
+ * OUTPUT: *
+ * ptr to a BaseNodeClass, NULL if none *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 03/24/1995 BRR : Created. *
+ *=============================================================================================*/
+BaseNodeClass * BaseClass::Next_Buildable(StructType type)
+{
+ /*
+ ** Loop through all node entries, returning a pointer to the first
+ ** un-built one that matches the requested type.
+ */
+ for (int i = 0; i < Nodes.Count(); i++) {
+
+ /*
+ ** For STRUCT_NONE, return the first hole found
+ */
+ if (type == STRUCT_NONE) {
+ if (!Is_Built(i)) {
+ return(&Nodes[i]);
+ }
+
+ } else {
+
+ /*
+ ** For a "real" building type, return the first hold for that type
+ */
+ if (Nodes[i].Type==type && !Is_Built(i)) {
+ return(&Nodes[i]);
+ }
+ }
+ }
+
+
+// If no entry could be found, then create a fake one that will allow
+// placement of the building. Make it static and reuse the next time this
+// routine is called.
+
+ return(NULL);
+}
+
+
+/***********************************************************************************************
+ * BaseClass::Read_INI -- INI reading routine *
+ * *
+ * INI entry format: *
+ * BLDG=COORDINATE *
+ * BLDG=COORDINATE *
+ * ... *
+ * *
+ * INPUT: *
+ * buffer pointer to loaded INI file *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * This routines assumes there is only one base defined for the scenario. *
+ * *
+ * HISTORY: *
+ * 03/24/1995 BRR : Created. *
+ * 02/20/1996 JLB : Fixed to know what house to build base from. *
+ *=============================================================================================*/
+void BaseClass::Read_INI(CCINIClass & ini)
+{
+ char buf[128];
+ char uname[10];
+ BaseNodeClass node; // node to add to list
+
+ Mono_Clear_Screen();
+ /*
+ ** First, determine the house of the human player, and set the Base's house
+ ** accordingly.
+ */
+ House = ini.Get_HousesType(INI_Name(), "Player", PlayerPtr->Class->House);
+
+ /*
+ ** Read the number of buildings that will go into the base node list
+ */
+ int count = ini.Get_Int(INI_Name(), "Count", 0);
+
+ /*
+ ** Read each entry in turn, in the same order they were written out.
+ */
+ for (int i = 0; i < count; i++) {
+
+ /*
+ ** Get an INI entry
+ */
+ sprintf(uname,"%03d",i);
+ ini.Get_String(INI_Name(), uname, NULL, buf, sizeof(buf));
+
+ /*
+ ** Set the node's building type
+ */
+ node.Type = BuildingTypeClass::From_Name(strtok(buf,","));
+
+ /*
+ ** Read & set the node's coordinate
+ */
+ node.Cell = atoi(strtok(NULL,","));
+
+ /*
+ ** Add this node to the Base's list
+ */
+ Nodes.Add(node);
+ }
+}
+
+
+/***********************************************************************************************
+ * BaseClass::Write_INI -- Writes all the base information to the INI database. *
+ * *
+ * Use this routine to write all prebuild base information to the INI database specified. *
+ * *
+ * INPUT: ini -- Reference to the INI database to store the data to. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: If there was any preexisting prebuild base data in the database, it will be *
+ * be erased by this routine. *
+ * *
+ * HISTORY: *
+ * 07/30/1996 JLB : Created. *
+ *=============================================================================================*/
+void BaseClass::Write_INI(CCINIClass & ini)
+{
+ /*
+ ** Clear out all existing base data from the ini file.
+ */
+ ini.Clear(INI_Name());
+
+ if (House != HOUSE_NONE) {
+
+ /*
+ ** Write out the owner of this buildable list.
+ */
+ ini.Put_HousesType(INI_Name(), "Player", House);
+
+ /*
+ ** Save the # of buildings in the Nodes list. This is essential because
+ ** they must be read in the same order they were created, so "000" must be
+ ** read first, etc.
+ */
+ ini.Put_Int(INI_Name(), "Count", Nodes.Count());
+
+ /*
+ ** Write each entry into the INI
+ */
+ for (int i = 0; i < Nodes.Count(); i++) {
+ char buf[128];
+ char uname[10];
+
+ sprintf(uname,"%03d",i);
+ sprintf(buf,"%s,%d",
+ BuildingTypeClass::As_Reference(Nodes[i].Type).IniName,
+ Nodes[i].Cell);
+
+ ini.Put_String(INI_Name(), uname, buf);
+ }
+ }
+}
diff --git a/CODE/BASE.H b/CODE/BASE.H
new file mode 100644
index 0000000..91d3336
--- /dev/null
+++ b/CODE/BASE.H
@@ -0,0 +1,130 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/BASE.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : BASE.H *
+ * *
+ * Programmer : Bill Randolph *
+ * *
+ * Start Date : 03/27/95 *
+ * *
+ * Last Update : March 27, 1995 *
+ * *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifndef BASE_H
+#define BASE_H
+
+
+/****************************************************************************
+** This class defines one "node" in the pre-built base list. Each node
+** contains a type of building to build, and the COORDINATE to build it at.
+*/
+class BaseNodeClass
+{
+ public:
+ BaseNodeClass(void) {};
+ BaseNodeClass(StructType building, CELL cell) : Type(building), Cell(cell) {};
+ int operator == (BaseNodeClass const & node);
+ int operator != (BaseNodeClass const & node);
+ int operator > (BaseNodeClass const & node);
+
+ StructType Type;
+ CELL Cell;
+};
+
+
+/****************************************************************************
+** This is the class that defines a pre-built base for the computer AI.
+** (Despite its name, this is NOT the "base" class for C&C's class hierarchy!)
+*/
+class BaseClass
+{
+ public:
+
+ /*
+ ** Constructor/Destructor
+ */
+ BaseClass(void) {};
+ virtual ~BaseClass() {Nodes.Clear();}
+
+ /*
+ ** Initialization
+ */
+ void Init(void) {House = HOUSE_NONE; Nodes.Clear();}
+
+ /*
+ ** The standard suite of load/save support routines
+ */
+ void Read_INI(CCINIClass & ini);
+ void Write_INI(CCINIClass & ini);
+ static char *INI_Name(void) {return "Base";}
+ bool Load(Straw & file);
+ bool Save(Pipe & file) const;
+ virtual void Code_Pointers(void) {};
+ virtual void Decode_Pointers(void) {};
+
+ /*
+ ** Tells if the given node has been built or not
+ */
+ bool Is_Built(int index) const;
+
+ /*
+ ** Returns a pointer to the object for the given node
+ */
+ BuildingClass * Get_Building(int index) const;
+
+ /*
+ ** Tells if the given building ptr is a node in this base's list.
+ */
+ bool Is_Node(BuildingClass const * obj);
+
+ /*
+ ** Returns a pointer to the requested node.
+ */
+ BaseNodeClass * Get_Node(BuildingClass const * obj);
+ BaseNodeClass * Get_Node(int index) { return (&Nodes[index]); }
+ BaseNodeClass * Get_Node(CELL cell);
+
+ /*
+ ** Returns a pointer to the next "hole" in the Nodes list.
+ */
+ BaseNodeClass * Next_Buildable(StructType type = STRUCT_NONE);
+
+ /*
+ ** This is the list of "nodes" that define the base. Portions of this
+ ** list can be pre-built by simply saving those buildings in the INI
+ ** along with non-base buildings, so Is_Built will return true for them.
+ */
+ DynamicVectorClass Nodes;
+
+ /*
+ ** This is the house this base belongs to.
+ */
+ HousesType House;
+};
+
+
+#endif
+
diff --git a/CODE/BASE64.CPP b/CODE/BASE64.CPP
new file mode 100644
index 0000000..5e77a85
--- /dev/null
+++ b/CODE/BASE64.CPP
@@ -0,0 +1,437 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/BASE64.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : BASE64.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 06/29/96 *
+ * *
+ * Last Update : July 6, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * Base64_Decode -- Decodes Base 64 data into its original data form. *
+ * Base64_Encode -- Encode data into Base 64 format. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#include "base64.h"
+#include
+
+/*
+** This is the magic padding character used to fill out the encoded data to a multiple of
+** 4 characters even though the source data is less than necessary to accomplish this.
+** The pad character lets the decoder know of this condition and it will compensate
+** accordingly.
+*/
+static char const * const _pad = "=";
+
+/*
+** This encoder translation table will convert a 6 bit number into an ASCII character.
+*/
+static char const * const _encoder = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/*
+** The decoder translation table takes an ASCII character and converts it into a
+** 6 bit number.
+*/
+#define BAD 0xFE // Ignore this character in source data.
+#define END 0xFF // Signifies premature end of input data.
+static unsigned char const _decoder[256] = {
+ BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,
+ BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,
+ BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,62,BAD,BAD,BAD,63,
+ 52,53,54,55,56,57,58,59,60,61,BAD,BAD,BAD,END,BAD,BAD,
+ BAD,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,
+ 15,16,17,18,19,20,21,22,23,24,25,BAD,BAD,BAD,BAD,BAD,
+ BAD,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
+ 41,42,43,44,45,46,47,48,49,50,51,BAD,BAD,BAD,BAD,BAD,
+ BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,
+ BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,
+ BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,
+ BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,
+ BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,
+ BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,
+ BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,
+ BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD
+};
+
+int const PacketChars = 4;
+
+
+/*
+** The packet type is used to construct and disect the Base64 data blocks. The data
+** consists of three source data bytes mapped onto four 6 bit Base64 code elements.
+*/
+typedef union {
+ struct {
+#ifdef BIG_ENDIAN
+ unsigned char C1;
+ unsigned char C2;
+ unsigned char C3;
+#else
+ unsigned char C3;
+ unsigned char C2;
+ unsigned char C1;
+#endif
+ unsigned char pad;
+ } Char;
+ struct {
+#ifdef BIG_ENDIAN
+ unsigned O1:6;
+ unsigned O2:6;
+ unsigned O3:6;
+ unsigned O4:6;
+#else
+ unsigned O4:6;
+ unsigned O3:6;
+ unsigned O2:6;
+ unsigned O1:6;
+#endif
+ unsigned pad:8;
+ } SubCode;
+ unsigned int Raw;
+} PacketType;
+
+
+/***********************************************************************************************
+ * Base64_Encode -- Encode data into Base 64 format. *
+ * *
+ * This will take an arbitrary length of source data and transform it into base 64 format *
+ * data. Base 64 format has the property of being very portable across text editors and *
+ * country character encoding schemes. As such it is ideal for e-mail. Note that the output *
+ * data will be about 33% larger than the source. *
+ * *
+ * INPUT: source -- Pointer to the source data to convert. *
+ * *
+ * slen -- The number of bytes to encode. *
+ * *
+ * dest -- Pointer to the destination buffer that will hold the encoded data. *
+ * *
+ * dlen -- The size of the destination buffer. *
+ * *
+ * OUTPUT: Returns with the number of bytes stored into the destination buffer. *
+ * *
+ * WARNINGS: Be sure that the destination buffer is big enough to hold the encoded output. *
+ * *
+ * HISTORY: *
+ * 07/06/1996 JLB : Created. *
+ *=============================================================================================*/
+int Base64_Encode(void const * source, int slen, void * dest, int dlen)
+{
+ /*
+ ** Check the parameters for legality.
+ */
+ if (source == NULL || slen == 0 || dest == NULL || dlen == 0) {
+ return(0);
+ }
+
+ /*
+ ** Process the source data in blocks of three bytes. Fewer than three bytes
+ ** results in special padding output characters (automatically discarded
+ ** during the decode process).
+ */
+ int total = 0;
+ unsigned char const * sptr = (unsigned char const *)source;
+ unsigned char * dptr = (unsigned char *)dest;
+ while (slen > 0 && dlen >= PacketChars) {
+
+ /*
+ ** Fetch 24 bits of source data.
+ */
+ PacketType packet;
+
+ int pad = 0;
+ packet.Raw = 0;
+ packet.Char.C1 = *sptr++;
+ slen--;
+ if (slen) {
+ packet.Char.C2 = *sptr++;
+ slen--;
+ } else {
+ pad++;
+ }
+ if (slen) {
+ packet.Char.C3 = *sptr++;
+ slen--;
+ } else {
+ pad++;
+ }
+
+ /*
+ ** Translate and write 4 characters of Base64 data. Pad with pad
+ ** characters if there is insufficient source data for a full packet.
+ */
+ *dptr++ = _encoder[packet.SubCode.O1];
+ *dptr++ = _encoder[packet.SubCode.O2];
+ if (pad < 2) {
+ *dptr++ = _encoder[packet.SubCode.O3];
+ } else {
+ *dptr++ = _pad[0];
+ }
+ if (pad < 1) {
+ *dptr++ = _encoder[packet.SubCode.O4];
+ } else {
+ *dptr++ = _pad[0];
+ }
+
+ dlen -= PacketChars;
+ total += PacketChars;
+ }
+
+ /*
+ ** Add a trailing null as a courtesy measure.
+ */
+ if (dlen > 0) {
+ *dptr = '\0';
+ }
+
+ /*
+ ** Return with the total number of characters in the output buffer.
+ */
+ return(total);
+}
+
+
+/***********************************************************************************************
+ * Base64_Decode -- Decodes Base 64 data into its original data form. *
+ * *
+ * Use this routine to decode base 64 data back into the original data. A property of this *
+ * decode process is that unrecognized input characters are ignored. This allows mangled *
+ * source (filled with line breaks or spaces) to be correctly decoded. The decode process *
+ * terminates when the end of the source data has been reached or the special end of data *
+ * marker is encountered. *
+ * *
+ * INPUT: source -- Pointer to the source data to decode. *
+ * *
+ * slen -- The number of bytes in the source data buffer. *
+ * *
+ * dest -- Pointer to the destination buffer to be filled with the decoded data. *
+ * *
+ * dlen -- The maximum size of the destination buffer. *
+ * *
+ * OUTPUT: Returns with the number of bytes stored into the destination buffer. This will *
+ * always be less than the number of source bytes (usually by about 33%). *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/06/1996 JLB : Created. *
+ *=============================================================================================*/
+int Base64_Decode(void const * source, int slen, void * dest, int dlen)
+{
+ /*
+ ** Check the parameters for legality.
+ */
+ if (source == NULL || slen == 0 || dest == NULL || dlen == 0) {
+ return(0);
+ }
+
+ int total = 0;
+ unsigned char const * sptr = (unsigned char const *)source;
+ unsigned char * dptr = (unsigned char *)dest;
+ while (slen > 0 && dlen > 0) {
+
+ PacketType packet;
+ packet.Raw = 0;
+
+ /*
+ ** Process input until a full packet has been accumulated or the
+ ** source is exhausted.
+ */
+ int pcount = 0;
+ while (pcount < PacketChars && slen > 0) {
+ unsigned char c = *sptr++;
+ slen--;
+
+ unsigned char code = _decoder[c];
+
+ /*
+ ** An unrecognized character is skipped.
+ */
+ if (code == BAD) continue;
+
+ /*
+ ** The "=" character signifies the end of data regardless of what
+ ** the source buffer length value may be.
+ */
+ if (code == END) {
+ slen = 0;
+ break;
+ }
+
+ /*
+ ** A valid Base64 character was found so add it to the packet
+ ** data.
+ */
+ switch (pcount) {
+ case 0:
+ packet.SubCode.O1 = code;
+ break;
+ case 1:
+ packet.SubCode.O2 = code;
+ break;
+ case 2:
+ packet.SubCode.O3 = code;
+ break;
+ case 3:
+ packet.SubCode.O4 = code;
+ break;
+ }
+ pcount++;
+ }
+
+ /*
+ ** A packet block is ready for output into the destination buffer.
+ */
+ *dptr++ = packet.Char.C1;
+ dlen--;
+ total++;
+ if (dlen > 0 && pcount > 2) {
+ *dptr++ = packet.Char.C2;
+ dlen--;
+ total++;
+ }
+ if (dlen > 0 && pcount > 3) {
+ *dptr++ = packet.Char.C3;
+ dlen--;
+ total++;
+ }
+ }
+
+ /*
+ ** Return with the total number of characters decoded into the
+ ** output buffer.
+ */
+ return(total);
+}
+
+
+/*
+Base64 Content-Transfer-Encoding
+
+The Base64 Content-Transfer-Encoding is designed to represent arbitrary
+sequences of octets in a form that need not be humanly readable. The encoding
+and decoding algorithms are simple, but the encoded data are consistently
+only about 33 percent larger than the unencoded data. This encoding is
+virtually identical to the one used in Privacy Enhanced Mail (PEM)
+applications, as defined in RFC 1421. The base64 encoding is adapted from
+RFC 1421, with one change: base64 eliminates the "*" mechanism for embedded
+clear text.
+
+A 65-character subset of US-ASCII is used, enabling 6 bits to be represented
+per printable character. (The extra 65th character, "=", is used to signify a
+special processing function.)
+
+NOTE:
+ This subset has the important property that it is represented identically
+ in all versions of ISO 646, including US ASCII, and all characters in the
+ subset are also represented identically in all versions of EBCDIC. Other
+ popular encodings, such as the encoding used by the uuencode utility and
+ the base85 encoding specified as part of Level 2 PostScript, do not share
+ these properties, and thus do not fulfill the portability requirements a
+ binary transport encoding for mail must meet.
+
+The encoding process represents 24-bit groups of input bits as output strings
+of 4 encoded characters. Proceeding from left to right, a 24-bit input group is
+formed by concatenating 3 8-bit input groups. These 24 bits are then treated as
+4 concatenated 6-bit groups, each of which is translated into a single digit in
+the base64 alphabet. When encoding a bit stream via the base64 encoding, the
+bit stream must be presumed to be ordered with the most-significant-bit first.
+That is, the first bit in the stream will be the high-order bit in the first
+byte, and the eighth bit will be the low-order bit in the first byte, and so on.
+
+Each 6-bit group is used as an index into an array of 64 printable characters.
+The character referenced by the index is placed in the output string. These
+characters, identified in Table 1, below, are selected so as to be universally
+representable, and the set excludes characters with particular significance to
+SMTP (e.g., ".", CR, LF) and to the encapsulation boundaries defined in this
+document (e.g., "-").
+
+Table 1: The Base64 Alphabet
+
+ Value Encoding Value Encoding Value Encoding Value Encoding
+ 0 A 17 R 34 i 51 z
+ 1 B 18 S 35 j 52 0
+ 2 C 19 T 36 k 53 1
+ 3 D 20 U 37 l 54 2
+ 4 E 21 V 38 m 55 3
+ 5 F 22 W 39 n 56 4
+ 6 G 23 X 40 o 57 5
+ 7 H 24 Y 41 p 58 6
+ 8 I 25 Z 42 q 59 7
+ 9 J 26 a 43 r 60 8
+ 10 K 27 b 44 s 61 9
+ 11 L 28 c 45 t 62 +
+ 12 M 29 d 46 u 63 /
+ 13 N 30 e 47 v
+ 14 O 31 f 48 w (pad) =
+ 15 P 32 g 49 x
+ 16 Q 33 h 50 y
+
+The output stream (encoded bytes) must be represented in lines of no more than
+76 characters each. All line breaks or other characters not found in Table 1
+must be ignored by decoding software. In base64 data, characters other than
+those in Table 1, line breaks, and other white space probably indicate a
+transmission error, about which a warning message or even a message rejection
+might be appropriate under some circumstances.
+
+Special processing is performed if fewer than 24 bits are available at the end
+of the data being encoded. A full encoding quantum is always completed at the
+end of a body. When fewer than 24 input bits are available in an input group,
+zero bits are added (on the right) to form an integral number of 6-bit groups.
+Padding at the end of the data is performed using the '=' character. Since all
+base64 input is an integral number of octets, only the following cases can
+arise: (1) the final quantum of encoding input is an integral multiple of 24
+bits; here, the final unit of encoded output will be an integral multiple of 4
+characters with no "=" padding, (2) the final quantum of encoding input is
+exactly 8 bits; here, the final unit of encoded output will be two characters
+followed by two "=" padding characters, or (3) the final quantum of encoding
+input is exactly 16 bits; here, the final unit of encoded output will be three
+characters followed by one "=" padding character.
+
+Because it is used only for padding at the end of the data, the occurrence of
+any '=' characters may be taken as evidence that the end of the data has been
+reached (without truncation in transit). No such assurance is possible,
+however, when the number of octets transmitted was a multiple of three.
+
+Any characters outside of the base64 alphabet are to be ignored in
+base64-encoded data. The same applies to any illegal sequence of characters in
+the base64 encoding, such as "====="
+
+Care must be taken to use the proper octets for line breaks if base64 encoding
+is applied directly to text material that has not been converted to canonical
+form. In particular, text line breaks must be converted into CRLF sequences
+prior to base64 encoding. The important thing to note is that this may be done
+directly by the encoder rather than in a prior canonicalization step in some
+implementations.
+
+NOTE:
+ There is no need to worry about quoting apparent encapsulation boundaries
+ within base64-encoded parts of multipart entities because no hyphen
+ characters are used in the base64 encoding.
+
+*/
\ No newline at end of file
diff --git a/CODE/BASE64.H b/CODE/BASE64.H
new file mode 100644
index 0000000..3a6d945
--- /dev/null
+++ b/CODE/BASE64.H
@@ -0,0 +1,40 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/BASE64.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : BASE64.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 06/29/96 *
+ * *
+ * Last Update : June 29, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+int Base64_Encode(void const * source, int slen, void * dest, int dlen);
+int Base64_Decode(void const * source, int slen, void * dest, int dlen);
diff --git a/CODE/BBDATA.CPP b/CODE/BBDATA.CPP
new file mode 100644
index 0000000..f241c42
--- /dev/null
+++ b/CODE/BBDATA.CPP
@@ -0,0 +1,300 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/BBDATA.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : BBDATA.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : May 23, 1994 *
+ * *
+ * Last Update : July 19, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * BulletTypeClass::As_Reference -- Returns with a reference to the bullet type object specif*
+ * BulletTypeClass::BulletTypeClass -- Constructor for bullet type objects. *
+ * BulletTypeClass::Init_Heap -- Initialize the heap objects for the bullet type. *
+ * BulletTypeClass::Load_Shapes -- Load shape data for bullet types. *
+ * BulletTypeClass::One_Time -- Performs the one time processing for bullets. *
+ * BulletTypeClass::Read_INI -- Fetch the bullet type data from the INI database. *
+ * BulletTypeClass::operator delete -- Deletes a bullet type object from the special heap. *
+ * BulletTypeClass::operator new -- Allocates a bullet type object from the special heap. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+
+
+
+/***********************************************************************************************
+ * BulletTypeClass::BulletTypeClass -- Constructor for bullet type objects. *
+ * *
+ * This is basically a constructor for static type objects used by bullets. All bullets *
+ * are of a type constructed by this routine at game initialization time. *
+ * *
+ * INPUT: see below... *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 10/17/1994 JLB : Created. *
+ * 07/17/1996 JLB : Uses correct default values. *
+ *=============================================================================================*/
+BulletTypeClass::BulletTypeClass(char const * name) :
+ ObjectTypeClass( RTTI_BULLETTYPE,
+ BulletTypes.ID(this),
+ true,
+ true,
+ false,
+ false,
+ true,
+ true,
+ false,
+ TXT_NONE,
+ name
+ ),
+ IsHigh(false),
+ IsShadow(true),
+ IsArcing(false),
+ IsDropping(false),
+ IsInvisible(false),
+ IsProximityArmed(false),
+ IsFlameEquipped(false),
+ IsFueled(false),
+ IsFaceless(true),
+ IsInaccurate(false),
+ IsTranslucent(false),
+ IsAntiAircraft(false),
+ IsAntiGround(true),
+ IsAntiSub(false),
+ IsDegenerate(false),
+ IsSubSurface(false),
+ IsParachuted(false),
+ IsGigundo(false),
+ Type(BulletType(ID)),
+ ROT(0),
+ Arming(0),
+ Tumble(0)
+{
+}
+
+
+/***********************************************************************************************
+ * BulletTypeClass::operator new -- Allocates a bullet type object from the special heap. *
+ * *
+ * This allocates a bullet type object from a special heap that is used just for *
+ * objects of this type. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with a pointer to an allocated block or NULL if the allocation could not *
+ * occur. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/06/1996 JLB : Created. *
+ *=============================================================================================*/
+void * BulletTypeClass::operator new(size_t)
+{
+ return(BulletTypes.Alloc());
+}
+
+
+/***********************************************************************************************
+ * BulletTypeClass::operator delete -- Deletes a bullet type object from the special heap. *
+ * *
+ * This is the counterpart to the operator new function for bullet type objects. It will *
+ * return the bullet type object back to the special heap used for bullet type object *
+ * allocation. *
+ * *
+ * INPUT: ptr -- Pointer to the bullet type object to free. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/06/1996 JLB : Created. *
+ *=============================================================================================*/
+void BulletTypeClass::operator delete(void * ptr)
+{
+ BulletTypes.Free((BulletTypeClass *)ptr);
+}
+
+
+/***********************************************************************************************
+ * BulletTypeClass::Init_Heap -- Initialize the heap objects for the bullet type. *
+ * *
+ * This performs any necessary initialization for the bullet types. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/06/1996 JLB : Created. *
+ *=============================================================================================*/
+void BulletTypeClass::Init_Heap(void)
+{
+ /*
+ ** These bullet type class objects must be allocated in the exact order that they
+ ** are specified in the BulletType enumeration. This is necessary because the heap
+ ** allocation block index serves double duty as the type number index.
+ */
+ new BulletTypeClass("Invisible"); // BULLET_INVISIBLE
+ new BulletTypeClass("Cannon"); // BULLET_CANNON
+ new BulletTypeClass("Ack"); // BULLET_ACK
+ new BulletTypeClass("Torpedo"); // BULLET_TORPEDO
+ new BulletTypeClass("FROG"); // BULLET_FROG
+ new BulletTypeClass("HeatSeeker"); // BULLET_HEAT_SEEKER
+ new BulletTypeClass("LaserGuided"); // BULLET_LASER_GUIDED
+ new BulletTypeClass("Lobbed"); // BULLET_LOBBED
+ new BulletTypeClass("Bomblet"); // BULLET_BOMBLET
+ new BulletTypeClass("Ballistic"); // BULLET_BALLISTIC
+ new BulletTypeClass("Parachute"); // BULLET_PARACHUTE
+ new BulletTypeClass("Fireball"); // BULLET_FIREBALL
+ new BulletTypeClass("LeapDog"); // BULLET_DOG
+ new BulletTypeClass("Catapult"); // BULLET_CATAPULT
+ new BulletTypeClass("AAMissile"); // BULLET_AAMISSILE
+ new BulletTypeClass("GPSSatellite");// BULLET_GPS_SATELLITE
+ new BulletTypeClass("NukeUp"); // BULLET_NUKE_UP
+ new BulletTypeClass("NukeDown"); // BULLET_NUKE_DOWN
+}
+
+
+/***********************************************************************************************
+ * BulletTypeClass::One_Time -- Performs the one time processing for bullets. *
+ * *
+ * This routine is used to perform any one time processing for the bullet type class. It *
+ * handles loading of the shape files. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: This routine must be called before any rendering of bullets occurs and should *
+ * only be called once. *
+ * *
+ * HISTORY: *
+ * 05/28/1994 JLB : Created. *
+ *=============================================================================================*/
+void BulletTypeClass::One_Time(void)
+{
+ /*
+ ** Load the bullet shapes.
+ */
+ for (BulletType index = BULLET_FIRST; index < BULLET_COUNT; index++) {
+ BulletTypeClass const & bullet = As_Reference(index);
+ char fullname[_MAX_FNAME+_MAX_EXT];
+
+ if (!bullet.IsInvisible) {
+ _makepath(fullname, NULL, NULL, bullet.GraphicName, ".SHP");
+
+ #ifdef NDEBUG
+ ((void const *&)bullet.ImageData) = MFCD::Retrieve(fullname);
+ #else
+ RawFileClass file(fullname);
+
+ if (file.Is_Available()) {
+ ((void const *&)bullet.ImageData) = Load_Alloc_Data(file);
+ } else {
+ ((void const *&)bullet.ImageData) = MFCD::Retrieve(fullname);
+ }
+ #endif
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * BulletTypeClass::As_Reference -- Returns with a reference to the bullet type object specifi *
+ * *
+ * Given a bullet type identifier, this routine will return a reference to the bullet type *
+ * object it refers to. *
+ * *
+ * INPUT: type -- The bullet type identifier to convert to a reference. *
+ * *
+ * OUTPUT: Returns with a reference to the bullet type object. *
+ * *
+ * WARNINGS: Make sure that the type parameter specified is a valid bullet type. If not, *
+ * then the results are undefined. *
+ * *
+ * HISTORY: *
+ * 07/06/1996 JLB : Created. *
+ *=============================================================================================*/
+BulletTypeClass & BulletTypeClass::As_Reference(BulletType type)
+{
+ return(*BulletTypes.Ptr(type));
+}
+
+
+/***********************************************************************************************
+ * BulletTypeClass::Read_INI -- Fetch the bullet type data from the INI database. *
+ * *
+ * Use this routine to fetch override information about this bullet type class object *
+ * from the INI database specified. *
+ * *
+ * INPUT: ini -- Reference to the INI database to examine. *
+ * *
+ * OUTPUT: bool; Was the section for this bullet found and the data extracted? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/19/1996 JLB : Created. *
+ *=============================================================================================*/
+bool BulletTypeClass::Read_INI(CCINIClass & ini)
+{
+ if (ini.Is_Present(Name())) {
+ Arming = ini.Get_Int(Name(), "Arm", Arming);
+ ROT = ini.Get_Int(Name(), "ROT", ROT);
+ Tumble = ini.Get_Int(Name(), "Frames", Tumble);
+ IsHigh = ini.Get_Bool(Name(), "High", IsHigh);
+ IsShadow = ini.Get_Bool(Name(), "Shadow", IsShadow);
+ IsArcing = ini.Get_Bool(Name(), "Arcing", IsArcing);
+ IsDropping = ini.Get_Bool(Name(), "Dropping", IsDropping);
+ IsInvisible = ini.Get_Bool(Name(), "Inviso", IsInvisible);
+ IsProximityArmed = ini.Get_Bool(Name(), "Proximity", IsProximityArmed);
+ IsFlameEquipped = ini.Get_Bool(Name(), "Animates", IsFlameEquipped);
+ IsFueled = ini.Get_Bool(Name(), "Ranged", IsFueled);
+ IsInaccurate = ini.Get_Bool(Name(), "Inaccuate", IsInaccurate);
+ IsAntiAircraft = ini.Get_Bool(Name(), "AA", IsAntiAircraft);
+ IsAntiGround = ini.Get_Bool(Name(), "AG", IsAntiGround);
+ IsAntiSub = ini.Get_Bool(Name(), "ASW", IsAntiSub);
+ IsDegenerate = ini.Get_Bool(Name(), "Degenerates", IsDegenerate);
+ IsSubSurface = ini.Get_Bool(Name(), "UnderWater", IsSubSurface);
+ IsParachuted = ini.Get_Bool(Name(), "Parachuted", IsParachuted);
+ IsFaceless = !ini.Get_Bool(Name(), "Rotates", !IsFaceless);
+ IsTranslucent = ini.Get_Bool(Name(), "Translucent", IsTranslucent);
+ IsGigundo = ini.Get_Bool(Name(), "Gigundo", IsGigundo);
+ ini.Get_String(Name(), "Image", "none", GraphicName, sizeof(GraphicName));
+ return(true);
+ }
+ return(false);
+}
diff --git a/CODE/BDATA.CPP b/CODE/BDATA.CPP
new file mode 100644
index 0000000..e6b715c
--- /dev/null
+++ b/CODE/BDATA.CPP
@@ -0,0 +1,3834 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/BDATA.CPP 2 3/03/97 10:37p Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : BDATA.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : September 10, 1993 *
+ * *
+ * Last Update : October 2, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * BuildingTypeClass::As_Reference -- Fetches reference to the building type specified. *
+ * BuildingTypeClass::Bib_And_Offset -- Determines the bib and appropriate cell offset. *
+ * BuildingTypeClass::BuildingTypeClass -- This is the constructor for the building types. *
+ * BuildingTypeClass::Coord_Fixup -- Adjusts coordinate to be legal for assignment. *
+ * BuildingTypeClass::Cost_Of -- Fetches the cost of this building. *
+ * BuildingTypeClass::Create_And_Place -- Creates and places a building object onto the map. *
+ * BuildingTypeClass::Create_One_Of -- Creates a building of this type. *
+ * BuildingTypeClass::Dimensions -- Fetches the pixel dimensions of the building. *
+ * BuildingTypeClass::Display -- Renders a generic view of building. *
+ * BuildingTypeClass::Flush_For_Placement -- Tries to clear placement area for this building *
+ * BuildingTypeClass::Full_Name -- Fetches the name to give this building. *
+ * BuildingTypeClass::Height -- Determines the height of the building in icons. *
+ * BuildingTypeClass::Init -- Performs theater specific initialization. *
+ * BuildingTypeClass::Init_Anim -- Initialize an animation control for a building. *
+ * BuildingTypeClass::Init_Heap -- Initialize the heap as necessary for the building type obj*
+ * BuildingTypeClass::Max_Pips -- Determines the maximum pips to display. *
+ * BuildingTypeClass::Occupy_List -- Fetches the occupy list for the building. *
+ * BuildingTypeClass::One_Time -- Performs special one time action for buildings. *
+ * BuildingTypeClass::Overlap_List -- Fetches the overlap list for the building. *
+ * BuildingTypeClass::Prep_For_Add -- Prepares scenario editor for adding an object. *
+ * BuildingTypeClass::Raw_Cost -- Fetches the raw (base) cost of this building type. *
+ * BuildingTypeClass::Read_INI -- Fetch building type data from the INI database. *
+ * BuildingTypeClass::Width -- Determines width of building in icons. *
+ * BuildingTypeClass::operator delete -- Deletes a building type object from the special heap*
+ * BuildingTypeClass::operator new -- Allocates a building type object from the special heap.*
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+
+
+#define FATSHIP
+
+#define MCW MAP_CELL_W
+
+#define XYCELL(x,y) (y*MAP_CELL_W+x)
+static short const ExitPyle[] = {
+ XYCELL(1,2),
+ XYCELL(2,2),
+ XYCELL(0,2),
+ XYCELL(-1,2),
+ XYCELL(-1,-1),
+ XYCELL(0,-1),
+ XYCELL(1,-1),
+ XYCELL(2,-1),
+ XYCELL(2,-1),
+ XYCELL(-1,0),
+ XYCELL(2,0),
+ XYCELL(2,1),
+ XYCELL(-1,1),
+ REFRESH_EOL
+};
+
+static short const ExitSub[] = {
+ XYCELL( 0, 2),
+ XYCELL( 2, 2),
+ XYCELL(-1, 2),
+ XYCELL( 1, 2),
+ XYCELL( 3, 2)
+};
+
+static short const ExitWeap[] = {
+ XYCELL(1,2),
+ XYCELL(-1,3),
+ XYCELL(0,3),
+ XYCELL(1,3),
+ XYCELL(-2,3),
+ XYCELL(2,3),
+ REFRESH_EOL
+};
+
+static short const ComList[] = {0, 1, MCW, MCW+1, REFRESH_EOL};
+static short const List000111111[] = {(MCW*1), (MCW*1)+1, (MCW*1)+2, (MCW*2), (MCW*2)+1, (MCW*2)+2, REFRESH_EOL};
+static short const List0010[] = {MCW, REFRESH_EOL};
+static short const List0011[] = {(MCW*1), (MCW*1)+1, REFRESH_EOL};
+static short const List010111100[] = {1, (MCW*1), (MCW*1)+1, (MCW*1)+2, (MCW*2), REFRESH_EOL};
+static short const List0111[] = {1, (MCW*1), (MCW*1)+1, REFRESH_EOL};
+static short const List1000[] = {0, REFRESH_EOL};
+static short const List101000011[] = {0, 2, (MCW*2)+1, (MCW*2)+2, REFRESH_EOL};
+static short const List1100[] = {0, 1, REFRESH_EOL};
+static short const List1101[] = {0, 1, (MCW*1)+1, REFRESH_EOL};
+static short const List11[] = {0, 1, REFRESH_EOL};
+static short const List12[] = {MCW, REFRESH_EOL};
+static short const List1[] = {0, REFRESH_EOL};
+static short const List21[] = {0, 1, REFRESH_EOL};
+static short const List22[] = {0, 1, MCW, MCW+1, REFRESH_EOL};
+static short const List22_0011[] = {MCW, MCW+1, REFRESH_EOL};
+static short const List22_1100[] = {0, 1, REFRESH_EOL};
+static short const List2[] = {0, 1, MCW+1, MCW, REFRESH_EOL};
+static short const List32[] = {0, 1, 2, MCW, MCW+1, MCW+2, REFRESH_EOL};
+//static short const List42[] = {0, 1, 2, 3, MCW, MCW+1, MCW+2, MCW+3, REFRESH_EOL};
+static short const ListFix[] = {1, MCW, MCW+1, MCW+2, MCW+MCW+1, REFRESH_EOL};
+static short const ListWeap[] = {0, 1, 2, (MCW*1), (MCW*1)+1, (MCW*1)+2, REFRESH_EOL};
+static short const ListWestwood[] = {1, 2, 3, MCW+1, MCW+2, MCW+3, REFRESH_EOL};
+static short const OListSAM[] = {-MCW, -(MCW-1), REFRESH_EOL};
+#ifdef FATSHIP
+static short const ListSPen[] = {0, 1, 2, MCW, MCW+1, MCW+2, MCW+MCW, MCW+MCW+1, MCW+MCW+2, REFRESH_EOL};
+static short const OListSPen[] = {REFRESH_EOL};
+#else
+static short const ListSPen[] = {1, MCW, MCW+1, MCW+2, MCW+MCW+1, REFRESH_EOL};
+static short const OListSPen[] = {0, 2, MCW+MCW, MCW+MCW+2, REFRESH_EOL};
+#endif
+static short const OListWestwood[] = {0, MCW, REFRESH_EOL};
+static short const StoreList[] = {0, REFRESH_EOL};
+
+static short const ListFactory[] = {0, 1, 2, (MCW*1), (MCW*1)+1, (MCW*1)+2, (MCW*2), (MCW*2)+1, (MCW*2)+2, REFRESH_EOL};
+
+static short const OListFix[] = {0, 2, MCW+MCW, MCW+MCW+2, REFRESH_EOL};
+static short const OListWeap[] = {REFRESH_EOL};
+static short const OComList[] = {1, REFRESH_EOL};
+static short const OList12[] = {0, REFRESH_EOL};
+static short const OListTmpl[] = {0, 1, 2, REFRESH_EOL};
+
+
+/***************************************************************************
+*/
+static BuildingTypeClass const ClassBarrel(
+ STRUCT_BARREL,
+ TXT_BARREL, // NAME: Short name of the structure.
+ "BARL", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ false, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ true, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ true, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassBarrel3(
+ STRUCT_BARREL3,
+ TXT_BARREL, // NAME: Short name of the structure.
+ "BRL3", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ false, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ true, // Is it invisible to radar?
+ false, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ true, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassAVMine(
+ STRUCT_AVMINE,
+ TXT_AVMINE, // NAME: Short name of the structure.
+ "MINV", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_NONE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ false, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ true, // Is it invisible to radar?
+ false, // Can the player select this?
+ false, // Is this a legal target for attack or move?
+ true, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassAPMine(
+ STRUCT_APMINE,
+ TXT_APMINE, // NAME: Short name of the structure.
+ "MINP", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_NONE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ false, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ true, // Is it invisible to radar?
+ false, // Can the player select this?
+ false, // Is this a legal target for attack or move?
+ true, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassIronCurtain(
+ STRUCT_IRON_CURTAIN,
+ TXT_IRON_CURTAIN, // NAME: Short name of the structure.
+ "IRON", // NAME: Short name of the structure.
+ FACING_S, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_22, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List22_0011,// OCCUPYLIST: List of active foundation squares.
+ (short const *)List22_1100 // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassForwardCom(
+ STRUCT_FORWARD_COM,
+ TXT_FORWARD_COM, // NAME: Short name of the structure.
+ "FCOM", // NAME: Short name of the structure.
+ FACING_S, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_22, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List22_0011,// OCCUPYLIST: List of active foundation squares.
+ (short const *)List22_1100 // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassAdvancedTech(
+ STRUCT_ADVANCED_TECH,
+ TXT_ADVANCED_TECH, // NAME: Short name of the structure.
+ "ATEK", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_22, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List22, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassChronosphere(
+ STRUCT_CHRONOSPHERE,
+ TXT_CHRONOSPHERE, // NAME: Short name of the structure.
+ "PDOX", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_22, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List22, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassWeapon(
+ STRUCT_WEAP,
+ TXT_WEAPON_FACTORY, // NAME: Short name of the structure.
+ "WEAP", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XY_Coord(CELL_LEPTON_W+(CELL_LEPTON_W/2), CELL_LEPTON_H), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ false, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_UNITTYPE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_32, // SIZE: Building size.
+ (short const *)ExitWeap, // Preferred exit cell list.
+ (short const *)ListWeap, // OCCUPYLIST: List of active foundation squares.
+ (short const *)OListWeap // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassShipYard(
+ STRUCT_SHIP_YARD,
+ TXT_SHIP_YARD, // NAME: Short name of the structure.
+ "SYRD", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(22+(CELL_PIXEL_W/2), ((CELL_PIXEL_H*2)-(CELL_PIXEL_H/2))), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ false, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_VESSELTYPE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_33, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)ListSPen, // OCCUPYLIST: List of active foundation squares.
+ (short const *)OListSPen // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassSubPen(
+ STRUCT_SUB_PEN,
+ TXT_SUB_PEN, // NAME: Short name of the structure.
+ "SPEN", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(22+(CELL_PIXEL_W/2), ((CELL_PIXEL_H*2)-(CELL_PIXEL_H/2))), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ false, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_VESSELTYPE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_33, // SIZE: Building size.
+ (short const *)ExitSub, // Preferred exit cell list.
+ (short const *)ListSPen, // OCCUPYLIST: List of active foundation squares.
+ (short const *)OListSPen // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassPillbox(
+ STRUCT_PILLBOX,
+ TXT_PILLBOX, // NAME: Short name of the structure.
+ "PBOX", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0010, // Vertical offset.
+ 0x0040, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ false, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassCamoPillbox(
+ STRUCT_CAMOPILLBOX,
+ TXT_CAMOPILLBOX, // NAME: Short name of the structure.
+ "HBOX", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0010, // Vertical offset.
+ 0x0040, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ false, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassTesla(
+ STRUCT_TESLA,
+ TXT_TESLA, // NAME: Short name of the structure.
+ "TSLA", // NAME: Short name of the structure.
+ FACING_S, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x00C8, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ false, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_12, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List12, // OCCUPYLIST: List of active foundation squares.
+ (short const *)OList12 // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassTurret(
+ STRUCT_TURRET,
+ TXT_TURRET, // NAME: Short name of the structure.
+ "GUN", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0030, // Vertical offset.
+ 0x0080, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ false, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ true, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ (DirType)208, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassAAGun(
+ STRUCT_AAGUN,
+ TXT_AAGUN, // NAME: Short name of the structure.
+ "AGUN", // NAME: Short name of the structure.
+ FACING_S, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ false, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ true, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_NE, // Starting idle frame to match construction.
+ BSIZE_12, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List12, // OCCUPYLIST: List of active foundation squares.
+ (short const *)OList12 // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassFlameTurret(
+ STRUCT_FLAME_TURRET,
+ TXT_FLAME_TURRET, // NAME: Short name of the structure.
+ "FTUR", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ false, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassConst(
+ STRUCT_CONST,
+ TXT_CONST_YARD, // NAME: Short name of the structure.
+ "FACT", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ false, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_BUILDINGTYPE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_33, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)ListFactory,// OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassFakeConst(
+ STRUCT_FAKECONST,
+ TXT_FAKE_CONST, // NAME: Short name of the structure.
+ "FACF", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ true, // Is this building a fake (decoy?)
+ false, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_33, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)ListFactory,// OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassFakeWeapon(
+ STRUCT_FAKEWEAP,
+ TXT_FAKE_WEAP, // NAME: Short name of the structure.
+ "WEAF", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(10+(CELL_PIXEL_W/2), ((CELL_PIXEL_H*3)-(CELL_PIXEL_H/2))-21), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ true, // Is this building a fake (decoy?)
+ false, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_32, // SIZE: Building size.
+ (short const *)ExitWeap, // Preferred exit cell list.
+ (short const *)ListWeap, // OCCUPYLIST: List of active foundation squares.
+ (short const *)OListWeap // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassRefinery(
+ STRUCT_REFINERY,
+ TXT_REFINERY, // NAME: Short name of the structure.
+ "PROC", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ false, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_33, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List010111100, // OCCUPYLIST: List of active foundation squares.
+ (short const *)List101000011 // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassStorage(
+ STRUCT_STORAGE,
+ TXT_STORAGE, // NAME: Short name of the structure.
+ "SILO", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ false, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)StoreList, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassHelipad(
+ STRUCT_HELIPAD,
+ TXT_HELIPAD, // NAME: Short name of the structure.
+ "HPAD", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ false, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_AIRCRAFTTYPE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_22, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List2, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassCommand(
+ STRUCT_RADAR,
+ TXT_COMMAND, // NAME: Short name of the structure.
+ "DOME", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_22, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)ComList, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassGapGenerator(
+ STRUCT_GAP,
+ TXT_GAP_GENERATOR, // NAME: Short name of the structure.
+ "GAP", // NAME: Short name of the structure.
+ FACING_S, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_12, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List0010, // OCCUPYLIST: List of active foundation squares.
+ (short const *)List1 // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassSAM(
+ STRUCT_SAM,
+ TXT_SAM, // NAME: Short name of the structure.
+ "SAM", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0030, // Vertical offset.
+ 0x0080, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ false, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ true, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_21, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List21, // OCCUPYLIST: List of active foundation squares.
+ (short const *)OListSAM // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassMissileSilo(
+ STRUCT_MSLO,
+ TXT_MSLO, // NAME: Short name of the structure.
+ "MSLO", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_21, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List21, // OCCUPYLIST: List of active foundation squares.
+ (short const *)OListSAM // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassAirStrip(
+ STRUCT_AIRSTRIP,
+ TXT_AIRSTRIP, // NAME: Short name of the structure.
+ "AFLD", // NAME: Short name of the structure.
+ FACING_S, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_AIRCRAFTTYPE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_32, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List32, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassPower(
+ STRUCT_POWER,
+ TXT_POWER, // NAME: Short name of the structure.
+ "POWR", // NAME: Short name of the structure.
+ FACING_S, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_22, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List22, // OCCUPYLIST: List of active foundation squares.
+ (short const *)List22_1100 // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassAdvancedPower(
+ STRUCT_ADVANCED_POWER,
+ TXT_ADVANCED_POWER, // NAME: Short name of the structure.
+ "APWR", // NAME: Short name of the structure.
+ FACING_S, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_33, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List000111111, // OCCUPYLIST: List of active foundation squares.
+ (short const *)OListTmpl // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassSovietTech(
+ STRUCT_SOVIET_TECH,
+ TXT_SOVIET_TECH, // NAME: Short name of the structure.
+ "STEK", // NAME: Short name of the structure.
+ FACING_S, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_33, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List000111111, // OCCUPYLIST: List of active foundation squares.
+ (short const *)OListTmpl // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassHospital(
+ STRUCT_HOSPITAL,
+ TXT_HOSPITAL, // NAME: Short name of the structure.
+ "HOSP", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_22, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List2, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassBioLab(
+ STRUCT_BIO_LAB,
+ TXT_BIO_LAB, // NAME: Short name of the structure.
+ "BIO", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_22, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List2, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassBarracks(
+ STRUCT_BARRACKS,
+ TXT_BARRACKS, // NAME: Short name of the structure.
+ "BARR", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+// XYP_COORD(24,47), // Exit point for produced units.
+ XYP_COORD(18,47), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_INFANTRYTYPE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_22, // SIZE: Building size.
+ (short const *)ExitPyle, // Preferred exit cell list.
+ (short const *)List22, // OCCUPYLIST: List of active foundation squares.
+ NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassTent(
+ STRUCT_TENT,
+ TXT_BARRACKS, // NAME: Short name of the structure.
+ "TENT", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(24,47), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_INFANTRYTYPE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_22, // SIZE: Building size.
+ (short const *)ExitPyle, // Preferred exit cell list.
+ (short const *)List22, // OCCUPYLIST: List of active foundation squares.
+ NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassKennel(
+ STRUCT_KENNEL,
+ TXT_KENNEL, // NAME: Short name of the structure.
+ "KENN", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(8,16), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_INFANTRYTYPE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+// (short const *)ExitPyle, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassFakeShipYard(
+ STRUCT_FAKE_YARD,
+ TXT_FAKE_YARD, // NAME: Short name of the structure.
+ "SYRF", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(22+(CELL_PIXEL_W/2), ((CELL_PIXEL_H*2)-(CELL_PIXEL_H/2))), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ true, // Is this building a fake (decoy?)
+ false, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_33, // SIZE: Building size.
+ (short const *)ExitWeap, // Preferred exit cell list.
+ (short const *)ListSPen, // OCCUPYLIST: List of active foundation squares.
+ (short const *)OListSPen // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassFakeSubPen(
+ STRUCT_FAKE_PEN,
+ TXT_FAKE_PEN, // NAME: Short name of the structure.
+ "SPEF", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(22+(CELL_PIXEL_W/2), ((CELL_PIXEL_H*2)-(CELL_PIXEL_H/2))), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ true, // Is this building a fake (decoy?)
+ false, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_33, // SIZE: Building size.
+ (short const *)ExitSub, // Preferred exit cell list.
+ (short const *)ListSPen, // OCCUPYLIST: List of active foundation squares.
+ (short const *)OListSPen // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassFakeCommand(
+ STRUCT_FAKE_RADAR,
+ TXT_FAKE_RADAR, // NAME: Short name of the structure.
+ "DOMF", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ true, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_22, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)ComList, // OCCUPYLIST: List of active foundation squares.
+ (short const *)OComList // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassRepair(
+ STRUCT_REPAIR,
+ TXT_FIX_IT, // NAME: Short name of the structure.
+ "FIX", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ false, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_33, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)ListFix, // OCCUPYLIST: List of active foundation squares.
+ (short const *)OListFix // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV01(
+ STRUCT_V01,
+ TXT_CIV1, // NAME: Short name of the structure.
+ "V01", // NAME: Short name of the structure.
+ FACING_S, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ true, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ true, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_22, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List0011, // OCCUPYLIST: List of active foundation squares.
+ (short const *)List1100 // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV02(
+ STRUCT_V02,
+ TXT_CIV2, // NAME: Short name of the structure.
+ "V02", // NAME: Short name of the structure.
+ FACING_S, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ true, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ true, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_22, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List0011, // OCCUPYLIST: List of active foundation squares.
+ (short const *)List1100 // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV03(
+ STRUCT_V03,
+ TXT_CIV3, // NAME: Short name of the structure.
+ "V03", // NAME: Short name of the structure.
+ FACING_S, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ true, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ true, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_22, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List0111, // OCCUPYLIST: List of active foundation squares.
+ (short const *)List1000 // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV04(
+ STRUCT_V04,
+ TXT_CIV4, // NAME: Short name of the structure.
+ "V04", // NAME: Short name of the structure.
+ FACING_S, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ true, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ true, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_22, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List0011, // OCCUPYLIST: List of active foundation squares.
+ (short const *)List1100 // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV05(
+ STRUCT_V05,
+ TXT_CIV5, // NAME: Short name of the structure.
+ "V05", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ true, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ true, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_21, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List11, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV06(
+ STRUCT_V06,
+ TXT_CIV6, // NAME: Short name of the structure.
+ "V06", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ true, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ true, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_21, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List11, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV07(
+ STRUCT_V07,
+ TXT_CIV7, // NAME: Short name of the structure.
+ "V07", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ true, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ true, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_21, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List11, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV08(
+ STRUCT_V08,
+ TXT_CIV8, // NAME: Short name of the structure.
+ "V08", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ true, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ true, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV09(
+ STRUCT_V09,
+ TXT_CIV9, // NAME: Short name of the structure.
+ "V09", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ true, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ true, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV10(
+ STRUCT_V10,
+ TXT_CIV10, // NAME: Short name of the structure.
+ "V10", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ true, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ true, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV11(
+ STRUCT_V11,
+ TXT_CIV11, // NAME: Short name of the structure.
+ "V11", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ true, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ true, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV12(
+ STRUCT_V12,
+ TXT_CIV12, // NAME: Short name of the structure.
+ "V12", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ true, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ true, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV13(
+ STRUCT_V13,
+ TXT_CIV13, // NAME: Short name of the structure.
+ "V13", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ true, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ true, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV14(
+ STRUCT_V14,
+ TXT_CIV14, // NAME: Short name of the structure.
+ "V14", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ true, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ true, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV15(
+ STRUCT_V15,
+ TXT_CIV15, // NAME: Short name of the structure.
+ "V15", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ true, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ true, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV16(
+ STRUCT_V16,
+ TXT_CIV16, // NAME: Short name of the structure.
+ "V16", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ true, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ true, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV17(
+ STRUCT_V17,
+ TXT_CIV17, // NAME: Short name of the structure.
+ "V17", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ true, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ true, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV18(
+ STRUCT_V18,
+ TXT_CIV18, // NAME: Short name of the structure.
+ "V18", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ true, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ true, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV19(
+ STRUCT_PUMP,
+ TXT_PUMP, // NAME: Short name of the structure.
+ "V19", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV20(
+ STRUCT_V20,
+ TXT_CIV20, // NAME: Short name of the structure.
+ "V20", // NAME: Short name of the structure.
+ FACING_S, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_22, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List0011, // OCCUPYLIST: List of active foundation squares.
+ (short const *)List1100 // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV21(
+ STRUCT_V21,
+ TXT_CIV21, // NAME: Short name of the structure.
+ "V21", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_22, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1101, // OCCUPYLIST: List of active foundation squares.
+ (short const *)List0010 // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV22(
+ STRUCT_V22,
+ TXT_CIV22, // NAME: Short name of the structure.
+ "V22", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_21, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List11, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV23(
+ STRUCT_V23,
+ TXT_CIV23, // NAME: Short name of the structure.
+ "V23", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV24(
+ STRUCT_V24,
+ TXT_CIV24, // NAME: Short name of the structure.
+ "V24", // NAME: Short name of the structure.
+ FACING_S, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_22, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List0011, // OCCUPYLIST: List of active foundation squares.
+ (short const *)List1100 // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV25(
+ STRUCT_V25,
+ TXT_CIV25, // NAME: Short name of the structure.
+ "V25", // NAME: Short name of the structure.
+ FACING_S, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_22, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List0111, // OCCUPYLIST: List of active foundation squares.
+ (short const *)List1000 // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV26(
+ STRUCT_V26,
+ TXT_CIV26, // NAME: Short name of the structure.
+ "V26", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_21, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List11, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV27(
+ STRUCT_V27,
+ TXT_CIV27, // NAME: Short name of the structure.
+ "V27", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV28(
+ STRUCT_V28,
+ TXT_CIV28, // NAME: Short name of the structure.
+ "V28", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV29(
+ STRUCT_V29,
+ TXT_CIV29, // NAME: Short name of the structure.
+ "V29", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV30(
+ STRUCT_V30,
+ TXT_CIV30, // NAME: Short name of the structure.
+ "V30", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_21, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List11, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV31(
+ STRUCT_V31,
+ TXT_CIV31, // NAME: Short name of the structure.
+ "V31", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_21, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List11, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV32(
+ STRUCT_V32,
+ TXT_CIV32, // NAME: Short name of the structure.
+ "V32", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_21, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List11, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV33(
+ STRUCT_V33,
+ TXT_CIV33, // NAME: Short name of the structure.
+ "V33", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_21, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List11, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV34(
+ STRUCT_V34,
+ TXT_CIV34, // NAME: Short name of the structure.
+ "V34", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV35(
+ STRUCT_V35,
+ TXT_CIV35, // NAME: Short name of the structure.
+ "V35", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+static BuildingTypeClass const ClassV36(
+ STRUCT_V36,
+ TXT_CIV36, // NAME: Short name of the structure.
+ "V36", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+static BuildingTypeClass const ClassV37(
+ STRUCT_V37,
+ TXT_CIV37, // NAME: Short name of the structure.
+ "V37", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ true, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_42, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)ListWestwood, // OCCUPYLIST: List of active foundation squares.
+ (short const *)OListWestwood // OVERLAPLIST:List of overlap cell offset.
+);
+static BuildingTypeClass const ClassMission(
+ STRUCT_MISSION,
+ TXT_CIVMISS, // NAME: Short name of the structure.
+ "MISS", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_32, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List32, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+// Sandbag wall
+static BuildingTypeClass const Sandbag(
+ STRUCT_SANDBAG_WALL,
+ TXT_SANDBAG_WALL, // NAME: Short name of the structure.
+ "SBAG", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_NONE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ false, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ true, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ false, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ true, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+// Cyclone fence
+static BuildingTypeClass const Cyclone(
+ STRUCT_CYCLONE_WALL,
+ TXT_CYCLONE_WALL, // NAME: Short name of the structure.
+ "CYCL", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_NONE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ false, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ true, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ false, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ true, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+// Brick wall
+static BuildingTypeClass const Brick(
+ STRUCT_BRICK_WALL,
+ TXT_BRICK_WALL, // NAME: Short name of the structure.
+ "BRIK", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_NONE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ false, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ true, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ false, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ true, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+// Barbwire wall
+static BuildingTypeClass const Barbwire(
+ STRUCT_BARBWIRE_WALL,
+ TXT_BARBWIRE_WALL, // NAME: Short name of the structure.
+ "BARB", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_NONE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ false, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ true, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ false, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ true, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+// Wood wall
+static BuildingTypeClass const Wood(
+ STRUCT_WOOD_WALL,
+ TXT_WOOD_WALL, // NAME: Short name of the structure.
+ "WOOD", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_NONE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ false, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ true, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ false, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ true, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+static BuildingTypeClass const Fence(
+ STRUCT_FENCE,
+ TXT_FENCE, // NAME: Short name of the structure.
+ "FENC", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_NONE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ false, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ true, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ false, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ true, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+
+
+#ifdef FIXIT_ANTS
+static BuildingTypeClass const ClassQueen(
+ STRUCT_QUEEN,
+ TXT_NONE, // NAME: Short name of the structure.
+ "QUEE", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(24,47), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ true, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ false, // Simple (one frame) damage imagery?
+ false, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ false, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ true, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_21, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List11, // OCCUPYLIST: List of active foundation squares.
+ NULL // OVERLAPLIST:List of overlap cell offset.
+);
+static BuildingTypeClass const ClassLarva1(
+ STRUCT_LARVA1,
+ TXT_NONE, // NAME: Short name of the structure.
+ "LAR1", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ false, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ true, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ true, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+static BuildingTypeClass const ClassLarva2(
+ STRUCT_LARVA2,
+ TXT_NONE, // NAME: Short name of the structure.
+ "LAR2", // NAME: Short name of the structure.
+ FACING_NONE, // Foundation direction from center of building.
+ XYP_COORD(0,0), // Exit point for produced units.
+ REMAP_ALTERNATE, // Sidebar remap logic.
+ 0x0000, // Vertical offset.
+ 0x0000, // Primary weapon offset along turret centerline.
+ 0x0000, // Primary weapon lateral offset along turret centerline.
+ false, // Is this building a fake (decoy?)
+ false, // Animation rate is regulated for constant speed?
+ true, // Always use the given name for the building?
+ false, // Is this a wall type structure?
+ true, // Simple (one frame) damage imagery?
+ true, // Is it invisible to radar?
+ true, // Can the player select this?
+ true, // Is this a legal target for attack or move?
+ true, // Is this an insignificant building?
+ false, // Theater specific graphic image?
+ false, // Does it have a rotating turret?
+ false, // Can the building be color remapped to indicate owner?
+ RTTI_NONE, // The object type produced at this factory.
+ DIR_N, // Starting idle frame to match construction.
+ BSIZE_11, // SIZE: Building size.
+ NULL, // Preferred exit cell list.
+ (short const *)List1, // OCCUPYLIST: List of active foundation squares.
+ (short const *)NULL // OVERLAPLIST:List of overlap cell offset.
+);
+#endif
+void const * BuildingTypeClass::WarFactoryOverlay;
+void const * LightningShapes;
+
+
+/***********************************************************************************************
+ * BuildingTypeClass::BuildingTypeClass -- This is the constructor for the building types. *
+ * *
+ * This is the constructor used to create the building types. *
+ * *
+ * INPUT: see below... *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/29/1994 JLB : Created. *
+ *=============================================================================================*/
+BuildingTypeClass::BuildingTypeClass(
+ StructType type,
+ int name,
+ char const * ininame,
+ FacingType foundation,
+ COORDINATE exitpoint,
+ RemapType remap,
+ int verticaloffset,
+ int primaryoffset,
+ int primarylateral,
+ bool is_fake,
+ bool is_regulated,
+ bool is_nominal,
+ bool is_wall,
+ bool is_simpledamage,
+ bool is_stealthy,
+ bool is_selectable,
+ bool is_legal_target,
+ bool is_insignificant,
+ bool is_theater,
+ bool is_turret_equipped,
+ bool is_remappable,
+ RTTIType tobuild,
+ DirType sframe,
+ BSizeType size,
+ short const * exitlist,
+ short const * sizelist,
+ short const * overlap) :
+ TechnoTypeClass(RTTI_BUILDINGTYPE,
+ int(type),
+ name,
+ ininame,
+ remap,
+ verticaloffset,
+ primaryoffset,
+ primarylateral,
+ primaryoffset,
+ primarylateral,
+ is_nominal,
+ is_stealthy,
+ is_selectable,
+ is_legal_target,
+ is_insignificant,
+ false,
+ is_theater,
+ is_turret_equipped,
+ is_remappable,
+ true,
+ (is_turret_equipped ? 32 : 1),
+ SPEED_NONE),
+ IsBase(true),
+ IsFake(is_fake),
+ IsBibbed(false),
+ IsWall(is_wall),
+ IsSimpleDamage(is_simpledamage),
+ IsCaptureable(false),
+ IsRegulated(is_regulated),
+ IsPowered(false),
+ IsUnsellable(false),
+ FoundationFace(foundation),
+ Adjacent(1),
+ ToBuild(tobuild),
+ ExitCoordinate(exitpoint),
+ ExitList(exitlist),
+ Type(type),
+ StartFace(sframe),
+ Capacity(0),
+ Power(0),
+ Drain(0),
+ Size(size),
+ OccupyList(sizelist),
+ OverlapList(overlap),
+ BuildupData(0)
+{
+
+ Anims[BSTATE_CONSTRUCTION].Start = 0;
+ Anims[BSTATE_CONSTRUCTION].Count = 1;
+ Anims[BSTATE_CONSTRUCTION].Rate = 0;
+
+ Anims[BSTATE_IDLE].Start = 0;
+ Anims[BSTATE_IDLE].Count = 1;
+ Anims[BSTATE_IDLE].Rate = 0;
+
+ Anims[BSTATE_ACTIVE].Start = 0;
+ Anims[BSTATE_ACTIVE].Count = 1;
+ Anims[BSTATE_ACTIVE].Rate = 0;
+
+ Anims[BSTATE_AUX1].Start = 0;
+ Anims[BSTATE_AUX1].Count = 1;
+ Anims[BSTATE_AUX1].Rate = 0;
+
+ Anims[BSTATE_AUX2].Start = 0;
+ Anims[BSTATE_AUX2].Count = 1;
+ Anims[BSTATE_AUX2].Rate = 0;
+}
+
+
+/***********************************************************************************************
+ * BuildingTypeClass::operator new -- Allocates a building type object from the special heap. *
+ * *
+ * This routine will allocate a building type object from the special heap used just for *
+ * allocation of object of this type. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with a pointer to the newly allocated object. If the allocation could not *
+ * succeed, then NULL will be returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/06/1996 JLB : Created. *
+ *=============================================================================================*/
+void * BuildingTypeClass::operator new(size_t)
+{
+ return(BuildingTypes.Alloc());
+}
+
+
+/***********************************************************************************************
+ * BuildingTypeClass::operator delete -- Deletes a building type object from the special heap. *
+ * *
+ * This will delete a previously allocated building type object. The memory is returned *
+ * to the special heap that is used for that purpose. *
+ * *
+ * INPUT: ptr -- Pointer to the building type object to return to the special heap. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/06/1996 JLB : Created. *
+ *=============================================================================================*/
+void BuildingTypeClass::operator delete(void * ptr)
+{
+ BuildingTypes.Free((BuildingTypeClass *)ptr);
+}
+
+
+/***********************************************************************************************
+ * BuildingTypeClass::Init_Heap -- Initialize the heap as necessary for the building type obje *
+ * *
+ * This routine performs the necessary heap initializations. Since we know exactly what *
+ * building type objects will be needed, they are pre-allocated at this time. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: Call this routine only once. *
+ * *
+ * HISTORY: *
+ * 07/06/1996 JLB : Created. *
+ *=============================================================================================*/
+void BuildingTypeClass::Init_Heap(void)
+{
+ /*
+ ** These building type class objects must be allocated in the exact order that they
+ ** are specified in the StructType enumeration. This is necessary because the heap
+ ** allocation block index serves double duty as the type number index.
+ */
+ new BuildingTypeClass(ClassAdvancedTech); // STRUCT_ADVANCED_TECH
+ new BuildingTypeClass(ClassIronCurtain); // STRUCT_IRON_CURTAIN
+ new BuildingTypeClass(ClassWeapon); // STRUCT_WEAP
+ new BuildingTypeClass(ClassChronosphere); // STRUCT_CHRONOSPHERE
+ new BuildingTypeClass(ClassPillbox); // STRUCT_PILLBOX
+ new BuildingTypeClass(ClassCamoPillbox); // STRUCT_CAMOPILLBOX
+ new BuildingTypeClass(ClassCommand); // STRUCT_RADAR
+ new BuildingTypeClass(ClassGapGenerator); // STRUCT_GAP
+ new BuildingTypeClass(ClassTurret); // STRUCT_TURRET
+ new BuildingTypeClass(ClassAAGun); // STRUCT_AAGUN
+ new BuildingTypeClass(ClassFlameTurret); // STRUCT_FLAME_TURRET
+ new BuildingTypeClass(ClassConst); // STRUCT_CONST
+ new BuildingTypeClass(ClassRefinery); // STRUCT_REFINERY
+ new BuildingTypeClass(ClassStorage); // STRUCT_STORAGE
+ new BuildingTypeClass(ClassHelipad); // STRUCT_HELIPAD
+ new BuildingTypeClass(ClassSAM); // STRUCT_SAM
+ new BuildingTypeClass(ClassAirStrip); // STRUCT_AIRSTRIP
+ new BuildingTypeClass(ClassPower); // STRUCT_POWER
+ new BuildingTypeClass(ClassAdvancedPower);// STRUCT_ADVANCED_POWER
+ new BuildingTypeClass(ClassSovietTech); // STRUCT_SOVIET_TECH
+ new BuildingTypeClass(ClassHospital); // STRUCT_HOSPITAL
+ new BuildingTypeClass(ClassBarracks); // STRUCT_BARRACKS
+ new BuildingTypeClass(ClassTent); // STRUCT_TENT
+ new BuildingTypeClass(ClassKennel); // STRUCT_KENNEL
+ new BuildingTypeClass(ClassRepair); // STRUCT_REPAIR
+ new BuildingTypeClass(ClassBioLab); // STRUCT_BIO_LAB
+ new BuildingTypeClass(ClassMission); // STRUCT_MISSION
+ new BuildingTypeClass(ClassShipYard); // STRUCT_SHIP_YARD
+ new BuildingTypeClass(ClassSubPen); // STRUCT_SUB_PEN
+ new BuildingTypeClass(ClassMissileSilo); // STRUCT_MSLO
+ new BuildingTypeClass(ClassForwardCom); // STRUCT_FORWARD_COM
+ new BuildingTypeClass(ClassTesla); // STRUCT_TESLA
+ new BuildingTypeClass(ClassFakeWeapon); // STRUCT_FAKEWEAP
+ new BuildingTypeClass(ClassFakeConst); // STRUCT_FAKECONST
+ new BuildingTypeClass(ClassFakeShipYard); // STRUCT_FAKE_YARD
+ new BuildingTypeClass(ClassFakeSubPen); // STRUCT_FAKE_PEN
+ new BuildingTypeClass(ClassFakeCommand); // STRUCT_FAKE_RADAR
+ new BuildingTypeClass(Sandbag); // STRUCT_SANDBAG_WALL
+ new BuildingTypeClass(Cyclone); // STRUCT_CYCLONE_WALL
+ new BuildingTypeClass(Brick); // STRUCT_BRICK_WALL
+ new BuildingTypeClass(Barbwire); // STRUCT_BARBWIRE_WALL
+ new BuildingTypeClass(Wood); // STRUCT_WOOD_WALL
+ new BuildingTypeClass(Fence); // STRUCT_FENCE
+ new BuildingTypeClass(ClassAVMine); // STRUCT_AVMINE
+ new BuildingTypeClass(ClassAPMine); // STRUCT_APMINE
+ new BuildingTypeClass(ClassV01); // STRUCT_V1
+ new BuildingTypeClass(ClassV02); // STRUCT_V2
+ new BuildingTypeClass(ClassV03); // STRUCT_V3
+ new BuildingTypeClass(ClassV04); // STRUCT_V4
+ new BuildingTypeClass(ClassV05); // STRUCT_V5
+ new BuildingTypeClass(ClassV06); // STRUCT_V6
+ new BuildingTypeClass(ClassV07); // STRUCT_V7
+ new BuildingTypeClass(ClassV08); // STRUCT_V8
+ new BuildingTypeClass(ClassV09); // STRUCT_V9
+ new BuildingTypeClass(ClassV10); // STRUCT_V10
+ new BuildingTypeClass(ClassV11); // STRUCT_V11
+ new BuildingTypeClass(ClassV12); // STRUCT_V12
+ new BuildingTypeClass(ClassV13); // STRUCT_V13
+ new BuildingTypeClass(ClassV14); // STRUCT_V14
+ new BuildingTypeClass(ClassV15); // STRUCT_V15
+ new BuildingTypeClass(ClassV16); // STRUCT_V16
+ new BuildingTypeClass(ClassV17); // STRUCT_V17
+ new BuildingTypeClass(ClassV18); // STRUCT_V18
+ new BuildingTypeClass(ClassV19); // STRUCT_PUMP
+ new BuildingTypeClass(ClassV20); // STRUCT_V20
+ new BuildingTypeClass(ClassV21); // STRUCT_V21
+ new BuildingTypeClass(ClassV22); // STRUCT_V22
+ new BuildingTypeClass(ClassV23); // STRUCT_V23
+ new BuildingTypeClass(ClassV24); // STRUCT_V24
+ new BuildingTypeClass(ClassV25); // STRUCT_V25
+ new BuildingTypeClass(ClassV26); // STRUCT_V26
+ new BuildingTypeClass(ClassV27); // STRUCT_V27
+ new BuildingTypeClass(ClassV28); // STRUCT_V28
+ new BuildingTypeClass(ClassV29); // STRUCT_V29
+ new BuildingTypeClass(ClassV30); // STRUCT_V30
+ new BuildingTypeClass(ClassV31); // STRUCT_V31
+ new BuildingTypeClass(ClassV32); // STRUCT_V32
+ new BuildingTypeClass(ClassV33); // STRUCT_V33
+ new BuildingTypeClass(ClassV34); // STRUCT_V34
+ new BuildingTypeClass(ClassV35); // STRUCT_V35
+ new BuildingTypeClass(ClassV36); // STRUCT_V36
+ new BuildingTypeClass(ClassV37); // STRUCT_V37
+ new BuildingTypeClass(ClassBarrel); // STRUCT_BARREL
+ new BuildingTypeClass(ClassBarrel3); // STRUCT_BARREL3
+
+#ifdef FIXIT_ANTS
+ new BuildingTypeClass(ClassQueen); // STRUCT_QUEEN
+ new BuildingTypeClass(ClassLarva1); // STRUCT_LARVA1
+ new BuildingTypeClass(ClassLarva2); // STRUCT_LARVA2
+#endif
+
+}
+
+
+/***********************************************************************************************
+ * BuildingTypeClass::One_Time -- Performs special one time action for buildings. *
+ * *
+ * This routine is used to do the one time action necessary to handle building type class *
+ * objects. This entails loading of the building shapes and the brain file used by *
+ * buildings. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: This routine should only be called ONCE. *
+ * *
+ * HISTORY: *
+ * 05/28/1994 JLB : Created. *
+ * 06/11/1994 JLB : Updated construction time and frame count logic. *
+ *=============================================================================================*/
+void BuildingTypeClass::One_Time(void)
+{
+ static const struct {
+ StructType Class; // Building class number.
+ BStateType Stage; // Animation sequence to assign animation range to.
+ int Start; // Starting frame number.
+ int Length; // Number of frames (-1 means use all frames).
+ int Rate; // Rate of animation.
+ } _anims[] = {
+ {STRUCT_CHRONOSPHERE, BSTATE_IDLE, 0, 4, 3}, // idling
+ {STRUCT_CHRONOSPHERE, BSTATE_ACTIVE, 4, 16,3}, // charging up and activating
+ {STRUCT_MSLO, BSTATE_IDLE, 0, 0, 0},
+ {STRUCT_MSLO, BSTATE_ACTIVE, 0, 5, 2}, // door opening
+ {STRUCT_MSLO, BSTATE_AUX1, 4, 1, 0}, // door held open
+ {STRUCT_MSLO, BSTATE_AUX2, 5, 3, 2}, // door closing
+ {STRUCT_CAMOPILLBOX, BSTATE_ACTIVE, 0, 2, 1},
+ {STRUCT_GAP, BSTATE_IDLE, 0, 32,3},
+ {STRUCT_AIRSTRIP, BSTATE_IDLE, 0, 0, 0},
+ {STRUCT_AIRSTRIP, BSTATE_AUX1, 0, 8, 3},
+ {STRUCT_BARRACKS, BSTATE_ACTIVE, 0, 10,3},
+ {STRUCT_BARRACKS, BSTATE_IDLE, 0, 10,3},
+ {STRUCT_TENT, BSTATE_ACTIVE, 0, 10,3},
+ {STRUCT_TENT, BSTATE_IDLE, 0, 10,3},
+#ifdef FIXIT_ANTS
+ {STRUCT_QUEEN, BSTATE_IDLE, 0, 10,3},
+#endif
+ {STRUCT_CONST, BSTATE_ACTIVE, 0, 26,3},
+ {STRUCT_FAKECONST, BSTATE_ACTIVE, 0, 26,3},
+ {STRUCT_HELIPAD, BSTATE_ACTIVE, 0, 7, 4},
+ {STRUCT_HELIPAD, BSTATE_IDLE, 0, 0, 0},
+ {STRUCT_HOSPITAL, BSTATE_IDLE, 0, 4, 3},
+ {STRUCT_PUMP, BSTATE_IDLE, 0, 14,4},
+ {STRUCT_REPAIR, BSTATE_ACTIVE, 0, 7, 2},
+ {STRUCT_REPAIR, BSTATE_IDLE, 0, 1, 0},
+ {STRUCT_V20, BSTATE_IDLE, 0, 3, 3},
+ {STRUCT_V21, BSTATE_IDLE, 0, 3, 3},
+ {STRUCT_V22, BSTATE_IDLE, 0, 3, 3},
+ {STRUCT_V23, BSTATE_IDLE, 0, 3, 3},
+ {STRUCT_WEAP, BSTATE_ACTIVE, 0, 1, 0},
+ {STRUCT_WEAP, BSTATE_IDLE, 0, 1, 0},
+ {STRUCT_FAKEWEAP, BSTATE_ACTIVE, 0, 1, 0},
+ {STRUCT_FAKEWEAP, BSTATE_IDLE, 0, 1, 0},
+ {STRUCT_IRON_CURTAIN, BSTATE_ACTIVE, 0, 11,3},
+ {STRUCT_TESLA, BSTATE_ACTIVE, 0, 10,2},
+ };
+
+ for (StructType sindex = STRUCT_FIRST; sindex < STRUCT_COUNT; sindex++) {
+ char fullname[_MAX_FNAME+_MAX_EXT];
+ char buffer[_MAX_FNAME];
+ BuildingTypeClass const & building = As_Reference(sindex);
+ /*
+ ** Fetch the sidebar cameo image for this building.
+ */
+ if (building.Level != -1) {
+// if (building.IsBuildable) {
+ sprintf(buffer, "%sICON", building.Graphic_Name());
+
+ if (building.IsFake) {
+ buffer[3] = 'F';
+ }
+
+ _makepath(fullname, NULL, NULL, buffer, ".SHP");
+ ((void const *&)building.CameoData) = MFCD::Retrieve(fullname);
+ }
+
+ /*
+ ** Fetch the construction animation for this building.
+ */
+ sprintf(buffer, "%sMAKE", building.Graphic_Name());
+ _makepath(fullname, NULL, NULL, buffer, ".SHP");
+ void const * dataptr;
+ dataptr = MFCD::Retrieve(fullname);
+ ((void const *&)building.BuildupData) = dataptr;
+ if (dataptr != NULL) {
+ int timedelay = 1;
+ int count = Get_Build_Frame_Count(dataptr);
+ if (count > 0) {
+ timedelay = (Rule.BuildupTime * TICKS_PER_MINUTE) / count;
+ }
+ building.Init_Anim(BSTATE_CONSTRUCTION, 0, count, timedelay);
+ }
+
+ /*
+ ** Fetch the normal game shape for this building.
+ */
+ _makepath(fullname, NULL, NULL, building.Graphic_Name(), ".SHP");
+ ((void const *&)building.ImageData) = MFCD::Retrieve(fullname);
+ }
+
+ // Try to load weap2.shp and tesla coil's lightning shapes
+ char fullname[_MAX_FNAME+_MAX_EXT];
+ _makepath(fullname, NULL, NULL, (char const *)"WEAP2",".SHP");
+ WarFactoryOverlay = MFCD::Retrieve(fullname);
+ _makepath(fullname, NULL, NULL, (char const *)"LITNING",".SHP");
+ LightningShapes = MFCD::Retrieve(fullname);
+
+ /*
+ ** Install all the special animation sequences for the different building types.
+ */
+ for (unsigned index = 0; index < (sizeof(_anims) / sizeof(_anims[0])); index++) {
+ As_Reference(_anims[index].Class).Init_Anim(_anims[index].Stage, _anims[index].Start, _anims[index].Length, _anims[index].Rate);
+ }
+
+}
+
+
+/***********************************************************************************************
+ * Struct_From_Name -- Find BData structure from its name. *
+ * *
+ * This routine will convert an ASCII name for a building class into *
+ * the actual building class it represents. *
+ * *
+ * INPUT: name -- ASCII representation of a building class. *
+ * *
+ * OUTPUT: Returns with the actual building class number that the string *
+ * represents. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 10/07/1992 JLB : Created. *
+ * 05/02/1994 JLB : Converted to member function. *
+ *=============================================================================================*/
+StructType BuildingTypeClass::From_Name(char const * name)
+{
+ if (name != NULL) {
+ for (StructType classid = STRUCT_FIRST; classid < STRUCT_COUNT; classid++) {
+ if (stricmp(As_Reference(classid).IniName, name) == 0) {
+ return(classid);
+ }
+ }
+ }
+ return(STRUCT_NONE);
+}
+
+
+#ifdef SCENARIO_EDITOR
+/***********************************************************************************************
+ * BuildingTypeClass::Display -- Renders a generic view of building. *
+ * *
+ * This routine is used to display a generic representation of the *
+ * building. Typical use of this occurs with the scenario editor. *
+ * *
+ * INPUT: x,y -- Coordinate to display the building (centered). *
+ * *
+ * window -- The window the building should be rendered *
+ * relative to. *
+ * *
+ * house -- The house color to use for the building. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/23/1994 JLB : Created. *
+ *=============================================================================================*/
+void BuildingTypeClass::Display(int x, int y, WindowNumberType window, HousesType ) const
+{
+ void const * ptr = Get_Cameo_Data();
+ if (ptr == NULL) {
+ IsTheaterShape = IsTheater;
+ ptr = Get_Image_Data();
+ }
+ CC_Draw_Shape(ptr, 0, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL);
+ IsTheaterShape = false;
+}
+
+
+/***********************************************************************************************
+ * BuildingTypeClass::Prep_For_Add -- Prepares scenario editor for adding a *
+ * *
+ * This routine is used to prepare the scenario editor for the addition *
+ * of a building object to the game. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/23/1994 JLB : Created. *
+ * 06/04/1994 JLB : Uses map editing interface routines. *
+ *=============================================================================================*/
+void BuildingTypeClass::Prep_For_Add(void)
+{
+ for (StructType index = STRUCT_FIRST; index < STRUCT_COUNT; index++) {
+ if (As_Reference(index).Get_Image_Data()) {
+ Map.Add_To_List(&As_Reference(index));
+ }
+ }
+}
+#endif
+
+
+/***********************************************************************************************
+ * BuildingTypeClass::Create_And_Place -- Creates and places a building object onto the map. *
+ * *
+ * This routine is used by the scenario editor to create and place buildings on the map. *
+ * *
+ * INPUT: cell -- The cell that the building is to be placed upon. *
+ * *
+ * house -- The owner of the building. *
+ * *
+ * OUTPUT: bool; Was the building successfully created and placed on the map? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/28/1994 JLB : Created. *
+ *=============================================================================================*/
+bool BuildingTypeClass::Create_And_Place(CELL cell, HousesType house) const
+{
+ BuildingClass * ptr;
+
+ ptr = new BuildingClass(Type, house);
+ if (ptr != NULL) {
+ return(ptr->Unlimbo(Cell_Coord(cell), DIR_N));
+ }
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * BuildingTypeClass::Create_One_Of -- Creates a building of this type. *
+ * *
+ * This routine will create a building object of this type. The building object is in a *
+ * limbo state. It is presumed that the building object will be unlimboed at the correct *
+ * place and time. Typical use is when the building is created in a factory situation *
+ * and will be placed on the map when construction completes. *
+ * *
+ * INPUT: house -- Pointer to the house that is to be the owner of the building. *
+ * *
+ * OUTPUT: Returns with a pointer to the building. If the building could not be created *
+ * then a NULL is returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/07/1994 JLB : Created. *
+ *=============================================================================================*/
+ObjectClass * BuildingTypeClass::Create_One_Of(HouseClass * house) const
+{
+ HousesType htype = HOUSE_NEUTRAL;
+ if (house != NULL) {
+ htype = house->Class->House;
+ }
+ return(new BuildingClass(Type, htype));
+}
+
+
+/***********************************************************************************************
+ * BuildingTypeClass::Init_Anim -- Initialize an animation control for a building. *
+ * *
+ * This routine will initialize one animation control element for a *
+ * specified building. This modifies a "const" class and thus must *
+ * perform some strategic casting to get away with this. *
+ * *
+ * INPUT: state -- The animation state to apply these data values to. *
+ * *
+ * start -- Starting frame for the building's animation. *
+ * *
+ * count -- The number of frames in this animation. *
+ * *
+ * rate -- The countdown timer between animation frames. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 04/18/1994 JLB : Created. *
+ *=============================================================================================*/
+void BuildingTypeClass::Init_Anim(BStateType state, int start, int count, int rate) const
+{
+ ((int &)Anims[state].Start) = start;
+ ((int &)Anims[state].Count) = count;
+ ((int &)Anims[state].Rate) = rate;
+}
+
+
+/***********************************************************************************************
+ * BuildingTypeClass::Init -- Performs theater specific initialization. *
+ * *
+ * This routine is used to perform any initialization that is custom per theater. *
+ * Typically, this is fetching the building shape data for those building types that have *
+ * theater specific art. *
+ * *
+ * INPUT: theater -- The theater to base this initialization on. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 01/21/1995 JLB : Created. *
+ *=============================================================================================*/
+void BuildingTypeClass::Init(TheaterType theater)
+{
+ if (theater != LastTheater) {
+ char fullname[_MAX_FNAME+_MAX_EXT];
+
+ for (StructType sindex = STRUCT_FIRST; sindex < STRUCT_COUNT; sindex++) {
+ BuildingTypeClass const * classptr = &As_Reference(sindex);
+
+ if (classptr->IsTheater) {
+ _makepath(fullname, NULL, NULL, classptr->Graphic_Name(), Theaters[theater].Suffix);
+ ((void const *&)classptr->ImageData) = MFCD::Retrieve(fullname);
+
+ /*
+ ** Buildup data is probably theater specific as well. Fetch a pointer to the
+ ** data at this time as well.
+ */
+ sprintf(fullname, "%sMAKE.%s", classptr->Graphic_Name(), Theaters[theater].Suffix);
+ ((void const *&)classptr->BuildupData) = MFCD::Retrieve(fullname);
+ if (classptr->BuildupData) {
+ int timedelay = 1;
+ int count = Get_Build_Frame_Count(classptr->BuildupData);
+ if (count != 0) {
+ timedelay = (5 * TICKS_PER_SECOND) / count;
+ }
+ classptr->Init_Anim(BSTATE_CONSTRUCTION, 0, count, timedelay);
+ }
+ }
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * BuildingTypeClass::Dimensions -- Fetches the pixel dimensions of the building. *
+ * *
+ * This routine will fetch the dimensions of the building (in pixels). These dimensions are *
+ * used to render the selection rectangle and the health bar. *
+ * *
+ * INPUT: width -- Reference to the pixel width (to be filled in). *
+ * *
+ * height -- Reference to the pixel height (to be filled in). *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 01/23/1995 JLB : Created. *
+ *=============================================================================================*/
+void BuildingTypeClass::Dimensions(int &width, int &height) const
+{
+ width = Width() * ICON_PIXEL_W;
+ width -= (width/5);
+ height = Height() * ICON_PIXEL_H;
+ height -= (height/5);
+}
+
+
+/***********************************************************************************************
+ * BuildingTypeClass::As_Reference -- Fetches reference to the building type specified. *
+ * *
+ * This routine will fetch a reference to the BuildingTypeClass as indicated by the *
+ * building type number specified. *
+ * *
+ * INPUT: type -- The building type number to convert into a BuildingTypeClass reference. *
+ * *
+ * OUTPUT: Returns with a reference to the building type class as indicated by the *
+ * parameter. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 01/23/1995 JLB : Created. *
+ *=============================================================================================*/
+BuildingTypeClass & BuildingTypeClass::As_Reference(StructType type)
+{
+ return(*BuildingTypes.Ptr(type));
+}
+
+
+/***********************************************************************************************
+ * BuildingTypeClass::Occupy_List -- Fetches the occupy list for the building. *
+ * *
+ * Use this routine to fetch the occupy list pointer for the building. The occupy list is *
+ * used to determine what cells the building occupies and thus precludes other buildings *
+ * or objects from using. *
+ * *
+ * INPUT: placement -- Is this for placement legality checking only? The normal condition *
+ * is for marking occupation flags. *
+ * *
+ * OUTPUT: Returns with a pointer to a cell offset list to be used to determine what cells *
+ * this building occupies. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 01/23/1995 JLB : Created. *
+ *=============================================================================================*/
+short const * BuildingTypeClass::Occupy_List(bool placement) const
+{
+ SmudgeType bib = SMUDGE_NONE;
+ CELL cell=0;
+
+ if (placement && Bib_And_Offset(bib, cell)) {
+
+ SmudgeTypeClass const & smudge = SmudgeTypeClass::As_Reference(bib);
+ static short _list[25];
+ short * dest = &_list[0];
+
+ /*
+ ** Copy the bib overlap list into the working buffer.
+ */
+ short const * src = smudge.Occupy_List();
+ while (*src != REFRESH_EOL) {
+ *dest++ = (*src++) + cell;
+ }
+
+ /*
+ ** Append the building occupy list to this working buffer.
+ */
+ src = OccupyList;
+ while (src && *src != REFRESH_EOL) {
+ *dest++ = *src++;
+ }
+ *dest = REFRESH_EOL;
+
+ return(&_list[0]);
+ }
+
+ if (OccupyList != NULL) {
+ return(OccupyList);
+ }
+
+ static short const _templap[] = {REFRESH_EOL};
+ return(&_templap[0]);
+}
+
+
+/***********************************************************************************************
+ * BuildingTypeClass::Overlap_List -- Fetches the overlap list for the building. *
+ * *
+ * This routine will fetch the overlap list for the building. The overlap list is used *
+ * to determine what cells the building's graphics cover, but is not considered to occupy *
+ * for movement purposes. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with a pointer to the cell offset list that is used to determine the *
+ * cells that this building overlaps. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 01/23/1995 JLB : Created. *
+ *=============================================================================================*/
+short const * BuildingTypeClass::Overlap_List(void) const
+{
+ if (OverlapList != NULL) {
+ return(OverlapList);
+ }
+
+ static short const _templap[] = {REFRESH_EOL};
+ return(&_templap[0]);
+}
+
+
+/***********************************************************************************************
+ * BuildingTypeClass::Width -- Determines width of building in icons. *
+ * *
+ * Use this routine to determine the width of the building type in icons. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the building width in icons. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 02/23/1995 JLB : Created. *
+ *=============================================================================================*/
+int BuildingTypeClass::Width(void) const
+{
+ static int width[BSIZE_COUNT] = {
+ 1,
+ 2,
+ 1,
+ 2,
+ 2,
+ 3,
+ 3,
+ 4,
+ 5
+ };
+ return(width[Size]);
+}
+
+
+/***********************************************************************************************
+ * BuildingTypeClass::Height -- Determines the height of the building in icons. *
+ * *
+ * Use this routine to find the height of the building in icons. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the building height in icons. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 02/23/1995 JLB : Created. *
+ *=============================================================================================*/
+int BuildingTypeClass::Height(bool bib) const
+{
+ static int height[BSIZE_COUNT] = {
+ 1,
+ 1,
+ 2,
+ 2,
+ 3,
+ 2,
+ 3,
+ 2,
+ 5
+ };
+ return(height[Size] + ((bib && IsBibbed) ? 1 : 0));
+}
+
+
+/***********************************************************************************************
+ * BuildingTypeClass::Bib_And_Offset -- Determines the bib and appropriate cell offset. *
+ * *
+ * This routine is used to determine what (if any) bib should be used for this building *
+ * and also the cell offset for the upper left corner of the bib smudge type. *
+ * *
+ * INPUT: bib -- Reference to the bib that should be used for this building. *
+ * *
+ * cell -- The cell offset for the upper left corner of the bib. This offset is *
+ * relative to the upper left corner of the building. *
+ * *
+ * OUTPUT: Is a bib required for this building? If the result is true, then the correct *
+ * bib and cell offset will be filled in. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/23/1995 JLB : Created. *
+ *=============================================================================================*/
+bool BuildingTypeClass::Bib_And_Offset(SmudgeType & bib, CELL & cell) const
+{
+ bib = SMUDGE_NONE;
+
+ if (IsBibbed) {
+ switch (Width()) {
+ case 2:
+ bib = SMUDGE_BIB3;
+ break;
+
+ case 3:
+ bib = SMUDGE_BIB2;
+ break;
+
+ case 4:
+ bib = SMUDGE_BIB1;
+ break;
+
+ default:
+ bib = SMUDGE_NONE;
+ break;
+ }
+
+ /*
+ ** Adjust the bib position for special buildings that have the bib as part
+ ** of the building art itself.
+ */
+ if (bib != SMUDGE_NONE) {
+ cell += ((Height()-1)*MAP_CELL_W);
+ }
+ }
+ return(bib != SMUDGE_NONE);
+}
+
+
+/***********************************************************************************************
+ * BuildingTypeClass::Max_Pips -- Determines the maximum pips to display. *
+ * *
+ * Use this routine to determine the maximum number of pips to display on this building *
+ * when it is rendered. Typically, this is the tiberium capacity divided by 100. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the number of pips to display on this building when selected. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/29/1995 JLB : Created. *
+ *=============================================================================================*/
+int BuildingTypeClass::Max_Pips(void) const
+{
+ int maxpips = (Width() * ICON_PIXEL_W) / 4;
+ return(Bound((int)(Capacity/100), 0, maxpips));
+}
+
+
+/***********************************************************************************************
+ * BuildingTypeClass::Raw_Cost -- Fetches the raw (base) cost of this building type. *
+ * *
+ * This routine is used to fetch the real raw base cost of the building. The raw cost *
+ * is the cost of the building less any free unit that would come with the building *
+ * if it were built in the normal fashion. Specifically, the helicopter cost is subtracted *
+ * from the helipad and the harvester cost is subtracted from the refinery. This cost *
+ * is used for refunding. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns the raw (base) cost to build the building of this type. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 09/21/1995 JLB : Created. *
+ *=============================================================================================*/
+int BuildingTypeClass::Raw_Cost(void) const
+{
+ int cost = TechnoTypeClass::Raw_Cost();
+
+ if (Type == STRUCT_HELIPAD && !Rule.IsSeparate) {
+ cost -= (AircraftTypeClass::As_Reference(AIRCRAFT_HIND).Cost + AircraftTypeClass::As_Reference(AIRCRAFT_HIND).Cost)/2;
+ }
+ if (Type == STRUCT_REFINERY) {
+ cost -= UnitTypeClass::As_Reference(UNIT_HARVESTER).Cost;
+ }
+ return(cost);
+}
+
+
+/***********************************************************************************************
+ * BuildingTypeClass::Cost_Of -- Fetches the cost of this building. *
+ * *
+ * This routine will fetch the cost to build the building of this type. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the cost to produce this building. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 09/21/1995 JLB : Created. *
+ *=============================================================================================*/
+int BuildingTypeClass::Cost_Of(void) const
+{
+ if (Rule.IsSeparate && Type == STRUCT_HELIPAD) {
+ return(Raw_Cost());
+ }
+ return(TechnoTypeClass::Cost_Of());
+}
+
+
+/***********************************************************************************************
+ * BuildingTypeClass::Flush_For_Placement -- Tries to clear placement area for this building t *
+ * *
+ * This routine is called when a clear space for placement is desired at the cell location *
+ * specified. Typical use of this routine is by the computer when it wants to build up *
+ * its base. *
+ * *
+ * INPUT: cell -- The cell that the building of this type would like to be placed down at. *
+ * *
+ * house -- Pointer to the house that want to clear the foundation zone. *
+ * *
+ * OUTPUT: Placement is temporarily blocked, please try again later? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 09/27/1995 JLB : Created. *
+ *=============================================================================================*/
+bool BuildingTypeClass::Flush_For_Placement(CELL cell, HouseClass * house) const
+{
+ bool again = false;
+ if (cell > 0) {
+ short const * list = Occupy_List(true);
+
+ while (*list != REFRESH_EOL) {
+ CELL newcell = cell + *list++;
+
+ if (Map.In_Radar(newcell)) {
+ TechnoClass * occupier = Map[newcell].Cell_Techno();
+ if (occupier != NULL) {
+ again = true;
+ if (occupier->House->Is_Ally(house) && occupier->Is_Foot() && !Target_Legal(((FootClass *)occupier)->NavCom)) {
+ Map[newcell].Incoming(0, true);
+ } else {
+// Base_Is_Attacked(occupier);
+ }
+ }
+ }
+ }
+ }
+ return(again);
+}
+
+
+/***********************************************************************************************
+ * BuildingTypeClass::Read_INI -- Fetch building type data from the INI database. *
+ * *
+ * This routine will fetch the building type class data from the INI database file. *
+ * *
+ * INPUT: ini -- Reference to the INI database that will be examined. *
+ * *
+ * OUTPUT: bool; Was the building entry found and the data extracted? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/19/1996 JLB : Created. *
+ *=============================================================================================*/
+bool BuildingTypeClass::Read_INI(CCINIClass & ini)
+{
+ if (TechnoTypeClass::Read_INI(ini)) {
+ Speed = ini.Get_Bool(Name(), "WaterBound", (Speed == SPEED_FLOAT)) ? SPEED_FLOAT : SPEED_NONE;
+ Capacity = ini.Get_Int(Name(), "Storage", Capacity);
+ Adjacent = ini.Get_Int(Name(), "Adjacent", Adjacent);
+ IsCaptureable = ini.Get_Bool(Name(), "Capturable", IsCaptureable);
+ IsPowered = ini.Get_Bool(Name(), "Powered", IsPowered);
+ IsBibbed = ini.Get_Bool(Name(), "Bib", IsBibbed);
+ IsUnsellable = ini.Get_Bool(Name(), "Unsellable", IsUnsellable);
+ IsBase = ini.Get_Bool(Name(), "BaseNormal", IsBase);
+ Power = ini.Get_Int(Name(), "Power", (Power > 0) ? Power : -Drain);
+ if (Power < 0) {
+ Drain = -Power;
+ Power = 0;
+ }
+ return(true);
+ }
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * BuildingTypeClass::Coord_Fixup -- Adjusts coordinate to be legal for assignment. *
+ * *
+ * This routine will adjust the specified coordinate so that it will be legal for assignment*
+ * to this building. All buildings are given a coordinate that is in the upper left corner *
+ * of a cell. This routine will drop the fractional component of the coordinate. *
+ * *
+ * INPUT: coord -- The coordinate to fixup into a legal to assign value. *
+ * *
+ * OUTPUT: Returns with a coordinate that can be assigned to the building. *
+ * *
+ * WARNINGS: The coordinate is not examined to see if the cell is legal for placing the *
+ * building. It merely adjusts the coordinate so that is legal at first glance. *
+ * *
+ * HISTORY: *
+ * 08/14/1996 JLB : Created. *
+ *=============================================================================================*/
+COORDINATE BuildingTypeClass::Coord_Fixup(COORDINATE coord) const
+{
+ return Coord_Whole(coord);
+}
+
+
+/***********************************************************************************************
+ * BuildingTypeClass::Full_Name -- Fetches the name to give this building. *
+ * *
+ * This routine will return the displayable given name for this building type. Normally, *
+ * this is the official name as well, however in the case of civilian buildings, the *
+ * name will just be "Civilian Building" unless special options are in place. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the text number of the building type. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 10/02/1996 JLB : Created. *
+ *=============================================================================================*/
+int BuildingTypeClass::Full_Name(void) const
+{
+ if (Debug_Map || Rule.IsNamed || *this < STRUCT_V01 || *this > STRUCT_V37) {
+ return(TechnoTypeClass::Full_Name());
+ }
+ return(TXT_CIVILIAN_BUILDING);
+}
diff --git a/CODE/BENCH.CPP b/CODE/BENCH.CPP
new file mode 100644
index 0000000..5ae69ad
--- /dev/null
+++ b/CODE/BENCH.CPP
@@ -0,0 +1,167 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/BENCH.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : BENCH.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 07/17/96 *
+ * *
+ * Last Update : July 18, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * Benchmark::Begin -- Start the benchmark operation. *
+ * Benchmark::Benchmark -- Constructor for the benchmark object. *
+ * Benchmark::End -- Mark the end of a benchmarked operation *
+ * Benchmark::Reset -- Clear out the benchmark statistics. *
+ * Benchmark::Value -- Fetch the current average benchmark time. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#include "bench.h"
+#include "mpu.h"
+
+
+/***********************************************************************************************
+ * Benchmark::Benchmark -- Constructor for the benchmark object. *
+ * *
+ * This will construct the benchmark object. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/18/1996 JLB : Created. *
+ *=============================================================================================*/
+Benchmark::Benchmark(void) :
+ Average(0),
+ Counter(0),
+ TotalCount(0)
+{
+}
+
+
+/***********************************************************************************************
+ * Benchmark::Reset -- Clear out the benchmark statistics. *
+ * *
+ * Use this routine to clear out all the accumulated statistics within this benchmark *
+ * object. The object is set just as if it was freshly constructed. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/18/1996 JLB : Created. *
+ *=============================================================================================*/
+void Benchmark::Reset(void)
+{
+ Average = 0;
+ Counter = 0;
+ TotalCount = 0;
+}
+
+
+/***********************************************************************************************
+ * Benchmark::Begin -- Start the benchmark operation. *
+ * *
+ * Call this routine before the operation to be benchmarked is begun. The corresponding *
+ * End() function must be called after the operation has completed. *
+ * *
+ * INPUT: reset -- Should the entire benchmark object be reset at this time as well? *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: The Begin() and End() functions are NOT nestable. *
+ * *
+ * HISTORY: *
+ * 07/18/1996 JLB : Created. *
+ *=============================================================================================*/
+void Benchmark::Begin(bool reset)
+{
+ if (reset) Reset();
+ Clock = 0;
+}
+
+
+/***********************************************************************************************
+ * Benchmark::End -- Mark the end of a benchmarked operation *
+ * *
+ * This routine is called at the end of the operation that is being benchmarked. It is *
+ * important to call this routine as soon as possible after the event being benchmarked *
+ * has completed. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: The Being() and End() functions are NOT nestable. *
+ * *
+ * HISTORY: *
+ * 07/18/1996 JLB : Created. *
+ *=============================================================================================*/
+void Benchmark::End(void)
+{
+ unsigned long value = Clock;
+
+ if (Counter == MAXIMUM_EVENT_COUNT) {
+ Average -= Average / MAXIMUM_EVENT_COUNT;
+ Average += value;
+ } else {
+ Average += value;
+ Counter++;
+ }
+ TotalCount++;
+}
+
+
+/***********************************************************************************************
+ * Benchmark::Value -- Fetch the current average benchmark time. *
+ * *
+ * This routine will take the statistics already accumulated and determine the average *
+ * time recorded. This value will be returned. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the average time that all events tracked by this object. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/18/1996 JLB : Created. *
+ *=============================================================================================*/
+unsigned long Benchmark::Value(void) const
+{
+ if (Counter) {
+ return(Average / Counter);
+ }
+ return(0);
+}
diff --git a/CODE/BENCH.H b/CODE/BENCH.H
new file mode 100644
index 0000000..53007d9
--- /dev/null
+++ b/CODE/BENCH.H
@@ -0,0 +1,122 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/BENCH.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : BENCH.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 07/17/96 *
+ * *
+ * Last Update : July 17, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#ifndef BENCH_H
+#define BENCH_H
+
+#include "mpu.h"
+#include "ftimer.h"
+
+/*
+** The "bool" integral type was defined by the C++ committee in
+** November of '94. Until the compiler supports this, use the following
+** definition.
+*/
+#ifndef __BORLANDC__
+#ifndef TRUE_FALSE_DEFINED
+#define TRUE_FALSE_DEFINED
+enum {false=0,true=1};
+typedef int bool;
+#endif
+#endif
+
+/*
+** This is a timer access object that will fetch the internal Pentium
+** clock value.
+*/
+class PentiumTimerClass
+{
+ public:
+ unsigned long operator () (void) const {unsigned long h;unsigned long l = Get_CPU_Clock(h);return((l >> 4) | (h << 28));}
+ operator unsigned long (void) const {unsigned long h;unsigned long l = Get_CPU_Clock(h);return((l >> 4) | (h << 28));}
+};
+
+
+/*
+** A performance tracking tool object. It is used to track elapsed time. Unlike a simple clock, this
+** class will keep a running average of the duration. Typical use of this would be to benchmark some
+** process that occurs multiple times. By benchmarking an average time, inconsistencies in a particular
+** run can be overcome.
+*/
+class Benchmark
+{
+ public:
+ Benchmark(void);
+
+ void Begin(bool reset=false);
+ void End(void);
+
+ void Reset(void);
+ unsigned long Value(void) const;
+ unsigned long Count(void) const {return(TotalCount);}
+
+ private:
+ /*
+ ** The maximum number of events to keep running average of. If
+ ** events exceed this number, then older events drop off the
+ ** accumulated time. This number needs to be as small as
+ ** is reasonable. The larger this number gets, the less magnitude
+ ** that the benchmark timer can handle. Example; At a value of
+ ** 256, the magnitude of the timer can only be 24 bits.
+ */
+ enum {MAXIMUM_EVENT_COUNT=256};
+
+ /*
+ ** This is the timer the is used to clock the events.
+ */
+ BasicTimerClass Clock;
+
+ /*
+ ** The total time off all events tracked so far.
+ */
+ unsigned long Average;
+
+ /*
+ ** The total number of events tracked so far.
+ */
+ unsigned long Counter;
+
+ /*
+ ** Absolute total number of events (possibly greater than the
+ ** number of events tracked in the average).
+ */
+ unsigned long TotalCount;
+};
+
+
+#endif
diff --git a/CODE/BFILE.MAK b/CODE/BFILE.MAK
new file mode 100644
index 0000000..19af58e
--- /dev/null
+++ b/CODE/BFILE.MAK
@@ -0,0 +1,2122 @@
+#
+# Command & Conquer Red Alert(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&c0\vcs\code\bfile.mav 5.0 11 Nov 1996 09:40: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 : MAKEFILE *
+#* *
+#* Programmer : Joe L. Bostic *
+#* *
+#* Start Date : March 25, 1993 *
+#* *
+#* Last Update : March 25, 1993 [JLB] *
+#* *
+#*-------------------------------------------------------------------------*
+
+
+# Comment out the following line to disable "include file autodependency".
+.AUTODEPEND
+#.SWAP
+
+!include "rules.mak"
+
+
+##########################################################################
+
+MAPFILES = \
+
+CACHEMAP = \
+ BRIEFING.AUD \
+ BAR3RED.SHP \
+ BAR3BLU.SHP \
+ COUNTRYA.SHP \
+ COUNTRYE.SHP \
+ CREDSA.SHP \
+ CREDSU.SHP \
+ HISCORE1.SHP \
+ HISCORE2.SHP \
+ TIME.SHP \
+ CLOCK1.AUD \
+ COUNTRY4.AUD \
+ MAPWIPE2.AUD \
+ MAPWIPE5.AUD \
+ TONEY10.AUD \
+ TONEY4.AUD \
+ TONEY7.AUD \
+ SFX4.AUD \
+ BEEPY6.AUD \
+ KEYSTROK.AUD \
+ APPEAR1.AUD \
+ SCOLD1.AUD \
+ COUNTRY1.AUD \
+ ALI-TRAN.WSA \
+ SOV-TRAN.WSA \
+ ALIBACKH.PCX \
+ SOVBACKH.PCX \
+ BAR3RHR.SHP \
+ BAR3BHR.SHP \
+ CREDSAHR.SHP \
+ CREDSUHR.SHP \
+ HISC1-HR.SHP \
+ HISC2-HR.SHP \
+ TIMEHR.SHP \
+ MLTIPLYR.WSA \
+
+LOCALFILES = \
+ PROLOG.CPS \
+ MAP.AUD \
+ TITLE.CPS \
+ PALETTE.CPS \
+ INTRO.AUD \
+ EGOPAL.PAL \
+ RULES.INI \
+ CREDITS.TXT \
+ ALIPAPER.CPS \
+ 3POINT.FNT \
+ 8POINT.FNT \
+ EDITFNT.FNT \
+ CONQUER.ENG \
+ DEBUG.ENG \
+ LED.FNT \
+ SNOW.PAL \
+ TEMPERAT.PAL \
+ INTERIOR.PAL \
+ VCR.FNT \
+ HOLE0000.LUT \
+ HOLE0001.LUT \
+ HOLE0002.LUT \
+ HOLE0003.LUT \
+ HOLE0004.LUT \
+ HOLE0005.LUT \
+ HOLE0006.LUT \
+ HOLE0007.LUT \
+ HOLE0008.LUT \
+ HOLE0009.LUT \
+ HOLE0010.LUT \
+ HOLE0011.LUT \
+ HOLE0012.LUT \
+ HOLE0013.LUT \
+ HOLE0014.LUT \
+ HOLE0015.LUT \
+ HOLE0016.LUT \
+ HOLE0017.LUT \
+ HOLE0018.LUT \
+ HOLE0019.LUT \
+ HOLE0020.LUT \
+ HOLE0021.LUT \
+ HOLE0022.LUT \
+ HOLE0023.LUT \
+ HOLE0024.LUT \
+ HOLE0025.LUT \
+ HOLE0026.LUT \
+ HOLE0027.LUT \
+ HOLE0028.LUT \
+ HOLE0029.LUT \
+ HOLE0030.LUT \
+ HOLE0031.LUT \
+ HOLE0032.LUT \
+ HOLE0033.LUT \
+ HOLE0034.LUT \
+ HOLE0035.LUT \
+ HOLE0036.LUT \
+ HOLE0037.LUT \
+ HOLE0038.LUT \
+ HOLE0039.LUT \
+ HOLE0040.LUT \
+ HOLE0041.LUT \
+ HOLE0042.LUT \
+ HOLE0043.LUT \
+ HOLE0044.LUT \
+ HOLE0045.LUT \
+ HOLE0046.LUT \
+ HOLE0047.LUT \
+# TEMPSCOR.FNT \
+# 6POINT.FNT \
+# GRAD6FNT.FNT \
+# SCOREFNT.FNT \
+
+
+# Files that have counterparts in both high and low resolutions.
+# These files will be built into the HIRES.MIX and LORES.MIX files.
+HILORES = \
+ TRANICON.SHP \
+ PIPS.SHP \
+ PULSE.SHP \
+ ATOMICON.SHP \
+ WARPICON.SHP \
+ C1.SHP \
+ C2.SHP \
+ CHAN.SHP \
+ DELPHI.SHP \
+ E1.SHP \
+ E2.SHP \
+ E3.SHP \
+ E4.SHP \
+ E5.SHP \
+ E6.SHP \
+ E7.SHP \
+ EINSTEIN.SHP \
+ GNRL.SHP \
+ MEDI.SHP \
+ SPY.SHP \
+ THF.SHP \
+ DD-BKGND.SHP \
+ DD-BOTM.SHP \
+ DD-CRNR.SHP \
+ DD-EDGE.SHP \
+ DD-LEFT.SHP \
+ DD-RIGHT.SHP \
+ DD-TOP.SHP \
+ 12METFNT.FNT \
+ GRAD6FNT.FNT \
+ HELP.FNT \
+ 6POINT.FNT \
+ TYPE.FNT \
+ SCOREFNT.FNT \
+ 1TNKICON.SHP \
+ 2TNKICON.SHP \
+ 3TNKICON.SHP \
+ 4TNKICON.SHP \
+ AFLDICON.SHP \
+ AGUNICON.SHP \
+ APCICON.SHP \
+ APWRICON.SHP \
+ ARTYICON.SHP \
+ ATEKICON.SHP \
+ BADRICON.SHP \
+ BARRICON.SHP \
+ BRIKICON.SHP \
+ BTN-DN.SHP \
+ BTN-PL.SHP \
+ BTN-ST.SHP \
+ BTN-UP.SHP \
+ CAICON.SHP \
+ CAMICON.SHP \
+ CLOCK.SHP \
+ DDICON.SHP \
+ DOGICON.SHP \
+ DOMEICON.SHP \
+ DOMFICON.SHP \
+ E1ICON.SHP \
+ E2ICON.SHP \
+ E3ICON.SHP \
+ E4ICON.SHP \
+ E6ICON.SHP \
+ E7ICON.SHP \
+ FACFICON.SHP \
+ FACTICON.SHP \
+ FENCICON.SHP \
+ FIXICON.SHP \
+ FTURICON.SHP \
+ GAPICON.SHP \
+ GPSSICON.SHP \
+ GUNICON.SHP \
+ HARVICON.SHP \
+ HBOXICON.SHP \
+ HELIICON.SHP \
+ HINDICON.SHP \
+ HPADICON.SHP \
+ INFXICON.SHP \
+ IRONICON.SHP \
+ JEEPICON.SHP \
+ KENNICON.SHP \
+ LSTICON.SHP \
+ MAP.SHP \
+ MCVICON.SHP \
+ MEDIICON.SHP \
+ MGGICON.SHP \
+ MIGICON.SHP \
+ MNLYICON.SHP \
+ MOUSE.SHP \
+ MRJICON.SHP \
+ MSLOICON.SHP \
+ NATORADR.SHP \
+ PBMBICON.SHP \
+ PBOXICON.SHP \
+ PDOXICON.SHP \
+ PINFICON.SHP \
+ POWER.SHP \
+ POWERBAR.SHP \
+ POWRICON.SHP \
+ PROCICON.SHP \
+ PTICON.SHP \
+ REPAIR.SHP \
+ SAMICON.SHP \
+ SBAGICON.SHP \
+ SELL.SHP \
+ SIDEBAR.SHP \
+ SILOICON.SHP \
+ SMIGICON.SHP \
+ SONRICON.SHP \
+ SOVPAPER.CPS \
+ SPEFICON.SHP \
+ SPENICON.SHP \
+ SPYICON.SHP \
+ SSICON.SHP \
+ STEKICON.SHP \
+ STRIP.SHP \
+ STRIPDN.SHP \
+ STRIPUP.SHP \
+ SYRDICON.SHP \
+ SYRFICON.SHP \
+ TABS.SHP \
+ TENTICON.SHP \
+ THFICON.SHP \
+ TRUKICON.SHP \
+ TSLAICON.SHP \
+ U2ICON.SHP \
+ USSRRADR.SHP \
+ V2RLICON.SHP \
+ WEAFICON.SHP \
+ WEAPICON.SHP \
+ YAKICON.SHP \
+ NRADRFRM.SHP \
+ URADRFRM.SHP \
+ SIDE1NA.SHP \
+ SIDE1US.SHP \
+ SIDE2NA.SHP \
+ SIDE2US.SHP \
+ SIDE3NA.SHP \
+ SIDE3US.SHP \
+ STRIPNA.SHP \
+ STRIPUS.SHP \
+
+# MOEBICON.SHP \
+
+HILORES1 = \
+ MECH.SHP \
+ SHOK.SHP \
+ CARRICON.SHP \
+ CTNKICON.SHP \
+ DTRKICON.SHP \
+ MECHICON.SHP \
+ MSUBICON.SHP \
+ QTNKICON.SHP \
+ SHOKICON.SHP \
+ STNKICON.SHP \
+ TTNKICON.SHP \
+
+# These helper macros substitute the extension so that
+# the appropriate art build rule will be invoked.
+xLOHILORES = $(HILORES:.SHP=.LOW)
+LOHILORES = $(xLOHILORES:.FNT=.LNT)
+xHIHILORES = $(HILORES:.SHP=.HI)
+HIHILORES = $(xHIHILORES:.FNT=.HNT)
+
+xLOHILORES1 = $(HILORES1:.SHP=.LOW)
+LOHILORES1 = $(xLOHILORES1:.FNT=.LNT)
+xHIHILORES1 = $(HILORES1:.SHP=.HI)
+HIHILORES1 = $(xHIHILORES1:.FNT=.HNT)
+
+#
+# Files required for hires/Win95 version only
+#
+# This mix file is cached
+#
+HIRESFILES = \
+ ALIPAPER.PCX \
+ PROLOG.PCX \
+ SOVPAPER.PCX \
+ AFTR_HI.PCX \
+ ALY1.PCX \
+ APC_HI.PCX \
+ APHI0049.PCX \
+ BNHI0020.PCX \
+ DCHI0040.PCX \
+ FRHI0166.PCX \
+ LAB.PCX \
+ LANDSBRG.PCX \
+ MAHI0107.PCX \
+ MIG_HI.PCX \
+ MTFACTHI.PCX \
+ NEEDLE.PCX \
+ SOV2.PCX \
+ SPY.PCX \
+ STALIN.PCX \
+ TENT.PCX \
+# ENG_HI.PCX \
+
+
+CONQUERFILES = \
+ PARABOMB.SHP \
+ RADARFRM.SHP \
+ ARMOR.SHP \
+ FPOWER.SHP \
+ SPEED.SHP \
+ TQUAKE.SHP \
+ H2O_EXP1.SHP \
+ H2O_EXP2.SHP \
+ H2O_EXP3.SHP \
+ FLAK.SHP \
+ EBTN-DN.SHP \
+ EBTN-UP.SHP \
+ ATOMSFX.SHP \
+ TWINKLE1.SHP \
+ TWINKLE2.SHP \
+ TWINKLE3.SHP \
+ CHRONBOX.SHP \
+ GPSBOX.SHP \
+ INVULBOX.SHP \
+ PARABOX.SHP \
+ SONARBOX.SHP \
+ SPUTNIK.SHP \
+ SPUTDOOR.SHP \
+ ATOMICDN.SHP \
+ ATOMICUP.SHP \
+ TYPE.FNT \
+ 120MM.SHP \
+ 1TNK.SHP \
+ 2TNK.SHP \
+ 3TNK.SHP \
+ 4TNK.SHP \
+ 50CAL.SHP \
+ AFLD.SHP \
+ AGUN.SHP \
+ APC.SHP \
+ APWR.SHP \
+ ART-EXP1.SHP \
+ ARTY.SHP \
+ ATEK.SHP \
+ BADR.SHP \
+ BARB.SHP \
+ BARL.SHP \
+ BARR.SHP \
+ BIO.SHP \
+ BOMB.SHP \
+ BOMBLET.SHP \
+ BRIK.SHP \
+ BRL3.SHP \
+ BURN-L.SHP \
+ BURN-M.SHP \
+ BURN-S.SHP \
+ CA.SHP \
+ CYCL.SHP \
+ DD.SHP \
+ DEVIATOR.SHP \
+ DOG.SHP \
+ DOGBULLT.SHP \
+ DOLLAR.SHP \
+ DOME.SHP \
+ DRAGON.SHP \
+ EARTH.SHP \
+ ELECTDOG.SHP \
+ EMPULSE.SHP \
+ FACT.SHP \
+ FB1.SHP \
+ FB2.SHP \
+ FBALL1.SHP \
+ FCOM.SHP \
+ FENC.SHP \
+ FIRE1.SHP \
+ FIRE2.SHP \
+ FIRE3.SHP \
+ FIRE4.SHP \
+ FIX.SHP \
+ FLAGFLY.SHP \
+ FLMSPT.SHP \
+ FPLS.SHP \
+ FRAG1.SHP \
+ FTNK.SHP \
+ FTUR.SHP \
+ GAP.SHP \
+ GUN.SHP \
+ GUNFIRE.SHP \
+ HARV.SHP \
+ HELI.SHP \
+ HIND.SHP \
+ HOSP.SHP \
+ HPAD.SHP \
+ INVUN.SHP \
+ IRON.SHP \
+ JEEP.SHP \
+ KENN.SHP \
+ LITNING.SHP \
+ LROTOR.SHP \
+ LST.SHP \
+ MCV.SHP \
+ MGG.SHP \
+ MGUN.SHP \
+ MHQ.SHP \
+ MIG.SHP \
+ MINE.SHP \
+ MINIGUN.SHP \
+ MINP.SHP \
+ MINV.SHP \
+ MISS.SHP \
+ MISSILE.SHP \
+ MISSILE2.SHP \
+ MLRS.SHP \
+ MNLY.SHP \
+ MRJ.SHP \
+ NAPALM1.SHP \
+ NAPALM2.SHP \
+ NAPALM3.SHP \
+ ORCA.SHP \
+ PARACH.SHP \
+ PATRIOT.SHP \
+ PBOX.SHP \
+ PDOX.SHP \
+ PIFF.SHP \
+ PIFFPIFF.SHP \
+ POWR.SHP \
+ PROC.SHP \
+ PT.SHP \
+ RAPID.SHP \
+ RROTOR.SHP \
+ SAM.SHP \
+ SAMFIRE.SHP \
+ SBAG.SHP \
+ SCRATE.SHP \
+ SELECT.SHP \
+ SHADOW.SHP \
+ SILO.SHP \
+ SMIG.SHP \
+ SMOKEY.SHP \
+ SMOKE_M.SHP \
+ SMOKLAND.SHP \
+ SPEN.SHP \
+ SS.SHP \
+ SSAM.SHP \
+ STEALTH2.SHP \
+ STEK.SHP \
+ STNK.SHP \
+ SYRD.SHP \
+ TENT.SHP \
+ TRAN.SHP \
+ TRANS.ICN \
+ TRUK.SHP \
+ TSLA.SHP \
+ TURR.SHP \
+ U2.SHP \
+ V19.SHP \
+ V2.SHP \
+ V2RL.SHP \
+ VEH-HIT1.SHP \
+ VEH-HIT2.SHP \
+ VEH-HIT3.SHP \
+ WAKE.SHP \
+ WCRATE.SHP \
+ WWCRATE.SHP \
+ WEAP.SHP \
+ WEAP2.SHP \
+ WOOD.SHP \
+ YAK.SHP \
+ AFLDMAKE.SHP \
+ AGUNMAKE.SHP \
+ APWRMAKE.SHP \
+ ATEKMAKE.SHP \
+ BARRMAKE.SHP \
+ BIOMAKE.SHP \
+ DOMEMAKE.SHP \
+ FACTMAKE.SHP \
+ FIXMAKE.SHP \
+ FTURMAKE.SHP \
+ GAPMAKE.SHP \
+ GUNMAKE.SHP \
+ HOSPMAKE.SHP \
+ HPADMAKE.SHP \
+ IRONMAKE.SHP \
+ KENNMAKE.SHP \
+ MINPMAKE.SHP \
+ MINVMAKE.SHP \
+ PBOXMAKE.SHP \
+ POWRMAKE.SHP \
+ PDOXMAKE.SHP \
+ PROCMAKE.SHP \
+ PUMPMAKE.SHP \
+ SAMMAKE.SHP \
+ SILOMAKE.SHP \
+ SPENMAKE.SHP \
+ STEKMAKE.SHP \
+ SYRDMAKE.SHP \
+ TENTMAKE.SHP \
+ TSLAMAKE.SHP \
+ WEAPMAKE.SHP \
+
+GENERALMAPFILES = \
+ MISSIONS.PKT \
+ CSTRIKE.PKT \
+ TUTORIAL.INI \
+ SCG01EA.INI \
+ SCG40EA.INI \
+ SCG41EA.INI \
+ SCG42EA.INI \
+ SCG43EA.INI \
+ SCG44EA.INI \
+ SCG45EA.INI \
+ SCG46EA.INI \
+ SCG47EA.INI \
+ SCG48EA.INI \
+ SCU40EA.INI \
+ SCU41EA.INI \
+ SCU42EA.INI \
+ SCU43EA.INI \
+ SCU44EA.INI \
+ SCU45EA.INI \
+ SCU46EA.INI \
+ SCU47EA.INI \
+ SCU48EA.INI \
+ SCU01EA.INI \
+ SCM01EA.INI \
+ SCM02EA.INI \
+ SCM03EA.INI \
+ SCM04EA.INI \
+ SCM05EA.INI \
+ SCM06EA.INI \
+ SCM07EA.INI \
+ SCM08EA.INI \
+ SCM09EA.INI \
+ SCM10EA.INI \
+ SCM11EA.INI \
+ SCM12EA.INI \
+ SCM13EA.INI \
+ SCM14EA.INI \
+ SCM15EA.INI \
+ SCM16EA.INI \
+ SCM17EA.INI \
+ SCM18EA.INI \
+ SCM19EA.INI \
+ SCM20EA.INI \
+ SCM21EA.INI \
+ SCM22EA.INI \
+ SCM23EA.INI \
+ SCM24EA.INI \
+ SCMD0EA.INI \
+ SCMD1EA.INI \
+ SCMD2EA.INI \
+ SCMD3EA.INI \
+ SCMD4EA.INI \
+ SCMD5EA.INI \
+ SCMD6EA.INI \
+ SCMD7EA.INI \
+ SCMD8EA.INI \
+ SCMD9EA.INI \
+ SCME0EA.INI \
+ SCME1EA.INI \
+ SCME2EA.INI \
+ SCME3EA.INI \
+ SCME4EA.INI \
+ SCME5EA.INI \
+ SCME6EA.INI \
+ SCME7EA.INI \
+ SCME8EA.INI \
+ SCME9EA.INI \
+ SCMF0EA.INI \
+ SCMF1EA.INI \
+ SCMF2EA.INI \
+ SCMF3EA.INI \
+ SCMF4EA.INI \
+ SCMF5EA.INI \
+ SCMF6EA.INI \
+ SCMF7EA.INI \
+ SCMF8EA.INI \
+ SCMF9EA.INI \
+ SCMG0EA.INI \
+ SCMG1EA.INI \
+ SCMG2EA.INI \
+ SCMG3EA.INI \
+ SCMG4EA.INI \
+ SCMG5EA.INI \
+ SCMG6EA.INI \
+ SCMG7EA.INI \
+ SCMG8EA.INI \
+ SCMG9EA.INI \
+ SCMH0EA.INI \
+ SCMH1EA.INI \
+ SCMH2EA.INI \
+ SCMH3EA.INI \
+ SCMH4EA.INI \
+ SCMH5EA.INI \
+ SCMH6EA.INI \
+ SCMH7EA.INI \
+ SCMH8EA.INI \
+ SCMH9EA.INI \
+ SCMI0EA.INI \
+ SCMI1EA.INI \
+ SCMI2EA.INI \
+ SCMI3EA.INI \
+ SCMI4EA.INI \
+ SCMI5EA.INI \
+ SCMI6EA.INI \
+ SCMI7EA.INI \
+ SCMI8EA.INI \
+ SCMI9EA.INI \
+ SCMJ0EA.INI \
+ SCMJ1EA.INI \
+ SCMJ2EA.INI \
+ SCMJ3EA.INI \
+ SCMJ4EA.INI \
+ SCMJ5EA.INI \
+ SCMJ6EA.INI \
+ SCMJ7EA.INI \
+ SCMJ8EA.INI \
+ SCMJ9EA.INI \
+ SCMK0EA.INI \
+ SCMK1EA.INI \
+ SCMK2EA.INI \
+ SCMK3EA.INI \
+ SCMK4EA.INI \
+ SCMK5EA.INI \
+ SCMK6EA.INI \
+ SCMK7EA.INI \
+ SCMK8EA.INI \
+ SCMK9EA.INI \
+ SCML0EA.INI \
+ SCML1EA.INI \
+ SCML2EA.INI \
+ SCML3EA.INI \
+ SCML4EA.INI \
+ SCML5EA.INI \
+ SCML6EA.INI \
+ SCML7EA.INI \
+ SCML8EA.INI \
+ SCML9EA.INI \
+ SCMM0EA.INI \
+ SCMM1EA.INI \
+ SCMM2EA.INI \
+ SCMM3EA.INI \
+ SCMM4EA.INI \
+ SCMM5EA.INI \
+ SCMM6EA.INI \
+ SCMM7EA.INI \
+ SCMM8EA.INI \
+ SCMM9EA.INI \
+ SCM25EA.INI \
+ SCM26EA.INI \
+ SCM27EA.INI \
+ SCM28EA.INI \
+ SCM29EA.INI \
+ SCM30EA.INI \
+ SCM31EA.INI \
+ SCM32EA.INI \
+ SCM33EA.INI \
+ SCM34EA.INI \
+ SCM35EA.INI \
+ SCM36EA.INI \
+ SCM37EA.INI \
+ SCM38EA.INI \
+ SCM39EA.INI \
+ SCM40EA.INI \
+ SCM41EA.INI \
+ SCM42EA.INI \
+ SCM43EA.INI \
+ SCM44EA.INI \
+ SCM45EA.INI \
+ SCM46EA.INI \
+ SCM47EA.INI \
+ SCM48EA.INI \
+ SCM49EA.INI \
+ SCM50EA.INI \
+ SCM51EA.INI \
+ SCM52EA.INI \
+ SCM53EA.INI \
+ SCM54EA.INI \
+ SCM55EA.INI \
+ SCM56EA.INI \
+ SCM57EA.INI \
+ SCM58EA.INI \
+ SCM59EA.INI \
+ SCM60EA.INI \
+ SCM61EA.INI \
+ SCM62EA.INI \
+ SCM63EA.INI \
+ SCM64EA.INI \
+ SCM65EA.INI \
+ SCM66EA.INI \
+ SCM67EA.INI \
+ SCM68EA.INI \
+ SCM69EA.INI \
+ SCM70EA.INI \
+ SCM71EA.INI \
+ SCM72EA.INI \
+ SCM73EA.INI \
+ SCM74EA.INI \
+ SCM75EA.INI \
+ SCM76EA.INI \
+ SCM77EA.INI \
+ SCM78EA.INI \
+ SCM79EA.INI \
+ SCM80EA.INI \
+ SCM81EA.INI \
+ SCM82EA.INI \
+ SCM83EA.INI \
+ SCM84EA.INI \
+ SCM85EA.INI \
+ SCM86EA.INI \
+ SCM87EA.INI \
+ SCM88EA.INI \
+ SCM89EA.INI \
+ SCM90EA.INI \
+ SCM91EA.INI \
+ SCM92EA.INI \
+ SCM93EA.INI \
+ SCM94EA.INI \
+ SCM95EA.INI \
+ SCM96EA.INI \
+ SCM97EA.INI \
+ SCM98EA.INI \
+ SCM99EA.INI \
+ SCM100EA.INI \
+ SCM101EA.INI \
+ SCM102EA.INI \
+ SCM103EA.INI \
+ SCM104EA.INI \
+ SCM105EA.INI \
+ SCM106EA.INI \
+ SCM107EA.INI \
+ SCM108EA.INI \
+ SCM109EA.INI \
+ SCM110EA.INI \
+ SCM111EA.INI \
+ SCM112EA.INI \
+ SCM113EA.INI \
+ SCM114EA.INI \
+ SCM115EA.INI \
+ SCM116EA.INI \
+ SCM117EA.INI \
+ SCM118EA.INI \
+ SCM119EA.INI \
+ SCM120EA.INI \
+ SCM121EA.INI \
+ SCM122EA.INI \
+ SCM123EA.INI \
+ SCM124EA.INI \
+ SCM125EA.INI \
+ SCM126EA.INI \
+ SCM127EA.INI \
+ SCM128EA.INI \
+ SCM129EA.INI \
+ SCM130EA.INI \
+
+NETMAPFILES = \
+
+# Files that aren't cached.
+GENERALFILES = \
+ AFTR_LO.CPS \
+ ALY1-LO.CPS \
+ APC_LO.CPS \
+ APLO0049.CPS \
+ BNLO0020.CPS \
+ DCLO0040.CPS \
+ FRLO0166.CPS \
+ LAB-LO.CPS \
+ LANDS-LO.CPS \
+ MALO0107.CPS \
+ MIG_LO.CPS \
+ MTFACTLO.CPS \
+ NEEDL-LO.CPS \
+ SOV2-LO.CPS \
+ SPY-LO.CPS \
+ STALN-LO.CPS \
+ TENT-LO.CPS \
+ TITLE.CPS \
+ PPAPER.CPS \
+ MSAA.WSA \
+ MSAB.WSA \
+ MSAC.WSA \
+ MSAD.WSA \
+ MSAE.WSA \
+ MSAF.WSA \
+ MSAG.WSA \
+ MSAH.WSA \
+ MSAI.WSA \
+ MSAJ.WSA \
+ MSAK.WSA \
+ MSAL.WSA \
+ MSAM.WSA \
+ MSAN.WSA \
+ MSSA.WSA \
+ MSSB.WSA \
+ MSSC.WSA \
+ MSSD.WSA \
+ MSSE.WSA \
+ MSSF.WSA \
+ MSSG.WSA \
+ MSSH.WSA \
+ MSSI.WSA \
+ MSSJ.WSA \
+ MSSK.WSA \
+ MSSL.WSA \
+ MSSM.WSA \
+ MSSN.WSA \
+
+INTERIORFILES = \
+ BOXES01.INT \
+ BOXES02.INT \
+ BOXES03.INT \
+ BOXES04.INT \
+ BOXES05.INT \
+ BOXES06.INT \
+ BOXES07.INT \
+ BOXES08.INT \
+ BOXES09.INT \
+ XTRA0001.INT \
+ XTRA0002.INT \
+ XTRA0003.INT \
+ XTRA0004.INT \
+ XTRA0005.INT \
+ XTRA0006.INT \
+ XTRA0007.INT \
+ XTRA0008.INT \
+ XTRA0009.INT \
+ XTRA0010.INT \
+ XTRA0011.INT \
+ XTRA0012.INT \
+ XTRA0013.INT \
+ XTRA0014.INT \
+ XTRA0015.INT \
+ XTRA0016.INT \
+ CLEAR1.INT \
+ MOVEFLSH.INT \
+ ARRO0001.INT \
+ ARRO0002.INT \
+ ARRO0003.INT \
+ ARRO0004.INT \
+ ARRO0005.INT \
+ ARRO0006.INT \
+ ARRO0007.INT \
+ ARRO0008.INT \
+ ARRO0009.INT \
+ ARRO0010.INT \
+ ARRO0011.INT \
+ ARRO0012.INT \
+ ARRO0013.INT \
+ ARRO0014.INT \
+ ARRO0015.INT \
+ FLOR0001.INT \
+ FLOR0002.INT \
+ FLOR0003.INT \
+ FLOR0004.INT \
+ FLOR0005.INT \
+ FLOR0006.INT \
+ FLOR0007.INT \
+ GFLR0001.INT \
+ GFLR0002.INT \
+ GFLR0003.INT \
+ GFLR0004.INT \
+ GFLR0005.INT \
+ GSTR0001.INT \
+ GSTR0002.INT \
+ GSTR0003.INT \
+ GSTR0004.INT \
+ GSTR0005.INT \
+ GSTR0006.INT \
+ GSTR0007.INT \
+ GSTR0008.INT \
+ GSTR0009.INT \
+ GSTR0010.INT \
+ GSTR0011.INT \
+ LWAL0001.INT \
+ LWAL0002.INT \
+ LWAL0003.INT \
+ LWAL0004.INT \
+ LWAL0005.INT \
+ LWAL0006.INT \
+ LWAL0007.INT \
+ LWAL0008.INT \
+ LWAL0009.INT \
+ LWAL0010.INT \
+ LWAL0011.INT \
+ LWAL0012.INT \
+ LWAL0013.INT \
+ LWAL0014.INT \
+ LWAL0015.INT \
+ LWAL0016.INT \
+ LWAL0017.INT \
+ LWAL0018.INT \
+ LWAL0019.INT \
+ LWAL0020.INT \
+ LWAL0021.INT \
+ LWAL0022.INT \
+ LWAL0023.INT \
+ LWAL0024.INT \
+ LWAL0025.INT \
+ LWAL0026.INT \
+ LWAL0027.INT \
+ STRP0001.INT \
+ STRP0002.INT \
+ STRP0003.INT \
+ STRP0004.INT \
+ STRP0005.INT \
+ STRP0006.INT \
+ STRP0007.INT \
+ STRP0008.INT \
+ STRP0009.INT \
+ STRP0010.INT \
+ STRP0011.INT \
+ WALL0001.INT \
+ WALL0002.INT \
+ WALL0003.INT \
+ WALL0004.INT \
+ WALL0005.INT \
+ WALL0006.INT \
+ WALL0007.INT \
+ WALL0008.INT \
+ WALL0009.INT \
+ WALL0010.INT \
+ WALL0011.INT \
+ WALL0012.INT \
+ WALL0013.INT \
+ WALL0014.INT \
+ WALL0015.INT \
+ WALL0016.INT \
+ WALL0017.INT \
+ WALL0018.INT \
+ WALL0019.INT \
+ WALL0020.INT \
+ WALL0021.INT \
+ WALL0022.INT \
+ WALL0023.INT \
+ WALL0024.INT \
+ WALL0025.INT \
+ WALL0026.INT \
+ WALL0027.INT \
+ WALL0028.INT \
+ WALL0029.INT \
+ WALL0030.INT \
+ WALL0031.INT \
+ WALL0032.INT \
+ WALL0033.INT \
+ WALL0034.INT \
+ WALL0035.INT \
+ WALL0036.INT \
+ WALL0037.INT \
+ WALL0038.INT \
+ WALL0039.INT \
+ WALL0040.INT \
+ WALL0041.INT \
+ WALL0042.INT \
+ WALL0043.INT \
+ WALL0044.INT \
+ WALL0045.INT \
+ WALL0046.INT \
+ WALL0047.INT \
+ WALL0048.INT \
+ WALL0049.INT \
+
+# Both the temperate and snow sets have identical template entries.
+TEMPERATEFILES = \
+ MINE.TEM \
+ ICE01.TEM \
+ ICE02.TEM \
+ ICE03.TEM \
+ ICE04.TEM \
+ ICE05.TEM \
+ MOVEFLSH.TEM \
+ BR1X.TEM \
+ BR2X.TEM \
+ BRIDGE1X.TEM \
+ BRIDGE2X.TEM \
+ BRIDGE1H.TEM \
+ BRIDGE2H.TEM \
+ F01.TEM \
+ F02.TEM \
+ F03.TEM \
+ F04.TEM \
+ F05.TEM \
+ F06.TEM \
+ ELECTRO.TEM \
+ B1.TEM \
+ B2.TEM \
+ B3.TEM \
+ BIB1.TEM \
+ BIB2.TEM \
+ BIB3.TEM \
+ BR1A.TEM \
+ BR1B.TEM \
+ BR1C.TEM \
+ BR2A.TEM \
+ BR2B.TEM \
+ BR2C.TEM \
+ BR3A.TEM \
+ BR3B.TEM \
+ BR3C.TEM \
+ BR3D.TEM \
+ BR3E.TEM \
+ BR3F.TEM \
+ BRIDGE1.TEM \
+ BRIDGE1D.TEM \
+ BRIDGE2.TEM \
+ BRIDGE2D.TEM \
+ CLEAR1.TEM \
+ CORPSE1.TEM \
+ CORPSE2.TEM \
+ CORPSE3.TEM \
+ CR1.TEM \
+ CR2.TEM \
+ CR3.TEM \
+ CR4.TEM \
+ CR5.TEM \
+ CR6.TEM \
+ D01.TEM \
+ D02.TEM \
+ D03.TEM \
+ D04.TEM \
+ D05.TEM \
+ D06.TEM \
+ D07.TEM \
+ D08.TEM \
+ D09.TEM \
+ D10.TEM \
+ D11.TEM \
+ D12.TEM \
+ D13.TEM \
+ D14.TEM \
+ D15.TEM \
+ D16.TEM \
+ D17.TEM \
+ D18.TEM \
+ D19.TEM \
+ D20.TEM \
+ D21.TEM \
+ D22.TEM \
+ D23.TEM \
+ D24.TEM \
+ D25.TEM \
+ D26.TEM \
+ D27.TEM \
+ D28.TEM \
+ D29.TEM \
+ D30.TEM \
+ D31.TEM \
+ D32.TEM \
+ D33.TEM \
+ D34.TEM \
+ D35.TEM \
+ D36.TEM \
+ D37.TEM \
+ D38.TEM \
+ D39.TEM \
+ D40.TEM \
+ D41.TEM \
+ D42.TEM \
+ D43.TEM \
+ D44.TEM \
+ D45.TEM \
+ FALLS1.TEM \
+ FALLS1A.TEM \
+ FALLS2.TEM \
+ FALLS2A.TEM \
+ FORD1.TEM \
+ FORD2.TEM \
+ GEM01.TEM \
+ GEM02.TEM \
+ GEM03.TEM \
+ GEM04.TEM \
+ GOLD01.TEM \
+ GOLD02.TEM \
+ GOLD03.TEM \
+ GOLD04.TEM \
+ HBOX.TEM \
+ MSLOMAKE.TEM \
+ HBOXMAKE.TEM \
+ MSLO.TEM \
+ P01.TEM \
+ P02.TEM \
+ P03.TEM \
+ P04.TEM \
+ P07.TEM \
+ P08.TEM \
+ P13.TEM \
+ P14.TEM \
+ RC01.TEM \
+ RC02.TEM \
+ RC03.TEM \
+ RC04.TEM \
+ RF01.TEM \
+ RF02.TEM \
+ RF03.TEM \
+ RF04.TEM \
+ RF05.TEM \
+ RF06.TEM \
+ RF07.TEM \
+ RF08.TEM \
+ RF09.TEM \
+ RF10.TEM \
+ RF11.TEM \
+ RV01.TEM \
+ RV02.TEM \
+ RV03.TEM \
+ RV04.TEM \
+ RV05.TEM \
+ RV06.TEM \
+ RV07.TEM \
+ RV08.TEM \
+ RV09.TEM \
+ RV10.TEM \
+ RV11.TEM \
+ RV12.TEM \
+ RV13.TEM \
+ RV14.TEM \
+ RV15.TEM \
+ S01.TEM \
+ S02.TEM \
+ S03.TEM \
+ S04.TEM \
+ S05.TEM \
+ S06.TEM \
+ S07.TEM \
+ S08.TEM \
+ S09.TEM \
+ S10.TEM \
+ S11.TEM \
+ S12.TEM \
+ S13.TEM \
+ S14.TEM \
+ S15.TEM \
+ S16.TEM \
+ S17.TEM \
+ S18.TEM \
+ S19.TEM \
+ S20.TEM \
+ S21.TEM \
+ S22.TEM \
+ S23.TEM \
+ S24.TEM \
+ S25.TEM \
+ S26.TEM \
+ S27.TEM \
+ S28.TEM \
+ S29.TEM \
+ S30.TEM \
+ S31.TEM \
+ S32.TEM \
+ S33.TEM \
+ S34.TEM \
+ S35.TEM \
+ S36.TEM \
+ S37.TEM \
+ S38.TEM \
+ SC1.TEM \
+ SC2.TEM \
+ SC3.TEM \
+ SC4.TEM \
+ SC5.TEM \
+ SC6.TEM \
+ SH01.TEM \
+ SH02.TEM \
+ SH03.TEM \
+ SH04.TEM \
+ SH05.TEM \
+ SH06.TEM \
+ SH07.TEM \
+ SH08.TEM \
+ SH09.TEM \
+ SH10.TEM \
+ SH11.TEM \
+ SH12.TEM \
+ SH13.TEM \
+ SH14.TEM \
+ SH15.TEM \
+ SH16.TEM \
+ SH17.TEM \
+ SH18.TEM \
+ SH19.TEM \
+ SH20.TEM \
+ SH21.TEM \
+ SH22.TEM \
+ SH23.TEM \
+ SH24.TEM \
+ SH25.TEM \
+ SH26.TEM \
+ SH27.TEM \
+ SH28.TEM \
+ SH29.TEM \
+ SH30.TEM \
+ SH31.TEM \
+ SH32.TEM \
+ SH33.TEM \
+ SH34.TEM \
+ SH35.TEM \
+ SH36.TEM \
+ SH37.TEM \
+ SH38.TEM \
+ SH39.TEM \
+ SH40.TEM \
+ SH41.TEM \
+ SH42.TEM \
+ SH43.TEM \
+ SH44.TEM \
+ SH45.TEM \
+ SH46.TEM \
+ SH47.TEM \
+ SH48.TEM \
+ SH49.TEM \
+ SH50.TEM \
+ SH51.TEM \
+ SH52.TEM \
+ SH53.TEM \
+ SH54.TEM \
+ SH55.TEM \
+ SH56.TEM \
+ T01.TEM \
+ T02.TEM \
+ T03.TEM \
+ T05.TEM \
+ T06.TEM \
+ T07.TEM \
+ T08.TEM \
+ T10.TEM \
+ T11.TEM \
+ T12.TEM \
+ T13.TEM \
+ T14.TEM \
+ T15.TEM \
+ T16.TEM \
+ T17.TEM \
+ TC01.TEM \
+ TC02.TEM \
+ TC03.TEM \
+ TC04.TEM \
+ TC05.TEM \
+ V01.TEM \
+ V02.TEM \
+ V03.TEM \
+ V04.TEM \
+ V05.TEM \
+ V06.TEM \
+ V07.TEM \
+ V08.TEM \
+ V09.TEM \
+ V10.TEM \
+ V11.TEM \
+ V12.TEM \
+ V13.TEM \
+ V14.TEM \
+ V15.TEM \
+ V16.TEM \
+ V17.TEM \
+ V18.TEM \
+ W1.TEM \
+ W2.TEM \
+ WC01.TEM \
+ WC02.TEM \
+ WC03.TEM \
+ WC04.TEM \
+ WC05.TEM \
+ WC06.TEM \
+ WC07.TEM \
+ WC08.TEM \
+ WC09.TEM \
+ WC10.TEM \
+ WC11.TEM \
+ WC12.TEM \
+ WC13.TEM \
+ WC14.TEM \
+ WC15.TEM \
+ WC16.TEM \
+ WC17.TEM \
+ WC18.TEM \
+ WC19.TEM \
+ WC20.TEM \
+ WC21.TEM \
+ WC22.TEM \
+ WC23.TEM \
+ WC24.TEM \
+ WC25.TEM \
+ WC26.TEM \
+ WC27.TEM \
+ WC28.TEM \
+ WC29.TEM \
+ WC30.TEM \
+ WC31.TEM \
+ WC32.TEM \
+ WC33.TEM \
+ WC34.TEM \
+ WC35.TEM \
+ WC36.TEM \
+ WC37.TEM \
+ WC38.TEM \
+
+# Every temperate theater terrain file has a snow theater counterpart.
+SNOWFILES = $(TEMPERATEFILES:.TEM=.SNO)
+
+# Sound effects (Juvenile or Adult)
+SFX = \
+
+# Generic wave files (never changes).
+WAVFILES = \
+ AACANON3.AUD \
+ BEEPSLCT.AUD \
+ BLEEP11.AUD \
+ BLEEP12.AUD \
+ BLEEP13.AUD \
+ BLEEP17.AUD \
+ BLEEP5.AUD \
+ BLEEP6.AUD \
+ BLEEP9.AUD \
+ BOMBIT1.AUD \
+ BUILD5.AUD \
+ BUZZY1.AUD \
+ CANNON1.AUD \
+ CANNON2.AUD \
+ CASHDN1.AUD \
+ CASHTURN.AUD \
+ CASHUP1.AUD \
+ CHRONO2.AUD \
+ CHROTNK1.AUD \
+ CHUTE1.AUD \
+ CMON1.AUD \
+ CRMBLE2.AUD \
+ DEDMAN1.AUD \
+ DEDMAN10.AUD \
+ DEDMAN2.AUD \
+ DEDMAN3.AUD \
+ DEDMAN4.AUD \
+ DEDMAN5.AUD \
+ DEDMAN6.AUD \
+ DEDMAN7.AUD \
+ DEDMAN8.AUD \
+ DOGG5P.AUD \
+ DOGW3PX.AUD \
+ DOGW5.AUD \
+ DOGW6.AUD \
+ DOGW7.AUD \
+ DOGY1.AUD \
+ EAFFIRM1.AUD \
+ EENGIN1.AUD \
+ EINAH1.AUD \
+ EINOK1.AUD \
+ EINYES1.AUD \
+ EMOVOUT1.AUD \
+ EYESSIR1.AUD \
+ FIREBL3.AUD \
+ FIRETRT1.AUD \
+ FIXIT1.AUD \
+ GIRLOKAY.AUD \
+ GIRLYEAH.AUD \
+ GOTIT1.AUD \
+ GRENADE1.AUD \
+ GUN11.AUD \
+ GUN13.AUD \
+ GUN27.AUD \
+ GUN5.AUD \
+ GUYOKAY1.AUD \
+ GUYYEAH1.AUD \
+ H2OBOMB2.AUD \
+ HEAL2.AUD \
+ HYDROD1.AUD \
+ INVUL2.AUD \
+ IRONCUR9.AUD \
+ JBURN1.AUD \
+ JCHRGE1.AUD \
+ JCRISP1.AUD \
+ JDANCE1.AUD \
+ JJUICE1.AUD \
+ JJUMP1.AUD \
+ JLIGHT1.AUD \
+ JPOWER1.AUD \
+ JSHOCK1.AUD \
+ JYES1.AUD \
+ KABOOM1.AUD \
+ KABOOM12.AUD \
+ KABOOM15.AUD \
+ KABOOM22.AUD \
+ KABOOM25.AUD \
+ KABOOM30.AUD \
+ KEEPEM1.AUD \
+ LAUGH1.AUD \
+ LEFTY1.AUD \
+ MADCHRG2.AUD \
+ MADEXPLO.AUD \
+ MAFFIRM1.AUD \
+ MBOSS1.AUD \
+ MHEAR1.AUD \
+ MHOTDIG1.AUD \
+ MHOWDY1.AUD \
+ MHUH1.AUD \
+ MGUNINF1.AUD \
+ MINE1.AUD \
+ MINEBLO1.AUD \
+ MINELAY1.AUD \
+ MISSILE1.AUD \
+ MISSILE6.AUD \
+ MISSILE7.AUD \
+ MLAFF1.AUD \
+ MMOVOUT1.AUD \
+ MRESPON1.AUD \
+ MRISE1.AUD \
+ MWRENCH1.AUD \
+ MYEEHAW1.AUD \
+ MYES1.AUD \
+ MYESSIR1.AUD \
+ ONIT1.AUD \
+ PILLBOX1.AUD \
+ PLACBLDG.AUD \
+ RABEEP1.AUD \
+ RADARDN1.AUD \
+ RADARON2.AUD \
+ RAMENU1.AUD \
+ ROKROLL1.AUD \
+ SAFFIRM1.AUD \
+ SANDBAG2.AUD \
+ SCOLDY1.AUD \
+ SCOMND1.AUD \
+ SHKTROP1.AUD \
+ SILENCER.AUD \
+ SINDEED1.AUD \
+ SKING1.AUD \
+ SMOUT1.AUD \
+ SOKAY1.AUD \
+ SONPULSE.AUD \
+ SONWAY1.AUD \
+ SPLASH9.AUD \
+ SQUISHY2.AUD \
+ SUBSHOW1.AUD \
+ SWHAT1.AUD \
+ SYEAH1.AUD \
+ SYESSIR1.AUD \
+ TANDETH1.AUD \
+ TANK5.AUD \
+ TANK6.AUD \
+ TESLA1.AUD \
+ TORPEDO1.AUD \
+ TSLACHG2.AUD \
+ TUFFGUY1.AUD \
+ TURRET1.AUD \
+ WALLKIL2.AUD \
+ YEAH1.AUD \
+ YES1.AUD \
+ YO1.AUD \
+
+# Vehicle responses
+RESPONSE1 = \
+ ACKNO.AUD \
+ AFFIRM1.AUD \
+ AWAIT1.AUD \
+ REPORT1.AUD \
+ VEHIC1.AUD \
+ YESSIR1.AUD \
+
+# Infantry responses
+RESPONSE2 = \
+ ACKNO.AUD \
+ AFFIRM1.AUD \
+ AWAIT1.AUD \
+ NOPROB.AUD \
+ OVEROUT.AUD \
+ READY.AUD \
+ REPORT1.AUD \
+ RITAWAY.AUD \
+ ROGER.AUD \
+ UGOTIT.AUD \
+ YESSIR1.AUD \
+
+#TSCOREFILES = \
+# cps\record.bin \
+# WIN1.AUD \
+# MAP1.AUD \
+
+VARFILES = \
+
+SCOREFILES = \
+ CREDITS.AUD \
+ AWAIT.AUD \
+ BIGF226M.AUD \
+ CRUS226M.AUD \
+ DENSE_R.AUD \
+ FAC1226M.AUD \
+ FAC2226M.AUD \
+ FOGGER1A.AUD \
+ HELL226M.AUD \
+ MUD1A.AUD \
+ RADIO2.AUD \
+ ROLLOUT.AUD \
+ RUN1226M.AUD \
+ SCORE.AUD \
+ SMSH226M.AUD \
+ SNAKE.AUD \
+ TERMINAT.AUD \
+ TREN226M.AUD \
+ TWIN.AUD \
+ VECTOR1A.AUD \
+ WORK226M.AUD \
+ 2ND_HAND.AUD \
+ ARAZIOD.AUD \
+ BACKSTAB.AUD \
+ CHAOS2.AUD \
+ SHUT_IT.AUD \
+ TWINMIX1.AUD \
+ UNDER3.AUD \
+ VR2.AUD \
+ BOG.AUD \
+ FLOAT_V2.AUD \
+ GLOOM.AUD \
+ GRNDWIRE.AUD \
+ RPT.AUD \
+ SEARCH.AUD \
+ TRACTION.AUD \
+ WASTELND.AUD \
+
+SPEECHFILES = \
+ STRCKIL1.AUD \
+ NOPOWR1.AUD \
+ SAVE1.AUD \
+ LOAD1.AUD \
+ 10MINR.AUD \
+ 1MINR.AUD \
+ 1OBJMET1.AUD \
+ 20MINR.AUD \
+ 2MINR.AUD \
+ 2OBJMET1.AUD \
+ 30MINR.AUD \
+ 3MINR.AUD \
+ 3OBJMET1.AUD \
+ 40MINR.AUD \
+ 4MINR.AUD \
+ 5MINR.AUD \
+ AAPPRO1.AUD \
+ AARIVE1.AUD \
+ AARIVE1.AUD \
+ AARRIVE1.AUD \
+ AARRIVN1.AUD \
+ AARRIVS1.AUD \
+ AARRIVW1.AUD \
+ AAVAIL1.AUD \
+ ABLDGIN1.AUD \
+ AFALLEN1.AUD \
+ ALAUNCH1.AUD \
+ APREP1.AUD \
+ AREADY1.AUD \
+ ARMORUP1.AUD \
+ ASELECT1.AUD \
+ ATLNCH1.AUD \
+ ATPREP1.AUD \
+ AUNITL1.AUD \
+ BASEATK1.AUD \
+ BCT1.AUD \
+ BLDGINF1.AUD \
+ BLDGPRG1.AUD \
+ CANCLD1.AUD \
+ CHROCHR1.AUD \
+ CHRORDY1.AUD \
+ CHROYES1.AUD \
+ CMDCNTR1.AUD \
+ CNTLDED1.AUD \
+ COMNDOF1.AUD \
+ COMNDOR1.AUD \
+ CONSCMP1.AUD \
+ CONVLST1.AUD \
+ CONVYAP1.AUD \
+ CREDIT1.AUD \
+ ENMYAPP1.AUD \
+ FIREPO1.AUD \
+ FLARE1.AUD \
+ FLAREE1.AUD \
+ FLAREN1.AUD \
+ FLARES1.AUD \
+ FLAREW1.AUD \
+ IRONCHG1.AUD \
+ IRONRDY1.AUD \
+ KOSYFRE1.AUD \
+ KOSYRES1.AUD \
+ LOPOWER1.AUD \
+ MERCF1.AUD \
+ MERCR1.AUD \
+ MISNLST1.AUD \
+ MISNWON1.AUD \
+ MTIMEIN1.AUD \
+ NAVYLST1.AUD \
+ NEWOPT1.AUD \
+ NOBUILD1.AUD \
+ NODEPLY1.AUD \
+ NOFUNDS1.AUD \
+ NOFUNDS1.AUD \
+ OBJMET1.AUD \
+ OBJNMET1.AUD \
+ OBJNRCH1.AUD \
+ OBJRCH1.AUD \
+ ONHOLD1.AUD \
+ OPTERM1.AUD \
+ PRIBLDG1.AUD \
+ PROGRES1.AUD \
+ PULSE1.AUD \
+ REINFOR1.AUD \
+ REPAIR1.AUD \
+ REPAIR1.AUD \
+ SATLNCH1.AUD \
+ SILOND1.AUD \
+ SLCTTGT1.AUD \
+ SOVEFAL1.AUD \
+ SOVEMP1.AUD \
+ SOVFAPP1.AUD \
+ SOVFORC1.AUD \
+ SOVREIN1.AUD \
+ SPYPLN1.AUD \
+ STRUCAP1.AUD \
+ STRUSLD1.AUD \
+ TANYAF1.AUD \
+ TANYAR1.AUD \
+ TARGFRE1.AUD \
+ TARGRES1.AUD \
+ TIMERGO1.AUD \
+ TIMERNO1.AUD \
+ TRAIN1.AUD \
+ UNITFUL1.AUD \
+ UNITLST1.AUD \
+ UNITRDY1.AUD \
+ UNITREP1.AUD \
+ UNITSLD1.AUD \
+ UNITSPD1.AUD \
+ XPLOPLC1.AUD \
+# ABLDGC1.AUD \
+# SOVBLDG1.AUD \
+# SOVSTRC1.AUD \
+# SOVUNTD1.AUD \
+# AUNITD1.AUD \
+# ASTRUCD1.AUD \
+
+#ALLIESVQ = \
+DUMMYVQ = \
+ AAGUN.VQA \
+ AFTRMATH.VQA \
+ ALLY1.VQA \
+ ALLY10.VQA \
+ ALLY10B.VQA \
+ ALLY11.VQA \
+ ALLY12.VQA \
+ ALLY14.VQA \
+ ALLY2.VQA \
+ ALLY4.VQA \
+ ALLY5.VQA \
+ ALLY6.VQA \
+ ALLY8.VQA \
+ ALLY9.VQA \
+ ALLYEND.VQA \
+ ALLYMORF.VQA \
+ APCESCPE.VQA \
+ ASSESS.VQA \
+ BATTLE.VQA \
+ 1BINOC.VQA \
+ BMAP.VQA \
+ BRDGTILT.VQA \
+ CRONTEST.VQA \
+ CRONFAIL.VQA \
+ DESTROYR.VQA \
+ DUD.VQA \
+ ELEVATOR.VQA \
+ FLARE.VQA \
+ FROZEN.VQA \
+ GRVESTNE.VQA \
+ LANDING.VQA \
+ MASASSLT.VQA \
+ MCV.VQA \
+ MCV_LAND.VQA \
+ MONTPASS.VQA \
+ OILDRUM.VQA \
+ OVERRUN.VQA \
+ PROLOG.VQA \
+ REDINTRO.VQA \
+ SHIPSINK.VQA \
+ SHORBOM1.VQA \
+ SHORBOM2.VQA \
+ SHORBOMB.VQA \
+ SNOWBOMB.VQA \
+ SOVIET1.VQA \
+ SOVTSTAR.VQA \
+ SPY.VQA \
+ TANYA1.VQA \
+ TANYA2.VQA \
+ TOOFAR.VQA \
+ TRINITY.VQA \
+# TRAILER.VQA \
+
+SOVIETVQ = \
+ AAGUN.VQA \
+ CRONFAIL.VQA \
+ AIRFIELD.VQA \
+ ALLY1.VQA \
+ ALLYMORF.VQA \
+ AVERTED.VQA \
+ BEACHEAD.VQA \
+ BMAP.VQA \
+ BOMBRUN.VQA \
+ COUNTDWN.VQA \
+ DOUBLE.VQA \
+ DPTHCHRG.VQA \
+ EXECUTE.VQA \
+ FLARE.VQA \
+ LANDING.VQA \
+ MCVBRDGE.VQA \
+ MIG.VQA \
+ MOVINGIN.VQA \
+ MTNKFACT.VQA \
+ NUKESTOK.VQA \
+ ONTHPRWL.VQA \
+ PERISCOP.VQA \
+ PROLOG.VQA \
+ RADRRAID.VQA \
+ REDINTRO.VQA \
+ SEARCH.VQA \
+ SFROZEN.VQA \
+ SITDUCK.VQA \
+ SLNTSRVC.VQA \
+ SNOWBOMB.VQA \
+ SNSTRAFE.VQA \
+ SOVBATL.VQA \
+ SOVCEMET.VQA \
+ SOVFINAL.VQA \
+ SOVIET1.VQA \
+ SOVIET10.VQA \
+ SOVIET11.VQA \
+ SOVIET12.VQA \
+ SOVIET13.VQA \
+ SOVIET14.VQA \
+ SOVIET2.VQA \
+ SOVIET3.VQA \
+ SOVIET4.VQA \
+ SOVIET5.VQA \
+ SOVIET6.VQA \
+ SOVIET7.VQA \
+ SOVIET8.VQA \
+ SOVIET9.VQA \
+ SOVMCV.VQA \
+ SOVTSTAR.VQA \
+ SPOTTER.VQA \
+ STRAFE.VQA \
+ TAKE_OFF.VQA \
+ TESLA.VQA \
+ V2ROCKET.VQA \
+# TRAILER.VQA \
+
+ALLIESVQ = \
+ AFTRMATH.VQA \
+ ALLY1.VQA \
+ ALLYMORF.VQA \
+ APCESCPE.VQA \
+ BATTLE.VQA \
+ BMAP.VQA \
+ CRONFAIL.VQA \
+ DPTHCHRG.VQA \
+ EXECUTE.VQA \
+ FLARE.VQA \
+ FROZEN.VQA \
+ GRVESTNE.VQA \
+ LANDING.VQA \
+ MASASSLT.VQA \
+ NUKESTOK.VQA \
+ ONTHPRWL.VQA \
+ OVERRUN.VQA \
+ PROLOG.VQA \
+ REDINTRO.VQA \
+ SFROZEN.VQA \
+ SLNTSRVC.VQA \
+ SNOWBOMB.VQA \
+ SNOWBASE.VQA \
+ SOVMCV.VQA \
+ SNSTRAFE.VQA \
+ SOVBATL.VQA \
+ SOVCEMET.VQA \
+ SOVIET1.VQA \
+ SOVTSTAR.VQA \
+ SPY.VQA \
+ STRAFE.VQA \
+ TESLA.VQA \
+ TOOFAR.VQA \
+ TRINITY.VQA \
+ V2ROCKET.VQA \
+# ANTEND.VQA \
+# ANTINTRO.VQA \
+
+
+
+# Files required for hires/Win95 version only
+#
+# This mix file is not cached
+#
+NOCACHEHIRESFILES= \
+ ENGLISH.VQA \
+ $(ALLIESVQ:.VQA=.VQP) \
+ $(SOVIETVQ:.VQA=.VQP) \
+
+
+LINTOBJECTS1 = $(OBJECTS:,=)
+LINTOBJECTS = $(LINTOBJECTS1:.OBJ=.LOB)
+
+# Mixfiles that should reside on the CD-ROM drive.
+CD1MIXFILES = \
+ CONQUER.MIX \
+ EDHI.MIX \
+ EDLO.MIX \
+ GENERAL.MIX \
+ INTERIOR.MIX \
+ MOVIES1.MIX \
+ SCORES.MIX \
+ SNOW.MIX \
+ SOUNDS.MIX \
+ RUSSIAN.MIX \
+ ALLIES.MIX \
+ TEMPERAT.MIX \
+
+
+
+# Mixfiles that should reside on the hard drive.
+LOCALMIXFILES = \
+ EDITOR.MIX \
+ HIRES.MIX \
+ LOCAL.MIX \
+ LORES.MIX \
+ NCHIRES.MIX \
+ SPEECH.MIX \
+
+
+# Mixfiles as they appear on the CD and hard drive.
+PACKFILES= $(.path.cd1)MAIN.MIX EXPAND2.MIX $(.path.cd1)tobreaki\REDALERT.MIX
+
+
+# Ant assets SOME ASSETS ARE HERE FOR OVERRIDING
+EXPANDFILES= \
+ ANT1.SHP \
+ ANT2.SHP \
+ ANT3.SHP \
+ QUEE.SHP \
+ CREDITS.ENG \
+ HILL01.TEM \
+ ANTBITE.AUD \
+ ANTDIE.AUD \
+ ANTDIE.SHP \
+ LAR1.SHP \
+ LAR2.SHP \
+ TITLE.PCX \
+ MISSION.INI \
+ BUZZY1.AUD \
+ STAVCMDR.AUD \
+ STAVCRSE.AUD \
+ STAVYES.AUD \
+ STAVMOV.AUD \
+ CONQUER.ENG \
+ RAMBO1.AUD \
+ RAMBO2.AUD \
+ RAMBO3.AUD \
+ TITLE.CPS \
+ TUTORIAL.INI \
+ BMAP.VQP \
+ ANTEND.VQP \
+ ANTINTRO.VQP \
+
+# Aftermath expansion files
+EXPAND2FILES= \
+ CARR.SHP \
+ CTNK.SHP \
+ DTRK.SHP \
+ MSUB.SHP \
+ QTNK.SHP \
+ TTNK.SHP \
+ STNK.SHP \
+ AFTRMATH.INI \
+ ANT1.SHP \
+ ANT2.SHP \
+ ANT3.SHP \
+ ANTBITE.AUD \
+ ANTDIE.AUD \
+ ANTDIE.SHP \
+ BUZZY1.AUD \
+ CONQUER.ENG \
+ CREDITS.TXT \
+ HILL01.TEM \
+ LAR1.SHP \
+ LAR2.SHP \
+ MISSION.INI \
+ MPLAYER.INI \
+ QUEE.SHP \
+ STAVCMDR.AUD \
+ STAVCRSE.AUD \
+ STAVYES.AUD \
+ STAVMOV.AUD \
+ TANK01.AUD \
+ TITLE.PCX \
+ TITLE.CPS \
+ TUTORIAL.INI \
+ BMAP.VQP \
+ stup_fix.shp \
+
+
+
+#############################################################
+# Rebuilds all the mixfiles.
+packfiles: always $(PACKFILES)
+
+always:
+ copy f:\projects\c&c0\editor\english\*.mix $(.path.mix) /u
+
+
+####################################################################
+# All mixfiles that exist on the CD-ROM are embedded within this mega-mixfile.
+$(.path.cd1)MAIN.MIX: $(CD1MIXFILES)
+ UTILS\MIXFILE -k -I$(.path.mix) &&!
+ $**
+! $(.path.cd1)$&.mix
+
+
+# All mixfiles that exist in the local directory are embedded within this mega-mixfile.
+$(.path.cd1)install\REDALERT.MIX: $(LOCALMIXFILES)
+ UTILS\MIXFILE -k -I$(.path.mix) &&!
+ $**
+! $(.path.cd1)install\$&.mix
+
+
+####################################################################
+# These are the various sub-mixfiles.
+CONQUER.MIX: $(CONQUERFILES) $(CACHEMAP) .\key.ini
+ UTILS\MIXFILE -k -h -I$(.path.cps) &&!
+ $(CONQUERFILES) $(CACHEMAP)
+! $(.path.mix)$&.mix
+
+TEMPERAT.MIX: $(TEMPERATEFILES) .\key.ini
+ UTILS\MIXFILE -h -k -I$(.path.cps) &&!
+ $(TEMPERATEFILES)
+! $(.path.mix)$&.mix
+
+SNOW.MIX: $(SNOWFILES) .\key.ini
+ UTILS\MIXFILE -h -k -I$(.path.cps) &&!
+ $(SNOWFILES)
+! $(.path.mix)$&.mix
+
+INTERIOR.MIX: $(INTERIORFILES) .\key.ini
+ UTILS\MIXFILE -h -k -I$(.path.cps) &&!
+ $(INTERIORFILES)
+! $(.path.mix)$&.mix
+
+GENERAL.MIX: $(GENERALFILES) $(GENERALMAPFILES) $(NETMAPFILES) $(MAPFILES) .\key.ini
+ UTILS\MIXFILE -k -I$(.path.cps) -I$(.path.ini) &&!
+ $(GENERALFILES) $(GENERALMAPFILES) $(NETMAPFILES) $(MAPFILES)
+! $(.path.mix)$&.mix
+
+SCORES.MIX: $(SCOREFILES)
+ UTILS\MIXFILE -k -I$(.path.cps) -I$(.path.ini) &&!
+ $**
+! $(.path.mix)$&.mix
+
+SOUNDS.MIX: $(WAVFILES) $(SFX)
+ UTILS\MIXFILE -h -k -EA60=V00 -EA61=V01 -EA62=V02 -EA63=V03 -I$(.path.aud) &&!
+ $**
+! $(.path.mix)$&.mix
+
+RUSSIAN.MIX: $(RESPONSE1:.AUD=.R00) $(RESPONSE2:.AUD=.R01) $(RESPONSE1:.AUD=.R02) $(RESPONSE2:.AUD=.R03)
+ UTILS\MIXFILE -h -k -I$(.path.aud) &&!
+ $**
+! $(.path.mix)$&.mix
+
+LIMITED.MIX: BLEEP11.AUD
+ UTILS\MIXFILE -h -k -I$(.path.aud) &&!
+ $**
+! $(.path.mix)$&.mix
+
+ALLIES.MIX: $(RESPONSE1:.AUD=.V00) $(RESPONSE2:.AUD=.V01) $(RESPONSE1:.AUD=.V02) $(RESPONSE2:.AUD=.V03)
+ UTILS\MIXFILE -h -k -I$(.path.aud) &&!
+ $**
+! $(.path.mix)$&.mix
+
+MOVIES1.MIX: $(ALLIESVQ)
+ UTILS\MIXFILE -k -I$(.path.vqa) &&!
+ $**
+! $(.path.mix)$&.mix
+
+
+
+
+
+NCHIRES.MIX: $(NOCACHEHIRESFILES:.SHP=.HI)
+ UTILS\MIXFILE -k -I$(.path.vqp) -I$(.path.cps) &&!
+ $(NOCACHEHIRESFILES)
+! $(.path.mix)$&.mix
+
+LOCAL.MIX: $(LOCALFILES) .\key.ini
+ UTILS\MIXFILE -h -k -E.A6=.AUD -I$(.path.ini) -I$(.path.txt) -I$(.path.cps) &&!
+ $(LOCALFILES)
+! $(.path.mix)$&.mix
+
+LORES.MIX: $(LOHILORES) .\key.ini
+ UTILS\MIXFILE -h -k -E.LOW=.SHP -E.LNT=.FNT -I$(.path.cps) &&!
+ $(LOHILORES)
+! $(.path.mix)$&.mix
+
+HIRES.MIX: $(HIRESFILES:.SHP=.HI) $(HIHILORES) .\key.ini
+ UTILS\MIXFILE -h -k -E.HI=.SHP -E.HNT=.FNT -I$(.path.cps) &&!
+ $(HIRESFILES:.SHP=.HI) $(HIHILORES)
+! $(.path.mix)$&.mix
+
+LORES1.MIX: $(LOHILORES1) .\key.ini
+ UTILS\MIXFILE -h -k -E.LOW=.SHP -E.LNT=.FNT -I$(.path.cps) &&!
+ $(LOHILORES1)
+! $(.path.mix)$&.mix
+
+HIRES1.MIX: $(HIHILORES1) .\key.ini
+ UTILS\MIXFILE -h -k -E.HI=.SHP -E.HNT=.FNT -I$(.path.cps) &&!
+ $(HIHILORES1)
+! $(.path.mix)$&.mix
+
+SPEECH.MIX: $(SPEECHFILES)
+ UTILS\MIXFILE -k -I$(.path.aud) &&!
+ $**
+! $(.path.mix)$&.mix
+
+
+EXPAND.MIX: $(EXPANDFILES)
+ UTILS\MIXFILE -k -I$(.path.mix) &&!
+ $**
+! $(.path.mix)$&.mix
+
+EXPAND2.MIX: $(EXPAND2FILES)
+ UTILS\MIXFILE -k -I$(.path.mix) &&!
+ $**
+! $(.path.mix)$&.mix
+
+#############################################################
+# Special rule to create the mouse shape (which must be a shape file)
+mouse.hi: $(.path.anm)hires\mouse.anm
+ -utils\makeshps $(.path.lbm)palettes\temperat.lbm &&!
+ &$(.path.anm)hires\mouse.anm;
+ end;
+! $(.path.hi)$&.hi $(SHAPEBUFFSIZE)
+
+# Special rule to create the mouse shape (which must be a shape file)
+mouse.low: $(.path.anm)lores\mouse.anm
+ -utils\makeshps $(.path.lbm)palettes\temperat.lbm &&!
+ &$(.path.anm)lores\mouse.anm;
+ end;
+! $(.path.low)$&.low $(SHAPEBUFFSIZE)
+
+
+#############################################################
+# Special build rule for radar animations so that they won't.
+#
+NATORADR.HI: $(.path.anm)hires\NATORADR.ANM
+ utils\newkeyf $** $(.path.hi)$&.hi -l -k
+
+USSRRADR.HI: $(.path.anm)hires\USSRRADR.ANM
+ utils\newkeyf $** $(.path.hi)$&.hi -l -k
+
+NATORADR.LOW: $(.path.anm)lores\NATORADR.ANM
+ utils\newkeyf $** $(.path.low)$&.low -l -k
+
+USSRRADR.LOW: $(.path.anm)lores\USSRRADR.ANM
+ utils\newkeyf $** $(.path.low)$&.low -l -k
+
+
+#############################################################
+# Debug text file creation.
+debug.eng: debug.txt
+ utils\textmake -b1000 eng\$&.txt $(.path.eng)$&.eng $&.h
+
+
diff --git a/CODE/BFILE2.MAK b/CODE/BFILE2.MAK
new file mode 100644
index 0000000..aa12b21
--- /dev/null
+++ b/CODE/BFILE2.MAK
@@ -0,0 +1,2098 @@
+#
+# Command & Conquer Red Alert(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&c0\vcs\code\bfile.mav 5.0 11 Nov 1996 09:40: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 : MAKEFILE *
+#* *
+#* Programmer : Joe L. Bostic *
+#* *
+#* Start Date : March 25, 1993 *
+#* *
+#* Last Update : March 25, 1993 [JLB] *
+#* *
+#*-------------------------------------------------------------------------*
+
+
+# Comment out the following line to disable "include file autodependency".
+.AUTODEPEND
+#.SWAP
+
+!include "rules.mak"
+
+
+##########################################################################
+
+MAPFILES = \
+
+CACHEMAP = \
+ BRIEFING.AUD \
+ BAR3RED.SHP \
+ BAR3BLU.SHP \
+ COUNTRYA.SHP \
+ COUNTRYE.SHP \
+ CREDSA.SHP \
+ CREDSU.SHP \
+ HISCORE1.SHP \
+ HISCORE2.SHP \
+ TIME.SHP \
+ CLOCK1.AUD \
+ COUNTRY4.AUD \
+ MAPWIPE2.AUD \
+ MAPWIPE5.AUD \
+ TONEY10.AUD \
+ TONEY4.AUD \
+ TONEY7.AUD \
+ SFX4.AUD \
+ BEEPY6.AUD \
+ KEYSTROK.AUD \
+ APPEAR1.AUD \
+ SCOLD1.AUD \
+ COUNTRY1.AUD \
+ ALI-TRAN.WSA \
+ SOV-TRAN.WSA \
+ ALIBACKH.PCX \
+ SOVBACKH.PCX \
+ BAR3RHR.SHP \
+ BAR3BHR.SHP \
+ CREDSAHR.SHP \
+ CREDSUHR.SHP \
+ HISC1-HR.SHP \
+ HISC2-HR.SHP \
+ TIMEHR.SHP \
+ MLTIPLYR.WSA \
+
+LOCALFILES = \
+ PROLOG.CPS \
+ MAP.AUD \
+ TITLE.CPS \
+ PALETTE.CPS \
+ INTRO.AUD \
+ EGOPAL.PAL \
+ RULES.INI \
+ CREDITS.TXT \
+ ALIPAPER.CPS \
+ 3POINT.FNT \
+ 8POINT.FNT \
+ EDITFNT.FNT \
+ CONQUER.ENG \
+ DEBUG.ENG \
+ LED.FNT \
+ SNOW.PAL \
+ TEMPERAT.PAL \
+ INTERIOR.PAL \
+ VCR.FNT \
+ HOLE0000.LUT \
+ HOLE0001.LUT \
+ HOLE0002.LUT \
+ HOLE0003.LUT \
+ HOLE0004.LUT \
+ HOLE0005.LUT \
+ HOLE0006.LUT \
+ HOLE0007.LUT \
+ HOLE0008.LUT \
+ HOLE0009.LUT \
+ HOLE0010.LUT \
+ HOLE0011.LUT \
+ HOLE0012.LUT \
+ HOLE0013.LUT \
+ HOLE0014.LUT \
+ HOLE0015.LUT \
+ HOLE0016.LUT \
+ HOLE0017.LUT \
+ HOLE0018.LUT \
+ HOLE0019.LUT \
+ HOLE0020.LUT \
+ HOLE0021.LUT \
+ HOLE0022.LUT \
+ HOLE0023.LUT \
+ HOLE0024.LUT \
+ HOLE0025.LUT \
+ HOLE0026.LUT \
+ HOLE0027.LUT \
+ HOLE0028.LUT \
+ HOLE0029.LUT \
+ HOLE0030.LUT \
+ HOLE0031.LUT \
+ HOLE0032.LUT \
+ HOLE0033.LUT \
+ HOLE0034.LUT \
+ HOLE0035.LUT \
+ HOLE0036.LUT \
+ HOLE0037.LUT \
+ HOLE0038.LUT \
+ HOLE0039.LUT \
+ HOLE0040.LUT \
+ HOLE0041.LUT \
+ HOLE0042.LUT \
+ HOLE0043.LUT \
+ HOLE0044.LUT \
+ HOLE0045.LUT \
+ HOLE0046.LUT \
+ HOLE0047.LUT \
+# TEMPSCOR.FNT \
+# 6POINT.FNT \
+# GRAD6FNT.FNT \
+# SCOREFNT.FNT \
+
+
+# Files that have counterparts in both high and low resolutions.
+# These files will be built into the HIRES.MIX and LORES.MIX files.
+HILORES = \
+ TRANICON.SHP \
+ PIPS.SHP \
+ PULSE.SHP \
+ ATOMICON.SHP \
+ WARPICON.SHP \
+ C1.SHP \
+ C2.SHP \
+ CHAN.SHP \
+ DELPHI.SHP \
+ E1.SHP \
+ E2.SHP \
+ E3.SHP \
+ E4.SHP \
+ E5.SHP \
+ E6.SHP \
+ E7.SHP \
+ EINSTEIN.SHP \
+ GNRL.SHP \
+ MECH.SHP \
+ MEDI.SHP \
+ SHOK.SHP \
+ SPY.SHP \
+ THF.SHP \
+ DD-BKGND.SHP \
+ DD-BOTM.SHP \
+ DD-CRNR.SHP \
+ DD-EDGE.SHP \
+ DD-LEFT.SHP \
+ DD-RIGHT.SHP \
+ DD-TOP.SHP \
+ 12METFNT.FNT \
+ GRAD6FNT.FNT \
+ HELP.FNT \
+ 6POINT.FNT \
+ TYPE.FNT \
+ SCOREFNT.FNT \
+ 1TNKICON.SHP \
+ 2TNKICON.SHP \
+ 3TNKICON.SHP \
+ 4TNKICON.SHP \
+ AFLDICON.SHP \
+ AGUNICON.SHP \
+ APCICON.SHP \
+ APWRICON.SHP \
+ ARTYICON.SHP \
+ ATEKICON.SHP \
+ BADRICON.SHP \
+ BARRICON.SHP \
+ BRIKICON.SHP \
+ BTN-DN.SHP \
+ BTN-PL.SHP \
+ BTN-ST.SHP \
+ BTN-UP.SHP \
+ CAICON.SHP \
+ CAMICON.SHP \
+ CARRICON.SHP \
+ CLOCK.SHP \
+ CTNKICON.SHP \
+ DDICON.SHP \
+ DOGICON.SHP \
+ DOMEICON.SHP \
+ DOMFICON.SHP \
+ DTRKICON.SHP \
+ E1ICON.SHP \
+ E2ICON.SHP \
+ E3ICON.SHP \
+ E4ICON.SHP \
+ E6ICON.SHP \
+ E7ICON.SHP \
+ FACFICON.SHP \
+ FACTICON.SHP \
+ FENCICON.SHP \
+ FIXICON.SHP \
+ FTURICON.SHP \
+ GAPICON.SHP \
+ GPSSICON.SHP \
+ GUNICON.SHP \
+ HARVICON.SHP \
+ HBOXICON.SHP \
+ HELIICON.SHP \
+ HINDICON.SHP \
+ HPADICON.SHP \
+ INFXICON.SHP \
+ IRONICON.SHP \
+ JEEPICON.SHP \
+ KENNICON.SHP \
+ LSTICON.SHP \
+ MAP.SHP \
+ MCVICON.SHP \
+ MECHICON.SHP \
+ MEDIICON.SHP \
+ MGGICON.SHP \
+ MIGICON.SHP \
+ MNLYICON.SHP \
+ MOUSE.SHP \
+ MRJICON.SHP \
+ MSLOICON.SHP \
+ MSUBICON.SHP \
+ NATORADR.SHP \
+ PBMBICON.SHP \
+ PBOXICON.SHP \
+ PDOXICON.SHP \
+ PINFICON.SHP \
+ POWER.SHP \
+ POWERBAR.SHP \
+ POWRICON.SHP \
+ PROCICON.SHP \
+ PTICON.SHP \
+ QTNKICON.SHP \
+ REPAIR.SHP \
+ SAMICON.SHP \
+ SBAGICON.SHP \
+ SELL.SHP \
+ SHOKICON.SHP \
+ SIDEBAR.SHP \
+ SILOICON.SHP \
+ SMIGICON.SHP \
+ SONRICON.SHP \
+ SOVPAPER.CPS \
+ SPEFICON.SHP \
+ SPENICON.SHP \
+ SPYICON.SHP \
+ SSICON.SHP \
+ STEKICON.SHP \
+ STRIP.SHP \
+ STRIPDN.SHP \
+ STRIPUP.SHP \
+ SYRDICON.SHP \
+ SYRFICON.SHP \
+ TABS.SHP \
+ TENTICON.SHP \
+ THFICON.SHP \
+ TRUKICON.SHP \
+ TTNKICON.SHP \
+ TSLAICON.SHP \
+ U2ICON.SHP \
+ USSRRADR.SHP \
+ V2RLICON.SHP \
+ WEAFICON.SHP \
+ WEAPICON.SHP \
+ YAKICON.SHP \
+ NRADRFRM.SHP \
+ URADRFRM.SHP \
+ SIDE1NA.SHP \
+ SIDE1US.SHP \
+ SIDE2NA.SHP \
+ SIDE2US.SHP \
+ SIDE3NA.SHP \
+ SIDE3US.SHP \
+ STRIPNA.SHP \
+ STRIPUS.SHP \
+
+# MOEBICON.SHP \
+
+# These helper macros substitute the extension so that
+# the appropriate art build rule will be invoked.
+xLOHILORES = $(HILORES:.SHP=.LOW)
+LOHILORES = $(xLOHILORES:.FNT=.LNT)
+xHIHILORES = $(HILORES:.SHP=.HI)
+HIHILORES = $(xHIHILORES:.FNT=.HNT)
+
+#
+# Files required for hires/Win95 version only
+#
+# This mix file is cached
+#
+HIRESFILES = \
+ ALIPAPER.PCX \
+ PROLOG.PCX \
+ SOVPAPER.PCX \
+ AFTR_HI.PCX \
+ ALY1.PCX \
+ APC_HI.PCX \
+ APHI0049.PCX \
+ BNHI0020.PCX \
+ DCHI0040.PCX \
+ FRHI0166.PCX \
+ LAB.PCX \
+ LANDSBRG.PCX \
+ MAHI0107.PCX \
+ MIG_HI.PCX \
+ MTFACTHI.PCX \
+ NEEDLE.PCX \
+ SOV2.PCX \
+ SPY.PCX \
+ STALIN.PCX \
+ TENT.PCX \
+# ENG_HI.PCX \
+
+
+CONQUERFILES = \
+ PARABOMB.SHP \
+ RADARFRM.SHP \
+ ARMOR.SHP \
+ FPOWER.SHP \
+ SPEED.SHP \
+ TQUAKE.SHP \
+ H2O_EXP1.SHP \
+ H2O_EXP2.SHP \
+ H2O_EXP3.SHP \
+ FLAK.SHP \
+ EBTN-DN.SHP \
+ EBTN-UP.SHP \
+ ATOMSFX.SHP \
+ TWINKLE1.SHP \
+ TWINKLE2.SHP \
+ TWINKLE3.SHP \
+ CHRONBOX.SHP \
+ GPSBOX.SHP \
+ INVULBOX.SHP \
+ PARABOX.SHP \
+ SONARBOX.SHP \
+ SPUTNIK.SHP \
+ SPUTDOOR.SHP \
+ ATOMICDN.SHP \
+ ATOMICUP.SHP \
+ TYPE.FNT \
+ 120MM.SHP \
+ 1TNK.SHP \
+ 2TNK.SHP \
+ 3TNK.SHP \
+ 4TNK.SHP \
+ 50CAL.SHP \
+ AFLD.SHP \
+ AGUN.SHP \
+ APC.SHP \
+ APWR.SHP \
+ ART-EXP1.SHP \
+ ARTY.SHP \
+ ATEK.SHP \
+ BADR.SHP \
+ BARB.SHP \
+ BARL.SHP \
+ BARR.SHP \
+ BIO.SHP \
+ BOMB.SHP \
+ BOMBLET.SHP \
+ BRIK.SHP \
+ BRL3.SHP \
+ BURN-L.SHP \
+ BURN-M.SHP \
+ BURN-S.SHP \
+ CA.SHP \
+ CYCL.SHP \
+ DD.SHP \
+ DEVIATOR.SHP \
+ DOG.SHP \
+ DOGBULLT.SHP \
+ DOLLAR.SHP \
+ DOME.SHP \
+ DRAGON.SHP \
+ EARTH.SHP \
+ ELECTDOG.SHP \
+ EMPULSE.SHP \
+ FACT.SHP \
+ FB1.SHP \
+ FB2.SHP \
+ FBALL1.SHP \
+ FCOM.SHP \
+ FENC.SHP \
+ FIRE1.SHP \
+ FIRE2.SHP \
+ FIRE3.SHP \
+ FIRE4.SHP \
+ FIX.SHP \
+ FLAGFLY.SHP \
+ FLMSPT.SHP \
+ FPLS.SHP \
+ FRAG1.SHP \
+ FTNK.SHP \
+ FTUR.SHP \
+ GAP.SHP \
+ GUN.SHP \
+ GUNFIRE.SHP \
+ HARV.SHP \
+ HELI.SHP \
+ HIND.SHP \
+ HOSP.SHP \
+ HPAD.SHP \
+ INVUN.SHP \
+ IRON.SHP \
+ JEEP.SHP \
+ KENN.SHP \
+ LITNING.SHP \
+ LROTOR.SHP \
+ LST.SHP \
+ MCV.SHP \
+ MGG.SHP \
+ MGUN.SHP \
+ MHQ.SHP \
+ MIG.SHP \
+ MINE.SHP \
+ MINIGUN.SHP \
+ MINP.SHP \
+ MINV.SHP \
+ MISS.SHP \
+ MISSILE.SHP \
+ MISSILE2.SHP \
+ MLRS.SHP \
+ MNLY.SHP \
+ MRJ.SHP \
+ NAPALM1.SHP \
+ NAPALM2.SHP \
+ NAPALM3.SHP \
+ ORCA.SHP \
+ PARACH.SHP \
+ PATRIOT.SHP \
+ PBOX.SHP \
+ PDOX.SHP \
+ PIFF.SHP \
+ PIFFPIFF.SHP \
+ POWR.SHP \
+ PROC.SHP \
+ PT.SHP \
+ RAPID.SHP \
+ RROTOR.SHP \
+ SAM.SHP \
+ SAMFIRE.SHP \
+ SBAG.SHP \
+ SCRATE.SHP \
+ SELECT.SHP \
+ SHADOW.SHP \
+ SILO.SHP \
+ SMIG.SHP \
+ SMOKEY.SHP \
+ SMOKE_M.SHP \
+ SMOKLAND.SHP \
+ SPEN.SHP \
+ SS.SHP \
+ SSAM.SHP \
+ STEALTH2.SHP \
+ STEK.SHP \
+ STNK.SHP \
+ SYRD.SHP \
+ TENT.SHP \
+ TRAN.SHP \
+ TRANS.ICN \
+ TRUK.SHP \
+ TSLA.SHP \
+ TURR.SHP \
+ U2.SHP \
+ V19.SHP \
+ V2.SHP \
+ V2RL.SHP \
+ VEH-HIT1.SHP \
+ VEH-HIT2.SHP \
+ VEH-HIT3.SHP \
+ WAKE.SHP \
+ WCRATE.SHP \
+ WWCRATE.SHP \
+ WEAP.SHP \
+ WEAP2.SHP \
+ WOOD.SHP \
+ YAK.SHP \
+ AFLDMAKE.SHP \
+ AGUNMAKE.SHP \
+ APWRMAKE.SHP \
+ ATEKMAKE.SHP \
+ BARRMAKE.SHP \
+ BIOMAKE.SHP \
+ DOMEMAKE.SHP \
+ FACTMAKE.SHP \
+ FIXMAKE.SHP \
+ FTURMAKE.SHP \
+ GAPMAKE.SHP \
+ GUNMAKE.SHP \
+ HOSPMAKE.SHP \
+ HPADMAKE.SHP \
+ IRONMAKE.SHP \
+ KENNMAKE.SHP \
+ MINPMAKE.SHP \
+ MINVMAKE.SHP \
+ PBOXMAKE.SHP \
+ POWRMAKE.SHP \
+ PDOXMAKE.SHP \
+ PROCMAKE.SHP \
+ PUMPMAKE.SHP \
+ SAMMAKE.SHP \
+ SILOMAKE.SHP \
+ SPENMAKE.SHP \
+ STEKMAKE.SHP \
+ SYRDMAKE.SHP \
+ TENTMAKE.SHP \
+ TSLAMAKE.SHP \
+ WEAPMAKE.SHP \
+
+GENERALMAPFILES = \
+ MISSIONS.PKT \
+ TUTORIAL.INI \
+ SCA01EA.INI \
+ SCA02EA.INI \
+ SCA03EA.INI \
+ SCA04EA.INI \
+ SCG01EA.INI \
+ SCG40EA.INI \
+ SCG41EA.INI \
+ SCG42EA.INI \
+ SCG43EA.INI \
+ SCG44EA.INI \
+ SCG45EA.INI \
+ SCG46EA.INI \
+ SCG47EA.INI \
+ SCG48EA.INI \
+ SCU40EA.INI \
+ SCU41EA.INI \
+ SCU42EA.INI \
+ SCU43EA.INI \
+ SCU44EA.INI \
+ SCU45EA.INI \
+ SCU46EA.INI \
+ SCU47EA.INI \
+ SCU48EA.INI \
+ SCU01EA.INI \
+ SCM01EA.INI \
+ SCM02EA.INI \
+ SCM03EA.INI \
+ SCM04EA.INI \
+ SCM05EA.INI \
+ SCM06EA.INI \
+ SCM07EA.INI \
+ SCM08EA.INI \
+ SCM09EA.INI \
+ SCM10EA.INI \
+ SCM11EA.INI \
+ SCM12EA.INI \
+ SCM13EA.INI \
+ SCM14EA.INI \
+ SCM15EA.INI \
+ SCM16EA.INI \
+ SCM17EA.INI \
+ SCM18EA.INI \
+ SCM19EA.INI \
+ SCM20EA.INI \
+ SCM21EA.INI \
+ SCM22EA.INI \
+ SCM23EA.INI \
+ SCM24EA.INI \
+ SCM25EA.INI \
+ SCM26EA.INI \
+ SCM27EA.INI \
+ SCM28EA.INI \
+ SCM29EA.INI \
+ SCM30EA.INI \
+ SCM31EA.INI \
+ SCM32EA.INI \
+ SCM33EA.INI \
+ SCM34EA.INI \
+ SCM35EA.INI \
+ SCM36EA.INI \
+ SCM37EA.INI \
+ SCM38EA.INI \
+ SCM39EA.INI \
+ SCM40EA.INI \
+ SCM41EA.INI \
+ SCM42EA.INI \
+ SCM43EA.INI \
+ SCM44EA.INI \
+ SCM45EA.INI \
+ SCM46EA.INI \
+ SCM47EA.INI \
+ SCM48EA.INI \
+ SCM49EA.INI \
+ SCM50EA.INI \
+ SCM51EA.INI \
+ SCM52EA.INI \
+ SCM53EA.INI \
+ SCM54EA.INI \
+ SCM55EA.INI \
+ SCM56EA.INI \
+ SCM57EA.INI \
+ SCM58EA.INI \
+ SCM59EA.INI \
+ SCM60EA.INI \
+ SCM61EA.INI \
+ SCM62EA.INI \
+ SCM63EA.INI \
+ SCM64EA.INI \
+ SCM65EA.INI \
+ SCM66EA.INI \
+ SCM67EA.INI \
+ SCM68EA.INI \
+ SCM69EA.INI \
+ SCM70EA.INI \
+ SCM71EA.INI \
+ SCM72EA.INI \
+ SCM73EA.INI \
+ SCM74EA.INI \
+ SCM75EA.INI \
+ SCM76EA.INI \
+ SCM77EA.INI \
+ SCM78EA.INI \
+ SCM79EA.INI \
+ SCM80EA.INI \
+ SCM81EA.INI \
+ SCM82EA.INI \
+ SCM83EA.INI \
+ SCM84EA.INI \
+ SCM85EA.INI \
+ SCM86EA.INI \
+ SCM87EA.INI \
+ SCM88EA.INI \
+ SCM89EA.INI \
+ SCM90EA.INI \
+ SCM91EA.INI \
+ SCM92EA.INI \
+ SCM93EA.INI \
+ SCM94EA.INI \
+ SCM95EA.INI \
+ SCM96EA.INI \
+ SCM97EA.INI \
+ SCM98EA.INI \
+ SCM99EA.INI \
+ SCM100EA.INI \
+ SCM101EA.INI \
+ SCM102EA.INI \
+ SCM103EA.INI \
+ SCM104EA.INI \
+ SCM105EA.INI \
+ SCM106EA.INI \
+ SCM107EA.INI \
+ SCM108EA.INI \
+ SCM109EA.INI \
+ SCM110EA.INI \
+ SCM111EA.INI \
+ SCM112EA.INI \
+ SCM113EA.INI \
+ SCM114EA.INI \
+ SCM115EA.INI \
+ SCM116EA.INI \
+ SCM117EA.INI \
+ SCM118EA.INI \
+ SCM119EA.INI \
+ SCM120EA.INI \
+ SCM121EA.INI \
+ SCM122EA.INI \
+ SCM123EA.INI \
+ SCM124EA.INI \
+ SCM125EA.INI \
+ SCM126EA.INI \
+ SCM127EA.INI \
+ SCM128EA.INI \
+ SCM129EA.INI \
+ SCM130EA.INI \
+ SCMD0EA.INI \
+ SCMD1EA.INI \
+ SCMD2EA.INI \
+ SCMD3EA.INI \
+ SCMD4EA.INI \
+ SCMD5EA.INI \
+ SCMD6EA.INI \
+ SCMD7EA.INI \
+ SCMD8EA.INI \
+ SCMD9EA.INI \
+ SCME0EA.INI \
+ SCME1EA.INI \
+ SCME2EA.INI \
+ SCME3EA.INI \
+ SCME4EA.INI \
+ SCME5EA.INI \
+ SCME6EA.INI \
+ SCME7EA.INI \
+ SCME8EA.INI \
+ SCME9EA.INI \
+ SCMF0EA.INI \
+ SCMF1EA.INI \
+ SCMF2EA.INI \
+ SCMF3EA.INI \
+ SCMF4EA.INI \
+ SCMF5EA.INI \
+ SCMF6EA.INI \
+ SCMF7EA.INI \
+ SCMF8EA.INI \
+ SCMF9EA.INI \
+ SCMG0EA.INI \
+ SCMG1EA.INI \
+ SCMG2EA.INI \
+ SCMG3EA.INI \
+ SCMG4EA.INI \
+ SCMG5EA.INI \
+ SCMG6EA.INI \
+ SCMG7EA.INI \
+ SCMG8EA.INI \
+ SCMG9EA.INI \
+ SCMH0EA.INI \
+ SCMH1EA.INI \
+ SCMH2EA.INI \
+ SCMH3EA.INI \
+ SCMH4EA.INI \
+ SCMH5EA.INI \
+ SCMH6EA.INI \
+ SCMH7EA.INI \
+ SCMH8EA.INI \
+ SCMH9EA.INI \
+ SCMI0EA.INI \
+ SCMI1EA.INI \
+ SCMI2EA.INI \
+ SCMI3EA.INI \
+ SCMI4EA.INI \
+ SCMI5EA.INI \
+ SCMI6EA.INI \
+ SCMI7EA.INI \
+ SCMI8EA.INI \
+ SCMI9EA.INI \
+ SCMJ0EA.INI \
+ SCMJ1EA.INI \
+ SCMJ2EA.INI \
+ SCMJ3EA.INI \
+ SCMJ4EA.INI \
+ SCMJ5EA.INI \
+ SCMJ6EA.INI \
+ SCMJ7EA.INI \
+ SCMJ8EA.INI \
+ SCMJ9EA.INI \
+ SCMK0EA.INI \
+ SCMK1EA.INI \
+ SCMK2EA.INI \
+ SCMK3EA.INI \
+ SCMK4EA.INI \
+ SCMK5EA.INI \
+ SCMK6EA.INI \
+ SCMK7EA.INI \
+ SCMK8EA.INI \
+ SCMK9EA.INI \
+ SCML0EA.INI \
+ SCML1EA.INI \
+ SCML2EA.INI \
+ SCML3EA.INI \
+ SCML4EA.INI \
+ SCML5EA.INI \
+ SCML6EA.INI \
+ SCML7EA.INI \
+ SCML8EA.INI \
+ SCML9EA.INI \
+ SCMM0EA.INI \
+ SCMM1EA.INI \
+ SCMM2EA.INI \
+ SCMM3EA.INI \
+ SCMM4EA.INI \
+ SCMM5EA.INI \
+ SCMM6EA.INI \
+ SCMM7EA.INI \
+ SCMM8EA.INI \
+ SCMM9EA.INI \
+
+NETMAPFILES = \
+
+# Files that aren't cached.
+GENERALFILES = \
+ AFTR_LO.CPS \
+ ALY1-LO.CPS \
+ APC_LO.CPS \
+ APLO0049.CPS \
+ BNLO0020.CPS \
+ DCLO0040.CPS \
+ FRLO0166.CPS \
+ LAB-LO.CPS \
+ LANDS-LO.CPS \
+ MALO0107.CPS \
+ MIG_LO.CPS \
+ MTFACTLO.CPS \
+ NEEDL-LO.CPS \
+ SOV2-LO.CPS \
+ SPY-LO.CPS \
+ STALN-LO.CPS \
+ TENT-LO.CPS \
+ TITLE.CPS \
+ PPAPER.CPS \
+ MSAA.WSA \
+ MSAB.WSA \
+ MSAC.WSA \
+ MSAD.WSA \
+ MSAE.WSA \
+ MSAF.WSA \
+ MSAG.WSA \
+ MSAH.WSA \
+ MSAI.WSA \
+ MSAJ.WSA \
+ MSAK.WSA \
+ MSAL.WSA \
+ MSAM.WSA \
+ MSAN.WSA \
+ MSSA.WSA \
+ MSSB.WSA \
+ MSSC.WSA \
+ MSSD.WSA \
+ MSSE.WSA \
+ MSSF.WSA \
+ MSSG.WSA \
+ MSSH.WSA \
+ MSSI.WSA \
+ MSSJ.WSA \
+ MSSK.WSA \
+ MSSL.WSA \
+ MSSM.WSA \
+ MSSN.WSA \
+
+INTERIORFILES = \
+ BOXES01.INT \
+ BOXES02.INT \
+ BOXES03.INT \
+ BOXES04.INT \
+ BOXES05.INT \
+ BOXES06.INT \
+ BOXES07.INT \
+ BOXES08.INT \
+ BOXES09.INT \
+ XTRA0001.INT \
+ XTRA0002.INT \
+ XTRA0003.INT \
+ XTRA0004.INT \
+ XTRA0005.INT \
+ XTRA0006.INT \
+ XTRA0007.INT \
+ XTRA0008.INT \
+ XTRA0009.INT \
+ XTRA0010.INT \
+ XTRA0011.INT \
+ XTRA0012.INT \
+ XTRA0013.INT \
+ XTRA0014.INT \
+ XTRA0015.INT \
+ XTRA0016.INT \
+ CLEAR1.INT \
+ MOVEFLSH.INT \
+ ARRO0001.INT \
+ ARRO0002.INT \
+ ARRO0003.INT \
+ ARRO0004.INT \
+ ARRO0005.INT \
+ ARRO0006.INT \
+ ARRO0007.INT \
+ ARRO0008.INT \
+ ARRO0009.INT \
+ ARRO0010.INT \
+ ARRO0011.INT \
+ ARRO0012.INT \
+ ARRO0013.INT \
+ ARRO0014.INT \
+ ARRO0015.INT \
+ FLOR0001.INT \
+ FLOR0002.INT \
+ FLOR0003.INT \
+ FLOR0004.INT \
+ FLOR0005.INT \
+ FLOR0006.INT \
+ FLOR0007.INT \
+ GFLR0001.INT \
+ GFLR0002.INT \
+ GFLR0003.INT \
+ GFLR0004.INT \
+ GFLR0005.INT \
+ GSTR0001.INT \
+ GSTR0002.INT \
+ GSTR0003.INT \
+ GSTR0004.INT \
+ GSTR0005.INT \
+ GSTR0006.INT \
+ GSTR0007.INT \
+ GSTR0008.INT \
+ GSTR0009.INT \
+ GSTR0010.INT \
+ GSTR0011.INT \
+ LWAL0001.INT \
+ LWAL0002.INT \
+ LWAL0003.INT \
+ LWAL0004.INT \
+ LWAL0005.INT \
+ LWAL0006.INT \
+ LWAL0007.INT \
+ LWAL0008.INT \
+ LWAL0009.INT \
+ LWAL0010.INT \
+ LWAL0011.INT \
+ LWAL0012.INT \
+ LWAL0013.INT \
+ LWAL0014.INT \
+ LWAL0015.INT \
+ LWAL0016.INT \
+ LWAL0017.INT \
+ LWAL0018.INT \
+ LWAL0019.INT \
+ LWAL0020.INT \
+ LWAL0021.INT \
+ LWAL0022.INT \
+ LWAL0023.INT \
+ LWAL0024.INT \
+ LWAL0025.INT \
+ LWAL0026.INT \
+ LWAL0027.INT \
+ STRP0001.INT \
+ STRP0002.INT \
+ STRP0003.INT \
+ STRP0004.INT \
+ STRP0005.INT \
+ STRP0006.INT \
+ STRP0007.INT \
+ STRP0008.INT \
+ STRP0009.INT \
+ STRP0010.INT \
+ STRP0011.INT \
+ WALL0001.INT \
+ WALL0002.INT \
+ WALL0003.INT \
+ WALL0004.INT \
+ WALL0005.INT \
+ WALL0006.INT \
+ WALL0007.INT \
+ WALL0008.INT \
+ WALL0009.INT \
+ WALL0010.INT \
+ WALL0011.INT \
+ WALL0012.INT \
+ WALL0013.INT \
+ WALL0014.INT \
+ WALL0015.INT \
+ WALL0016.INT \
+ WALL0017.INT \
+ WALL0018.INT \
+ WALL0019.INT \
+ WALL0020.INT \
+ WALL0021.INT \
+ WALL0022.INT \
+ WALL0023.INT \
+ WALL0024.INT \
+ WALL0025.INT \
+ WALL0026.INT \
+ WALL0027.INT \
+ WALL0028.INT \
+ WALL0029.INT \
+ WALL0030.INT \
+ WALL0031.INT \
+ WALL0032.INT \
+ WALL0033.INT \
+ WALL0034.INT \
+ WALL0035.INT \
+ WALL0036.INT \
+ WALL0037.INT \
+ WALL0038.INT \
+ WALL0039.INT \
+ WALL0040.INT \
+ WALL0041.INT \
+ WALL0042.INT \
+ WALL0043.INT \
+ WALL0044.INT \
+ WALL0045.INT \
+ WALL0046.INT \
+ WALL0047.INT \
+ WALL0048.INT \
+ WALL0049.INT \
+
+# Both the temperate and snow sets have identical template entries.
+TEMPERATEFILES = \
+ MINE.TEM \
+ ICE01.TEM \
+ ICE02.TEM \
+ ICE03.TEM \
+ ICE04.TEM \
+ ICE05.TEM \
+ MOVEFLSH.TEM \
+ BR1X.TEM \
+ BR2X.TEM \
+ BRIDGE1X.TEM \
+ BRIDGE2X.TEM \
+ BRIDGE1H.TEM \
+ BRIDGE2H.TEM \
+ F01.TEM \
+ F02.TEM \
+ F03.TEM \
+ F04.TEM \
+ F05.TEM \
+ F06.TEM \
+ ELECTRO.TEM \
+ B1.TEM \
+ B2.TEM \
+ B3.TEM \
+ BIB1.TEM \
+ BIB2.TEM \
+ BIB3.TEM \
+ BR1A.TEM \
+ BR1B.TEM \
+ BR1C.TEM \
+ BR2A.TEM \
+ BR2B.TEM \
+ BR2C.TEM \
+ BR3A.TEM \
+ BR3B.TEM \
+ BR3C.TEM \
+ BR3D.TEM \
+ BR3E.TEM \
+ BR3F.TEM \
+ BRIDGE1.TEM \
+ BRIDGE1D.TEM \
+ BRIDGE2.TEM \
+ BRIDGE2D.TEM \
+ CLEAR1.TEM \
+ CORPSE1.TEM \
+ CORPSE2.TEM \
+ CORPSE3.TEM \
+ CR1.TEM \
+ CR2.TEM \
+ CR3.TEM \
+ CR4.TEM \
+ CR5.TEM \
+ CR6.TEM \
+ D01.TEM \
+ D02.TEM \
+ D03.TEM \
+ D04.TEM \
+ D05.TEM \
+ D06.TEM \
+ D07.TEM \
+ D08.TEM \
+ D09.TEM \
+ D10.TEM \
+ D11.TEM \
+ D12.TEM \
+ D13.TEM \
+ D14.TEM \
+ D15.TEM \
+ D16.TEM \
+ D17.TEM \
+ D18.TEM \
+ D19.TEM \
+ D20.TEM \
+ D21.TEM \
+ D22.TEM \
+ D23.TEM \
+ D24.TEM \
+ D25.TEM \
+ D26.TEM \
+ D27.TEM \
+ D28.TEM \
+ D29.TEM \
+ D30.TEM \
+ D31.TEM \
+ D32.TEM \
+ D33.TEM \
+ D34.TEM \
+ D35.TEM \
+ D36.TEM \
+ D37.TEM \
+ D38.TEM \
+ D39.TEM \
+ D40.TEM \
+ D41.TEM \
+ D42.TEM \
+ D43.TEM \
+ D44.TEM \
+ D45.TEM \
+ FALLS1.TEM \
+ FALLS1A.TEM \
+ FALLS2.TEM \
+ FALLS2A.TEM \
+ FORD1.TEM \
+ FORD2.TEM \
+ GEM01.TEM \
+ GEM02.TEM \
+ GEM03.TEM \
+ GEM04.TEM \
+ GOLD01.TEM \
+ GOLD02.TEM \
+ GOLD03.TEM \
+ GOLD04.TEM \
+ HBOX.TEM \
+ MSLOMAKE.TEM \
+ HBOXMAKE.TEM \
+ MSLO.TEM \
+ P01.TEM \
+ P02.TEM \
+ P03.TEM \
+ P04.TEM \
+ P07.TEM \
+ P08.TEM \
+ P13.TEM \
+ P14.TEM \
+ RC01.TEM \
+ RC02.TEM \
+ RC03.TEM \
+ RC04.TEM \
+ RF01.TEM \
+ RF02.TEM \
+ RF03.TEM \
+ RF04.TEM \
+ RF05.TEM \
+ RF06.TEM \
+ RF07.TEM \
+ RF08.TEM \
+ RF09.TEM \
+ RF10.TEM \
+ RF11.TEM \
+ RV01.TEM \
+ RV02.TEM \
+ RV03.TEM \
+ RV04.TEM \
+ RV05.TEM \
+ RV06.TEM \
+ RV07.TEM \
+ RV08.TEM \
+ RV09.TEM \
+ RV10.TEM \
+ RV11.TEM \
+ RV12.TEM \
+ RV13.TEM \
+ RV14.TEM \
+ RV15.TEM \
+ S01.TEM \
+ S02.TEM \
+ S03.TEM \
+ S04.TEM \
+ S05.TEM \
+ S06.TEM \
+ S07.TEM \
+ S08.TEM \
+ S09.TEM \
+ S10.TEM \
+ S11.TEM \
+ S12.TEM \
+ S13.TEM \
+ S14.TEM \
+ S15.TEM \
+ S16.TEM \
+ S17.TEM \
+ S18.TEM \
+ S19.TEM \
+ S20.TEM \
+ S21.TEM \
+ S22.TEM \
+ S23.TEM \
+ S24.TEM \
+ S25.TEM \
+ S26.TEM \
+ S27.TEM \
+ S28.TEM \
+ S29.TEM \
+ S30.TEM \
+ S31.TEM \
+ S32.TEM \
+ S33.TEM \
+ S34.TEM \
+ S35.TEM \
+ S36.TEM \
+ S37.TEM \
+ S38.TEM \
+ SC1.TEM \
+ SC2.TEM \
+ SC3.TEM \
+ SC4.TEM \
+ SC5.TEM \
+ SC6.TEM \
+ SH01.TEM \
+ SH02.TEM \
+ SH03.TEM \
+ SH04.TEM \
+ SH05.TEM \
+ SH06.TEM \
+ SH07.TEM \
+ SH08.TEM \
+ SH09.TEM \
+ SH10.TEM \
+ SH11.TEM \
+ SH12.TEM \
+ SH13.TEM \
+ SH14.TEM \
+ SH15.TEM \
+ SH16.TEM \
+ SH17.TEM \
+ SH18.TEM \
+ SH19.TEM \
+ SH20.TEM \
+ SH21.TEM \
+ SH22.TEM \
+ SH23.TEM \
+ SH24.TEM \
+ SH25.TEM \
+ SH26.TEM \
+ SH27.TEM \
+ SH28.TEM \
+ SH29.TEM \
+ SH30.TEM \
+ SH31.TEM \
+ SH32.TEM \
+ SH33.TEM \
+ SH34.TEM \
+ SH35.TEM \
+ SH36.TEM \
+ SH37.TEM \
+ SH38.TEM \
+ SH39.TEM \
+ SH40.TEM \
+ SH41.TEM \
+ SH42.TEM \
+ SH43.TEM \
+ SH44.TEM \
+ SH45.TEM \
+ SH46.TEM \
+ SH47.TEM \
+ SH48.TEM \
+ SH49.TEM \
+ SH50.TEM \
+ SH51.TEM \
+ SH52.TEM \
+ SH53.TEM \
+ SH54.TEM \
+ SH55.TEM \
+ SH56.TEM \
+ T01.TEM \
+ T02.TEM \
+ T03.TEM \
+ T05.TEM \
+ T06.TEM \
+ T07.TEM \
+ T08.TEM \
+ T10.TEM \
+ T11.TEM \
+ T12.TEM \
+ T13.TEM \
+ T14.TEM \
+ T15.TEM \
+ T16.TEM \
+ T17.TEM \
+ TC01.TEM \
+ TC02.TEM \
+ TC03.TEM \
+ TC04.TEM \
+ TC05.TEM \
+ V01.TEM \
+ V02.TEM \
+ V03.TEM \
+ V04.TEM \
+ V05.TEM \
+ V06.TEM \
+ V07.TEM \
+ V08.TEM \
+ V09.TEM \
+ V10.TEM \
+ V11.TEM \
+ V12.TEM \
+ V13.TEM \
+ V14.TEM \
+ V15.TEM \
+ V16.TEM \
+ V17.TEM \
+ V18.TEM \
+ W1.TEM \
+ W2.TEM \
+ WC01.TEM \
+ WC02.TEM \
+ WC03.TEM \
+ WC04.TEM \
+ WC05.TEM \
+ WC06.TEM \
+ WC07.TEM \
+ WC08.TEM \
+ WC09.TEM \
+ WC10.TEM \
+ WC11.TEM \
+ WC12.TEM \
+ WC13.TEM \
+ WC14.TEM \
+ WC15.TEM \
+ WC16.TEM \
+ WC17.TEM \
+ WC18.TEM \
+ WC19.TEM \
+ WC20.TEM \
+ WC21.TEM \
+ WC22.TEM \
+ WC23.TEM \
+ WC24.TEM \
+ WC25.TEM \
+ WC26.TEM \
+ WC27.TEM \
+ WC28.TEM \
+ WC29.TEM \
+ WC30.TEM \
+ WC31.TEM \
+ WC32.TEM \
+ WC33.TEM \
+ WC34.TEM \
+ WC35.TEM \
+ WC36.TEM \
+ WC37.TEM \
+ WC38.TEM \
+
+# Every temperate theater terrain file has a snow theater counterpart.
+SNOWFILES = $(TEMPERATEFILES:.TEM=.SNO)
+
+# Sound effects (Juvenile or Adult)
+SFX = \
+
+# Generic wave files (never changes).
+WAVFILES = \
+ AACANON3.AUD \
+ BEEPSLCT.AUD \
+ BLEEP11.AUD \
+ BLEEP12.AUD \
+ BLEEP13.AUD \
+ BLEEP17.AUD \
+ BLEEP5.AUD \
+ BLEEP6.AUD \
+ BLEEP9.AUD \
+ BOMBIT1.AUD \
+ BUILD5.AUD \
+ BUZZY1.AUD \
+ CANNON1.AUD \
+ CANNON2.AUD \
+ CASHDN1.AUD \
+ CASHTURN.AUD \
+ CASHUP1.AUD \
+ CHRONO2.AUD \
+ CHROTNK1.AUD \
+ CHUTE1.AUD \
+ CMON1.AUD \
+ CRMBLE2.AUD \
+ DEDMAN1.AUD \
+ DEDMAN10.AUD \
+ DEDMAN2.AUD \
+ DEDMAN3.AUD \
+ DEDMAN4.AUD \
+ DEDMAN5.AUD \
+ DEDMAN6.AUD \
+ DEDMAN7.AUD \
+ DEDMAN8.AUD \
+ DOGG5P.AUD \
+ DOGW3PX.AUD \
+ DOGW5.AUD \
+ DOGW6.AUD \
+ DOGW7.AUD \
+ DOGY1.AUD \
+ EAFFIRM1.AUD \
+ EENGIN1.AUD \
+ EINAH1.AUD \
+ EINOK1.AUD \
+ EINYES1.AUD \
+ EMOVOUT1.AUD \
+ EYESSIR1.AUD \
+ FIREBL3.AUD \
+ FIRETRT1.AUD \
+ FIXIT1.AUD \
+ GIRLOKAY.AUD \
+ GIRLYEAH.AUD \
+ GOTIT1.AUD \
+ GRENADE1.AUD \
+ GUN11.AUD \
+ GUN13.AUD \
+ GUN27.AUD \
+ GUN5.AUD \
+ GUYOKAY1.AUD \
+ GUYYEAH1.AUD \
+ H2OBOMB2.AUD \
+ HEAL2.AUD \
+ HYDROD1.AUD \
+ INVUL2.AUD \
+ IRONCUR9.AUD \
+ JBURN1.AUD \
+ JCHRGE1.AUD \
+ JCRISP1.AUD \
+ JDANCE1.AUD \
+ JJUICE1.AUD \
+ JJUMP1.AUD \
+ JLIGHT1.AUD \
+ JPOWER1.AUD \
+ JSHOCK1.AUD \
+ JYES1.AUD \
+ KABOOM1.AUD \
+ KABOOM12.AUD \
+ KABOOM15.AUD \
+ KABOOM22.AUD \
+ KABOOM25.AUD \
+ KABOOM30.AUD \
+ KEEPEM1.AUD \
+ LAUGH1.AUD \
+ LEFTY1.AUD \
+ MADCHRG2.AUD \
+ MADEXPLO.AUD \
+ MAFFIRM1.AUD \
+ MBOSS1.AUD \
+ MHEAR1.AUD \
+ MHOTDIG1.AUD \
+ MHOWDY1.AUD \
+ MHUH1.AUD \
+ MGUNINF1.AUD \
+ MINE1.AUD \
+ MINEBLO1.AUD \
+ MINELAY1.AUD \
+ MISSILE1.AUD \
+ MISSILE6.AUD \
+ MISSILE7.AUD \
+ MLAFF1.AUD \
+ MMOVOUT1.AUD \
+ MRESPON1.AUD \
+ MRISE1.AUD \
+ MWRENCH1.AUD \
+ MYEEHAW1.AUD \
+ MYES1.AUD \
+ MYESSIR1.AUD \
+ ONIT1.AUD \
+ PILLBOX1.AUD \
+ PLACBLDG.AUD \
+ RABEEP1.AUD \
+ RADARDN1.AUD \
+ RADARON2.AUD \
+ RAMENU1.AUD \
+ ROKROLL1.AUD \
+ SAFFIRM1.AUD \
+ SANDBAG2.AUD \
+ SCOLDY1.AUD \
+ SCOMND1.AUD \
+ SHKTROP1.AUD \
+ SILENCER.AUD \
+ SINDEED1.AUD \
+ SKING1.AUD \
+ SMOUT1.AUD \
+ SOKAY1.AUD \
+ SONPULSE.AUD \
+ SONWAY1.AUD \
+ SPLASH9.AUD \
+ SQUISHY2.AUD \
+ SUBSHOW1.AUD \
+ SWHAT1.AUD \
+ SYEAH1.AUD \
+ SYESSIR1.AUD \
+ TANDETH1.AUD \
+ TANK5.AUD \
+ TANK6.AUD \
+ TESLA1.AUD \
+ TORPEDO1.AUD \
+ TSLACHG2.AUD \
+ TUFFGUY1.AUD \
+ TURRET1.AUD \
+ WALLKIL2.AUD \
+ YEAH1.AUD \
+ YES1.AUD \
+ YO1.AUD \
+
+# Vehicle responses
+RESPONSE1 = \
+ ACKNO.AUD \
+ AFFIRM1.AUD \
+ AWAIT1.AUD \
+ REPORT1.AUD \
+ VEHIC1.AUD \
+ YESSIR1.AUD \
+
+# Infantry responses
+RESPONSE2 = \
+ ACKNO.AUD \
+ AFFIRM1.AUD \
+ AWAIT1.AUD \
+ NOPROB.AUD \
+ OVEROUT.AUD \
+ READY.AUD \
+ REPORT1.AUD \
+ RITAWAY.AUD \
+ ROGER.AUD \
+ UGOTIT.AUD \
+ YESSIR1.AUD \
+
+#TSCOREFILES = \
+# cps\record.bin \
+# WIN1.AUD \
+# MAP1.AUD \
+
+VARFILES = \
+
+SCOREFILES = \
+ CREDITS.AUD \
+ AWAIT.AUD \
+ BIGF226M.AUD \
+ CRUS226M.AUD \
+ DENSE_R.AUD \
+ FAC1226M.AUD \
+ FAC2226M.AUD \
+ FOGGER1A.AUD \
+ HELL226M.AUD \
+ MUD1A.AUD \
+ RADIO2.AUD \
+ ROLLOUT.AUD \
+ RUN1226M.AUD \
+ SCORE.AUD \
+ SMSH226M.AUD \
+ SNAKE.AUD \
+ TERMINAT.AUD \
+ TREN226M.AUD \
+ TWIN.AUD \
+ VECTOR1A.AUD \
+ WORK226M.AUD \
+ 2ND_HAND.AUD \
+ ARAZIOD.AUD \
+ BACKSTAB.AUD \
+ CHAOS2.AUD \
+ SHUT_IT.AUD \
+ TWINMIX1.AUD \
+ UNDER3.AUD \
+ VR2.AUD \
+ BOG.AUD \
+ FLOAT_V2.AUD \
+ GLOOM.AUD \
+ GRNDWIRE.AUD \
+ RPT.AUD \
+ SEARCH.AUD \
+ TRACTION.AUD \
+ WASTELND.AUD \
+
+SPEECHFILES = \
+ STRCKIL1.AUD \
+ NOPOWR1.AUD \
+ SAVE1.AUD \
+ LOAD1.AUD \
+ 10MINR.AUD \
+ 1MINR.AUD \
+ 1OBJMET1.AUD \
+ 20MINR.AUD \
+ 2MINR.AUD \
+ 2OBJMET1.AUD \
+ 30MINR.AUD \
+ 3MINR.AUD \
+ 3OBJMET1.AUD \
+ 40MINR.AUD \
+ 4MINR.AUD \
+ 5MINR.AUD \
+ AAPPRO1.AUD \
+ AARIVE1.AUD \
+ AARIVE1.AUD \
+ AARRIVE1.AUD \
+ AARRIVN1.AUD \
+ AARRIVS1.AUD \
+ AARRIVW1.AUD \
+ AAVAIL1.AUD \
+ ABLDGIN1.AUD \
+ AFALLEN1.AUD \
+ ALAUNCH1.AUD \
+ APREP1.AUD \
+ AREADY1.AUD \
+ ARMORUP1.AUD \
+ ASELECT1.AUD \
+ ATLNCH1.AUD \
+ ATPREP1.AUD \
+ AUNITL1.AUD \
+ BASEATK1.AUD \
+ BCT1.AUD \
+ BLDGINF1.AUD \
+ BLDGPRG1.AUD \
+ CANCLD1.AUD \
+ CHROCHR1.AUD \
+ CHRORDY1.AUD \
+ CHROYES1.AUD \
+ CMDCNTR1.AUD \
+ CNTLDED1.AUD \
+ COMNDOF1.AUD \
+ COMNDOR1.AUD \
+ CONSCMP1.AUD \
+ CONVLST1.AUD \
+ CONVYAP1.AUD \
+ CREDIT1.AUD \
+ ENMYAPP1.AUD \
+ FIREPO1.AUD \
+ FLARE1.AUD \
+ FLAREE1.AUD \
+ FLAREN1.AUD \
+ FLARES1.AUD \
+ FLAREW1.AUD \
+ IRONCHG1.AUD \
+ IRONRDY1.AUD \
+ KOSYFRE1.AUD \
+ KOSYRES1.AUD \
+ LOPOWER1.AUD \
+ MERCF1.AUD \
+ MERCR1.AUD \
+ MISNLST1.AUD \
+ MISNWON1.AUD \
+ MTIMEIN1.AUD \
+ NAVYLST1.AUD \
+ NEWOPT1.AUD \
+ NOBUILD1.AUD \
+ NODEPLY1.AUD \
+ NOFUNDS1.AUD \
+ NOFUNDS1.AUD \
+ OBJMET1.AUD \
+ OBJNMET1.AUD \
+ OBJNRCH1.AUD \
+ OBJRCH1.AUD \
+ ONHOLD1.AUD \
+ OPTERM1.AUD \
+ PRIBLDG1.AUD \
+ PROGRES1.AUD \
+ PULSE1.AUD \
+ REINFOR1.AUD \
+ REPAIR1.AUD \
+ REPAIR1.AUD \
+ SATLNCH1.AUD \
+ SILOND1.AUD \
+ SLCTTGT1.AUD \
+ SOVEFAL1.AUD \
+ SOVEMP1.AUD \
+ SOVFAPP1.AUD \
+ SOVFORC1.AUD \
+ SOVREIN1.AUD \
+ SPYPLN1.AUD \
+ STRUCAP1.AUD \
+ STRUSLD1.AUD \
+ TANYAF1.AUD \
+ TANYAR1.AUD \
+ TARGFRE1.AUD \
+ TARGRES1.AUD \
+ TIMERGO1.AUD \
+ TIMERNO1.AUD \
+ TRAIN1.AUD \
+ UNITFUL1.AUD \
+ UNITLST1.AUD \
+ UNITRDY1.AUD \
+ UNITREP1.AUD \
+ UNITSLD1.AUD \
+ UNITSPD1.AUD \
+ XPLOPLC1.AUD \
+# ABLDGC1.AUD \
+# SOVBLDG1.AUD \
+# SOVSTRC1.AUD \
+# SOVUNTD1.AUD \
+# AUNITD1.AUD \
+# ASTRUCD1.AUD \
+
+#ALLIESVQ = \
+DUMMYVQ = \
+ AAGUN.VQA \
+ AFTRMATH.VQA \
+ ALLY1.VQA \
+ ALLY10.VQA \
+ ALLY10B.VQA \
+ ALLY11.VQA \
+ ALLY12.VQA \
+ ALLY14.VQA \
+ ALLY2.VQA \
+ ALLY4.VQA \
+ ALLY5.VQA \
+ ALLY6.VQA \
+ ALLY8.VQA \
+ ALLY9.VQA \
+ ALLYEND.VQA \
+ ALLYMORF.VQA \
+ APCESCPE.VQA \
+ ASSESS.VQA \
+ BATTLE.VQA \
+ 1BINOC.VQA \
+ BMAP.VQA \
+ BRDGTILT.VQA \
+ CRONTEST.VQA \
+ CRONFAIL.VQA \
+ DESTROYR.VQA \
+ DUD.VQA \
+ ELEVATOR.VQA \
+ FLARE.VQA \
+ FROZEN.VQA \
+ GRVESTNE.VQA \
+ LANDING.VQA \
+ MASASSLT.VQA \
+ MCV.VQA \
+ MCV_LAND.VQA \
+ MONTPASS.VQA \
+ OILDRUM.VQA \
+ OVERRUN.VQA \
+ PROLOG.VQA \
+ REDINTRO.VQA \
+ SHIPSINK.VQA \
+ SHORBOM1.VQA \
+ SHORBOM2.VQA \
+ SHORBOMB.VQA \
+ SNOWBOMB.VQA \
+ SOVIET1.VQA \
+ SOVTSTAR.VQA \
+ SPY.VQA \
+ TANYA1.VQA \
+ TANYA2.VQA \
+ TOOFAR.VQA \
+ TRINITY.VQA \
+# TRAILER.VQA \
+
+SOVIETVQ = \
+ AAGUN.VQA \
+ CRONFAIL.VQA \
+ AIRFIELD.VQA \
+ ALLY1.VQA \
+ ALLYMORF.VQA \
+ AVERTED.VQA \
+ BEACHEAD.VQA \
+ BMAP.VQA \
+ BOMBRUN.VQA \
+ COUNTDWN.VQA \
+ DOUBLE.VQA \
+ DPTHCHRG.VQA \
+ EXECUTE.VQA \
+ FLARE.VQA \
+ LANDING.VQA \
+ MCVBRDGE.VQA \
+ MIG.VQA \
+ MOVINGIN.VQA \
+ MTNKFACT.VQA \
+ NUKESTOK.VQA \
+ ONTHPRWL.VQA \
+ PERISCOP.VQA \
+ PROLOG.VQA \
+ RADRRAID.VQA \
+ REDINTRO.VQA \
+ SEARCH.VQA \
+ SFROZEN.VQA \
+ SITDUCK.VQA \
+ SLNTSRVC.VQA \
+ SNOWBOMB.VQA \
+ SNSTRAFE.VQA \
+ SOVBATL.VQA \
+ SOVCEMET.VQA \
+ SOVFINAL.VQA \
+ SOVIET1.VQA \
+ SOVIET10.VQA \
+ SOVIET11.VQA \
+ SOVIET12.VQA \
+ SOVIET13.VQA \
+ SOVIET14.VQA \
+ SOVIET2.VQA \
+ SOVIET3.VQA \
+ SOVIET4.VQA \
+ SOVIET5.VQA \
+ SOVIET6.VQA \
+ SOVIET7.VQA \
+ SOVIET8.VQA \
+ SOVIET9.VQA \
+ SOVMCV.VQA \
+ SOVTSTAR.VQA \
+ SPOTTER.VQA \
+ STRAFE.VQA \
+ TAKE_OFF.VQA \
+ TESLA.VQA \
+ V2ROCKET.VQA \
+# TRAILER.VQA \
+
+ALLIESVQ = \
+ SOVIET1.VQA \
+ ALLY1.VQA \
+ V2ROCKET.VQA \
+ BMAP.VQA \
+ SNSTRAFE.VQA \
+ SOVTSTAR.VQA \
+ SOVBATL.VQA \
+ SOVCEMET.VQA \
+ FLARE.VQA \
+ ALLYMORF.VQA \
+ SPY.VQA \
+ FROZEN.VQA \
+ GRVESTNE.VQA \
+ CRONFAIL.VQA \
+ APCESCPE.VQA \
+ EXECUTE.VQA \
+ TOOFAR.VQA \
+ TRINITY.VQA \
+ TESLA.VQA \
+ MASASSLT.VQA \
+ OVERRUN.VQA \
+ PROLOG.VQA \
+ REDINTRO.VQA \
+ AFTRMATH.VQA \
+ PROLOG.VQA \
+ SNOWBOMB.VQA \
+ LANDING.VQA \
+ SFROZEN.VQA \
+ ANTEND.VQA \
+ ANTINTRO.VQA \
+
+
+
+# Files required for hires/Win95 version only
+#
+# This mix file is not cached
+#
+NOCACHEHIRESFILES= \
+ ENGLISH.VQA \
+ $(ALLIESVQ:.VQA=.VQP) \
+ $(SOVIETVQ:.VQA=.VQP) \
+
+
+LINTOBJECTS1 = $(OBJECTS:,=)
+LINTOBJECTS = $(LINTOBJECTS1:.OBJ=.LOB)
+
+# Mixfiles that should reside on the CD-ROM drive.
+CD1MIXFILES = \
+ CONQUER.MIX \
+ EDHI.MIX \
+ EDLO.MIX \
+ GENERAL.MIX \
+ INTERIOR.MIX \
+ MOVIES1.MIX \
+ SCORES.MIX \
+ SNOW.MIX \
+ SOUNDS.MIX \
+ RUSSIAN.MIX \
+ ALLIES.MIX \
+ TEMPERAT.MIX \
+
+
+
+# Mixfiles that should reside on the hard drive.
+LOCALMIXFILES = \
+ EDITOR.MIX \
+ HIRES.MIX \
+ LOCAL.MIX \
+ LORES.MIX \
+ NCHIRES.MIX \
+ SPEECH.MIX \
+
+
+# Mixfiles as they appear on the CD and hard drive.
+PACKFILES= $(.path.cd1)MAIN.MIX EXPAND2.MIX $(.path.cd1)tobreaki\REDALERT.MIX
+
+
+# Ant assets SOME ASSETS ARE HERE FOR OVERRIDING
+EXPANDFILES= \
+ ANT1.SHP \
+ ANT2.SHP \
+ ANT3.SHP \
+ QUEE.SHP \
+ CREDITS.ENG \
+ HILL01.TEM \
+ ANTBITE.AUD \
+ ANTDIE.AUD \
+ ANTDIE.SHP \
+ LAR1.SHP \
+ LAR2.SHP \
+ TITLE.PCX \
+ MISSION.INI \
+ BUZZY1.AUD \
+ STAVCMDR.AUD \
+ STAVCRSE.AUD \
+ STAVYES.AUD \
+ STAVMOV.AUD \
+ CONQUER.ENG \
+ RAMBO1.AUD \
+ RAMBO2.AUD \
+ RAMBO3.AUD \
+ TITLE.CPS \
+ ANTEND.VQP \
+ TUTORIAL.INI \
+ ANTINTRO.VQP \
+ BMAP.VQP \
+
+# Aftermath expansion files
+EXPAND2FILES= \
+ CARR.SHP \
+ CTNK.SHP \
+ DTRK.SHP \
+ MSUB.SHP \
+ QTNK.SHP \
+ TTNK.SHP \
+ STNK.SHP \
+ AFTRMATH.INI \
+ ANT1.SHP \
+ ANT2.SHP \
+ ANT3.SHP \
+ ANTBITE.AUD \
+ ANTDIE.AUD \
+ ANTDIE.SHP \
+ BMAP.VQP \
+ BUZZY1.AUD \
+ CONQUER.ENG \
+ CREDITS.ENG \
+ HILL01.TEM \
+ LAR1.SHP \
+ LAR2.SHP \
+ MISSION.INI \
+ MPLAYER.INI \
+ QUEE.SHP \
+ STAVCMDR.AUD \
+ STAVCRSE.AUD \
+ STAVYES.AUD \
+ STAVMOV.AUD \
+ TITLE.PCX \
+ TITLE.CPS \
+ TUTORIAL.INI \
+
+
+
+#############################################################
+# Rebuilds all the mixfiles.
+packfiles: always $(PACKFILES)
+
+always:
+ copy f:\projects\c&c0\editor\english\*.mix $(.path.mix) /u
+
+
+####################################################################
+# All mixfiles that exist on the CD-ROM are embedded within this mega-mixfile.
+$(.path.cd1)MAIN.MIX: $(CD1MIXFILES)
+ UTILS\MIXFILE -k -I$(.path.mix) &&!
+ $**
+! $(.path.cd1)$&.mix
+
+
+# All mixfiles that exist in the local directory are embedded within this mega-mixfile.
+$(.path.cd1)install\REDALERT.MIX: $(LOCALMIXFILES)
+ UTILS\MIXFILE -k -I$(.path.mix) &&!
+ $**
+! $(.path.cd1)install\$&.mix
+
+
+####################################################################
+# These are the various sub-mixfiles.
+CONQUER.MIX: $(CONQUERFILES) $(CACHEMAP) .\key.ini
+ UTILS\MIXFILE -k -h -I$(.path.cps) &&!
+ $(CONQUERFILES) $(CACHEMAP)
+! $(.path.mix)$&.mix
+
+TEMPERAT.MIX: $(TEMPERATEFILES) .\key.ini
+ UTILS\MIXFILE -h -k -I$(.path.cps) &&!
+ $(TEMPERATEFILES)
+! $(.path.mix)$&.mix
+
+SNOW.MIX: $(SNOWFILES) .\key.ini
+ UTILS\MIXFILE -h -k -I$(.path.cps) &&!
+ $(SNOWFILES)
+! $(.path.mix)$&.mix
+
+INTERIOR.MIX: $(INTERIORFILES) .\key.ini
+ UTILS\MIXFILE -h -k -I$(.path.cps) &&!
+ $(INTERIORFILES)
+! $(.path.mix)$&.mix
+
+GENERAL.MIX: $(GENERALFILES) $(GENERALMAPFILES) $(NETMAPFILES) $(MAPFILES) .\key.ini
+ UTILS\MIXFILE -k -I$(.path.cps) -I$(.path.ini) &&!
+ $(GENERALFILES) $(GENERALMAPFILES) $(NETMAPFILES) $(MAPFILES)
+! $(.path.mix)$&.mix
+
+SCORES.MIX: $(SCOREFILES)
+ UTILS\MIXFILE -k -I$(.path.cps) -I$(.path.ini) &&!
+ $**
+! $(.path.mix)$&.mix
+
+SOUNDS.MIX: $(WAVFILES) $(SFX)
+ UTILS\MIXFILE -h -k -EA60=V00 -EA61=V01 -EA62=V02 -EA63=V03 -I$(.path.aud) &&!
+ $**
+! $(.path.mix)$&.mix
+
+RUSSIAN.MIX: $(RESPONSE1:.AUD=.R00) $(RESPONSE2:.AUD=.R01) $(RESPONSE1:.AUD=.R02) $(RESPONSE2:.AUD=.R03)
+ UTILS\MIXFILE -h -k -I$(.path.aud) &&!
+ $**
+! $(.path.mix)$&.mix
+
+LIMITED.MIX: BLEEP11.AUD
+ UTILS\MIXFILE -h -k -I$(.path.aud) &&!
+ $**
+! $(.path.mix)$&.mix
+
+ALLIES.MIX: $(RESPONSE1:.AUD=.V00) $(RESPONSE2:.AUD=.V01) $(RESPONSE1:.AUD=.V02) $(RESPONSE2:.AUD=.V03)
+ UTILS\MIXFILE -h -k -I$(.path.aud) &&!
+ $**
+! $(.path.mix)$&.mix
+
+MOVIES1.MIX: $(ALLIESVQ)
+ UTILS\MIXFILE -k -I$(.path.vqa) &&!
+ $**
+! $(.path.mix)$&.mix
+
+
+
+
+
+NCHIRES.MIX: $(NOCACHEHIRESFILES:.SHP=.HI)
+ UTILS\MIXFILE -k -I$(.path.vqp) -I$(.path.cps) &&!
+ $(NOCACHEHIRESFILES)
+! $(.path.mix)$&.mix
+
+LOCAL.MIX: $(LOCALFILES) .\key.ini
+ UTILS\MIXFILE -h -k -E.A6=.AUD -I$(.path.ini) -I$(.path.txt) -I$(.path.cps) &&!
+ $(LOCALFILES)
+! $(.path.mix)$&.mix
+
+LORES.MIX: $(LOHILORES) .\key.ini
+ UTILS\MIXFILE -h -k -E.LOW=.SHP -E.LNT=.FNT -I$(.path.cps) &&!
+ $(LOHILORES)
+! $(.path.mix)$&.mix
+
+HIRES.MIX: $(HIRESFILES:.SHP=.HI) $(HIHILORES) .\key.ini
+ UTILS\MIXFILE -h -k -E.HI=.SHP -E.HNT=.FNT -I$(.path.cps) &&!
+ $(HIRESFILES:.SHP=.HI) $(HIHILORES)
+! $(.path.mix)$&.mix
+
+SPEECH.MIX: $(SPEECHFILES)
+ UTILS\MIXFILE -k -I$(.path.aud) &&!
+ $**
+! $(.path.mix)$&.mix
+
+
+EXPAND.MIX: $(EXPANDFILES)
+ UTILS\MIXFILE -k -I$(.path.mix) &&!
+ $**
+! $(.path.mix)$&.mix
+
+EXPAND2.MIX: $(EXPAND2FILES)
+ UTILS\MIXFILE -k -I$(.path.mix) &&!
+ $**
+! $(.path.mix)$&.mix
+
+#############################################################
+# Special rule to create the mouse shape (which must be a shape file)
+mouse.hi: $(.path.anm)hires\mouse.anm
+ -utils\makeshps $(.path.lbm)palettes\temperat.lbm &&!
+ &$(.path.anm)hires\mouse.anm;
+ end;
+! $(.path.hi)$&.hi $(SHAPEBUFFSIZE)
+
+# Special rule to create the mouse shape (which must be a shape file)
+mouse.low: $(.path.anm)lores\mouse.anm
+ -utils\makeshps $(.path.lbm)palettes\temperat.lbm &&!
+ &$(.path.anm)lores\mouse.anm;
+ end;
+! $(.path.low)$&.low $(SHAPEBUFFSIZE)
+
+
+#############################################################
+# Special build rule for radar animations so that they won't.
+#
+NATORADR.HI: $(.path.anm)hires\NATORADR.ANM
+ utils\newkeyf $** $(.path.hi)$&.hi -l -k
+
+USSRRADR.HI: $(.path.anm)hires\USSRRADR.ANM
+ utils\newkeyf $** $(.path.hi)$&.hi -l -k
+
+NATORADR.LOW: $(.path.anm)lores\NATORADR.ANM
+ utils\newkeyf $** $(.path.low)$&.low -l -k
+
+USSRRADR.LOW: $(.path.anm)lores\USSRRADR.ANM
+ utils\newkeyf $** $(.path.low)$&.low -l -k
+
+
+#############################################################
+# Debug text file creation.
+debug.eng: debug.txt
+ utils\textmake -b1000 eng\$&.txt $(.path.eng)$&.eng $&.h
+
+
diff --git a/CODE/BFIOFILE.CPP b/CODE/BFIOFILE.CPP
new file mode 100644
index 0000000..e5d840f
--- /dev/null
+++ b/CODE/BFIOFILE.CPP
@@ -0,0 +1,987 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/BFIOFILE.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Westwood Library *
+ * *
+ * File Name : RAMFILE.CPP *
+ * *
+ * Programmer : David R. Dettmer *
+ * *
+ * Start Date : November 10, 1995 *
+ * *
+ * Last Update : November 10, 1995 [DRD] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * BufferIOFileClass::BufferIOFileClass -- Filename based constructor for a file object. *
+ * BufferIOFileClass::BufferIOFileClass -- default constructor for a file object. *
+ * BufferIOFileClass::Cache -- Load part or all of a file data into RAM. *
+ * BufferIOFileClass::Close -- Perform a closure of the file. *
+ * BufferIOFileClass::Commit -- Writes the cache to the file if it has changed. *
+ * BufferIOFileClass::Free -- Frees the allocated buffer. *
+ * BufferIOFileClass::Is_Available -- Checks for existence of file cached or on disk. *
+ * BufferIOFileClass::Is_Open -- Determines if the file is open. *
+ * BufferIOFileClass::Open -- Assigns name and opens file in one operation. *
+ * BufferIOFileClass::Open -- Opens the file object with the rights specified. *
+ * BufferIOFileClass::Read -- Reads data from the file cache. *
+ * BufferIOFileClass::Seek -- Moves the current file pointer in the file. *
+ * BufferIOFileClass::Set_Name -- Checks for name changed for a cached file. *
+ * BufferIOFileClass::Size -- Determines size of file (in bytes). *
+ * BufferIOFileClass::Write -- Writes data to the file cache. *
+ * BufferIOFileClass::~BufferIOFileClass -- Destructor for the file object. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#include "bfiofile.h"
+#include
+
+
+/***********************************************************************************************
+ * BufferIOFileClass::BufferIOFileClass -- Filename based constructor for a file object. *
+ * *
+ * This constructor is called when a file object is created with a supplied filename, but *
+ * not opened at the same time. In this case, an assumption is made that the supplied *
+ * filename is a constant string. A duplicate of the filename string is not created since *
+ * it would be wasteful in that case. *
+ * *
+ * INPUT: filename -- The filename to assign to this file object. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 11/10/1995 DRD : Created. *
+ *=============================================================================================*/
+BufferIOFileClass::BufferIOFileClass(char const * filename) :
+ IsAllocated(false),
+ IsOpen(false),
+ IsDiskOpen(false),
+ IsCached(false),
+ IsChanged(false),
+ UseBuffer(false),
+ BufferRights(0),
+ Buffer(0),
+ BufferSize(0),
+ BufferPos(0),
+ BufferFilePos(0),
+ BufferChangeBeg(-1),
+ BufferChangeEnd(-1),
+ FileSize(0),
+ FilePos(0),
+ TrueFileStart(0)
+{
+ BufferIOFileClass::Set_Name(filename);
+}
+
+
+/***********************************************************************************************
+ * BufferIOFileClass::BufferIOFileClass -- default constructor for a file object. *
+ * *
+ * This is the default constructor for a file object. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 11/10/1995 DRD : Created. *
+ *=============================================================================================*/
+BufferIOFileClass::BufferIOFileClass(void) :
+ IsAllocated(false),
+ IsOpen(false),
+ IsDiskOpen(false),
+ IsCached(false),
+ IsChanged(false),
+ UseBuffer(false),
+ BufferRights(0),
+ Buffer(0),
+ BufferSize(0),
+ BufferPos(0),
+ BufferFilePos(0),
+ BufferChangeBeg(-1),
+ BufferChangeEnd(-1),
+ FileSize(0),
+ FilePos(0),
+ TrueFileStart(0)
+{
+}
+
+
+/***********************************************************************************************
+ * BufferIOFileClass::~BufferIOFileClass -- Destructor for the file object. *
+ * *
+ * This destructor will free all memory allocated thru using Cache routines. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 11/10/1995 DRD : Created. *
+ *=============================================================================================*/
+BufferIOFileClass::~BufferIOFileClass(void)
+{
+ Free();
+}
+
+
+/***********************************************************************************************
+ * BufferIOFileClass::Cache -- Load part or all of a file data into RAM. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: bool; Was the file load successful? It could fail if there wasn't enough room *
+ * to allocate the raw data block. *
+ * *
+ * WARNINGS: This routine goes to disk for a potentially very long time. *
+ * *
+ * HISTORY: *
+ * 11/10/1995 DRD : Created. *
+ *=============================================================================================*/
+bool BufferIOFileClass::Cache( long size, void * ptr )
+{
+ if (Buffer) {
+ //
+ // if trying to cache again with size or ptr fail
+ //
+ if (size || ptr) {
+ return( false );
+ } else {
+ return( true );
+ }
+ }
+
+ if ( Is_Available() ) {
+ FileSize = Size();
+ } else {
+ FileSize = 0;
+ }
+
+ if (size) {
+ //
+ // minimum buffer size for performance
+ //
+ if (size < MINIMUM_BUFFER_SIZE) {
+ size = MINIMUM_BUFFER_SIZE;
+
+ /*
+ ** Specifying a size smaller than the minimum is an error
+ ** IF a buffer pointer was also specified. In such a case the
+ ** system cannot use the buffer.
+ */
+ if (ptr) {
+ Error(EINVAL);
+ }
+ }
+
+ BufferSize = size;
+ } else {
+ BufferSize = FileSize;
+ }
+
+ //
+ // if size == 0 and a ptr to a buffer is specified then that is invalid.
+ // if the BufferSize is 0 then this must be a new file and no size was
+ // specified so exit.
+ //
+ if ( (size == 0 && ptr) || !BufferSize) {
+ return( false );
+ }
+
+ if (ptr) {
+ Buffer = ptr;
+ } else {
+ Buffer = new char [BufferSize];
+ }
+
+ if (Buffer) {
+ IsAllocated = true;
+ IsDiskOpen = false;
+ BufferPos = 0;
+ BufferFilePos = 0;
+ BufferChangeBeg = -1;
+ BufferChangeEnd = -1;
+ FilePos = 0;
+ TrueFileStart = 0;
+
+ //
+ // the file was checked for availability then set the FileSize
+ //
+ if (FileSize) {
+ long readsize;
+ int opened = false;
+ long prevpos = 0;
+
+
+ if (FileSize <= BufferSize) {
+ readsize = FileSize;
+ } else {
+ readsize = BufferSize;
+ }
+
+ if ( Is_Open() ) {
+ //
+ // get previous file position
+ //
+ prevpos = Seek(0);
+
+ //
+ // get true file position
+ //
+ if ( RawFileClass::Is_Open() ) {
+ TrueFileStart = RawFileClass::Seek(0);
+ } else {
+ TrueFileStart = prevpos;
+ }
+
+ if (FileSize <= BufferSize) {
+ //
+ // if previous position is non-zero seek to the beginning
+ //
+ if (prevpos) {
+ Seek(0, SEEK_SET);
+ }
+
+ //
+ // set the buffer position for future reads/writes
+ //
+ BufferPos = prevpos;
+ } else {
+ BufferFilePos = prevpos;
+ }
+
+ FilePos = prevpos;
+ } else {
+ if ( Open() ) {
+ TrueFileStart = RawFileClass::Seek(0);
+ opened = true;
+ }
+ }
+
+ long actual = Read(Buffer, readsize);
+
+ if (actual != readsize) {
+ Error(EIO);
+ }
+
+ if (opened) {
+ Close();
+ } else {
+ //
+ // seek to the previous position in the file
+ //
+ Seek(prevpos, SEEK_SET);
+ }
+
+ IsCached = true;
+ }
+
+ UseBuffer = true;
+ return(true);
+ }
+
+ Error(ENOMEM);
+
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * BufferIOFileClass::Free -- Frees the allocated buffer. *
+ * *
+ * This routine will free the buffer. By using this in conjunction with the *
+ * Cache() function, one can maintain tight control of memory usage. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 11/10/1995 DRD : Created. *
+ *=============================================================================================*/
+void BufferIOFileClass::Free(void)
+{
+ if (Buffer) {
+ if (IsAllocated) {
+ delete [] Buffer;
+ IsAllocated = false;
+ }
+
+ Buffer = 0;
+ }
+
+ BufferSize = 0;
+ IsOpen = false;
+ IsCached = false;
+ IsChanged = false;
+ UseBuffer = false;
+}
+
+
+/***********************************************************************************************
+ * BufferIOFileClass::Commit -- Writes the cache to the file if it has changed. *
+ * *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: false, did not need to write the buffer. *
+ * true, wrote the buffer. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 11/15/1995 DRD : Created. *
+ *=============================================================================================*/
+bool BufferIOFileClass::Commit( void )
+{
+ long size;
+
+
+ if (UseBuffer) {
+ if (IsChanged) {
+ size = BufferChangeEnd - BufferChangeBeg;
+
+ if (IsDiskOpen) {
+ RawFileClass::Seek( TrueFileStart + BufferFilePos +
+ BufferChangeBeg, SEEK_SET );
+ RawFileClass::Write( Buffer, size );
+ RawFileClass::Seek( TrueFileStart + FilePos, SEEK_SET );
+ } else {
+ RawFileClass::Open();
+ RawFileClass::Seek( TrueFileStart + BufferFilePos +
+ BufferChangeBeg, SEEK_SET );
+ RawFileClass::Write( Buffer, size );
+ RawFileClass::Close();
+ }
+
+ IsChanged = false;
+ return( true );
+ } else {
+ return( false );
+ }
+ } else {
+ return( false );
+ }
+}
+
+
+/***********************************************************************************************
+ * BufferIOFileClass::Set_Name -- Checks for name changed for a cached file. *
+ * *
+ * Checks for a previous filename and that it is cached. If so, then check the *
+ * new filename against the old. If they are the same then return that filename. *
+ * Otherwise, the file object's name is set with just the raw filename as passed *
+ * to this routine. *
+ * *
+ * INPUT: filename -- Pointer to the filename to set as the name of this file object. *
+ * *
+ * OUTPUT: Returns a pointer to the final and complete filename of this file object. This *
+ * may have a path attached to the file. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 11/15/1995 DRD : Created. *
+ *=============================================================================================*/
+char const * BufferIOFileClass::Set_Name(char const * filename)
+{
+ if ( File_Name() && UseBuffer) {
+ if ( strcmp(filename, File_Name() ) == 0) {
+ return( File_Name() );
+ } else {
+ Commit();
+ IsCached = false;
+ }
+ }
+
+ RawFileClass::Set_Name(filename);
+ return( File_Name() );
+}
+
+
+/***********************************************************************************************
+ * BufferIOFileClass::Is_Available -- Checks for existence of file cached or on disk. *
+ * *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: bool; Is the file available for opening? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 11/16/1995 DRD : Created. *
+ *=============================================================================================*/
+int BufferIOFileClass::Is_Available(int )
+{
+ if (UseBuffer) {
+ return(true);
+ }
+
+ return( RawFileClass::Is_Available() );
+}
+
+
+/***********************************************************************************************
+ * BufferIOFileClass::Is_Open -- Determines if the file is open. *
+ * *
+ * If part or all of the file is cached, then return that it is opened. A closed file *
+ * doesn't have a valid pointer. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: bool; Is the file open? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 11/14/1995 DRD : Created. *
+ *=============================================================================================*/
+int BufferIOFileClass::Is_Open(void) const
+{
+ if (IsOpen && UseBuffer) {
+ return( true );
+ }
+
+ return( RawFileClass::Is_Open() );
+}
+
+
+/***********************************************************************************************
+ * BufferIOFileClass::Open -- Assigns name and opens file in one operation. *
+ * *
+ * This routine will assign the specified filename to the file object and open it at the *
+ * same time. If the file object was already open, then it will be closed first. If the *
+ * file object was previously assigned a filename, then it will be replaced with the new *
+ * name. Typically, this routine is used when an anonymous file object has been crated and *
+ * now it needs to be assigned a name and opened. *
+ * *
+ * INPUT: filename -- The filename to assign to this file object. *
+ * *
+ * rights -- The open file access rights to use. *
+ * *
+ * OUTPUT: bool; Was the file opened? The return value of this is moot, since the open file *
+ * is designed to never return unless it succeeded. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 11/14/1995 DRD : Created. *
+ *=============================================================================================*/
+int BufferIOFileClass::Open(char const * filename, int rights)
+{
+ Set_Name(filename);
+ return( BufferIOFileClass::Open( rights ) );
+}
+
+
+/***********************************************************************************************
+ * BufferIOFileClass::Open -- Opens the file object with the rights specified. *
+ * *
+ * This routine is used to open the specified file object with the access rights indicated. *
+ * This only works if the file has already been assigned a filename. It is guaranteed, by *
+ * the error handler, that this routine will always return with success. *
+ * *
+ * INPUT: rights -- The file access rights to use when opening this file. This is a *
+ * combination of READ and/or WRITE bit flags. *
+ * *
+ * OUTPUT: bool; Was the file opened successfully? This will always return true by reason of *
+ * the error handler. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 11/14/1995 DRD : Created. *
+ *=============================================================================================*/
+int BufferIOFileClass::Open(int rights)
+{
+ BufferIOFileClass::Close();
+
+ if (UseBuffer) {
+
+ BufferRights = rights; // save rights requested for checks later
+
+ if (rights != READ ||
+ (rights == READ && FileSize > BufferSize) ) {
+
+ if (rights == WRITE) {
+ RawFileClass::Open( rights );
+ RawFileClass::Close();
+ rights = READ | WRITE;
+ TrueFileStart = 0; // now writing to single file
+ }
+
+ if (TrueFileStart) {
+ UseBuffer = false;
+ Open( rights );
+ UseBuffer = true;
+ } else {
+ RawFileClass::Open( rights );
+ }
+
+ IsDiskOpen = true;
+
+ if (BufferRights == WRITE) {
+ FileSize = 0;
+ }
+
+ } else {
+ IsDiskOpen = false;
+ }
+
+ BufferPos = 0;
+ BufferFilePos = 0;
+ BufferChangeBeg = -1;
+ BufferChangeEnd = -1;
+ FilePos = 0;
+ IsOpen = true;
+ } else {
+ RawFileClass::Open( rights );
+ }
+
+ return( true );
+}
+
+
+/***********************************************************************************************
+ * BufferIOFileClass::Write -- Writes data to the file cache. *
+ * *
+ * *
+ * INPUT: buffer -- Pointer to the buffer that holds the data to be written. *
+ * *
+ * size -- The number of bytes to write. *
+ * *
+ * OUTPUT: Returns the number of bytes actually written. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 11/15/1995 DRD : Created. *
+ *=============================================================================================*/
+long BufferIOFileClass::Write(void const * buffer, long size)
+{
+ int opened = false;
+
+ if ( !Is_Open() ) {
+ if (!Open(WRITE)) {
+ return(0);
+ }
+ TrueFileStart = RawFileClass::Seek(0);
+ opened = true;
+ }
+
+ if (UseBuffer) {
+ long sizewritten = 0;
+
+ if (BufferRights != READ) {
+ while (size) {
+ long sizetowrite;
+
+ if (size >= (BufferSize - BufferPos) ) {
+ sizetowrite = (BufferSize - BufferPos);
+ } else {
+ sizetowrite = size;
+ }
+
+ if (sizetowrite != BufferSize) {
+
+ if ( !IsCached ) {
+ long readsize;
+
+ if (FileSize < BufferSize) {
+ readsize = FileSize;
+ BufferFilePos = 0;
+ } else {
+ readsize = BufferSize;
+ BufferFilePos = FilePos;
+ }
+
+ if (TrueFileStart) {
+ UseBuffer = false;
+ Seek( FilePos, SEEK_SET );
+ Read( Buffer, BufferSize );
+ Seek( FilePos, SEEK_SET );
+ UseBuffer = true;
+ } else {
+ RawFileClass::Seek( BufferFilePos, SEEK_SET );
+ RawFileClass::Read( Buffer, readsize );
+ }
+
+ BufferPos = 0;
+ BufferChangeBeg = -1;
+ BufferChangeEnd = -1;
+
+ IsCached = true;
+ }
+ }
+
+ memmove((char *)Buffer + BufferPos, (char *)buffer + sizewritten, sizetowrite);
+
+ IsChanged = true;
+ sizewritten += sizetowrite;
+ size -= sizetowrite;
+
+ if (BufferChangeBeg == -1) {
+ BufferChangeBeg = BufferPos;
+ BufferChangeEnd = BufferPos;
+ } else {
+ if (BufferChangeBeg > BufferPos) {
+ BufferChangeBeg = BufferPos;
+ }
+ }
+
+ BufferPos += sizetowrite;
+
+ if (BufferChangeEnd < BufferPos) {
+ BufferChangeEnd = BufferPos;
+ }
+
+ FilePos = BufferFilePos + BufferPos;
+
+ if (FileSize < FilePos) {
+ FileSize = FilePos;
+ }
+
+ //
+ // end of buffer reached?
+ //
+ if (BufferPos == BufferSize) {
+ Commit();
+
+ BufferPos = 0;
+ BufferFilePos = FilePos;
+ BufferChangeBeg = -1;
+ BufferChangeEnd = -1;
+
+ if (size && FileSize > FilePos) {
+ if (TrueFileStart) {
+ UseBuffer = false;
+ Seek( FilePos, SEEK_SET );
+ Read( Buffer, BufferSize );
+ Seek( FilePos, SEEK_SET );
+ UseBuffer = true;
+ } else {
+ RawFileClass::Seek( FilePos, SEEK_SET );
+ RawFileClass::Read( Buffer, BufferSize );
+ }
+ } else {
+ IsCached = false;
+ }
+ }
+ }
+ } else {
+ Error(EACCES);
+ }
+
+ size = sizewritten;
+ } else {
+ size = RawFileClass::Write(buffer, size);
+ }
+
+ if (opened) {
+ Close();
+ }
+
+ return( size );
+}
+
+
+/***********************************************************************************************
+ * BufferIOFileClass::Read -- Reads data from the file cache. *
+ * *
+ * *
+ * INPUT: buffer -- Pointer to the buffer to place the read data. *
+ * *
+ * size -- The number of bytes to read. *
+ * *
+ * OUTPUT: Returns the actual number of bytes read (this could be less than requested). *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 11/15/1995 DRD : Created. *
+ *=============================================================================================*/
+long BufferIOFileClass::Read(void * buffer, long size)
+{
+ int opened = false;
+
+ if ( !Is_Open() ) {
+ if ( Open() ) {
+ TrueFileStart = RawFileClass::Seek(0);
+ opened = true;
+ }
+ }
+
+ if (UseBuffer) {
+ long sizeread = 0;
+
+ if (BufferRights != WRITE) {
+ while (size) {
+ long sizetoread;
+
+ if (size >= (BufferSize - BufferPos) ) {
+ sizetoread = (BufferSize - BufferPos);
+ } else {
+ sizetoread = size;
+ }
+
+ if ( !IsCached ) {
+ long readsize;
+
+ if (FileSize < BufferSize) {
+ readsize = FileSize;
+ BufferFilePos = 0;
+ } else {
+ readsize = BufferSize;
+ BufferFilePos = FilePos;
+ }
+
+ if (TrueFileStart) {
+ UseBuffer = false;
+ Seek( FilePos, SEEK_SET );
+ Read( Buffer, BufferSize );
+ Seek( FilePos, SEEK_SET );
+ UseBuffer = true;
+ } else {
+ RawFileClass::Seek( BufferFilePos, SEEK_SET );
+ RawFileClass::Read( Buffer, readsize );
+ }
+
+ BufferPos = 0;
+ BufferChangeBeg = -1;
+ BufferChangeEnd = -1;
+
+ IsCached = true;
+ }
+
+ memmove((char *)buffer + sizeread, (char *)Buffer + BufferPos, sizetoread);
+
+ sizeread += sizetoread;
+ size -= sizetoread;
+ BufferPos += sizetoread;
+ FilePos = BufferFilePos + BufferPos;
+
+ //
+ // end of buffer reached?
+ //
+ if (BufferPos == BufferSize) {
+ Commit();
+
+ BufferPos = 0;
+ BufferFilePos = FilePos;
+ BufferChangeBeg = -1;
+ BufferChangeEnd = -1;
+
+ if (size && FileSize > FilePos) {
+ if (TrueFileStart) {
+ UseBuffer = false;
+ Seek( FilePos, SEEK_SET );
+ Read( Buffer, BufferSize );
+ Seek( FilePos, SEEK_SET );
+ UseBuffer = true;
+ } else {
+ RawFileClass::Seek( FilePos, SEEK_SET );
+ RawFileClass::Read( Buffer, BufferSize );
+ }
+ } else {
+ IsCached = false;
+ }
+ }
+ }
+ } else {
+ Error(EACCES);
+ }
+
+ size = sizeread;
+ } else {
+ size = RawFileClass::Read(buffer, size);
+ }
+
+ if (opened) {
+ Close();
+ }
+
+ return( size );
+}
+
+
+/***********************************************************************************************
+ * BufferIOFileClass::Seek -- Moves the current file pointer in the file. *
+ * *
+ * This routine will change the current file pointer to the position specified. It follows *
+ * the same rules the a normal Seek() does, but if the file is part of the mixfile system, *
+ * then only the position value needs to be updated. *
+ * *
+ * INPUT: pos -- The position to move the file to relative to the position indicated *
+ * by the "dir" parameter. *
+ * *
+ * dir -- The direction to affect the position change against. This can be *
+ * either SEEK_CUR, SEEK_END, or SEEK_SET. *
+ * *
+ * OUTPUT: Returns with the position of the new location. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 11/15/1995 DRD : Created. *
+ *=============================================================================================*/
+long BufferIOFileClass::Seek(long pos, int dir)
+{
+ if (UseBuffer) {
+ bool adjusted = false;
+
+ switch (dir) {
+ case SEEK_END:
+ FilePos = FileSize;
+ break;
+
+ case SEEK_SET:
+ FilePos = 0;
+ break;
+
+ case SEEK_CUR:
+ default:
+ break;
+ }
+
+ if (TrueFileStart) {
+ if (pos >= TrueFileStart) {
+ pos -= TrueFileStart;
+ adjusted = true;
+ }
+ }
+
+ FilePos += pos;
+
+ if (FilePos < 0) {
+ FilePos = 0;
+ }
+
+ if (FilePos > FileSize ) {
+ FilePos = FileSize;
+ }
+
+ if (FileSize <= BufferSize) {
+ BufferPos = FilePos;
+ } else {
+ if (FilePos >= BufferFilePos &&
+ FilePos < (BufferFilePos + BufferSize) ) {
+ BufferPos = FilePos - BufferFilePos;
+ } else {
+ Commit();
+// check!!
+ if (TrueFileStart) {
+ UseBuffer = false;
+ Seek(FilePos, SEEK_SET);
+ UseBuffer = true;
+ } else {
+ RawFileClass::Seek(FilePos, SEEK_SET);
+ }
+
+ IsCached = false;
+ }
+ }
+
+ if (TrueFileStart && adjusted) {
+ return( FilePos + TrueFileStart );
+ }
+
+ return( FilePos );
+ }
+
+ return( RawFileClass::Seek(pos, dir) );
+}
+
+
+/***********************************************************************************************
+ * BufferIOFileClass::Size -- Determines size of file (in bytes). *
+ * *
+ * If part or all of the file is cached, then the size of the file is already *
+ * determined and available. Otherwise, go to the low level system to find the file *
+ * size. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the number of bytes in the file. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 11/14/1995 DRD : Created. *
+ *=============================================================================================*/
+long BufferIOFileClass::Size(void)
+{
+ if (IsOpen && UseBuffer) {
+ return( FileSize );
+ }
+
+ return( RawFileClass::Size() );
+}
+
+
+/***********************************************************************************************
+ * BufferIOFileClass::Close -- Perform a closure of the file. *
+ * *
+ * Call Commit() to write the buffer if the file is cached and the buffer has changed, *
+ * then call lower level Close(). *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 11/14/1995 DRD : Created. *
+ *=============================================================================================*/
+void BufferIOFileClass::Close(void)
+{
+ if (UseBuffer) {
+ Commit();
+
+ if (IsDiskOpen) {
+
+ if (TrueFileStart) {
+ UseBuffer = false;
+ Close();
+ UseBuffer = true;
+ } else {
+ RawFileClass::Close();
+ }
+
+ IsDiskOpen = false;
+ }
+
+ IsOpen = false;
+ } else {
+ RawFileClass::Close();
+ }
+}
+
diff --git a/CODE/BFIOFILE.H b/CODE/BFIOFILE.H
new file mode 100644
index 0000000..8244650
--- /dev/null
+++ b/CODE/BFIOFILE.H
@@ -0,0 +1,95 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/BFIOFILE.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Westwood Library *
+ * *
+ * File Name : BFIOFILE.H *
+ * *
+ * Programmer : David R. Dettmer *
+ * *
+ * Start Date : November 10, 1995 *
+ * *
+ * Last Update : November 10, 1995 [DRD] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifndef BFIOFILE_H
+#define BFIOFILE_H
+
+#include "rawfile.h"
+
+/*
+** This derivation of the raw file class handles buffering the input/output in order to
+** achieve greater speed. The buffering is not active by default. It must be activated
+** by setting the appropriate buffer through the Cache() function.
+*/
+class BufferIOFileClass : public RawFileClass
+{
+ public:
+
+ BufferIOFileClass(char const *filename);
+ BufferIOFileClass(void);
+ virtual ~BufferIOFileClass(void);
+
+ bool Cache( long size=0, void *ptr=NULL );
+ void Free( void );
+ bool Commit( void );
+ virtual char const * Set_Name(char const *filename);
+ virtual int Is_Available(int forced=false);
+ virtual int Is_Open(void) const;
+ virtual int Open(char const *filename, int rights=READ);
+ virtual int Open(int rights=READ);
+ virtual long Read(void *buffer, long size);
+ virtual long Seek(long pos, int dir=SEEK_CUR);
+ virtual long Size(void);
+ virtual long Write(void const *buffer, long size);
+ virtual void Close(void);
+
+ enum {MINIMUM_BUFFER_SIZE=1024};
+
+ private:
+
+ unsigned IsAllocated:1;
+ unsigned IsOpen:1;
+ unsigned IsDiskOpen:1;
+ unsigned IsCached:1;
+ unsigned IsChanged:1;
+ unsigned UseBuffer:1;
+
+ int BufferRights;
+
+ void *Buffer;
+
+ long BufferSize;
+ long BufferPos;
+ long BufferFilePos;
+ long BufferChangeBeg;
+ long BufferChangeEnd;
+ long FileSize;
+ long FilePos;
+ long TrueFileStart;
+};
+
+#endif
diff --git a/CODE/BIGCHECK.CPP b/CODE/BIGCHECK.CPP
new file mode 100644
index 0000000..673cb8a
--- /dev/null
+++ b/CODE/BIGCHECK.CPP
@@ -0,0 +1,84 @@
+/*
+** Command & Conquer Red Alert(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 .
+*/
+
+// BigCheck.cpp
+// ajw 9/14/98
+
+#ifdef WOLAPI_INTEGRATION
+
+#include "function.h"
+#include "bigcheck.h"
+
+//***********************************************************************************************
+int BigCheckBoxClass::Draw_Me( int forced )
+{
+ if (ToggleClass::Draw_Me(forced))
+ {
+ Hide_Mouse();
+
+ if( !IsOn )
+ {
+ if( !IsDisabled )
+ CC_Draw_Shape( MFCD::Retrieve( "bigcheck.shp" ), 0, X, Y, WINDOW_MAIN, SHAPE_NORMAL );
+ else
+ CC_Draw_Shape( MFCD::Retrieve( "bigcheck.shp" ), 2, X, Y, WINDOW_MAIN, SHAPE_NORMAL );
+ }
+ else
+ {
+ if( !IsDisabled )
+ CC_Draw_Shape( MFCD::Retrieve( "bigcheck.shp" ), 1, X, Y, WINDOW_MAIN, SHAPE_NORMAL );
+ else
+ CC_Draw_Shape( MFCD::Retrieve( "bigcheck.shp" ), 3, X, Y, WINDOW_MAIN, SHAPE_NORMAL );
+ }
+
+ TextPrintType flags = TextFlags;
+
+ RemapControlType* pScheme;
+
+// if( !IsDisabled )
+ pScheme = GadgetClass::Get_Color_Scheme();
+// else
+// {
+// pScheme = &GreyScheme;
+// flags = flags | TPF_MEDIUM_COLOR;
+// }
+
+ Conquer_Clip_Text_Print( szCaption, X + BIGCHECK_OFFSETX, Y + BIGCHECK_OFFSETY, pScheme, TBLACK, flags, Width, 0 );
+
+ Show_Mouse();
+ return true;
+ }
+ return false;
+}
+
+//***********************************************************************************************
+int BigCheckBoxClass::Action(unsigned flags, KeyNumType & key)
+{
+/* if( flags & LEFTPRESS )
+ {
+ if (IsOn) {
+ Turn_Off();
+ } else {
+ Turn_On();
+ }
+ }
+*/
+ return(ToggleClass::Action(flags, key));
+}
+
+#endif
diff --git a/CODE/BIGCHECK.H b/CODE/BIGCHECK.H
new file mode 100644
index 0000000..308b580
--- /dev/null
+++ b/CODE/BIGCHECK.H
@@ -0,0 +1,74 @@
+/*
+** Command & Conquer Red Alert(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 .
+*/
+
+// Bigcheck.h
+// ajw 9/14/98
+
+#ifdef WOLAPI_INTEGRATION
+
+#ifndef BIGCHECKBOX_H
+#define BIGCHECKBOX_H
+
+#include "toggle.h"
+
+#define BIGCHECK_OFFSETX 20
+#define BIGCHECK_OFFSETY 0
+
+//***********************************************************************************************
+class BigCheckBoxClass : public ToggleClass
+{
+public:
+ BigCheckBoxClass( unsigned id, int x, int y, int w, int h, const char* szCaptionIn, TextPrintType TextFlags,
+ bool bInitiallyChecked = false ) :
+ ToggleClass( id, x, y, w, h ),
+ TextFlags( TextFlags )
+ {
+ szCaption = new char[ strlen( szCaptionIn ) + 1 ];
+ strcpy( szCaption, szCaptionIn );
+ if( bInitiallyChecked )
+ Turn_On();
+ IsToggleType = 1;
+ }
+ virtual ~BigCheckBoxClass()
+ {
+ delete [] szCaption;
+ }
+
+ virtual int Draw_Me(int forced=false);
+ virtual int Action(unsigned flags, KeyNumType & key);
+
+ bool Toggle()
+ {
+ if( IsOn )
+ {
+ Turn_Off();
+ return false;
+ }
+ Turn_On();
+ return true;
+ }
+
+protected:
+ TextPrintType TextFlags;
+ char* szCaption;
+
+};
+
+#endif
+
+#endif
diff --git a/CODE/BLOWFISH.CPP b/CODE/BLOWFISH.CPP
new file mode 100644
index 0000000..60e2ded
--- /dev/null
+++ b/CODE/BLOWFISH.CPP
@@ -0,0 +1,598 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/BLOWFISH.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : BLOWFISH.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 04/14/96 *
+ * *
+ * Last Update : July 8, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * BlowfishEngine::Decrypt -- Decrypts data using blowfish algorithm. *
+ * BlowfishEngine::Encrypt -- Encrypt an arbitrary block of data. *
+ * BlowfishEngine::Process_Block -- Process a block of data using Blowfish algorithm. *
+ * BlowfishEngine::Sub_Key_Encrypt -- Encrypts a block for use in S-Box processing. *
+ * BlowfishEngine::Submit_Key -- Submit a key that will allow data processing. *
+ * BlowfishEngine::~BlowfishEngine -- Destructor for the Blowfish engine. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#include "blowfish.h"
+#include
+#include
+
+
+/*
+** Byte order controlled long integer. This integer is constructed
+** so that character 0 (C0) is the most significant byte of the
+** integer. This is biased toward big endian architecture, but that
+** just happens to be how the Blowfish algorithm was designed.
+*/
+typedef union {
+ unsigned long Long;
+ struct {
+ unsigned char C3;
+ unsigned char C2;
+ unsigned char C1;
+ unsigned char C0;
+ } Char;
+} Int;
+
+
+/***********************************************************************************************
+ * BlowfishEngine::~BlowfishEngine -- Destructor for the Blowfish engine. *
+ * *
+ * This destructor will clear out the s-box tables so that even if the memory for the *
+ * class remains, it will contain no compromising data. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/08/1996 JLB : Created. *
+ *=============================================================================================*/
+BlowfishEngine::~BlowfishEngine(void)
+{
+ if (IsKeyed) {
+ Submit_Key(NULL, 0);
+ }
+}
+
+
+/***********************************************************************************************
+ * BlowfishEngine::Submit_Key -- Submit a key that will allow data processing. *
+ * *
+ * This routine must be called before any data can be encrypted or decrypted. This routine *
+ * need only be called when the key is to be changed or set for the first time. Once the *
+ * key has been set, the engine may be used to encrypt, decrypt, or both operations *
+ * indefinitely. The key must be 56 bytes or less in length. This is necessary because *
+ * any keys longer than that will not correctly affect the encryption process. *
+ * *
+ * If the key pointer is NULL, then the S-Box tables are reset to identity. This will *
+ * mask the previous key setting. Use this method to clear the engine after processing in *
+ * order to gain a measure of security. *
+ * *
+ * INPUT: key -- Pointer to the key data block. *
+ * *
+ * length -- The length of the submitted key. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: This is a time consuming process. *
+ * *
+ * HISTORY: *
+ * 04/14/1996 JLB : Created. *
+ *=============================================================================================*/
+void BlowfishEngine::Submit_Key(void const * key, int length)
+{
+ assert(length <= MAX_KEY_LENGTH);
+
+ /*
+ ** Initialize the permutation and S-Box tables to a known
+ ** constant value.
+ */
+ memcpy(P_Encrypt, P_Init, sizeof(P_Init));
+ memcpy(P_Decrypt, P_Init, sizeof(P_Init));
+ memcpy(bf_S, S_Init, sizeof(S_Init));
+
+ /*
+ ** Validate parameters.
+ */
+ if (key == 0 || length == 0) {
+ IsKeyed = false;
+ return;
+ }
+
+ /*
+ ** Combine the key with the permutation table. Wrap the key
+ ** as many times as necessary to ensure that the entire
+ ** permutation table has been modified. The key is lifted
+ ** into a long by using endian independent means.
+ */
+ int j = 0;
+ unsigned char const * key_ptr = (unsigned char const *)key;
+ unsigned long * p_ptr = &P_Encrypt[0];
+ for (int index = 0; index < ROUNDS+2; index++) {
+ unsigned long data = 0;
+
+ data = (data << CHAR_BIT) | key_ptr[j++ % length];
+ data = (data << CHAR_BIT) | key_ptr[j++ % length];
+ data = (data << CHAR_BIT) | key_ptr[j++ % length];
+ data = (data << CHAR_BIT) | key_ptr[j++ % length];
+
+ *p_ptr++ ^= data;
+ }
+
+ /*
+ ** The permutation table must be scrambled by means of the key. This
+ ** is how the key is factored into the encryption -- by merely altering
+ ** the permutation (and S-Box) tables. Because this transformation alters
+ ** the table data WHILE it is using the table data, the tables are
+ ** thoroughly obfuscated by this process.
+ */
+ unsigned long left = 0x00000000L;
+ unsigned long right = 0x00000000L;
+ unsigned long * p_en = &P_Encrypt[0]; // Encryption table.
+ unsigned long * p_de = &P_Decrypt[ROUNDS+1]; // Decryption table.
+ for (int p_index = 0; p_index < ROUNDS+2; p_index += 2) {
+ Sub_Key_Encrypt(left, right);
+
+ *p_en++ = left;
+ *p_en++ = right;
+
+ *p_de-- = left;
+ *p_de-- = right;
+ }
+
+ /*
+ ** Perform a similar transmutation to the S-Box tables. Also notice that the
+ ** working 64 bit number is carried into this process from the previous
+ ** operation.
+ */
+ for (int sbox_index = 0; sbox_index < 4; sbox_index++) {
+ for (int ss_index = 0; ss_index < UCHAR_MAX+1; ss_index += 2) {
+ Sub_Key_Encrypt(left, right);
+ bf_S[sbox_index][ss_index] = left;
+ bf_S[sbox_index][ss_index + 1] = right;
+ }
+ }
+
+ IsKeyed = true;
+}
+
+
+/***********************************************************************************************
+ * BlowfishEngine::Encrypt -- Encrypt an arbitrary block of data. *
+ * *
+ * Use this routine to encrypt an arbitrary block of data. The block must be an even *
+ * multiple of 8 bytes. Any bytes left over will not be encrypted. The 8 byte requirement *
+ * is necessary because the underlying algorithm processes blocks in 8 byte chunks. *
+ * Partial blocks are unrecoverable and useless. *
+ * *
+ * INPUT: plaintext-- Pointer to the data block to be encrypted. *
+ * *
+ * length -- The length of the data block. *
+ * *
+ * cyphertext- Pointer to the output buffer that will hold the encrypted data. *
+ * *
+ * OUTPUT: Returns with the actual number of bytes encrypted. *
+ * *
+ * WARNINGS: You must submit the key before calling this routine. This will only encrypt *
+ * the plaintext in 8 byte increments. Modulo bytes left over are not processed. *
+ * *
+ * HISTORY: *
+ * 04/14/1996 JLB : Created. *
+ *=============================================================================================*/
+int BlowfishEngine::Encrypt(void const * plaintext, int length, void * cyphertext)
+{
+ if (plaintext == 0 || length == 0) {
+ return(0);
+ }
+ if (cyphertext == 0) cyphertext = (void *)plaintext;
+
+ if (IsKeyed) {
+
+ /*
+ ** Validate parameters.
+ */
+ int blocks = length / BYTES_PER_BLOCK;
+
+ /*
+ ** Process the buffer in 64 bit chunks.
+ */
+ for (int index = 0; index < blocks; index++) {
+ Process_Block(plaintext, cyphertext, P_Encrypt);
+ plaintext = ((char *)plaintext) + BYTES_PER_BLOCK;
+ cyphertext = ((char *)cyphertext) + BYTES_PER_BLOCK;
+ }
+ int encrypted = blocks * BYTES_PER_BLOCK;
+
+ /*
+ ** Copy over any trailing left over appendix bytes.
+ */
+ if (encrypted < length) {
+ memmove(cyphertext, plaintext, length - encrypted);
+ }
+
+ return(encrypted);
+ }
+
+ /*
+ ** Non-keyed processing merely copies the data.
+ */
+ if (plaintext != cyphertext) {
+ memmove(cyphertext, plaintext, length);
+ }
+ return(length);
+}
+
+
+/***********************************************************************************************
+ * BlowfishEngine::Decrypt -- Decrypt an arbitrary block of data. *
+ * *
+ * Use this routine to decrypt an arbitrary block of data. The block must be an even *
+ * multiple of 8 bytes. Any bytes left over will not be decrypted. The 8 byte requirement *
+ * is necessary because the underlying algorithm processes blocks in 8 byte chunks. *
+ * Partial blocks are unrecoverable and useless. *
+ * *
+ * INPUT: cyphertext- Pointer to the data block to be decrypted. *
+ * *
+ * length -- The length of the data block. *
+ * *
+ * plaintext-- Pointer to the output buffer that will hold the decrypted data. *
+ * *
+ * OUTPUT: Returns with the actual number of bytes decrypted. *
+ * *
+ * WARNINGS: You must submit the key before calling this routine. This will only decrypt *
+ * the cyphertext in 8 byte increments. Modulo bytes left over are not processed. *
+ * *
+ * HISTORY: *
+ * 04/14/1996 JLB : Created. *
+ *=============================================================================================*/
+int BlowfishEngine::Decrypt(void const * cyphertext, int length, void * plaintext)
+{
+ if (cyphertext == 0 || length == 0) {
+ return(0);
+ }
+ if (plaintext == 0) plaintext = (void *)cyphertext;
+
+ if (IsKeyed) {
+
+ /*
+ ** Validate parameters.
+ */
+ int blocks = length / BYTES_PER_BLOCK;
+
+ /*
+ ** Process the buffer in 64 bit chunks.
+ */
+ for (int index = 0; index < blocks; index++) {
+ Process_Block(cyphertext, plaintext, P_Decrypt);
+ cyphertext = ((char *)cyphertext) + BYTES_PER_BLOCK;
+ plaintext = ((char *)plaintext) + BYTES_PER_BLOCK;
+ }
+ int encrypted = blocks * BYTES_PER_BLOCK;
+
+ /*
+ ** Copy over any trailing left over appendix bytes.
+ */
+ if (encrypted < length) {
+ memmove(plaintext, cyphertext, length - encrypted);
+ }
+
+ return(encrypted);
+ }
+
+ /*
+ ** Non-keyed processing merely copies the data.
+ */
+ if (plaintext != cyphertext) {
+ memmove(plaintext, cyphertext, length);
+ }
+ return(length);
+}
+
+
+/***********************************************************************************************
+ * BlowfishEngine::Process_Block -- Process a block of data using Blowfish algorithm. *
+ * *
+ * This is the main processing routine for encryption and decryption. The algorithm *
+ * consists of a 16 round Feistal network and uses mathematics from different algebraic *
+ * groups (strengthens against differential cryptanalysis). The large S-Boxes and the *
+ * rounds strengthen it against linear cryptanalysis. *
+ * *
+ * INPUT: plaintext -- Pointer to the source text (it actually might be a pointer to *
+ * the cyphertext if this is called as a decryption process). *
+ * *
+ * cyphertext -- Pointer to the output buffer that will hold the processed block. *
+ * *
+ * ptable -- Pointer to the permutation table. This algorithm will encrypt *
+ * and decrypt using the same S-Box tables. The encryption control *
+ * is handled by the permutation table. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: The source and destination buffers must be 8 bytes long. *
+ * *
+ * HISTORY: *
+ * 04/19/1996 JLB : Created. *
+ *=============================================================================================*/
+void BlowfishEngine::Process_Block(void const * plaintext, void * cyphertext, unsigned long const * ptable)
+{
+ /*
+ ** Input the left and right halves of the source block such that
+ ** the byte order is constant regardless of the endian
+ ** persuasion of the current processor. The blowfish algorithm is
+ ** biased toward "big endian" architecture and some optimizations
+ ** could be done for big endian processors in that case.
+ */
+ unsigned char const * source = (unsigned char const *)plaintext;
+ Int left;
+ left.Char.C0 = *source++;
+ left.Char.C1 = *source++;
+ left.Char.C2 = *source++;
+ left.Char.C3 = *source++;
+
+ Int right;
+ right.Char.C0 = *source++;
+ right.Char.C1 = *source++;
+ right.Char.C2 = *source++;
+ right.Char.C3 = *source;
+
+ /*
+ ** Perform all Feistal rounds on the block. This is the encryption/decryption
+ ** process. Since there is an exchange that occurs after each round, two
+ ** rounds are combined in this loop to avoid unnecessary exchanging.
+ */
+ for (int index = 0; index < ROUNDS/2; index++) {
+ left.Long ^= *ptable++;
+ right.Long ^= ((( bf_S[0][left.Char.C0] + bf_S[1][left.Char.C1]) ^ bf_S[2][left.Char.C2]) + bf_S[3][left.Char.C3]);
+ right.Long ^= *ptable++;
+ left.Long ^= ((( bf_S[0][right.Char.C0] + bf_S[1][right.Char.C1]) ^ bf_S[2][right.Char.C2]) + bf_S[3][right.Char.C3]);
+ }
+
+ /*
+ ** The final two longs in the permutation table are processed into the block.
+ ** The left and right halves are still reversed as a side effect of the last
+ ** round.
+ */
+ left.Long ^= *ptable++;
+ right.Long ^= *ptable;
+
+ /*
+ ** The final block data is output in endian architecture
+ ** independent format. Notice that the blocks are output as
+ ** right first and left second. This is to counteract the final
+ ** superfluous exchange that occurs as a side effect of the
+ ** encryption rounds.
+ */
+ unsigned char * out = (unsigned char *)cyphertext;
+ *out++ = right.Char.C0;
+ *out++ = right.Char.C1;
+ *out++ = right.Char.C2;
+ *out++ = right.Char.C3;
+
+ *out++ = left.Char.C0;
+ *out++ = left.Char.C1;
+ *out++ = left.Char.C2;
+ *out = left.Char.C3;
+}
+
+
+/***********************************************************************************************
+ * BlowfishEngine::Sub_Key_Encrypt -- Encrypts a block for use in S-Box processing. *
+ * *
+ * This is the same as the normal process block function but it doesn't have the endian *
+ * fixup logic. Since this routine is only called for S-Box table generation and it is *
+ * known that the S-Box initial data is already in local machine endian format, the *
+ * byte order fixups are not needed. This also has a tendency to speed up S-Box generation *
+ * as well. *
+ * *
+ * INPUT: left -- The left half of the data block. *
+ * *
+ * right -- The right half of the data block. *
+ * *
+ * OUTPUT: none, but the processed block is stored back into the left and right half *
+ * integers. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 04/19/1996 JLB : Created. *
+ *=============================================================================================*/
+void BlowfishEngine::Sub_Key_Encrypt(unsigned long & left, unsigned long & right)
+{
+ Int l;
+ l.Long = left;
+
+ Int r;
+ r.Long = right;
+
+ for (int index = 0; index < ROUNDS; index += 2) {
+ l.Long ^= P_Encrypt[index];
+ r.Long ^= ((( bf_S[0][l.Char.C0] + bf_S[1][l.Char.C1]) ^ bf_S[2][l.Char.C2]) + bf_S[3][l.Char.C3]);
+ r.Long ^= P_Encrypt[index+1];
+ l.Long ^= ((( bf_S[0][r.Char.C0] + bf_S[1][r.Char.C1]) ^ bf_S[2][r.Char.C2]) + bf_S[3][r.Char.C3]);
+ }
+ left = r.Long ^ P_Encrypt[ROUNDS+1];
+ right = l.Long ^ P_Encrypt[ROUNDS];
+}
+
+
+/*
+** These tables have the bytes stored in machine endian format. Because of this,
+** a special block cypher routine is needed when the sub-keys are generated.
+** This is kludgier than it otherwise should be. However, storing these
+** integers in machine independent format would be even more painful.
+*/
+
+unsigned long const BlowfishEngine::P_Init[BlowfishEngine::ROUNDS+2] = {
+ 0x243F6A88U,0x85A308D3U,0x13198A2EU,0x03707344U,0xA4093822U,0x299F31D0U,0x082EFA98U,0xEC4E6C89U,
+ 0x452821E6U,0x38D01377U,0xBE5466CFU,0x34E90C6CU,0xC0AC29B7U,0xC97C50DDU,0x3F84D5B5U,0xB5470917U,
+ 0x9216D5D9U,0x8979FB1BU
+} ;
+
+unsigned long const BlowfishEngine::S_Init[4][UCHAR_MAX+1] = {
+ {
+ 0xD1310BA6U,0x98DFB5ACU,0x2FFD72DBU,0xD01ADFB7U,0xB8E1AFEDU,0x6A267E96U,0xBA7C9045U,0xF12C7F99U,
+ 0x24A19947U,0xB3916CF7U,0x0801F2E2U,0x858EFC16U,0x636920D8U,0x71574E69U,0xA458FEA3U,0xF4933D7EU,
+ 0x0D95748FU,0x728EB658U,0x718BCD58U,0x82154AEEU,0x7B54A41DU,0xC25A59B5U,0x9C30D539U,0x2AF26013U,
+ 0xC5D1B023U,0x286085F0U,0xCA417918U,0xB8DB38EFU,0x8E79DCB0U,0x603A180EU,0x6C9E0E8BU,0xB01E8A3EU,
+ 0xD71577C1U,0xBD314B27U,0x78AF2FDAU,0x55605C60U,0xE65525F3U,0xAA55AB94U,0x57489862U,0x63E81440U,
+ 0x55CA396AU,0x2AAB10B6U,0xB4CC5C34U,0x1141E8CEU,0xA15486AFU,0x7C72E993U,0xB3EE1411U,0x636FBC2AU,
+ 0x2BA9C55DU,0x741831F6U,0xCE5C3E16U,0x9B87931EU,0xAFD6BA33U,0x6C24CF5CU,0x7A325381U,0x28958677U,
+ 0x3B8F4898U,0x6B4BB9AFU,0xC4BFE81BU,0x66282193U,0x61D809CCU,0xFB21A991U,0x487CAC60U,0x5DEC8032U,
+ 0xEF845D5DU,0xE98575B1U,0xDC262302U,0xEB651B88U,0x23893E81U,0xD396ACC5U,0x0F6D6FF3U,0x83F44239U,
+ 0x2E0B4482U,0xA4842004U,0x69C8F04AU,0x9E1F9B5EU,0x21C66842U,0xF6E96C9AU,0x670C9C61U,0xABD388F0U,
+ 0x6A51A0D2U,0xD8542F68U,0x960FA728U,0xAB5133A3U,0x6EEF0B6CU,0x137A3BE4U,0xBA3BF050U,0x7EFB2A98U,
+ 0xA1F1651DU,0x39AF0176U,0x66CA593EU,0x82430E88U,0x8CEE8619U,0x456F9FB4U,0x7D84A5C3U,0x3B8B5EBEU,
+ 0xE06F75D8U,0x85C12073U,0x401A449FU,0x56C16AA6U,0x4ED3AA62U,0x363F7706U,0x1BFEDF72U,0x429B023DU,
+ 0x37D0D724U,0xD00A1248U,0xDB0FEAD3U,0x49F1C09BU,0x075372C9U,0x80991B7BU,0x25D479D8U,0xF6E8DEF7U,
+ 0xE3FE501AU,0xB6794C3BU,0x976CE0BDU,0x04C006BAU,0xC1A94FB6U,0x409F60C4U,0x5E5C9EC2U,0x196A2463U,
+ 0x68FB6FAFU,0x3E6C53B5U,0x1339B2EBU,0x3B52EC6FU,0x6DFC511FU,0x9B30952CU,0xCC814544U,0xAF5EBD09U,
+ 0xBEE3D004U,0xDE334AFDU,0x660F2807U,0x192E4BB3U,0xC0CBA857U,0x45C8740FU,0xD20B5F39U,0xB9D3FBDBU,
+ 0x5579C0BDU,0x1A60320AU,0xD6A100C6U,0x402C7279U,0x679F25FEU,0xFB1FA3CCU,0x8EA5E9F8U,0xDB3222F8U,
+ 0x3C7516DFU,0xFD616B15U,0x2F501EC8U,0xAD0552ABU,0x323DB5FAU,0xFD238760U,0x53317B48U,0x3E00DF82U,
+ 0x9E5C57BBU,0xCA6F8CA0U,0x1A87562EU,0xDF1769DBU,0xD542A8F6U,0x287EFFC3U,0xAC6732C6U,0x8C4F5573U,
+ 0x695B27B0U,0xBBCA58C8U,0xE1FFA35DU,0xB8F011A0U,0x10FA3D98U,0xFD2183B8U,0x4AFCB56CU,0x2DD1D35BU,
+ 0x9A53E479U,0xB6F84565U,0xD28E49BCU,0x4BFB9790U,0xE1DDF2DAU,0xA4CB7E33U,0x62FB1341U,0xCEE4C6E8U,
+ 0xEF20CADAU,0x36774C01U,0xD07E9EFEU,0x2BF11FB4U,0x95DBDA4DU,0xAE909198U,0xEAAD8E71U,0x6B93D5A0U,
+ 0xD08ED1D0U,0xAFC725E0U,0x8E3C5B2FU,0x8E7594B7U,0x8FF6E2FBU,0xF2122B64U,0x8888B812U,0x900DF01CU,
+ 0x4FAD5EA0U,0x688FC31CU,0xD1CFF191U,0xB3A8C1ADU,0x2F2F2218U,0xBE0E1777U,0xEA752DFEU,0x8B021FA1U,
+ 0xE5A0CC0FU,0xB56F74E8U,0x18ACF3D6U,0xCE89E299U,0xB4A84FE0U,0xFD13E0B7U,0x7CC43B81U,0xD2ADA8D9U,
+ 0x165FA266U,0x80957705U,0x93CC7314U,0x211A1477U,0xE6AD2065U,0x77B5FA86U,0xC75442F5U,0xFB9D35CFU,
+ 0xEBCDAF0CU,0x7B3E89A0U,0xD6411BD3U,0xAE1E7E49U,0x00250E2DU,0x2071B35EU,0x226800BBU,0x57B8E0AFU,
+ 0x2464369BU,0xF009B91EU,0x5563911DU,0x59DFA6AAU,0x78C14389U,0xD95A537FU,0x207D5BA2U,0x02E5B9C5U,
+ 0x83260376U,0x6295CFA9U,0x11C81968U,0x4E734A41U,0xB3472DCAU,0x7B14A94AU,0x1B510052U,0x9A532915U,
+ 0xD60F573FU,0xBC9BC6E4U,0x2B60A476U,0x81E67400U,0x08BA6FB5U,0x571BE91FU,0xF296EC6BU,0x2A0DD915U,
+ 0xB6636521U,0xE7B9F9B6U,0xFF34052EU,0xC5855664U,0x53B02D5DU,0xA99F8FA1U,0x08BA4799U,0x6E85076AU,
+ },{
+ 0x4B7A70E9U,0xB5B32944U,0xDB75092EU,0xC4192623U,0xAD6EA6B0U,0x49A7DF7DU,0x9CEE60B8U,0x8FEDB266U,
+ 0xECAA8C71U,0x699A17FFU,0x5664526CU,0xC2B19EE1U,0x193602A5U,0x75094C29U,0xA0591340U,0xE4183A3EU,
+ 0x3F54989AU,0x5B429D65U,0x6B8FE4D6U,0x99F73FD6U,0xA1D29C07U,0xEFE830F5U,0x4D2D38E6U,0xF0255DC1U,
+ 0x4CDD2086U,0x8470EB26U,0x6382E9C6U,0x021ECC5EU,0x09686B3FU,0x3EBAEFC9U,0x3C971814U,0x6B6A70A1U,
+ 0x687F3584U,0x52A0E286U,0xB79C5305U,0xAA500737U,0x3E07841CU,0x7FDEAE5CU,0x8E7D44ECU,0x5716F2B8U,
+ 0xB03ADA37U,0xF0500C0DU,0xF01C1F04U,0x0200B3FFU,0xAE0CF51AU,0x3CB574B2U,0x25837A58U,0xDC0921BDU,
+ 0xD19113F9U,0x7CA92FF6U,0x94324773U,0x22F54701U,0x3AE5E581U,0x37C2DADCU,0xC8B57634U,0x9AF3DDA7U,
+ 0xA9446146U,0x0FD0030EU,0xECC8C73EU,0xA4751E41U,0xE238CD99U,0x3BEA0E2FU,0x3280BBA1U,0x183EB331U,
+ 0x4E548B38U,0x4F6DB908U,0x6F420D03U,0xF60A04BFU,0x2CB81290U,0x24977C79U,0x5679B072U,0xBCAF89AFU,
+ 0xDE9A771FU,0xD9930810U,0xB38BAE12U,0xDCCF3F2EU,0x5512721FU,0x2E6B7124U,0x501ADDE6U,0x9F84CD87U,
+ 0x7A584718U,0x7408DA17U,0xBC9F9ABCU,0xE94B7D8CU,0xEC7AEC3AU,0xDB851DFAU,0x63094366U,0xC464C3D2U,
+ 0xEF1C1847U,0x3215D908U,0xDD433B37U,0x24C2BA16U,0x12A14D43U,0x2A65C451U,0x50940002U,0x133AE4DDU,
+ 0x71DFF89EU,0x10314E55U,0x81AC77D6U,0x5F11199BU,0x043556F1U,0xD7A3C76BU,0x3C11183BU,0x5924A509U,
+ 0xF28FE6EDU,0x97F1FBFAU,0x9EBABF2CU,0x1E153C6EU,0x86E34570U,0xEAE96FB1U,0x860E5E0AU,0x5A3E2AB3U,
+ 0x771FE71CU,0x4E3D06FAU,0x2965DCB9U,0x99E71D0FU,0x803E89D6U,0x5266C825U,0x2E4CC978U,0x9C10B36AU,
+ 0xC6150EBAU,0x94E2EA78U,0xA5FC3C53U,0x1E0A2DF4U,0xF2F74EA7U,0x361D2B3DU,0x1939260FU,0x19C27960U,
+ 0x5223A708U,0xF71312B6U,0xEBADFE6EU,0xEAC31F66U,0xE3BC4595U,0xA67BC883U,0xB17F37D1U,0x018CFF28U,
+ 0xC332DDEFU,0xBE6C5AA5U,0x65582185U,0x68AB9802U,0xEECEA50FU,0xDB2F953BU,0x2AEF7DADU,0x5B6E2F84U,
+ 0x1521B628U,0x29076170U,0xECDD4775U,0x619F1510U,0x13CCA830U,0xEB61BD96U,0x0334FE1EU,0xAA0363CFU,
+ 0xB5735C90U,0x4C70A239U,0xD59E9E0BU,0xCBAADE14U,0xEECC86BCU,0x60622CA7U,0x9CAB5CABU,0xB2F3846EU,
+ 0x648B1EAFU,0x19BDF0CAU,0xA02369B9U,0x655ABB50U,0x40685A32U,0x3C2AB4B3U,0x319EE9D5U,0xC021B8F7U,
+ 0x9B540B19U,0x875FA099U,0x95F7997EU,0x623D7DA8U,0xF837889AU,0x97E32D77U,0x11ED935FU,0x16681281U,
+ 0x0E358829U,0xC7E61FD6U,0x96DEDFA1U,0x7858BA99U,0x57F584A5U,0x1B227263U,0x9B83C3FFU,0x1AC24696U,
+ 0xCDB30AEBU,0x532E3054U,0x8FD948E4U,0x6DBC3128U,0x58EBF2EFU,0x34C6FFEAU,0xFE28ED61U,0xEE7C3C73U,
+ 0x5D4A14D9U,0xE864B7E3U,0x42105D14U,0x203E13E0U,0x45EEE2B6U,0xA3AAABEAU,0xDB6C4F15U,0xFACB4FD0U,
+ 0xC742F442U,0xEF6ABBB5U,0x654F3B1DU,0x41CD2105U,0xD81E799EU,0x86854DC7U,0xE44B476AU,0x3D816250U,
+ 0xCF62A1F2U,0x5B8D2646U,0xFC8883A0U,0xC1C7B6A3U,0x7F1524C3U,0x69CB7492U,0x47848A0BU,0x5692B285U,
+ 0x095BBF00U,0xAD19489DU,0x1462B174U,0x23820E00U,0x58428D2AU,0x0C55F5EAU,0x1DADF43EU,0x233F7061U,
+ 0x3372F092U,0x8D937E41U,0xD65FECF1U,0x6C223BDBU,0x7CDE3759U,0xCBEE7460U,0x4085F2A7U,0xCE77326EU,
+ 0xA6078084U,0x19F8509EU,0xE8EFD855U,0x61D99735U,0xA969A7AAU,0xC50C06C2U,0x5A04ABFCU,0x800BCADCU,
+ 0x9E447A2EU,0xC3453484U,0xFDD56705U,0x0E1E9EC9U,0xDB73DBD3U,0x105588CDU,0x675FDA79U,0xE3674340U,
+ 0xC5C43465U,0x713E38D8U,0x3D28F89EU,0xF16DFF20U,0x153E21E7U,0x8FB03D4AU,0xE6E39F2BU,0xDB83ADF7U,
+ },{
+ 0xE93D5A68U,0x948140F7U,0xF64C261CU,0x94692934U,0x411520F7U,0x7602D4F7U,0xBCF46B2EU,0xD4A20068U,
+ 0xD4082471U,0x3320F46AU,0x43B7D4B7U,0x500061AFU,0x1E39F62EU,0x97244546U,0x14214F74U,0xBF8B8840U,
+ 0x4D95FC1DU,0x96B591AFU,0x70F4DDD3U,0x66A02F45U,0xBFBC09ECU,0x03BD9785U,0x7FAC6DD0U,0x31CB8504U,
+ 0x96EB27B3U,0x55FD3941U,0xDA2547E6U,0xABCA0A9AU,0x28507825U,0x530429F4U,0x0A2C86DAU,0xE9B66DFBU,
+ 0x68DC1462U,0xD7486900U,0x680EC0A4U,0x27A18DEEU,0x4F3FFEA2U,0xE887AD8CU,0xB58CE006U,0x7AF4D6B6U,
+ 0xAACE1E7CU,0xD3375FECU,0xCE78A399U,0x406B2A42U,0x20FE9E35U,0xD9F385B9U,0xEE39D7ABU,0x3B124E8BU,
+ 0x1DC9FAF7U,0x4B6D1856U,0x26A36631U,0xEAE397B2U,0x3A6EFA74U,0xDD5B4332U,0x6841E7F7U,0xCA7820FBU,
+ 0xFB0AF54EU,0xD8FEB397U,0x454056ACU,0xBA489527U,0x55533A3AU,0x20838D87U,0xFE6BA9B7U,0xD096954BU,
+ 0x55A867BCU,0xA1159A58U,0xCCA92963U,0x99E1DB33U,0xA62A4A56U,0x3F3125F9U,0x5EF47E1CU,0x9029317CU,
+ 0xFDF8E802U,0x04272F70U,0x80BB155CU,0x05282CE3U,0x95C11548U,0xE4C66D22U,0x48C1133FU,0xC70F86DCU,
+ 0x07F9C9EEU,0x41041F0FU,0x404779A4U,0x5D886E17U,0x325F51EBU,0xD59BC0D1U,0xF2BCC18FU,0x41113564U,
+ 0x257B7834U,0x602A9C60U,0xDFF8E8A3U,0x1F636C1BU,0x0E12B4C2U,0x02E1329EU,0xAF664FD1U,0xCAD18115U,
+ 0x6B2395E0U,0x333E92E1U,0x3B240B62U,0xEEBEB922U,0x85B2A20EU,0xE6BA0D99U,0xDE720C8CU,0x2DA2F728U,
+ 0xD0127845U,0x95B794FDU,0x647D0862U,0xE7CCF5F0U,0x5449A36FU,0x877D48FAU,0xC39DFD27U,0xF33E8D1EU,
+ 0x0A476341U,0x992EFF74U,0x3A6F6EABU,0xF4F8FD37U,0xA812DC60U,0xA1EBDDF8U,0x991BE14CU,0xDB6E6B0DU,
+ 0xC67B5510U,0x6D672C37U,0x2765D43BU,0xDCD0E804U,0xF1290DC7U,0xCC00FFA3U,0xB5390F92U,0x690FED0BU,
+ 0x667B9FFBU,0xCEDB7D9CU,0xA091CF0BU,0xD9155EA3U,0xBB132F88U,0x515BAD24U,0x7B9479BFU,0x763BD6EBU,
+ 0x37392EB3U,0xCC115979U,0x8026E297U,0xF42E312DU,0x6842ADA7U,0xC66A2B3BU,0x12754CCCU,0x782EF11CU,
+ 0x6A124237U,0xB79251E7U,0x06A1BBE6U,0x4BFB6350U,0x1A6B1018U,0x11CAEDFAU,0x3D25BDD8U,0xE2E1C3C9U,
+ 0x44421659U,0x0A121386U,0xD90CEC6EU,0xD5ABEA2AU,0x64AF674EU,0xDA86A85FU,0xBEBFE988U,0x64E4C3FEU,
+ 0x9DBC8057U,0xF0F7C086U,0x60787BF8U,0x6003604DU,0xD1FD8346U,0xF6381FB0U,0x7745AE04U,0xD736FCCCU,
+ 0x83426B33U,0xF01EAB71U,0xB0804187U,0x3C005E5FU,0x77A057BEU,0xBDE8AE24U,0x55464299U,0xBF582E61U,
+ 0x4E58F48FU,0xF2DDFDA2U,0xF474EF38U,0x8789BDC2U,0x5366F9C3U,0xC8B38E74U,0xB475F255U,0x46FCD9B9U,
+ 0x7AEB2661U,0x8B1DDF84U,0x846A0E79U,0x915F95E2U,0x466E598EU,0x20B45770U,0x8CD55591U,0xC902DE4CU,
+ 0xB90BACE1U,0xBB8205D0U,0x11A86248U,0x7574A99EU,0xB77F19B6U,0xE0A9DC09U,0x662D09A1U,0xC4324633U,
+ 0xE85A1F02U,0x09F0BE8CU,0x4A99A025U,0x1D6EFE10U,0x1AB93D1DU,0x0BA5A4DFU,0xA186F20FU,0x2868F169U,
+ 0xDCB7DA83U,0x573906FEU,0xA1E2CE9BU,0x4FCD7F52U,0x50115E01U,0xA70683FAU,0xA002B5C4U,0x0DE6D027U,
+ 0x9AF88C27U,0x773F8641U,0xC3604C06U,0x61A806B5U,0xF0177A28U,0xC0F586E0U,0x006058AAU,0x30DC7D62U,
+ 0x11E69ED7U,0x2338EA63U,0x53C2DD94U,0xC2C21634U,0xBBCBEE56U,0x90BCB6DEU,0xEBFC7DA1U,0xCE591D76U,
+ 0x6F05E409U,0x4B7C0188U,0x39720A3DU,0x7C927C24U,0x86E3725FU,0x724D9DB9U,0x1AC15BB4U,0xD39EB8FCU,
+ 0xED545578U,0x08FCA5B5U,0xD83D7CD3U,0x4DAD0FC4U,0x1E50EF5EU,0xB161E6F8U,0xA28514D9U,0x6C51133CU,
+ 0x6FD5C7E7U,0x56E14EC4U,0x362ABFCEU,0xDDC6C837U,0xD79A3234U,0x92638212U,0x670EFA8EU,0x406000E0U,
+ },{
+ 0x3A39CE37U,0xD3FAF5CFU,0xABC27737U,0x5AC52D1BU,0x5CB0679EU,0x4FA33742U,0xD3822740U,0x99BC9BBEU,
+ 0xD5118E9DU,0xBF0F7315U,0xD62D1C7EU,0xC700C47BU,0xB78C1B6BU,0x21A19045U,0xB26EB1BEU,0x6A366EB4U,
+ 0x5748AB2FU,0xBC946E79U,0xC6A376D2U,0x6549C2C8U,0x530FF8EEU,0x468DDE7DU,0xD5730A1DU,0x4CD04DC6U,
+ 0x2939BBDBU,0xA9BA4650U,0xAC9526E8U,0xBE5EE304U,0xA1FAD5F0U,0x6A2D519AU,0x63EF8CE2U,0x9A86EE22U,
+ 0xC089C2B8U,0x43242EF6U,0xA51E03AAU,0x9CF2D0A4U,0x83C061BAU,0x9BE96A4DU,0x8FE51550U,0xBA645BD6U,
+ 0x2826A2F9U,0xA73A3AE1U,0x4BA99586U,0xEF5562E9U,0xC72FEFD3U,0xF752F7DAU,0x3F046F69U,0x77FA0A59U,
+ 0x80E4A915U,0x87B08601U,0x9B09E6ADU,0x3B3EE593U,0xE990FD5AU,0x9E34D797U,0x2CF0B7D9U,0x022B8B51U,
+ 0x96D5AC3AU,0x017DA67DU,0xD1CF3ED6U,0x7C7D2D28U,0x1F9F25CFU,0xADF2B89BU,0x5AD6B472U,0x5A88F54CU,
+ 0xE029AC71U,0xE019A5E6U,0x47B0ACFDU,0xED93FA9BU,0xE8D3C48DU,0x283B57CCU,0xF8D56629U,0x79132E28U,
+ 0x785F0191U,0xED756055U,0xF7960E44U,0xE3D35E8CU,0x15056DD4U,0x88F46DBAU,0x03A16125U,0x0564F0BDU,
+ 0xC3EB9E15U,0x3C9057A2U,0x97271AECU,0xA93A072AU,0x1B3F6D9BU,0x1E6321F5U,0xF59C66FBU,0x26DCF319U,
+ 0x7533D928U,0xB155FDF5U,0x03563482U,0x8ABA3CBBU,0x28517711U,0xC20AD9F8U,0xABCC5167U,0xCCAD925FU,
+ 0x4DE81751U,0x3830DC8EU,0x379D5862U,0x9320F991U,0xEA7A90C2U,0xFB3E7BCEU,0x5121CE64U,0x774FBE32U,
+ 0xA8B6E37EU,0xC3293D46U,0x48DE5369U,0x6413E680U,0xA2AE0810U,0xDD6DB224U,0x69852DFDU,0x09072166U,
+ 0xB39A460AU,0x6445C0DDU,0x586CDECFU,0x1C20C8AEU,0x5BBEF7DDU,0x1B588D40U,0xCCD2017FU,0x6BB4E3BBU,
+ 0xDDA26A7EU,0x3A59FF45U,0x3E350A44U,0xBCB4CDD5U,0x72EACEA8U,0xFA6484BBU,0x8D6612AEU,0xBF3C6F47U,
+ 0xD29BE463U,0x542F5D9EU,0xAEC2771BU,0xF64E6370U,0x740E0D8DU,0xE75B1357U,0xF8721671U,0xAF537D5DU,
+ 0x4040CB08U,0x4EB4E2CCU,0x34D2466AU,0x0115AF84U,0xE1B00428U,0x95983A1DU,0x06B89FB4U,0xCE6EA048U,
+ 0x6F3F3B82U,0x3520AB82U,0x011A1D4BU,0x277227F8U,0x611560B1U,0xE7933FDCU,0xBB3A792BU,0x344525BDU,
+ 0xA08839E1U,0x51CE794BU,0x2F32C9B7U,0xA01FBAC9U,0xE01CC87EU,0xBCC7D1F6U,0xCF0111C3U,0xA1E8AAC7U,
+ 0x1A908749U,0xD44FBD9AU,0xD0DADECBU,0xD50ADA38U,0x0339C32AU,0xC6913667U,0x8DF9317CU,0xE0B12B4FU,
+ 0xF79E59B7U,0x43F5BB3AU,0xF2D519FFU,0x27D9459CU,0xBF97222CU,0x15E6FC2AU,0x0F91FC71U,0x9B941525U,
+ 0xFAE59361U,0xCEB69CEBU,0xC2A86459U,0x12BAA8D1U,0xB6C1075EU,0xE3056A0CU,0x10D25065U,0xCB03A442U,
+ 0xE0EC6E0EU,0x1698DB3BU,0x4C98A0BEU,0x3278E964U,0x9F1F9532U,0xE0D392DFU,0xD3A0342BU,0x8971F21EU,
+ 0x1B0A7441U,0x4BA3348CU,0xC5BE7120U,0xC37632D8U,0xDF359F8DU,0x9B992F2EU,0xE60B6F47U,0x0FE3F11DU,
+ 0xE54CDA54U,0x1EDAD891U,0xCE6279CFU,0xCD3E7E6FU,0x1618B166U,0xFD2C1D05U,0x848FD2C5U,0xF6FB2299U,
+ 0xF523F357U,0xA6327623U,0x93A83531U,0x56CCCD02U,0xACF08162U,0x5A75EBB5U,0x6E163697U,0x88D273CCU,
+ 0xDE966292U,0x81B949D0U,0x4C50901BU,0x71C65614U,0xE6C6C7BDU,0x327A140AU,0x45E1D006U,0xC3F27B9AU,
+ 0xC9AA53FDU,0x62A80F00U,0xBB25BFE2U,0x35BDD2F6U,0x71126905U,0xB2040222U,0xB6CBCF7CU,0xCD769C2BU,
+ 0x53113EC0U,0x1640E3D3U,0x38ABBD60U,0x2547ADF0U,0xBA38209CU,0xF746CE76U,0x77AFA1C5U,0x20756060U,
+ 0x85CBFE4EU,0x8AE88DD8U,0x7AAAF9B0U,0x4CF9AA7EU,0x1948C25CU,0x02FB8A8CU,0x01C36AE4U,0xD6EBE1F9U,
+ 0x90D4F869U,0xA65CDEA0U,0x3F09252DU,0xC208E69FU,0xB74E6132U,0xCE77E25BU,0x578FDFE3U,0x3AC372E6U
+ }
+};
+
diff --git a/CODE/BLOWFISH.H b/CODE/BLOWFISH.H
new file mode 100644
index 0000000..62107b2
--- /dev/null
+++ b/CODE/BLOWFISH.H
@@ -0,0 +1,117 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/BLOWFISH.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : BLOWFISH.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 04/14/96 *
+ * *
+ * Last Update : April 14, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifndef BLOWFISH_H
+#define BLOWFISH_H
+
+#include
+
+
+/*
+** The "bool" integral type was defined by the C++ committee in
+** November of '94. Until the compiler supports this, use the following
+** definition.
+*/
+#ifndef __BORLANDC__
+#ifndef TRUE_FALSE_DEFINED
+#define TRUE_FALSE_DEFINED
+enum {false=0,true=1};
+typedef int bool;
+#endif
+#endif
+
+
+/*
+** This engine will process data blocks by encryption and decryption.
+** The "Blowfish" algorithm is in the public domain. It uses
+** a Feistal network (similar to IDEA). It has no known
+** weaknesses, but is still relatively new. Blowfish is particularly strong
+** against brute force attacks. It is also quite strong against linear and
+** differential cryptanalysis. Its weakness is that it takes a relatively
+** long time to set up with a new key (1/100th of a second on a P6-200).
+** The time to set up a key is equivalent to encrypting 4240 bytes.
+*/
+class BlowfishEngine {
+ public:
+ BlowfishEngine(void) : IsKeyed(false) {}
+ ~BlowfishEngine(void);
+
+ void Submit_Key(void const * key, int length);
+
+ int Encrypt(void const * plaintext, int length, void * cyphertext);
+ int Decrypt(void const * cyphertext, int length, void * plaintext);
+
+ /*
+ ** This is the maximum key length supported.
+ */
+ enum {MAX_KEY_LENGTH=56};
+
+ private:
+ bool IsKeyed;
+
+ void Sub_Key_Encrypt(unsigned long & left, unsigned long & right);
+
+ void Process_Block(void const * plaintext, void * cyphertext, unsigned long const * ptable);
+ void Initialize_Tables(void);
+
+ enum {
+ ROUNDS = 16, // Feistal round count (16 is standard).
+ BYTES_PER_BLOCK=8 // The number of bytes in each cypher block (don't change).
+ };
+
+ /*
+ ** Initialization data for sub keys. The initial values are constant and
+ ** filled with a number generated from pi. Thus they are not random but
+ ** they don't hold a weak pattern either.
+ */
+ static unsigned long const P_Init[(int)ROUNDS+2];
+ static unsigned long const S_Init[4][UCHAR_MAX+1];
+
+ /*
+ ** Permutation tables for encryption and decryption.
+ */
+ unsigned long P_Encrypt[(int)ROUNDS+2];
+ unsigned long P_Decrypt[(int)ROUNDS+2];
+
+ /*
+ ** S-Box tables (four).
+ */
+ unsigned long bf_S[4][UCHAR_MAX+1];
+};
+
+#endif
+
diff --git a/CODE/BLOWPIPE.CPP b/CODE/BLOWPIPE.CPP
new file mode 100644
index 0000000..25fcccb
--- /dev/null
+++ b/CODE/BLOWPIPE.CPP
@@ -0,0 +1,202 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/BLOWPIPE.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : BLOWPIPE.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 06/30/96 *
+ * *
+ * Last Update : July 3, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * BlowPipe::Flush -- Flushes any pending data out the pipe. *
+ * BlowPipe::Key -- Submit a key to the blowfish pipe handler. *
+ * BlowPipe::Put -- Submit a block of data for encrypt/decrypt. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#include "blowpipe.h"
+#include
+#include
+
+
+/***********************************************************************************************
+ * BlowPipe::Flush -- Flushes any pending data out the pipe. *
+ * *
+ * If there is any pending data in the holding buffer, then this routine will force it to *
+ * be flushed out the end of the pipe. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the actual number of bytes output at the end final distant pipe *
+ * segment in the chain. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+int BlowPipe::Flush(void)
+{
+ int total = 0;
+ if (Counter > 0 && BF != NULL) {
+ total += Pipe::Put(Buffer, Counter);
+ }
+ Counter = 0;
+ total += Pipe::Flush();
+ return(total);
+}
+
+
+/***********************************************************************************************
+ * BlowPipe::Put -- Submit a block of data for encrypt/decrypt. *
+ * *
+ * This will take the data block specified and process it before passing it on to the next *
+ * link in the pipe chain. A key must be submitted before this routine will actually perform*
+ * any processing. Prior to key submission, the data is passed through unchanged. *
+ * *
+ * INPUT: source -- Pointer to the buffer that contains the data to pass through. *
+ * *
+ * length -- The length of the data in the buffer. *
+ * *
+ * OUTPUT: Returns with then actual number of bytes output at the final distant end link in *
+ * the pipe chain. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+int BlowPipe::Put(void const * source, int slen)
+{
+ if (source == NULL || slen < 1) {
+ return(Pipe::Put(source, slen));
+ }
+
+ /*
+ ** If there is no blowfish engine present, then merely pass the data through
+ ** unchanged in any way.
+ */
+ if (BF == NULL) {
+ return(Pipe::Put(source, slen));
+ }
+
+ int total = 0;
+
+ /*
+ ** If there is a partial block accumulated, then tag on the new data to
+ ** this block and process it if the block is full. Proceed with the bulk
+ ** processing if there are any left over bytes from this step. This step
+ ** can be skipped if there are no pending bytes in the buffer.
+ */
+ if (Counter) {
+ int sublen = (sizeof(Buffer)-Counter < slen) ? (sizeof(Buffer)-Counter) : slen;
+ memmove(&Buffer[Counter], source, sublen);
+ Counter += sublen;
+ source = ((char *)source) + sublen;
+ slen -= sublen;
+
+ if (Counter == sizeof(Buffer)) {
+ if (Control == DECRYPT) {
+ BF->Decrypt(Buffer, sizeof(Buffer), Buffer);
+ } else {
+ BF->Encrypt(Buffer, sizeof(Buffer), Buffer);
+ }
+ total += Pipe::Put(Buffer, sizeof(Buffer));
+ Counter = 0;
+ }
+ }
+
+ /*
+ ** Process the input data in blocks until there is not enough
+ ** source data to fill a full block of data.
+ */
+ while (slen >= sizeof(Buffer)) {
+ if (Control == DECRYPT) {
+ BF->Decrypt(source, sizeof(Buffer), Buffer);
+ } else {
+ BF->Encrypt(source, sizeof(Buffer), Buffer);
+ }
+ total += Pipe::Put(Buffer, sizeof(Buffer));
+ source = ((char *)source) + sizeof(Buffer);
+ slen -= sizeof(Buffer);
+ }
+
+ /*
+ ** If there are any left over bytes, then they must be less than the size of
+ ** the staging buffer. Store the bytes in the staging buffer for later
+ ** processing.
+ */
+ if (slen > 0) {
+ memmove(Buffer, source, slen);
+ Counter = slen;
+ }
+
+ /*
+ ** Return with the total number of bytes flushed out to the final end of the
+ ** pipe chain.
+ */
+ return(total);
+}
+
+
+/***********************************************************************************************
+ * BlowPipe::Key -- Submit a key to the blowfish pipe handler. *
+ * *
+ * This routine will take the key provided and use it to process the data that passes *
+ * through this pipe. Prior to a key being submitted, the data passes through the pipe *
+ * unchanged. *
+ * *
+ * INPUT: key -- Pointer to the key data to use. *
+ * *
+ * length-- The length of the key. The key length must not be greater than 56 bytes. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+void BlowPipe::Key(void const * key, int length)
+{
+ /*
+ ** Create the blowfish engine if one isn't already present.
+ */
+ if (BF == NULL) {
+ BF = new BlowfishEngine;
+ }
+
+ assert(BF != NULL);
+
+ if (BF != NULL) {
+ BF->Submit_Key(key, length);
+ }
+}
+
+
+
diff --git a/CODE/BLOWPIPE.H b/CODE/BLOWPIPE.H
new file mode 100644
index 0000000..47f2d92
--- /dev/null
+++ b/CODE/BLOWPIPE.H
@@ -0,0 +1,85 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/BLOWPIPE.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : BLOWPIPE.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 06/30/96 *
+ * *
+ * Last Update : June 30, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#ifndef BLOWPIPE_H
+#define BLOWPIPE_H
+
+#include "pipe.h"
+#include "blowfish.h"
+
+/*
+** Performs Blowfish encryption/decryption on the data stream that is piped
+** through this class.
+*/
+class BlowPipe : public Pipe
+{
+ public:
+ typedef enum CryptControl {
+ ENCRYPT,
+ DECRYPT
+ } CryptControl;
+
+ BlowPipe(CryptControl control) : BF(NULL), Counter(0), Control(control) {}
+ virtual ~BlowPipe(void) {delete BF;BF = NULL;}
+ virtual int Flush(void);
+
+ virtual int Put(void const * source, int slen);
+
+ // Submit key for blowfish engine.
+ void Key(void const * key, int length);
+
+ protected:
+ /*
+ ** The Blowfish engine used for encryption/decryption. If this pointer is
+ ** NULL, then this indicates that the blowfish engine is not active and no
+ ** key has been submitted. All data would pass through this pipe unchanged
+ ** in that case.
+ */
+ BlowfishEngine * BF;
+
+ private:
+ char Buffer[8];
+ int Counter;
+ CryptControl Control;
+
+ BlowPipe(BlowPipe & rvalue);
+ BlowPipe & operator = (BlowPipe const & pipe);
+};
+
+
+#endif
diff --git a/CODE/BLWSTRAW.CPP b/CODE/BLWSTRAW.CPP
new file mode 100644
index 0000000..9931811
--- /dev/null
+++ b/CODE/BLWSTRAW.CPP
@@ -0,0 +1,161 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/BLWSTRAW.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : BLWSTRAW.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 07/02/96 *
+ * *
+ * Last Update : July 3, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * BlowStraw::Get -- Fetch a block of data from the straw. *
+ * BlowStraw::Key -- Submit a key to the Blowfish straw. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#include "blwstraw.h"
+#include
+#include
+
+
+/***********************************************************************************************
+ * BlowStraw::Get -- Fetch a block of data from the straw. *
+ * *
+ * This routine will take a block of data from the straw and process it according to the *
+ * encrypt/decrypt flag and the key supplied. Prior to a key be supplied, the data passes *
+ * through this straw unchanged. *
+ * *
+ * INPUT: source -- Pointer to the buffer to hold the data being requested. *
+ * *
+ * length -- The length of the data being requested. *
+ * *
+ * OUTPUT: Returns with the actual number of bytes stored into the buffer. If the number *
+ * returned is less than the number requested, then this indicates that the data *
+ * source has been exhausted. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+int BlowStraw::Get(void * source, int slen)
+{
+ /*
+ ** Verify the parameter for legality.
+ */
+ if (source == NULL || slen <= 0) {
+ return(0);
+ }
+
+ /*
+ ** If there is no blowfish engine present, then merely pass the data through
+ ** unchanged.
+ */
+ if (BF == NULL) {
+ return(Straw::Get(source, slen));
+ }
+
+ int total = 0;
+
+ while (slen > 0) {
+
+ /*
+ ** If there are any left over bytes in the buffer, pass them
+ ** through first.
+ */
+ if (Counter > 0) {
+ int sublen = (slen < Counter) ? slen : Counter;
+ memmove(source, &Buffer[sizeof(Buffer)-Counter], sublen);
+ Counter -= sublen;
+ source = ((char *)source) + sublen;
+ slen -= sublen;
+ total += sublen;
+ }
+ if (slen == 0) break;
+
+ /*
+ ** Fetch and encrypt/decrypt the next block.
+ */
+ int incount = Straw::Get(Buffer, sizeof(Buffer));
+ if (incount == 0) break;
+
+ /*
+ ** Only full blocks are processed. Partial blocks are
+ ** merely passed through unchanged.
+ */
+ if (incount == sizeof(Buffer)) {
+ if (Control == DECRYPT) {
+ BF->Decrypt(Buffer, incount, Buffer);
+ } else {
+ BF->Encrypt(Buffer, incount, Buffer);
+ }
+ } else {
+ memmove(&Buffer[sizeof(Buffer)-incount], Buffer, incount);
+ }
+ Counter = incount;
+ }
+
+ /*
+ ** Return with the total number of bytes placed into the buffer.
+ */
+ return(total);
+}
+
+
+/***********************************************************************************************
+ * BlowStraw::Key -- Submit a key to the Blowfish straw. *
+ * *
+ * This will take the key specified and use it to process the data that flows through this *
+ * straw segment. Prior to a key being submitted, the data will flow through unchanged. *
+ * *
+ * INPUT: key -- Pointer to the key to submit. *
+ * *
+ * length-- The length of the key. The length must not exceed 56 bytes. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+void BlowStraw::Key(void const * key, int length)
+{
+ /*
+ ** Create the blowfish engine if one isn't already present.
+ */
+ if (BF == NULL) {
+ BF = new BlowfishEngine;
+ }
+
+ assert(BF != NULL);
+
+ if (BF != NULL) {
+ BF->Submit_Key(key, length);
+ }
+}
diff --git a/CODE/BLWSTRAW.H b/CODE/BLWSTRAW.H
new file mode 100644
index 0000000..8b5658b
--- /dev/null
+++ b/CODE/BLWSTRAW.H
@@ -0,0 +1,87 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/BLWSTRAW.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : BLWSTRAW.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 07/02/96 *
+ * *
+ * Last Update : July 2, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#ifndef BLWSTRAW_H
+#define BLWSTRAW_H
+
+#include "straw.h"
+#include "blowfish.h"
+
+
+/*
+** Performs Blowfish encryption/decryption to the data that is drawn through this straw. The
+** process is controlled by the key which must be submitted to the class before any data
+** manipulation will occur. The Blowfish algorithm is symmetric, thus the same key is used
+** for encryption as is for decryption.
+*/
+class BlowStraw : public Straw
+{
+ public:
+ typedef enum CryptControl {
+ ENCRYPT,
+ DECRYPT
+ } CryptControl;
+
+ BlowStraw(CryptControl control) : BF(NULL), Counter(0), Control(control) {}
+ virtual ~BlowStraw(void) {delete BF;BF = NULL;}
+
+ virtual int Get(void * source, int slen);
+
+ // Submit key for blowfish engine.
+ void Key(void const * key, int length);
+
+ protected:
+ /*
+ ** The Blowfish engine used for encryption/decryption. If this pointer is
+ ** NULL, then this indicates that the blowfish engine is not active and no
+ ** key has been submitted. All data would pass through this straw unchanged
+ ** in that case.
+ */
+ BlowfishEngine * BF;
+
+ private:
+ char Buffer[8];
+ int Counter;
+ CryptControl Control;
+
+ BlowStraw(BlowStraw & rvalue);
+ BlowStraw & operator = (BlowStraw const & straw);
+};
+
+
+#endif
diff --git a/CODE/BMP8.CPP b/CODE/BMP8.CPP
new file mode 100644
index 0000000..5cad8f4
--- /dev/null
+++ b/CODE/BMP8.CPP
@@ -0,0 +1,189 @@
+/*
+** Command & Conquer Red Alert(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 "bmp8.h"
+
+//***********************************************************************************************
+BMP8::~BMP8()
+{
+ // free resources
+ if( hBitmap )
+ ::DeleteObject( hBitmap );
+ if( hPal )
+ ::DeleteObject( hPal );
+}
+
+//***********************************************************************************************
+bool BMP8::Init( const char* szFile, HWND hWnd )
+{
+ int i;
+ char string[128];
+ DWORD dwRead;
+ BITMAPFILEHEADER bitmapHeader;
+ BITMAPINFOHEADER bitmapInfoHeader;
+ LPLOGPALETTE lpLogPalette;
+ char *palData;
+ HGLOBAL hmem2;
+ LPVOID lpvBits;
+ PAINTSTRUCT ps;
+ HDC hdc;
+ HPALETTE select;
+ UINT realize;
+ RECT rect;
+
+
+ // Remember window handle for use later.
+ this->hWnd = hWnd;
+
+ // Retrieve a handle identifying the file.
+ HANDLE hFile = ::CreateFile(
+ szFile,
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ (LPSECURITY_ATTRIBUTES)NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_READONLY,
+ (HANDLE)NULL );
+
+ if( !hFile )
+ return false;
+
+ // Retrieve the BITMAPFILEHEADER structure.
+ ::ReadFile( hFile, &bitmapHeader, sizeof(BITMAPFILEHEADER), &dwRead, (LPOVERLAPPED)NULL );
+
+ // Retrieve the BITMAPFILEHEADER structure.
+ ::ReadFile( hFile, &bitmapInfoHeader, sizeof(BITMAPINFOHEADER), &dwRead, (LPOVERLAPPED)NULL );
+
+ // Allocate memory for the BITMAPINFO structure.
+ HGLOBAL infoHeaderMem = ::GlobalAlloc( GHND, sizeof(BITMAPINFOHEADER) + ((1<bmiHeader.biSize = bitmapInfoHeader.biSize;
+ lpHeaderMem->bmiHeader.biWidth = bitmapInfoHeader.biWidth;
+ lpHeaderMem->bmiHeader.biHeight = bitmapInfoHeader.biHeight;
+ lpHeaderMem->bmiHeader.biPlanes = bitmapInfoHeader.biPlanes;
+ lpHeaderMem->bmiHeader.biBitCount = bitmapInfoHeader.biBitCount;
+ lpHeaderMem->bmiHeader.biCompression = bitmapInfoHeader.biCompression;
+ lpHeaderMem->bmiHeader.biSizeImage = bitmapInfoHeader.biSizeImage;
+ lpHeaderMem->bmiHeader.biXPelsPerMeter = bitmapInfoHeader.biXPelsPerMeter;
+ lpHeaderMem->bmiHeader.biYPelsPerMeter = bitmapInfoHeader.biYPelsPerMeter;
+ lpHeaderMem->bmiHeader.biClrUsed = bitmapInfoHeader.biClrUsed;
+ lpHeaderMem->bmiHeader.biClrImportant = bitmapInfoHeader.biClrImportant;
+
+ // Retrieve the color table.
+ // 1 << bitmapInfoHeader.biBitCount == 2 ^ bitmapInfoHeader.biBitCount
+ ::ReadFile( hFile, lpHeaderMem->bmiColors, ((1<palVersion=0x300;
+ lpLogPalette->palNumEntries=256;
+
+ palData = (char*)lpHeaderMem->bmiColors;
+
+ for( i = 0; i < 256; i++ )
+ {
+ lpLogPalette->palPalEntry[i].peRed = *palData++;
+ lpLogPalette->palPalEntry[i].peGreen = *palData++;
+ lpLogPalette->palPalEntry[i].peBlue = *palData++;
+ lpLogPalette->palPalEntry[i].peFlags = *palData++;
+ }
+ hPal = ::CreatePalette( lpLogPalette );
+ delete [] lpLogPalette;
+
+ // Allocate memory for the required number of bytes.
+ hmem2 = ::GlobalAlloc( GHND, (bitmapHeader.bfSize - bitmapHeader.bfOffBits) );
+
+ lpvBits = ::GlobalLock( hmem2 );
+
+ // Retrieve the bitmap data.
+ ::ReadFile( hFile, lpvBits, (bitmapHeader.bfSize - bitmapHeader.bfOffBits), &dwRead, (LPOVERLAPPED)NULL );
+
+ // Create a bitmap from the data stored in the .BMP file.
+ hdc = ::GetDC( hWnd );
+ select = ::SelectPalette( hdc, hPal, 0 );
+ if( !select )
+ return false;
+ realize = ::RealizePalette( hdc );
+ if( realize == GDI_ERROR )
+ return false;
+
+ hBMP = ::CreateDIBitmap( hdc, &bitmapInfoHeader, CBM_INIT, lpvBits, lpHeaderMem, DIB_RGB_COLORS );
+ ::ReleaseDC( hWnd, hdc );
+
+ // Unlock the global memory objects and close the .BMP file.
+ ::GlobalUnlock( infoHeaderMem );
+ ::GlobalUnlock( hmem2 );
+ ::CloseHandle( hFile );
+
+ if( !hBMP )
+ return false;
+
+ return true;
+}
+
+
+bit8 BMP8::drawBmp(void)
+{
+ // Paint the window (and draw the bitmap).
+
+ PAINTSTRUCT ps;
+ HDC hdc;
+ char string[128];
+
+ InvalidateRect(WindowHandle_,NULL,FALSE); // keep windows from screwing up the
+ // redrawing (as much).
+ hdc=BeginPaint(WindowHandle_,&ps);
+
+ //Do palette stuff
+ HPALETTE select=SelectPalette(ps.hdc,PalHandle_,0);
+ if (select==NULL)
+ {
+ sprintf(string,"Select Pal Fail: %d",GetLastError());
+ MessageBox(NULL,string,"OK",MB_OK);
+ }
+ UINT realize=RealizePalette(ps.hdc);
+ if (realize==GDI_ERROR)
+ {
+ sprintf(string,"Realize Pal Fail: %d",GetLastError());
+ MessageBox(NULL,string,"OK",MB_OK);
+ }
+
+ HDC hdcMem = CreateCompatibleDC(ps.hdc);
+ SelectObject(hdcMem, BitmapHandle_);
+ BITMAP bm;
+ GetObject(BitmapHandle_, sizeof(BITMAP), (LPSTR) &bm);
+
+ /// for non-stretching version
+ ///////BitBlt(ps.hdc, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
+
+ RECT clientRect;
+ GetClientRect(WindowHandle_,&clientRect);
+ SetStretchBltMode(ps.hdc,COLORONCOLOR);
+ StretchBlt(ps.hdc,0,0,clientRect.right,clientRect.bottom,hdcMem,0,0,bm.bmWidth,
+ bm.bmHeight,SRCCOPY);
+
+
+ DeleteDC(hdcMem);
+ EndPaint(WindowHandle_,&ps);
+ return(TRUE);
+}
diff --git a/CODE/BMP8.H b/CODE/BMP8.H
new file mode 100644
index 0000000..25adc97
--- /dev/null
+++ b/CODE/BMP8.H
@@ -0,0 +1,43 @@
+/*
+** Command & Conquer Red Alert(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 .
+*/
+
+#ifndef BMP8_H
+#define BMP8_H
+
+//#include
+//#include
+//#include "wstypes.h"
+//#include "winblows.h"
+
+class BMP8
+{
+public:
+ BMP8() : hBMP( NULL ), hPal( NULL ), hWnd( NULL ) {}
+ ~BMP8();
+
+ bool Init( const char* szFile, HWND hWnd );
+ bool Draw(void); // call this from your WM_PAINT message
+
+private:
+ HBITMAP hBMP;
+ HPALETTE hPal;
+ HWND hWnd;
+};
+
+
+#endif
\ No newline at end of file
diff --git a/CODE/BUFF.CPP b/CODE/BUFF.CPP
new file mode 100644
index 0000000..fe9c29f
--- /dev/null
+++ b/CODE/BUFF.CPP
@@ -0,0 +1,220 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/BUFF.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : BUFF.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 07/29/96 *
+ * *
+ * Last Update : September 7, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * Buffer::Buffer -- Constructor for buffer object. *
+ * Buffer::Buffer -- Copy constructor for buffer object. *
+ * Buffer::Buffer -- Self-allocating constructor for buffer object. *
+ * Buffer::Reset -- Clears the buffer object to null state. *
+ * Buffer::operator = -- Assignment operator for the buffer object. *
+ * Buffer::~Buffer -- Destructor for buffer object. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#include "buff.h"
+#include
+
+
+/***********************************************************************************************
+ * Buffer::Buffer -- Constructor for buffer object. *
+ * *
+ * This is the normal constructor for a buffer object. The buffer pointer and size are *
+ * specified as parameters. *
+ * *
+ * INPUT: buffer -- Pointer to the buffer. *
+ * *
+ * size -- The size of the buffer. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: It is possible to construct a Buffer object that has a pointer but a size *
+ * value of zero. The Buffer object can still be used for its pointer, but it *
+ * any function that requires a size will fail. *
+ * *
+ * HISTORY: *
+ * 07/29/1996 JLB : Created. *
+ *=============================================================================================*/
+Buffer::Buffer(void * buffer, long size) :
+ BufferPtr(buffer),
+ Size(size),
+ IsAllocated(false)
+{
+}
+
+
+// Alternate constructor for char * pointer.
+Buffer::Buffer(char * buffer, long size) :
+ BufferPtr(buffer),
+ Size(size),
+ IsAllocated(false)
+{
+}
+
+
+// Alternate constructor for void const * pointer.
+Buffer::Buffer(void const * buffer, long size) :
+ BufferPtr((void*)buffer),
+ Size(size),
+ IsAllocated(false)
+{
+}
+
+
+/***********************************************************************************************
+ * Buffer::Buffer -- Self-allocating constructor for buffer object. *
+ * *
+ * This construtor for a buffer object will automatically allocate the bytes necessary *
+ * to fulfill the size requested. This object is also responsible for deleting the buffer *
+ * it allocated. *
+ * *
+ * INPUT: size -- The size of the buffer to allocated. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: There is no way to tell if the allocation failed. To verify, call Get_Buffer *
+ * and compare with NULL. *
+ * *
+ * HISTORY: *
+ * 07/29/1996 JLB : Created. *
+ *=============================================================================================*/
+Buffer::Buffer(long size) :
+ BufferPtr(NULL),
+ Size(size),
+ IsAllocated(false)
+{
+ if (size > 0) {
+ BufferPtr = new char[size];
+ IsAllocated = true;
+ }
+}
+
+
+/***********************************************************************************************
+ * Buffer::Buffer -- Copy constructor for buffer object. *
+ * *
+ * This will make a duplicate of the specified buffer object. The ownership of the pointer *
+ * remains with the original object. This prevents multiple deletion of the same pointer. *
+ * *
+ * INPUT: buffer -- Reference to the buffer object to be dupilcated. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/02/1996 JLB : Created. *
+ *=============================================================================================*/
+Buffer::Buffer(Buffer const & buffer) :
+ IsAllocated(false)
+{
+ BufferPtr = buffer.BufferPtr;
+ Size = buffer.Size;
+}
+
+
+/***********************************************************************************************
+ * Buffer::operator = -- Assignment operator for the buffer object. *
+ * *
+ * This will make a duplicate of the buffer object specified. Any buffer pointed to by the *
+ * left hand buffer will be lost (possibley freed as a result). *
+ * *
+ * INPUT: buffer -- Reference to the right hand buffer object. *
+ * *
+ * OUTPUT: Returns with a reference to the copied buffer object. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/02/1996 JLB : Created. *
+ *=============================================================================================*/
+Buffer & Buffer::operator = (Buffer const & buffer)
+{
+ if (buffer != this) {
+ if (IsAllocated) {
+ delete [] BufferPtr;
+ }
+ IsAllocated = false;
+ BufferPtr = buffer.BufferPtr;
+ Size = buffer.Size;
+ }
+ return(*this);
+}
+
+
+/***********************************************************************************************
+ * Buffer::~Buffer -- Destructor for buffer object. *
+ * *
+ * This destructor will free any buffer it is responsible for. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/29/1996 JLB : Created. *
+ *=============================================================================================*/
+Buffer::~Buffer(void)
+{
+ Reset();
+}
+
+
+/***********************************************************************************************
+ * Buffer::Reset -- Clears the buffer object to null state. *
+ * *
+ * This routine will bring the buffer object into a null (newly constructed) state. If *
+ * there was any buffer allocated or referred to by this object, it will be freed or *
+ * dereferenced as necessary. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: This routine will free the buffer if it is responsible for doing so when *
+ * it is no longer referenced. *
+ * *
+ * HISTORY: *
+ * 09/07/1996 JLB : Created. *
+ *=============================================================================================*/
+void Buffer::Reset(void)
+{
+ if (IsAllocated) {
+ delete [] BufferPtr;
+ }
+ BufferPtr = NULL;
+ Size = 0;
+ IsAllocated = false;
+}
diff --git a/CODE/BUFF.H b/CODE/BUFF.H
new file mode 100644
index 0000000..1beca3f
--- /dev/null
+++ b/CODE/BUFF.H
@@ -0,0 +1,99 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/BUFF.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : BUFF.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 07/29/96 *
+ * *
+ * Last Update : July 29, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#ifndef CCBUFF_H
+#define CCBUFF_H
+
+
+/*
+** The "bool" integral type was defined by the C++ committee in
+** November of '94. Until the compiler supports this, use the following
+** definition.
+*/
+#ifndef __BORLANDC__
+#ifndef TRUE_FALSE_DEFINED
+#define TRUE_FALSE_DEFINED
+enum {false=0,true=1};
+typedef int bool;
+#endif
+#endif
+
+/*
+** A general purpose buffer pointer handler object. It holds not only the pointer to the
+** buffer, but its size as well. By using this class instead of separate pointer and size
+** values, function interfaces and algorithms become simpler to manage and understand.
+*/
+class Buffer {
+ public:
+ Buffer(char * ptr, long size=0);
+ Buffer(void * ptr=0, long size=0);
+ Buffer(void const * ptr, long size=0);
+ Buffer(long size);
+ Buffer(Buffer const & buffer);
+ ~Buffer(void);
+
+ Buffer & operator = (Buffer const & buffer);
+ operator void * (void) const {return(BufferPtr);}
+ operator char * (void) const {return((char *)BufferPtr);}
+
+ void Reset(void);
+ void * Get_Buffer(void) const {return(BufferPtr);}
+ long Get_Size(void) const {return(Size);}
+ bool Is_Valid(void) const {return(BufferPtr != 0);}
+
+ protected:
+
+ /*
+ ** Pointer to the buffer memory.
+ */
+ void * BufferPtr;
+
+ /*
+ ** The size of the buffer memory.
+ */
+ long Size;
+
+ /*
+ ** Was the buffer allocated by this class? If so, then this class
+ ** will be responsible for freeing the buffer.
+ */
+ bool IsAllocated;
+};
+
+
+#endif
diff --git a/CODE/BUFFERX.H b/CODE/BUFFERX.H
new file mode 100644
index 0000000..a9e1f82
--- /dev/null
+++ b/CODE/BUFFERX.H
@@ -0,0 +1,101 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/BUFFERX.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : BUFFER.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 05/04/96 *
+ * *
+ * Last Update : May 4, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#ifndef BUFFERx_H
+#define BUFFERx_H
+
+#include "wwfile.h"
+
+/*
+** This is a transmuter interface designed to aid implementation of compression, encryption, or
+** data analysis classes. The Transmuter should be derived into a class that performs the necessary
+** processing.
+*/
+class Transmuter {
+ public:
+ Transmuter(void) : Output(0) {}
+ virtual ~Transmuter(void) {}
+
+ /*
+ ** These are the interface function that are used to pass data to the transmuter. The
+ ** default implementation of these functions do nothing other than pass the data onto
+ ** the subsequent transmuter. For practical use, these functions should be overloaded to
+ ** do something more useful.
+ */
+ virtual void Attach(Transmuter * transmuter) {Output = transmuter;}
+ virtual void Flush(void) {if (Output) Output->Flush();}
+ virtual void Put(const void * input, unsigned length) {if (Output) Output->Put(input, length);}
+
+ protected:
+
+ /*
+ ** Pointer to the output transmuter.
+ */
+ Transmuter * Output;
+};
+
+
+class FileTransmuter {
+ public:
+ FileTransmuter(FileClass * file = NULL) : OutputFile(file) {}
+
+ virtual void Attach(FileClass * file) {OutputFile = file;}
+ virtual void Flush(void) {}
+ virtual void Put(const void * input, unsigned length) {if (OutputFile) OutputFile->Write(input, length);}
+
+ protected:
+ FileClass * OutputFile;
+};
+
+
+class BufferTransmuter {
+ public:
+ BufferTransmuter(void * buffer = NULL) : BufferPtr(buffer) {}
+
+ virtual void Attach(void * buffer) {BufferPtr = buffer;}
+ virtual void Flush(void) {}
+ virtual void Put(const void * input, unsigned length) {if (BufferPtr) {memcpy(BufferPtr, input, length);((char *&)BufferPtr) += length;}}
+
+ protected:
+ void * BufferPtr;
+};
+
+
+#endif
+
+
diff --git a/CODE/BUGS.TXT b/CODE/BUGS.TXT
new file mode 100644
index 0000000..418c724
--- /dev/null
+++ b/CODE/BUGS.TXT
@@ -0,0 +1,64 @@
+Fixed:
+A415,A436,A437,A446,A447,A453,A466,A475,A488,
+B171,B326,B407,B443,B530,B550,B566,B578,B600,
+B609,B626,B628,B632,B637,B641,B644,B655,B658,
+B675,B678,B684,B685,B688,B690,B698,B711,B715,
+B726,B730,B731,B729,B732,B733,B738,B743,B744,
+B747,B748,B750,B753,B759,B762,B766,B767,B768,
+B770,B774,B775,B777,B778,B780,B781,B786,B795,
+B797,B798,B805,C1001,C1007,C1011,C1012,C1014,
+C1017,C1018,C1020,C1024,C1031,C1037,C1042,C1045,
+C1052,C1061,C1054,C288,C487,C543,C546,
+C666,C671,C697,C702,C773,C841,C856,C869,C871,
+C887,C888,C904,C919,C922,C923,C925,C926
+C927,C930,C932,C936,C938,C939,C942,C945,C947,
+C953,C954,C962,C966,C967,C968,C975,C976,C977
+C981,C982
+
+Unsolved:
+A035,A248,A352,A383,A417,A419,A420,A427,A445,A449,
+A450,A455,A456,A457,A469,A470,A477,A478,A480,B005,
+B042,B206,B207,B247,B284,B356,B418,B430,B467,B536,
+B548,B549,B559,B633,B674,B686,B703,B714,B725,B740,
+B745,B785,B787,B794,B796,B799,B802,B809,B817,B823,
+C033,C072,C1000,C1002,C1004,C1006,C1008,C1009,C1013,C1016,
+C1022,C1026,C1028,C1029,C1030,C1047,C1053,C1054,C1056,C1057,
+C1059,C197,C318,C407,C409,C453,C502,C527,C530,C551,
+C554,C573,C606,C676,C682,C694,C750,C776,C793,C811,
+C821,C834,C867,C872,C877,C881,C896,C898,C901,C921,
+C928,C929,C931,C933,C934,C940,C941,C943,C944,C946,
+C952,C961,C964,C965,C969,C971,C794,C987,C988,C992,
+C997
+
+Old / Need more info / non-repeatable (maybe it's fixed now?):
+A380,A388,A401,A414,A482,B445,B791,
+A342,A402,A413,A434,A443,A460,A461,A462,A463,A468,
+A472,A473,A476,A485,A489,A490,B1044,B513,B524,B608
+B721,B773,B789,B807,B812,C570,C633,C654,C744,C778,
+C853,C913,C963,C991,C998
+
+Modem/Net Bug (for Bill):
+A431,A452,A454,A464,A471,A481,A483,A486,A487,
+B742,B760,B761,B764,B765,B772,B792,C1025,C1036,C1038
+C883,C886,C895,C950,C951,C956,C993,C995
+
+Design Bug (for Erik):
+A432,A433,A448,A479,B041,B414,B659,B717,B719,B720,
+B722,B723,B727,B728,B735,B736,B737,B741,
+B756,B757,B758,B784,B802,B803,B808,B810,B815,
+B816,B818,B822,C1010,C1023,C1027,C1035,C1044,
+C1062,C534,C568,C637,C663,C918,C920,C924,C948,
+C949,C986,C996,C999
+
+Setup/Install/README.TXT Bug:
+A458,A467,A484,B395,C957,C958,C959
+
+Not Bug, repeated bug, suggestion, can't fix, or huh?:
+A459,B1024,B218,B507,B522,B541,B546,B592,B593,B596,
+B687,B724,B739,B746,B749,B763,B776,B779,B782,B783,
+B790,B793,B806,B819,B820,B821,C1003,C1005,C1015,C1019,
+C1032,C1033,C1034,C1039,C1040,C1043,C1048,
+C1050,C1051,C1055,C1058,C1060,C1063,C336,C377,
+C491,C537,C541,C578,C691,C703,C716,C781,C792,C838,
+C854,C860,C879,C912,C915,C935,C937,C955,C970,C978,
+C979,C980,C983,C984,C985,C990
\ No newline at end of file
diff --git a/CODE/BUILDING.CPP b/CODE/BUILDING.CPP
new file mode 100644
index 0000000..b47c8b2
--- /dev/null
+++ b/CODE/BUILDING.CPP
@@ -0,0 +1,5712 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/BUILDING.CPP 5 3/13/97 5:18p Joe_b $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : BUILDING.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : September 10, 1993 *
+ * *
+ * Last Update : October 27, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * BuildingClass::AI -- Handles non-graphic AI processing for buildings. *
+ * BuildingClass::Active_Click_With -- Handles cell selection for buildings. *
+ * BuildingClass::Animation_AI -- Handles normal building animation processing. *
+ * BuildingClass::Assign_Target -- Assigns a target to the building. *
+ * BuildingClass::Begin_Mode -- Begins an animation mode for the building. *
+ * BuildingClass::BuildingClass -- Constructor for buildings. *
+ * BuildingClass::Can_Demolish -- Can the player demolish (sell back) the building? *
+ * BuildingClass::Can_Enter_Cell -- Determines if building can be placed down. *
+ * BuildingClass::Can_Fire -- Determines if this building can fire. *
+ * BuildingClass::Can_Player_Move -- Can this building be moved? *
+ * BuildingClass::Captured -- Captures the building. *
+ * BuildingClass::Center_Coord -- Fetches the center coordinate for the building. *
+ * BuildingClass::Charging_AI -- Handles the special charging logic for Tesla coils. *
+ * BuildingClass::Check_Point -- Fetches the landing checkpoint for the given flight pattern.*
+ * BuildingClass::Click_With -- Handles clicking on the map while the building is selected. *
+ * BuildingClass::Crew_Type -- This determines the crew that this object generates. *
+ * BuildingClass::Death_Announcement -- Announce the death of this building. *
+ * BuildingClass::Debug_Dump -- Displays building status to the monochrome screen. *
+ * BuildingClass::Detach -- Handles target removal from the game system. *
+ * BuildingClass::Detach_All -- Possibly abandons production according to factory type. *
+ * BuildingClass::Docking_Coord -- Fetches the coordinate to use for docking. *
+ * BuildingClass::Draw_It -- Displays the building at the location specified. *
+ * BuildingClass::Drop_Debris -- Drops rubble when building is destroyed. *
+ * BuildingClass::Enter_Idle_Mode -- The building will enter its idle mode. *
+ * BuildingClass::Exit_Coord -- Determines location where object will leave it. *
+ * BuildingClass::Exit_Object -- Initiates an object to leave the building. *
+ * BuildingClass::Factory_AI -- Handle factory production and initiation. *
+ * BuildingClass::Find_Exit_Cell -- Find a clear location to exit an object from this buildin*
+ * BuildingClass::Fire_Direction -- Fetches the direction of firing. *
+ * BuildingClass::Fire_Out -- Handles when attached animation expires. *
+ * BuildingClass::Flush_For_Placement -- Handles clearing a zone for object placement. *
+ * BuildingClass::Get_Image_Data -- Fetch the image pointer for the building. *
+ * BuildingClass::Grand_Opening -- Handles construction completed special operations. *
+ * BuildingClass::Greatest_Threat -- Searches for target that building can fire upon. *
+ * BuildingClass::How_Many_Survivors -- This determine the maximum number of survivors. *
+ * BuildingClass::Init -- Initialize the building system to an empty null state. *
+ * BuildingClass::Limbo -- Handles power adjustment as building goes into limbo. *
+ * BuildingClass::Mark -- Building interface to map rendering system. *
+ * BuildingClass::Mission_Attack -- Handles attack mission for building. *
+ * BuildingClass::Mission_Construction -- Handles mission construction. *
+ * BuildingClass::Mission_Deconstruction -- Handles building deconstruction. *
+ * BuildingClass::Mission_Guard -- Handles guard mission for combat buildings. *
+ * BuildingClass::Mission_Harvest -- Handles refinery unloading harvesters. *
+ * BuildingClass::Mission_Missile -- State machine for nuclear missile launch. *
+ * BuildingClass::Mission_Repair -- Handles the repair (active) state for building. *
+ * BuildingClass::Mission_Unload -- Handles the unload mission for a building. *
+ * BuildingClass::Pip_Count -- Determines "full" pips to display for building. *
+ * BuildingClass::Power_Output -- Fetches the current power output from this building. *
+ * BuildingClass::Read_INI -- Reads buildings from INI file. *
+ * BuildingClass::Receive_Message -- Handle an incoming message to the building. *
+ * BuildingClass::Remap_Table -- Fetches the remap table to use for this building. *
+ * BuildingClass::Remove_Gap_Effect -- Stop a gap generator from jamming cells *
+ * BuildingClass::Repair -- Initiates or terminates the repair process. *
+ * BuildingClass::Repair_AI -- Handle the repair (and sell) logic for the building. *
+ * BuildingClass::Revealed -- Reveals the building to the specified house. *
+ * BuildingClass::Rotation_AI -- Process any turret rotation required of this building. *
+ * BuildingClass::Sell_Back -- Controls the sell back (demolish) operation. *
+ * BuildingClass::Shape_Number -- Fetch the shape number for this building. *
+ * BuildingClass::Sort_Y -- Returns the building coordinate used for sorting. *
+ * BuildingClass::Take_Damage -- Inflicts damage points upon a building. *
+ * BuildingClass::Target_Coord -- Return the coordinate to use when firing on this building. *
+ * BuildingClass::Toggle_Primary -- Toggles the primary factory state. *
+ * BuildingClass::Turret_Facing -- Fetches the turret facing for this building. *
+ * BuildingClass::Unlimbo -- Removes a building from limbo state. *
+ * BuildingClass::Update_Buildables -- Informs sidebar of additional construction options. *
+ * BuildingClass::Value -- Determine the value of this building. *
+ * BuildingClass::What_Action -- Determines action to perform if click on specified object. *
+ * BuildingClass::What_Action -- Determines what action will occur. *
+ * BuildingClass::Write_INI -- Write out the building data to the INI file specified. *
+ * BuildingClass::delete -- Deallocates building object. *
+ * BuildingClass::new -- Allocates a building object from building pool. *
+ * BuildingClass::~BuildingClass -- Destructor for building type objects. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+
+
+enum SAMState {
+ SAM_READY, // Launcher can be facing any direction tracking targets.
+ SAM_FIRING // Stationary while missile is being fired.
+};
+
+
+/***************************************************************************
+** Center of building offset table.
+*/
+COORDINATE const BuildingClass::CenterOffset[BSIZE_COUNT] = {
+ 0x00800080L,
+ 0x008000FFL,
+ 0x00FF0080L,
+ 0x00FF00FFL,
+ 0x018000FFL,
+ 0x00FF0180L,
+ 0x01800180L,
+
+ 0x00FF0200L,
+
+ 0x02800280L,
+};
+
+
+/***********************************************************************************************
+ * BuildingClass::Receive_Message -- Handle an incoming message to the building. *
+ * *
+ * This routine handles an incoming message to the building. Messages regulate the *
+ * various cooperative ventures between buildings and units. This might include such *
+ * actions as coordinating the construction yard animation with the actual building's *
+ * construction animation. *
+ * *
+ * INPUT: from -- The originator of the message received. *
+ * *
+ * message -- The radio message received. *
+ * *
+ * param -- Reference to an optional parameter that might be used to return *
+ * extra information to the message originator. *
+ * *
+ * OUTPUT: Returns with the response to the message (typically, this is just RADIO_OK). *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/09/1994 JLB : Created. *
+ * 06/26/1995 JLB : Forces refinery load anim to start immediately. *
+ * 08/13/1995 JLB : Uses ScenarioInit for special loose "CAN_LOAD" check. *
+ *=============================================================================================*/
+RadioMessageType BuildingClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param)
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ switch (message) {
+
+ /*
+ ** This message is received as a request to attach/load/dock with this building.
+ ** Verify that this is allowed and return the appropriate response.
+ */
+ case RADIO_CAN_LOAD:
+ TechnoClass::Receive_Message(from, message, param);
+ if (!House->Is_Ally(from)) return(RADIO_STATIC);
+ if (Mission == MISSION_CONSTRUCTION || Mission == MISSION_DECONSTRUCTION || BState == BSTATE_CONSTRUCTION || (!ScenarioInit && In_Radio_Contact() && Contact_With_Whom() != from)) return(RADIO_NEGATIVE);
+ switch (Class->Type) {
+ case STRUCT_AIRSTRIP:
+ if (from->What_Am_I() == RTTI_AIRCRAFT && ((AircraftClass const *)from)->Class->IsFixedWing) {
+ return(RADIO_ROGER);
+ }
+ break;
+
+ case STRUCT_HELIPAD:
+ if (from->What_Am_I() == RTTI_AIRCRAFT && !((AircraftClass const *)from)->Class->IsFixedWing) {
+ return(RADIO_ROGER);
+ }
+ break;
+
+ case STRUCT_REPAIR:
+ if (from->What_Am_I() == RTTI_UNIT || (from->What_Am_I() == RTTI_AIRCRAFT)) {
+ if (Transmit_Message(RADIO_ON_DEPOT, from) != RADIO_ROGER) {
+ return(RADIO_ROGER);
+ }
+ }
+ return(RADIO_NEGATIVE);
+
+ case STRUCT_REFINERY:
+ if (from->What_Am_I() == RTTI_UNIT &&
+ *((UnitClass *)from) == UNIT_HARVESTER &&
+ (ScenarioInit || !Is_Something_Attached())) {
+
+ return(RADIO_ROGER);
+ }
+ break;
+
+ default:
+ break;
+ }
+ return(RADIO_STATIC);
+
+ /*
+ ** This message is received when the object has attached itself to this
+ ** building.
+ */
+ case RADIO_IM_IN:
+ if (Mission == MISSION_DECONSTRUCTION) {
+ return(RADIO_NEGATIVE);
+ }
+ switch (Class->Type) {
+ case STRUCT_REPAIR:
+ IsReadyToCommence = true;
+ Assign_Mission(MISSION_REPAIR);
+ from->Assign_Mission(MISSION_SLEEP);
+ return(RADIO_ROGER);
+
+ case STRUCT_AIRSTRIP:
+ case STRUCT_HELIPAD:
+ Assign_Mission(MISSION_REPAIR);
+ from->Assign_Mission(MISSION_SLEEP);
+ return(RADIO_ROGER);
+
+ case STRUCT_REFINERY:
+ Mark(MARK_CHANGE);
+ from->Assign_Mission(MISSION_UNLOAD);
+ return(RADIO_ROGER);
+
+ default:
+ break;
+ }
+ break;
+
+ /*
+ ** Docking maneuver maintenance message. See if new order should be given to the
+ ** unit trying to dock.
+ */
+ case RADIO_DOCKING:
+ TechnoClass::Receive_Message(from, message, param);
+
+ /*
+ ** When in radio contact for loading, the refinery starts
+ ** flashing the lights.
+ */
+ if (*this == STRUCT_REFINERY && BState != BSTATE_FULL) {
+ Begin_Mode(BSTATE_FULL);
+ }
+
+ /*
+ ** If this building is already in radio contact, then it might
+ ** be able to satisfy the request to load by bumping off any
+ ** preoccupying task.
+ */
+ if (*this == STRUCT_REPAIR) {
+ if (Contact_With_Whom() != from) {
+ if (Transmit_Message(RADIO_ON_DEPOT) == RADIO_ROGER) {
+ if (Transmit_Message(RADIO_NEED_REPAIR) == RADIO_NEGATIVE) {
+ Transmit_Message(RADIO_RUN_AWAY);
+ return(RADIO_ROGER);
+ }
+// } else {
+// if (Transmit_Message(RADIO_NEED_TO_MOVE, from) == RADIO_ROGER) {
+// param = (long)As_Target();
+// Transmit_Message(RADIO_MOVE_HERE, param, from);
+// }
+ }
+ } else {
+ if (Transmit_Message(RADIO_NEED_REPAIR) == RADIO_NEGATIVE) {
+ return(RADIO_NEGATIVE);
+ }
+ }
+ }
+
+ /*
+ ** Establish contact with the object if this building isn't already in contact
+ ** with another.
+ */
+ if (!In_Radio_Contact()) {
+ Transmit_Message(RADIO_HELLO, from);
+ }
+
+ if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) {
+ switch (Class->Type) {
+ case STRUCT_AIRSTRIP:
+ param = As_Target();
+ break;
+
+ case STRUCT_HELIPAD:
+ param = As_Target();
+ break;
+
+ case STRUCT_REPAIR:
+ Transmit_Message(RADIO_TETHER);
+ param = ::As_Target(Coord_Cell(Center_Coord()));
+ break;
+
+ case STRUCT_REFINERY:
+ param = ::As_Target(Coord_Cell(Adjacent_Cell(Center_Coord(), DIR_S)));
+ break;
+ }
+
+ /*
+ ** Tell the harvester to move to the docking pad of the building.
+ */
+ if (Transmit_Message(RADIO_MOVE_HERE, param) == RADIO_YEA_NOW_WHAT) {
+
+ /*
+ ** Since the harvester is already there, tell it to begin the backup
+ ** procedure now. If it can't, then tell it to get outta here.
+ */
+ Transmit_Message(RADIO_TETHER);
+ if (*this == STRUCT_REFINERY && Transmit_Message(RADIO_BACKUP_NOW, from) != RADIO_ROGER) {
+ from->Scatter(NULL, true, true);
+ }
+ }
+ }
+ return(RADIO_ROGER);
+
+ /*
+ ** If a transport or harvester is requesting permission to head toward, dock
+ ** and load/unload, check to make sure that this is allowed given the current
+ ** state of the building.
+ */
+ case RADIO_ARE_REFINERY:
+ if (Is_Something_Attached() || In_Radio_Contact() || IsInLimbo || House->Class->House != from->Owner() || (*this != STRUCT_REFINERY/* && *this != STRUCT_REPAIR*/)) {
+ return(RADIO_NEGATIVE);
+ }
+ return(RADIO_ROGER);
+
+ /*
+ ** Someone is telling us that it is starting construction. This should only
+ ** occur if this is a construction yard and a building was just placed on
+ ** the map.
+ */
+ case RADIO_BUILDING:
+ Assign_Mission(MISSION_REPAIR);
+ TechnoClass::Receive_Message(from, message, param);
+ return(RADIO_ROGER);
+
+ /*
+ ** Someone is telling us that they have finished construction. This should
+ ** only occur if this is a construction yard and the building that was being
+ ** constructed has finished. In this case, stop the construction yard
+ ** animation.
+ */
+ case RADIO_COMPLETE:
+ if (Mission != MISSION_DECONSTRUCTION) {
+ Assign_Mission(MISSION_GUARD);
+ }
+ TechnoClass::Receive_Message(from, message, param);
+ return(RADIO_ROGER);
+
+ /*
+ ** This message may occur unexpectedly if the unit in contact with this
+ ** building is suddenly destroyed. Handle any cleanup necessary. For example,
+ ** a construction yard should stop its construction animation in this case.
+ */
+ case RADIO_OVER_OUT:
+ Begin_Mode(BSTATE_IDLE);
+ TechnoClass::Receive_Message(from, message, param);
+ return(RADIO_ROGER);
+
+ /*
+ ** This message is received when an object has completely left
+ ** building. Sometimes special cleanup action is required when
+ ** this event occurs.
+ */
+ case RADIO_UNLOADED:
+ if (*this == STRUCT_REPAIR) {
+ if (Distance(from) < 0x0180) {
+ return(RADIO_ROGER);
+ }
+ }
+ TechnoClass::Receive_Message(from, message, param);
+ if (*this == STRUCT_WEAP || *this == STRUCT_AIRSTRIP || *this == STRUCT_REPAIR) return(RADIO_RUN_AWAY);
+ return(RADIO_ROGER);
+
+ default:
+ break;
+ }
+
+ /*
+ ** Pass along the message to the default message handler in the radio itself.
+ */
+ return(TechnoClass::Receive_Message(from, message, param));
+}
+
+
+#ifdef CHEAT_KEYS
+/***********************************************************************************************
+ * BuildingClass::Debug_Dump -- Displays building status to the monochrome screen. *
+ * *
+ * This utility function will output the current status of the building class to the *
+ * monochrome screen. It is through this data that bugs may be fixed or detected. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/31/1994 JLB : Created. *
+ *=============================================================================================*/
+void BuildingClass::Debug_Dump(MonoClass * mono) const
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ mono->Set_Cursor(0, 0);
+ mono->Print(Text_String(TXT_DEBUG_BUILDING));
+ mono->Fill_Attrib(66, 13, 12, 1, IsRepairing ? MonoClass::INVERSE : MonoClass::NORMAL);
+ mono->Fill_Attrib(66, 14, 12, 1, IsToRebuild ? MonoClass::INVERSE : MonoClass::NORMAL);
+ mono->Fill_Attrib(66, 15, 12, 1, IsAllowedToSell ? MonoClass::INVERSE : MonoClass::NORMAL);
+ mono->Fill_Attrib(66, 16, 12, 1, IsCharging ? MonoClass::INVERSE : MonoClass::NORMAL);
+ mono->Fill_Attrib(66, 17, 12, 1, IsCharged ? MonoClass::INVERSE : MonoClass::NORMAL);
+ mono->Fill_Attrib(66, 18, 12, 1, IsJamming ? MonoClass::INVERSE : MonoClass::NORMAL);
+ mono->Fill_Attrib(66, 19, 12, 1, IsJammed ? MonoClass::INVERSE : MonoClass::NORMAL);
+
+ mono->Set_Cursor(1, 11);
+ if (Factory) {
+ mono->Printf("%s %d%%", Factory->Get_Object()->Class_Of().IniName, (100*Factory->Completion())/FactoryClass::STEP_COUNT);
+ }
+
+ TechnoClass::Debug_Dump(mono);
+}
+#endif
+
+
+/***********************************************************************************************
+ * BuildingClass::Draw_It -- Displays the building at the location specified. *
+ * *
+ * This is the low level graphic routine that displays the building at the location *
+ * specified. *
+ * *
+ * INPUT: x,y -- The coordinate to draw the building at. *
+ * *
+ * window -- The clipping window to use. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/20/1994 JLB : Created. *
+ * 06/27/1994 JLB : Takes a clipping window parameter. *
+ * 07/06/1995 JLB : Handles damaged silos correctly. *
+ *=============================================================================================*/
+void BuildingClass::Draw_It(int x, int y, WindowNumberType window) const
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ /*
+ ** The shape file to use for rendering depends on whether the building
+ ** is undergoing construction or not.
+ */
+ void const * shapefile = Get_Image_Data();
+ if (shapefile == NULL) return;
+
+ /*
+ ** Actually draw the building shape.
+ */
+ IsTheaterShape = Class->IsTheater; //Let Build_Frame know if this is a theater specific shape
+ Techno_Draw_Object(shapefile, Shape_Number(), x, y, window);
+ IsTheaterShape = false;
+
+ /*
+ ** Patch for adding overlay onto weapon factory. Only add the overlay if
+ ** the building has more than 1 hp. Also, if the building's in radio
+ ** contact, he must be unloading a constructed vehicle, so draw that
+ ** vehicle before drawing the overlay.
+ */
+ if (BState != BSTATE_CONSTRUCTION) {
+
+ /*
+ ** A Tethered object is always rendered AFTER the building.
+ */
+ if (IsTethered && In_Radio_Contact() && !Contact_With_Whom()->IsInLimbo && Contact_With_Whom()->What_Am_I() != RTTI_BUILDING) {
+ TechnoClass * contact = Contact_With_Whom();
+
+ assert(contact->IsActive);
+ int xxx = x + ((int)Lepton_To_Pixel((int)Coord_X(contact->Render_Coord())) - (int)Lepton_To_Pixel((int)Coord_X(Render_Coord())));
+ int yyy = y + ((int)Lepton_To_Pixel((int)Coord_Y(contact->Render_Coord())) - (int)Lepton_To_Pixel((int)Coord_Y(Render_Coord())));
+ contact->Draw_It(xxx, yyy, window);
+ contact->IsToDisplay = false;
+ }
+
+ /*
+ ** Draw the weapon factory custom overlay graphic.
+ */
+ if ( (*this == STRUCT_WEAP || *this == STRUCT_FAKEWEAP)) {
+ int shapenum = Door_Stage();
+ if (Health_Ratio() <= Rule.ConditionYellow) shapenum += 4;
+ Techno_Draw_Object(Class->WarFactoryOverlay, shapenum, x, y, window);
+ }
+
+ /*
+ ** Draw any repair feedback graphic required.
+ */
+ if (IsRepairing && IsWrenchVisible) {
+ CC_Draw_Shape(ObjectTypeClass::SelectShapes, SELECT_WRENCH, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL);
+ }
+ }
+
+ TechnoClass::Draw_It(x, y, window);
+
+ /*
+ ** If this is a factory that we're spying on, show what it's producing
+ */
+ if (SpiedBy & (1<<(PlayerPtr->Class->House)) && IsSelected) {
+
+ /*
+ ** Fetch the factory that is associate with this building. For computer controlled buildings, the
+ ** factory pointer is integral to the building itself. For human controlled buildings, the factory
+ ** pointer is part of the house structure and must be retrieved from there.
+ */
+ FactoryClass * factory = NULL;
+ if (House->IsHuman) {
+ factory = House->Fetch_Factory(Class->ToBuild);
+ } else {
+ factory = Factory;
+ }
+
+ /*
+ ** If there is a factory associated with this building, then fetch any attached
+ ** object under production and display its cameo image over the top of this building.
+ */
+ if (factory != NULL) {
+ TechnoClass * obj = factory->Get_Object();
+ if (obj != NULL) {
+#ifdef FIXIT_CSII
+ CC_Draw_Shape(obj->Techno_Type_Class()->Get_Cameo_Data(), 0, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_NORMAL, NULL);
+#else
+ void const * remapper = obj->House->Remap_Table(false, obj->Techno_Type_Class()->Remap);
+ CC_Draw_Shape(obj->Techno_Type_Class()->Get_Cameo_Data(), 0, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL | ((remapper != NULL) ? SHAPE_FADING : SHAPE_NORMAL), remapper);
+#endif
+ }
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Shape_Number -- Fetch the shape number for this building. *
+ * *
+ * This routine will examine the current state of the building and return with the shape *
+ * number to use. The shape number is subordinate to the building graphic image data. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the shape number to use when rendering this building. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/29/1996 JLB : Created. *
+ *=============================================================================================*/
+int BuildingClass::Shape_Number(void) const
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ int shapenum = Fetch_Stage();
+
+ /*
+ ** The shape file to use for rendering depends on whether the building
+ ** is undergoing construction or not.
+ */
+ if (BState == BSTATE_CONSTRUCTION) {
+
+ /*
+ ** If the building is deconstructing, then the display frame progresses
+ ** from the end to the beginning. Reverse the shape number accordingly.
+ */
+ if (Mission == MISSION_DECONSTRUCTION) {
+ shapenum = (Class->Anims[BState].Start+Class->Anims[BState].Count-1)-shapenum;
+ }
+
+ } else {
+
+ /*
+ ** If this is a camouflaged pill box and it is not owned by the player, then
+ ** it is displayed with the MEGA-camouflaged imagery.
+ */
+ if ((!IsOwnedByPlayer) && (*this == STRUCT_CAMOPILLBOX)) {
+ shapenum += 1;
+ }
+
+ /*
+ ** The Tesla Coil has a stage value that can be overridden by
+ ** its current state.
+ */
+ if (*this == STRUCT_TESLA) {
+ if (IsCharged) {
+ shapenum = 3;
+ } else {
+ if (IsCharging) {
+ shapenum = Fetch_Stage();
+ } else {
+ shapenum = 0;
+ }
+ }
+ }
+
+ /*
+ ** Buildings that contain a turret handle their shape determination
+ ** differently than normal buildings. They need to take into consideration
+ ** the direction the turret is facing.
+ */
+ if (Class->IsTurretEquipped) {
+ shapenum = UnitClass::BodyShape[Dir_To_32(PrimaryFacing.Current())];
+
+ if (*this == STRUCT_SAM) {
+
+ /*
+ ** SAM sites that are free to rotate fetch their animation frame
+ ** from the building's turret facing. All other animation stages
+ ** fetch their frame from the embedded animation sequencer.
+ */
+// if (Status == SAM_READY || Status == SAM_FIRING || Status == SAM_LOCKING) {
+// shapenum = Fetch_Stage();
+// }
+ if (Health_Ratio() <= Rule.ConditionYellow) {
+ shapenum += 35;
+ }
+ } else {
+ if (IsInRecoilState) {
+ shapenum += 32;
+ }
+ if (Health_Ratio() <= Rule.ConditionYellow) {
+ shapenum += 64;
+ }
+ }
+ } else {
+
+ /*
+ ** If it is a significantly damaged weapons factory, it is shown in
+ ** the worst state possible.
+ */
+ if (*this == STRUCT_WEAP || *this == STRUCT_FAKEWEAP) {
+ shapenum = 0;
+ if (Health_Ratio() <= Rule.ConditionYellow) {
+ shapenum = 1;
+ }
+
+ } else {
+
+ /*
+ ** Special render stage for silos. The stage is dependent on the current
+ ** Tiberium collected as it relates to Tiberium capacity.
+ */
+ if (*this == STRUCT_STORAGE) {
+
+ int level = 0;
+ if (House->Capacity) {
+ level = (House->Tiberium * 5) / House->Capacity;
+ }
+
+ shapenum += Bound(level, 0, 4);
+ if (Health_Ratio() <= Rule.ConditionYellow) {
+ shapenum += 5;
+ }
+
+ } else {
+
+ /*
+ ** If below half strenth, then show the damage frames of the
+ ** building.
+ */
+ if (Health_Ratio() <= Rule.ConditionYellow) {
+ int last1 = Class->Anims[BSTATE_IDLE].Start + Class->Anims[BSTATE_IDLE].Count;
+ int last2 = Class->Anims[BSTATE_ACTIVE].Start + Class->Anims[BSTATE_ACTIVE].Count;
+ int largest = max(last1, last2);
+ last2 = Class->Anims[BSTATE_AUX1].Start + Class->Anims[BSTATE_AUX1].Count;
+ largest = max(largest, last2);
+ last2 = Class->Anims[BSTATE_AUX2].Start + Class->Anims[BSTATE_AUX2].Count;
+ largest = max(largest, last2);
+ shapenum += largest;
+ }
+ }
+ }
+ }
+ }
+ return(shapenum);
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Mark -- Building interface to map rendering system. *
+ * *
+ * This routine is used to mark the map cells so that when it renders *
+ * the underlying icons will also be updated as necessary. *
+ * *
+ * INPUT: mark -- Type of image change (MARK_UP, _DOWN, _CHANGE) *
+ * MARK_UP -- Building is removed. *
+ * MARK_CHANGE -- Building changes shape. *
+ * MARK_DOWN -- Building is added. *
+ * *
+ * OUTPUT: bool; Did the mark operation succeed? Failure could be the result of marking down *
+ * when the building is already marked down, or visa versa. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 03/31/1994 JLB : Created. *
+ * 04/15/1994 JLB : Converted to member function. *
+ * 04/16/1994 JLB : Added health bar tracking. *
+ * 12/23/1994 JLB : Calls low level check before proceeding. *
+ * 01/27/1995 JLB : Special road spacer template added. *
+ *=============================================================================================*/
+bool BuildingClass::Mark(MarkType mark)
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ if (TechnoClass::Mark(mark)) {
+ short const * offset = Overlap_List();
+ short const * occupy = Occupy_List();
+ CELL cell = Coord_Cell(Coord);
+ SmudgeType bib;
+
+ switch (mark) {
+ case MARK_UP:
+ Map.Pick_Up(cell, this);
+ if (Class->Bib_And_Offset(bib, cell)) {
+ SmudgeClass * smudge = new SmudgeClass(bib);
+ if (smudge != NULL) {
+ smudge->Disown(cell);
+ delete smudge;
+ }
+ }
+ break;
+
+ case MARK_DOWN:
+
+ /*
+ ** Special wall logic is handled here. A building that is really a wall
+ ** gets converted into an overlay wall type when it is placed down. The
+ ** actual building object itself is destroyed.
+ */
+ if (Class->IsWall) {
+ switch (Class->Type) {
+ case STRUCT_BRICK_WALL:
+ new OverlayClass(OVERLAY_BRICK_WALL, cell, House->Class->House);
+ break;
+
+ case STRUCT_BARBWIRE_WALL:
+ new OverlayClass(OVERLAY_BARBWIRE_WALL, cell, House->Class->House);
+ break;
+
+ case STRUCT_SANDBAG_WALL:
+ new OverlayClass(OVERLAY_SANDBAG_WALL, cell, House->Class->House);
+ break;
+
+ case STRUCT_WOOD_WALL:
+ new OverlayClass(OVERLAY_WOOD_WALL, cell, House->Class->House);
+ break;
+
+ case STRUCT_CYCLONE_WALL:
+ new OverlayClass(OVERLAY_CYCLONE_WALL, cell, House->Class->House);
+ break;
+
+ case STRUCT_FENCE:
+ new OverlayClass(OVERLAY_FENCE, cell, House->Class->House);
+ break;
+
+ default:
+ break;
+ }
+ Transmit_Message(RADIO_OVER_OUT);
+ delete this;
+
+ } else {
+ if (Can_Enter_Cell(cell) == MOVE_OK) {
+ /*
+ ** Determine if a bib is required for this building. If one is, then
+ ** create and place it.
+ */
+ CELL newcell = cell;
+ if (Class->Bib_And_Offset(bib, newcell)) {
+ new SmudgeClass(bib, Cell_Coord(newcell), Class->IsBase ? House->Class->House : HOUSE_NONE);
+ }
+
+ Map.Place_Down(cell, this);
+ } else {
+ return(false);
+ }
+ }
+ break;
+
+ case MARK_CHANGE_REDRAW:
+ Map.Refresh_Cells(cell, Overlap_List(true));
+ break;
+
+ default:
+ Map.Refresh_Cells(cell, Overlap_List(false));
+ Map.Refresh_Cells(cell, occupy);
+ break;
+ }
+ return(true);
+ }
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::AI -- Handles non-graphic AI processing for buildings. *
+ * *
+ * This function is to handle the AI logic for the building. The graphic logic (facing, *
+ * firing, and animation) is handled elsewhere. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/31/1994 JLB : Created. *
+ * 12/26/1994 JLB : Handles production. *
+ * 06/11/1995 JLB : Revamped. *
+ *=============================================================================================*/
+void BuildingClass::AI(void)
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ /*
+ ** Process building animation state changes. Transition to a following state
+ ** if there is one specified and the current animation sequence has expired.
+ ** This process must occur before mission AI since the mission AI relies on
+ ** the bstate change to occur immediately before the MissionClass::AI.
+ */
+ Animation_AI();
+
+ /*
+ ** If now is a good time to act on a new mission, then do so. This process occurs
+ ** here because some outside event may have requested a mission change for the building.
+ ** Such outside requests (player input) must be initiated BEFORE the normal AI process.
+ */
+ if (IsReadyToCommence && BState != BSTATE_CONSTRUCTION) {
+
+ /*
+ ** Clear the commencement flag ONLY if something actually occurred. By acting
+ ** this way, a building can set the IsReadyToCommence flag before it goes
+ ** to "sleep" knowing that it will wake up as soon as a new mission comes
+ ** along.
+ */
+ if (Commence()) {
+ IsReadyToCommence = false;
+ }
+ }
+
+ /*
+ ** Proceed with normal logic processing. This is where the mission processing
+ ** occurs. This call must be located after the animation sequence makes the
+ ** transition to the next frame (see above) in order for the mission logic to
+ ** act at the exact moment of graphic transition BEFORE it has a chance to
+ ** be displayed.
+ */
+ TechnoClass::AI();
+
+ /*
+ ** Bail if the object died in the AI routine.
+ */
+ if (!IsActive) {
+ return;
+ }
+
+ /*
+ ** Building ammo is instantly reloaded.
+ */
+ if (!Ammo) {
+ Ammo = Class->MaxAmmo;
+ }
+
+ /*
+ ** If now is a good time to act on a new mission, then do so. This occurs here because
+ ** some AI event may have requested a mission change (usually from another mission
+ ** state machine). This must occur here before it has a chance to render.
+ */
+ if (IsReadyToCommence) {
+
+ /*
+ ** Clear the commencement flag ONLY if something actually occurred. By acting
+ ** this way, a building can set the IsReadyToCommence flag before it goes
+ ** to "sleep" knowing that it will wake up as soon as a new mission comes
+ ** along.
+ */
+ if (Commence()) {
+ IsReadyToCommence = false;
+ }
+ }
+
+ /*
+ ** If a change of animation was requested, then make the change
+ ** now. The building animation system acts independently but subordinate
+ ** to the mission state machine system. By performing the animation change-up
+ ** here, the mission AI system is ensured of immediate visual affect when it
+ ** decides to change the animation state of the building.
+ */
+ if (QueueBState != BSTATE_NONE) {
+ if (BState != QueueBState) {
+ BState = QueueBState;
+ BuildingTypeClass::AnimControlType const * ctrl = Fetch_Anim_Control();
+ if (BState == BSTATE_CONSTRUCTION || BState == BSTATE_IDLE) {
+ Set_Rate(Options.Normalize_Delay(ctrl->Rate));
+ } else {
+ Set_Rate(ctrl->Rate);
+ }
+ Set_Stage(ctrl->Start);
+ }
+ QueueBState = BSTATE_NONE;
+ }
+
+ /*
+ ** If the building's strength has changed, then update the power
+ ** accordingly.
+ */
+ if (Strength != LastStrength) {
+ int oldpower = Power_Output();
+ LastStrength = Strength;
+ int newpower = Power_Output();
+ House->Adjust_Power(newpower - oldpower);
+ }
+
+ /*
+ ** Check to see if the destruction countdown timer is active. If so, then decrement it.
+ ** When this timer reaches zero, the building is removed from the map. All the explosions
+ ** are presumed to be in progress at this time.
+ */
+ if (Strength == 0) {
+ if (CountDown == 0) {
+ Limbo();
+ Drop_Debris(WhomToRepay);
+ delete this;
+ }
+ return;
+ }
+
+ /*
+ ** Charging logic.
+ */
+ Charging_AI();
+
+ /*
+ ** Handle any repair process that may be going on.
+ */
+ Repair_AI();
+
+ /*
+ ** For computer controlled buildings, determine what should be produced and start
+ ** production accordingly.
+ */
+ Factory_AI();
+
+ /*
+ ** Check for demolition timeout. When timeout has expired, the building explodes.
+ */
+ if (IsGoingToBlow && CountDown == 0) {
+ int damage = Strength;
+ Take_Damage(damage, 0, WARHEAD_FIRE, As_Techno(WhomToRepay), true);
+ if (!IsActive) {
+ return;
+ }
+ Mark(MARK_CHANGE);
+ }
+
+ /*
+ ** Turret equiped buildings must handle turret rotation logic here. This entails
+ ** rotating the turret to the desired facing as well as figuring out what that
+ ** desired facing should be.
+ */
+ Rotation_AI();
+
+ /*
+ ** Gap Generators need to scan if they've just become activated, or if
+ ** the power has just come on enough so they can scan. Also, they need
+ ** to un-jam if the power has just dropped off.
+ */
+ if (*this == STRUCT_GAP) {
+ if (Arm == 0) {
+ IsJamming = false;
+ Arm = TICKS_PER_MINUTE * Rule.GapRegenInterval + Random_Pick(1, TICKS_PER_SECOND);
+ }
+
+ if (!IsJamming) {
+ if (House->Power_Fraction() >= 1) {
+ Map.Jam_From(Coord_Cell(Center_Coord()), Rule.GapShroudRadius, House);
+ IsJamming = true;
+ }
+ } else {
+ if (House->Power_Fraction() < 1) {
+ IsJamming = false;
+ Map.UnJam_From(Coord_Cell(Center_Coord()), Rule.GapShroudRadius, House);
+ }
+ }
+ }
+
+ /*
+ ** Radar facilities and SAMs need to check for the proximity of a mobile
+ ** radar jammer.
+ */
+ if ((*this == STRUCT_RADAR || *this == STRUCT_SAM) && (Frame % TICKS_PER_SECOND) == 0) {
+ IsJammed = false;
+ for (int index = 0; index < Units.Count(); index++) {
+ UnitClass * obj = Units.Ptr(index);
+ if (obj != NULL &&
+ !obj->IsInLimbo &&
+ !obj->House->Is_Ally(House) &&
+ obj->Class->IsJammer &&
+ Distance(obj) <= Rule.RadarJamRadius) {
+
+ IsJammed = true;
+ break;
+ }
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Unlimbo -- Removes a building from limbo state. *
+ * *
+ * Use this routine to transform a building that has been held in limbo *
+ * state, into one that really exists on the map. Once a building as *
+ * been unlimboed, then it becomes a normal object in the game world. *
+ * *
+ * INPUT: pos -- The position to place the building on the map. *
+ * *
+ * dir (optional) -- not used for this class *
+ * *
+ * OUTPUT: bool; Was the unlimbo successful? *
+ * *
+ * WARNINGS: The unlimbo operation might not be successful if the *
+ * building could not be placed at the location specified. *
+ * *
+ * HISTORY: *
+ * 04/16/1994 JLB : Created. *
+ * 06/07/1994 JLB : Matches virtual function format for base class. *
+ * 05/09/1995 JLB : Handles wall placement. *
+ * 06/18/1995 JLB : Checks for wall legality before placing down. *
+ *=============================================================================================*/
+bool BuildingClass::Unlimbo(COORDINATE coord, DirType dir)
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ /*
+ ** If this is a wall type building, then it never gets unlimboed. Instead, it gets
+ ** converted to an overlay type.
+ */
+ if (Class->IsWall) {
+ if (Can_Enter_Cell(Coord_Cell(coord), FACING_NONE) == MOVE_OK) {
+ OverlayType otype = OVERLAY_NONE;
+ switch (Class->Type) {
+ case STRUCT_SANDBAG_WALL:
+ otype = OVERLAY_SANDBAG_WALL;
+ break;
+
+ case STRUCT_CYCLONE_WALL:
+ otype = OVERLAY_CYCLONE_WALL;
+ break;
+
+ case STRUCT_BRICK_WALL:
+ otype = OVERLAY_BRICK_WALL;
+ break;
+
+ case STRUCT_BARBWIRE_WALL:
+ otype = OVERLAY_BARBWIRE_WALL;
+ break;
+
+ case STRUCT_WOOD_WALL:
+ otype = OVERLAY_WOOD_WALL;
+ break;
+
+ case STRUCT_FENCE:
+ otype = OVERLAY_FENCE;
+ break;
+
+ default:
+ otype = OVERLAY_NONE;
+ break;
+
+ }
+ if (otype != OVERLAY_NONE) {
+ ObjectClass * o = OverlayTypeClass::As_Reference(otype).Create_One_Of(House);
+ if (o && o->Unlimbo(coord)) {
+ Map[coord].Owner = House->Class->House;
+ Transmit_Message(RADIO_OVER_OUT);
+ Map.Sight_From(Coord_Cell(coord), Class->SightRange, House);
+ delete this;
+ return(true);
+ }
+ }
+ }
+ return(false);
+ }
+
+ /*
+ ** Normal building unlimbo process.
+ */
+ if (TechnoClass::Unlimbo(coord, dir)) {
+
+ /*
+ ** Ensure that the owning house knows about the
+ ** new object.
+ */
+ House->BScan |= (1L << Class->Type);
+ House->ActiveBScan |= (1L << Class->Type);
+
+ /*
+ ** Recalculate the center point of the house's base.
+ */
+ House->Recalc_Center();
+
+ /*
+ ** Update the total factory type, assuming this building has a factory.
+ */
+ House->Active_Add(this);
+
+ /*
+ ** Possibly the sidebar will be affected by this addition.
+ */
+ House->IsRecalcNeeded = true;
+ LastStrength = 0;
+
+ if ((!IsDiscoveredByPlayer && Map[coord].IsVisible) || Session.Type != GAME_NORMAL) {
+ Revealed(PlayerPtr);
+ }
+ if (!House->IsHuman) {
+ Revealed(House);
+ }
+
+ if (IsOwnedByPlayer) {
+ Map.PowerClass::IsToRedraw = true;
+ Map.Flag_To_Redraw(false);
+ }
+
+ if ((Class->Ownable & (HOUSEF_GOOD | HOUSEF_BAD)) != (HOUSEF_GOOD | HOUSEF_BAD)) {
+ if (Class->Ownable & HOUSEF_GOOD) {
+ ActLike = HOUSE_GREECE;
+ } else {
+ ActLike = HOUSE_USSR;
+ }
+ }
+
+ return(true);
+ }
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Take_Damage -- Inflicts damage points upon a building. *
+ * *
+ * This routine will inflict damage points upon the specified building. *
+ * It will handle the damage animation and building destruction. Use *
+ * this routine whenever a building is attacked. *
+ * *
+ * INPUT: damage -- Amount of damage to inflict. *
+ * *
+ * distance -- The distance from the damage center point to the object's center point.*
+ * *
+ * warhead -- The kind of damage to inflict. *
+ * *
+ * source -- The source of the damage. This is used to change targeting. *
+ * *
+ * forced -- Is the damage forced upon the object regardless of whether it *
+ * is normally immune? *
+ * *
+ * OUTPUT: true/false; Was the building destroyed? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/21/1991 : Created. *
+ * 04/15/1994 JLB : Converted to member function. *
+ * 04/16/1994 JLB : Added warhead modifier to damage. *
+ * 06/03/1994 JLB : Added source of damage as target value. *
+ * 06/20/1994 JLB : Source is a base class pointer. *
+ * 11/22/1994 JLB : Shares base damage handler for techno objects. *
+ * 07/15/1995 JLB : Power ratio gets adjusted. *
+ *=============================================================================================*/
+ResultType BuildingClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, int forced)
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ ResultType res = RESULT_NONE;
+ int shakes;
+
+ if (this != source /*&& !Class->IsInsignificant*/) {
+
+ if (source) {
+ House->LATime = Frame;
+ House->LAType = source->What_Am_I();
+ House->LAZone = House->Which_Zone(this);
+ House->LAEnemy = source->Owner();
+
+ if (!House->Is_Ally(source)) {
+ House->Enemy = source->Owner();
+ }
+
+ Base_Is_Attacked(source);
+ }
+
+ short const * offset = Occupy_List();
+
+ /*
+ ** Memorize who they used to be in radio contact with.
+ */
+ TechnoClass *tech = Contact_With_Whom();
+ /*
+ ** Perform the low level damage assessment.
+ */
+ res = TechnoClass::Take_Damage(damage, distance, warhead, source, forced);
+ switch (res) {
+ case RESULT_DESTROYED:
+
+ /*
+ ** Add the building to the base prebuild list if allowed. This will force
+ ** the computer to rebuild this structure if it can.
+ */
+ if (IsToRebuild && Class->Level != -1 && Base.House == House->Class->House && Base.Get_Node(this) == 0) {
+// if (IsToRebuild && Class->IsBuildable && Base.House == House->Class->House && Base.Get_Node(this) == 0) {
+ Base.Nodes.Add(BaseNodeClass(Class->Type, Coord_Cell(Coord)));
+ }
+
+ /*
+ ** Destroy all attached objects.
+ */
+ while (Attached_Object()) {
+ FootClass * obj = Detach_Object();
+
+ Detach_All(true);
+ delete obj;
+ }
+
+ /*
+ ** If we were in contact with a landed plane, blow the plane up too.
+ */
+ if (tech && tech->IsActive && tech->What_Am_I() == RTTI_AIRCRAFT && ((AircraftClass *)tech)->Class->IsFixedWing && ((AircraftClass *)tech)->In_Which_Layer() == LAYER_GROUND) {
+ int damage = 500;
+ tech->Take_Damage(damage, 0, WARHEAD_AP, source, forced);
+ }
+
+ Sound_Effect(VOC_KABOOM22, Coord);
+ while (*offset != REFRESH_EOL) {
+ CELL cell = Coord_Cell(Coord) + *offset++;
+
+ /*
+ ** If the building is destroyed, then lots of
+ ** explosions occur.
+ */
+ new SmudgeClass(Random_Pick(SMUDGE_CRATER1, SMUDGE_CRATER6), Cell_Coord(cell));
+ if (Percent_Chance(50)) {
+ new AnimClass(ANIM_FIRE_SMALL, Coord_Scatter(Cell_Coord(cell), 0x0080), Random_Pick(0, 7), Random_Pick(1, 3));
+ if (Percent_Chance(50)) {
+ new AnimClass(ANIM_FIRE_MED, Coord_Scatter(Cell_Coord(cell), 0x0040), Random_Pick(0, 7), Random_Pick(1, 3));
+ }
+ }
+ new AnimClass(ANIM_FBALL1, Coord_Scatter(Cell_Coord(cell), 0x0040), Random_Pick(0, 3));
+ }
+
+ shakes = Class->Cost_Of() / 400;
+ if (shakes) {
+ Shake_The_Screen(shakes);
+ }
+ Sound_Effect(VOC_CRUMBLE, Coord);
+ if (Mission == MISSION_DECONSTRUCTION) {
+ CountDown = 0;
+ Set_Rate(0);
+ } else {
+ CountDown = 8;
+ }
+
+ /*
+ ** If it is in radio contact and the object seems to be attached, then tell
+ ** it to run away.
+ */
+ if (In_Radio_Contact() && Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) {
+ Transmit_Message(RADIO_RUN_AWAY);
+ }
+
+ /*
+ ** A force destruction will not generate survivors.
+ */
+ if (forced || *this == STRUCT_KENNEL) {
+ IsSurvivorless = true;
+ }
+
+ /*
+ ** Destruction of a radar facility or advanced communications
+ ** center will cause the spiedby field to change...
+ */
+ if (SpiedBy) {
+ SpiedBy = 0;
+ StructType struc = *this;
+ if (struc == STRUCT_RADAR /* || struc == STRUCT_EYE */) {
+ Update_Radar_Spied();
+ }
+ }
+
+ /*
+ ** Destruction of a gap generator will cause the cells it affects
+ ** to stop being jammed.
+ */
+ if (*this == STRUCT_GAP) {
+ Remove_Gap_Effect();
+ }
+
+ /*
+ ** Destruction of a shipyard or sub pen may cause attached ships
+ ** who are repairing themselves to discontinue repairs.
+ */
+ if (*this == STRUCT_SHIP_YARD || *this == STRUCT_SUB_PEN) {
+ for (int index = 0; index < Vessels.Count(); index++) {
+ VesselClass *obj = Vessels.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House == House) {
+ if (obj->IsSelfRepairing) {
+ if (::Distance(Center_Coord(), obj->Center_Coord()) < 0x0200) {
+ obj->IsSelfRepairing = false;
+ obj->IsToSelfRepair = false;
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ ** Destruction of a barrel will cause the surrounding squares to
+ ** be hit with damage.
+ */
+ if (*this == STRUCT_BARREL || *this == STRUCT_BARREL3) {
+ COORDINATE center = Center_Coord();
+ CELL cellcenter = Coord_Cell(center);
+
+ BulletClass * bullet;
+
+ bullet = new BulletClass(BULLET_INVISIBLE, ::As_Target(Adjacent_Cell(cellcenter, FACING_N)), 0, 200, WARHEAD_FIRE, MPH_MEDIUM_FAST);
+ if (bullet) {
+ bullet->Unlimbo(center, DIR_N);
+ }
+
+ bullet = new BulletClass(BULLET_INVISIBLE, ::As_Target(Adjacent_Cell(cellcenter, FACING_E)), 0, 200, WARHEAD_FIRE, MPH_MEDIUM_FAST);
+ if (bullet) {
+ bullet->Unlimbo(center, DIR_E);
+ }
+
+ bullet = new BulletClass(BULLET_INVISIBLE, ::As_Target(Adjacent_Cell(cellcenter, FACING_S)), 0, 200, WARHEAD_FIRE, MPH_MEDIUM_FAST);
+ if (bullet) {
+ bullet->Unlimbo(center, DIR_S);
+ }
+
+ bullet = new BulletClass(BULLET_INVISIBLE, ::As_Target(Adjacent_Cell(cellcenter, FACING_W)), 0, 200, WARHEAD_FIRE, MPH_MEDIUM_FAST);
+ if (bullet) {
+ bullet->Unlimbo(center, DIR_W);
+ }
+ }
+ break;
+
+ case RESULT_HALF:
+ if (*this == STRUCT_PUMP) {
+ AnimClass * anim = new AnimClass(ANIM_OILFIELD_BURN, Coord_Add(Coord, 0x00400130L), 1);
+ if (anim) {
+ anim->Attach_To(this);
+ }
+ }
+ // Fall into next case.
+
+ case RESULT_MAJOR:
+ Sound_Effect(VOC_KABOOM1, Coord);
+ while (*offset != REFRESH_EOL) {
+ CELL cell = Coord_Cell(Coord) + *offset++;
+ AnimClass * anim = NULL;
+
+ /*
+ ** Show pieces of fire to indicate that a significant change in
+ ** damage level has occurred.
+ */
+ if (warhead == WARHEAD_FIRE) {
+ switch (Random_Pick(0, 5+Class->Width()+Class->Height())) {
+ case 0:
+ break;
+
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ anim = new AnimClass(ANIM_ON_FIRE_SMALL, Coord_Scatter(Cell_Coord(cell), 0x0060), 0, Random_Pick(1, 3));
+ break;
+
+ case 6:
+ case 7:
+ case 8:
+ anim = new AnimClass(ANIM_ON_FIRE_MED, Coord_Scatter(Cell_Coord(cell), 0x0060), 0, Random_Pick(1, 3));
+ break;
+
+ case 9:
+ anim = new AnimClass(ANIM_ON_FIRE_BIG, Coord_Scatter(Cell_Coord(cell), 0x0060), 0, 1);
+ break;
+
+ default:
+ break;
+ }
+ } else {
+ if (Percent_Chance(50)) {
+ /*
+ ** Building may catch on fire, but only if it wasn't a
+ ** renovator that caused the damage.
+ */
+ if (source == NULL || source->What_Am_I() != RTTI_INFANTRY || *(InfantryClass *)source != INFANTRY_RENOVATOR) {
+ anim = new AnimClass(ANIM_FIRE_SMALL, Coord_Scatter(Cell_Coord(cell), 0x0060), Random_Pick(0, 7), Random_Pick(1, 3));
+ }
+ }
+ }
+ /*
+ ** If the animation was created, then attach it to the building.
+ */
+ if (anim) {
+ anim->Attach_To(this);
+ }
+ }
+ break;
+
+ case RESULT_NONE:
+ break;
+
+ case RESULT_LIGHT:
+ break;
+ }
+
+ if (source && res != RESULT_NONE) {
+
+ /*
+ ** If any damage occurred, then inform the house of this fact. If it is the player's
+ ** house, it might announce this fact.
+ */
+ if (!Class->IsInsignificant) {
+ House->Attacked();
+ }
+
+ /*
+ ** Save the type of the house that's doing the damage, so if the building burns
+ ** to death credit can still be given for the kill
+ */
+ WhoLastHurtMe = source->Owner();
+
+ /*
+ ** When certain buildings are hit, they "snap out of it" and
+ ** return fire if they are able and allowed.
+ */
+ if (*this != STRUCT_SAM && *this != STRUCT_AAGUN &&
+ !House->Is_Ally(source) &&
+ Class->PrimaryWeapon != NULL &&
+ (!Target_Legal(TarCom) || !In_Range(TarCom))) {
+
+ if (source->What_Am_I() != RTTI_AIRCRAFT && (!House->IsHuman || Rule.IsSmartDefense)) {
+ Assign_Target(source->As_Target());
+ } else {
+
+ /*
+ ** Generate a random rotation effect since there is nothing else that this
+ ** building can do.
+ */
+ if (!PrimaryFacing.Is_Rotating()) {
+ PrimaryFacing.Set_Desired(Random_Pick(DIR_N, DIR_MAX));
+ }
+ }
+ }
+ }
+ }
+
+ return(res);
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::new -- Allocates a building object from building pool. *
+ * *
+ * This routine will allocate a building slot from the building alloc *
+ * system. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with a pointer to the allocated building. If NULL is *
+ * returned, then this indicates a failure to allocate. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 04/11/1994 JLB : Created. *
+ * 04/21/1994 JLB : Converted to operator new. *
+ * 05/17/1994 JLB : Revamped allocation scheme *
+ * 07/29/1994 JLB : Simplified. *
+ *=============================================================================================*/
+void * BuildingClass::operator new(size_t )
+{
+ void * ptr = Buildings.Allocate();
+ if (ptr) {
+ ((BuildingClass *)ptr)->IsActive = true;
+ }
+ return(ptr);
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::delete -- Deallocates building object. *
+ * *
+ * This is the memory deallocation operation for a building object. *
+ * Since buildings are allocated out of a fixed memory block, all that *
+ * is needed is to flag the unit as inactive. *
+ * *
+ * INPUT: ptr -- Pointer to building to deallocate. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 04/21/1994 JLB : Created. *
+ *=============================================================================================*/
+void BuildingClass::operator delete(void *ptr)
+{
+ if (ptr) {
+ ((BuildingClass *)ptr)->IsActive = false;
+ }
+ Buildings.Free((BuildingClass *)ptr);
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::BuildingClass -- Constructor for buildings. *
+ * *
+ * This routine inserts a building into the object tracking system. *
+ * It is placed into a limbo state unless a location is provided for *
+ * it to unlimbo at. *
+ * *
+ * INPUT: type -- The structure type to make this object. *
+ * *
+ * house -- The owner of this building. *
+ * *
+ * pos -- The position to unlimbo the building. If -1 is *
+ * specified, then the building remains in a limbo *
+ * state. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 04/21/1994 JLB : Created. *
+ * 08/07/1995 JLB : Fixed act like value to match expected value. *
+ *=============================================================================================*/
+BuildingClass::BuildingClass(StructType type, HousesType house) :
+ TechnoClass(RTTI_BUILDING, Buildings.ID(this), house),
+ Class(BuildingTypes.Ptr((int)type)),
+ Factory(0),
+ ActLike(House->ActLike),
+ IsToRebuild(false),
+ IsToRepair(false),
+ IsAllowedToSell(true),
+ IsReadyToCommence(false),
+ IsRepairing(false),
+ IsWrenchVisible(false),
+ IsGoingToBlow(false),
+ IsSurvivorless(false),
+ IsCharging(false),
+ IsCharged(false),
+ IsCaptured(false),
+ IsJamming(false),
+ IsJammed(false),
+ HasFired(false),
+ HasOpened(false),
+ CountDown(0),
+ BState(BSTATE_NONE),
+ QueueBState(BSTATE_NONE),
+ WhoLastHurtMe(house),
+ WhomToRepay(TARGET_NONE),
+ AnimToTrack(TARGET_NONE),
+ LastStrength(0),
+ PlacementDelay(0)
+{
+ House->Tracking_Add(this);
+ IsSecondShot = !Class->Is_Two_Shooter();
+ Strength = Class->MaxStrength;
+ Ammo = Class->MaxAmmo;
+
+ /*
+ ** If the building could never be built, then it can never be sold either. This
+ ** is due to the lack of buildup animation.
+ */
+ if (Class->Get_Buildup_Data() != NULL) {
+// if (!Class->IsBuildable) {
+ IsAllowedToSell = false;
+ }
+
+// if (Session.Type == GAME_INTERNET) {
+// House->BuildingTotals->Increment_Unit_Total( (int) type);
+// }
+
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::~BuildingClass -- Destructor for building type objects. *
+ * *
+ * This destructor for building objects will put the building in limbo if possible. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 01/18/1995 JLB : Created. *
+ *=============================================================================================*/
+BuildingClass::~BuildingClass(void)
+{
+ if (GameActive && Class) {
+ if (House) {
+ House->Tracking_Remove(this);
+ }
+ BuildingClass::Limbo();
+ }
+ Class = 0;
+
+ delete (FactoryClass *)Factory;
+ Factory = 0;
+ ID = -1;
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Drop_Debris -- Drops rubble when building is destroyed. *
+ * *
+ * This routine is called when a building is destroyed. It handles *
+ * placing the rubble down. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/14/1994 JLB : Created. *
+ * 06/13/1995 JLB : Added smoke and normal infantry survivor possibility. *
+ * 07/16/1995 JLB : Survival rate depends on if captured or sabotaged. *
+ *=============================================================================================*/
+void BuildingClass::Drop_Debris(TARGET source)
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ CELL const * offset;
+ CELL cell;
+
+ /*
+ ** Generate random survivors from the destroyed building.
+ */
+ cell = Coord_Cell(Coord);
+ offset = Occupy_List();
+ int odds = 2;
+ if (Target_Legal(WhomToRepay)) odds -= 1;
+ if (IsCaptured) odds += 6;
+ int count = How_Many_Survivors();
+ while (*offset != REFRESH_EOL) {
+ CELL newcell;
+
+ newcell = cell + *offset++;
+ CellClass const * cellptr = &Map[newcell];
+
+ /*
+ ** Infantry could run out of a destroyed building.
+ */
+ if (!House->IsToDie && count > 0) {
+ InfantryClass * i = NULL;
+
+ if (Random_Pick(0, odds) == 1) {
+ i = NULL;
+ InfantryType typ = Crew_Type();
+ if (typ != INFANTRY_NONE) i = new InfantryClass(typ, House->Class->House);
+ if (i != NULL) {
+ if (Class->Get_Buildup_Data() != NULL && i->Class->IsNominal) i->IsTechnician = true;
+ ScenarioInit++;
+ if (i->Unlimbo(Cell_Coord(newcell), DIR_N)) {
+ count--;
+ i->Strength = Random_Pick(5, (int)i->Class->MaxStrength);
+ i->Scatter(0, true);
+ if (source != TARGET_NONE && !House->Is_Ally(As_Object(source))) {
+ i->Assign_Mission(MISSION_ATTACK);
+ i->Assign_Target(source);
+ } else {
+ if (House->IsHuman) {
+ i->Assign_Mission(MISSION_GUARD);
+ } else {
+ i->Assign_Mission(MISSION_HUNT);
+ }
+ }
+ } else {
+ delete i;
+ }
+ ScenarioInit--;
+ }
+ }
+ }
+
+ /*
+ ** Smoke and fire only appear on terrestrail cells. They should not appear on
+ ** rivers, clifs, or water cells.
+ */
+ if (cellptr->Is_Clear_To_Move(SPEED_TRACK, true, true)) {
+
+ /*
+ ** Possibly add some smoke rising from the ashes of the building.
+ */
+ switch (Random_Pick(0, 5)) {
+ case 0:
+ case 1:
+ case 2:
+ new AnimClass(ANIM_SMOKE_M, Coord_Scatter(Cell_Coord(newcell), 0x0050, false), Random_Pick(0, 5), Random_Pick(1, 2));
+ break;
+
+ default:
+ break;
+ }
+
+ /*
+ ** The building always scars the ground in some fashion.
+ */
+ if (Percent_Chance(25)) {
+ new SmudgeClass(Random_Pick(SMUDGE_SCORCH1, SMUDGE_SCORCH6), Cell_Coord(newcell));
+ } else {
+ new SmudgeClass(Random_Pick(SMUDGE_CRATER1, SMUDGE_CRATER6), Coord_Scatter(Cell_Coord(newcell), 0x0080, false));
+ }
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Active_Click_With -- Handles clicking on the map while the building is selected.*
+ * *
+ * This interface routine handles when the player clicks on the map while this building *
+ * is currently selected. This is used to assign an override target to a turret or *
+ * guard tower. *
+ * *
+ * INPUT: target -- The target that was clicked upon. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/28/1994 JLB : Created. *
+ *=============================================================================================*/
+void BuildingClass::Active_Click_With(ActionType action, ObjectClass * object)
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ if (action == ACTION_ATTACK && object != NULL) {
+ Player_Assign_Mission(MISSION_ATTACK, object->As_Target());
+ }
+
+ if (action == ACTION_SELF && Class->Is_Factory()) {
+ OutList.Add(EventClass(EventClass::PRIMARY, TargetClass(this)));
+ }
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Active_Click_With -- Handles cell selection for buildings. *
+ * *
+ * This routine really only serves one purpose -- to allow targeting of the ground for *
+ * buildings that are equipped with weapons. *
+ * *
+ * INPUT: action -- The requested action to perform. *
+ * *
+ * cell -- The cell location to perform the action upon. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/04/1995 JLB : Created. *
+ * 10/04/1995 JLB : Handles construction yard undeploy to move logic. *
+ *=============================================================================================*/
+void BuildingClass::Active_Click_With(ActionType action, CELL cell)
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ if (action == ACTION_ATTACK) {
+ Player_Assign_Mission(MISSION_ATTACK, ::As_Target(cell));
+ }
+
+ if (action == ACTION_MOVE && *this == STRUCT_CONST) {
+ OutList.Add(EventClass(EventClass::ARCHIVE, TargetClass(this), TargetClass(cell)));
+ OutList.Add(EventClass(EventClass::SELL, TargetClass(this)));
+ }
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Assign_Target -- Assigns a target to the building. *
+ * *
+ * Assigning of a target to a building makes sense if the building is one that can attack. *
+ * This routine would be used to assign the attack target to a turret or guard tower. *
+ * *
+ * INPUT: target -- The target that was clicked on while this building was selected. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/28/1994 JLB : Created. *
+ * 11/02/1994 JLB : Checks for range before assigning target. *
+ *=============================================================================================*/
+void BuildingClass::Assign_Target(TARGET target)
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ if (*this != STRUCT_SAM && *this != STRUCT_AAGUN && !In_Range(target, 0)) {
+ target = TARGET_NONE;
+ }
+
+ TechnoClass::Assign_Target(target);
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Init -- Initialize the building system to an empty null state. *
+ * *
+ * This routine initializes the building system in preparation for a scenario load. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 09/19/1994 JLB : Created. *
+ *=============================================================================================*/
+void BuildingClass::Init(void)
+{
+ Buildings.Free_All();
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Exit_Object -- Initiates an object to leave the building. *
+ * *
+ * This function is used to cause an object to exit the building. It is called when a *
+ * factory produces a vehicle or other mobile object and that object needs to exit the *
+ * building to join the ranks of a regular unit. Typically, the object is placed down on *
+ * the map such that it overlaps the building and then it is given a movement order so that *
+ * it will move to an adjacent free cell. *
+ * *
+ * INPUT: base -- Pointer to the object that is to exit the building. *
+ * *
+ * OUTPUT: Returns the success rating for the exit attempt; *
+ * 0 = complete failure (refund money please) *
+ * 1 = temporarily prevented (try again later please) *
+ * 2 = successful *
+ * *
+ * WARNINGS: The building is placed in radio contact with the object. The object is in a *
+ * tethered condition. This condition will be automatically broken when the *
+ * object reaches the adjacent square. *
+ * *
+ * HISTORY: *
+ * 11/28/1994 JLB : Created. *
+ * 04/10/1995 JLB : Handles building production by computer. *
+ * 06/17/1995 JLB : Handles refinery exit. *
+ *=============================================================================================*/
+int BuildingClass::Exit_Object(TechnoClass * base)
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ if (!base) return(0);
+
+ TechnoTypeClass const * ttype = (TechnoTypeClass const *)&base->Class_Of();
+
+ /*
+ ** A unit exiting a building is always considered to be "locked". That means, it
+ ** will be considered as to have legally entered the visible map domain.
+ */
+ base->IsLocked = true;
+
+ /*
+ ** Find a good cell to unload the object to. The object, probably a vehicle
+ ** will drive/walk to the adjacent free cell.
+ */
+ CELL cell = 0;
+
+ switch (base->What_Am_I()) {
+
+ case RTTI_AIRCRAFT:
+ if (!In_Radio_Contact()) {
+ AircraftClass * air = (AircraftClass *)base;
+
+ air->Height = 0;
+ ScenarioInit++;
+ if (air->Unlimbo(Docking_Coord(), air->Pose_Dir())) {
+ Transmit_Message(RADIO_HELLO, air);
+ Transmit_Message(RADIO_TETHER);
+ ScenarioInit--;
+ return(2);
+ }
+ ScenarioInit--;
+ } else {
+ AircraftClass * air = (AircraftClass *)base;
+
+ if (Cell_X(Coord_Cell(Center_Coord())) - Map.MapCellX < Map.MapCellWidth/2) {
+ cell = XY_Cell(Map.MapCellX-1, Random_Pick(0, Map.MapCellHeight-1)+Map.MapCellY);
+ } else {
+ cell = XY_Cell(Map.MapCellX+Map.MapCellWidth, Random_Pick(0, Map.MapCellHeight-1)+Map.MapCellY);
+ }
+ ScenarioInit++;
+ if (air->Unlimbo(Cell_Coord(cell), DIR_N)) {
+//BG air->Assign_Destination(::As_Target(Nearby_Location(air)));
+/*BG*/ air->Assign_Destination(::As_Target(air->Nearby_Location(this)));
+ air->Assign_Mission(MISSION_MOVE);
+ ScenarioInit--;
+ return(2);
+ }
+ ScenarioInit--;
+ }
+ break;
+
+ case RTTI_VESSEL:
+ switch (Class->Type) {
+ case STRUCT_SUB_PEN:
+ case STRUCT_SHIP_YARD:
+ ScenarioInit++;
+ cell = Find_Exit_Cell(base);
+ if (cell != 0 && base->Unlimbo(Cell_Coord(cell), Direction(Cell_Coord(cell)))) {
+ base->Assign_Mission(MISSION_GUARD);
+ ScenarioInit--;
+ return(2);
+ }
+ ScenarioInit--;
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case RTTI_INFANTRY:
+ case RTTI_UNIT:
+ switch (Class->Type) {
+ case STRUCT_REFINERY:
+ if (base->What_Am_I() == RTTI_UNIT) {
+ cell = Coord_Cell(Center_Coord());
+ UnitClass * unit = (UnitClass *)base;
+
+ cell = Adjacent_Cell(cell, FACING_SW);
+ ScenarioInit++;
+ if (unit->Unlimbo(Cell_Coord(Adjacent_Cell(cell, DIR_S)), DIR_SW_X2)) {
+ unit->PrimaryFacing = DIR_S;
+ unit->Assign_Mission(MISSION_HARVEST);
+ }
+ ScenarioInit--;
+ } else {
+ base->Scatter(0, true);
+ }
+ break;
+
+ case STRUCT_WEAP:
+ if (Mission == MISSION_UNLOAD) {
+ for(int index = 0; index < Buildings.Count(); index++) {
+ BuildingClass *bldg = Buildings.Ptr(index);
+ if (bldg->Owner() == Owner() && *bldg == STRUCT_WEAP && bldg != this && bldg->Mission == MISSION_GUARD && !bldg->Factory) {
+ FactoryClass * temp = Factory;
+ bldg->Factory = Factory;
+ Factory = 0;
+ int retval = (bldg->Exit_Object(base));
+ bldg->Factory = 0;
+ Factory = temp;
+ return(retval);
+ }
+ }
+ return(1); // fail while we're still unloading previous
+ }
+ ScenarioInit++;
+ if (base->Unlimbo(Exit_Coord(), DIR_S)) {
+ base->Mark(MARK_UP);
+ base->Coord = Exit_Coord();
+ base->Mark(MARK_DOWN);
+ Transmit_Message(RADIO_HELLO, base);
+ Transmit_Message(RADIO_TETHER);
+ Assign_Mission(MISSION_UNLOAD);
+ ScenarioInit--;
+ return(2);
+ }
+ ScenarioInit--;
+ break;
+
+ case STRUCT_BARRACKS:
+ case STRUCT_TENT:
+ case STRUCT_KENNEL:
+
+ cell = Find_Exit_Cell(base);
+ if (cell != 0) {
+ DirType dir = Direction(cell);
+ COORDINATE start = Exit_Coord();
+
+ ScenarioInit++;
+ if (base->Unlimbo(start, dir)) {
+
+ base->Assign_Mission(MISSION_MOVE);
+
+ /*
+ ** When disembarking from a transport then guard an area around the
+ ** center of the base.
+ */
+ base->Assign_Destination(::As_Target(cell));
+ if (House->IQ >= Rule.IQGuardArea) {
+ base->Assign_Mission(MISSION_GUARD_AREA);
+ base->ArchiveTarget = ::As_Target(House->Where_To_Go((FootClass *)base));
+ }
+
+ /*
+ ** Establish radio contact so unload coordination can occur. This
+ ** radio contact should always succeed.
+ */
+ if (Transmit_Message(RADIO_HELLO, base) == RADIO_ROGER) {
+ Transmit_Message(RADIO_UNLOAD);
+ }
+ ScenarioInit--;
+ return(2);
+ }
+ ScenarioInit--;
+ }
+ break;
+
+ default:
+ cell = Find_Exit_Cell(base);
+ if (cell != 0) {
+ DirType dir = Direction(cell);
+ COORDINATE start = Exit_Coord();
+
+ ScenarioInit++;
+ if (base->Unlimbo(start, dir)) {
+
+ base->Assign_Mission(MISSION_MOVE);
+
+ /*
+ ** When disembarking from a transport then guard an area around the
+ ** center of the base.
+ */
+ base->Assign_Destination(::As_Target(cell));
+ if (House->IQ >= Rule.IQGuardArea) {
+ base->Assign_Mission(MISSION_GUARD_AREA);
+ base->ArchiveTarget = ::As_Target(House->Where_To_Go((FootClass *)base));
+ }
+ ScenarioInit--;
+ return(2);
+ }
+ ScenarioInit--;
+ }
+ break;
+ }
+ break;
+
+ case RTTI_BUILDING:
+
+ if (!House->IsHuman) {
+
+ /*
+ ** Find the next available spot to place this newly created building. If the
+ ** building could be placed at the desired location, fine. If not, then this
+ ** routine will return failure. The calling routine will probably abandon this
+ ** building in preference to building another.
+ */
+ BaseNodeClass * node = Base.Next_Buildable(((BuildingClass *)base)->Class->Type);
+ COORDINATE coord = 0;
+ if (node) {
+ coord = Cell_Coord(node->Cell);
+ } else {
+
+ /*
+ ** Find a suitable new spot to place.
+ */
+ coord = House->Find_Build_Location((BuildingClass *)base);
+ }
+
+ if (coord) {
+ if (Flush_For_Placement(base, Coord_Cell(coord))) {
+ return(1);
+ }
+ if (base->Unlimbo(coord)) {
+ if (node && ((BuildingClass *)base)->Class->Type == House->BuildStructure) {
+ House->BuildStructure = STRUCT_NONE;
+ }
+ return(2);
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ /*
+ ** Failure to exit the object results in a false return value.
+ */
+ return(0);
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Update_Buildables -- Informs sidebar of additional construction options. *
+ * *
+ * This routine will tell the sidebar of objects that can be built. The function is called *
+ * whenever a building matures. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 11/11/1994 JLB : Created. *
+ * 12/23/1994 JLB : Only updates for PLAYER buildings. *
+ *=============================================================================================*/
+void BuildingClass::Update_Buildables(void)
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ if (House == PlayerPtr && !IsInLimbo && IsDiscoveredByPlayer) {
+ switch (Class->ToBuild) {
+ StructType i;
+ UnitType u;
+ InfantryType f;
+ AircraftType a;
+ VesselType v;
+
+ case RTTI_VESSELTYPE:
+ for (v = VESSEL_FIRST; v < VESSEL_COUNT; v++) {
+ if (PlayerPtr->Can_Build(&VesselTypeClass::As_Reference(v), ActLike)) {
+ Map.Add(RTTI_VESSELTYPE, v);
+ }
+ }
+ break;
+
+ case RTTI_BUILDINGTYPE:
+ for (i = STRUCT_FIRST; i < STRUCT_COUNT; i++) {
+ if (PlayerPtr->Can_Build(&BuildingTypeClass::As_Reference(i), ActLike)) {
+ Map.Add(RTTI_BUILDINGTYPE, i);
+ }
+ }
+ break;
+
+ case RTTI_UNITTYPE:
+ for (u = UNIT_FIRST; u < UNIT_COUNT; u++) {
+ if (PlayerPtr->Can_Build(&UnitTypeClass::As_Reference(u), ActLike)) {
+ Map.Add(RTTI_UNITTYPE, u);
+ }
+ }
+ break;
+
+ case RTTI_INFANTRYTYPE:
+ for (f = INFANTRY_FIRST; f < INFANTRY_COUNT; f++) {
+ if (PlayerPtr->Can_Build(&InfantryTypeClass::As_Reference(f), ActLike)) {
+ if (InfantryTypeClass::As_Reference(f).IsDog) {
+ if (*this == STRUCT_KENNEL) {
+ Map.Add(RTTI_INFANTRYTYPE, f);
+ }
+ } else {
+ if (*this != STRUCT_KENNEL) {
+ Map.Add(RTTI_INFANTRYTYPE, f);
+ }
+ }
+ }
+ }
+ break;
+
+ case RTTI_AIRCRAFTTYPE:
+ for (a = AIRCRAFT_FIRST; a < AIRCRAFT_COUNT; a++) {
+ if (PlayerPtr->Can_Build(&AircraftTypeClass::As_Reference(a), ActLike)) {
+ Map.Add(RTTI_AIRCRAFTTYPE, a);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Fire_Out -- Handles when attached animation expires. *
+ * *
+ * This routine is used to perform any fixups necessary when the attached animation has *
+ * terminated. This occurs when the fire & smoke animation that a SAM site produces stops. *
+ * At that point, normal reload procedures can commence. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 11/30/1994 JLB : Created. *
+ *=============================================================================================*/
+void BuildingClass::Fire_Out(void)
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Limbo -- Handles power adjustment as building goes into limbo. *
+ * *
+ * This routine will handle the power adjustments for the associated house when the *
+ * building goes into limbo. This means that its power drain or production is subtracted *
+ * from the house accumulated totals. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: bool; Was the building limboed? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 12/24/1994 JLB : Created. *
+ *=============================================================================================*/
+bool BuildingClass::Limbo(void)
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ if (!IsInLimbo) {
+
+ /*
+ ** Update the total factory type, assuming this building has a factory.
+ */
+ House->Active_Remove(this);
+ House->IsRecalcNeeded = true;
+ House->Recalc_Center();
+
+ /*
+ ** Update the power status of the owner's house.
+ */
+ House->Adjust_Power(-Power_Output());
+ House->Adjust_Drain(-Class->Drain);
+ House->Adjust_Capacity(-Class->Capacity, true);
+ if (House == PlayerPtr) {
+ Map.PowerClass::IsToRedraw = true;
+ Map.Flag_To_Redraw(false);
+ }
+
+ /*
+ ** This could be a building that builds. If so, then the sidebar may need adjustment.
+ ** Set IsInLimbo to true to "fool" the sidebar into knowing that this building
+ ** isn't available. Set it back to false so the rest of the Limbo code works.
+ ** Otherwise, the sidebar won't properly remove non-available buildables.
+ */
+// if (IsOwnedByPlayer && !ScenarioInit) {
+// IsInLimbo = true;
+// Map.Recalc();
+// IsInLimbo = false;
+// }
+ }
+ return(TechnoClass::Limbo());
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Turret_Facing -- Fetches the turret facing for this building. *
+ * *
+ * This will return the turret facing for this building. Some buildings don't have a *
+ * visual turret (e.g., pillbox) so they return a turret facing that always faces their *
+ * current target. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the current facing of the turret. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/29/1996 JLB : Created. *
+ *=============================================================================================*/
+DirType BuildingClass::Turret_Facing(void) const
+{
+ if (!Class->IsTurretEquipped && Target_Legal(TarCom)) {
+ return(::Direction(Center_Coord(), As_Coord(TarCom)));
+ }
+ return(PrimaryFacing.Current());
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Greatest_Threat -- Searches for target that building can fire upon. *
+ * *
+ * This routine intercepts the Greatest_Threat function so that it can add the ability *
+ * to search for ground targets, if this isn't a SAM site. *
+ * *
+ * INPUT: threat -- The base threat control value. Typically, it might be THREAT_RANGE *
+ * or THREAT_NORMAL. *
+ * *
+ * OUTPUT: Returns with a suitable target. If none could be found, then TARGET_NONE is *
+ * returned instead. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 01/01/1995 JLB : Created. *
+ *=============================================================================================*/
+TARGET BuildingClass::Greatest_Threat(ThreatType threat) const
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ if (Class->PrimaryWeapon != NULL) {
+ threat = threat | Class->PrimaryWeapon->Allowed_Threats();
+ }
+ if (Class->SecondaryWeapon != NULL) {
+ threat = threat | Class->SecondaryWeapon->Allowed_Threats();
+ }
+ if (House->IsHuman) {
+ threat = threat & ~THREAT_BUILDINGS;
+ }
+ threat = threat | THREAT_RANGE;
+
+// if (Class->PrimaryWeapon != NULL) {
+// if (Class->PrimaryWeapon->Bullet->IsAntiAircraft) {
+// threat = threat | THREAT_AIR;
+// }
+// if (Class->PrimaryWeapon->Bullet->IsAntiGround) {
+// threat = threat | THREAT_BUILDINGS|THREAT_INFANTRY|THREAT_BOATS|THREAT_VEHICLES;
+// }
+// threat = threat | THREAT_RANGE;
+// }
+ return(TechnoClass::Greatest_Threat(threat));
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Grand_Opening -- Handles construction completed special operations. *
+ * *
+ * This routine is called when construction has finished. Typically, this enables *
+ * new production options for factories. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 01/08/1995 JLB : Created. *
+ * 06/13/1995 JLB : Added helipad. *
+ *=============================================================================================*/
+void BuildingClass::Grand_Opening(bool captured)
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ if (!HasOpened || captured) {
+ HasOpened = true;
+
+ /*
+ ** Adjust the owning house according to the power, drain, and Tiberium capacity that
+ ** this building has.
+ */
+ House->Adjust_Drain(Class->Drain);
+ House->Adjust_Capacity(Class->Capacity);
+ House->IsRecalcNeeded = true;
+
+ /* SPECIAL CASE:
+ ** Tiberium Refineries get a free harvester. Add a harvester to the
+ ** reinforcement list at this time.
+ */
+ if (*this == STRUCT_REFINERY && !ScenarioInit && !captured && !Debug_Map && (!House->IsHuman || PurchasePrice == 0 || PurchasePrice > Class->Raw_Cost())) {
+ CELL cell = Coord_Cell(Adjacent_Cell(Center_Coord(), DIR_S));
+
+ UnitClass * unit = new UnitClass(UNIT_HARVESTER, House->Class->House);
+ if (unit != NULL) {
+
+ /*
+ ** Try to place down the harvesters. If it could not be placed, then try
+ ** to place it in a nearby location.
+ */
+ if (!unit->Unlimbo(Cell_Coord(cell), DIR_W)) {
+ cell = unit->Nearby_Location(this);
+
+ /*
+ ** If the harvester could still not be placed, then refund the money
+ ** to the owner and then bail.
+ */
+ if (!unit->Unlimbo(Cell_Coord(cell), DIR_SW)) {
+ House->Refund_Money(unit->Class->Cost_Of());
+ delete unit;
+ }
+ }
+ } else {
+
+ /*
+ ** If the harvester could not be created in the first place, then give
+ ** the full refund price to the owning player.
+ */
+ House->Refund_Money(UnitTypeClass::As_Reference(UNIT_HARVESTER).Cost_Of());
+ }
+ }
+
+ /*
+ ** Helicopter pads get a free attack helicopter.
+ */
+ if (!Rule.IsSeparate && *this == STRUCT_HELIPAD && !captured) {
+ ScenarioInit++;
+ AircraftClass * air = 0;
+ if (House->ActLike == HOUSE_USSR || House->ActLike == HOUSE_BAD || House->ActLike == HOUSE_UKRAINE) {
+ air = new AircraftClass(AIRCRAFT_HIND, House->Class->House);
+ } else {
+ air = new AircraftClass(AIRCRAFT_LONGBOW, House->Class->House);
+ }
+ if (air) {
+ air->Height = 0;
+ if (air->Unlimbo(Docking_Coord(), air->Pose_Dir())) {
+ air->Assign_Mission(MISSION_GUARD);
+ air->Transmit_Message(RADIO_HELLO, this);
+ Transmit_Message(RADIO_TETHER);
+ }
+ }
+ ScenarioInit--;
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Repair -- Initiates or terminates the repair process. *
+ * *
+ * This routine will start, stop, or toggle the repair process. When a building repairs, it *
+ * occurs incrementally over time. *
+ * *
+ * INPUT: control -- Determines how to control the repair process. *
+ * 0: Turns repair process off (if it was on). *
+ * 1: Turns repair process on (if it was off). *
+ * -1:Toggles repair process to other state. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 01/08/1995 JLB : Created. *
+ *=============================================================================================*/
+void BuildingClass::Repair(int control)
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ switch (control) {
+ case -1:
+ IsRepairing = (IsRepairing == false);
+ break;
+
+ case 1:
+ if (IsRepairing) return;
+ IsRepairing = true;
+ break;
+
+ case 0:
+ if (!IsRepairing) return;
+ IsRepairing = false;
+ break;
+
+ default:
+ break;
+ }
+
+ /*
+ ** At this point, we know that the repair state has changed. Perform
+ ** appropriate action.
+ */
+ VocType soundid = VOC_NONE;
+ if (IsRepairing) {
+ if (Strength == Class->MaxStrength) {
+ soundid = VOC_SCOLD;
+ } else {
+ soundid = VOC_CLICK;
+ if (House->IsPlayerControl) {
+ Clicked_As_Target();
+ }
+ IsWrenchVisible = true;
+ }
+ } else {
+ soundid = VOC_CLICK;
+ }
+ if (House->IsPlayerControl) {
+ Sound_Effect(soundid, Coord);
+ }
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Sell_Back -- Controls the sell back (demolish) operation. *
+ * *
+ * This routine will initiate or stop the sell back process for a building. It is called *
+ * when the player clicks on a building when the sell mode is active. *
+ * *
+ * INPUT: control -- The action to perform. 0 = turn deconstruction off, 1 = deconstruct, *
+ * -1 = toggle deconstruction state. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/25/1995 JLB : Created. *
+ *=============================================================================================*/
+void BuildingClass::Sell_Back(int control)
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ if (Class->Get_Buildup_Data()) {
+ bool decon = false;
+ switch (control) {
+ case -1:
+ decon = (Mission != MISSION_DECONSTRUCTION);
+ break;
+
+ case 1:
+ if (Mission == MISSION_DECONSTRUCTION) return;
+ if (IsGoingToBlow) return;
+ decon = true;
+ break;
+
+ case 0:
+ if (Mission != MISSION_DECONSTRUCTION) return;
+ decon = false;
+ break;
+
+ default:
+ break;
+ }
+
+ /*
+ ** At this point, we know that the repair state has changed. Perform
+ ** appropriate action.
+ */
+ if (decon) {
+ Assign_Mission(MISSION_DECONSTRUCTION);
+ Commence();
+ if (House->IsPlayerControl) {
+ Clicked_As_Target();
+ }
+ }
+ if (House->IsPlayerControl) {
+ Sound_Effect(VOC_CLICK);
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::What_Action -- Determines action to perform if click on specified object. *
+ * *
+ * This routine will determine what action to perform if the mouse was clicked on the *
+ * object specified. This determination is used to control the mouse imagery and the *
+ * function process when the mouse button is pressed. *
+ * *
+ * INPUT: object -- Pointer to the object that, if clicked on, will control what action *
+ * is to be performed. *
+ * *
+ * OUTPUT: Returns with the ActionType that will occur if the mouse is clicked over the *
+ * object specified while the building is currently selected. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 01/18/1995 JLB : Created. *
+ *=============================================================================================*/
+ActionType BuildingClass::What_Action(ObjectClass const * object) const
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ ActionType action = TechnoClass::What_Action(object);
+
+ if (action == ACTION_SELF) {
+ int index;
+ if (Class->Is_Factory() && PlayerPtr == House && *House->Factory_Counter(Class->ToBuild) > 1) {
+ switch (Class->ToBuild) {
+ case RTTI_INFANTRYTYPE:
+ case RTTI_INFANTRY:
+ action = ACTION_NONE;
+ if (*this == STRUCT_KENNEL) {
+ for (index = 0; index < Buildings.Count(); index++) {
+ BuildingClass *bldg = Buildings.Ptr(index);
+ if (bldg != this && bldg->Owner() == Owner() && *bldg == STRUCT_KENNEL) {
+ action = ACTION_SELF;
+ break;
+ }
+ }
+ } else {
+ for (index = 0; index < Buildings.Count(); index++) {
+ BuildingClass *bldg = Buildings.Ptr(index);
+ if (bldg != this && bldg->Owner() == Owner() && bldg->Class->ToBuild == RTTI_INFANTRYTYPE && *bldg != STRUCT_KENNEL) {
+ action = ACTION_SELF;
+ break;
+ }
+ }
+ }
+ break;
+
+ case RTTI_NONE:
+ action = ACTION_NONE;
+ break;
+
+ default:
+ break;
+ }
+
+ } else {
+ action = ACTION_NONE;
+ }
+ }
+
+ /*
+ ** Don't allow targeting of SAM sites, even if the CTRL key
+ ** is held down. Also don't allow targeting if the object is too
+ ** far away.
+ */
+ if (action == ACTION_ATTACK && (*this == STRUCT_SAM || *this == STRUCT_AAGUN || !In_Range(object, 0))) {
+ action = ACTION_NONE;
+ }
+
+ if (action == ACTION_MOVE) {
+ action = ACTION_NONE;
+ }
+
+ return(action);
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::What_Action -- Determines what action will occur. *
+ * *
+ * This routine examines the cell specified and returns with the action that will be *
+ * performed if that cell were clicked upon while the building is selected. *
+ * *
+ * INPUT: cell -- The cell to examine. *
+ * *
+ * OUTPUT: Returns the ActionType that indicates what should occur if the mouse is clicked *
+ * on this cell. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 01/18/1995 JLB : Created. *
+ *=============================================================================================*/
+ActionType BuildingClass::What_Action(CELL cell) const
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ ActionType action = TechnoClass::What_Action(cell);
+
+ if (action == ACTION_MOVE && (*this != STRUCT_CONST || !Rule.IsMCVDeploy)) {
+ action = ACTION_NONE;
+ }
+
+ /*
+ ** Don't allow targeting of SAM sites, even if the CTRL key
+ ** is held down.
+ */
+ if (action == ACTION_ATTACK && Class->PrimaryWeapon != NULL && !Class->PrimaryWeapon->Bullet->IsAntiGround) {
+// if (action == ACTION_ATTACK && (*this == STRUCT_SAM || *this == STRUCT_AAGUN)) {
+ action = ACTION_NONE;
+ }
+
+ return(action);
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Begin_Mode -- Begins an animation mode for the building. *
+ * *
+ * This routine will start the building animating. This animation will loop indefinitely *
+ * until explicitly stopped. *
+ * *
+ * INPUT: bstate -- The animation state to initiate. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: The building graphic state will reflect the first stage of this animation the *
+ * very next time it is rendered. *
+ * *
+ * HISTORY: *
+ * 06/25/1995 JLB : Created. *
+ * 07/02/1995 JLB : Uses normalize animation rate where applicable. *
+ *=============================================================================================*/
+void BuildingClass::Begin_Mode(BStateType bstate)
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ QueueBState = bstate;
+ if (BState == BSTATE_NONE || bstate == BSTATE_CONSTRUCTION || ScenarioInit) {
+ BState = bstate;
+ QueueBState = BSTATE_NONE;
+ BuildingTypeClass::AnimControlType const * ctrl = Fetch_Anim_Control();
+
+ int rate = ctrl->Rate;
+ if (Class->IsRegulated && bstate != BSTATE_CONSTRUCTION) {
+ rate = Options.Normalize_Delay(rate);
+ }
+ Set_Rate(rate);
+ Set_Stage(ctrl->Start);
+ }
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Center_Coord -- Fetches the center coordinate for the building. *
+ * *
+ * This routine is used to set the center coordinate for this building. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the coordinate for the center location for the building. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 03/10/1995 JLB : Created. *
+ *=============================================================================================*/
+COORDINATE BuildingClass::Center_Coord(void) const
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ return(Coord_Add(Coord, CenterOffset[Class->Size]));
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Docking_Coord -- Fetches the coordinate to use for docking. *
+ * *
+ * This routine will return the coordinate to use when an object wishes to dock with this *
+ * building. Normally the docking coordinate would be the center of the building. *
+ * Exceptions to this would be the airfield and helipad. Their docking coordinates are *
+ * offset to match the building artwork. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the coordinate to head to when trying to dock with this building. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 09/21/1995 JLB : Created. *
+ *=============================================================================================*/
+COORDINATE BuildingClass::Docking_Coord(void) const
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ if (*this == STRUCT_HELIPAD) {
+ return(Coord_Add(Coord, XYP_COORD(24, 18)));
+ }
+ if (*this == STRUCT_AIRSTRIP) {
+ return(Coord_Add(Coord, XYP_COORD(ICON_PIXEL_W + ICON_PIXEL_W/2, 28)));
+ }
+ return(TechnoClass::Docking_Coord());
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Can_Fire -- Determines if this building can fire. *
+ * *
+ * Use this routine to see if the building can fire its weapon. *
+ * *
+ * *
+ * INPUT: target -- The target that firing upon is desired. *
+ * *
+ * which -- Which weapon to use when firing. 0=primary, 1=secondary. *
+ * *
+ * OUTPUT: Returns with the fire possibility code. If firing is allowed, then FIRE_OK is *
+ * returned. Other cases will result in appropriate fire code value that indicates *
+ * why firing is not allowed. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/03/1995 JLB : Created. *
+ *=============================================================================================*/
+FireErrorType BuildingClass::Can_Fire(TARGET target, int which) const
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ FireErrorType canfire = TechnoClass::Can_Fire(target, which);
+
+ if (canfire == FIRE_OK) {
+
+ /*
+ ** Double check to make sure that the facing is roughly toward
+ ** the target. If the difference is too great, then firing is
+ ** temporarily postponed.
+ */
+ if (Class->IsTurretEquipped) {
+ int diff = PrimaryFacing.Difference(Direction(TarCom));
+ diff = abs(diff);
+ if (ABS(diff) > (*this == STRUCT_SAM ? 64 : 8)) {
+// if (ABS(diff) > 8) {
+ return(FIRE_FACING);
+ }
+
+ /*
+ ** If the turret is rotating then firing must be delayed.
+ */
+// if (PrimaryFacing.Is_Rotating()) {
+// return(FIRE_ROTATING);
+// }
+ }
+
+ /*
+ ** Certain buildings cannot fire if there is insufficient power.
+ */
+ if (Class->IsPowered && House->Power_Fraction() < 1) {
+ return(FIRE_BUSY);
+ }
+
+ /*
+ ** If an obelisk can fire, check the state of charge.
+ */
+ if (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->IsElectric && !IsCharged) {
+ return(FIRE_BUSY);
+ }
+ }
+ return(canfire);
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Toggle_Primary -- Toggles the primary factory state. *
+ * *
+ * This routine will change the primary factory state of this building. The primary *
+ * factory is the one that units will be produced from (by default). *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Is this building NOW the primary factory? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/03/1995 JLB : Created. *
+ *=============================================================================================*/
+bool BuildingClass::Toggle_Primary(void)
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ if (IsLeader) {
+ IsLeader = false;
+ } else {
+ for (int index = 0; index < Buildings.Count(); index++) {
+ BuildingClass * building = Buildings.Ptr(index);
+
+ if (!building->IsInLimbo && building->Owner() == Owner() && building->Class->ToBuild == Class->ToBuild) {
+ if (Class->ToBuild == RTTI_INFANTRYTYPE) {
+ if (*building == STRUCT_KENNEL && *this == STRUCT_KENNEL) {
+ building->IsLeader = false;
+ } else {
+ if (*building != STRUCT_KENNEL && *this != STRUCT_KENNEL) {
+ building->IsLeader = false;
+ }
+ }
+ } else {
+ building->IsLeader = false;
+ }
+ }
+ }
+ IsLeader = true;
+ if ((HouseClass *)House == PlayerPtr) {
+ Speak(VOX_PRIMARY_SELECTED);
+ }
+ }
+ Mark(MARK_CHANGE);
+ return(IsLeader);
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Captured -- Captures the building. *
+ * *
+ * This routine will change the owner of the building. It handles updating any related *
+ * game systems as a result. Factories are the most prone to have great game related *
+ * consequences when captured. This could also affect the sidebar and building ownership. *
+ * *
+ * INPUT: newowner -- Pointer to the house that is now the new owner. *
+ * *
+ * OUTPUT: Was the capture attempt successful? *
+ * *
+ * WARNINGS: Capturing could fail if the house is already owned by the one specified or *
+ * the building isn't allowed to be captured. *
+ * *
+ * HISTORY: *
+ * 05/03/1995 JLB : Created. *
+ * 07/05/1995 JLB : Fixed production problem with capturing enemy buildings. *
+ *=============================================================================================*/
+bool BuildingClass::Captured(HouseClass * newowner)
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ if (Class->IsCaptureable && newowner != House) {
+#ifdef TOFIX
+ switch (Owner()) {
+ case HOUSE_GOOD:
+ Speak(VOX_GDI_CAPTURED);
+ break;
+
+ case HOUSE_BAD:
+ Speak(VOX_NOD_CAPTURED);
+ break;
+ }
+#endif
+
+ /*
+ ** Make sure the capturer isn't spying on his own building, and if
+ ** it was a radar facility, update the target house's RadarSpied field.
+ */
+ if (SpiedBy & (1<<(newowner->Class->House)) ) {
+ SpiedBy -= (1<<(newowner->Class->House));
+ if (*this == STRUCT_RADAR) {
+ Update_Radar_Spied();
+ }
+ }
+
+ if (House == PlayerPtr) {
+ Map.PowerClass::IsToRedraw = true;
+ Map.Flag_To_Redraw(false);
+ }
+
+ if (*this == STRUCT_GAP) {
+ Remove_Gap_Effect();
+ IsJamming = false;
+ Arm = 0;
+ }
+
+ /*
+ ** Add this building to the list of buildings captured this game. For internet stats purposes.
+ */
+ if (Session.Type == GAME_INTERNET) {
+ newowner->CapturedBuildings->Increment_Unit_Total (Class->Type);
+ }
+
+ House->Adjust_Power(-Power_Output());
+ LastStrength = 0;
+ House->Adjust_Drain(-Class->Drain);
+ int booty = House->Adjust_Capacity(-Class->Capacity, true);
+
+ /*
+ ** If there is something loaded, then it gets captured as well.
+ */
+ TechnoClass * tech = Attached_Object();
+ if (tech) tech->Captured(newowner);
+
+ /*
+ ** If something isn't technically attached, but is sitting on this
+ ** building for another reason (e.g., helicopter on helipad), then it
+ ** gets captured as well.
+ */
+ tech = Contact_With_Whom();
+ if (tech) {
+ if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER && (::Distance(tech->Center_Coord(), Docking_Coord()) < 0x0040 ||
+ (tech->What_Am_I() == RTTI_AIRCRAFT && ((AircraftClass *)tech)->Class->IsFixedWing && ((AircraftClass *)tech)->In_Which_Layer() == LAYER_GROUND)) ) {
+ tech->Captured(newowner);
+ } else {
+ Transmit_Message(RADIO_RUN_AWAY);
+ Transmit_Message(RADIO_OVER_OUT);
+ }
+ }
+
+ /*
+ ** Abort any computer production in progress.
+ */
+ if (Factory) {
+ delete (FactoryClass *)Factory;
+ Factory = 0;
+ }
+
+ /*
+ ** Decrement the factory counter for the original owner.
+ */
+ House->Active_Remove(this);
+
+ /*
+ ** Flag that both owners now need to update their buildable lists.
+ */
+ House->IsRecalcNeeded = true;
+ newowner->IsRecalcNeeded = true;
+ HouseClass * oldowner = House;
+ TARGET tocap = As_Target();
+
+ IsCaptured = true;
+ TechnoClass::Captured(newowner);
+
+ oldowner->ToCapture = tocap;
+ oldowner->Recalc_Center();
+ House->Recalc_Center();
+ if (House->ToCapture == As_Target()) {
+ House->ToCapture = TARGET_NONE;
+ }
+
+ SmudgeType bib;
+ CELL cell = Coord_Cell(Coord);
+ if (Class->Bib_And_Offset(bib, cell)) {
+ SmudgeClass * smudge = new SmudgeClass(bib);
+ if (smudge) {
+ smudge->Disown(cell);
+ delete smudge;
+ }
+#ifdef FIXIT_CAPTURE_BIB
+ if (Session.Type == GAME_NORMAL) {
+ new SmudgeClass(bib, Cell_Coord(cell), Class->IsBase ? House->Class->House : HOUSE_NONE);
+ } else {
+ new SmudgeClass(bib, Cell_Coord(cell), House->Class->House);
+ }
+#else
+ new SmudgeClass(bib, Cell_Coord(cell), House->Class->House);
+#endif
+ }
+
+ House->Harvested(booty);
+ House->Stole(Refund_Amount());
+
+ /*
+ ** Increment the factory count for the new owner.
+ */
+ House->Active_Add(this);
+
+ IsRepairing = false;
+ Grand_Opening(true);
+
+ Mark(MARK_CHANGE);
+
+ /*
+ ** Perform a look operation when captured if it was the player
+ ** that performed the capture.
+ */
+ if (House == PlayerPtr) {
+ Look(false);
+ }
+ /*
+ ** If it was spied upon by the player who just captured it, clear the
+ ** spiedby flag for that house.
+ */
+ if (SpiedBy & (1 << (newowner->Class->House))) {
+ SpiedBy &= ~(1 << (newowner->Class->House));
+ }
+
+ /*
+ ** Update the new building's colors on the radar map.
+ */
+ short const * offset = Occupy_List();
+ while (*offset != REFRESH_EOL) {
+ CELL cell = Coord_Cell(Coord) + *offset++;
+ Map.Radar_Pixel(cell);
+ }
+ return(true);
+ }
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Sort_Y -- Returns the building coordinate used for sorting. *
+ * *
+ * The coordinate value returned from this function should be used for sorting purposes. *
+ * It has special offset adjustment applied so that vehicles don't overlap (as much). *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with a coordinate value suitable to be used for sorting. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/23/1995 JLB : Created. *
+ * 06/19/1995 JLB : Handles buildings that come with bibs built-in. *
+ *=============================================================================================*/
+COORDINATE BuildingClass::Sort_Y(void) const
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ if (*this == STRUCT_REPAIR) {
+ return(Coord);
+ }
+ if (*this == STRUCT_BARRACKS /*|| *this == STRUCT_POWER*/) {
+ return(Center_Coord());
+ }
+ if (*this == STRUCT_REFINERY) {
+ return(Center_Coord());
+ }
+
+ /*
+ ** Mines need to bias their sort location such that they are typically drawn
+ ** before any objects that might overlap them.
+ */
+ if (*this == STRUCT_AVMINE || *this == STRUCT_APMINE) {
+ return(Coord_Move(Center_Coord(), DIR_N, CELL_LEPTON_H));
+ }
+
+ return(Coord_Add(Center_Coord(), XY_Coord(0, (Class->Height()*256)/3)));
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Can_Enter_Cell -- Determines if building can be placed down. *
+ * *
+ * This routine will determine if the building can be placed down at the location *
+ * specified. *
+ * *
+ * INPUT: cell -- The cell to examine. This is usually the cell of the upper left corner *
+ * of the building if it were to be placed down. *
+ * *
+ * OUTPUT: Returns with the move legality value for placement at the location specified. This *
+ * will either be MOVE_OK or MOVE_NO. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/25/1995 JLB : Created. *
+ *=============================================================================================*/
+MoveType BuildingClass::Can_Enter_Cell(CELL cell, FacingType) const
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ if (*this == STRUCT_CONST && IsDown) {
+ return(Map[cell].Is_Clear_To_Build(Class->Speed) ? MOVE_OK : MOVE_NO);
+ }
+
+ if (!Debug_Map && ScenarioInit == 0 && Session.Type == GAME_NORMAL && House->IsPlayerControl && !Map[cell].IsMapped) {
+ return(MOVE_NO);
+ }
+
+ return(Class->Legal_Placement(cell) ? MOVE_OK : MOVE_NO);
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Can_Demolish -- Can the player demolish (sell back) the building? *
+ * *
+ * Determines if the player can sell this building. Selling is possible if the building *
+ * is not currently in construction or deconstruction animation. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Can the building be demolished at this time? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/25/1995 JLB : Created. *
+ * 07/01/1995 JLB : If there is no buildup data, then the building can't be sold. *
+ * 07/17/1995 JLB : Cannot sell a refinery that has a harvester attached. *
+ *=============================================================================================*/
+bool BuildingClass::Can_Demolish(void) const
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ if (Class->IsUnsellable) return(false);
+
+ if (Class->Get_Buildup_Data() && BState != BSTATE_CONSTRUCTION && Mission != MISSION_DECONSTRUCTION && Mission != MISSION_CONSTRUCTION) {
+ if (*this == STRUCT_REFINERY && Is_Something_Attached()) return(false);
+ return(true);
+ }
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Mission_Guard -- Handles guard mission for combat buildings. *
+ * *
+ * Buildings that can attack are given this mission. They will wait until a suitable target *
+ * comes within range and then launch into the attack mission. Buildings that have no *
+ * weaponry will just sit in this routine forever. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the number of game frames to delay before this routine will be called *
+ * again. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/25/1995 JLB : Created. *
+ *=============================================================================================*/
+int BuildingClass::Mission_Guard(void)
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ /*
+ ** If this building has a weapon, then search for a target to attack. When
+ ** a target is found, switch into attack mode to deal with the threat.
+ */
+ if (Is_Weapon_Equipped()) {
+
+ /*
+ ** Weapon equipped buildings are ALWAYS ready to launch into another mission if
+ ** they are sitting around in guard mode.
+ */
+ IsReadyToCommence = true;
+
+ /*
+ ** If there is no target available, then search for one.
+ */
+ if (!Target_Legal(TarCom)) {
+ ThreatType threat = THREAT_NORMAL;
+ Assign_Target(Greatest_Threat(threat));
+ }
+
+ /*
+ ** There is a valid target. Switch into attack mode right away.
+ */
+ if (Target_Legal(TarCom)) {
+ Assign_Mission(MISSION_ATTACK);
+ Commence();
+ return(1);
+ }
+ } else {
+
+ /*
+ ** This is the very simple state machine that basically does
+ ** nothing. This is the mode that non weapon equipped buildings
+ ** are normally in.
+ */
+ enum {
+ INITIAL_ENTRY,
+ IDLE
+ };
+ switch (Status) {
+ case INITIAL_ENTRY:
+ Begin_Mode(BSTATE_IDLE);
+ Status = IDLE;
+ break;
+
+ case IDLE:
+ /*
+ ** Special case to break out of guard mode if this is a repair
+ ** facility and there is a customer waiting at the grease pit.
+ */
+ if (*this == STRUCT_REPAIR &&
+ In_Radio_Contact() &&
+ Contact_With_Whom()->Is_Techno() &&
+ ((TechnoClass *)Contact_With_Whom())->Mission == MISSION_ENTER &&
+ Distance(Contact_With_Whom()) < 0x0040 &&
+ Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) {
+
+ Assign_Mission(MISSION_REPAIR);
+ return(1);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (*this == STRUCT_REPAIR) {
+ return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2));
+ } else {
+ return(MissionControl[Mission].Normal_Delay() * 3 + Random_Pick(0, 2));
+ }
+ }
+ return(MissionControl[Mission].AA_Delay() + Random_Pick(0, 2));
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Mission_Construction -- Handles mission construction. *
+ * *
+ * This routine will handle mission construction. When this mission is complete, the *
+ * building will begin normal operation. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the number of game frames to delay before calling this routine *
+ * again. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/25/1995 JLB : Created. *
+ *=============================================================================================*/
+int BuildingClass::Mission_Construction(void)
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ enum {
+ INITIAL,
+ DURING
+ };
+ switch (Status) {
+ case INITIAL:
+ Begin_Mode(BSTATE_CONSTRUCTION);
+ Transmit_Message(RADIO_BUILDING);
+ if (House->IsPlayerControl) {
+ Sound_Effect(VOC_CONSTRUCTION, Coord);
+ }
+ Status = DURING;
+ break;
+
+ case DURING:
+ if (IsReadyToCommence) {
+
+ /*
+ ** When construction is complete, then transmit this
+ ** to the construction yard so that it can stop its
+ ** construction animation.
+ */
+ Transmit_Message(RADIO_COMPLETE); // "I'm finished."
+ Transmit_Message(RADIO_OVER_OUT); // "You're free."
+ Begin_Mode(BSTATE_IDLE);
+ Grand_Opening();
+ Assign_Mission(MISSION_GUARD);
+ PrimaryFacing = Class->StartFace;
+ }
+ break;
+
+ default:
+ break;
+ }
+ return(1);
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Mission_Deconstruction -- Handles building deconstruction. *
+ * *
+ * This state machine is only used when the building is deconstructing as a result of *
+ * selling. When this mission is finished, the building will no longer exist. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the number of game frames to delay before calling this routine again. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/25/1995 JLB : Created. *
+ * 08/13/1995 JLB : Enable selling of units on a repair bay. *
+ * 08/20/1995 JLB : Scatters infantry from scattered starting points. *
+ *=============================================================================================*/
+int BuildingClass::Mission_Deconstruction(void)
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ /*
+ ** Always force repair off.
+ */
+ Repair(0);
+
+ enum {
+ INITIAL,
+ HOLDING,
+ DURING
+ };
+ switch (Status) {
+ case INITIAL:
+
+ /*
+ ** Special check for the repair bay which has the ability to sell
+ ** whatever is on it. If there is something on the repair bay, then
+ ** it will be sold. If there is nothing on the repair bay, then
+ ** the repair bay itself will be sold.
+ */
+ if ( (*this == STRUCT_REPAIR || *this == STRUCT_AIRSTRIP) && Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER && Distance(Contact_With_Whom()) < 0x0080) {
+ TechnoClass * tech = Contact_With_Whom();
+ Transmit_Message(RADIO_OVER_OUT);
+ if (IsOwnedByPlayer) Speak(VOX_UNIT_SOLD);
+ tech->Sell_Back(1);
+ Assign_Mission(MISSION_GUARD);
+ return(1);
+ }
+
+ /*
+ ** Selling off a shipyard or sub pen may cause attached ships
+ ** who are repairing themselves to discontinue repairs.
+ */
+ if (*this == STRUCT_SHIP_YARD || *this == STRUCT_SUB_PEN) {
+ for (int index = 0; index < Vessels.Count(); index++) {
+ VesselClass * obj = Vessels.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House == House) {
+ if (obj->IsSelfRepairing) {
+ if (::Distance(Center_Coord(), obj->Center_Coord()) < 0x0200) {
+ obj->IsSelfRepairing = false;
+ obj->IsToSelfRepair = false;
+ }
+ }
+ }
+ }
+ }
+
+ IsReadyToCommence = false;
+ Transmit_Message(RADIO_RUN_AWAY);
+ Status = HOLDING;
+ break;
+
+ case HOLDING:
+ if (!IsTethered) {
+
+ /*
+ ** The crew will evacuate from the building. The number of crew
+ ** members leaving is equal to the unrecovered cost of the building
+ ** divided by 100 (the typical cost of a minigunner infantryman).
+ */
+ if (!Target_Legal(ArchiveTarget) || !Rule.IsMCVDeploy || *this != STRUCT_CONST) {
+ int count = How_Many_Survivors();
+ bool engine = false;
+
+ while (count) {
+
+ /*
+ ** Ensure that the player only gets ONE engineer and not from a captured
+ ** construction yard.
+ */
+ InfantryType typ = Crew_Type();
+ while (typ == INFANTRY_RENOVATOR && engine) {
+ typ = Crew_Type();
+ }
+ if (typ == INFANTRY_RENOVATOR) engine = true;
+
+ InfantryClass * infantry = 0;
+ if (typ != INFANTRY_NONE) infantry = new InfantryClass(typ, House->Class->House);
+ if (infantry != NULL) {
+ ScenarioInit++;
+ COORDINATE coord = Coord_Add(Center_Coord(), XYP_COORD(0, -12));
+ coord = Map[coord].Closest_Free_Spot(coord, false);
+
+ if (infantry->Unlimbo(coord, DIR_N)) {
+ if (infantry->Class->IsNominal) infantry->IsTechnician = true;
+ ScenarioInit--;
+ infantry->Scatter(0, true);
+ ScenarioInit++;
+ infantry->Assign_Mission(MISSION_GUARD_AREA);
+ } else {
+ delete infantry;
+ }
+ ScenarioInit--;
+ }
+ count--;
+ }
+ }
+
+ if (House->IsPlayerControl) {
+ Sound_Effect(VOC_CASHTURN, Coord);
+ }
+ Status = DURING;
+ Begin_Mode(BSTATE_CONSTRUCTION);
+ Detach_All(true);
+ Transmit_Message(RADIO_OVER_OUT);
+ IsReadyToCommence = false;
+ break;
+ }
+ Transmit_Message(RADIO_RUN_AWAY);
+ break;
+
+ case DURING:
+ if (IsReadyToCommence) {
+ House->IsRecalcNeeded = true;
+ if (IsOwnedByPlayer) Speak(VOX_STRUCTURE_SOLD);
+
+ /*
+ ** Construction yards that deconstruct, really just revert back
+ ** to an MCV.
+ */
+ if (Target_Legal(ArchiveTarget) && *this == STRUCT_CONST && House->IsHuman && Strength > 0) {
+ ScenarioInit++;
+ UnitClass * unit = new UnitClass(UNIT_MCV, House->Class->House);
+ ScenarioInit--;
+ if (unit != NULL) {
+
+ /*
+ ** Unlimbo the MCV onto the map. The MCV should start in the same
+ ** health condition that the construction yard was in.
+ */
+ fixed ratio = Health_Ratio();
+ int money = Refund_Amount();
+ TARGET arch = ArchiveTarget;
+ COORDINATE place = Coord_Snap(Adjacent_Cell(Coord, DIR_SE));
+
+ delete this;
+
+ if (unit->Unlimbo(place, DIR_SW)) {
+ unit->Strength = unit->Class_Of().MaxStrength * ratio;
+
+ /*
+ ** Lift the move destination from the building and assign
+ ** it to the unit.
+ */
+ if (Target_Legal(arch)) {
+ unit->Assign_Destination(arch);
+ unit->Assign_Mission(MISSION_MOVE);
+ }
+
+ } else {
+
+ /*
+ ** If, for some strange reason, the MCV could not be placed on the
+ ** map, then give the player some money to compensate.
+ */
+ House->Refund_Money(money);
+ }
+ } else {
+ House->Refund_Money(Refund_Amount());
+ delete this;
+ }
+
+ } else {
+
+ /*
+ ** Selling off a gap generator will cause the cells it affects
+ ** to stop being jammed.
+ */
+ if (*this == STRUCT_GAP) {
+ Remove_Gap_Effect();
+ }
+
+ /*
+ ** A sold building still counts as a kill, but it just isn't directly
+ ** attributed to the enemy.
+ */
+ WhoLastHurtMe = HOUSE_NONE;
+ Record_The_Kill(NULL);
+
+ /*
+ ** The player gets part of the money back for the sell.
+ */
+ House->Refund_Money(Refund_Amount());
+ House->Stole(-Refund_Amount());
+ Limbo();
+
+ /*
+ ** Finally, delete the building from the game.
+ */
+ delete this;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ return(1);
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Mission_Attack -- Handles attack mission for building. *
+ * *
+ * Buildings that can attack are processed by this attack mission state machine. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the number of game frames to delay before calling this routine *
+ * again. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/25/1995 JLB : Created. *
+ * 02/22/1996 JLB : SAM doesn't lower back into ground. *
+ *=============================================================================================*/
+int BuildingClass::Mission_Attack(void)
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ if (*this == STRUCT_SAM) {
+ switch (Status) {
+
+ /*
+ ** This is the target tracking state of the launcher. It will rotate
+ ** to face the current TarCom of the launcher.
+ */
+ case SAM_READY:
+ if ((Class->IsPowered && House->Power_Fraction() < 1) || IsJammed) {
+ return(1);
+ }
+ if (!Target_Legal(TarCom) || !Is_Target_Aircraft(TarCom) || As_Aircraft(TarCom)->Height == 0) {
+ Assign_Target(TARGET_NONE);
+ Status = SAM_READY;
+ Assign_Mission(MISSION_GUARD);
+ Commence();
+ return(1);
+ } else {
+ if (!PrimaryFacing.Is_Rotating()) {
+ DirType facing = Direction(TarCom);
+ if (PrimaryFacing.Difference(facing)) {
+ PrimaryFacing.Set_Desired(facing);
+ } else {
+ Status = SAM_FIRING;
+ }
+ }
+ }
+ return(1);
+
+ /*
+ ** The launcher is in the process of firing.
+ */
+ case SAM_FIRING:
+ if (!Target_Legal(TarCom) || !Is_Target_Aircraft(TarCom) || As_Aircraft(TarCom)->Height == 0) {
+ Assign_Target(TARGET_NONE);
+ Status = SAM_READY;
+ } else {
+ FireErrorType error = Can_Fire(TarCom, 0);
+ if (error == FIRE_ILLEGAL || error == FIRE_CANT || error == FIRE_RANGE) {
+ Assign_Target(TARGET_NONE);
+ Status = SAM_READY;
+ } else {
+ if (error == FIRE_FACING) {
+ Status = SAM_READY;
+ } else {
+ if (error == FIRE_OK) {
+ Fire_At(TarCom, 0);
+ Fire_At(TarCom, 1);
+ Status = SAM_READY;
+ }
+ }
+ }
+ }
+ return(1);
+
+ default:
+ break;
+ }
+ return(MissionControl[Mission].AA_Delay() + Random_Pick(0, 2));
+
+ }
+
+ if (!Target_Legal(TarCom)) {
+ Assign_Target(TARGET_NONE);
+ Assign_Mission(MISSION_GUARD);
+ Commence();
+ return(1);
+ }
+
+ int primary = What_Weapon_Should_I_Use(TarCom);
+ IsReadyToCommence = true;
+ switch (Can_Fire(TarCom, primary)) {
+ case FIRE_ILLEGAL:
+ case FIRE_CANT:
+ case FIRE_RANGE:
+ case FIRE_AMMO:
+ Assign_Target(TARGET_NONE);
+ Assign_Mission(MISSION_GUARD);
+ Commence();
+ break;
+
+ case FIRE_FACING:
+ PrimaryFacing.Set_Desired(Direction(TarCom));
+ return(2);
+
+ case FIRE_REARM:
+ PrimaryFacing.Set_Desired(Direction(TarCom));
+ return(Arm);
+
+ case FIRE_BUSY:
+ return(1);
+
+ case FIRE_CLOAKED:
+ Do_Uncloak();
+ break;
+
+ case FIRE_OK:
+ Fire_At(TarCom, primary);
+ return(1);
+
+ default:
+ break;
+ }
+ PrimaryFacing.Set_Desired(Direction(TarCom));
+ return(1);
+// return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2));
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Mission_Harvest -- Handles refinery unloading harvesters. *
+ * *
+ * This state machine handles the refinery when it unloads the harvester. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the number of game frames to delay before calling this routine *
+ * again. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/25/1995 JLB : Created. *
+ *=============================================================================================*/
+int BuildingClass::Mission_Harvest(void)
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ enum {
+ INITIAL, // Dock the Tiberium cannister.
+ WAIT_FOR_DOCK, // Waiting for docking to complete.
+ MIDDLE, // Offload "bails" of tiberium.
+ WAIT_FOR_UNDOCK // Waiting for undocking to complete.
+ };
+ switch (Status) {
+ case INITIAL:
+ Status = WAIT_FOR_DOCK;
+ break;
+
+ case WAIT_FOR_DOCK:
+ if (IsReadyToCommence) {
+ IsReadyToCommence = false;
+ Status = MIDDLE;
+ }
+ break;
+
+ case MIDDLE:
+ if (IsReadyToCommence) {
+ IsReadyToCommence = false;
+
+ /*
+ ** Force any bib squatters to scatter.
+ */
+ Map[Adjacent_Cell(Coord_Cell(Center_Coord()), DIR_S)].Incoming(0, true, true);
+
+ FootClass * techno = Attached_Object();
+ if (techno) {
+ int bail = techno->Offload_Tiberium_Bail();
+
+ if (bail) {
+ House->Harvested(bail);
+ if (techno->Tiberium_Load() > 0) {
+ return(1);
+ }
+ }
+ }
+ Status = WAIT_FOR_UNDOCK;
+ }
+ break;
+
+ case WAIT_FOR_UNDOCK:
+ if (IsReadyToCommence) {
+
+ /*
+ ** Detach harvester and go back into idle state.
+ */
+ Assign_Mission(MISSION_GUARD);
+ }
+ break;
+
+ default:
+ break;
+ }
+ return(1);
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Mission_Repair -- Handles the repair (active) state for building. *
+ * *
+ * This state machine is used when the building is active in some sort of repair or *
+ * construction mode. The construction yard will animate. The repair facility will repair *
+ * anything that it docked on it. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the number of game frames to delay before calling this routine again. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/25/1995 JLB : Created. *
+ * 06/25/1995 JLB : Handles repair facility *
+ * 07/29/1995 JLB : Repair rate is controlled by power rating. *
+ *=============================================================================================*/
+int BuildingClass::Mission_Repair(void)
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ if (*this == STRUCT_CONST) {
+ enum {
+ INITIAL,
+ DURING
+ };
+ switch (Status) {
+ case INITIAL:
+ Begin_Mode(BSTATE_ACTIVE);
+ Status = DURING;
+ break;
+
+ case DURING:
+ if (!In_Radio_Contact()) {
+ Assign_Mission(MISSION_GUARD);
+ }
+ break;
+
+ default:
+ break;
+ }
+ return(1);
+ }
+
+ if (*this == STRUCT_REPAIR) {
+ enum {
+ INITIAL,
+ IDLE,
+ DURING
+ };
+ switch (Status) {
+ case INITIAL:
+ {
+ if (!In_Radio_Contact()) {
+ Begin_Mode(BSTATE_IDLE);
+ Assign_Mission(MISSION_GUARD);
+ return(1);
+ }
+ IsReadyToCommence = false;
+ int distance = 0x10;
+ TechnoClass *tech = Contact_With_Whom();
+
+ /*
+ ** BG: If the unit to repair is an aircraft, and the aircraft is
+ ** fixed-wing, and it's landed, be much more liberal with the
+ ** distance check. Fixed-wing aircraft are very inaccurate with
+ ** their landings.
+ */
+ if (tech->What_Am_I() == RTTI_AIRCRAFT) {
+ if ( ((AircraftClass *)tech)->Class->IsFixedWing &&
+ ((AircraftClass *)tech)->In_Which_Layer() == LAYER_GROUND) {
+ distance = 0x80;
+ }
+ }
+ if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER && Distance(Contact_With_Whom()) < distance) {
+ Status = IDLE;
+ return(TICKS_PER_SECOND/4);
+ }
+ break;
+ }
+
+ case IDLE:
+ if (!In_Radio_Contact()) {
+ Assign_Mission(MISSION_GUARD);
+ return(1);
+ }
+
+ if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) {
+ TechnoClass * radio = Contact_With_Whom();
+
+ if ( ((radio->Health_Ratio() < Rule.ConditionGreen) ||
+ (radio->What_Am_I() == RTTI_UNIT && *(UnitClass *)radio == UNIT_MINELAYER))
+ && Transmit_Message(RADIO_REPAIR) == RADIO_ROGER) {
+
+ /*
+ ** If the object over the repair bay is marked as useless, then
+ ** sell it back to get some money.
+ */
+ if (radio->IsUseless) {
+ if (!radio->House->IsHuman) {
+ radio->Sell_Back(1);
+ }
+ Status = INITIAL;
+ IsReadyToCommence = true;
+ } else {
+ if (IsOwnedByPlayer) Speak(VOX_REPAIRING);
+ Status = DURING;
+ Begin_Mode(BSTATE_ACTIVE);
+ IsReadyToCommence = false;
+ }
+ } else {
+// Transmit_Message(RADIO_RUN_AWAY);
+///*BG*/ if(radio->Health_Ratio() >= Rule.ConditionGreen) {
+// Transmit_Message(RADIO_RUN_AWAY);
+// }
+ }
+ }
+ break;
+
+ case DURING:
+ if (!In_Radio_Contact()) {
+ Begin_Mode(BSTATE_IDLE);
+ Status = IDLE;
+ return(1);
+ }
+
+ /*
+ ** Check to see if the repair light blink has completed and the attached
+ ** unit is not doing something else. If these conditions are favorable,
+ ** the repair can proceed another step.
+ */
+ if (IsReadyToCommence && Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) {
+ IsReadyToCommence = false;
+
+ /*
+ ** Tell the attached unit to repair one step. It will respond with how
+ ** it fared.
+ */
+ switch (Transmit_Message(RADIO_REPAIR)) {
+
+ /*
+ ** The repair step proceeded smoothly. Proceed normally with the
+ ** repair process.
+ */
+ case RADIO_ROGER:
+ break;
+
+ /*
+ ** The repair operation was aborted because of some reason. Presume
+ ** that the reason is because of low cash.
+ */
+ case RADIO_CANT:
+ if (IsOwnedByPlayer) Speak(VOX_NO_CASH);
+ Begin_Mode(BSTATE_IDLE);
+ Status = IDLE;
+ break;
+
+ /*
+ ** The repair step resulted in a completely repaired unit.
+ */
+ case RADIO_ALL_DONE:
+ if (IsOwnedByPlayer) Speak(VOX_UNIT_REPAIRED);
+// Transmit_Message(RADIO_RUN_AWAY);
+ Begin_Mode(BSTATE_IDLE);
+ Status = IDLE;
+ break;
+
+ /*
+ ** The repair step could not be completed because this unit is already
+ ** at full strength.
+ */
+ case RADIO_NEGATIVE:
+ default:
+// Transmit_Message(RADIO_RUN_AWAY);
+ Begin_Mode(BSTATE_IDLE);
+ Status = IDLE;
+ break;
+
+ }
+ }
+ return(1);
+
+ default:
+ break;
+ }
+ return(MissionControl[Mission].Normal_Delay());
+ }
+
+ if (*this == STRUCT_HELIPAD || *this == STRUCT_AIRSTRIP) {
+ enum {
+ INITIAL,
+ DURING
+ };
+ switch (Status) {
+ case INITIAL:
+ if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER && Transmit_Message(RADIO_PREPARED) == RADIO_NEGATIVE) {
+ Begin_Mode(BSTATE_ACTIVE);
+ Contact_With_Whom()->Assign_Mission(MISSION_SLEEP);
+ Status = DURING;
+ return(1);
+ }
+ Assign_Mission(MISSION_GUARD);
+ break;
+
+ case DURING:
+ if (IsReadyToCommence) {
+ if (!In_Radio_Contact() || Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_NEGATIVE) {
+ Assign_Mission(MISSION_GUARD);
+ return(1);
+ }
+
+ if (Transmit_Message(RADIO_PREPARED) == RADIO_ROGER) {
+ Contact_With_Whom()->Assign_Mission(MISSION_GUARD);
+ Assign_Mission(MISSION_GUARD);
+ return(1);
+ }
+
+ if (Transmit_Message(RADIO_RELOAD) != RADIO_ROGER) {
+ Assign_Mission(MISSION_GUARD);
+ Contact_With_Whom()->Assign_Mission(MISSION_GUARD);
+ return(1);
+ } else {
+ fixed pfrac = Saturate(House->Power_Fraction(), 1);
+ if (pfrac < fixed::_1_2) pfrac = fixed::_1_2;
+ int time = Inverse(pfrac) * Rule.ReloadRate * TICKS_PER_MINUTE;
+// int time = Bound((int)(TICKS_PER_SECOND * Saturate(House->Power_Fraction(), 1)), 0, TICKS_PER_SECOND);
+// time = (TICKS_PER_SECOND*3) - time;
+ IsReadyToCommence = false;
+ return(time);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ return(3);
+ }
+ return(TICKS_PER_SECOND);
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Mission_Missile -- State machine for nuclear missile launch. *
+ * *
+ * This handles the Temple of Nod launching its nuclear missile. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the number of frames to delay before calling this routine again. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/04/1995 JLB : Commented. *
+ *=============================================================================================*/
+int BuildingClass::Mission_Missile(void)
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ if (*this == STRUCT_ADVANCED_TECH) {
+ enum {
+ DOOR_OPENING,
+ LAUNCH_UP,
+ SATELLITE_DEPLOY,
+ DONE_LAUNCH
+ };
+
+ switch (Status) {
+
+ /*
+ ** The initial case is responsible for starting the door
+ ** opening on the building, the missile rising, and smoke broiling.
+ */
+ case DOOR_OPENING:
+ {
+#ifdef FIXIT_VERSION_3
+ COORDINATE door = Coord_Move(Center_Coord(), (DirType)0xC0, 0x30);
+ AnimClass * sput = new AnimClass(ANIM_SPUTDOOR, door);
+ if (sput) {
+ IsReadyToCommence = false;
+ Status = LAUNCH_UP;
+ AnimToTrack = sput->As_Target();
+ }
+#else
+ IsReadyToCommence = false;
+ COORDINATE door = Coord_Move(Center_Coord(), (DirType)0xC0, 0x30);
+ AnimClass * sput = new AnimClass(ANIM_SPUTDOOR, door);
+ Status = LAUNCH_UP;
+ AnimToTrack = sput->As_Target();
+ return(1);
+#endif
+ }
+
+ /*
+ ** Once the smoke has been going for a little while this
+ ** actually handles launching the missile into the air.
+ */
+ case LAUNCH_UP:
+ {
+ AnimClass * sput = As_Animation(AnimToTrack);
+ if (sput) {
+ if (sput->Fetch_Stage() >= 19) {
+ CELL center = Coord_Cell(Center_Coord());
+ CELL cell = XY_Cell( Cell_X(center), 1);
+ TARGET targ = ::As_Target(cell);
+
+ BulletClass * bullet = new BulletClass(BULLET_GPS_SATELLITE, targ, this, 200, WARHEAD_FIRE, MPH_ROCKET);
+ if (bullet) {
+ COORDINATE launch = Coord_Move(Center_Coord(), (DirType)0xC0, 0x30);
+ if (!bullet->Unlimbo(launch, DIR_N)) {
+ delete bullet;
+ bullet = NULL;
+ }
+ }
+
+ if (bullet) {
+ Assign_Mission(MISSION_GUARD);
+ }
+ }
+ }
+ }
+ return(1);
+ }
+ }
+
+ if (*this == STRUCT_MSLO) {
+ enum {
+ INITIAL,
+ DOOR_OPENING,
+ LAUNCH_UP,
+ LAUNCH_DOWN,
+ DONE_LAUNCH
+ };
+
+ switch (Status) {
+
+ /*
+ ** The initial case is responsible for starting the door
+ ** opening on the building.
+ */
+ case INITIAL:
+ IsReadyToCommence = false;
+ Begin_Mode(BSTATE_ACTIVE); // open the door
+ Status = DOOR_OPENING;
+ return(1);
+
+ /*
+ ** This polls for the case when the door is actually open and
+ ** then kicks off the missile smoke.
+ */
+ case DOOR_OPENING:
+ if (IsReadyToCommence) {
+ Begin_Mode(BSTATE_AUX1); // hold the door open
+ Status = LAUNCH_UP;
+ return(14);
+ }
+ return(1);
+
+ /*
+ ** Once the smoke has been going for a little while this
+ ** actually handles launching the missile into the air.
+ */
+ case LAUNCH_UP:
+ {
+ CELL center = Coord_Cell(Center_Coord());
+ CELL cell = XY_Cell( Cell_X(center), 1);
+ TARGET targ = ::As_Target(cell);
+ BulletClass * bullet = new BulletClass(BULLET_NUKE_UP, targ, this, 200, WARHEAD_HE, MPH_VERY_FAST);
+ if (bullet) {
+ COORDINATE launch = Coord_Move(Center_Coord(), (DirType)28, 0xA0);
+ if (!bullet->Unlimbo(launch, DIR_N)) {
+ delete bullet;
+ bullet = NULL;
+ }
+ }
+
+ if (bullet) {
+ Speak(VOX_ABOMB_LAUNCH);
+ Status = LAUNCH_DOWN;
+ /*
+ ** Hack: If it's the artificial nukes, don't let the bullets come down (as
+ ** they're the only ones that blow up). We know it's artificial if you're
+ ** at tech level 10 or below, because you can't build the nuclear silo until
+ ** tech level 15 or so.
+ */
+ if (House->Control.TechLevel <= 10) {
+ return(6);
+ }
+ bullet = new BulletClass(BULLET_NUKE_DOWN, ::As_Target(House->NukeDest), this, 200, WARHEAD_NUKE, MPH_VERY_FAST);
+ if (bullet) {
+ int celly = Cell_Y(House->NukeDest);
+ celly -= 64;
+ if (celly < 1) celly = 1;
+ COORDINATE start = Cell_Coord(XY_Cell(Cell_X(House->NukeDest), celly));
+ if (!bullet->Unlimbo(start, DIR_S)) {
+ delete bullet;
+ }
+ }
+ return(8 * TICKS_PER_SECOND);
+ }
+ }
+ return(1);
+
+ /*
+ ** Once the missile is in the air, this handles waiting for
+ ** the missile to be off the screen and then launching one down
+ ** over the target.
+ */
+ case LAUNCH_DOWN:
+ {
+ Begin_Mode(BSTATE_AUX2); // start the door closing
+
+#ifdef OBSOLETE
+ /*
+ ** Hack: If it's the artificial nukes, don't let the bullets come down (as
+ ** they're the only ones that blow up). We know it's artificial if you're
+ ** at tech level 10 or below, because you can't build the nuclear silo until
+ ** tech level 15 or so.
+ */
+ if (House->Control.TechLevel <= 10) {
+ Status = DONE_LAUNCH;
+ return(6);
+ }
+ BulletClass * bullet = new BulletClass(BULLET_NUKE_DOWN, ::As_Target(House->NukeDest), this, 200, WARHEAD_NUKE, MPH_VERY_FAST);
+ if (bullet) {
+ int celly = Cell_Y(House->NukeDest);
+ celly -= 15;
+ if (celly < 1) celly = 1;
+ COORDINATE start = Cell_Coord(XY_Cell(Cell_X(House->NukeDest), celly));
+ if (!bullet->Unlimbo(start, DIR_S)) {
+ delete bullet;
+ }
+ }
+ if (bullet) {
+#endif
+ Status = DONE_LAUNCH;
+ return(6);
+ }
+#ifdef OBSOLETE
+ }
+ return(1);
+#endif
+
+ /*
+ ** Once the missile is done launching this handles allowing
+ ** the building to sit there with its door closed.
+ */
+ case DONE_LAUNCH:
+ Begin_Mode(BSTATE_IDLE); // keep the door closed.
+ Assign_Mission(MISSION_GUARD);
+ return(60);
+ }
+ }
+ return(MissionControl[Mission].Normal_Delay());
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Revealed -- Reveals the building to the specified house. *
+ * *
+ * This routine will reveal the building to the specified house. It will handle updating *
+ * the sidebar for player owned buildings. A player owned building that hasn't been *
+ * revealed, is in a state of pseudo-limbo. It cannot be used for any of its special *
+ * abilities even though it exists on the map for all other purposes. *
+ * *
+ * INPUT: house -- The house that this building is being revealed to. *
+ * *
+ * OUTPUT: Was this building revealed by this procedure? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/25/1995 JLB : Created. *
+ *=============================================================================================*/
+bool BuildingClass::Revealed(HouseClass * house)
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ if (TechnoClass::Revealed(house)) {
+
+ if (!ScenarioInit) {
+ House->JustBuiltStructure = Class->Type;
+ House->IsBuiltSomething = true;
+ }
+ House->IsRecalcNeeded = true;
+
+ /*
+ ** Perform any grand opening here so that in the scenarios where a player
+ ** owned house is not yet revealed, it won't be reflected in the sidebar
+ ** selection icons.
+ */
+ if (!In_Radio_Contact() && House->IsHuman && Mission != MISSION_CONSTRUCTION) {
+ Grand_Opening();
+ } else {
+ if (!In_Radio_Contact() && !House->IsHuman && house == House && Mission != MISSION_CONSTRUCTION) {
+ Grand_Opening();
+ }
+ }
+
+ return(true);
+ }
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Enter_Idle_Mode -- The building will enter its idle mode. *
+ * *
+ * This routine is called when the exact mode of the building isn't known. By examining *
+ * the building's condition, this routine will assign an appropriate mission. *
+ * *
+ * INPUT: initial -- This this being called during scenario init? *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/25/1995 JLB : Created. *
+ *=============================================================================================*/
+void BuildingClass::Enter_Idle_Mode(bool initial)
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ /*
+ ** Assign an appropriate mission for the building. If the ScenarioInit flag is true, then
+ ** this must be an initial building. Start such buildings in idle state. For other buildings
+ ** it indicates that it is being placed during game play and thus it must start in
+ ** the "construction" mission.
+ */
+ MissionType mission = MISSION_GUARD;
+
+
+ if (!initial || ScenarioInit || Debug_Map) {
+ Begin_Mode(BSTATE_IDLE);
+ mission = MISSION_GUARD;
+ } else {
+ Begin_Mode(BSTATE_CONSTRUCTION);
+ mission = MISSION_CONSTRUCTION;
+ }
+ Assign_Mission(mission);
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Pip_Count -- Determines "full" pips to display for building. *
+ * *
+ * This routine will determine the number of pips that should be filled in when rendering *
+ * the building. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns the number of pips to display as filled in. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/28/1995 JLB : Created. *
+ *=============================================================================================*/
+int BuildingClass::Pip_Count(void) const
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ return(Class->Max_Pips() * House->Tiberium_Fraction());
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Death_Announcement -- Announce the death of this building. *
+ * *
+ * This routine is called when the building is destroyed by "unnatural" means. Typically *
+ * as a result of combat. If the building is known to the player, then it should be *
+ * announced. *
+ * *
+ * INPUT: source -- The object most directly responsible for the building's death. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/04/1995 JLB : Created. *
+ *=============================================================================================*/
+void BuildingClass::Death_Announcement(TechnoClass const * source) const
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ if (source != NULL && House->IsPlayerControl) {
+ Speak(VOX_STRUCTURE_DESTROYED);
+ }
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Fire_Direction -- Fetches the direction of firing. *
+ * *
+ * This routine will return with the default direction to use when firing from this *
+ * building. This is the facing of the turret except for the case of non-turret equipped *
+ * buildings that have a weapon (e.g., guard tower). *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the default firing direction for this building. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/04/1995 JLB : Created. *
+ *=============================================================================================*/
+DirType BuildingClass::Fire_Direction(void) const
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ if (Class->IsTurretEquipped) {
+ return(PrimaryFacing.Current());
+ }
+ return(Direction(TarCom));
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Remap_Table -- Fetches the remap table to use for this building. *
+ * *
+ * Use this routine to fetch the remap table to use. This override function is needed *
+ * because the default remap table for techno objects presumes the object is a unit. *
+ * Buildings aren't units. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the proper remap table to use for this building. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/08/1995 JLB : Created. *
+ *=============================================================================================*/
+void const * BuildingClass::Remap_Table(void)
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ return(House->Remap_Table(IsBlushing, Class->Remap));
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Mission_Unload -- Handles the unload mission for a building. *
+ * *
+ * This is the unload mission for a building. This really only applies to the weapon's *
+ * factory, since it needs the sophistication of an unload mission due to the door *
+ * animation. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the number of game frames to delay before calling this routine *
+ * again. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/29/1995 JLB : Created. *
+ *=============================================================================================*/
+int BuildingClass::Mission_Unload(void)
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ if (*this == STRUCT_WEAP) {
+ CELL cell = Coord_Cell(Coord) + Class->ExitList[0];
+ COORDINATE coord = Cell_Coord(cell);
+ CellClass * cellptr = &Map[cell];
+ enum {
+ INITIAL,
+ CLEAR_BIB,
+ OPEN,
+ LEAVE,
+ CLOSE
+ };
+ enum {
+ DOOR_STAGES = 5,
+ DOOR_RATE = 8
+ };
+ UnitClass * unit;
+ switch (Status) {
+ /*
+ ** Start the door opening.
+ */
+ case INITIAL:
+// if (cellptr->Cell_Techno()) {
+// cellptr->Incoming(0, true);
+// }
+ unit = (UnitClass *)Contact_With_Whom();
+ if (unit) {
+ unit->Assign_Mission(MISSION_GUARD);
+ unit->Commence();
+ }
+ Open_Door(DOOR_RATE, DOOR_STAGES);
+ Status = CLEAR_BIB;
+ break;
+
+ /*
+ ** Now that the occupants can peek out the door, they will tell
+ ** everyone that could be blocking the way, that they should
+ ** scatter away.
+ */
+ case CLEAR_BIB:
+ if (cellptr->Cell_Techno()) {
+ cellptr->Incoming(0, true, true);
+
+ /*
+ ** Scatter everything around the weapon's factory door.
+ */
+ for (FacingType f = FACING_FIRST; f < FACING_COUNT; f++) {
+ CellClass * cptr = &cellptr->Adjacent_Cell(f);
+ if (cptr->Cell_Building() == NULL) {
+ cptr->Incoming(coord, true, true);
+ }
+ }
+ } else {
+ Status = OPEN;
+ }
+ break;
+
+ /*
+ ** When the door is finally open and the way is clear, tell the
+ ** unit to drive out.
+ */
+ case OPEN:
+ if (Is_Door_Open()) {
+ unit = (UnitClass *)Contact_With_Whom();
+ if (unit) {
+ unit->Assign_Mission(MISSION_MOVE);
+
+ if (House->IQ >= Rule.IQGuardArea) {
+ unit->Assign_Mission(MISSION_GUARD_AREA);
+ unit->ArchiveTarget = ::As_Target(House->Where_To_Go(unit));
+ }
+ unit->Force_Track(DriveClass::OUT_OF_WEAPON_FACTORY, coord);
+// unit->Force_Track(DriveClass::OUT_OF_WEAPON_FACTORY, Adjacent_Cell(Adjacent_Cell(Center_Coord(), FACING_S), FACING_S));
+ unit->Set_Speed(128);
+ Status = LEAVE;
+ } else {
+ Close_Door(DOOR_RATE, DOOR_STAGES);
+ Status = CLOSE;
+ }
+ }
+ break;
+
+ /*
+ ** Wait until the unit has completely left the building.
+ */
+ case LEAVE:
+ if (!IsTethered) {
+ Close_Door(DOOR_RATE, DOOR_STAGES);
+ Status = CLOSE;
+ } else {
+
+// if (In_Radio_Contact() && !((FootClass *)Contact_With_Whom())->IsDriving) {
+// Transmit_Message(RADIO_OVER_OUT);
+// }
+
+ }
+ break;
+
+ /*
+ ** Wait while the door closes.
+ */
+ case CLOSE:
+ if (Is_Door_Closed()) {
+ Enter_Idle_Mode();
+ }
+ break;
+
+ default:
+ break;
+ }
+ return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2));
+ }
+
+ Assign_Mission(MISSION_GUARD);
+ return(1);
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Power_Output -- Fetches the current power output from this building. *
+ * *
+ * This routine will return the current power output for this building. The power output *
+ * is adjusted according to the damage level of the building. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns the current power output for this building. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/29/1995 JLB : Created. *
+ *=============================================================================================*/
+int BuildingClass::Power_Output(void) const
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ if (Class->Power) {
+ return(Class->Power * fixed(LastStrength, Class->MaxStrength));
+ }
+ return(0);
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Detach -- Handles target removal from the game system. *
+ * *
+ * This routine is called when the specified target is about to be removed from the game *
+ * system. *
+ * *
+ * INPUT: target -- The target to be removed from this building's targeting computer. *
+ * *
+ * all -- Is the target about to be completely eliminated? *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/29/1995 JLB : Created. *
+ *=============================================================================================*/
+void BuildingClass::Detach(TARGET target, bool all)
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ TechnoClass::Detach(target, all);
+ if (target == WhomToRepay) {
+ WhomToRepay = TARGET_NONE;
+ }
+ if (target == AnimToTrack) {
+ AnimToTrack = TARGET_NONE;
+ }
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Crew_Type -- This determines the crew that this object generates. *
+ * *
+ * When selling very cheap buildings (such as the silo), a technician will pop out since *
+ * generating minigunners would be overkill -- the player could use this loophole to *
+ * gain an advantage. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns the infantry type that this building will generate as a survivor. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/05/1995 JLB : Created. *
+ *=============================================================================================*/
+InfantryType BuildingClass::Crew_Type(void) const
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ switch (Class->Type) {
+ case STRUCT_STORAGE:
+ if (Percent_Chance(50)) {
+ return(INFANTRY_C1);
+ } else {
+ return(INFANTRY_C7);
+ }
+
+ case STRUCT_CONST:
+ if (!IsCaptured && House->IsHuman && Percent_Chance(25)) {
+ return(INFANTRY_RENOVATOR);
+ }
+ break;
+
+ case STRUCT_KENNEL:
+ if (Percent_Chance(50)) {
+ return(INFANTRY_DOG);
+ } else {
+ return(INFANTRY_NONE);
+ }
+
+ case STRUCT_TENT:
+ case STRUCT_BARRACKS:
+ return(INFANTRY_E1);
+
+ default:
+ break;
+ }
+ return(TechnoClass::Crew_Type());
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Detach_All -- Possibly abandons production according to factory type. *
+ * *
+ * When this routine is called, it indicates that the building is about to be destroyed *
+ * or captured. In such a case any production it may be doing, must be abandoned. *
+ * *
+ * INPUT: all -- Is the object about the be completely destroyed? *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/05/1995 JLB : Created. *
+ *=============================================================================================*/
+void BuildingClass::Detach_All(bool all)
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ /*
+ ** If it is producing something, then it must be abandoned.
+ */
+ if (Factory) {
+ Factory->Abandon();
+ delete (FactoryClass *)Factory;
+ Factory = 0;
+ }
+
+ /*
+ ** If the owner HouseClass is building something, and this building can
+ ** build that thing, we may be the last building for that house that can
+ ** build that thing; if so, abandon production of it.
+ */
+ if (House) {
+ FactoryClass * factory = House->Fetch_Factory(Class->ToBuild);
+
+ /*
+ ** If a factory was found, then temporarily disable this building and then
+ ** determine if any object that is being produced can still be produced. If
+ ** not, then the object being produced must be abandoned.
+ */
+ if (factory) {
+ TechnoClass * object = factory->Get_Object();
+ IsInLimbo = true;
+ if (object && !object->Techno_Type_Class()->Who_Can_Build_Me(true, false, House->Class->House)) {
+ House->Abandon_Production(Class->ToBuild);
+ }
+ IsInLimbo = false;
+ }
+ }
+
+ TechnoClass::Detach_All(all);
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Flush_For_Placement -- Handles clearing a zone for object placement. *
+ * *
+ * This routine is used to clear the way for placement of the specified object (usually *
+ * a building). If there are friendly units blocking the placement area, they are told *
+ * to scatter. Enemy blocking units are attacked. *
+ * *
+ * INPUT: techno -- Pointer to the object that is desired to be placed. *
+ * *
+ * cell -- The cell that placement wants to occur at. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/06/1995 JLB : Created. *
+ * 09/27/1995 JLB : Revised to use type class function. *
+ *=============================================================================================*/
+bool BuildingClass::Flush_For_Placement(TechnoClass * techno, CELL cell)
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ if (techno) {
+ return (((BuildingTypeClass const &)techno->Class_Of()).Flush_For_Placement(cell, House));
+ }
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Find_Exit_Cell -- Find a clear location to exit an object from this building *
+ * *
+ * This routine is called when the building needs to discharge a unit. It will find a *
+ * nearby (adjacent) cell that is clear enough for the specified object to enter. Typical *
+ * use of this routine is when the airfield disgorges its cargo. *
+ * *
+ * INPUT: techno -- Pointer to the object that wishes to exit this building. *
+ * *
+ * OUTPUT: Returns with the cell number to use for object placement. If no free location *
+ * could be found, then zero (0) is returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 09/21/1995 JLB : Created. *
+ * 02/20/1996 JLB : Added default case for exit cell calculation. *
+ *=============================================================================================*/
+CELL BuildingClass::Find_Exit_Cell(TechnoClass const * techno) const
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ CELL const * ptr;
+ CELL origin = Coord_Cell(Coord);
+
+ ptr = Class->ExitList;
+ if (ptr != NULL) {
+ while (*ptr != REFRESH_EOL) {
+ CELL cell = origin + *ptr++;
+ if (Map.In_Radar(cell) && techno->Can_Enter_Cell(cell) == MOVE_OK) {
+ return(cell);
+ }
+ }
+ } else {
+ int x1, x2;
+ int y1, y2;
+ CELL cell;
+
+ y1 = -1;
+ y2 = Class->Height();
+ for (x1 = -1; x1 <= Class->Width(); x1++) {
+ cell = origin + x1 + (y1 * MAP_CELL_W);
+ if (Map.In_Radar(cell) && techno->Can_Enter_Cell(cell) == MOVE_OK) {
+ return(cell);
+ }
+ cell = origin + x1 + (y2 * MAP_CELL_W);
+ if (Map.In_Radar(cell) && techno->Can_Enter_Cell(cell) == MOVE_OK) {
+ return(cell);
+ }
+ }
+
+ x1 = -1;
+ x2 = Class->Width();
+ for (y1 = -1; y1 <= Class->Height(); y1++) {
+ cell = origin + (y1 * MAP_CELL_W) + x1;
+ if (Map.In_Radar(cell) && techno->Can_Enter_Cell(cell) == MOVE_OK) {
+ return(cell);
+ }
+ cell = origin + (y1 * MAP_CELL_W) + x2;
+ if (Map.In_Radar(cell) && techno->Can_Enter_Cell(cell) == MOVE_OK) {
+ return(cell);
+ }
+ }
+ }
+ return(0);
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Can_Player_Move -- Can this building be moved? *
+ * *
+ * This routine answers the question 'can this building be moved?' Typically, only the *
+ * construction yard can be moved and it does this by undeploying back into a MCV. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Can the building move to a new location under player control? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 10/04/1995 JLB : Created. *
+ *=============================================================================================*/
+bool BuildingClass::Can_Player_Move(void) const
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ return(*this == STRUCT_CONST);
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Exit_Coord -- Determines location where object will leave it. *
+ * *
+ * This routine will return the coordinate where an object that wishes to leave the *
+ * building will exit at. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the coordinate that the object should be created at. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 02/20/1996 JLB : Created. *
+ *=============================================================================================*/
+COORDINATE BuildingClass::Exit_Coord(void) const
+{
+ assert(Buildings.ID(this) == ID);
+ assert(IsActive);
+
+ if (Class->ExitCoordinate) {
+ return(Coord_Add(Coord, Class->ExitCoordinate));
+ }
+ return(TechnoClass::Exit_Coord());
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Check_Point -- Fetches the landing checkpoint for the given flight pattern. *
+ * *
+ * Use this routine to coordinate a landing operation. The specified checkpoint is *
+ * converted into a cell number. The landing aircraft should fly over that cell and then *
+ * request the next check point. *
+ * *
+ * INPUT: cp -- The check point to convert to a cell number. *
+ * *
+ * OUTPUT: Returns with the cell that the aircraft should fly over in order to complete *
+ * that portion of the landing pattern. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/06/1996 JLB : Created. *
+ *=============================================================================================*/
+CELL BuildingClass::Check_Point(CheckPointType cp) const
+{
+ CELL xoffset = 6; // Downwind offset.
+ CELL yoffset = 5; // Crosswind offset.
+ CELL cell = Coord_Cell(Center_Coord());
+
+ switch (cp) {
+ case CHECK_STACK:
+ xoffset = 0;
+ break;
+
+ case CHECK_CROSSWIND:
+ yoffset = 0;
+ break;
+
+ case CHECK_DOWNWIND:
+ default:
+ break;
+ }
+
+ if ((Cell_X(cell) - Map.MapCellX) > Map.MapCellWidth/2) {
+ xoffset = -xoffset;
+ }
+
+ if ((Cell_Y(cell) - Map.MapCellY) > Map.MapCellHeight/2) {
+ yoffset = -yoffset;
+ }
+
+ return(XY_Cell(Cell_X(cell)+xoffset, Cell_Y(cell)+yoffset));
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Update_Radar_Spied - set house's RadarSpied field appropriately. *
+ * *
+ * This routine is called when a radar facility is captured or destroyed. It fills in the *
+ * RadarSpied field of the house based on whether there's a spied-upon radar facility or not*
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: House->RadarSpied field gets set appropriately. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 03/22/1996 BWG : Created. *
+ *=============================================================================================*/
+void BuildingClass::Update_Radar_Spied(void)
+{
+ House->RadarSpied = 0;
+ for (int index = 0; index < Buildings.Count(); index++) {
+ BuildingClass * obj = Buildings.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House == House) {
+ if (*obj == STRUCT_RADAR /* || *obj == STRUCT_EYE */) {
+ House->RadarSpied |= obj->SpiedBy;
+ }
+ }
+ }
+ Map.RadarClass::Flag_To_Redraw(true);
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Read_INI -- Reads buildings from INI file. *
+ * *
+ * This is the basic scenario initialization of building function. It *
+ * is called when reading the scenario startup INI file and it handles *
+ * creation of all specified buildings. *
+ * *
+ * INI entry format: *
+ * Housename, Typename, Strength, Cell, Facing, Triggername *
+ * *
+ * INPUT: buffer -- Pointer to the loaded INI file data. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/24/1994 JLB : Created. *
+ *=============================================================================================*/
+void BuildingClass::Read_INI(CCINIClass & ini)
+{
+ BuildingClass * b; // Working unit pointer.
+ HousesType bhouse; // Building house.
+ StructType classid; // Building type.
+ CELL cell; // Cell of building.
+ char buf[128];
+ char * trigname; // building's trigger's name
+
+
+ int len = ini.Entry_Count(INI_Name());
+ for (int index = 0; index < len; index++) {
+ char const * entry = ini.Get_Entry(INI_Name(), index);
+
+ /*
+ ** Get a building entry.
+ */
+ ini.Get_String(INI_Name(), entry, NULL, buf, sizeof(buf));
+
+ /*
+ ** 1st token: house name.
+ */
+ bhouse = HouseTypeClass::From_Name(strtok(buf, ","));
+
+ /*
+ ** 2nd token: building name.
+ */
+ classid = BuildingTypeClass::From_Name(strtok(NULL, ","));
+
+ if (bhouse != HOUSE_NONE && classid != STRUCT_NONE) {
+ int strength;
+ DirType facing;
+
+ /*
+ ** 3rd token: strength.
+ */
+ strength = atoi(strtok(NULL, ","));
+
+ /*
+ ** 4th token: cell #.
+ */
+ cell = atoi(strtok(NULL, ","));
+
+ /*
+ ** 5th token: facing.
+ */
+ facing = (DirType)atoi(strtok(NULL, ","));
+
+ /*
+ ** 6th token: triggername (can be NULL).
+ */
+ trigname = strtok(NULL, ",");
+
+ bool sellable = false;
+ char * token_pointer = strtok(NULL, ",");
+ if (token_pointer) {
+ sellable = atoi(token_pointer);
+ }
+
+ bool rebuild = false;
+ token_pointer = strtok(NULL, ",");
+ if (token_pointer) {
+ rebuild = atoi(token_pointer);
+ }
+
+ b = new BuildingClass(classid, bhouse);
+ if (b) {
+
+ TriggerTypeClass * tp = TriggerTypeClass::From_Name(trigname);
+ if (tp) {
+ TriggerClass * tt = Find_Or_Make(tp);
+ if (tt) {
+ tt->AttachCount++;
+ b->Trigger = tt;
+ }
+ }
+ b->IsAllowedToSell = sellable;
+ b->IsToRebuild = rebuild;
+ b->IsToRepair = rebuild || *b == STRUCT_CONST;
+
+ if (b->Unlimbo(Cell_Coord(cell), facing)) {
+ strength = min(strength, 0x100);
+ strength = b->Class->MaxStrength * fixed(strength, 256);
+ b->Strength = strength;
+ if (b->Strength > b->Class->MaxStrength-3) b->Strength = b->Class->MaxStrength;
+ b->IsALemon = false;
+ } else {
+
+ /*
+ ** If the building could not be unlimboed on the map, then this indicates
+ ** a serious error. Delete the building.
+ */
+ delete b;
+ }
+ }
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Write_INI -- Write out the building data to the INI file specified. *
+ * *
+ * This will store the building data (as it relates to scenario initialization) to the *
+ * INI database specified. *
+ * *
+ * INPUT: ini -- Reference to the INI database that the building data will be stored to. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/06/1996 JLB : Created. *
+ *=============================================================================================*/
+void BuildingClass::Write_INI(CCINIClass & ini)
+{
+ /*
+ ** First, clear out all existing building data from the ini file.
+ */
+ ini.Clear(INI_Name());
+
+ /*
+ ** Write the data out.
+ */
+ for (int index = 0; index < Buildings.Count(); index++) {
+ BuildingClass * building = Buildings.Ptr(index);
+ if (!building->IsInLimbo) {
+ char uname[10];
+ char buf[127];
+
+ sprintf(uname, "%d", index);
+ sprintf(buf, "%s,%s,%d,%u,%d,%s,%d,%d",
+ building->House->Class->IniName,
+ building->Class->IniName,
+ building->Health_Ratio()*256,
+ Coord_Cell(building->Coord),
+ building->PrimaryFacing.Current(),
+ building->Trigger.Is_Valid() ? building->Trigger->Class->IniName : "None",
+ building->IsAllowedToSell,
+ building->IsToRebuild
+ );
+ ini.Put_String(INI_Name(), uname, buf);
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Target_Coord -- Return the coordinate to use when firing on this building. *
+ * *
+ * This routine will determine the "center" location of this building for purposes of *
+ * targeting. Usually, this location is somewhere near the foundation of the building. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the coordinate to use when firing upon this building (or trying to *
+ * walk onto it). *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/19/1996 JLB : Created. *
+ *=============================================================================================*/
+COORDINATE BuildingClass::Target_Coord(void) const
+{
+ COORDINATE coord = Center_Coord();
+
+ if (Class->FoundationFace != FACING_NONE) {
+ return(Adjacent_Cell(coord, Class->FoundationFace));
+ }
+ return(coord);
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Factory_AI -- Handle factory production and initiation. *
+ * *
+ * Some building (notably the computer controlled ones) can have a factory object attached. *
+ * This routine handles processing of that factory and also detecting when production *
+ * should begin in order to initiate production. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: Only call this routine once per building per game logic loop. *
+ * *
+ * HISTORY: *
+ * 07/29/1996 JLB : Created. *
+ *=============================================================================================*/
+void BuildingClass::Factory_AI(void)
+{
+ /*
+ ** Handle any production tied to this building. Only computer controlled buildings have
+ ** production attached to the building itself. The player uses the sidebar interface for
+ ** all production control.
+ */
+ if (Factory.Is_Valid() && Factory->Has_Completed() && PlacementDelay == 0) {
+ TechnoClass * product = Factory->Get_Object();
+// FactoryClass * fact = Factory;
+
+ switch (Exit_Object(product)) {
+
+ /*
+ ** If the object could not leave the factory, then either request
+ ** a transport, place the (what must be a) building using another method, or
+ ** abort the production and refund money.
+ */
+ case 0:
+ Factory->Abandon();
+ delete (FactoryClass *)Factory;
+ Factory = 0;
+ break;
+
+ /*
+ ** Exiting this building is prevented by some temporary blockage. Wait
+ ** a bit before trying again.
+ */
+ case 1:
+ PlacementDelay = TICKS_PER_SECOND*3;
+ break;
+
+ /*
+ ** The object was successfully sent from this factory. Inform the house
+ ** tracking logic that the requested object has been produced.
+ */
+ case 2:
+ switch (product->What_Am_I()) {
+ case RTTI_VESSEL:
+ House->JustBuiltVessel = ((VesselClass*)product)->Class->Type;
+ House->IsBuiltSomething = true;
+ break;
+
+ case RTTI_UNIT:
+ House->JustBuiltUnit = ((UnitClass*)product)->Class->Type;
+ House->IsBuiltSomething = true;
+ break;
+
+ case RTTI_INFANTRY:
+ House->JustBuiltInfantry = ((InfantryClass*)product)->Class->Type;
+ House->IsBuiltSomething = true;
+ break;
+
+ case RTTI_BUILDING:
+ House->JustBuiltStructure = ((BuildingClass*)product)->Class->Type;
+ House->IsBuiltSomething = true;
+ break;
+
+ case RTTI_AIRCRAFT:
+ House->JustBuiltAircraft = ((AircraftClass*)product)->Class->Type;
+ House->IsBuiltSomething = true;
+ break;
+
+ default:
+ break;
+ }
+// fact->Completed();
+ Factory->Completed();
+// delete fact;
+ delete (FactoryClass *)Factory;
+ Factory = 0;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /*
+ ** Pick something to create for this factory.
+ */
+ if (House->IsStarted && Mission != MISSION_CONSTRUCTION && Mission != MISSION_DECONSTRUCTION) {
+
+ /*
+ ** Buildings that produce other objects have special factory logic handled here.
+ */
+ if (Class->ToBuild != RTTI_NONE) {
+ if (Factory.Is_Valid()) {
+
+ /*
+ ** If production has halted, then just abort production and make the
+ ** funds available for something else.
+ */
+ if (PlacementDelay == 0 && !Factory->Is_Building()) {
+ Factory->Abandon();
+ delete (FactoryClass *)Factory;
+ Factory = 0;
+ }
+
+ } else {
+
+ /*
+ ** Only look to start production if there is at least a small amount of
+ ** money available. In cases where there is no practical money left, then
+ ** production can never complete -- don't bother starting it.
+ */
+ if (House->IsStarted && House->Available_Money() > 10) {
+ TechnoTypeClass const * techno = House->Suggest_New_Object(Class->ToBuild, *this == STRUCT_KENNEL);
+
+ /*
+ ** If a suitable object type was selected for production, then start
+ ** producing it now.
+ */
+ if (techno != NULL) {
+ Factory = new FactoryClass;
+ if (Factory.Is_Valid()) {
+ if (!Factory->Set(*techno, *House)) {
+ delete (FactoryClass *)Factory;
+ Factory = 0;
+ } else {
+ House->Production_Begun(Factory->Get_Object());
+ Factory->Start();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Rotation_AI -- Process any turret rotation required of this building. *
+ * *
+ * Some buildings have a turret and this routine handles processing the turret rotation. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: Only call this routine once per building per game logic loop. *
+ * *
+ * HISTORY: *
+ * 07/29/1996 JLB : Created. *
+ * 10/27/1996 JLB : Rotation does not occur if power and no power avail. *
+ *=============================================================================================*/
+void BuildingClass::Rotation_AI(void)
+{
+ if (Class->IsTurretEquipped &&
+ Mission != MISSION_CONSTRUCTION &&
+ Mission != MISSION_DECONSTRUCTION &&
+ (!Class->IsPowered || House->Power_Fraction() >= 1)) {
+
+ /*
+ ** Rotate turret to match desired facing.
+ */
+ if (PrimaryFacing.Is_Rotating()) {
+ if (PrimaryFacing.Rotation_Adjust(Class->ROT)) {
+ Mark(MARK_CHANGE);
+ }
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Charging_AI -- Handles the special charging logic for Tesla coils. *
+ * *
+ * This handles the special logic required of the charging tesla coil. It requires special *
+ * processing since its charge up is dependant upon the target and power surplus of the *
+ * owning house. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/29/1996 JLB : Created. *
+ *=============================================================================================*/
+void BuildingClass::Charging_AI(void)
+{
+ if (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->IsElectric && BState != BSTATE_CONSTRUCTION) {
+ if (Target_Legal(TarCom) && House->Power_Fraction() >= 1) {
+ if (!IsCharged) {
+ if (IsCharging) {
+// if (stagechange) {
+ Mark(MARK_CHANGE);
+ if (Fetch_Stage() >= 9) {
+ IsCharged = true;
+ IsCharging = false;
+ Set_Rate(0);
+ }
+// }
+ } else if (!Arm) {
+ IsCharged = false;
+ IsCharging = true;
+ Set_Stage(0);
+ Set_Rate(3);
+ Sound_Effect(VOC_TESLA_POWER_UP, Coord);
+ }
+ }
+ } else {
+ if (IsCharging || IsCharged) {
+ Mark(MARK_CHANGE);
+ IsCharging = false;
+ IsCharged = false;
+ Set_Stage(0);
+ Set_Rate(0);
+ }
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Repair_AI -- Handle the repair (and sell) logic for the building. *
+ * *
+ * This routine handle the repair animation and healing logic. It also detects when the *
+ * (computer controlled) building should begin repair or sell itself. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: Only call this routine once per building per game logic loop. *
+ * *
+ * HISTORY: *
+ * 07/29/1996 JLB : Created. *
+ *=============================================================================================*/
+void BuildingClass::Repair_AI(void)
+{
+ if (House->IQ >= Rule.IQRepairSell && Mission != MISSION_CONSTRUCTION && Mission != MISSION_DECONSTRUCTION) {
+ /*
+ ** Possibly start repair process if the building is below half strength.
+ */
+// unsigned ratio = MIN(House->Smartness, 0x00F0);
+ if (Can_Repair()) {
+ if (House->Available_Money() >= Rule.RepairThreshhold) {
+ if (!House->DidRepair) {
+ if (!IsRepairing && (IsCaptured || IsToRepair || House->IsHuman || Session.Type != GAME_NORMAL)) {
+ House->DidRepair = true; // flag that this house did its repair allocation for this frame
+ Repair(1);
+
+ if (!House->IsHuman) {
+ House->RepairTimer = Random_Pick((int)(House->RepairDelay * (TICKS_PER_MINUTE/4)), (int)(House->RepairDelay * TICKS_PER_MINUTE * 2));
+ }
+ }
+ }
+ } else {
+ if ((Session.Type != GAME_NORMAL || IsAllowedToSell) && IsTickedOff && House->Control.TechLevel >= Rule.IQSellBack && Random_Pick(0, 50) < House->Control.TechLevel && !Trigger.Is_Valid() && *this != STRUCT_CONST && Health_Ratio() < Rule.ConditionRed) {
+ Sell_Back(1);
+ }
+ }
+ }
+ }
+
+ /*
+ ** If it is repairing, then apply any repair effects as necessary.
+ */
+ if (IsRepairing && (Frame % (Rule.RepairRate * TICKS_PER_MINUTE)) == 0) {
+ IsWrenchVisible = (IsWrenchVisible == false);
+ Mark(MARK_CHANGE);
+ int cost = Class->Repair_Cost();
+ int step = Class->Repair_Step();
+
+ /*
+ ** Check for and expend any necessary monies to continue the repair.
+ */
+ if (House->Available_Money() >= cost) {
+ House->Spend_Money(cost);
+ Strength += step;
+
+ if (Strength >= Class->MaxStrength) {
+ Strength = Class->MaxStrength;
+ IsRepairing = false;
+ }
+ } else {
+ IsRepairing = false;
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Animation_AI -- Handles normal building animation processing. *
+ * *
+ * This will process the general building animation mechanism. It detects when the *
+ * building animation sequence has completed and flags the building to perform mission *
+ * changes as a result. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: Call this routine only once per building per game logic loop. *
+ * *
+ * HISTORY: *
+ * 07/29/1996 JLB : Created. *
+ *=============================================================================================*/
+void BuildingClass::Animation_AI(void)
+{
+ bool stagechange = Graphic_Logic();
+ bool toloop = false;
+
+ /*
+ ** Always refresh the SAM site if it has an animation change.
+ */
+ if (*this == STRUCT_SAM && stagechange) Mark(MARK_CHANGE);
+
+ if ((!Class->IsTurretEquipped && *this != STRUCT_TESLA) || Mission == MISSION_CONSTRUCTION || Mission == MISSION_DECONSTRUCTION) {
+ if (stagechange) {
+
+ /*
+ ** Check for animation end or if special case of MCV deconstructing when it is allowed
+ ** to convert back into an MCV.
+ */
+ BuildingTypeClass::AnimControlType const * ctrl = Fetch_Anim_Control();
+
+ /*
+ ** When the last frame of the current animation sequence is reached, flag that
+ ** a new mission may be started. This must occur before the animation actually
+ ** loops so that if a mission change does occur, it will have a chance to change
+ ** the building graphic before the last frame is replaced by the first frame of
+ ** the loop.
+ */
+ if (Fetch_Stage() == ctrl->Start+ctrl->Count-1 || (!Target_Legal(ArchiveTarget) /*Special.IsMCVDeploy*/ && *this == STRUCT_CONST && Mission == MISSION_DECONSTRUCTION && Fetch_Stage() == (42-19))) {
+ IsReadyToCommence = true;
+ }
+
+ /*
+ ** If the animation advances beyond the last frame, then start the animation
+ ** sequence over from the beginning.
+ */
+ if (Fetch_Stage() >= ctrl->Start+ctrl->Count) {
+ toloop = true;
+ }
+ Mark(MARK_CHANGE);
+ } else {
+ if (BState == BSTATE_NONE || Fetch_Rate() == 0) {
+ IsReadyToCommence = true;
+ }
+ }
+ }
+
+ /*
+ ** If there is a door that is animating, then it might cause this building
+ ** to be redrawn. Check for and flag to redraw as necessary.
+ */
+ if (Time_To_Redraw()) {
+ Clear_Redraw_Flag();
+ Mark(MARK_CHANGE);
+ }
+
+ /*
+ ** The animation sequence has looped. Restart it and flag this loop condition.
+ ** This is used to tell the mission system that the animation has completed. It
+ ** also signals that now is a good time to act on any pending mission.
+ */
+ if (toloop) {
+ BuildingTypeClass::AnimControlType const * ctrl = Fetch_Anim_Control();
+ if (BState == BSTATE_CONSTRUCTION || BState == BSTATE_IDLE) {
+ Set_Rate(Options.Normalize_Delay(ctrl->Rate));
+ } else {
+ Set_Rate(ctrl->Rate);
+ }
+ Set_Stage(ctrl->Start);
+ Mark(MARK_CHANGE);
+ }
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::How_Many_Survivors -- This determine the maximum number of survivors. *
+ * *
+ * This routine is called to determine how many survivors should run from this building *
+ * when it is either sold or destroyed. Buildings that are captured have fewer survivors. *
+ * The number of survivors is a portion of the cost of the building divided by the cost *
+ * of a minigunner. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the number of soldiers that should run from this building. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/04/1996 JLB : Created. *
+ *=============================================================================================*/
+int BuildingClass::How_Many_Survivors(void) const
+{
+ if (IsSurvivorless || !Class->IsCrew) return(0);
+
+ int divisor = InfantryTypeClass::As_Reference(INFANTRY_E1).Raw_Cost();
+ if (divisor == 0) return(0);
+ if (IsCaptured) divisor *= 2;
+ int count = (Class->Raw_Cost() * Rule.SurvivorFraction) / divisor;
+ return(Bound(count, 1, 5));
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Get_Image_Data -- Fetch the image pointer for the building. *
+ * *
+ * This routine will return with a pointer to the shape data for the building. The shape *
+ * data is different than normal when the building is undergoing construction and *
+ * disassembly. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with a pointer to the shape data for this building. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/06/1996 JLB : Created. *
+ *=============================================================================================*/
+void const * BuildingClass::Get_Image_Data(void) const
+{
+ if (BState == BSTATE_CONSTRUCTION) {
+ return(Class->Get_Buildup_Data());
+ }
+ return(TechnoClass::Get_Image_Data());
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Value -- Determine the value of this building. *
+ * *
+ * The value of the building is normally just its ordinary assigned value. However, in the *
+ * case of fakes, the value is artificially enhanced to match the structure that is *
+ * being faked. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the point value of the building type. *
+ * *
+ * WARNINGS: The point value returned should not be used for scoring, only for target *
+ * scanning. *
+ * *
+ * HISTORY: *
+ * 09/16/1996 JLB : Created. *
+ *=============================================================================================*/
+int BuildingClass::Value(void) const
+{
+ if (Class->IsFake) {
+ switch (Class->Type) {
+ case STRUCT_FAKEWEAP:
+ return(BuildingTypeClass::As_Reference(STRUCT_WEAP).Reward + BuildingTypeClass::As_Reference(STRUCT_WEAP).Risk);
+
+ case STRUCT_FAKECONST:
+ return(BuildingTypeClass::As_Reference(STRUCT_CONST).Reward + BuildingTypeClass::As_Reference(STRUCT_CONST).Risk);
+
+ case STRUCT_FAKE_YARD:
+ return(BuildingTypeClass::As_Reference(STRUCT_SHIP_YARD).Reward + BuildingTypeClass::As_Reference(STRUCT_SHIP_YARD).Risk);
+
+ case STRUCT_FAKE_PEN:
+ return(BuildingTypeClass::As_Reference(STRUCT_SUB_PEN).Reward + BuildingTypeClass::As_Reference(STRUCT_SUB_PEN).Risk);
+
+ case STRUCT_FAKE_RADAR:
+ return(BuildingTypeClass::As_Reference(STRUCT_RADAR).Reward + BuildingTypeClass::As_Reference(STRUCT_RADAR).Risk);
+
+ default:
+ break;
+ }
+ }
+ return(TechnoClass::Value());
+}
+
+
+/***********************************************************************************************
+ * BuildingClass::Remove_Gap_Effect -- Stop a gap generator from jamming cells. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: *
+ * *
+ * HISTORY: *
+ * 09/20/1996 BWG : Created. *
+ *=============================================================================================*/
+void BuildingClass::Remove_Gap_Effect(void)
+{
+ // unjam this one's field...
+ Map.UnJam_From(Coord_Cell(Center_Coord()), Rule.GapShroudRadius, House);
+ if (!House->IsPlayerControl && PlayerPtr->IsGPSActive) {
+ Map.Sight_From(Coord_Cell(Center_Coord()), Rule.GapShroudRadius, PlayerPtr);
+ }
+ // and rejam any overlapping buildings' fields
+ for (int index = 0; index < Buildings.Count(); index++) {
+ BuildingClass *obj = Buildings.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House == House && *obj == STRUCT_GAP && obj!=this) {
+ obj->IsJamming = false;
+ obj->Arm = 0;
+// Map.Jam_From(Coord_Cell(obj->Center_Coord()), Rule.GapShroudRadius, PlayerPtr);
+ }
+ }
+}
+
+
+short const * BuildingClass::Overlap_List(bool redraw) const
+{
+ if ((SpiedBy & (1 << PlayerPtr->Class->House)) != 0 && IsSelected && (*this == STRUCT_BARRACKS || *this == STRUCT_TENT)) {
+ static short const _list[] = {
+ -1, 2, (MAP_CELL_W*1)-1, (MAP_CELL_W*1)+2, REFRESH_EOL
+ };
+ return(_list);
+ }
+ return(TechnoClass::Overlap_List(redraw));
+}
diff --git a/CODE/BUILDING.H b/CODE/BUILDING.H
new file mode 100644
index 0000000..ecc5d0b
--- /dev/null
+++ b/CODE/BUILDING.H
@@ -0,0 +1,356 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/BUILDING.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : BUILDING.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : April 14, 1994 *
+ * *
+ * Last Update : April 14, 1994 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifndef BUILDING_H
+#define BUILDING_H
+
+#include "radio.h"
+#include "cargo.h"
+#include "mission.h"
+#include "bullet.h"
+#include "target.h"
+#include "factory.h"
+#include "techno.h"
+
+#define MAX_DOOR_STAGE 18 // # of frames of door opening on weapons factory
+#define DOOR_OPEN_STAGE 9 // frame on which the door is entirely open
+#define MAX_REPAIR_ANIM_STAGE 5 // # of stages of anim for repair center cycling
+
+/****************************************************************************
+** For each instance of a building in the game, there is one of
+** these structures. This structure holds information that is specific
+** and dynamic for a particular building.
+*/
+class BuildingClass : public TechnoClass
+{
+ public:
+
+ /*
+ ** This points to the control data that gives this building its characteristics.
+ */
+ CCPtr Class;
+
+ /*
+ ** If this building is in the process of producing something, then this
+ ** will point to the factory manager.
+ */
+ CCPtr Factory;
+
+ /*
+ ** This is the house that originally owned this factory. Objects buildable
+ ** by this house type will be produced from this factory regardless of who
+ ** the current owner is.
+ */
+ HousesType ActLike;
+
+ /*
+ ** This building should be rebuilt if it is destroyed. This is in spite
+ ** of the condition of the prebuilt base list.
+ */
+ unsigned IsToRebuild:1;
+
+ /*
+ ** Is the building allowed to repair itself?
+ */
+ unsigned IsToRepair:1;
+
+ /*
+ ** If the computer owns this building, then it is allowed to sell it if
+ ** the situation warrants it. In the other case, it cannot sell the
+ ** building regardless of conditions.
+ */
+ unsigned IsAllowedToSell:1;
+
+ /*
+ ** If the building is at a good point to change orders, then this
+ ** flag will be set to true.
+ */
+ unsigned IsReadyToCommence:1;
+
+ /*
+ ** If this building is currently spending money to repair itself, then
+ ** this flag is true. It will automatically be set to false when the building
+ ** has reached full strength, when money is exhausted, or if the player
+ ** specifically stops the repair process.
+ */
+ unsigned IsRepairing:1;
+
+ /*
+ ** If repair is currently in progress and this flag is true, then a wrench graphic
+ ** will be overlaid on the building to give visual feedback for the repair process.
+ */
+ unsigned IsWrenchVisible:1;
+
+ /*
+ ** This flag is set when a commando has raided the building and planted
+ ** plastic explosives. When the CommandoCountDown timer expires, the
+ ** building takes massive damage.
+ */
+ unsigned IsGoingToBlow:1;
+
+ /*
+ ** If this building was destroyed by some method that would prevent
+ ** survivors, then this flag will be true.
+ */
+ unsigned IsSurvivorless:1;
+
+ /*
+ ** These state control variables are used by the obelisk for the charging
+ ** animation.
+ */
+ unsigned IsCharging:1;
+ unsigned IsCharged:1;
+
+ /*
+ ** A building that has been captured will not contain the full compliment
+ ** of crew. This is true even if it subsequently gets captured back.
+ */
+ unsigned IsCaptured:1;
+
+ /*
+ ** Used by the gap generator to decide if it should jam or unjam
+ */
+ unsigned IsJamming:1;
+
+ /*
+ ** Used by radar facilities to know if they're being jammed by a mobile
+ ** radar jammer
+ */
+ unsigned IsJammed:1;
+
+ /*
+ ** Used only by advanced tech center, this keeps track of whether the
+ ** GPS satellite has been fired or not.
+ */
+ unsigned HasFired:1;
+
+ /*
+ ** If Grand_Opening was already called for this building, then this
+ ** flag will be true. By utilizing this flag, multiple inadvertant
+ ** calls to Grand_Opening won't cause problems.
+ */
+ unsigned HasOpened:1;
+
+ /*
+ ** Special countdown to destruction value. If the building is destroyed,
+ ** it won't actually be removed from the map until this value reaches
+ ** zero. This delay is for cosmetic reasons.
+ */
+ CDTimerClass CountDown;
+
+ /*
+ ** This is the current animation processing state that the building is
+ ** in.
+ */
+ BStateType BState;
+ BStateType QueueBState;
+
+ /*
+ ** For multiplayer games, this keeps track of the last house to damage
+ ** this building, so if it burns to death or otherwise gradually dies,
+ ** proper credit can be given for the kill.
+ */
+ HousesType WhoLastHurtMe;
+
+ /*
+ ** This is the saboteur responsible for this building's destruction.
+ */
+ TARGET WhomToRepay;
+
+ /*
+ ** This is a record of the last strength of the building. Every so often,
+ ** it will compare this strength to the current strength. If there is a
+ ** discrepancy, then the owner power is adjusted accordingly.
+ */
+ int LastStrength;
+
+ /*
+ ** This is a target id of an animation we're keeping track of. Examples
+ ** of this usage are the advanced tech center, which needs to know
+ ** when the sputdoor animation has reached a certain stage.
+ */
+ TARGET AnimToTrack;
+
+ /*
+ ** This is the countdown timer that regulates placement retry logic
+ ** for factory type buildings.
+ */
+ CDTimerClass PlacementDelay;
+
+ /*---------------------------------------------------------------------
+ ** Constructors, Destructors, and overloaded operators.
+ */
+ static void * operator new(size_t size);
+ static void * operator new(size_t , void * ptr) {return(ptr);};
+ static void operator delete(void *ptr);
+ BuildingClass(StructType type, HousesType house);
+#ifdef FIXIT_MULTI_SAVE
+ BuildingClass(NoInitClass const & x) : TechnoClass(x), Class(x), Factory(x), CountDown(x), PlacementDelay(x) {};
+#else
+ BuildingClass(NoInitClass const & x) : TechnoClass(x), Class(x), CountDown(x), PlacementDelay(x) {};
+#endif
+ virtual ~BuildingClass(void);
+ operator StructType(void) const {return Class->Type;};
+
+ /*---------------------------------------------------------------------
+ ** Member function prototypes.
+ */
+ static void Init(void);
+
+ TARGET Target_Scan(void);
+ BuildingTypeClass::AnimControlType const * Fetch_Anim_Control(void) {return (&Class->Anims[BState]);};
+
+ /*
+ ** Query functions.
+ */
+ virtual int Value(void) const;
+ virtual void const * Get_Image_Data(void) const;
+ virtual int How_Many_Survivors(void) const;
+ virtual DirType Turret_Facing(void) const;
+ virtual CELL Find_Exit_Cell(TechnoClass const * techno) const;
+ virtual InfantryType Crew_Type(void) const;
+ virtual int Pip_Count(void) const;
+ virtual bool Can_Player_Move(void) const;
+ virtual ActionType What_Action(ObjectClass const * target) const;
+ virtual ActionType What_Action(CELL cell) const;
+ virtual bool Can_Demolish(void) const;
+ virtual ObjectTypeClass const & Class_Of(void) const {return *Class;};
+ virtual DirType Fire_Direction(void) const;
+ virtual short const * Overlap_List(bool redraw=false) const;
+ int Shape_Number(void) const;
+ int Power_Output(void) const;
+ CELL Check_Point(CheckPointType cp) const;
+
+ /*
+ ** Coordinate inquiry functions. These are used for both display and
+ ** combat purposes.
+ */
+ virtual COORDINATE Target_Coord(void) const;
+ virtual COORDINATE Docking_Coord(void) const;
+ virtual COORDINATE Center_Coord(void) const;
+ virtual COORDINATE Sort_Y(void) const;
+ virtual COORDINATE Exit_Coord(void) const;
+
+ /*
+ ** Object entry and exit from the game system.
+ */
+ virtual void Detach(TARGET target, bool all);
+ virtual void Detach_All(bool all=true);
+ virtual void Grand_Opening(bool captured = false);
+ virtual void Update_Buildables(void);
+ virtual MoveType Can_Enter_Cell(CELL cell, FacingType = FACING_NONE) const;
+ virtual bool Unlimbo(COORDINATE , DirType dir = DIR_N);
+ virtual bool Limbo(void);
+
+ /*
+ ** Display and rendering support functionality. Supports imagery and how
+ ** object interacts with the map and thus indirectly controls rendering.
+ */
+ virtual void const * Remap_Table(void);
+ virtual int Exit_Object(TechnoClass * base);
+ virtual void Draw_It(int x, int y, WindowNumberType window) const;
+ virtual bool Mark(MarkType mark=MARK_CHANGE);
+ virtual void Fire_Out(void);
+ void Begin_Mode(BStateType bstate);
+
+ /*
+ ** User I/O.
+ */
+ virtual void Active_Click_With(ActionType action, ObjectClass * object);
+ virtual void Active_Click_With(ActionType action, CELL cell);
+
+ /*
+ ** Combat related.
+ */
+ virtual void Death_Announcement(TechnoClass const * source=0) const;
+ virtual FireErrorType Can_Fire(TARGET, int which) const;
+ virtual TARGET Greatest_Threat(ThreatType threat) const;
+ virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source=0, bool forced=false);
+ virtual bool Captured(HouseClass * newowner);
+ void Update_Radar_Spied(void);
+
+ /*
+ ** AI.
+ */
+ void Charging_AI(void);
+ void Rotation_AI(void);
+ void Factory_AI(void);
+ void Repair_AI(void);
+ void Animation_AI(void);
+ virtual bool Revealed(HouseClass * house);
+ virtual void Repair(int control);
+ virtual void Sell_Back(int control);
+ virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param);
+ virtual void AI(void);
+ virtual void Assign_Target(TARGET target);
+ virtual bool Toggle_Primary(void);
+ bool Flush_For_Placement(TechnoClass * techno, CELL cell);
+
+ virtual int Mission_Unload(void);
+ virtual int Mission_Repair(void);
+ virtual int Mission_Attack(void);
+ virtual int Mission_Harvest(void);
+ virtual int Mission_Guard(void);
+ virtual int Mission_Construction(void);
+ virtual int Mission_Deconstruction(void);
+ virtual int Mission_Missile(void);
+ virtual void Enter_Idle_Mode(bool initial=false);
+ void Remove_Gap_Effect(void);
+
+ /*
+ ** Scenario and debug support.
+ */
+ #ifdef CHEAT_KEYS
+ virtual void Debug_Dump(MonoClass *mono) const;
+ #endif
+
+ /*
+ ** File I/O.
+ */
+ static void Read_INI(CCINIClass & ini);
+ static void Write_INI(CCINIClass & ini);
+ static char *INI_Name(void) {return "STRUCTURES";};
+ bool Load(Straw & file);
+ bool Save(Pipe & file) const;
+
+ private:
+ void Drop_Debris(TARGET source = TARGET_NONE);
+
+ static COORDINATE const CenterOffset[BSIZE_COUNT];
+};
+
+#endif
diff --git a/CODE/BULLET.CPP b/CODE/BULLET.CPP
new file mode 100644
index 0000000..778d780
--- /dev/null
+++ b/CODE/BULLET.CPP
@@ -0,0 +1,1070 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/BULLET.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : BULLET.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : April 23, 1994 *
+ * *
+ * Last Update : October 10, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * BulletClass::AI -- Logic processing for bullet. *
+ * BulletClass::BulletClass -- Bullet constructor. *
+ * BulletClass::Bullet_Explodes -- Performs bullet explosion logic. *
+ * BulletClass::Detach -- Removes specified target from this bullet's targeting system. *
+ * BulletClass::Draw_It -- Displays the bullet at location specified. *
+ * BulletClass::In_Which_Layer -- Fetches the layer that the bullet resides in. *
+ * BulletClass::Init -- Clears the bullets array for scenario preparation. *
+ * BulletClass::Is_Forced_To_Explode -- Checks if bullet should explode NOW. *
+ * BulletClass::Mark -- Performs related map refreshing under bullet. *
+ * BulletClass::Occupy_List -- Determines the bullet occupation list. *
+ * BulletClass::Shape_Number -- Fetches the shape number for the bullet object. *
+ * BulletClass::Sort_Y -- Sort coordinate for bullet rendering. *
+ * BulletClass::Target_Coord -- Fetches coordinate to use when firing on this object. *
+ * BulletClass::Unlimbo -- Transitions a bullet object into the game render/logic system. *
+ * BulletClass::delete -- Bullet memory delete. *
+ * BulletClass::new -- Allocates memory for bullet object. *
+ * BulletClass::~BulletClass -- Destructor for bullet objects. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+
+
+/***********************************************************************************************
+ * BulletClass::BulletClass -- Bullet constructor. *
+ * *
+ * This is the constructor for the bullet class. It handles all *
+ * initialization of the bullet and starting it in motion toward its *
+ * target. *
+ * *
+ * INPUT: id -- The type of bullet this is (could be missile). *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/02/1994 JLB : Created. *
+ * 06/20/1994 JLB : Firer is a base class pointer. *
+ * 12/10/1994 JLB : Auto calculate range optional. *
+ * 12/12/1994 JLB : Handles small arms as an instantaneous effect. *
+ * 12/23/1994 JLB : Fixed scatter algorithm for non-homing projectiles. *
+ * 12/31/1994 JLB : Removed range parameter (not needed). *
+ *=============================================================================================*/
+BulletClass::BulletClass(BulletType id, TARGET target, TechnoClass * payback, int strength, WarheadType warhead, int speed) :
+ ObjectClass(RTTI_BULLET, Bullets.ID(this)),
+ Class(BulletTypes.Ptr((int)id)),
+ Payback(payback),
+ PrimaryFacing(DIR_N),
+ IsInaccurate(false),
+ IsToAnimate(false),
+ IsLocked(true),
+ TarCom(target),
+ MaxSpeed(speed),
+ Warhead(warhead)
+{
+ Strength = strength;
+ Height = FLIGHT_LEVEL;
+}
+
+
+/***********************************************************************************************
+ * BulletClass::~BulletClass -- Destructor for bullet objects. *
+ * *
+ * The bullet destructor must detect if a dog has been attached to this bullet. If so, *
+ * then the attached dog must be unlimboed back onto the map. This operation is necessary *
+ * because, unlike other objects, the dog flies with the bullet it fires. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/06/1996 JLB : Created. *
+ *=============================================================================================*/
+BulletClass::~BulletClass(void)
+{
+ if (GameActive) {
+
+ /*
+ ** SPECIAL CASE:
+ ** The dog is attached to the dog bullet in a limbo state. When the bullet is
+ ** destroyed, the dog must come back out of limbo at the closest location possible to
+ ** the bullet.
+ */
+ if (Payback != NULL && Payback->What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)Payback)->Class->IsDog) {
+
+ InfantryClass * dog = (InfantryClass *)Payback;
+ if (dog) {
+ bool unlimbo = false;
+ DirType dogface = dog->PrimaryFacing;
+ COORDINATE newcoord = Coord;
+
+ /*
+ ** Ensure that the coordinate, that the dog is to appear at, is legal. If not,
+ ** then find a nearby legal location.
+ */
+ if (Can_Enter_Cell(newcoord) != MOVE_OK) {
+ newcoord = Map.Nearby_Location(Coord_Cell(newcoord), dog->Class->Speed);
+ }
+
+ /*
+ ** Try to put the dog down where the target impacted. If we can't
+ ** put it in that cell, then scan through the adjacent cells,
+ ** starting with our current heading, until we find a place where
+ ** we can put him down. If all 8 adjacent cell checks fail, then
+ ** just delete the dog.
+ */
+ for (int i = -1; i < 8; i++) {
+ if (i != -1) {
+ newcoord = Adjacent_Cell(Coord, FacingType(i));
+ }
+ ScenarioInit++;
+ if (dog->Unlimbo(newcoord, dog->PrimaryFacing)) {
+ dog->Mark(MARK_DOWN);
+ dog->Do_Action(DO_DOG_MAUL, true);
+ if (dog->WasSelected) {
+ dog->Select();
+ }
+ ScenarioInit--;
+ unlimbo = true;
+ break;
+ }
+ ScenarioInit--;
+ }
+
+ Payback = 0;
+
+ if (!unlimbo) {
+ delete dog;
+ }
+ }
+ }
+ BulletClass::Limbo();
+ }
+
+ Class=0;
+ Payback=0;
+}
+
+
+/***********************************************************************************************
+ * BulletClass::new -- Allocates memory for bullet object. *
+ * *
+ * This function will "allocate" a block of memory for a bullet object. *
+ * This memory block is merely lifted from a fixed pool of blocks. *
+ * *
+ * INPUT: size -- The size of the memory block needed. *
+ * *
+ * OUTPUT: Returns with a pointer to an available bullet object block. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/02/1994 JLB : Created. *
+ *=============================================================================================*/
+void * BulletClass::operator new(size_t )
+{
+ void * ptr = Bullets.Allocate();
+ if (ptr) {
+ ((BulletClass *)ptr)->IsActive = true;
+ }
+ return(ptr);
+}
+
+
+/***********************************************************************************************
+ * BulletClass::delete -- Bullet memory delete. *
+ * *
+ * Since bullets memory is merely "allocated" out of a pool, it never *
+ * actually gets deleted. *
+ * *
+ * INPUT: ptr -- Generic pointer to bullet object. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/02/1994 JLB : Created. *
+ *=============================================================================================*/
+void BulletClass::operator delete(void * ptr)
+{
+ if (ptr) {
+ ((BulletClass *)ptr)->IsActive = false;
+ }
+ Bullets.Free((BulletClass *)ptr);
+}
+
+
+/***********************************************************************************************
+ * BulletClass::Occupy_List -- Determines the bullet occupation list. *
+ * *
+ * This function will determine the cell occupation list and return a pointer to it. Most *
+ * bullets are small and the list is usually short, but on occasion, it can be a list that *
+ * rivals the size of regular vehicles. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with a pointer to the cell offset list that covers all the cells a bullet *
+ * is over. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/20/1994 JLB : Created. *
+ * 01/05/1995 JLB : Handles projectiles with altitude. *
+ *=============================================================================================*/
+short const * BulletClass::Occupy_List(bool) const
+{
+ assert(Bullets.ID(this) == ID);
+ assert(IsActive);
+
+ /*
+ ** Super-gigundo units use the >= 64 coord spillage list logic.
+ */
+ if (Class->IsGigundo) {
+ static short _list[] = {
+ -1, 0, 1,
+ MAP_CELL_W*1-1, MAP_CELL_W*1, MAP_CELL_W*1+1,
+ -MAP_CELL_W*1-1, -MAP_CELL_W*1, -MAP_CELL_W*1+1,
+ MAP_CELL_W*2-1, MAP_CELL_W*2, MAP_CELL_W*2+1,
+ -MAP_CELL_W*2-1, -MAP_CELL_W*2, -MAP_CELL_W*2+1,
+ -MAP_CELL_W*3-1, -MAP_CELL_W*3, -MAP_CELL_W*3+1,
+ REFRESH_EOL
+ };
+ return(_list);
+// return(Coord_Spillage_List(Coord, 64));
+ }
+
+ /*
+ ** Flying units need a special adjustment to the spillage list to take into account
+ ** that the bullet imagery and the shadow are widely separated.
+ */
+ if (Height > 0) {
+ static short _list[25];
+ const short * ptr = Coord_Spillage_List(Coord, 5);
+ int index = 0;
+ CELL cell1 = Coord_Cell(Coord);
+
+ while (ptr[index] != REFRESH_EOL) {
+ _list[index] = ptr[index];
+ index++;
+ }
+
+ COORDINATE coord = Coord_Move(Coord, DIR_N, Height);
+ CELL cell2 = Coord_Cell(coord);
+ ptr = Coord_Spillage_List(coord, 5);
+ while (*ptr != REFRESH_EOL) {
+ _list[index++] = *ptr + (cell2 - cell1);
+ ptr++;
+ }
+ _list[index] = REFRESH_EOL;
+ return(_list);
+ }
+
+ return(Coord_Spillage_List(Coord, 10));
+}
+
+
+/***********************************************************************************************
+ * BulletClass::Mark -- Performs related map refreshing under bullet. *
+ * *
+ * This routine marks the objects under the bullet so that they will *
+ * be redrawn. This is necessary as the bullet moves -- objects under *
+ * its path need to be restored. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/02/1994 JLB : Created. *
+ *=============================================================================================*/
+bool BulletClass::Mark(MarkType mark)
+{
+ assert(Bullets.ID(this) == ID);
+ assert(IsActive);
+
+ if (ObjectClass::Mark(mark)) {
+ if (!Class->IsInvisible) {
+ Map.Refresh_Cells(Coord_Cell(Coord), Occupy_List());
+ }
+ return(true);
+ }
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * BulletClass::AI -- Logic processing for bullet. *
+ * *
+ * This routine will perform all logic (flight) logic on the bullet. *
+ * Primarily this is motion, fuse tracking, and detonation logic. Call *
+ * this routine no more than once per bullet per game tick. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/02/1994 JLB : Created. *
+ *=============================================================================================*/
+void BulletClass::AI(void)
+{
+ assert(Bullets.ID(this) == ID);
+ assert(IsActive);
+
+ COORDINATE coord;
+
+ ObjectClass::AI();
+
+ if (!IsActive) return;
+
+ /*
+ ** Ballistic objects are handled here.
+ */
+ bool forced = false; // Forced explosion.
+ if ((Class->IsArcing || Class->IsDropping) && !IsFalling) {
+ forced = true;
+ }
+
+ /*
+ ** Homing projectiles constantly change facing to face toward the target but
+ ** they only do so every other game frame (improves game speed and makes
+ ** missiles not so deadly).
+ */
+ if ((Frame & 0x01) && Class->ROT != 0 && Target_Legal(TarCom)) {
+ PrimaryFacing.Set_Desired(Direction256(Coord, ::As_Coord(TarCom)));
+ }
+
+ /*
+ ** Move the projectile forward according to its speed
+ ** and direction.
+ */
+ coord = Coord;
+ if (Class->IsFlameEquipped) {
+ if (IsToAnimate) {
+ if (stricmp(Class->GraphicName, "FB1") == 0) {
+ new AnimClass(ANIM_FBALL_FADE, coord, 1);
+ } else {
+ new AnimClass(ANIM_SMOKE_PUFF, coord, 1);
+ }
+ }
+ IsToAnimate = !IsToAnimate;
+ }
+
+ /*
+ ** Handle any body rotation at this time. This process must
+ ** occur every game fame in order to achieve smooth rotation.
+ */
+ if (PrimaryFacing.Is_Rotating()) {
+ PrimaryFacing.Rotation_Adjust(Class->ROT);
+ }
+ switch (Physics(coord, PrimaryFacing)) {
+ /*
+ ** When a projectile reaches the edge of the world, it
+ ** vanishes from existence -- presumed to explode off
+ ** map.
+ */
+ case IMPACT_EDGE:
+ Mark();
+ if (Payback != NULL && Class->Type == BULLET_GPS_SATELLITE) {
+ if (Payback->House == PlayerPtr) {
+ if (!Map.Is_Radar_Active()) {
+ Map.Radar_Activate(1);
+ }
+ for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) {
+ Map.Map_Cell(cell, PlayerPtr);
+ }
+ Map.RadarClass::Flag_To_Redraw(true);
+ }
+ Payback->House->IsGPSActive = true;
+ Payback->House->IsVisionary = true;
+ }
+#ifdef OBSOLETE
+ /*
+ ** Hack: If it's the artificial nukes, don't let the bullets come down (as
+ ** they're the only ones that blow up). We know it's artificial if you're
+ ** at tech level 10 or below, because you can't build the nuclear silo until
+ ** tech level 15 or so.
+ */
+ if (Payback != NULL && Class->Type == BULLET_NUKE_UP && Payback->House->Control.TechLevel <= 10) {
+ BulletClass * bullet = new BulletClass(BULLET_NUKE_DOWN, ::As_Target(Payback->House->NukeDest), Payback, 200, WARHEAD_NUKE, MPH_VERY_FAST);
+ if (bullet) {
+ int celly = Cell_Y(Payback->House->NukeDest);
+ celly -= 15;
+ if (celly < 1) celly = 1;
+ COORDINATE start = Cell_Coord(XY_Cell(Cell_X(Payback->House->NukeDest), celly));
+ if (!bullet->Unlimbo(start, DIR_S)) {
+ delete bullet;
+ }
+ }
+ }
+#endif
+ delete this;
+ break;
+
+ default:
+ case IMPACT_NONE:
+
+ /*
+ ** The projectile has moved. Check its fuse. If detonation
+ ** is signaled, then do so. Otherwise, just move.
+ */
+ case IMPACT_NORMAL:
+ Mark();
+// if(Class->Type == BULLET_NUKE_DOWN) {
+// Render(true);
+// }
+ if (Class->Type == BULLET_NUKE_UP) {
+ if (Payback != NULL) {
+ if (Distance(Payback->As_Target()) > 0x0C00) {
+ delete this;
+ return;
+ }
+ }
+ }
+ Coord = coord;
+
+ /*
+ ** See if the bullet should be forced to explode now in spite of what
+ ** the fuse would otherwise indicate. Maybe the bullet hit a wall?
+ */
+ if (!forced) {
+ forced = Is_Forced_To_Explode(Coord);
+ }
+
+ /*
+ ** If the bullet is not to explode, then perform normal flight
+ ** maintenance (usually nothing). Otherwise, explode and then
+ ** delete the bullet.
+ */
+ if (!forced && (Class->IsDropping || !Fuse_Checkup(Coord))) {
+ /*
+ ** Certain projectiles lose strength when they travel.
+ */
+ if (Class->IsDegenerate && Strength > 5) {
+ Strength--;
+ }
+
+ } else {
+ Bullet_Explodes(forced);
+ delete this;
+ }
+ break;
+ }
+
+}
+
+
+/***********************************************************************************************
+ * BulletClass::Shape_Number -- Fetches the shape number for the bullet object. *
+ * *
+ * Use this routine to fetch a shape number to use for this bullet object. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the shape number to use when drawing this bullet. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/06/1996 JLB : Created. *
+ *=============================================================================================*/
+int BulletClass::Shape_Number(void) const
+{
+ int shapenum = 0;
+
+ if (!Class->IsFaceless) {
+ shapenum = UnitClass::BodyShape[Dir_To_32(PrimaryFacing)];
+ }
+
+ /*
+ ** For tumbling projectiles, fetch offset stage.
+ */
+ if (Class->Tumble > 0) {
+ shapenum += (long)Frame % Class->Tumble;
+ }
+
+ return(shapenum);
+}
+
+
+/***********************************************************************************************
+ * BulletClass::Draw_It -- Displays the bullet at location specified. *
+ * *
+ * This routine displays the bullet visual at the location specified. *
+ * *
+ * INPUT: x,y -- The center coordinate to render the bullet at. *
+ * *
+ * window -- The window to clip to. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/20/1994 JLB : Created. *
+ * 06/27/1994 JLB : Takes a window clipping parameter. *
+ * 01/08/1995 JLB : Handles translucent colors if necessary. *
+ *=============================================================================================*/
+void BulletClass::Draw_It(int x, int y, WindowNumberType window) const
+{
+ assert(Bullets.ID(this) == ID);
+ assert(IsActive);
+
+ /*
+ ** Certain projectiles aren't visible. This includes small bullets (which are actually
+ ** invisible) and flame thrower flames (which are rendered as an animation instead of a projectile).
+ */
+ if (Class->IsInvisible) return;
+
+ /*
+ ** If there is no shape loaded for this object, then
+ ** it obviously can't be rendered -- just bail.
+ */
+ void const * shapeptr = Get_Image_Data();
+ if (shapeptr == NULL) return;
+
+ /*
+ ** Get the basic shape number for this projectile.
+ */
+ int shapenum = Shape_Number();
+
+ /*
+ ** For flying projectiles, draw the shadow and adjust the actual projectile body
+ ** render position.
+ */
+ if (Height > 0 && Class->IsShadow) {
+
+ if (Class->IsParachuted) {
+ CC_Draw_Shape(AnimTypeClass::As_Reference(ANIM_PARA_BOMB).Get_Image_Data(), 1, x+Lepton_To_Pixel(Height/2), y+10, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, NULL, DisplayClass::UnitShadow);
+ } else {
+ CC_Draw_Shape(shapeptr, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, NULL, DisplayClass::UnitShadow);
+ }
+ y -= Lepton_To_Pixel(Height);
+ }
+
+ /*
+ ** Draw the main body of the projectile.
+ */
+ ShapeFlags_Type flags = SHAPE_NORMAL;
+ if (Class->IsTranslucent) {
+ flags = SHAPE_GHOST;
+ }
+ if (Class->IsSubSurface) {
+ CC_Draw_Shape(shapeptr, shapenum, x, y, window, flags|SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, NULL, DisplayClass::FadingShade);
+ } else {
+ CC_Draw_Shape(shapeptr, shapenum, x, y, window, flags|SHAPE_CENTER|SHAPE_WIN_REL, NULL, DisplayClass::UnitShadow);
+ }
+}
+
+
+/***********************************************************************************************
+ * BulletClass::Init -- Clears the bullets array for scenario preparation. *
+ * *
+ * This routine will zero out the bullet tracking list and object array in preparation for *
+ * the start of a new scenario. All bullets cease to exists after this function is *
+ * called. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/15/1994 JLB : Created. *
+ *=============================================================================================*/
+void BulletClass::Init(void)
+{
+ Bullets.Free_All();
+}
+
+
+/***********************************************************************************************
+ * BulletClass::Detach -- Removes specified target from this bullet's targeting system. *
+ * *
+ * When an object is removed from the game system, it must be removed all targeting and *
+ * tracking systems as well. This routine is used to remove the specified object from the *
+ * bullet. If the object isn't part of this bullet's tracking system, then no action is *
+ * performed. *
+ * *
+ * INPUT: target -- The target to remove from this tracking system. *
+ * *
+ * all -- Is the target going away for good as opposed to just cloaking/hiding? *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 09/24/1994 JLB : Created. *
+ *=============================================================================================*/
+void BulletClass::Detach(TARGET target, bool all)
+{
+ assert(Bullets.ID(this) == ID);
+ assert(IsActive);
+
+ ObjectClass * obj = As_Object(target);
+ if (Payback != NULL && obj == Payback) {
+
+ /*
+ ** If we're being called as a result of the dog that fired us being put
+ ** in limbo, then don't detach. If for any other reason, detach.
+ */
+ if (Payback->What_Am_I() != RTTI_INFANTRY || !((InfantryClass *)Payback)->Class->IsDog) {
+ Payback = 0;
+ }
+ }
+
+ if (all && target == TarCom) {
+ TarCom = TARGET_NONE;
+ }
+}
+
+
+/***********************************************************************************************
+ * BulletClass::Unlimbo -- Transitions a bullet object into the game render/logic system. *
+ * *
+ * This routine is used to take a bullet object that is in limbo and transition it to the *
+ * game system. A bullet object so transitioned, will be drawn and logic processing *
+ * performed. In effect, it comes into existence. *
+ * *
+ * INPUT: coord -- The location where the bullet object is to appear. *
+ * *
+ * dir -- The initial facing for the bullet object. *
+ * *
+ * OUTPUT: bool; Was the unlimbo successful? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 01/10/1995 JLB : Created. *
+ *=============================================================================================*/
+bool BulletClass::Unlimbo(COORDINATE coord, DirType dir)
+{
+ assert(Bullets.ID(this) == ID);
+ assert(IsActive);
+
+ /*
+ ** Try to unlimbo the bullet as far as the base class is concerned. Use the already
+ ** set direction and strength if the "punt" values were passed in. This allows a bullet
+ ** to be setup prior to being launched.
+ */
+ if (!Class->IsHigh) {
+ Height = 0;
+ }
+ if (ObjectClass::Unlimbo(coord)) {
+ Map.Remove(this, In_Which_Layer());
+
+ COORDINATE tcoord = As_Coord(TarCom);
+
+ /*
+ ** Homing projectiles (missiles) do NOT override facing. They just fire in the
+ ** direction specified and let the chips fall where they may.
+ */
+ if (Class->ROT == 0 && !Class->IsDropping) {
+ dir = Direction(tcoord);
+ }
+
+ /*
+ ** Possibly adjust the target if this projectile is inaccurate. This occurs whenever
+ ** certain weapons are trained upon targets they were never designed to attack. Example: when
+ ** turrets or anti-tank missiles are fired at infantry. Indirect
+ ** fire is inherently inaccurate.
+ */
+ if (IsInaccurate || Class->IsInaccurate ||
+ ((Is_Target_Cell(TarCom) || Is_Target_Infantry(TarCom)) && (Warhead == WARHEAD_AP || Class->IsFueled))) {
+
+ /*
+ ** Inaccuracy for low velocity or homing projectiles manifests itself as a standard
+ ** Circular Error of Probability (CEP) algorithm. High speed projectiles usually
+ ** just overshoot the target by extending the straight line flight.
+ */
+ if (/*Class->ROT != 0 ||*/ Class->IsArcing) {
+ int scatterdist = (::Distance(coord, tcoord)/16)-0x0040;
+ scatterdist = min(scatterdist, Rule.HomingScatter);
+ scatterdist = max(scatterdist, 0);
+
+ dir = (DirType)((dir + (Random_Pick(0, 10)-5)) & 0x00FF);
+ tcoord = Coord_Scatter(tcoord, Random_Pick(0, scatterdist));
+ } else {
+ int scatterdist = (::Distance(coord, tcoord)/16)-0x0040;
+ scatterdist = min(scatterdist, Rule.BallisticScatter);
+ scatterdist = max(scatterdist, 0);
+ tcoord = Coord_Move(tcoord, dir, Random_Pick(0, scatterdist));
+ }
+ }
+
+ /*
+ ** For very fast and invisible projectiles, just make the projectile exist at the target
+ ** location and dispense with the actual flight.
+ */
+ if (MaxSpeed == MPH_LIGHT_SPEED && Class->IsInvisible) {
+ Coord = tcoord;
+ }
+
+ /*
+ ** Set the range equal to either the class defined range or the calculated
+ ** number of game frames it would take for the projectile to reach the target.
+ */
+ int range = 0xFF;
+ if (!Class->IsDropping) {
+ range = (::Distance(tcoord, Coord) / MaxSpeed) + 4;
+ }
+
+ /*
+ ** Projectile speed is usually the default value for that projectile, but
+ ** certain projectiles alter speed according to the distance to the
+ ** target.
+ */
+ int speed = MaxSpeed;
+ if (speed == MPH_LIGHT_SPEED) speed = MPH_IMMOBILE;
+ if (Class->IsArcing) {
+ speed = MaxSpeed + (Distance(tcoord) / 32);
+
+ /*
+ ** Set minimum speed (i.e., distance) for arcing projectiles.
+ */
+ speed = max(speed, 25);
+ }
+ if (!Class->IsDropping) {
+ Fly_Speed(255, (MPHType)speed);
+ }
+
+ /*
+ ** Arm the fuse.
+ */
+ Arm_Fuse(Coord, tcoord, range, ((As_Aircraft(TarCom)!=0) ? 0 : Class->Arming));
+
+ /*
+ ** Projectiles that make a ballistic flight to impact point must determine a
+ ** vertical component for the projectile launch. This is crudely simulated
+ ** by biasing ground speed according to target distance and then giving
+ ** enough vertical velocity to keep the projectile airborne for the
+ ** desired amount of time. The mathematically correct solution would be to
+ ** calculate launch angle (given fixed projectile velocity) and then derive
+ ** the vertical and horizontal components. That solution would require a
+ ** square root and an arcsine lookup table. OUCH!
+ */
+ Riser = 0;
+ if (Class->IsArcing) {
+ IsFalling = true;
+ Height = 1;
+ Riser = ((Distance(tcoord)/2) / (speed+1)) * Rule.Gravity;
+ Riser = max(Riser, 10);
+ }
+ if (Class->IsDropping) {
+ IsFalling = true;
+ Height = FLIGHT_LEVEL;
+// Height = Pixel_To_Lepton(24);
+ Riser = 0;
+ if (Class->IsParachuted) {
+ AnimClass * anim = new AnimClass(ANIM_PARA_BOMB, Target_Coord());
+// AnimClass * anim = new AnimClass(ANIM_PARACHUTE, Target_Coord());
+ if (anim) {
+ anim->Attach_To(this);
+ }
+ }
+ }
+ Map.Submit(this, In_Which_Layer());
+
+ PrimaryFacing = dir;
+ return(true);
+ }
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * BulletClass::Target_Coord -- Fetches coordinate to use when firing on this object. *
+ * *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the coordinate that should be used when firing at the object. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 09/21/1995 JLB : Created. *
+ *=============================================================================================*/
+COORDINATE BulletClass::Target_Coord(void) const
+{
+ assert(Bullets.ID(this) == ID);
+ assert(IsActive);
+
+ return(Coord_Add(XY_Coord(0, -Height), Coord));
+}
+
+
+/***********************************************************************************************
+ * BulletClass::Sort_Y -- Sort coordinate for bullet rendering. *
+ * *
+ * This will return the coordinate to use when sorting this bullet in the display list. *
+ * Typically, this only occurs for bullets in the ground layer. Since bullets are to be *
+ * seen a bit more than the normal sorting order would otherwise imply, bias the sort *
+ * value such that bullets will tend to be drawn on top of the objects. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the coordinate to use when sorting this bullet in the display list. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 10/02/1996 JLB : Created. *
+ *=============================================================================================*/
+COORDINATE BulletClass::Sort_Y(void) const
+{
+ assert(this != 0);
+ assert(IsActive);
+
+ return(Coord_Move(Coord, DIR_S, CELL_LEPTON_H/2));
+}
+
+
+/***********************************************************************************************
+ * BulletClass::In_Which_Layer -- Fetches the layer that the bullet resides in. *
+ * *
+ * This examines the bullet to determine what rendering layer it should be in. The *
+ * normal logic applies unless this is a torpedo. A torpedo is always in the surface *
+ * layer. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the render layer that this bullet should reside in. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 10/10/1996 JLB : Created. *
+ *=============================================================================================*/
+LayerType BulletClass::In_Which_Layer(void) const
+{
+ if (Class->IsSubSurface) {
+ return(LAYER_SURFACE);
+ }
+ return(ObjectClass::In_Which_Layer());
+}
+
+
+/***********************************************************************************************
+ * BulletClass::Is_Forced_To_Explode -- Checks if bullet should explode NOW. *
+ * *
+ * This routine will examine the bullet and where it is travelling in order to determine *
+ * if it should prematurely explode. Typical of this would be when a bullet hits a wall *
+ * or a torpedo hits a ship -- regardless of where the projectile was originally aimed. *
+ * *
+ * INPUT: coord -- The new coordinate to place the bullet at presuming it is forced to *
+ * explode and a modification of the bullet's coordinate is needed. *
+ * Otherwise, the coordinate is not modified. *
+ * *
+ * OUTPUT: bool; Should the bullet explode now? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 10/10/1996 JLB : Created. *
+ *=============================================================================================*/
+bool BulletClass::Is_Forced_To_Explode(COORDINATE & coord) const
+{
+ coord = Coord;
+ CellClass const * cellptr = &Map[coord];
+
+ /*
+ ** Check for impact on a wall or other high obstacle.
+ */
+ if (!Class->IsHigh && cellptr->Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(cellptr->Overlay).IsHigh) {
+ coord = Cell_Coord(Coord_Cell(coord));
+ return(true);
+ }
+
+ /*
+ ** Check to make sure that underwater projectiles (torpedoes) will not
+ ** travel in anything but water.
+ */
+ if (Class->IsSubSurface) {
+ int d = ::Distance(Coord_Fraction(coord), XY_Coord(CELL_LEPTON_W/2, CELL_LEPTON_W/2));
+ if (cellptr->Land_Type() != LAND_WATER || (d < CELL_LEPTON_W/3 && cellptr->Cell_Techno() != NULL && cellptr->Cell_Techno() != Payback)) {
+
+ /*
+ ** Force explosion to be at center of techno object if one is present.
+ */
+ if (cellptr->Cell_Techno() != NULL) {
+ coord = cellptr->Cell_Techno()->Target_Coord();
+ }
+
+ /*
+ ** However, if the torpedo was blocked by a bridge, then force the
+ ** torpedo to explode on top of that bridge cell.
+ */
+ if (cellptr->Is_Bridge_Here()) {
+ coord = Coord_Snap(coord);
+ }
+
+ return(true);
+ }
+ }
+
+ /*
+ ** Bullets are generally more effective when they are fired at aircraft.
+ */
+ if (Class->IsAntiAircraft && As_Aircraft(TarCom) && Distance(TarCom) < 0x0080) {
+ return(true);
+ }
+
+ /*
+ ** No reason for forced explosion was detected, so return 'false' to
+ ** indicate that no forced explosion is required.
+ */
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * BulletClass::Bullet_Explodes -- Performs bullet explosion logic. *
+ * *
+ * This handles the exploding bullet action. It will generate the animation and the *
+ * damage as necessary. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: The bullet should be deleted after this routine is called. *
+ * *
+ * HISTORY: *
+ * 10/10/1996 JLB : Created. *
+ *=============================================================================================*/
+void BulletClass::Bullet_Explodes(bool forced)
+{
+ /*
+ ** When the target is reached, explode and do the damage
+ ** required of it. For homing objects, don't force the explosion to
+ ** match the target position. Non-homing projectiles adjust position so
+ ** that they hit the target. This compensates for the error in line of
+ ** flight logic.
+ */
+ if ( (Payback != NULL && Payback->What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)Payback)->Class->IsDog) ||
+ (!forced && !Class->IsArcing && Class->ROT == 0 && Fuse_Target())) {
+ Coord = Fuse_Target();
+ }
+
+ /*
+ ** Non-aircraft targets apply damage to the ground.
+ */
+ if (!Is_Target_Aircraft(TarCom) || As_Aircraft(TarCom)->In_Which_Layer() == LAYER_GROUND) {
+ Explosion_Damage(Coord, Strength, Payback, Warhead);
+ if (!IsActive) return;
+
+ } else {
+
+ /*
+ ** Special damage apply for SAM missiles. This is the only way that missile
+ ** damage affects the aircraft target.
+ */
+ if (Distance(TarCom) < 0x0080) {
+ AircraftClass * object = As_Aircraft(TarCom);
+
+ int str = Strength;
+ if (object) object->Take_Damage(str, 0, Warhead, Payback);
+ }
+ }
+
+ /*
+ ** For projectiles that are invisible while travelling toward the target,
+ ** allow scatter effect for the impact animation.
+ */
+ if (Class->IsInvisible) {
+ Coord = Coord_Scatter(Coord, 0x0020);
+ }
+
+ /*
+ ** Fetch the land type that the explosion will be upon. Special case for
+ ** flying aircraft targets, their land type will be LAND_NONE.
+ */
+ CellClass const * cellptr = &Map[Coord];
+ LandType land = cellptr->Land_Type();
+ if (Is_Target_Aircraft(TarCom) && As_Aircraft(TarCom)->In_Which_Layer() == LAYER_TOP) {
+ land = LAND_NONE;
+ }
+
+ AnimType anim = Combat_Anim(Strength, Warhead, land);
+
+ /*
+ ** If it's a water explosion that's going to play, don't play it
+ ** if its cell is the same as the center cell of the target ship.
+ */
+ if (anim >= ANIM_WATER_EXP1 && anim <= ANIM_WATER_EXP3 && Is_Target_Vessel(TarCom)) {
+ if (Coord_Cell(Coord) == Coord_Cell(As_Vessel(TarCom)->Center_Coord())) {
+ anim = (AnimType) (ANIM_VEH_HIT1 + (anim - ANIM_WATER_EXP1));
+ }
+ }
+
+ if (anim != ANIM_NONE) {
+ AnimClass * aptr = new AnimClass(anim, Coord);
+ /*
+ ** Special case trap: if they're making the nuclear explosion,
+ ** and no anim is available, force the nuclear damage anyway
+ ** because nuke damage is done in the middle of the animation
+ ** and if there's no animation, there won't be any damage.
+ */
+ if (!aptr && anim == ANIM_ATOM_BLAST) {
+ HousesType house = HOUSE_NONE;
+ if (Payback) {
+ house = Payback->House->Class->House;
+ }
+ AnimClass::Do_Atom_Damage(house, Coord_Cell(Coord));
+ }
+ }
+
+// if (Payback && Payback->House == PlayerPtr && stricmp(Class->Name(), "GPSSATELLITE") == 0) {
+ if (Payback && Class->Type == BULLET_GPS_SATELLITE) {
+ if (Payback->House == PlayerPtr) {
+ if (!Map.Is_Radar_Active()) {
+ Map.Radar_Activate(1);
+ }
+ for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) {
+ Map.Map_Cell(cell, PlayerPtr);
+ }
+ Map.RadarClass::Flag_To_Redraw(true);
+ }
+// Sound_Effect(VOC_SATTACT2);
+ Payback->House->IsGPSActive = true;
+ Payback->House->IsVisionary = true;
+ }
+}
diff --git a/CODE/BULLET.H b/CODE/BULLET.H
new file mode 100644
index 0000000..7ac09f6
--- /dev/null
+++ b/CODE/BULLET.H
@@ -0,0 +1,149 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/BULLET.H 2 3/06/97 1:46p Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : BULLET.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : April 23, 1994 *
+ * *
+ * Last Update : April 23, 1994 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifndef BULLET_H
+#define BULLET_H
+
+#include "object.h"
+#include "fly.h"
+#include "fuse.h"
+
+
+class BulletClass : public ObjectClass,
+ public FlyClass,
+ public FuseClass
+{
+ public:
+
+ /*
+ ** This specifies exactly what kind of bullet this is. All of the static attributes
+ ** for this bullet is located in the BulletTypeClass pointed to by this variable.
+ */
+ CCPtr Class;
+
+ private:
+ /*
+ ** Records who sent this "present" so that an appropriate "thank you" can
+ ** be returned.
+ */
+ TechnoClass * Payback;
+
+ /*
+ ** This is the facing that the projectile is travelling.
+ */
+ FacingClass PrimaryFacing;
+
+ public:
+
+ /*---------------------------------------------------------------------
+ ** Constructors, Destructors, and overloaded operators.
+ */
+ static void * operator new(size_t size);
+ static void * operator new(size_t , void * ptr) {return(ptr);};
+ static void operator delete(void *ptr);
+ BulletClass(BulletType id, TARGET target, TechnoClass * Payback, int strength, WarheadType warhead, int speed);
+#ifdef FIXIT_MULTI_SAVE
+ BulletClass(NoInitClass const & x) : ObjectClass(x), Class(x), FlyClass(x), FuseClass(x), PrimaryFacing(x) {};
+#else
+ BulletClass(NoInitClass const & x) : ObjectClass(x), Class(x), FlyClass(x), FuseClass(x) {};
+#endif
+ virtual ~BulletClass(void);
+ operator BulletType(void) const {return Class->Type;};
+
+ /*---------------------------------------------------------------------
+ ** Member function prototypes.
+ */
+ static void Init(void);
+
+ bool Is_Forced_To_Explode(COORDINATE & coord) const;
+ void Bullet_Explodes(bool forced);
+ int Shape_Number(void) const;
+ virtual LayerType In_Which_Layer(void) const;
+ virtual COORDINATE Sort_Y(void) const;
+ virtual void Assign_Target(TARGET target) {TarCom = target;};
+ virtual bool Unlimbo(COORDINATE , DirType facing = DIR_N);
+ virtual ObjectTypeClass const & Class_Of(void) const {return *Class;};
+ virtual void Detach(TARGET target, bool all);
+ virtual void Draw_It(int x, int y, WindowNumberType window) const;
+ virtual bool Mark(MarkType mark=MARK_CHANGE);
+ virtual void AI(void);
+ virtual short const * Occupy_List(bool = false) const;
+ virtual short const * Overlap_List(void) const {return Occupy_List(false);};
+ virtual COORDINATE Target_Coord(void) const;
+
+ /*
+ ** File I/O.
+ */
+ bool Load(Straw & file);
+ bool Save(Pipe & file) const;
+ virtual void Code_Pointers(void);
+ virtual void Decode_Pointers(void);
+
+ /*
+ ** If this bullet is forced to be inaccurate because of some outside means. A tank
+ ** firing while moving is a good example.
+ */
+ unsigned IsInaccurate:1;
+
+ private:
+ // Crude animation flag.
+ unsigned IsToAnimate:1;
+
+ /*
+ ** Is this missile allowed to come in from out of bounds?
+ */
+ unsigned IsLocked:1;
+
+ /*
+ ** This is the target of the projectile. It is especially significant for those projectiles
+ ** that home in on a target.
+ */
+ TARGET TarCom;
+
+ /*
+ ** The speed of this projectile.
+ */
+ int MaxSpeed;
+
+ /*
+ ** The warhead of this projectile.
+ */
+ WarheadType Warhead;
+};
+
+#endif
+
diff --git a/CODE/C&CZERO.PJT b/CODE/C&CZERO.PJT
new file mode 100644
index 0000000..5200c7c
--- /dev/null
+++ b/CODE/C&CZERO.PJT
@@ -0,0 +1,410 @@
+;Codewright Project File (do not remove or modify this line)
+[ProjInit]
+ProjSetConfigFlags=0x00010140
+
+[Files]
+c:\projects\c&c\code\aadata.cpp
+c:\projects\c&czero\code\aadata.cpp
+c:\projects\c&czero\code\abstract.cpp
+c:\projects\c&czero\code\abstract.h
+c:\projects\c&czero\code\adata.cpp
+c:\projects\c&czero\code\ADPCM.CPP
+c:\projects\c&czero\code\aircraft.cpp
+c:\projects\c&czero\code\aircraft.h
+c:\projects\c&czero\code\alloc.cpp
+c:\projects\c&czero\code\anim.cpp
+c:\projects\c&czero\code\anim.h
+c:\projects\c&czero\code\audio.cpp
+c:\projects\c&czero\code\audio.h
+c:\projects\c&czero\code\base.cpp
+c:\projects\c&czero\code\base.h
+c:\projects\c&czero\code\bbdata.cpp
+c:\projects\c&czero\code\bdata.cpp
+c:\projects\c&czero\code\BFIOFILE.CPP
+c:\projects\c&czero\code\BFIOFILE.H
+c:\projects\c&czero\code\building.cpp
+c:\projects\c&czero\code\building.h
+c:\projects\c&czero\code\bullet.cpp
+c:\projects\c&czero\code\bullet.h
+c:\projects\c&czero\code\cargo.cpp
+c:\projects\c&czero\code\cargo.h
+c:\projects\c&czero\code\CARRY.CPP
+c:\projects\c&czero\code\CARRY.H
+c:\projects\c&czero\code\ccfile.cpp
+c:\projects\c&czero\code\ccfile.h
+c:\projects\c&czero\code\cdata.cpp
+c:\projects\c&czero\code\cdfile.cpp
+c:\projects\c&czero\code\cdfile.h
+c:\projects\c&czero\code\cell.cpp
+c:\projects\c&czero\code\cell.h
+c:\projects\c&czero\code\checkbox.cpp
+c:\projects\c&czero\code\checkbox.h
+c:\projects\c&czero\code\cheklist.cpp
+c:\projects\c&czero\code\cheklist.h
+c:\projects\c&czero\code\colrlist.cpp
+c:\projects\c&czero\code\colrlist.h
+c:\projects\c&czero\code\combat.cpp
+c:\projects\c&czero\code\combuf.cpp
+c:\projects\c&czero\code\combuf.h
+c:\projects\c&czero\code\compat.h
+c:\projects\c&czero\code\comqueue.cpp
+c:\projects\c&czero\code\comqueue.h
+c:\projects\c&czero\code\confdlg.cpp
+c:\projects\c&czero\code\confdlg.h
+c:\projects\c&czero\code\connect.cpp
+c:\projects\c&czero\code\connect.h
+c:\projects\c&czero\code\connmgr.h
+c:\projects\c&czero\code\conquer.cpp
+c:\projects\c&czero\code\conquer.h
+c:\projects\c&czero\code\const.cpp
+c:\projects\c&czero\code\control.cpp
+c:\projects\c&czero\code\control.h
+c:\projects\c&czero\code\coord.cpp
+c:\projects\c&czero\code\crc.cpp
+c:\projects\c&czero\code\crc.h
+c:\projects\c&czero\code\credits.cpp
+c:\projects\c&czero\code\credits.h
+c:\projects\c&czero\code\crew.cpp
+c:\projects\c&czero\code\crew.h
+c:\projects\c&czero\code\cwstub.c
+c:\projects\c&czero\code\debug.cpp
+c:\projects\c&czero\code\debug.h
+c:\projects\c&czero\code\defines.h
+c:\projects\c&czero\code\descdlg.cpp
+c:\projects\c&czero\code\descdlg.h
+c:\projects\c&czero\code\dial8.cpp
+c:\projects\c&czero\code\dial8.h
+c:\projects\c&czero\code\dialog.cpp
+c:\projects\c&czero\code\display.cpp
+c:\projects\c&czero\code\display.h
+c:\projects\c&czero\code\door.cpp
+c:\projects\c&czero\code\door.h
+c:\projects\c&czero\code\dpmi.cpp
+c:\projects\c&czero\code\dpmi.h
+c:\projects\c&czero\code\drive.cpp
+c:\projects\c&czero\code\drive.h
+c:\projects\c&czero\code\drop.cpp
+c:\projects\c&czero\code\drop.h
+c:\projects\c&czero\code\DTABLE.CPP
+c:\projects\c&czero\code\edit.cpp
+c:\projects\c&czero\code\edit.h
+c:\projects\c&czero\code\ending.cpp
+c:\projects\c&czero\code\ending.h
+c:\projects\c&czero\code\event.cpp
+c:\projects\c&czero\code\event.h
+c:\projects\c&czero\code\expand.cpp
+c:\projects\c&czero\code\externs.h
+c:\projects\c&czero\code\FACE.CPP
+c:\projects\c&czero\code\FACE.H
+c:\projects\c&czero\code\facing.cpp
+c:\projects\c&czero\code\facing.h
+c:\projects\c&czero\code\factory.cpp
+c:\projects\c&czero\code\factory.h
+c:\projects\c&czero\code\findpath.cpp
+c:\projects\c&czero\code\flasher.cpp
+c:\projects\c&czero\code\flasher.h
+c:\projects\c&czero\code\fly.cpp
+c:\projects\c&czero\code\fly.h
+c:\projects\c&czero\code\foot.cpp
+c:\projects\c&czero\code\foot.h
+c:\projects\c&czero\code\ftimer.h
+c:\projects\c&czero\code\function.h
+c:\projects\c&czero\code\fuse.cpp
+c:\projects\c&czero\code\fuse.h
+c:\projects\c&czero\code\gadget.cpp
+c:\projects\c&czero\code\gadget.h
+c:\projects\c&czero\code\gamedlg.cpp
+c:\projects\c&czero\code\gamedlg.h
+c:\projects\c&czero\code\gauge.cpp
+c:\projects\c&czero\code\gauge.h
+c:\projects\c&czero\code\globals.cpp
+c:\projects\c&czero\code\goptions.cpp
+c:\projects\c&czero\code\goptions.h
+c:\projects\c&czero\code\gscreen.cpp
+c:\projects\c&czero\code\gscreen.h
+c:\projects\c&czero\code\hdata.cpp
+c:\projects\c&czero\code\heap.cpp
+c:\projects\c&czero\code\heap.h
+c:\projects\c&czero\code\help.cpp
+c:\projects\c&czero\code\help.h
+c:\projects\c&czero\code\house.cpp
+c:\projects\c&czero\code\house.h
+c:\projects\c&czero\code\HSV.CPP
+c:\projects\c&czero\code\HSV.H
+c:\projects\c&czero\code\idata.cpp
+c:\projects\c&czero\code\infantry.cpp
+c:\projects\c&czero\code\infantry.h
+c:\projects\c&czero\code\ini.cpp
+c:\projects\c&czero\code\inibin.cpp
+c:\projects\c&czero\code\inicode.cpp
+c:\projects\c&czero\code\init.cpp
+c:\projects\c&czero\code\intro.cpp
+c:\projects\c&czero\code\intro.h
+c:\projects\c&czero\code\iomap.cpp
+c:\projects\c&czero\code\ioobj.cpp
+c:\projects\c&czero\code\ipx.cpp
+c:\projects\c&czero\code\ipx.h
+c:\projects\c&czero\code\ipxaddr.cpp
+c:\projects\c&czero\code\ipxaddr.h
+c:\projects\c&czero\code\ipxconn.cpp
+c:\projects\c&czero\code\ipxconn.h
+c:\projects\c&czero\code\ipxgconn.cpp
+c:\projects\c&czero\code\ipxgconn.h
+c:\projects\c&czero\code\ipxmgr.cpp
+c:\projects\c&czero\code\ipxmgr.h
+c:\projects\c&czero\code\itable.cpp
+c:\projects\c&czero\code\jshell.cpp
+c:\projects\c&czero\code\jshell.h
+c:\projects\c&czero\code\keyframe.cpp
+c:\projects\c&czero\code\layer.cpp
+c:\projects\c&czero\code\layer.h
+c:\projects\c&czero\code\lcwuncmp.cpp
+c:\projects\c&czero\code\led.h
+c:\projects\c&czero\code\link.cpp
+c:\projects\c&czero\code\link.h
+c:\projects\c&czero\code\lint.h
+c:\projects\c&czero\code\list.cpp
+c:\projects\c&czero\code\list.h
+c:\projects\c&czero\code\loaddlg.cpp
+c:\projects\c&czero\code\loaddlg.h
+c:\projects\c&czero\code\logic.cpp
+c:\projects\c&czero\code\logic.h
+c:\projects\c&czero\code\map.cpp
+c:\projects\c&czero\code\map.h
+c:\projects\c&czero\code\mapeddlg.cpp
+c:\projects\c&czero\code\mapedit.cpp
+c:\projects\c&czero\code\mapedit.h
+c:\projects\c&czero\code\mapedplc.cpp
+c:\projects\c&czero\code\mapedsel.cpp
+c:\projects\c&czero\code\mapedtm.cpp
+c:\projects\c&czero\code\mapsel.cpp
+c:\projects\c&czero\code\menus.cpp
+c:\projects\c&czero\code\message.h
+c:\projects\c&czero\code\mission.cpp
+c:\projects\c&czero\code\mission.h
+c:\projects\c&czero\code\mixfile.cpp
+c:\projects\c&czero\code\mixfile.h
+c:\projects\c&czero\code\monoc.cpp
+c:\projects\c&czero\code\monoc.h
+c:\projects\c&czero\code\mouse.cpp
+c:\projects\c&czero\code\mouse.h
+c:\projects\c&czero\code\mplayer.cpp
+c:\projects\c&czero\code\msgbox.cpp
+c:\projects\c&czero\code\msgbox.h
+c:\projects\c&czero\code\msglist.cpp
+c:\projects\c&czero\code\msglist.h
+c:\projects\c&czero\code\netdlg.cpp
+c:\projects\c&czero\code\nullconn.cpp
+c:\projects\c&czero\code\nullconn.h
+c:\projects\c&czero\code\nulldlg.cpp
+c:\projects\c&czero\code\nullmgr.cpp
+c:\projects\c&czero\code\nullmgr.h
+c:\projects\c&czero\code\object.cpp
+c:\projects\c&czero\code\object.h
+c:\projects\c&czero\code\odata.cpp
+c:\projects\c&czero\code\options.cpp
+c:\projects\c&czero\code\options.h
+c:\projects\c&czero\code\overlay.cpp
+c:\projects\c&czero\code\overlay.h
+c:\projects\c&czero\code\PALETTE.CPP
+c:\projects\c&czero\code\PALETTE.H
+c:\projects\c&czero\code\phone.h
+c:\projects\c&czero\code\power.cpp
+c:\projects\c&czero\code\power.h
+c:\projects\c&czero\code\profile.cpp
+c:\projects\c&czero\code\queue.cpp
+c:\projects\c&czero\code\queue.h
+c:\projects\c&czero\code\radar.cpp
+c:\projects\c&czero\code\radar.h
+c:\projects\c&czero\code\radio.cpp
+c:\projects\c&czero\code\radio.h
+c:\projects\c&czero\code\rand.cpp
+c:\projects\c&czero\code\RANDOM.CPP
+c:\projects\c&czero\code\RANDOM.H
+c:\projects\c&czero\code\rawfile.cpp
+c:\projects\c&czero\code\rawfile.h
+c:\projects\c&czero\code\region.h
+c:\projects\c&czero\code\reinf.cpp
+c:\projects\c&czero\code\RGB.CPP
+c:\projects\c&czero\code\RGB.H
+c:\projects\c&czero\code\ROTBMP.CPP
+c:\projects\c&czero\code\ROTBMP.H
+c:\projects\c&czero\code\savedlg.h
+c:\projects\c&czero\code\saveload.cpp
+c:\projects\c&czero\code\scenario.cpp
+c:\projects\c&czero\code\SCENARIO.H
+c:\projects\c&czero\code\score.cpp
+c:\projects\c&czero\code\score.h
+c:\projects\c&czero\code\screen.h
+c:\projects\c&czero\code\scroll.cpp
+c:\projects\c&czero\code\scroll.h
+c:\projects\c&czero\code\sdata.cpp
+c:\projects\c&czero\code\SESSION.CPP
+c:\projects\c&czero\code\SESSION.H
+c:\projects\c&czero\code\shapebtn.cpp
+c:\projects\c&czero\code\shapebtn.h
+c:\projects\c&czero\code\sidebar.cpp
+c:\projects\c&czero\code\sidebar.h
+c:\projects\c&czero\code\slider.cpp
+c:\projects\c&czero\code\slider.h
+c:\projects\c&czero\code\smudge.cpp
+c:\projects\c&czero\code\smudge.h
+c:\projects\c&czero\code\sounddlg.cpp
+c:\projects\c&czero\code\sounddlg.h
+c:\projects\c&czero\code\special.cpp
+c:\projects\c&czero\code\special.h
+c:\projects\c&czero\code\SPRITE.CPP
+c:\projects\c&czero\code\stage.h
+c:\projects\c&czero\code\startup.cpp
+c:\projects\c&czero\code\super.cpp
+c:\projects\c&czero\code\super.h
+c:\projects\c&czero\code\tab.cpp
+c:\projects\c&czero\code\tab.h
+c:\projects\c&czero\code\TACTION.CPP
+c:\projects\c&czero\code\TACTION.H
+c:\projects\c&czero\code\target.cpp
+c:\projects\c&czero\code\target.h
+c:\projects\c&czero\code\tdata.cpp
+c:\projects\c&czero\code\team.cpp
+c:\projects\c&czero\code\team.h
+c:\projects\c&czero\code\teamtype.cpp
+c:\projects\c&czero\code\teamtype.h
+c:\projects\c&czero\code\techno.cpp
+c:\projects\c&czero\code\techno.h
+c:\projects\c&czero\code\template.cpp
+c:\projects\c&czero\code\template.h
+c:\projects\c&czero\code\terrain.cpp
+c:\projects\c&czero\code\terrain.h
+c:\projects\c&czero\code\TEVENT.CPP
+c:\projects\c&czero\code\TEVENT.H
+c:\projects\c&czero\code\textbtn.cpp
+c:\projects\c&czero\code\textbtn.h
+c:\projects\c&czero\code\theme.cpp
+c:\projects\c&czero\code\theme.h
+c:\projects\c&czero\code\toggle.cpp
+c:\projects\c&czero\code\toggle.h
+c:\projects\c&czero\code\trigger.cpp
+c:\projects\c&czero\code\trigger.h
+c:\projects\c&czero\code\txtlabel.cpp
+c:\projects\c&czero\code\txtlabel.h
+c:\projects\c&czero\code\type.h
+c:\projects\c&czero\code\udata.cpp
+c:\projects\c&czero\code\unit.cpp
+c:\projects\c&czero\code\unit.h
+c:\projects\c&czero\code\vdata.cpp
+c:\projects\c&czero\code\vector.cpp
+c:\projects\c&czero\code\vector.h
+c:\projects\c&czero\code\VERSION.CPP
+c:\projects\c&czero\code\VERSION.H
+c:\projects\c&czero\code\vessel.cpp
+c:\projects\c&czero\code\vessel.h
+c:\projects\c&czero\code\visudlg.cpp
+c:\projects\c&czero\code\visudlg.h
+c:\projects\c&czero\code\watcom.h
+c:\projects\c&czero\code\wwalloc.h
+c:\projects\c&czero\code\wwfile.h
+C:\projects\c&czero\code\2KEYFRAM.CPP
+C:\projects\c&czero\code\BLOWFISH.CPP
+C:\projects\c&czero\code\BLOWFISH.H
+C:\projects\c&czero\code\FILEPCX.H
+C:\projects\c&czero\code\INT.CPP
+C:\projects\c&czero\code\INT.H
+C:\projects\c&czero\code\interpal.cpp
+C:\projects\c&czero\code\language.h
+C:\projects\c&czero\code\MP.CPP
+C:\projects\c&czero\code\MP.H
+C:\projects\c&czero\code\RNG.H
+C:\projects\c&czero\code\SHA.CPP
+C:\projects\c&czero\code\SHA.H
+C:\projects\c&czero\code\tarcom.cpp
+C:\projects\c&czero\code\tcpip.h
+C:\projects\c&czero\code\winstub.cpp
+c:\projects\c&czero\code\BUFFERX.H
+c:\projects\c&czero\code\ini.h
+c:\projects\c&czero\code\listnode.h
+c:\projects\c&czero\code\rules.cpp
+c:\projects\c&czero\code\rules.h
+c:\projects\c&czero\code\tarcom.h
+c:\projects\c&czero\code\turret.cpp
+c:\projects\c&czero\code\turret.h
+c:\projects\c&czero\code\warhead.h
+c:\projects\c&czero\code\weapon.h
+
+[State]
+SysSetCwd='C:\projects\c&czero\code'
+SrchSetFlags=0x000320aa
+FileSortMode=0x0
+StateWindowFrame=37,16,923,511,0x62c9f5fa
+_StateWindow=0,0,990,647,0x00100018,'C:\projects\c&czero\code\palette.cpp',240,15,244,32,32,0,32,32,32,32,8,4294967295,4294967295,1,10,'',12,255,48,0,7,243,252,253,247,249,247,93,1,400,0,246,252,248,244,247,15,15,15,15,0,0
+_StateBuffer='C:\projects\c&czero\code\palette.cpp',0x0400048e,93,1,25,'4 7','',0x0,''
+_StateBuffer='C:\projects\c&czero\code\jshell.h',0x0400048e,1,2,25,'4 7','',0x0,''
+_StateBuffer='C:\projects\c&czero\code\sprite.cpp',0x0400048e,35,1,25,'4 7','',0x0,''
+_StateBuffer='C:\projects\c&czero\code\wwfile.h',0x0400048e,21,1,25,'4 7','',0x0,''
+_StateBuffer='C:\projects\c&czero\win32lib\INCLUDE\RAWFILE.H',0x0400048e,1,1,25,'4 7','',0x0,''
+_StateBuffer='C:\projects\c&czero\code\ROTBMP.H',0x0400048e,9,1,25,'4 7','',0x0,''
+_StateBuffer='C:\projects\c&czero\code\rotbmp.cpp',0x0400048e,6,1,25,'4 7','',0x0,''
+_StateBuffer='C:\projects\c&czero\code\function.h',0x0400048e,157,1,25,'4 7','',0x0,''
+_StateBuffer='C:\projects\c&czero\wwflat32\INCLUDE\gbuffer.h',0x0400048e,265,26,25,'4 7','',0x0,''
+_StateBuffer='C:\projects\c&czero\code\anim.cpp',0x0400048e,682,1,25,'4 7','',0x0,''
+_StateBuffer='C:\projects\c&czero\code\makefile',0x0400048e,420,1,25,'5 9','',0x0,''
+_StateBuffer='C:\projects\c&czero\code\crc.cpp',0x0400048e,1,1,25,'4 7','',0x0,''
+_StateBuffer='C:\projects\c&czero\code\PALETTE.H',0x0400048e,1,1,25,'4 7','',0x0,''
+_StateHistory=SEARCH,'::Mission_U','Force_','MOUSE_','jshell','WindowsTim','LINKFILE','game.dat','wwflat','wwlib','Bitmap'
+_StateHistory=REPLACE,'building','bindex','vindex','iindex','AIRCRAFT','AircraftType','aircraft','aindex','_Scale_To_256','tech'
+_StateHistory=XMACRO,'small','sort','SORT','sort','SORT','sort','SORT','sort','SORT','sort'
+_StateHistory=FILELIST,'C:\projects\c&czero\wwflat32\INCLUDE\gbuffer.h','C:\projects\c&czero\code\function.h','C:\projects\c&czero\code\rotbmp.cpp','C:\projects\c&czero\code\ROTBMP.H','C:\projects\c&czero\code\jshell.h','C:\projects\c&czero\code\palette.cpp','C:\projects\c&czero\code\crc.cpp','C:\projects\c&czero\code\makefile','C:\projects\c&czero\code\PALETTE.H','C:\projects\c&czero\code\anim.cpp'
+_StateHistory=EDITFILE,'palette.cpp','crc.cpp','makefile','palette.cpp','palette.h','makefile','anim.cpp','palette.cpp','function.h','jshell.h'
+_StateHistory=DIRECTORY,'c:\\','c:\projects\c&c\code','e:\c&czero\code','e:','c:\projects\c&czero\code','c:','C:\PROJECTS\C&Czero\code','c:\projects\C&Czero\code'
+_StateHistory=GOTOMARK,'2','1','2','1','2','3','1','2','3','1'
+_StateHistory=OUTNAME,'tab.cpp','power.cpp','2txtprnt.asm','display.cpp','scroll.cpp','sidebar.cpp','power.cpp','help.cpp','tab.cpp','mapeddlg.cpp'
+_StateHistory=GOTOLINE,'1665','410','93','375','590','231','246','616','384','686'
+_StateHistory=WBLOCK,'c:\temp\face.cpp','c:\temp\face.h'
+_StateHistory=FILTER,'sort'
+_StateHistory=DOSHISTORY,'eset path','ndos','eset path','set path=%PATH%;c:\utils','ts *.cpp Interpolated','n','d:','exit','ts *.cpp Interpolated','exit'
+_StateMark=MARK_POSITION,1,'C:\projects\c&czero\wwflat32\INCLUDE\gbuffer.h',265,25,0
+_StateMark=MARK_POSITION,1,'C:\projects\c&czero\code\jshell.h',1,1,0
+
+[Editor]
+ClipboardSetTermStr='\r\n',0
+ClipboardEnableTermStr=1
+ClipboardSetSepStr='\r\n',0
+ClipboardEnableSepStr=1
+ScrapSetCount=1
+VCSProject=''
+VCSProjectPath=''
+VCSProjectLocalPath=''
+_RestoreSysFlags=0x6249f5fa, 0xfffffffc
+
+[Compiler]
+TagSetCmd='${HOME}${WTAGS} -oc -d -t${TAGFILE}.tag -p${TAGFILE}.ptg',0x8000060
+BrowseSetFile='c:\projects\c&czero\code\c&czero.ptg'
+TagSetFile='c:\projects\c&czero\code\c&czero.tag'
+CompilerAddCmd='Microsoft Assembler','ftee masm -w2 -zi %r%e;',1073741824,'',0,'',16,'',16,'',0,'',0,'_MicrosoftErrorInfo','','','','%v%p'
+CompilerAddResponse='Microsoft Assembler',
+CompilerAddCmd='Borland C++','ftee bcc -S %r.c',1073741824,'',0,'ftee make %r.obj',16,'ftee make %r.obj',16,'',0,'',0,'_BorlandCppErrorInfo','','','','%v%p'
+CompilerAddResponse='Borland C++',
+CompilerAddCmd='Borland Turbo Assembler','ftee make %r.obj',1073741824,'',0,'ftee make %r.obj',16,'',16,'',0,'',0,'_TasmErrorInfo','','','','%v%p'
+CompilerAddResponse='Borland Turbo Assembler',
+CompilerAddCmd='$_cw_proj_hash_$','',1073741825,'',1,'ftee watcom\binw\wmake %r.obj',209,'ftee m.bat',209,'ftee joemake',209,'g.bat -hansolo cheater -xm -editor',224,'_MSLinkErrorInfo','_MicrosoftErrorInfo','_NMakeErrorInfo','proj.err','c:\projects\c&czero\code'
+CompilerAddResponse='$_cw_proj_hash_$',
+CompilerAddCmd='Default Project','',1073741824,'',0,'ftee make',16,'ftee make',16,'',0,'',0,'_ErrorInfoDefault','','','proj.err','%v%p'
+CompilerAddResponse='Default Project',
+CompilerAddCmd='Microsoft C','ftee cl -c -AL -Gsw -Ow -Zpe %r%e',1073741824,'',0,'',16,'',16,'',0,'',0,'_MicrosoftErrorInfo','','','','%v%p'
+CompilerAddResponse='Microsoft C',
+CompilerAddCmd='Script','ftee make %r.inf',1073741824,'',0,'ftee make',16,'ftee make',16,'',0,'',0,'_BorlandCppErrorInfo','','','',''
+CompilerAddResponse='Script',
+CompilerAddCmd='Zortech C++','ftee ztc -a -b -c -g -ml -W %r%e',1073741824,'',0,'',16,'',16,'',0,'',0,'_ZortechCppErrorInfo','','','','%v%p'
+CompilerAddResponse='Zortech C++',
+CompilerAddCmd='Microsoft C(NT-i386)','${FTEE} cl -DSTRICT -c -W3 -G3 -D_X86_=1 -DWIN32 %r%e',1073741936,'${FTEE} cl -DSTRICT -c -W3 -G3 -D_X86_=1 -DWIN32 %r%e',112,'',144,'',144,'',0,'',0,'_MicrosoftErrorInfo','','','',''
+CompilerAddResponse='Microsoft C(NT-i386)',
+CompilerAssign='Borland C++','.scr'
+CompilerNewExt=.bas
+CompilerAssign='Borland C++','.int'
+CompilerAssign='Borland C++','.c'
+CompilerNewExt=.prg
+CompilerAssign='Microsoft C','.h'
+CompilerAssign='Borland C++','.cpp'
+CompilerAssign='Default Project','.*'
+CompilerAssign='Microsoft C(NT-i386)','.cxx'
+CompilerAssign='Borland Turbo Assembler','.asm'
+CompilerAssign='Microsoft C(NT-i386)','.hpp'
diff --git a/CODE/CARGO.CPP b/CODE/CARGO.CPP
new file mode 100644
index 0000000..a88574b
--- /dev/null
+++ b/CODE/CARGO.CPP
@@ -0,0 +1,183 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CARGO.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CARGO.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : April 23, 1994 *
+ * *
+ * Last Update : 10/31/94 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * CargoClass::Attach -- Add unit to cargo hold. *
+ * CargoClass::Attached_Object -- Determine attached unit pointer. *
+ * CargoClass::Debug_Dump -- Displays the cargo value to the monochrome screen. *
+ * CargoClass::Detach_Object -- Removes a unit from the cargo hold. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+
+
+#ifdef CHEAT_KEYS
+/***********************************************************************************************
+ * CargoClass::Debug_Dump -- Displays the cargo value to the monochrome screen. *
+ * *
+ * This routine is used to dump the current cargo value to the monochrome monitor. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/02/1994 JLB : Created. *
+ *=============================================================================================*/
+void CargoClass::Debug_Dump(MonoClass * mono) const
+{
+ if (How_Many()) {
+ mono->Set_Cursor(63, 3);
+ mono->Printf("(%d)%04X", How_Many(), Attached_Object());
+ }
+}
+#endif
+
+
+/***********************************************************************************************
+ * CargoClass::Attach -- Add unit to cargo hold. *
+ * *
+ * This routine will add the specified unit to the cargo hold. The *
+ * unit will chain to any existing units in the hold. The chaining is *
+ * in a LIFO order. *
+ * *
+ * INPUT: object-- Pointer to the object to attach to the cargo hold. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 04/23/1994 JLB : Created. *
+ * 10/31/94 JLB : Handles chained objects. *
+ *=============================================================================================*/
+void CargoClass::Attach(FootClass * object)
+{
+ /*
+ ** If there is no object, then no action is necessary.
+ */
+ if (object == NULL) return;
+
+ object->Limbo();
+
+ /*
+ ** Attach any existing cargo hold object to the end of the list as indicated by the
+ ** object pointer passed into this routine. This is necessary because several objects may
+ ** be attached at one time or several objects may be attached as a result of several calls
+ ** to this routine. Either case must be handled properly.
+ */
+ ObjectClass * o = object->Next;
+ while (o != NULL) {
+ if (o->Next == (void*)NULL) break;
+ o = o->Next;
+ }
+ if (o != NULL) {
+ o->Next = CargoHold;
+ } else {
+ object->Next = CargoHold;
+ }
+
+ /*
+ ** Finally, assign the object pointer as the first object attached to this cargo hold.
+ */
+ CargoHold = object;
+ Quantity = 0;
+ object = CargoHold;
+ while (object != NULL) {
+ Quantity++;
+ object = (FootClass *)(ObjectClass *)object->Next;
+ }
+}
+
+
+/***********************************************************************************************
+ * CargoClass::Detach_Object -- Removes a unit from the cargo hold. *
+ * *
+ * This routine will take a unit from the cargo hold and extract it. *
+ * The unit extracted is the last unit added to the hold. If there *
+ * is no unit in the hold or the occupant is not a unit, then NULL is *
+ * returned. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with a pointer to the unit that has been extracted. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 04/23/1994 JLB : Created. *
+ * 06/07/1994 JLB : Handles generic object types. *
+ *=============================================================================================*/
+FootClass * CargoClass::Detach_Object(void)
+{
+ TechnoClass * unit = Attached_Object();
+
+ if (unit != NULL) {
+ CargoHold = (FootClass *)(ObjectClass *)unit->Next;
+ unit->Next = 0;
+ Quantity--;
+ }
+ return((FootClass *)unit);
+}
+
+
+/***********************************************************************************************
+ * CargoClass::Attached_Object -- Determine attached unit pointer. *
+ * *
+ * This routine will return with a pointer to the attached unit if one *
+ * is present. One would need to know this if this is a transport *
+ * unit and it needs to unload. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns a pointer to the attached unit. If there is no *
+ * attached unit, then return NULL. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 09/07/1992 JLB : Created. *
+ * 06/07/1994 JLB : Handles generic object types. *
+ *=============================================================================================*/
+FootClass * CargoClass::Attached_Object(void) const
+{
+ if (Is_Something_Attached()) {
+ return(CargoHold);
+ }
+ return(NULL);
+}
+
+
diff --git a/CODE/CARGO.H b/CODE/CARGO.H
new file mode 100644
index 0000000..f6a078f
--- /dev/null
+++ b/CODE/CARGO.H
@@ -0,0 +1,93 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CARGO.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CARGO.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : April 23, 1994 *
+ * *
+ * Last Update : April 23, 1994 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifndef CARGO_H
+#define CARGO_H
+
+class FootClass;
+
+/****************************************************************************
+** This class handles the basic cargo logic.
+*/
+class CargoClass {
+ public:
+
+ /*---------------------------------------------------------------------
+ ** Constructors, Destructors, and overloaded operators.
+ */
+ CargoClass(void) : Quantity(0), CargoHold(0) {};
+ CargoClass(NoInitClass const & ) {};
+ ~CargoClass(void) {CargoHold=0;};
+
+ /*---------------------------------------------------------------------
+ ** Member function prototypes.
+ */
+
+ #ifdef CHEAT_KEYS
+ void Debug_Dump(MonoClass *mono) const;
+ #endif
+ void AI(void) {};
+
+ int How_Many(void) const {return Quantity;};
+ bool Is_Something_Attached(void) const {return (CargoHold != 0);};
+ FootClass * Attached_Object(void) const;
+ FootClass * Detach_Object(void);
+ void Attach(FootClass * object);
+
+ /*
+ ** File I/O.
+ */
+ void Code_Pointers(void);
+ void Decode_Pointers(void);
+
+ private:
+
+ /*
+ ** This is the number of objects attached to this cargo hold. For transporter
+ ** objects, they might contain more than one object.
+ */
+ unsigned char Quantity;
+
+ /*
+ ** This is the target value of any attached object. A value of zero indicates
+ ** that no object is attached.
+ */
+ FootClass * CargoHold;
+};
+
+#endif
+
diff --git a/CODE/CARRY.CPP b/CODE/CARRY.CPP
new file mode 100644
index 0000000..1535297
--- /dev/null
+++ b/CODE/CARRY.CPP
@@ -0,0 +1,153 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CARRY.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CARRY.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 02/26/96 *
+ * *
+ * Last Update : May 10, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * CarryoverClass::CarryoverClass -- Constructor for carry over objects. *
+ * CarryoverClass::Create -- Creates a carried over object. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#include "function.h"
+
+
+/***********************************************************************************************
+ * CarryoverClass::CarryoverClass -- Constructor for carry over objects. *
+ * *
+ * This is the constructor for a carry over object. Such an object is used to record the *
+ * object that will be "carried over" into a new scenario at some future time. *
+ * *
+ * INPUT: techno -- Pointer to the object that will be carried over. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/10/1996 JLB : Created. *
+ *=============================================================================================*/
+CarryoverClass::CarryoverClass(TechnoClass * techno) :
+ RTTI(RTTI_NONE),
+ Cell(0),
+ Strength(0),
+ House(HOUSE_NONE)
+{
+ if (techno) {
+ RTTI = techno->What_Am_I();
+
+ switch (RTTI) {
+ case RTTI_UNIT:
+ Type.Unit = ((UnitClass *)techno)->Class->Type;
+ break;
+
+ case RTTI_BUILDING:
+ Type.Building = ((BuildingClass *)techno)->Class->Type;
+ break;
+
+ case RTTI_INFANTRY:
+ Type.Infantry = ((InfantryClass *)techno)->Class->Type;
+ break;
+
+ case RTTI_VESSEL:
+ Type.Vessel = ((VesselClass *)techno)->Class->Type;
+ break;
+
+ default:
+ break;
+ }
+
+ House = techno->Owner();
+ Strength = techno->Strength;
+ Cell = Coord_Cell(techno->Coord);
+ }
+}
+
+
+/***********************************************************************************************
+ * CarryoverClass::Create -- Creates a carried over object. *
+ * *
+ * Use this routine to convert a carried over object into an actual object that will be *
+ * placed on the map. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: bool; Was the object successfully created and placed on the map? *
+ * *
+ * WARNINGS: This routine might not place the object if the old map location was invalid *
+ * or there are other barriers to the object's creation and placement. *
+ * *
+ * HISTORY: *
+ * 05/10/1996 JLB : Created. *
+ *=============================================================================================*/
+bool CarryoverClass::Create(void) const
+{
+ TechnoClass * techno = 0;
+ TechnoTypeClass const * ttype = 0;
+
+ switch (RTTI) {
+ case RTTI_UNIT:
+ ttype = &UnitTypeClass::As_Reference(Type.Unit);
+ techno = new UnitClass(Type.Unit, House);
+ break;
+
+ case RTTI_INFANTRY:
+ ttype = &InfantryTypeClass::As_Reference(Type.Infantry);
+ techno = new InfantryClass(Type.Infantry, House);
+ break;
+
+ case RTTI_BUILDING:
+ ttype = &BuildingTypeClass::As_Reference(Type.Building);
+ techno = new BuildingClass(Type.Building, House);
+ break;
+
+ case RTTI_VESSEL:
+ ttype = &VesselTypeClass::As_Reference(Type.Vessel);
+ techno = new VesselClass(Type.Vessel, House);
+ break;
+ }
+
+ if (techno) {
+ bool oldscen = ScenarioInit;
+ techno->Strength = Strength;
+ if (RTTI == RTTI_INFANTRY) {
+ ScenarioInit = 0;
+ }
+ techno->Unlimbo(Cell_Coord(Cell));
+ if (RTTI == RTTI_INFANTRY) {
+ ScenarioInit = oldscen;
+ }
+ }
+
+ return(false);
+}
+
diff --git a/CODE/CARRY.H b/CODE/CARRY.H
new file mode 100644
index 0000000..fb045d7
--- /dev/null
+++ b/CODE/CARRY.H
@@ -0,0 +1,84 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CARRY.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CARRY.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 02/26/96 *
+ * *
+ * Last Update : February 26, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#ifndef CARRY_H
+#define CARRY_H
+
+class CarryoverClass : public LinkClass {
+ public:
+ CarryoverClass(TechnoClass * techno = 0);
+ CarryoverClass(NoInitClass const & x) : LinkClass(x) {}
+
+ bool Create(void) const;
+
+ protected:
+ /*
+ ** What type of object this is.
+ */
+ RTTIType RTTI;
+
+ /*
+ ** This is the object type that is to be carried over. The exact nature of
+ ** this type depends on the RTTI value. Only certain object types are
+ ** recorded.
+ */
+ union {
+ StructType Building;
+ UnitType Unit;
+ InfantryType Infantry;
+ VesselType Vessel;
+ } Type;
+
+ /*
+ ** The location of the object.
+ */
+ CELL Cell;
+
+ /*
+ ** The strength of the object at the time is was recorded.
+ */
+ int Strength;
+
+ /*
+ ** This is the owner of the object.
+ */
+ HousesType House;
+};
+
+
+#endif
diff --git a/CODE/CCDDE.CPP b/CODE/CCDDE.CPP
new file mode 100644
index 0000000..06a69bb
--- /dev/null
+++ b/CODE/CCDDE.CPP
@@ -0,0 +1,431 @@
+/*
+** Command & Conquer Red Alert(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 - Red Alert *
+ * *
+ * File Name : CCDDE.CPP *
+ * *
+ * Programmer : Steve Tall *
+ * *
+ * Start Date : 10/04/95 *
+ * *
+ * Last Update : August 5th, 1996 [ST] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Overview: *
+ * C&C's interface to the DDE class *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * *
+ * Functions: *
+ * DDE_Callback -- DDE server callback function *
+ * DDEServerClass::DDEServerClass -- class constructor *
+ * DDEServerClass::Enable -- Enables the DDE callback *
+ * DDEServerClass::Disable -- Disables the DDE callback *
+ * DDEServerClass::~DDEServerClass -- class destructor *
+ * DDESC::Callback -- callback function. Called from the DDE_Callback wrapper function *
+ * DDESC::Get_MPlayer_Game_Info -- returns a pointer to the multiplayer setup info from wchat *
+ * DDESC::Delete_MPlayer_Game_Info -- clears out multi player game setup info *
+ * DDESC::Time_Since_Heartbeat -- returns the time in ticks since the last heartbeat from wchat*
+ * *
+ * Send_Data_To_DDE_Server -- sends a packet to WChat *
+ * *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifdef WIN32
+
+#include
+#include "ccdde.h"
+#include
+#include
+
+DDEServerClass DDEServer; //Instance of the DDE Server class
+
+Instance_Class *DDE_Class = NULL; // pointer for client callback
+ // this *must* be called DDE_Class
+
+BOOL RA95AlreadyRunning = FALSE; //Was there an instance of Red Alert 95 already running when we started?
+
+/*
+** Misc externs so we dont have to include FUNCTION.H
+*/
+extern HWND MainWindow;
+extern TimerClass GameTimer;
+extern BOOL GameTimerInUse;
+extern void WWDebugString (char *string);
+
+
+/***********************************************************************************************
+ * DDE_Callback -- DDE server callback function *
+ * *
+ * Just acts as a wrapper for the DDEServerClass callback function *
+ * *
+ * INPUT: ptr to data from client *
+ * length of data *
+ * *
+ * OUTPUT: Nothing *
+ * *
+ * WARNINGS: None *
+ * *
+ * HISTORY: *
+ * 6/8/96 3:19PM ST : Created *
+ *=============================================================================================*/
+BOOL CALLBACK DDE_Callback (unsigned char *data, long length)
+{
+ return (DDEServer.Callback(data, length));
+}
+
+
+
+
+/***********************************************************************************************
+ * DDEServerClass::DDEServerClass -- class constructor *
+ * *
+ * *
+ * *
+ * INPUT: Nothing *
+ * *
+ * OUTPUT: Nothing *
+ * *
+ * WARNINGS: None *
+ * *
+ * HISTORY: *
+ * 6/8/96 3:20PM ST : Created *
+ *=============================================================================================*/
+DDEServerClass::DDEServerClass(void)
+{
+ MPlayerGameInfo = NULL; //Flag that we havnt received a start game info packet yet
+
+ //DDE_Class = new Instance_Class ("CONQUER", "WCHAT");
+ DDE_Class = new Instance_Class ("REDALERT", "WCHAT");
+
+ DDE_Class->Enable_Callback( TRUE );
+ IsEnabled = TRUE;
+
+ if (DDE_Class->Test_Server_Running(DDE_Class->local_name)){
+ RA95AlreadyRunning = TRUE;
+ }else{
+ DDE_Class->Register_Server( DDE_Callback );
+ }
+}
+
+
+
+/***********************************************************************************************
+ * DDEServerClass::Enable -- Enables the DDE callback *
+ * *
+ * *
+ * *
+ * INPUT: Nothing *
+ * *
+ * OUTPUT: Nothing *
+ * *
+ * WARNINGS: None *
+ * *
+ * HISTORY: *
+ * 8/5/96 9:44PM ST : Created *
+ *=============================================================================================*/
+void DDEServerClass::Enable(void)
+{
+ if (!IsEnabled){
+ DDE_Class->Enable_Callback( TRUE );
+ IsEnabled = TRUE;
+ }
+}
+
+
+
+/***********************************************************************************************
+ * DDEServerClass::Disable -- Disables the DDE callback *
+ * *
+ * *
+ * *
+ * INPUT: Nothing *
+ * *
+ * OUTPUT: Nothing *
+ * *
+ * WARNINGS: None *
+ * *
+ * HISTORY: *
+ * 8/5/96 9:44PM ST : Created *
+ *=============================================================================================*/
+void DDEServerClass::Disable(void)
+{
+ if (IsEnabled){
+ DDE_Class->Enable_Callback( FALSE );
+ IsEnabled = FALSE;
+ }
+}
+
+
+
+
+
+
+/***********************************************************************************************
+ * DDEServerClass::~DDEServerClass -- class destructor *
+ * *
+ * *
+ * *
+ * INPUT: Nothing *
+ * *
+ * OUTPUT: Nothing *
+ * *
+ * WARNINGS: None *
+ * *
+ * HISTORY: *
+ * 6/8/96 3:20PM ST : Created *
+ *=============================================================================================*/
+DDEServerClass::~DDEServerClass(void)
+{
+ Delete_MPlayer_Game_Info();
+ delete( DDE_Class );
+}
+
+
+
+/***********************************************************************************************
+ * DDESC::Callback -- callback function. Called from the DDE_Callback wrapper function *
+ * *
+ * *
+ * *
+ * INPUT: data from DDE client *
+ * length of data *
+ * *
+ * OUTPUT: Nothing *
+ * *
+ * WARNINGS: Data has length and type as first 2 ints *
+ * *
+ * HISTORY: *
+ * 6/8/96 3:21PM ST : Created *
+ *=============================================================================================*/
+BOOL DDEServerClass::Callback(unsigned char *data, long length)
+{
+
+ /*
+ ** If the packet length < 0 then this is a special advisory packet
+ */
+ if ( length<0 ) {
+
+ switch( length ) {
+
+ case DDE_ADVISE_CONNECT:
+ WWDebugString("RA95 - DDE advisory: client connect detected.");
+ return TRUE;
+
+ case DDE_ADVISE_DISCONNECT:
+ WWDebugString("RA95 - DDE advisory: client disconnect detected.");
+ return TRUE;
+
+ default:
+ WWDebugString("RA95 - DDE advisory: Unknown DDE advise type.");
+ return FALSE;
+ }
+
+ }else{
+
+ /*
+ ** Packet must be at least the length of the packet type & size fields to be valid
+ */
+ if (length < 2*sizeof(int)) {
+ WWDebugString ("RA95 - Received invalid packet.");
+ return (FALSE);
+ }
+
+ /*
+ ** Find out what kind of packet this is and its length.
+ */
+ int *packet_pointer = (int *)data;
+ int actual_length = ntohl(*packet_pointer++);
+ int packet_type = ntohl(*packet_pointer++);
+
+ /*
+ ** Strip the ID int from the start of the packet
+ */
+ data += 2*sizeof (int);
+ length -= 2*sizeof (int);
+ actual_length -= 2*sizeof (int);
+
+ /*
+ ** Take the appropriate action for the packet type
+ */
+ switch ( packet_type ){
+
+ /*
+ ** This is a packet with the info required for starting a new internet game. This is really
+ * just C&CSPAWN.INI sent from WChat instead of read from disk.
+ */
+ case DDE_PACKET_START_MPLAYER_GAME:
+ WWDebugString("RA95 - Received start game packet.");
+ Delete_MPlayer_Game_Info();
+ MPlayerGameInfo = new char [actual_length + 1];
+ memcpy (MPlayerGameInfo, data, actual_length);
+ *(MPlayerGameInfo + actual_length) = 0; //Terminator in case we treat it as a string
+ MPlayerGameInfoLength = actual_length;
+ LastHeartbeat = 0;
+ break;
+
+ case DDE_TICKLE:
+ WWDebugString("RA95 - Received 'tickle' packet.");
+ //SetForegroundWindow ( MainWindow );
+ //ShowWindow ( MainWindow, SW_SHOWMAXIMIZED );
+ break;
+
+ case DDE_PACKET_HEART_BEAT:
+ WWDebugString("RA95 - Received heart beat packet.");
+ if (GameTimerInUse){
+ LastHeartbeat = GameTimer.Time();
+ }else{
+ LastHeartbeat = 0;
+ }
+ break;
+
+ default:
+ WWDebugString("RA95 - Received unrecognised packet.");
+ break;
+
+ }
+ }
+
+ return (TRUE);
+
+}
+
+
+
+/***********************************************************************************************
+ * DDESC::Get_MPlayer_Game_Info -- returns a pointer to the multiplayer setup info from wchat *
+ * *
+ * *
+ * *
+ * INPUT: Nothing *
+ * *
+ * OUTPUT: ptr to data in .INI file format *
+ * *
+ * WARNINGS: None *
+ * *
+ * HISTORY: *
+ * 6/8/96 3:23PM ST : Created *
+ *=============================================================================================*/
+char *DDEServerClass::Get_MPlayer_Game_Info (void)
+{
+ return (MPlayerGameInfo);
+}
+
+
+
+/***********************************************************************************************
+ * DDESC::Delete_MPlayer_Game_Info -- clears out multi player game setup info *
+ * *
+ * *
+ * *
+ * INPUT: Nothing *
+ * *
+ * OUTPUT: Nothing *
+ * *
+ * WARNINGS: None *
+ * *
+ * HISTORY: *
+ * 6/8/96 3:24PM ST : Created *
+ *=============================================================================================*/
+void DDEServerClass::Delete_MPlayer_Game_Info(void)
+{
+ if (MPlayerGameInfo){
+ delete [] MPlayerGameInfo;
+ MPlayerGameInfo = NULL;
+ }
+}
+
+
+
+/***********************************************************************************************
+ * DDESC::Time_Since_Heartbeat -- returns the time in ticks since the last heartbeat from wchat*
+ * *
+ * *
+ * *
+ * INPUT: Nothing *
+ * *
+ * OUTPUT: time since heartbeat *
+ * *
+ * WARNINGS: None *
+ * *
+ * HISTORY: *
+ * 6/9/96 11:05PM ST : Created *
+ *=============================================================================================*/
+int DDEServerClass::Time_Since_Heartbeat(void)
+{
+ return (GameTimer.Time() - LastHeartbeat);
+}
+
+
+
+
+/***********************************************************************************************
+ * Send_Data_To_DDE_Server -- sends a packet to WChat *
+ * *
+ * *
+ * *
+ * INPUT: ptr to the data to send *
+ * length of data *
+ * packet type identifier *
+ * *
+ * OUTPUT: true if packet successfully sent *
+ * *
+ * WARNINGS: None *
+ * *
+ * HISTORY: *
+ * 6/9/96 11:07PM ST : Created *
+ *=============================================================================================*/
+BOOL Send_Data_To_DDE_Server (char *data, int length, int packet_type)
+{
+ if( DDE_Class->Open_Poke_Connection(DDE_Class->remote_name) == FALSE) {
+ WWDebugString("RA95 - Failed to connect for POKE!");
+ return (FALSE);
+ }
+
+ char *poke_data = new char [length + 2*sizeof(int)];
+
+ int *poke_data_int = (int*)poke_data;
+
+ *poke_data_int = htonl (length + 2*sizeof(int));
+ *(poke_data_int+1)= htonl (packet_type);
+
+ memcpy (poke_data + 8, data, length);
+
+
+ if(DDE_Class->Poke_Server( (LPBYTE) poke_data, ntohl(*poke_data_int) ) == FALSE) {
+ WWDebugString("RA95 - POKE failed!\n");
+ DDE_Class->Close_Poke_Connection(); // close down the link
+ delete poke_data;
+ return (FALSE);
+ }
+
+ DDE_Class->Close_Poke_Connection(); // close down the link
+
+ delete poke_data;
+
+ return (TRUE);
+}
+
+
+#endif //WIN32
diff --git a/CODE/CCDDE.H b/CODE/CCDDE.H
new file mode 100644
index 0000000..c4a6020
--- /dev/null
+++ b/CODE/CCDDE.H
@@ -0,0 +1,89 @@
+/*
+** Command & Conquer Red Alert(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 - Red Alert *
+ * *
+ * File Name : CCDDE.H *
+ * *
+ * Programmer : Steve Tall *
+ * *
+ * Start Date : 10/04/95 *
+ * *
+ * Last Update : August 5th, 1996 [ST] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Overview: *
+ * C&C's interface to the DDE class *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * *
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifdef WIN32
+
+#include "dde.h"
+
+class DDEServerClass {
+
+ public:
+
+ DDEServerClass (void);
+ ~DDEServerClass (void);
+
+
+ char *Get_MPlayer_Game_Info (void); //Returns pointer to game info
+ int Get_MPlayer_Game_Info_Length(){return(MPlayerGameInfoLength);}; //Len of game info
+ BOOL Callback(unsigned char *data, long length); //DDE callback function
+ void Delete_MPlayer_Game_Info(void); //release the game info memory
+ void Enable(void); //Enable the DDE callback
+ void Disable(void); //Disable the DDE callback
+ int Time_Since_Heartbeat(void); //Returns the time since the last hearbeat from WChat
+
+ /*
+ ** Enumeration for DDE packet types from WChat
+ */
+ enum {
+ DDE_PACKET_START_MPLAYER_GAME, //Start game packet. This includes game options
+ DDE_PACKET_GAME_RESULTS, //Game results packet. The game statistics.
+ DDE_PACKET_HEART_BEAT, //Heart beat packet so we know WChat is still there.
+ DDE_TICKLE, //Message to prompt other app to take focus.
+ DDE_CONNECTION_FAILED
+ };
+
+
+ private:
+
+ char *MPlayerGameInfo; //Pointer to game start packet
+ int MPlayerGameInfoLength; //Length of game start packet.
+ BOOL IsEnabled; //Flag for DDE callback enable
+ int LastHeartbeat; // Time since last heartbeat packet was received from WChat
+
+};
+
+
+extern DDEServerClass DDEServer;
+extern BOOL Send_Data_To_DDE_Server (char *data, int length, int packet_type);
+
+
+#endif //WIN32
diff --git a/CODE/CCFILE.CPP b/CODE/CCFILE.CPP
new file mode 100644
index 0000000..4cc104a
--- /dev/null
+++ b/CODE/CCFILE.CPP
@@ -0,0 +1,693 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CCFILE.CPP 2 3/13/97 2:05p Steve_tall $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CCFILE.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : August 8, 1994 *
+ * *
+ * Last Update : August 5, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * CCFileClass::CCFileClass -- Default constructor for file object. *
+ * CCFileClass::CCFileClass -- Filename based constructor for C&C file. *
+ * CCFileClass::Close -- Closes the file. *
+ * CCFileClass::Error -- Handles displaying a file error message. *
+ * CCFileClass::Is_Available -- Checks for existence of file on disk or in mixfile. *
+ * CCFileClass::Is_Open -- Determines if the file is open. *
+ * CCFileClass::Open -- Opens a file from either the mixfile system or the rawfile system. *
+ * CCFileClass::Read -- Reads data from the file. *
+ * CCFileClass::Seek -- Moves the current file pointer in the file. *
+ * CCFileClass::Size -- Determines the size of the file. *
+ * CCFileClass::Write -- Writes data to the file (non mixfile files only). *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#include "function.h"
+#include
+#include "ccfile.h"
+
+
+/***********************************************************************************************
+ * CCFileClass::CCFileClass -- Filename based constructor for C&C file. *
+ * *
+ * Use this constructor for a file when the filename is known at construction time. *
+ * *
+ * INPUT: filename -- Pointer to the filename to use for this file object. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: The filename pointer is presumed to be inviolate throughout the duration of *
+ * the file object. If this is not guaranteed, then use the default constructor *
+ * and then set the name manually. *
+ * *
+ * HISTORY: *
+ * 03/20/1995 JLB : Created. *
+ *=============================================================================================*/
+CCFileClass::CCFileClass(char const * filename) :
+ Position(0)
+{
+ CCFileClass::Set_Name(filename);
+}
+
+
+/***********************************************************************************************
+ * CCFileClass::CCFileClass -- Default constructor for file object. *
+ * *
+ * This is the default constructor for a C&C file object. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 03/20/1995 JLB : Created. *
+ *=============================================================================================*/
+CCFileClass::CCFileClass(void) :
+ Position(0)
+{
+}
+
+
+/***********************************************************************************************
+ * CCFileClass::Error -- Handles displaying a file error message. *
+ * *
+ * Display an error message as indicated. If it is allowed to retry, then pressing a key *
+ * will return from this function. Otherwise, it will exit the program with "exit()". *
+ * *
+ * INPUT: error -- The error number (same as the DOSERR.H error numbers). *
+ * *
+ * canretry -- Can this routine exit normally so that retrying can occur? If this is *
+ * false, then the program WILL exit in this routine. *
+ * *
+ * filename -- Optional filename to report with this error. If no filename is *
+ * supplied, then no filename is listed in the error message. *
+ * *
+ * OUTPUT: none, but this routine might not return at all if the "canretry" parameter is *
+ * false or the player pressed ESC. *
+ * *
+ * WARNINGS: This routine may not return at all. It handles being in text mode as well as *
+ * if in a graphic mode. *
+ * *
+ * HISTORY: *
+ * 10/17/1994 JLB : Created. *
+ *=============================================================================================*/
+void CCFileClass::Error(int , int , char const * )
+{
+ if (!Force_CD_Available(RequiredCD)) {
+ //Prog_End();
+ Emergency_Exit(EXIT_FAILURE);
+ }
+}
+
+
+/***********************************************************************************************
+ * CCFileClass::Write -- Writes data to the file (non mixfile files only). *
+ * *
+ * This routine will write data to the file, but NOT to a file that is part of a mixfile. *
+ * *
+ * INPUT: buffer -- Pointer to the buffer that holds the data to be written. *
+ * *
+ * size -- The number of bytes to write. *
+ * *
+ * OUTPUT: Returns the number of bytes actually written. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/08/1994 JLB : Created. *
+ *=============================================================================================*/
+long CCFileClass::Write(void const * buffer, long size)
+{
+ /*
+ ** If this is part of a mixfile, then writing is not allowed. Error out with a fatal
+ ** message.
+ */
+ if (Is_Resident()) {
+ Error(EACCES, false, File_Name());
+ }
+
+ return(CDFileClass::Write(buffer, size));
+}
+
+
+/***********************************************************************************************
+ * CCFileClass::Read -- Reads data from the file. *
+ * *
+ * This routine determines if the file is part of the mixfile system. If it is, then *
+ * the file is copied from RAM if it is located there. Otherwise it is read from disk *
+ * according to the correct position of the file within the parent mixfile. *
+ * *
+ * INPUT: buffer -- Pointer to the buffer to place the read data. *
+ * *
+ * size -- The number of bytes to read. *
+ * *
+ * OUTPUT: Returns the actual number of bytes read (this could be less than requested). *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/08/1994 JLB : Created. *
+ *=============================================================================================*/
+long CCFileClass::Read(void * buffer, long size)
+{
+ bool opened = false;
+
+ /*
+ ** If the file isn't currently open, then open it.
+ */
+ if (!Is_Open()) {
+ if (Open()) {
+ opened = true;
+ }
+ }
+
+ /*
+ ** If the file is part of a loaded mixfile, then a mere copy is
+ ** all that is required for the read.
+ */
+ if (Is_Resident()) {
+ long maximum = Data.Get_Size() - Position;
+
+ size = maximum < size ? maximum : size;
+// size = MIN(maximum, size);
+ if (size) {
+ memmove(buffer, (char *)Data + Position, size);
+// Mem_Copy((char *)Pointer + Position, buffer, size);
+ Position += size;
+ }
+ if (opened) Close();
+ return(size);
+ }
+
+ long s = CDFileClass::Read(buffer, size);
+
+ /*
+ ** If the file was opened by this routine, then close it at this time.
+ */
+ if (opened) Close();
+
+ /*
+ ** Return with the number of bytes read.
+ */
+ return(s);
+}
+
+
+/***********************************************************************************************
+ * CCFileClass::Seek -- Moves the current file pointer in the file. *
+ * *
+ * This routine will change the current file pointer to the position specified. It follows *
+ * the same rules the a normal Seek() does, but if the file is part of the mixfile system, *
+ * then only the position value needs to be updated. *
+ * *
+ * INPUT: pos -- The position to move the file to relative to the position indicated *
+ * by the "dir" parameter. *
+ * *
+ * dir -- The direction to affect the position change against. This can be *
+ * either SEEK_CUR, SEEK_END, or SEEK_SET. *
+ * *
+ * OUTPUT: Returns with the position of the new location. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/08/1994 JLB : Created. *
+ *=============================================================================================*/
+long CCFileClass::Seek(long pos, int dir)
+{
+ /*
+ ** When the file is resident, a mere adjustment of the virtual file position is
+ ** all that is required of a seek.
+ */
+ if (Is_Resident()) {
+ switch (dir) {
+ case SEEK_END:
+ Position = Data.Get_Size();
+ break;
+
+ case SEEK_SET:
+ Position = 0;
+ break;
+
+ case SEEK_CUR:
+ default:
+ break;
+ }
+ Position += pos;
+ Position = Position < 0 ? 0 : Position;
+ Position = Position > Data.Get_Size() ? Data.Get_Size() : Position;
+// Position = Bound(Position+pos, 0L, Length);
+ return(Position);
+ }
+ return(CDFileClass::Seek(pos, dir));
+}
+
+
+/***********************************************************************************************
+ * CCFileClass::Size -- Determines the size of the file. *
+ * *
+ * If the file is part of the mixfile system, then the size of the file is already *
+ * determined and available. Otherwise, go to the low level system to find the file *
+ * size. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the size of the file in bytes. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/08/1994 JLB : Created. *
+ * 08/05/1996 JLB : Handles returning size of embedded file. *
+ *=============================================================================================*/
+long CCFileClass::Size(void)
+{
+ /*
+ ** If the file is resident, the the size is already known. Just return the size in this
+ ** case.
+ */
+ if (Is_Resident()) return(Data.Get_Size());
+
+ /*
+ ** If the file is not available as a stand alone file, then search for it in the
+ ** mixfiles in order to get its size.
+ */
+ if (!CDFileClass::Is_Available()) {
+ long length = 0;
+ MFCD::Offset(File_Name(), NULL, NULL, NULL, &length);
+ return(length);
+ }
+
+ return(CDFileClass::Size());
+}
+
+
+/***********************************************************************************************
+ * CCFileClass::Is_Available -- Checks for existence of file on disk or in mixfile. *
+ * *
+ * This routine will examine the mixfile system looking for the file. If the file could *
+ * not be found there, then the disk is examined directly. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: bool; Is the file available for opening? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/08/1994 JLB : Created. *
+ *=============================================================================================*/
+int CCFileClass::Is_Available(int )
+{
+ /*
+ ** A file that is open is presumed available.
+ */
+ if (Is_Open()) return(true);
+
+ /*
+ ** A file that is part of a mixfile is also presumed available.
+ */
+ if (MFCD::Offset(File_Name())) {
+ return(true);
+ }
+
+ /*
+ ** Otherwise a manual check of the file system is required to
+ ** determine if the file is actually available.
+ */
+ return(CDFileClass::Is_Available());
+}
+
+
+/***********************************************************************************************
+ * CCFileClass::Is_Open -- Determines if the file is open. *
+ * *
+ * A mixfile is open if there is a pointer to the mixfile data. In absence of this, *
+ * the the file is open if the file handle is valid. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: bool; Is the file open? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/08/1994 JLB : Created. *
+ *=============================================================================================*/
+int CCFileClass::Is_Open(void) const
+{
+ /*
+ ** If the file is part of a cached file, then return that it is opened. A closed file
+ ** doesn't have a valid pointer.
+ */
+ if (Is_Resident()) return(true);
+
+ /*
+ ** Otherwise, go to a lower level to determine if the file is open.
+ */
+ return(CDFileClass::Is_Open());
+}
+
+
+/***********************************************************************************************
+ * CCFileClass::Close -- Closes the file. *
+ * *
+ * If this is a mixfile file, then only the pointers need to be adjusted. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/08/1994 JLB : Created. *
+ *=============================================================================================*/
+void CCFileClass::Close(void)
+{
+ new(&Data) ::Buffer;
+ Position = 0; // Starts at beginning offset.
+ CDFileClass::Close();
+}
+
+
+/***********************************************************************************************
+ * CCFileClass::Open -- Opens a file from either the mixfile system or the rawfile system. *
+ * *
+ * This routine will open the specified file. It examines the mixfile system to find a *
+ * match. If one is found then the file is "opened" in a special cached way. Otherwise *
+ * it is opened as a standard DOS file. *
+ * *
+ * INPUT: rights -- The access rights desired. *
+ * *
+ * OUTPUT: bool; Was the file opened successfully? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/08/1994 JLB : Created. *
+ *=============================================================================================*/
+int CCFileClass::Open(int rights)
+{
+ /*
+ ** Always close the file if it was open.
+ */
+ Close();
+
+ /*
+ ** Perform a preliminary check to see if the specified file
+ ** exists on the disk. If it does, then open this file regardless
+ ** of whether it also exists in RAM. This is slower, but allows
+ ** upgrade files to work.
+ */
+ if ((rights & WRITE) || CDFileClass::Is_Available()) {
+ return(CDFileClass::Open(rights));
+ }
+
+ /*
+ ** Check to see if file is part of a mixfile and that mixfile is currently loaded
+ ** into RAM.
+ */
+ MFCD * mixfile = NULL;
+ void * pointer = NULL;
+ long length = 0;
+ long start = 0;
+ if (MFCD::Offset(File_Name(), &pointer, &mixfile, &start, &length)) {
+
+ assert(mixfile != NULL);
+
+ /*
+ ** If the mixfile is located on disk, then fake out the file system to read from
+ ** the mixfile, but think it is reading from a solitary file.
+ */
+ if (pointer == NULL && mixfile != NULL) {
+
+ /*
+ ** This is a legitimate open to the file. All access to the file through this
+ ** file object will be appropriately adjusted for mixfile support however. Also
+ ** note that the filename attached to this object is NOT the same as the file
+ ** attached to the file handle.
+ */
+ char * dupfile = strdup(File_Name());
+ Open(mixfile->Filename, READ);
+ Searching(false); // Disable multi-drive search.
+ Set_Name(dupfile);
+ Searching(true);
+ free(dupfile);
+ Bias(0);
+ Bias(start, length);
+ Seek(0, SEEK_SET);
+ } else {
+ new (&Data) ::Buffer(pointer, length);
+ Position = 0;
+ }
+
+ } else {
+
+ /*
+ ** The file cannot be found in any mixfile, so it must reside as
+ ** an individual file on the disk. Or else it is just plain missing.
+ */
+ return(CDFileClass::Open(rights));
+ }
+ return(true);
+}
+
+
+/***********************************************************************************************
+ * CCFileClass::Get_Date_Time -- Gets the date and time the file was last modified. *
+ * *
+ * Use this routine to get the date and time of the file. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the file date and time as a long. *
+ * Use the YEAR(long), MONTH(),.... *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 11/14/1995 DRD : Created. *
+ *=============================================================================================*/
+unsigned long CCFileClass::Get_Date_Time(void)
+{
+ unsigned long datetime;
+ MFCD * mixfile;
+
+ datetime = CDFileClass::Get_Date_Time();
+
+ if ( !datetime ) {
+ if (MFCD::Offset(File_Name(), NULL, &mixfile, NULL, NULL)) {
+ //
+ // check for nested MIX files
+ //
+ return( CCFileClass(mixfile->Filename).Get_Date_Time() );
+ }
+ // else return 0 indicating no file
+ }
+
+ return( datetime );
+}
+
+
+/***********************************************************************************************
+ * CCFileClass::Set_Date_Time -- Sets the date and time the file was last modified. *
+ * *
+ * Use this routine to set the date and time of the file. *
+ * *
+ * INPUT: the file date and time as a long *
+ * *
+ * OUTPUT: successful or not if the file date and time was changed. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 11/14/1995 DRD : Created. *
+ *=============================================================================================*/
+bool CCFileClass::Set_Date_Time( unsigned long datetime )
+{
+ bool status;
+ MFCD * mixfile;
+
+ status = CDFileClass::Set_Date_Time( datetime );
+
+ if ( !status ) {
+ if (MFCD::Offset(File_Name(), NULL, &mixfile, NULL, NULL)) {
+ //
+ // check for nested MIX files
+ //
+ return( CCFileClass(mixfile->Filename).Set_Date_Time( datetime ) );
+ }
+ // else return 0 indicating no file
+ }
+
+ return( status );
+}
+
+
+/***********************************************************************************
+** Backward compatibility section.
+*/
+//extern "C" {
+
+
+static CCFileClass Handles[10];
+
+int __cdecl Open_File(char const * file_name, int mode)
+{
+ for (int index = 0; index < ARRAY_SIZE(Handles); index++) {
+ if (!Handles[index].Is_Open()) {
+ if (Handles[index].Open(file_name, mode)) {
+ return(index);
+ }
+ break;
+ }
+ }
+ return(WWERROR);
+}
+
+void __cdecl Close_File(int handle)
+{
+ if (handle != WWERROR && Handles[handle].Is_Open()) {
+ Handles[handle].Close();
+ }
+}
+
+long __cdecl Read_File(int handle, void * buf, unsigned long bytes)
+{
+ if (handle != WWERROR && Handles[handle].Is_Open()) {
+ return(Handles[handle].Read(buf, bytes));
+ }
+ return(0);
+}
+
+long __cdecl Write_File(int handle, void const * buf, unsigned long bytes)
+{
+ if (handle != WWERROR && Handles[handle].Is_Open()) {
+ return(Handles[handle].Write(buf, bytes));
+ }
+ return(0);
+}
+
+int __cdecl Find_File(char const * file_name)
+{
+ CCFileClass file(file_name);
+ return(file.Is_Available());
+}
+
+#ifdef NEVER
+int __cdecl Delete_File(char const * file_name)
+{
+ return(CCFileClass(file_name).Delete());
+}
+
+int __cdecl Create_File(char const * file_name)
+{
+ return(CCFileClass(file_name).Create());
+}
+
+unsigned long __cdecl Load_Data(char const * name, void * ptr, unsigned long size)
+{
+ return(CCFileClass(name).Read(ptr, size));
+}
+#endif
+
+void * __cdecl Load_Alloc_Data(char const * name, int )
+{
+ CCFileClass file(name);
+
+ return(Load_Alloc_Data(file));
+}
+
+unsigned long __cdecl File_Size(int handle)
+{
+ if (handle != WWERROR && Handles[handle].Is_Open()) {
+ return(Handles[handle].Size());
+ }
+ return(0);
+}
+
+#ifdef NEVER
+unsigned long __cdecl Write_Data(char const * name, void const * ptr, unsigned long size)
+{
+ return(CCFileClass(name).Write(ptr, size));
+}
+#endif
+
+unsigned long __cdecl Seek_File(int handle, long offset, int starting)
+{
+ if (handle != WWERROR && Handles[handle].Is_Open()) {
+ return(Handles[handle].Seek(offset, starting));
+ }
+ return(0);
+}
+
+#ifdef NEVER
+bool __cdecl Multi_Drive_Search(bool on)
+{
+// return(CCFileClass::Multi_Drive_Search(on));
+ return(on);
+}
+
+void __cdecl WWDOS_Init(void)
+{
+}
+
+void __cdecl WWDOS_Shutdown(void)
+{
+}
+
+int __cdecl Find_Disk_Number(char const *)
+{
+ return(0);
+}
+#endif
+
+//unsigned long __cdecl Load_Uncompress(char const * file, BuffType uncomp_buff, BuffType dest_buff, void * reserved_data)
+//{
+// return(Load_Uncompress(CCFileClass(file), uncomp_buff, dest_buff, reserved_data));
+// return(CCFileClass(file).Load_Uncompress(uncomp_buff, dest_buff, reserved_data));
+//}
+
+#ifdef WIN32
+extern "C" {
+int MaxDevice;
+int DefaultDrive;
+char CallingDOSInt;
+
+}
+#endif
+
+
+void Unfragment_File_Cache(void)
+{
+}
+
+
diff --git a/CODE/CCFILE.H b/CODE/CCFILE.H
new file mode 100644
index 0000000..2a34322
--- /dev/null
+++ b/CODE/CCFILE.H
@@ -0,0 +1,107 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CCFILE.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CCFILE.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : October 17, 1994 *
+ * *
+ * Last Update : October 17, 1994 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifndef CCFILE_H
+#define CCFILE_H
+
+//#include
+#include
+#include "mixfile.h"
+#include "cdfile.h"
+#include "buff.h"
+
+
+/*
+** This derived class for file access knows about mixfiles (packed files). It can handle opening
+** a file that is embedded within a mixfile. This is true if the mixfile is cached or resides on
+** disk. It is functionally similar to pakfiles, except much faster and less RAM intensive.
+*/
+class CCFileClass : public CDFileClass
+{
+ public:
+ CCFileClass(char const * filename);
+ CCFileClass(void);
+ virtual ~CCFileClass(void) {Position = 0;};
+
+ // Delete should be overloaded here as well. Don't allow deletes of mixfiles.
+
+ bool Is_Resident(void) const {return(Data.Get_Buffer() != NULL);}
+ virtual int Is_Available(int forced=false);
+ virtual int Is_Open(void) const;
+ virtual int Open(char const * filename, int rights=READ) {Set_Name(filename);return Open(rights);};
+ virtual int Open(int rights=READ);
+ virtual long Read(void * buffer, long size);
+ virtual long Seek(long pos, int dir=SEEK_CUR);
+ virtual long Size(void);
+ virtual long Write(void const * buffer, long size);
+ virtual void Close(void);
+ virtual unsigned long Get_Date_Time(void);
+ virtual bool Set_Date_Time(unsigned long datetime);
+ virtual void Error(int error, int canretry = false, char const * filename=NULL);
+
+ private:
+
+ /*
+ ** This indicates the file is actually part of a resident image of the mixfile
+ ** itself. In this case, the embedded file handle is invalid. All file access actually
+ ** gets routed through the cached version of the file. This is a pointer to the start
+ ** of the RAM image of the file.
+ */
+ ::Buffer Data;
+// void * Pointer;
+
+ /*
+ ** This is the size of the file if it was embedded in a mixfile. The size must be manually
+ ** kept track of because the DOS file size is invalid.
+ */
+// long Length;
+
+ /*
+ ** This is the current seek position of the file. It is duplicated here if the file is
+ ** part of a mixfile since the DOS seek position is not accurate. This value will
+ ** range from zero to the size of the file in bytes.
+ */
+ long Position;
+
+ // Force these to never be invoked.
+ CCFileClass const & operator = (CCFileClass const & c);
+ CCFileClass (CCFileClass const & );
+};
+
+class MixFileClass;
+
+#endif
diff --git a/CODE/CCINI.CPP b/CODE/CCINI.CPP
new file mode 100644
index 0000000..ec46756
--- /dev/null
+++ b/CODE/CCINI.CPP
@@ -0,0 +1,1487 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CCINI.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CCINI.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 05/24/96 *
+ * *
+ * Last Update : November 1, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * CCINIClass::Calculate_Message_Digest -- Calculate a message digest for the current databas*
+ * CCINIClass::Get_AnimType -- Fetch an animation type number from the INI database. *
+ * CCINIClass::Get_ArmorType -- Fetches the armor type from the INI database. *
+ * CCINIClass::Get_Buildings -- Fetch a building bitfield from the INI database. *
+ * CCINIClass::Get_BulletType -- Fetch the bullet identifier from the INI database. *
+ * CCINIClass::Get_CrateType -- Fetches a crate type value from the INI database. *
+ * CCINIClass::Get_HousesType -- Fetch a house identifier from the INI database. *
+ * CCINIClass::Get_Lepton -- Fetches a lepton value from the INI database. *
+ * CCINIClass::Get_MPHType -- Fetches the speed value as a number from 0 to 100. *
+ * CCINIClass::Get_OverlayType -- Fetch the overlay identifier from the INI database. *
+ * CCINIClass::Get_Owners -- Fetch the owners (list of house bits). *
+ * CCINIClass::Get_SourceType -- Fetch the source (edge) type from the INI database. *
+ * CCINIClass::Get_TerrainType -- Fetch the terrain type identifier from the INI database. *
+ * CCINIClass::Get_TheaterType -- Fetch the theater type from the INI database. *
+ * CCINIClass::Get_ThemeType -- Fetch the theme identifier. *
+ * CCINIClass::Get_TriggerType -- Fetch the trigger type identifier from the INI database. *
+ * CCINIClass::Get_Unique_ID -- Fetch a unique identifier number for the INI file. *
+ * CCINIClass::Get_VQType -- Fetch the VQ movie identifier from the INI database. *
+ * CCINIClass::Get_VocType -- Fetch a voc (sound effect) from the INI database. *
+ * CCINIClass::Get_WarheadType -- Fetch the warhead type from the INI database. *
+ * CCINIClass::Get_WeaponType -- Fetches the weapon type from the INI database. *
+ * CCINIClass::Invalidate_Message_Digest -- Flag message digest as being invalid. *
+ * CCINIClass::Load -- Load the INI database from the data stream specified. *
+ * CCINIClass::Load -- Load the INI database from the file specified. *
+ * CCINIClass::Put_AnimType -- Stores the animation identifier to the INI database. *
+ * CCINIClass::Put_ArmorType -- Store the armor type to the INI database. *
+ * CCINIClass::Put_Buildings -- Store a building list to the INI database. *
+ * CCINIClass::Put_BulletType -- Store the projectile identifier into the INI database. *
+ * CCINIClass::Put_CrateType -- Stores the crate value in the section and entry specified. *
+ * CCINIClass::Put_HousesType -- Store a house identifier to the INI database. *
+ * CCINIClass::Put_Lepton -- Stores a lepton value to the INI database. *
+ * CCINIClass::Put_MPHType -- Stores the speed value to the section & entry specified. *
+ * CCINIClass::Put_OverlayType -- Store the overlay identifier into the INI database. *
+ * CCINIClass::Put_Owners -- Store the house bitfield to the INI database. *
+ * CCINIClass::Put_SourceType -- Store the source (edge) identifier to the INI database. *
+ * CCINIClass::Put_TerrainType -- Store the terrain type number to the INI database. *
+ * CCINIClass::Put_TheaterType -- Store the theater identifier to the INI database. *
+ * CCINIClass::Put_ThemeType -- Store the theme identifier to the INI database. *
+ * CCINIClass::Put_TriggerType -- Store the trigger identifier to the INI database. *
+ * CCINIClass::Put_VQType -- Store the VQ movie identifier into the INI database. *
+ * CCINIClass::Put_VocType -- Store a sound effect identifier into the INI database. *
+ * CCINIClass::Put_WarheadType -- Stores the warhead identifier to the INI database. *
+ * CCINIClass::Put_WeaponType -- Store the weapon identifier to the INI database. *
+ * CCINIClass::Save -- Pipes the INI database to the pipe specified. *
+ * CCINIClass::Save -- Save the INI data to the file specified. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#include "function.h"
+
+
+/***********************************************************************************************
+ * CCINIClass::Load -- Load the INI database from the file specified. *
+ * *
+ * This routine will load the database from the file specified in much the same manner *
+ * that the INIClass load function works. However, this class will examine the message *
+ * digest (if present) and compare it to the actual digest. If they differ, a special *
+ * return value is used. This will allow verification of the integrity of the ini data. *
+ * *
+ * INPUT: file -- Reference to the file that will be read from. *
+ * *
+ * withdigest -- Should a message digest be examined when loaded. If there is a *
+ * mismatch detected, then an error will be returned. *
+ * *
+ * OUTPUT: If the file was not read, returns 0. If the file was read ok, returns 1. If the *
+ * file was read ok, but the digest doesn't verify, returns 2. *
+ * *
+ * WARNINGS: If no message digest was present in the INI file, then no verification can *
+ * be performed. *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ * 08/21/1996 JLB : Handles digest control. *
+ *=============================================================================================*/
+bool CCINIClass::Load(FileClass & file, bool withdigest)
+{
+ return(Load(FileStraw(file), withdigest));
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Load -- Load the INI database from the data stream specified. *
+ * *
+ * This will load the INI database and in the process, it will fetch and verify any *
+ * message digest present. *
+ * *
+ * INPUT: straw -- The data stream to fetch the INI data from. *
+ * *
+ * withdigest -- Should a message digest be examined when loaded. If there is a *
+ * mismatch detected, then an error will be returned. *
+ * *
+ * OUTPUT: bool; Was the database loaded ok? (hack: returns "2" if digest doesn't match). *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/10/1996 JLB : Created. *
+ * 08/21/1996 JLB : Handles message digest control. *
+ *=============================================================================================*/
+bool CCINIClass::Load(Straw & file, bool withdigest)
+{
+ bool ok = INIClass::Load(file);
+
+ Invalidate_Message_Digest();
+ if (ok && withdigest) {
+
+ /*
+ ** If a digest is present, fetch it.
+ */
+ unsigned char digest[20];
+ int len = Get_UUBlock("Digest", digest, sizeof(digest));
+ if (len > 0) {
+ Clear("Digest");
+
+ /*
+ ** Calculate the message digest for the INI data that was read.
+ */
+ Calculate_Message_Digest();
+
+ /*
+ ** If the message digests don't match, then return with the special error code.
+ */
+ if (memcmp(digest, Digest, sizeof(digest)) != 0) {
+ return(2);
+ }
+ }
+ }
+ return(ok);
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Save -- Save the INI data to the file specified. *
+ * *
+ * This routine will save the INI data to the file. It will add a message digest so that *
+ * validity check can be performed when the INI data is subsequently read. *
+ * *
+ * INPUT: file -- Reference to the file to write the INI data to. *
+ * *
+ * withdigest -- Should a message digest be generated and saved with the INI *
+ * data file? *
+ * *
+ * OUTPUT: bool; Was the INI data saved? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ * 08/21/1996 JLB : Handles message digest control. *
+ *=============================================================================================*/
+int CCINIClass::Save(FileClass & file, bool withdigest) const
+{
+ return(Save(FilePipe(file), withdigest));
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Save -- Pipes the INI database to the pipe specified. *
+ * *
+ * This routine will pipe the INI data to the pipe segment specified. It is functionally *
+ * the same as the save operation. A message digest is added to the output data so that *
+ * validity check can occur during a subsequent read. *
+ * *
+ * INPUT: straw -- Reference to the pipe that will receive the output ini data stream. *
+ * *
+ * withdigest -- Should a message digest be generated and saved with the INI *
+ * data file? *
+ * *
+ * OUTPUT: Returns with the number of bytes output to the pipe. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ * 08/21/1996 JLB : Handles message digest control. *
+ *=============================================================================================*/
+int CCINIClass::Save(Pipe & pipe, bool withdigest) const
+{
+ if (!withdigest) {
+ return(INIClass::Save(pipe));
+ }
+
+ /*
+ ** Just in case these entries are present, clear them out.
+ */
+ ((CCINIClass *)this)->Clear("Digest");
+
+ /*
+ ** Calculate what the new digest should be.
+ */
+ ((CCINIClass *)this)->Calculate_Message_Digest();
+
+ /*
+ ** Store the actual digest into the INI database.
+ */
+ ((CCINIClass *)this)->Put_UUBlock("Digest", Digest, sizeof(Digest));
+
+ /*
+ ** Output the database to the pipe specified.
+ */
+ int length = INIClass::Save(pipe);
+
+ /*
+ ** Remove the digest from the database. It shouldn't stick around as if it were real data
+ ** since it isn't really part of the INI database proper.
+ */
+ ((CCINIClass *)this)->Clear("Digest");
+
+ /*
+ ** Finally, return with the total number of bytes send out the pipe.
+ */
+ return(length);
+}
+
+
+static inline int _Scale_To_256(int val)
+{
+ val = min(val, 100);
+ val = max(val, 0);
+ val = ((val * 256) / 100);
+ val = min(val, 255);
+ return(val);
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Get_Lepton -- Fetches a lepton value from the INI database. *
+ * *
+ * This routine will fetch the lepton value as if it were expressed as cells. Example; *
+ * a value of 1 would mean 256 in leptons. *
+ * *
+ * INPUT: section -- The section identifier to look under. *
+ * *
+ * entry -- The entry identifier to find. *
+ * *
+ * defvalue -- The default value to use if the specified section and entry could *
+ * not be located. *
+ * *
+ * OUTPUT: Returns with the lepton value of the section & entry specified. If not found, then *
+ * the default value is returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+LEPTON CCINIClass::Get_Lepton(char const * section, char const * entry, LEPTON defvalue) const
+{
+ fixed result = Get_Fixed(section, entry, fixed(defvalue, CELL_LEPTON_W));
+ return(result * CELL_LEPTON_W);
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Put_Lepton -- Stores a lepton value to the INI database. *
+ * *
+ * This routine will store the lepton value as if it were expressed in cells. Example; *
+ * A lepton of 128 will be stored as ".5". *
+ * *
+ * INPUT: section -- The section identifier to store the value under. *
+ * *
+ * entry -- The entry to store the lepton value at. *
+ * *
+ * value -- The lepton value to store. *
+ * *
+ * OUTPUT: bool; Was the lepton value stored? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+bool CCINIClass::Put_Lepton(char const * section, char const * entry, LEPTON value)
+{
+ return(Put_Fixed(section, entry, fixed(value, CELL_LEPTON_W)));
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Get_MPHType -- Fetches the speed value as a number from 0 to 100. *
+ * *
+ * This routine will fetch the speed value as if it were expressed as a number from 0 *
+ * to 100. The value of 100 would translate into a speed of 256 leptons per game frame. *
+ * *
+ * INPUT: section -- The section identifier to search for the entry under. *
+ * *
+ * entry -- The entry identifier to find. *
+ * *
+ * defvalue -- The default speed value to use if the entry could not be located. *
+ * *
+ * OUTPUT: Returns with the speed value. If no entry could be found, then the default value *
+ * will be returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+MPHType CCINIClass::Get_MPHType(char const * section, char const * entry, MPHType defvalue) const
+{
+ int val = Get_Int(section, entry, ((int)defvalue * 100) / 256);
+ return (MPHType(_Scale_To_256(val)));
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Put_MPHType -- Stores the speed value to the section & entry specified. *
+ * *
+ * Use this routine to store a speed value into the INI database. The number stored will *
+ * be in a 0..100 format. A speed of 256 leptons per tick would be stored as 100. *
+ * *
+ * INPUT: section -- The section identifier to store the entry under. *
+ * *
+ * entry -- The entry identifier to store the speed value to. *
+ * *
+ * value -- The speed value to store. *
+ * *
+ * OUTPUT: bool; Was the speed value stored? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+bool CCINIClass::Put_MPHType(char const * section, char const * entry, MPHType value)
+{
+ return(Put_Int(section, entry, ((int)value * 100) / 256));
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Get_Owners -- Fetch the owners (list of house bits). *
+ * *
+ * Use this to fetch a house bit array value from the INI database. This value will be *
+ * various bit positions set (1 << house#) for each house specified in the database. *
+ * Houses can be specified in series by the house name separated by commas or by the *
+ * special group names of "soviet", and "allies" to cover the houses that are members of *
+ * these groups. *
+ * *
+ * INPUT: section -- The section identifier to search for the entry under. *
+ * *
+ * entry -- The entry identifier to search for. *
+ * *
+ * defvalue -- The default house bitfield to use if the entry could not be found. *
+ * *
+ * OUTPUT: Returns with the house bitfield value. If the entry could not be found, then the *
+ * default value is returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+long CCINIClass::Get_Owners(char const * section, char const * entry, long defvalue) const
+{
+ char buffer[128];
+ long ownable = defvalue;
+
+ if (Get_String(section, entry, "", buffer, sizeof(buffer))) {
+
+ ownable = 0;
+ char * name = strtok(buffer, ",");
+
+ while (name) {
+ ownable |= Owner_From_Name(name);
+ name = strtok(NULL, ",");
+ }
+ }
+ return(ownable);
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Put_Owners -- Store the house bitfield to the INI database. *
+ * *
+ * Use this routine to store the house bitfield data into the database. The bitfield format *
+ * matches the format used by the Get_Owners function. Example; if both England and *
+ * Spain were specified in the bitfield, the entry would be stored as "England,Spain". *
+ * *
+ * INPUT: section -- The section identifier to store the entry under. *
+ * *
+ * entry -- The entry identifier that is assigned the value. *
+ * *
+ * value -- The value to assign to the entry. *
+ * *
+ * OUTPUT: bool; Was the entry stored in the INI database? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+bool CCINIClass::Put_Owners(char const * section, char const * entry, long value)
+{
+ char buffer[128];
+
+ buffer[0] = '\0';
+
+ if ((value & HOUSEF_ALLIES) == HOUSEF_ALLIES) {
+ strcat(buffer, "allies");
+ value &= ~HOUSEF_ALLIES;
+ }
+ if ((value & HOUSEF_SOVIET) == HOUSEF_SOVIET) {
+ if (buffer[0] != '\0') {
+ strcat(buffer, ",");
+ }
+ strcat(buffer, "soviet");
+ value &= ~HOUSEF_SOVIET;
+ }
+
+ for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) {
+ if ((value & (1 << house)) != 0) {
+ if (buffer[0] != '\0') {
+ strcat(buffer, ",");
+ }
+ strcat(buffer, HouseTypeClass::As_Reference(house).Name());
+ }
+ }
+
+ if (buffer[0] != '\0') {
+ return(Put_String(section, entry, buffer));
+ }
+ return(true);
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Get_ArmorType -- Fetches the armor type from the INI database. *
+ * *
+ * This routine will fetch the armor type from the database. *
+ * *
+ * INPUT: section -- Identifier for the section to search for the entry under. *
+ * *
+ * entry -- Th identifier for the entry to search for. *
+ * *
+ * defvalue -- The default value to use if the entry could not be located. *
+ * *
+ * OUTPUT: Returns with the armor type specified in the INI database. If it could not be *
+ * found, then the default value is returned instead. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+ArmorType CCINIClass::Get_ArmorType(char const * section, char const * entry, ArmorType defvalue) const
+{
+ char buffer[128];
+
+ Get_String(section, entry, ArmorName[defvalue], buffer, sizeof(buffer));
+ return(Armor_From_Name(buffer));
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Put_ArmorType -- Store the armor type to the INI database. *
+ * *
+ * Use this routine to store the specified armor type to the INI database. *
+ * *
+ * INPUT: section -- The section identifier to store the entry under. *
+ * *
+ * entry -- The entry to store the value at. *
+ * *
+ * value -- The value to store in the database. *
+ * *
+ * OUTPUT: bool; Was the entry stored in the database? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+bool CCINIClass::Put_ArmorType(char const * section, char const * entry, ArmorType value)
+{
+ return(Put_String(section, entry, ArmorName[value]));
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Get_VocType -- Fetch a voc (sound effect) from the INI database. *
+ * *
+ * This routine will fetch a voc number from the database. The voc number will either *
+ * be a valid sound effect or VOC_NONE if no match could be found. *
+ * *
+ * INPUT: section -- Identifier for the section to search for the entry under. *
+ * *
+ * entry -- The entry to search for. *
+ * *
+ * defvalue -- The default value to return if the entry could not be located. *
+ * *
+ * OUTPUT: Returns with the sound effect (VocType) from the INI database. If the entry could *
+ * not be located, then the default value is returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+VocType CCINIClass::Get_VocType(char const * section, char const * entry, VocType defvalue) const
+{
+ char buffer[128];
+
+ Get_String(section, entry, Voc_Name(defvalue), buffer, sizeof(buffer));
+ return(Voc_From_Name(buffer));
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Put_VocType -- Store a sound effect identifier into the INI database. *
+ * *
+ * Use this routine to store a voc identifier (stored a the text name of the sound) into *
+ * the INI database. *
+ * *
+ * INPUT: section -- Identifier for the section to store the entry under. *
+ * *
+ * entry -- The entry to assign the value to. *
+ * *
+ * value -- The sound effect to store to the entry. *
+ * *
+ * OUTPUT: bool; Was the sound effect entry stored? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+bool CCINIClass::Put_VocType(char const * section, char const * entry, VocType value)
+{
+ if (value == VOC_NONE) {
+ return(Put_String(section, entry, ""));
+ }
+ return(Put_String(section, entry, Voc_Name(value)));
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Get_AnimType -- Fetch an animation type number from the INI database. *
+ * *
+ * This will fetch an AnimType number from the INI database. The anim is stored as a text *
+ * name of the art file used for that anim. *
+ * *
+ * INPUT: section -- The section to search for the entry under. *
+ * *
+ * entry -- The entry to search for. *
+ * *
+ * defvalue -- The default AnimType to use if the entry could not be located. *
+ * *
+ * OUTPUT: Returns with the anim type specified in the database. If it could not be found, *
+ * then the default value is returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+AnimType CCINIClass::Get_AnimType(char const * section, char const * entry, AnimType defvalue) const
+{
+ char buffer[128];
+
+ Get_String(section, entry, Anim_Name(defvalue), buffer, sizeof(buffer));
+ return(Anim_From_Name(buffer));
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Put_AnimType -- Stores the animation identifier to the INI database. *
+ * *
+ * This routine will store the animation identifier (stored as the text name of the art *
+ * file it uses) to the INI database. *
+ * *
+ * INPUT: section -- The section identifier to place the entry under. *
+ * *
+ * entry -- The entry identifier to assign the animation number to. *
+ * *
+ * value -- The animation identifier to store with the entry. *
+ * *
+ * OUTPUT: bool; Was the animation identifier stored? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+bool CCINIClass::Put_AnimType(char const * section, char const * entry, AnimType value)
+{
+ if (value == ANIM_NONE) {
+ return(Put_String(section, entry, ""));
+ }
+ return(Put_String(section, entry, Anim_Name(value)));
+}
+
+
+UnitType CCINIClass::Get_UnitType(char const * section, char const * entry, UnitType defvalue) const
+{
+ char buffer[128];
+
+ char const * def = "";
+ if (defvalue != UNIT_NONE) {
+ def = UnitTypeClass::As_Reference(defvalue).Name();
+ }
+ Get_String(section, entry, def, buffer, sizeof(buffer));
+ return(UnitTypeClass::From_Name(buffer));
+}
+
+
+bool CCINIClass::Put_UnitType(char const * section, char const * entry, UnitType value)
+{
+ if (value == UNIT_NONE) {
+ return(Put_String(section, entry, ""));
+ }
+ return(Put_String(section, entry, UnitTypeClass::As_Reference(value).Name()));
+}
+
+
+
+/***********************************************************************************************
+ * CCINIClass::Get_WeaponType -- Fetches the weapon type from the INI database. *
+ * *
+ * This routine will fetch the weapon type from the INI database. The weapon type is *
+ * stored as a custom identifier string. *
+ * *
+ * INPUT: section -- The section identifier to search for the entry under. *
+ * *
+ * entry -- The entry identifier to search for. *
+ * *
+ * defvalue -- The default weapon value to return if the entry could not be located. *
+ * *
+ * OUTPUT: Returns with the weapon type specified by the entry. If the entry could not be *
+ * found then the default value is returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+WeaponType CCINIClass::Get_WeaponType(char const * section, char const * entry, WeaponType defvalue) const
+{
+ char buffer[128];
+
+ if (Get_String(section, entry, "", buffer, sizeof(buffer))) {
+ return(Weapon_From_Name(buffer));
+ }
+ return(defvalue);
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Put_WeaponType -- Store the weapon identifier to the INI database. *
+ * *
+ * Store the weapon identifier (as custom string name) to the INI database. *
+ * *
+ * INPUT: section -- Identifier for the section to store the entry under. *
+ * *
+ * entry -- Identifier to store the weapon identifier with. *
+ * *
+ * value -- The weapon identifier to store. *
+ * *
+ * OUTPUT: bool; Was the weapon identifier stored? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+bool CCINIClass::Put_WeaponType(char const * section, char const * entry, WeaponType value)
+{
+ if (value == WEAPON_NONE) {
+ return(Put_String(section, entry, ""));
+ }
+ return(Put_String(section, entry, WeaponTypeClass::As_Pointer(value)->Name()));
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Get_WarheadType -- Fetch the warhead type from the INI database. *
+ * *
+ * Will fetch the warhead identifier from the INI database. *
+ * *
+ * INPUT: section -- The identifier for the section to search for the entry under. *
+ * *
+ * entry -- The entry to search for. *
+ * *
+ * defvalue -- The default return value to use if the entry could not be located. *
+ * *
+ * OUTPUT: Returns with the found warhead type. If the entry could not be found then the *
+ * default warhead value is returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+WarheadType CCINIClass::Get_WarheadType(char const * section, char const * entry, WarheadType defvalue) const
+{
+ char buffer[128];
+
+ if (Get_String(section, entry, "", buffer, sizeof(buffer))) {
+ for (WarheadType wh = WARHEAD_FIRST; wh < WARHEAD_COUNT; wh++) {
+ if (stricmp(WarheadTypeClass::As_Pointer(wh)->Name(), buffer) == 0) {
+ return(wh);
+ }
+ }
+ }
+ return(defvalue);
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Put_WarheadType -- Stores the warhead identifier to the INI database. *
+ * *
+ * This will store the weapon identifier specified to the INI database. *
+ * *
+ * INPUT: section -- The section identifier to store the entry under. *
+ * *
+ * entry -- The entry to store the warhead identifier. *
+ * *
+ * value -- The warhead identifier to store. *
+ * *
+ * OUTPUT: bool; Was the warhead identifier stored to the database? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+bool CCINIClass::Put_WarheadType(char const * section, char const * entry, WarheadType value)
+{
+ if (value == WARHEAD_NONE) {
+ return(Put_String(section, entry, ""));
+ }
+ return(Put_String(section, entry, WarheadTypeClass::As_Pointer(value)->Name()));
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Get_OverlayType -- Fetch the overlay identifier from the INI database. *
+ * *
+ * This routine will fetch the overlay identifier from the database. *
+ * *
+ * INPUT: section -- Identifier for the section to search for the entry under. *
+ * *
+ * entry -- The entry to search for. *
+ * *
+ * defvalue -- The default value to use if the entry could not be located. *
+ * *
+ * OUTPUT: Returns with the overlay identifier found. If it could not be found, then the *
+ * default value is returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+OverlayType CCINIClass::Get_OverlayType(char const * section, char const * entry, OverlayType defvalue) const
+{
+ char buffer[128];
+
+ if (Get_String(section, entry, "", buffer, sizeof(buffer))) {
+ return(OverlayTypeClass::From_Name(buffer));
+ }
+ return(defvalue);
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Put_OverlayType -- Store the overlay identifier into the INI database. *
+ * *
+ * Use this routine to store the overlay identifier into the INI database. *
+ * *
+ * INPUT: section -- Identifier for to search for the entry under. *
+ * *
+ * entry -- The entry to search for. *
+ * *
+ * value -- The overlay type value to store with the entry. *
+ * *
+ * OUTPUT: bool; Was the overlay value stored? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+bool CCINIClass::Put_OverlayType(char const * section, char const * entry, OverlayType value)
+{
+ assert(value != OVERLAY_NONE);
+ return(Put_String(section, entry, OverlayTypeClass::As_Reference(value).Name()));
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Get_BulletType -- Fetch the bullet identifier from the INI database. *
+ * *
+ * Use this routine to fetch the bullet type identifier from the INI database. *
+ * *
+ * INPUT: section -- The section identifier to search for the entry under. *
+ * *
+ * entry -- The entry to search for. *
+ * *
+ * defvalue -- The default bullet type value to return if the entry could not be *
+ * located. *
+ * *
+ * OUTPUT: Returns with the bullet type identifier found. If the entry could not be found *
+ * then the default value is returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+BulletType CCINIClass::Get_BulletType(char const * section, char const * entry, BulletType defvalue) const
+{
+ char buffer[128];
+
+ if (Get_String(section, entry, "", buffer, sizeof(buffer))) {
+ for (BulletType proj = BULLET_FIRST; proj < BULLET_COUNT; proj++) {
+ if (stricmp(BulletTypeClass::As_Reference(proj).Name(), buffer) == 0) {
+// if (stricmp(ProjectileNames[proj], buffer) == 0) {
+ return(proj);
+ }
+ }
+ }
+ return(defvalue);
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Put_BulletType -- Store the projectile identifier into the INI database. *
+ * *
+ * This routine will store the projectile name (as the identifier) to the INI database. *
+ * *
+ * INPUT: section -- The section to store the entry under. *
+ * *
+ * entry -- The entry identifier to store the projectile value with. *
+ * *
+ * value -- The projectile identifier to store. *
+ * *
+ * OUTPUT: bool; Was the projectile identifier stored? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+bool CCINIClass::Put_BulletType(char const * section, char const * entry, BulletType value)
+{
+ if (value == BULLET_NONE) {
+ return(Put_String(section, entry, ""));
+ }
+ return(Put_String(section, entry, BulletTypeClass::As_Reference(value).Name()));
+// return(Put_String(section, entry, ProjectileNames[value]));
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Get_HousesType -- Fetch a house identifier from the INI database. *
+ * *
+ * Use this routine to fetch an individual house identifier from the INI database. This is *
+ * somewhat similar to the Get_Owners function but is limited to a single house. *
+ * *
+ * INPUT: section -- Identifier for the section to search for the entry under. *
+ * *
+ * entry -- Identifier for the entry to search for. *
+ * *
+ * defvalue -- The default value to use if the entry could not be located. *
+ * *
+ * OUTPUT: Returns with the house identifier if it was found. If not found, then the default *
+ * value is returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+HousesType CCINIClass::Get_HousesType(char const * section, char const * entry, HousesType defvalue) const
+{
+ char buffer[128];
+
+ if (Get_String(section, entry, "", buffer, sizeof(buffer))) {
+ return(HouseTypeClass::From_Name(buffer));
+ }
+ return(defvalue);
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Put_HousesType -- Store a house identifier to the INI database. *
+ * *
+ * Use this routine to store the specified house identifier to the INI database. *
+ * *
+ * INPUT: section -- The section to store the entry under. *
+ * *
+ * entry -- Identifier for the entry to search for. *
+ * *
+ * value -- The house identifier to store in the database. *
+ * *
+ * OUTPUT: bool; Was the house identifier stored? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+bool CCINIClass::Put_HousesType(char const * section, char const * entry, HousesType value)
+{
+ return(Put_String(section, entry, HouseTypeClass::As_Reference(value).Name()));
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Get_VQType -- Fetch the VQ movie identifier from the INI database. *
+ * *
+ * Fetches the VQ movie name (identifier) from the INI database. *
+ * *
+ * INPUT: section -- Identifier for the section to search for the entry under. *
+ * *
+ * entry -- Identifier for the entry to search for. *
+ * *
+ * defvalue -- The default value to use if the entry could not be located. *
+ * *
+ * OUTPUT: Returns with the VQ movie identifier found. If the entry could not be located, *
+ * then the default value is returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+VQType CCINIClass::Get_VQType(char const * section, char const * entry, VQType defvalue) const
+{
+ char buffer[128];
+
+ if (Get_String(section, entry, "", buffer, sizeof(buffer))) {
+ for (VQType vq = VQ_FIRST; vq < VQ_COUNT; vq++) {
+ if (stricmp(buffer, VQName[vq]) == 0) {
+ return(vq);
+ }
+ }
+ }
+ return(defvalue);
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Put_VQType -- Store the VQ movie identifier into the INI database. *
+ * *
+ * Use this routine to store the VQ movie identifier into the INI database. *
+ * *
+ * INPUT: section -- The section to store the entry under. *
+ * *
+ * entry -- Identifier for the entry to store. *
+ * *
+ * value -- The VQ movie identifier to store to the INI database. *
+ * *
+ * OUTPUT: bool; Was the VQ identifier stored? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+bool CCINIClass::Put_VQType(char const * section, char const * entry, VQType value)
+{
+ if (value == VQ_NONE) {
+ return(Put_String(section, entry, ""));
+ }
+ return(Put_String(section, entry, VQName[value]));
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Get_TheaterType -- Fetch the theater type from the INI database. *
+ * *
+ * This will fetch the theater identifier from the INI database. *
+ * *
+ * INPUT: section -- Identifier for the section to search for the entry under. *
+ * *
+ * entry -- Identifier for the entry to search for. *
+ * *
+ * defvalue -- The default value to use if the entry could not be located. *
+ * *
+ * OUTPUT: Returns with the theater type found. If the entry could not be found, then the *
+ * default value is returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+TheaterType CCINIClass::Get_TheaterType(char const * section, char const * entry, TheaterType defvalue) const
+{
+ char buffer[128];
+
+ if (Get_String(section, entry, "", buffer, sizeof(buffer))) {
+ return(Theater_From_Name(buffer));
+ }
+ return(defvalue);
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Put_TheaterType -- Store the theater identifier to the INI database. *
+ * *
+ * Use this routine to store the theater name to the INI database. *
+ * *
+ * INPUT: section -- The section to store the entry under. *
+ * *
+ * entry -- Identifier for the entry to store. *
+ * *
+ * value -- The theater identifier to store. *
+ * *
+ * OUTPUT: bool; Was the theater identifier stored? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+bool CCINIClass::Put_TheaterType(char const * section, char const * entry, TheaterType value)
+{
+ return(Put_String(section, entry, Theaters[value].Name));
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Get_TriggerType -- Fetch the trigger type identifier from the INI database. *
+ * *
+ * This routine will fetch the trigger type identifier from the INI database. *
+ * *
+ * INPUT: section -- The section to search for the entry under. *
+ * *
+ * entry -- Identifier of the entry to search for. *
+ * *
+ * OUTPUT: Returns with the trigger type pointer if a match was found. No match found will *
+ * return a NULL. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+TriggerTypeClass * CCINIClass::Get_TriggerType(char const * section, char const * entry) const
+{
+ char buffer[128];
+
+ if (Get_String(section, entry, "", buffer, sizeof(buffer))) {
+ return(TriggerTypeClass::From_Name(buffer));
+ }
+ return(NULL);
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Put_TriggerType -- Store the trigger identifier to the INI database. *
+ * *
+ * This routine will store the trigger (as its name) to the INI database. *
+ * *
+ * INPUT: section -- The section to store the entry under. *
+ * *
+ * entry -- The entry name to store the trigger identifier to. *
+ * *
+ * value -- The trigger type to store. The trigger name will be stored. *
+ * *
+ * OUTPUT: bool; Was the trigger name stored? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+bool CCINIClass::Put_TriggerType(char const * section, char const * entry, TriggerTypeClass * value)
+{
+ return(Put_String(section, entry, value->Name()));
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Get_ThemeType -- Fetch the theme identifier. *
+ * *
+ * This routine will fetch the theme identifier from the INI database. *
+ * *
+ * INPUT: section -- The section to search for the entry under. *
+ * *
+ * entry -- Identifier of the entry to search for. *
+ * *
+ * defvalue -- The default theme identifier to return if the entry could not be found.*
+ * *
+ * OUTPUT: Returns with the theme identifier if it was found. If not found, then the default *
+ * value is returned instead. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+ThemeType CCINIClass::Get_ThemeType(char const * section, char const * entry, ThemeType defvalue) const
+{
+ char buffer[128];
+
+ if (Get_String(section, entry, "", buffer, sizeof(buffer))) {
+ return(Theme.From_Name(buffer));
+ }
+ return(defvalue);
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Put_ThemeType -- Store the theme identifier to the INI database. *
+ * *
+ * This routine will store the specified theme identifier to the INI database. *
+ * *
+ * INPUT: section -- Identifier for the section to store the entry under. *
+ * *
+ * entry -- Identifier for the entry to store the value to. *
+ * *
+ * value -- The theme identifier to store. *
+ * *
+ * OUTPUT: bool; Was the theme identifier stored. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+bool CCINIClass::Put_ThemeType(char const * section, char const * entry, ThemeType value)
+{
+ return(Put_String(section, entry, Theme.Base_Name(value)));
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Get_SourceType -- Fetch the source (edge) type from the INI database. *
+ * *
+ * This routine will fetch the source (reinforcement edge) identifier from the INI *
+ * database. *
+ * *
+ * INPUT: section -- Identifier for the section that the entry will be searched under. *
+ * *
+ * entry -- Identifier for the entry that will be searched for. *
+ * *
+ * defvalue -- The default value to return if the entry could not be located. *
+ * *
+ * OUTPUT: Returns with the source type of the entry if found. If not found, then the *
+ * default value is returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+SourceType CCINIClass::Get_SourceType(char const * section, char const * entry, SourceType defvalue) const
+{
+ char buffer[128];
+
+ if (Get_String(section, entry, "", buffer, sizeof(buffer))) {
+ return(Source_From_Name(buffer));
+ }
+ return(defvalue);
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Put_SourceType -- Store the source (edge) identifier to the INI database. *
+ * *
+ * This will store the source type (reinforcement edge) to the INI database. *
+ * *
+ * INPUT: section -- The section to store the entry under. *
+ * *
+ * entry -- Identifier of the entry to store the source identifier to. *
+ * *
+ * value -- The source (edge) value to store. *
+ * *
+ * OUTPUT: bool; Was the source identifier stored? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+bool CCINIClass::Put_SourceType(char const * section, char const * entry, SourceType value)
+{
+ return(Put_String(section, entry, SourceName[value]));
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Get_CrateType -- Fetches a crate type value from the INI database. *
+ * *
+ * This will return with the crate type specified in the INI database. *
+ * *
+ * INPUT: section -- Identifier for the section to search under. *
+ * *
+ * entry -- The entry to find the matching crate value for. *
+ * *
+ * defvalue -- The default crate value to return if the entry could not be found. *
+ * *
+ * OUTPUT: Returns with the crate type identified with the specified entry. If the entry *
+ * could not be located, then the default value is returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/08/1996 JLB : Created. *
+ *=============================================================================================*/
+CrateType CCINIClass::Get_CrateType(char const * section, char const * entry, CrateType defvalue) const
+{
+ char buffer[128];
+
+ if (Get_String(section, entry, "", buffer, sizeof(buffer))) {
+ return(Crate_From_Name(buffer));
+ }
+ return(defvalue);
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Put_CrateType -- Stores the crate value in the section and entry specified. *
+ * *
+ * This will store the specified crate value to the section and entry specified. *
+ * *
+ * INPUT: section -- The section identifier to store the entry under. *
+ * *
+ * entry -- The entry identifier to store the crate value with. *
+ * *
+ * value -- The crate value to store. *
+ * *
+ * OUTPUT: bool; Was the crate value stored to the INI database? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/08/1996 JLB : Created. *
+ *=============================================================================================*/
+bool CCINIClass::Put_CrateType(char const * section, char const * entry, CrateType value)
+{
+ return(Put_String(section, entry, CrateNames[value]));
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Get_TerrainType -- Fetch the terrain type identifier from the INI database. *
+ * *
+ * Fetches the terrain type number from the INI database. *
+ * *
+ * INPUT: section -- The section to search for the entry under. *
+ * *
+ * entry -- Identifier for the entry to search for. *
+ * *
+ * defvalue -- The default value to use if the entry could not be located. *
+ * *
+ * OUTPUT: Returns with the terrain type if found. If the entry wasn't found, then the *
+ * default value will be returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+TerrainType CCINIClass::Get_TerrainType(char const * section, char const * entry, TerrainType defvalue) const
+{
+ char buffer[128];
+
+ if (Get_String(section, entry, "", buffer, sizeof(buffer))) {
+ return(TerrainTypeClass::From_Name(strtok(buffer, ",")));
+ }
+ return(defvalue);
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Put_TerrainType -- Store the terrain type number to the INI database. *
+ * *
+ * This will store the terrain type identifier to the INI database. *
+ * *
+ * INPUT: section -- The section to store the entry under. *
+ * *
+ * entry -- Identifier that the terrain number will be stored to. *
+ * *
+ * value -- The terrain type identifier to store. *
+ * *
+ * OUTPUT: bool; Was the terrain identifier stored? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+bool CCINIClass::Put_TerrainType(char const * section, char const * entry, TerrainType value)
+{
+ return(Put_String(section, entry, TerrainTypeClass::As_Reference(value).Name()));
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Get_Buildings -- Fetch a building bitfield from the INI database. *
+ * *
+ * This routing will fetch the a list of buildings from the INI database. The buildings *
+ * are expressed as a comma separated list of building identifiers. The return value is *
+ * a composite of bits that represent these buildings -- one bit per building type. *
+ * *
+ * INPUT: section -- The section to search for the entry under. *
+ * *
+ * entry -- The entry to fetch the building list from. *
+ * *
+ * defvalue -- The default value to return if the section and entry could not be *
+ * located. *
+ * *
+ * OUTPUT: Returns with the building list (as a bitfield). If the entry could not be *
+ * found, the the default value is returned instead. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/11/1996 JLB : Created. *
+ *=============================================================================================*/
+long CCINIClass::Get_Buildings(char const * section, char const * entry, long defvalue) const
+{
+ char buffer[128];
+ long pre;
+
+ if (Get_String(section, entry, "", buffer, sizeof(buffer))) {
+
+ pre = 0;
+ char * token = strtok(buffer, ",");
+ while (token != NULL && *token != '\0') {
+ StructType building = BuildingTypeClass::From_Name(token);
+ if (building != STRUCT_NONE) {
+ pre |= (1L << building);
+ }
+ token = strtok(NULL, ",");
+ }
+ } else {
+ pre = defvalue;
+ }
+
+ return(pre);
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Put_Buildings -- Store a building list to the INI database. *
+ * *
+ * This will store a list of buildings to the INI database. The buildings are listed by *
+ * their identifier names separated by commas. *
+ * *
+ * INPUT: section -- The identifier for the section to store the entry under. *
+ * *
+ * entry -- The entry to store the building list to. *
+ * *
+ * value -- A list of buildings (in the form of a bit field -- one bit per *
+ * building type). *
+ * *
+ * OUTPUT: Was the building list stored to the INI file? *
+ * *
+ * WARNINGS: This is limited to the buildings that can be expressed in a bitfield long. *
+ * Which means, there can be only a maximum of 32 building types listed and *
+ * even then, the total line length generated must not exceed 128 bytes. *
+ * *
+ * HISTORY: *
+ * 07/11/1996 JLB : Created. *
+ *=============================================================================================*/
+bool CCINIClass::Put_Buildings(char const * section, char const * entry, long value)
+{
+ char buffer[128] = "";
+ int maxi = (32 < STRUCT_COUNT) ? 32 : STRUCT_COUNT;
+
+ for (StructType index = STRUCT_FIRST; index < maxi; index++) {
+ if ((value & (1L << index)) != 0) {
+
+ if (buffer[0] != '\0') {
+ strcat(buffer, ",");
+ }
+ strcat(buffer, BuildingTypeClass::As_Reference(index).IniName);
+ }
+ }
+
+ return(Put_String(section, entry, buffer));
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Get_Unique_ID -- Fetch a unique identifier number for the INI file. *
+ * *
+ * This is a shorthand version of the message digest. It calculates the ID number from the *
+ * message digest itself. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with a 32 bit unique identifier number for the INI database. *
+ * *
+ * WARNINGS: Since the return value is only 32 bits, it is much less secure than the *
+ * complete message digest. *
+ * *
+ * HISTORY: *
+ * 11/01/1996 JLB : Created. *
+ *=============================================================================================*/
+int CCINIClass::Get_Unique_ID(void) const
+{
+ if (!IsDigestPresent) {
+ ((CCINIClass *)this)->Calculate_Message_Digest();
+ }
+
+ return(CRCEngine()(&Digest[0], sizeof(Digest)));
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Calculate_Message_Digest -- Calculate a message digest for the current database *
+ * *
+ * This will calculate a new message digest according to the current state of the INI *
+ * database. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: If the database is changed in any fashion, this message digest will be rendered *
+ * obsolete. *
+ * *
+ * HISTORY: *
+ * 11/01/1996 JLB : Created. *
+ *=============================================================================================*/
+void CCINIClass::Calculate_Message_Digest(void)
+{
+ /*
+ ** Calculate the message digest for the INI data that was read.
+ */
+ SHAPipe sha;
+ INIClass::Save(sha);
+ sha.Result(Digest);
+ IsDigestPresent = true;
+}
+
+
+/***********************************************************************************************
+ * CCINIClass::Invalidate_Message_Digest -- Flag message digest as being invalid. *
+ * *
+ * This flags the message digest as being invalid so that it will be recalculated when *
+ * needed. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 11/01/1996 JLB : Created. *
+ *=============================================================================================*/
+void CCINIClass::Invalidate_Message_Digest(void)
+{
+ IsDigestPresent = false;
+}
diff --git a/CODE/CCINI.H b/CODE/CCINI.H
new file mode 100644
index 0000000..17fbdbe
--- /dev/null
+++ b/CODE/CCINI.H
@@ -0,0 +1,120 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CCINI.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CCINI.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 05/24/96 *
+ * *
+ * Last Update : May 24, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifndef CCINI_H
+#define CCINI_H
+
+#include "ini.h"
+#include "fixed.h"
+#include "pk.h"
+
+class TriggerTypeClass;
+
+/*
+** The advanced version of the INI database manager. It handles the C&C expansion types and
+** identifiers. In addition, it automatically stores a message digest with the INI data
+** so that verification can occur.
+*/
+class CCINIClass : public INIClass
+{
+ public:
+ CCINIClass(void) : IsDigestPresent(false) {}
+
+ bool Load(FileClass & file, bool withdigest);
+ bool Load(Straw & file, bool withdigest);
+ int Save(FileClass & file, bool withdigest) const;
+ int Save(Pipe & pipe, bool withdigest) const;
+
+ long Get_Buildings(char const * section, char const * entry, long defvalue) const;
+ UnitType Get_UnitType(char const * section, char const * entry, UnitType defvalue) const;
+ AnimType Get_AnimType(char const * section, char const * entry, AnimType defvalue) const;
+ ArmorType Get_ArmorType(char const * section, char const * entry, ArmorType defvalue) const;
+ BulletType Get_BulletType(char const * section, char const * entry, BulletType defvalue) const;
+ HousesType Get_HousesType(char const * section, char const * entry, HousesType defvalue) const;
+ LEPTON Get_Lepton(char const * section, char const * entry, LEPTON defvalue) const;
+ MPHType Get_MPHType(char const * section, char const * entry, MPHType defvalue) const;
+ OverlayType Get_OverlayType(char const * section, char const * entry, OverlayType defvalue) const;
+ SourceType Get_SourceType(char const * section, char const * entry, SourceType defvalue) const;
+ TerrainType Get_TerrainType(char const * section, char const * entry, TerrainType defvalue) const;
+ TheaterType Get_TheaterType(char const * section, char const * entry, TheaterType defvalue) const;
+ ThemeType Get_ThemeType(char const * section, char const * entry, ThemeType defvalue) const;
+ TriggerTypeClass * Get_TriggerType(char const * section, char const * entry) const;
+ VQType Get_VQType(char const * section, char const * entry, VQType defvalue) const;
+ VocType Get_VocType(char const * section, char const * entry, VocType defvalue) const;
+ WarheadType Get_WarheadType(char const * section, char const * entry, WarheadType defvalue) const;
+ WeaponType Get_WeaponType(char const * section, char const * entry, WeaponType defvalue) const;
+ long Get_Owners(char const * section, char const * entry, long defvalue) const;
+ CrateType Get_CrateType(char const * section, char const * entry, CrateType defvalue) const;
+
+
+ bool Put_Buildings(char const * section, char const * entry, long value);
+ bool Put_AnimType(char const * section, char const * entry, AnimType value);
+ bool Put_UnitType(char const * section, char const * entry, UnitType value);
+ bool Put_ArmorType(char const * section, char const * entry, ArmorType value);
+ bool Put_BulletType(char const * section, char const * entry, BulletType value);
+ bool Put_HousesType(char const * section, char const * entry, HousesType value);
+ bool Put_Lepton(char const * section, char const * entry, LEPTON value);
+ bool Put_MPHType(char const * section, char const * entry, MPHType value);
+ bool Put_VQType(char const * section, char const * entry, VQType value);
+ bool Put_OverlayType(char const * section, char const * entry, OverlayType value);
+ bool Put_Owners(char const * section, char const * entry, long value);
+ bool Put_SourceType(char const * section, char const * entry, SourceType value);
+ bool Put_TerrainType(char const * section, char const * entry, TerrainType value);
+ bool Put_TheaterType(char const * section, char const * entry, TheaterType value);
+ bool Put_ThemeType(char const * section, char const * entry, ThemeType value);
+ bool Put_TriggerType(char const * section, char const * entry, TriggerTypeClass * value);
+ bool Put_VocType(char const * section, char const * entry, VocType value);
+ bool Put_WarheadType(char const * section, char const * entry, WarheadType value);
+ bool Put_WeaponType(char const * section, char const * entry, WeaponType value);
+ bool Put_CrateType(char const * section, char const * entry, CrateType value);
+
+ int Get_Unique_ID(void) const;
+
+ private:
+ void Calculate_Message_Digest(void);
+ void Invalidate_Message_Digest(void);
+
+ bool IsDigestPresent:1;
+
+ /*
+ ** This is the message digest (SHA) of the INI database that was embedded as part of
+ ** the INI file.
+ */
+ unsigned char Digest[20];
+};
+
+#endif
diff --git a/CODE/CCMPATH.CPP b/CODE/CCMPATH.CPP
new file mode 100644
index 0000000..9d466cc
--- /dev/null
+++ b/CODE/CCMPATH.CPP
@@ -0,0 +1,421 @@
+/*
+** Command & Conquer Red Alert(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 : Command & Conquer *
+ * *
+ * File Name : CCMPATH.CPP *
+ * *
+ * Programmer : Bill R. Randolph *
+ * *
+ * Start Date : 01/09/96 *
+ * *
+ * Last Update : January 11, 1996 [BRR] *
+ * *
+ *-------------------------------------------------------------------------*
+ * Functions: *
+ * Init_MPATH -- Performs MPATH-specific initialization *
+ * Shutdown_MPATH -- Shuts down MPATH connections *
+ * Connect_MPATH -- Waits for connections to other players *
+ * Destroy_MPATH_Connection -- Destroys the given connection *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+#include "function.h"
+
+
+/***************************************************************************
+ * Init_MPATH -- Performs MPATH-specific initialization *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * 1 = OK, 0 = error *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 01/09/1996 BRR : Created. *
+ *=========================================================================*/
+int Init_MPATH(void)
+{
+#if(MPATH)
+ //------------------------------------------------------------------------
+ // Allocate a packet buffer for MPATH's use
+ //------------------------------------------------------------------------
+ Session.MPathPacket = new char[Session.MPathSize];
+
+ //------------------------------------------------------------------------
+ // Read the multiplayer settings from the CONQUER.INI file, and the game
+ // options from the options file.
+ //------------------------------------------------------------------------
+ Session.Read_MultiPlayer_Settings();
+
+ if (!Read_MPATH_Game_Options()) {
+ WWMessageBox().Process("Unable to load game settings!");
+ //Prog_End();
+ Emergency_Exit(0);
+ }
+
+ //------------------------------------------------------------------------
+ // Flush all incoming packets
+ //------------------------------------------------------------------------
+ MPath->Flush_All();
+
+ //------------------------------------------------------------------------
+ // Form connections to all other players
+ //------------------------------------------------------------------------
+ Connect_MPATH();
+
+ //------------------------------------------------------------------------
+ // Set multiplayer values for the local system, and timing values.
+ //------------------------------------------------------------------------
+ Session.CommProtocol = COMM_PROTOCOL_MULTI_E_COMP;
+
+ return (1);
+#else
+ return (1);
+#endif
+
+} // end of Init_MPATH
+
+
+/***************************************************************************
+ * Shutdown_MPATH -- Shuts down MPATH connections *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 01/09/1996 BRR : Created. *
+ *=========================================================================*/
+void Shutdown_MPATH(void)
+{
+#if(MPATH)
+ CDTimerClass timer;
+
+ //------------------------------------------------------------------------
+ // Wait a full second before exiting, to ensure all packets get sent.
+ //------------------------------------------------------------------------
+ timer = 60;
+ while (timer) ;
+
+ //------------------------------------------------------------------------
+ // Free memory
+ //------------------------------------------------------------------------
+ if (Session.MPathPacket) {
+ delete [] Session.MPathPacket;
+ Session.MPathPacket = NULL;
+ }
+
+ if (MPath) {
+ delete MPath;
+ MPath = NULL;
+ }
+
+ return;
+
+#endif
+} // end of Shutdown_MPATH
+
+
+/***************************************************************************
+ * Connect_MPATH -- Waits for connections to other players *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 01/10/1996 BRR : Created. *
+ *=========================================================================*/
+void Connect_MPATH(void)
+{
+#if(MPATH)
+ typedef struct ConnectPacketTag {
+ NetCommandType Dummy; // packet type; set to PING
+ char Name[MPLAYER_NAME_MAX]; // player's name
+ HousesType House; // player's ActLike
+ unsigned char Color; // player's Color
+ } ConnectPacketType;
+ int num_players;
+ int num_found;
+ ConnectPacketType send_packet;
+ ConnectPacketType receive_packet;
+ int address;
+ int found;
+ int size;
+ int i;
+ CDTimerClass send_timer;
+ NodeNameType *who;
+
+ enum {
+ D_TXT6_H = 7,
+ D_MARGIN = 5,
+ };
+ static int x,y,w,h;
+ char const *buf1;
+ char const *buf2;
+
+ int display = 0;
+ RemapControlType * scheme = GadgetClass::Get_Color_Scheme();
+
+ //
+ // Clear the Players list
+ //
+ while (Session.Players.Count() > 0) {
+ delete Session.Players[0];
+ Session.Players.Delete(Session.Players[0]);
+ }
+
+ //
+ // Add myself to the list first thing
+ //
+ who = new NodeNameType;
+ strcpy(who->Name, Session.Handle);
+ who->Player.House = Session.House;
+ who->Player.Color = Session.ColorIdx;
+ Session.Players.Add (who);
+
+ //
+ // Find out how many players to wait for
+ //
+ num_players = MPath->Find_Num_Connections();
+ num_found = 0;
+ Session.NumPlayers = num_players + 1;
+
+ //
+ // Send out a packet announcing my presence
+ //
+ send_packet.Dummy = NET_PING;
+ strcpy(send_packet.Name, Session.Handle);
+ send_packet.House = Session.House;
+ send_packet.Color = Session.ColorIdx;
+ MPath->Send_Global_Message(&send_packet, sizeof(send_packet), 0, 0);
+
+ //
+ // Start our packet-sending timer
+ //
+ send_timer = 240;
+
+ //
+ // Wait for all players to enter the game
+ //
+ display = 1;
+ while (num_found < num_players) {
+
+ #ifdef WIN32
+ /*
+ ** If we have just received input focus again after running in the background then
+ ** we need to redraw.
+ */
+ if (AllSurfaces.SurfacesRestored) {
+ AllSurfaces.SurfacesRestored=FALSE;
+ display = 1;
+ }
+ #endif
+
+ if (display) {
+ Fancy_Text_Print("", 0, 0, 0, 0, TPF_TEXT);
+ buf1 = Text_String(TXT_WAITING_FOR_CONNECTIONS);
+ buf2 = Text_String(TXT_PRESS_ESC);
+ w = MAX(String_Pixel_Width(buf1),String_Pixel_Width(buf2));
+ w += (D_MARGIN * 2);
+ h = (D_TXT6_H * 2) + (D_MARGIN * 7);
+ x = 160 - (w / 2);
+ y = 100 - (h / 2);
+ Hide_Mouse();
+ //Set_Logic_Page(SeenBuff);
+ Dialog_Box(x * RESFACTOR, y * RESFACTOR, w * RESFACTOR, h * RESFACTOR);
+
+ Fancy_Text_Print(buf1,
+ 160 * RESFACTOR,
+ (y + (D_MARGIN * 2)) * RESFACTOR,
+ scheme, TBLACK, TPF_CENTER | TPF_TEXT);
+ Fancy_Text_Print(buf2,
+ 160 * RESFACTOR,
+ (y + (D_MARGIN * 2) + D_TXT6_H + D_MARGIN) * RESFACTOR,
+ scheme, TBLACK, TPF_CENTER | TPF_TEXT);
+ Show_Mouse();
+ display = 0;
+ }
+
+ MPath->Service();
+
+ //
+ // Check for an incoming packet; if a PING comes in, see if we already
+ // have this player in our Players list. If not, add him.
+ //
+ if (MPath->Get_Global_Message (&receive_packet, &size, &address) &&
+ receive_packet.Dummy == NET_PING) {
+ found = 0;
+ for (i = 1; i < Session.Players.Count(); i++) {
+ if (Session.Players[i]->MPathAddress == address) {
+ found = 1;
+ break;
+ }
+ }
+
+ //
+ // Create a new connection and a new node in the list.
+ //
+ if (!found) {
+
+ who = new NodeNameType;
+ strcpy(who->Name, receive_packet.Name);
+ who->MPathAddress = address;
+ who->Player.House = receive_packet.House;
+ who->Player.Color = (PlayerColorType)receive_packet.Color;
+ Session.Players.Add (who);
+
+ num_found++;
+
+ MPath->Send_Global_Message(&send_packet, sizeof(send_packet), 1,
+ address);
+ }
+ }
+
+ //
+ // If the user hits ESC, bail out.
+ //
+ if (Keyboard->Check()) {
+ if (Keyboard->Get() == KN_ESC) {
+ //Prog_End();
+ Emergency_Exit(0);
+ }
+ }
+
+ //
+ // When our timer expires, re-send the packet.
+ //
+ if (!send_timer) {
+ send_packet.Dummy = NET_PING;
+ MPath->Send_Global_Message(&send_packet, sizeof(send_packet), 0, 0);
+ send_timer = 240;
+ }
+ }
+
+#else
+ return;
+#endif
+}
+
+
+/***************************************************************************
+ * Destroy_MPATH_Connection -- Destroys the given connection *
+ * *
+ * INPUT: *
+ * id connection ID to destroy *
+ * error 0 = user signed off; 1 = connection error; otherwise, *
+ * no error is shown. *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 01/11/1996 BRR : Created. *
+ *=========================================================================*/
+void Destroy_MPATH_Connection(int id, int error)
+{
+#if(MPATH)
+ int i;
+ HouseClass *housep;
+ char txt[80];
+
+ //------------------------------------------------------------------------
+ // Do nothing if the house isn't human.
+ //------------------------------------------------------------------------
+ housep = HouseClass::As_Pointer((HousesType)id);
+ if (!housep || !housep->IsHuman)
+ return;
+
+ /*------------------------------------------------------------------------
+ Create a message to display to the user
+ ------------------------------------------------------------------------*/
+ txt[0] = '\0';
+ if (error==1) {
+ sprintf(txt,Text_String(TXT_CONNECTION_LOST),MPath->Connection_Name(id));
+ } else if (error==0) {
+ sprintf(txt,Text_String(TXT_LEFT_GAME),MPath->Connection_Name(id));
+ }
+
+ if (strlen(txt)) {
+ Session.Messages.Add_Message (NULL,0, txt, housep->RemapColor,
+ TPF_TEXT, Rule.MessageDelay * TICKS_PER_MINUTE);
+ Map.Flag_To_Redraw(false);
+ }
+
+ //------------------------------------------------------------------------
+ // Remove this player from the Players vector
+ //------------------------------------------------------------------------
+ for (i = 0; i < Session.Players.Count(); i++) {
+ if (!stricmp(Session.Players[i]->Name,housep->IniName)) {
+ delete Session.Players[i];
+ Session.Players.Delete(Session.Players[i]);
+ break;
+ }
+ }
+
+ /*------------------------------------------------------------------------
+ Delete the MPATH connection
+ ------------------------------------------------------------------------*/
+ MPath->Delete_Connection(id);
+
+ //------------------------------------------------------------------------
+ // Turn the player's house over to the computer's AI
+ //------------------------------------------------------------------------
+ housep->IsHuman = false;
+ housep->IQ = Rule.MaxIQ;
+ strcpy (housep->IniName,Text_String(TXT_COMPUTER));
+
+ Session.NumPlayers--;
+
+ /*------------------------------------------------------------------------
+ If we're the last player left, tell the user.
+ ------------------------------------------------------------------------*/
+ if (Session.NumPlayers == 1) {
+ sprintf(txt,"%s",Text_String(TXT_JUST_YOU_AND_ME));
+ Session.Messages.Add_Message (NULL, 0, txt, housep->RemapColor,
+ TPF_TEXT, Rule.MessageDelay * TICKS_PER_MINUTE);
+ Map.Flag_To_Redraw(false);
+ }
+
+#else
+ id = id;
+ error = error;
+
+#endif
+} // end of Destroy_MPATH_Connection
+
+
+/***************************** end of ccmpath.cpp **************************/
diff --git a/CODE/CCPTR.CPP b/CODE/CCPTR.CPP
new file mode 100644
index 0000000..580d301
--- /dev/null
+++ b/CODE/CCPTR.CPP
@@ -0,0 +1,76 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CCPTR.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CCPTR.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 06/07/96 *
+ * *
+ * Last Update : July 6, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * CCPtr::operator > -- Greater than comparison operator. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#include "function.h"
+
+/*
+** These member functions for the CCPtr class cannot be declared inside the
+** class definition since they could refer to other objects that themselves
+** contain CCPtr objects. The recursive nature of this type of declaration
+** is not handled by Watcom, hence the body declaration is dislocated here.
+*/
+template
+CCPtr::CCPtr(T * ptr) : ID(-1)
+{
+ if (ptr != NULL) {
+ ID = ptr->ID;
+ }
+}
+
+
+/***********************************************************************************************
+ * CCPtr::operator > -- Greater than comparison operator. *
+ * *
+ * This will compare two pointer value to see if the left hand value is greater than the *
+ * right hand. The values are compared by comparing based on their Name() functions. *
+ * *
+ * INPUT: rvalue -- The right handle CCPtr value. *
+ * *
+ * OUTPUT: Is the left hand value greater than the right hand value? *
+ * *
+ * WARNINGS: The values pointed to by CCPtr must have a Name() function defined. *
+ * *
+ * HISTORY: *
+ * 07/06/1996 JLB : Created. *
+ *=============================================================================================*/
+template
+bool CCPtr::operator > (CCPtr const & rvalue) const
+{
+ return (stricmp((*this)->Name(), rvalue->Name()) > 0);
+}
diff --git a/CODE/CCPTR.H b/CODE/CCPTR.H
new file mode 100644
index 0000000..d798247
--- /dev/null
+++ b/CODE/CCPTR.H
@@ -0,0 +1,115 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CCPTR.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CCPTR.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 06/07/96 *
+ * *
+ * Last Update : June 7, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#ifndef CCPTR_H
+#define CCPTR_H
+
+/*
+** The CCPtr class is designed for a specific purpose. It functions like a pointer except that
+** it requires no fixups for saving and loading. If pointer fixups are not an issue, than using
+** regular pointers would be more efficient.
+*/
+template
+class CCPtr
+{
+ public:
+ CCPtr(void) : ID(-1) {};
+ CCPtr(NoInitClass const & ) {};
+ CCPtr(T * ptr);
+
+ operator T * (void) const {
+ if (ID == -1) return(NULL);
+ assert(Heap != NULL && (unsigned)ID < Heap->Length());
+ return((T*) (*Heap)[ID]);
+ }
+ T & operator * (void) const {
+ assert(Heap != NULL && (unsigned)ID < Heap->Length());
+ return(*(T*)(*Heap)[ID]);
+ }
+ T * operator -> (void) const {
+ if (ID == -1) return(NULL);
+ assert(Heap != NULL && (unsigned)ID < Heap->Length());
+ return((T*) (*Heap)[ID]);
+ }
+
+ bool Is_Valid(void) const {return(ID != -1);}
+
+ bool operator == (CCPtr const & rvalue) const {return(ID == rvalue.ID);}
+ bool operator != (CCPtr const & rvalue) const {return(ID != rvalue.ID);}
+ bool operator > (CCPtr const & rvalue) const;
+ bool operator <= (CCPtr const & rvalue) const {return (rvalue > *this);}
+ bool operator < (CCPtr const & rvalue) const {return (*this != rvalue && rvalue > *this);}
+ bool operator >= (CCPtr const & rvalue) const {return (*this == rvalue || rvalue > *this);}
+
+ long Raw(void) const {return(ID);}
+ void Set_Raw(long value) {ID = value;}
+
+ private:
+
+ static FixedIHeapClass * Heap;
+
+ /*
+ ** This is the ID number of the object it refers to. By using an ID number, this class can
+ ** be saved and loaded without pointer fixups.
+ */
+ int ID;
+};
+
+/*
+** These template helper functions tell the compiler what to do in the
+** ambiguous case of a CCPtr on one side and a regular type pointer on the
+** other side. In such a case the compiler could create a temp CCPtr object
+** OR call the conversion operator on the existing CCPtr object. Either way
+** is technically valid, but the compiler doesn't know which is better so it
+** generates an error. These routines force the conversion operator rather than
+** creating a temporary object. This presumes that the conversion operator is
+** cheaper than constructing a temporary and that cheaper solutions are desirable.
+*/
+template
+int operator == (CCPtr & lvalue, T * rvalue)
+{
+ return((T*)lvalue == rvalue);
+}
+
+template
+int operator == (T * lvalue, CCPtr & rvalue)
+{
+ return(lvalue == (T*)rvalue);
+}
+
+#endif
diff --git a/CODE/CCTEN.CPP b/CODE/CCTEN.CPP
new file mode 100644
index 0000000..a39c884
--- /dev/null
+++ b/CODE/CCTEN.CPP
@@ -0,0 +1,603 @@
+/*
+** Command & Conquer Red Alert(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 : Command & Conquer *
+ * *
+ * File Name : CCTEN.CPP *
+ * *
+ * Programmer : Bill R. Randolph *
+ * *
+ * Start Date : 01/09/96 *
+ * *
+ * Last Update : November 27, 1996 [BRR] *
+ * *
+ *-------------------------------------------------------------------------*
+ * Functions: *
+ * Init_TEN -- Performs TEN-specific initialization *
+ * Shutdown_TEN -- Shuts down TEN connections *
+ * Connect_TEN -- Waits for connections to other players *
+ * Destroy_TEN_Connection -- Destroys the given connection *
+ * Debug_Mono -- Custom mono prints *
+ * Send_TEN_Win_Packet -- Sends a win packet to server *
+ * Send_TEN_Alliance -- Sends an ally/enemy packet to server *
+ * Send_TEN_Out_Of_Sync -- Announces this game out of sync *
+ * Send_TEN_Packet_Too_Late -- Announces packet-received-too-late *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+#include "function.h"
+
+#if(TEN)
+#ifdef WIN32
+#define WINDOWS
+#endif
+#include "ten.h"
+#endif
+
+void Connect_TEN(void);
+void Debug_Mono(void);
+
+/***************************************************************************
+ * Init_TEN -- Performs TEN-specific initialization *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * 1 = OK, 0 = error *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 01/09/1996 BRR : Created. *
+ *=========================================================================*/
+int Init_TEN(void)
+{
+#if(TEN)
+ //------------------------------------------------------------------------
+ // Allocate a packet buffer for TEN's use
+ //------------------------------------------------------------------------
+ Session.TenPacket = new char[Session.TenSize];
+
+ //------------------------------------------------------------------------
+ // Read the multiplayer settings from the CONQUER.INI file, and the game
+ // options from the options file.
+ //------------------------------------------------------------------------
+ Session.Read_MultiPlayer_Settings();
+
+ if (!Read_TEN_Game_Options()) {
+ WWMessageBox().Process("Unable to load game settings!");
+ //Prog_End();
+ Emergency_Exit(0);
+ }
+
+ //------------------------------------------------------------------------
+ // Flush all incoming packets
+ //------------------------------------------------------------------------
+ Ten->Flush_All();
+
+ //------------------------------------------------------------------------
+ // Form connections to all other players
+ //------------------------------------------------------------------------
+ Connect_TEN();
+
+ //------------------------------------------------------------------------
+ // Set multiplayer values for the local system, and timing values.
+ //------------------------------------------------------------------------
+ Session.CommProtocol = COMM_PROTOCOL_MULTI_E_COMP;
+
+ return (1);
+#else
+ return (1);
+#endif
+
+} // end of Init_TEN
+
+
+/***************************************************************************
+ * Shutdown_TEN -- Shuts down TEN connections *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 01/09/1996 BRR : Created. *
+ *=========================================================================*/
+void Shutdown_TEN(void)
+{
+#if(TEN)
+ CDTimerClass timer;
+
+ //------------------------------------------------------------------------
+ // Wait a full second before exiting, to ensure all packets get sent.
+ //------------------------------------------------------------------------
+ timer = 60;
+ while (timer) ;
+
+ //------------------------------------------------------------------------
+ // Free memory
+ //------------------------------------------------------------------------
+ if (Session.TenPacket) {
+ delete [] Session.TenPacket;
+ Session.TenPacket = NULL;
+ }
+
+ if (Ten) {
+ delete Ten;
+ Ten = NULL;
+ }
+
+ return;
+
+#endif
+} // end of Shutdown_TEN
+
+
+/***************************************************************************
+ * Connect_TEN -- Waits for connections to other players *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * MPlayerCount must have been initialized at this point. *
+ * *
+ * HISTORY: *
+ * 01/10/1996 BRR : Created. *
+ *=========================================================================*/
+void Connect_TEN(void)
+{
+#if(TEN)
+ typedef struct ConnectPacketTag {
+ NetCommandType Dummy; // packet type; set to PING
+ char Name[MPLAYER_NAME_MAX]; // player's name
+ HousesType House; // player's ActLike
+ unsigned char Color; // player's Color
+ } ConnectPacketType;
+ int num_players;
+ int num_found;
+ ConnectPacketType send_packet;
+ ConnectPacketType receive_packet;
+ int address;
+ int found;
+ int size;
+ int i;
+ CDTimerClass send_timer;
+ NodeNameType *who;
+
+ enum {
+ D_TXT6_H = 7,
+ D_MARGIN = 5,
+ };
+ static int x,y,w,h;
+ char const *buf1;
+ char const *buf2;
+
+ int display = 0;
+ RemapControlType * scheme = GadgetClass::Get_Color_Scheme();
+
+ //
+ // Clear the Players list
+ //
+ while (Session.Players.Count() > 0) {
+ delete Session.Players[0];
+ Session.Players.Delete(Session.Players[0]);
+ }
+
+ //
+ // Add myself to the list first thing
+ //
+ who = new NodeNameType;
+ strcpy(who->Name, Session.Handle);
+ who->Player.House = Session.House;
+ who->Player.Color = Session.ColorIdx;
+ Session.Players.Add (who);
+
+ //
+ // Find out how many players to wait for
+ //
+ num_players = Session.NumPlayers - 1;
+ num_found = 0;
+
+ //
+ // Send out a packet announcing my presence
+ //
+ send_packet.Dummy = NET_PING;
+ strcpy(send_packet.Name, Session.Handle);
+ send_packet.House = Session.House;
+ send_packet.Color = Session.ColorIdx;
+ Ten->Send_Global_Message(&send_packet, sizeof(send_packet), 0, -1);
+
+ //
+ // Start our packet-sending timer
+ //
+ send_timer = 240;
+
+ //
+ // Wait for all players to enter the game
+ //
+ display = 1;
+ while (num_found < num_players) {
+
+ #ifdef WIN32
+ /*
+ ** If we have just received input focus again after running in the background then
+ ** we need to redraw.
+ */
+ if (AllSurfaces.SurfacesRestored) {
+ AllSurfaces.SurfacesRestored=FALSE;
+ display = 1;
+ }
+ #endif
+
+ if (display) {
+ Fancy_Text_Print("", 0, 0, 0, 0, TPF_TEXT);
+ buf1 = Text_String(TXT_WAITING_FOR_CONNECTIONS);
+ buf2 = Text_String(TXT_PRESS_ESC);
+ w = MAX(String_Pixel_Width(buf1),String_Pixel_Width(buf2));
+ w += (D_MARGIN * 2);
+ h = (D_TXT6_H * 2) + (D_MARGIN * 7);
+ x = 160 - (w / 2);
+ y = 100 - (h / 2);
+ Hide_Mouse();
+ //Set_Logic_Page(SeenBuff);
+ Dialog_Box(x * RESFACTOR, y * RESFACTOR, w * RESFACTOR, h * RESFACTOR);
+
+ Fancy_Text_Print(buf1,
+ 160 * RESFACTOR,
+ (y + (D_MARGIN * 2)) * RESFACTOR,
+ scheme, TBLACK, TPF_CENTER | TPF_TEXT);
+ Fancy_Text_Print(buf2,
+ 160 * RESFACTOR,
+ (y + (D_MARGIN * 2) + D_TXT6_H + D_MARGIN) * RESFACTOR,
+ scheme, TBLACK, TPF_CENTER | TPF_TEXT);
+ Show_Mouse();
+ display = 0;
+ }
+
+ Ten->Service();
+
+ //
+ // Check for an incoming packet; if a PING comes in, see if we already
+ // have this player in our Players list. If not, add him.
+ //
+ if (Ten->Get_Global_Message (&receive_packet, &size, &address) &&
+ receive_packet.Dummy == NET_PING) {
+ found = 0;
+ for (i = 1; i < Session.Players.Count(); i++) {
+ if (Session.Players[i]->TenAddress == address) {
+ found = 1;
+ break;
+ }
+ }
+
+ //
+ // Create a new connection and a new node in the list.
+ //
+ if (!found) {
+
+ who = new NodeNameType;
+ strcpy(who->Name, receive_packet.Name);
+ who->TenAddress = address;
+ who->Player.House = receive_packet.House;
+ who->Player.Color = (PlayerColorType)receive_packet.Color;
+ Session.Players.Add (who);
+
+ num_found++;
+
+ Ten->Send_Global_Message(&send_packet, sizeof(send_packet), 1,
+ address);
+ }
+ }
+
+ //
+ // If the user hits ESC, bail out.
+ //
+ if (Keyboard->Check()) {
+ if (Keyboard->Get() == KN_ESC) {
+ //Prog_End();
+ Emergency_Exit(0);
+ }
+ }
+
+ //
+ // When our timer expires, re-send the packet.
+ //
+ if (!send_timer) {
+ send_packet.Dummy = NET_PING;
+ Ten->Send_Global_Message(&send_packet, sizeof(send_packet), 0, -1);
+ send_timer = 240;
+ }
+ }
+
+#else
+ return;
+#endif
+}
+
+
+/***************************************************************************
+ * Destroy_TEN_Connection -- Destroys the given connection *
+ * *
+ * INPUT: *
+ * id connection ID to destroy (must be a HousesType) *
+ * error 0 = user signed off; 1 = connection error; otherwise, *
+ * no error is shown. *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 01/11/1996 BRR : Created. *
+ *=========================================================================*/
+void Destroy_TEN_Connection(int id, int error)
+{
+#if(TEN)
+ int i;
+ HouseClass *housep;
+ char txt[80];
+
+ //------------------------------------------------------------------------
+ // Do nothing if the house isn't human.
+ //------------------------------------------------------------------------
+ housep = HouseClass::As_Pointer((HousesType)id);
+ if (!housep || !housep->IsHuman)
+ return;
+
+ /*------------------------------------------------------------------------
+ Create a message to display to the user
+ ------------------------------------------------------------------------*/
+ txt[0] = '\0';
+ if (error==1) {
+ sprintf(txt,Text_String(TXT_CONNECTION_LOST),Ten->Connection_Name(id));
+ } else if (error==0) {
+ sprintf(txt,Text_String(TXT_LEFT_GAME),Ten->Connection_Name(id));
+ }
+
+ if (strlen(txt)) {
+ Session.Messages.Add_Message (NULL,0, txt, housep->RemapColor,
+ TPF_TEXT, Rule.MessageDelay * TICKS_PER_MINUTE);
+ Map.Flag_To_Redraw(false);
+ }
+
+ //------------------------------------------------------------------------
+ // Remove this player from the Players vector
+ //------------------------------------------------------------------------
+ for (i = 0; i < Session.Players.Count(); i++) {
+ if (!stricmp(Session.Players[i]->Name,housep->IniName)) {
+ delete Session.Players[i];
+ Session.Players.Delete(Session.Players[i]);
+ break;
+ }
+ }
+
+ /*------------------------------------------------------------------------
+ Delete the TEN connection
+ ------------------------------------------------------------------------*/
+ Ten->Delete_Connection(id);
+
+ //------------------------------------------------------------------------
+ // Turn the player's house over to the computer's AI
+ //------------------------------------------------------------------------
+ housep->IsHuman = false;
+ housep->IQ = Rule.MaxIQ;
+ strcpy (housep->IniName,Text_String(TXT_COMPUTER));
+
+ Session.NumPlayers--;
+
+ /*------------------------------------------------------------------------
+ If we're the last player left, tell the user.
+ ------------------------------------------------------------------------*/
+ if (Session.NumPlayers == 1) {
+ sprintf(txt,"%s",Text_String(TXT_JUST_YOU_AND_ME));
+ Session.Messages.Add_Message (NULL, 0, txt, housep->RemapColor,
+ TPF_TEXT, Rule.MessageDelay * TICKS_PER_MINUTE);
+ Map.Flag_To_Redraw(false);
+ }
+
+#else
+ id = id;
+ error = error;
+
+#endif
+} // end of Destroy_TEN_Connection
+
+
+/***************************************************************************
+ * Debug_Mono -- Custom mono prints *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 11/27/1996 BRR : Created. *
+ *=========================================================================*/
+void Debug_Mono(void)
+{
+#if(TEN)
+ int i;
+ int id;
+
+ Mono_Printf("STATE: # Connections:%d\n",Ten->Num_Connections());
+ for (i=0;iNum_Connections();i++) {
+ id = Ten->Connection_ID(i);
+ Mono_Printf("Connection %d: Name:%s, ID:%d, Address:%d\n",
+ i,
+ Ten->Connection_Name(id),
+ Ten->Connection_ID(i),
+ Ten->Connection_Address(id));
+ }
+
+#endif
+}
+
+
+/***************************************************************************
+ * Send_TEN_Win_Packet -- Sends a win packet to server *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 11/27/1996 BRR : Created. *
+ *=========================================================================*/
+void Send_TEN_Win_Packet(void)
+{
+#if(TEN)
+ char winbuf[80];
+ char idbuf[20];
+ int first = 1;
+ HouseClass *hptr;
+ int i;
+
+ //
+ // Build a special text buffer to send to the TEN server. Format:
+ // "winner 'id id'", where 'id' is the Ten Player ID of each player
+ // on the winning team. (For TEN, the color index is the player ID.)
+ //
+ sprintf(winbuf,"winner '");
+ for (i = 0; i < Session.Players.Count(); i++) {
+ hptr = HouseClass::As_Pointer(Session.Players[i]->Player.ID);
+ if (!hptr->IsDefeated) {
+ if (!first) {
+ strcat(winbuf," ");
+ } else {
+ first = 0;
+ }
+ sprintf(idbuf,"%d", Session.Players[i]->Player.Color);
+ strcat (winbuf, idbuf);
+ }
+ }
+ strcat (winbuf,"' ");
+ tenArSetPlayerState(winbuf);
+#endif // TEN
+
+} // end of Send_TEN_Win_Packet
+
+
+/***************************************************************************
+ * Send_TEN_Alliance -- Sends an ally/enemy packet to server *
+ * *
+ * INPUT: *
+ * whom name of player we're allying / enemying with *
+ * ally 1 = we're allying; 0 = we're breaking the alliance *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 11/27/1996 BRR : Created. *
+ *=========================================================================*/
+void Send_TEN_Alliance(char *whom, int ally)
+{
+#if(TEN)
+ char buf[80];
+
+ if (ally) {
+ sprintf(buf,"ally '%s' ",whom);
+ } else {
+ sprintf(buf,"enemy '%s' ",whom);
+ }
+
+ tenArSetPlayerState(buf);
+
+#else
+ whom = whom;
+ ally = ally;
+#endif // TEN
+
+} // end of Send_TEN_Alliance
+
+
+/***************************************************************************
+ * Send_TEN_Out_Of_Sync -- Announces this game out of sync *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 11/27/1996 BRR : Created. *
+ *=========================================================================*/
+void Send_TEN_Out_Of_Sync(void)
+{
+#if(TEN)
+ tenArSetPlayerState("sync '1' ");
+#endif // TEN
+
+} // end of Send_TEN_Out_Of_Sync
+
+
+/***************************************************************************
+ * Send_TEN_Packet_Too_Late -- Announces packet-received-too-late *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 11/27/1996 BRR : Created. *
+ *=========================================================================*/
+void Send_TEN_Packet_Too_Late(void)
+{
+#if(TEN)
+ tenArSetPlayerState("toolate '1' ");
+#endif // TEN
+
+} // end of Send_TEN_Packet_Too_Late
+
+
+/***************************** end of ccten.cpp *****************************/
diff --git a/CODE/CC_ICON.RC b/CODE/CC_ICON.RC
new file mode 100644
index 0000000..c9e749c
--- /dev/null
+++ b/CODE/CC_ICON.RC
@@ -0,0 +1,6 @@
+
+#define ICON_1 1
+
+
+ICON_1 ICON "redalert.ico"
+
diff --git a/CODE/CC_ICON.RH b/CODE/CC_ICON.RH
new file mode 100644
index 0000000..206d6bc
--- /dev/null
+++ b/CODE/CC_ICON.RH
@@ -0,0 +1 @@
+#define ICON_1 1
diff --git a/CODE/CDATA.CPP b/CODE/CDATA.CPP
new file mode 100644
index 0000000..39cf45c
--- /dev/null
+++ b/CODE/CDATA.CPP
@@ -0,0 +1,3324 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CDATA.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CDATA.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : May 16, 1994 *
+ * *
+ * Last Update : July 6, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * TemplateTypeClass::As_Reference -- Fetches a reference to the template specified. *
+ * TemplateTypeClass::Create_And_Place -- Creates and places a template object on the map. *
+ * TemplateTypeClass::Create_One_Of -- Creates an object of this template type. *
+ * TemplateTypeClass::Display -- Displays a generic representation of template. *
+ * TemplateTypeClass::From_Name -- Determine template from ASCII name. *
+ * TemplateTypeClass::Init -- Loads graphic data for templates. *
+ * TemplateTypeClass::Land_Type -- Determines land type from template and icon number. *
+ * TemplateTypeClass::Occupy_List -- Determines occupation list. *
+ * TemplateTypeClass::One_Time -- Performs one-time initialization *
+ * TemplateTypeClass::Prep_For_Add -- Prepares to add template to scenario. *
+ * TemplateTypeClass::TemplateTypeClass -- Constructor for template type objects. *
+ * TemplateTypeClass::operator delete -- Deletes a template type object. *
+ * TemplateTypeClass::operator new -- Allocates a template type from special heap. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+
+
+static TemplateTypeClass const Empty(
+ TEMPLATE_CLEAR1,
+ THEATERF_TEMPERATE|THEATERF_SNOW|THEATERF_INTERIOR,
+ "CLEAR1",
+ TXT_CLEAR
+);
+static TemplateTypeClass const Clear(
+ TEMPLATE_CLEAR1,
+ THEATERF_TEMPERATE|THEATERF_SNOW|THEATERF_INTERIOR,
+ "CLEAR1",
+ TXT_CLEAR
+);
+static TemplateTypeClass const Road01(
+ TEMPLATE_ROAD01,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D01",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road02(
+ TEMPLATE_ROAD02,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D02",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road03(
+ TEMPLATE_ROAD03,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D03",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road04(
+ TEMPLATE_ROAD04,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D04",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road05(
+ TEMPLATE_ROAD05,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D05",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road06(
+ TEMPLATE_ROAD06,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D06",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road07(
+ TEMPLATE_ROAD07,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D07",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road08(
+ TEMPLATE_ROAD08,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D08",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road09(
+ TEMPLATE_ROAD09,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D09",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road10(
+ TEMPLATE_ROAD10,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D10",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road11(
+ TEMPLATE_ROAD11,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D11",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road12(
+ TEMPLATE_ROAD12,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D12",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road13(
+ TEMPLATE_ROAD13,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D13",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road14(
+ TEMPLATE_ROAD14,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D14",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road15(
+ TEMPLATE_ROAD15,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D15",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road16(
+ TEMPLATE_ROAD16,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D16",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road17(
+ TEMPLATE_ROAD17,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D17",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road18(
+ TEMPLATE_ROAD18,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D18",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road19(
+ TEMPLATE_ROAD19,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D19",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road20(
+ TEMPLATE_ROAD20,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D20",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road21(
+ TEMPLATE_ROAD21,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D21",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road22(
+ TEMPLATE_ROAD22,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D22",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road23(
+ TEMPLATE_ROAD23,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D23",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road24(
+ TEMPLATE_ROAD24,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D24",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road25(
+ TEMPLATE_ROAD25,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D25",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road26(
+ TEMPLATE_ROAD26,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D26",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road27(
+ TEMPLATE_ROAD27,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D27",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road28(
+ TEMPLATE_ROAD28,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D28",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road29(
+ TEMPLATE_ROAD29,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D29",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road30(
+ TEMPLATE_ROAD30,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D30",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road31(
+ TEMPLATE_ROAD31,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D31",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road32(
+ TEMPLATE_ROAD32,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D32",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road33(
+ TEMPLATE_ROAD33,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D33",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road34(
+ TEMPLATE_ROAD34,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D34",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road35(
+ TEMPLATE_ROAD35,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D35",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road36(
+ TEMPLATE_ROAD36,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D36",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road37(
+ TEMPLATE_ROAD37,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D37",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road38(
+ TEMPLATE_ROAD38,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D38",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road39(
+ TEMPLATE_ROAD39,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D39",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road40(
+ TEMPLATE_ROAD40,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D40",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road41(
+ TEMPLATE_ROAD41,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D41",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road42(
+ TEMPLATE_ROAD42,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D42",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road43(
+ TEMPLATE_ROAD43,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D43",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road44(
+ TEMPLATE_ROAD44,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D44",
+ TXT_ROAD
+);
+static TemplateTypeClass const Road45(
+ TEMPLATE_ROAD45,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "D45",
+ TXT_ROAD
+);
+static TemplateTypeClass const Water(
+ TEMPLATE_WATER,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "W1",
+ TXT_WATER
+);
+static TemplateTypeClass const Water2(
+ TEMPLATE_WATER2,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "W2",
+ TXT_WATER
+);
+static TemplateTypeClass const Shore01(
+ TEMPLATE_SHORE01,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH01",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore02(
+ TEMPLATE_SHORE02,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH02",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore03(
+ TEMPLATE_SHORE03,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH03",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore04(
+ TEMPLATE_SHORE04,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH04",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore05(
+ TEMPLATE_SHORE05,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH05",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore06(
+ TEMPLATE_SHORE06,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH06",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore07(
+ TEMPLATE_SHORE07,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH07",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore08(
+ TEMPLATE_SHORE08,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH08",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore09(
+ TEMPLATE_SHORE09,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH09",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore10(
+ TEMPLATE_SHORE10,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH10",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore11(
+ TEMPLATE_SHORE11,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH11",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore12(
+ TEMPLATE_SHORE12,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH12",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore13(
+ TEMPLATE_SHORE13,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH13",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore14(
+ TEMPLATE_SHORE14,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH14",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore15(
+ TEMPLATE_SHORE15,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH15",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore16(
+ TEMPLATE_SHORE16,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH16",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore17(
+ TEMPLATE_SHORE17,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH17",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore18(
+ TEMPLATE_SHORE18,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH18",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore19(
+ TEMPLATE_SHORE19,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH19",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore20(
+ TEMPLATE_SHORE20,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH20",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore21(
+ TEMPLATE_SHORE21,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH21",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore22(
+ TEMPLATE_SHORE22,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH22",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore23(
+ TEMPLATE_SHORE23,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH23",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore24(
+ TEMPLATE_SHORE24,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH24",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore25(
+ TEMPLATE_SHORE25,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH25",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore26(
+ TEMPLATE_SHORE26,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH26",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore27(
+ TEMPLATE_SHORE27,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH27",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore28(
+ TEMPLATE_SHORE28,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH28",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore29(
+ TEMPLATE_SHORE29,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH29",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore30(
+ TEMPLATE_SHORE30,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH30",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore31(
+ TEMPLATE_SHORE31,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH31",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore32(
+ TEMPLATE_SHORE32,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH32",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore33(
+ TEMPLATE_SHORE33,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH33",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore34(
+ TEMPLATE_SHORE34,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH34",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore35(
+ TEMPLATE_SHORE35,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH35",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore36(
+ TEMPLATE_SHORE36,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH36",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore37(
+ TEMPLATE_SHORE37,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH37",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore38(
+ TEMPLATE_SHORE38,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH38",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore39(
+ TEMPLATE_SHORE39,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH39",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore40(
+ TEMPLATE_SHORE40,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH40",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore41(
+ TEMPLATE_SHORE41,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH41",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore42(
+ TEMPLATE_SHORE42,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH42",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore43(
+ TEMPLATE_SHORE43,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH43",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore44(
+ TEMPLATE_SHORE44,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH44",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore45(
+ TEMPLATE_SHORE45,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH45",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore46(
+ TEMPLATE_SHORE46,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH46",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore47(
+ TEMPLATE_SHORE47,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH47",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore48(
+ TEMPLATE_SHORE48,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH48",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore49(
+ TEMPLATE_SHORE49,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH49",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore50(
+ TEMPLATE_SHORE50,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH50",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore51(
+ TEMPLATE_SHORE51,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH51",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore52(
+ TEMPLATE_SHORE52,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH52",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore53(
+ TEMPLATE_SHORE53,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH53",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore54(
+ TEMPLATE_SHORE54,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH54",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore55(
+ TEMPLATE_SHORE55,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH55",
+ TXT_SHORE
+);
+static TemplateTypeClass const Shore56(
+ TEMPLATE_SHORE56,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "SH56",
+ TXT_SHORE
+);
+static TemplateTypeClass const Boulder1(
+ TEMPLATE_BOULDER1,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "B1",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Boulder2(
+ TEMPLATE_BOULDER2,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "B2",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Boulder3(
+ TEMPLATE_BOULDER3,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "B3",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Boulder4(
+ TEMPLATE_BOULDER4,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "B4",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Boulder5(
+ TEMPLATE_BOULDER5,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "B5",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Boulder6(
+ TEMPLATE_BOULDER6,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "B6",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope01(
+ TEMPLATE_SLOPE01,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S01",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope02(
+ TEMPLATE_SLOPE02,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S02",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope03(
+ TEMPLATE_SLOPE03,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S03",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope04(
+ TEMPLATE_SLOPE04,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S04",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope05(
+ TEMPLATE_SLOPE05,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S05",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope06(
+ TEMPLATE_SLOPE06,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S06",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope07(
+ TEMPLATE_SLOPE07,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S07",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope08(
+ TEMPLATE_SLOPE08,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S08",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope09(
+ TEMPLATE_SLOPE09,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S09",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope10(
+ TEMPLATE_SLOPE10,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S10",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope11(
+ TEMPLATE_SLOPE11,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S11",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope12(
+ TEMPLATE_SLOPE12,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S12",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope13(
+ TEMPLATE_SLOPE13,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S13",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope14(
+ TEMPLATE_SLOPE14,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S14",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope15(
+ TEMPLATE_SLOPE15,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S15",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope16(
+ TEMPLATE_SLOPE16,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S16",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope17(
+ TEMPLATE_SLOPE17,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S17",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope18(
+ TEMPLATE_SLOPE18,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S18",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope19(
+ TEMPLATE_SLOPE19,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S19",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope20(
+ TEMPLATE_SLOPE20,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S20",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope21(
+ TEMPLATE_SLOPE21,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S21",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope22(
+ TEMPLATE_SLOPE22,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S22",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope23(
+ TEMPLATE_SLOPE23,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S23",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope24(
+ TEMPLATE_SLOPE24,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S24",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope25(
+ TEMPLATE_SLOPE25,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S25",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope26(
+ TEMPLATE_SLOPE26,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S26",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope27(
+ TEMPLATE_SLOPE27,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S27",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope28(
+ TEMPLATE_SLOPE28,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S28",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope29(
+ TEMPLATE_SLOPE29,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S29",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope30(
+ TEMPLATE_SLOPE30,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S30",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope31(
+ TEMPLATE_SLOPE31,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S31",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope32(
+ TEMPLATE_SLOPE32,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S32",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope33(
+ TEMPLATE_SLOPE33,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S33",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope34(
+ TEMPLATE_SLOPE34,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S34",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope35(
+ TEMPLATE_SLOPE35,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S35",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope36(
+ TEMPLATE_SLOPE36,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S36",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope37(
+ TEMPLATE_SLOPE37,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S37",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Slope38(
+ TEMPLATE_SLOPE38,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "S38",
+ TXT_SLOPE
+);
+static TemplateTypeClass const Patch01(
+ TEMPLATE_PATCH01,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "P01",
+ TXT_PATCH
+);
+static TemplateTypeClass const Patch02(
+ TEMPLATE_PATCH02,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "P02",
+ TXT_PATCH
+);
+static TemplateTypeClass const Patch03(
+ TEMPLATE_PATCH03,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "P03",
+ TXT_PATCH
+);
+static TemplateTypeClass const Patch04(
+ TEMPLATE_PATCH04,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "P04",
+ TXT_PATCH
+);
+static TemplateTypeClass const Patch07(
+ TEMPLATE_PATCH07,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "P07",
+ TXT_PATCH
+);
+static TemplateTypeClass const Patch08(
+ TEMPLATE_PATCH08,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "P08",
+ TXT_PATCH
+);
+static TemplateTypeClass const Patch13(
+ TEMPLATE_PATCH13,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "P13",
+ TXT_PATCH
+);
+static TemplateTypeClass const Patch14(
+ TEMPLATE_PATCH14,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "P14",
+ TXT_PATCH
+);
+static TemplateTypeClass const Patch15(
+ TEMPLATE_PATCH15,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "P15",
+ TXT_PATCH
+);
+static TemplateTypeClass const River01(
+ TEMPLATE_RIVER01,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "RV01",
+ TXT_RIVER
+);
+static TemplateTypeClass const River02(
+ TEMPLATE_RIVER02,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "RV02",
+ TXT_RIVER
+);
+static TemplateTypeClass const River03(
+ TEMPLATE_RIVER03,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "RV03",
+ TXT_RIVER
+);
+static TemplateTypeClass const River04(
+ TEMPLATE_RIVER04,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "RV04",
+ TXT_RIVER
+);
+static TemplateTypeClass const River05(
+ TEMPLATE_RIVER05,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "RV05",
+ TXT_RIVER
+);
+static TemplateTypeClass const River06(
+ TEMPLATE_RIVER06,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "RV06",
+ TXT_RIVER
+);
+static TemplateTypeClass const River07(
+ TEMPLATE_RIVER07,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "RV07",
+ TXT_RIVER
+);
+static TemplateTypeClass const River08(
+ TEMPLATE_RIVER08,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "RV08",
+ TXT_RIVER
+);
+static TemplateTypeClass const River09(
+ TEMPLATE_RIVER09,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "RV09",
+ TXT_RIVER
+);
+static TemplateTypeClass const River10(
+ TEMPLATE_RIVER10,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "RV10",
+ TXT_RIVER
+);
+static TemplateTypeClass const River11(
+ TEMPLATE_RIVER11,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "RV11",
+ TXT_RIVER
+);
+static TemplateTypeClass const River12(
+ TEMPLATE_RIVER12,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "RV12",
+ TXT_RIVER
+);
+static TemplateTypeClass const River13(
+ TEMPLATE_RIVER13,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "RV13",
+ TXT_RIVER
+);
+static TemplateTypeClass const River14(
+ TEMPLATE_RIVER14,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "RV14",
+ TXT_RIVER
+);
+static TemplateTypeClass const River15(
+ TEMPLATE_RIVER15,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "RV15",
+ TXT_RIVER
+);
+static TemplateTypeClass const Ford1(
+ TEMPLATE_FORD1,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "FORD1",
+ TXT_RIVER
+);
+static TemplateTypeClass const Ford2(
+ TEMPLATE_FORD2,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "FORD2",
+ TXT_RIVER
+);
+static TemplateTypeClass const Falls1(
+ TEMPLATE_FALLS1,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "FALLS1",
+ TXT_RIVER
+);
+static TemplateTypeClass const Falls1a(
+ TEMPLATE_FALLS1A,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "FALLS1A",
+ TXT_RIVER
+);
+static TemplateTypeClass const Falls2(
+ TEMPLATE_FALLS2,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "FALLS2",
+ TXT_RIVER
+);
+static TemplateTypeClass const Falls2a(
+ TEMPLATE_FALLS2A,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "FALLS2A",
+ TXT_RIVER
+);
+static TemplateTypeClass const Bridge1x(
+ TEMPLATE_BRIDGE1X,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "BRIDGE1X",
+ TXT_BRIDGE
+);
+static TemplateTypeClass const Bridge1(
+ TEMPLATE_BRIDGE1,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "BRIDGE1",
+ TXT_BRIDGE
+);
+static TemplateTypeClass const Bridge1h(
+ TEMPLATE_BRIDGE1H,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "BRIDGE1H",
+ TXT_BRIDGE
+);
+static TemplateTypeClass const Bridge1d(
+ TEMPLATE_BRIDGE1D,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "BRIDGE1D",
+ TXT_BRIDGE
+);
+static TemplateTypeClass const Bridge2x(
+ TEMPLATE_BRIDGE2X,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "BRIDGE2X",
+ TXT_BRIDGE
+);
+static TemplateTypeClass const Bridge2(
+ TEMPLATE_BRIDGE2,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "BRIDGE2",
+ TXT_BRIDGE
+);
+static TemplateTypeClass const Bridge2h(
+ TEMPLATE_BRIDGE2H,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "BRIDGE2H",
+ TXT_BRIDGE
+);
+static TemplateTypeClass const Bridge2d(
+ TEMPLATE_BRIDGE2D,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "BRIDGE2D",
+ TXT_BRIDGE
+);
+static TemplateTypeClass const Bridge1ax(
+ TEMPLATE_BRIDGE_1AX,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "BR1X",
+ TXT_BRIDGE
+);
+static TemplateTypeClass const Bridge1a(
+ TEMPLATE_BRIDGE_1A,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "BR1A",
+ TXT_BRIDGE
+);
+static TemplateTypeClass const Bridge1b(
+ TEMPLATE_BRIDGE_1B,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "BR1B",
+ TXT_BRIDGE
+);
+static TemplateTypeClass const Bridge1c(
+ TEMPLATE_BRIDGE_1C,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "BR1C",
+ TXT_BRIDGE
+);
+static TemplateTypeClass const Bridge2ax(
+ TEMPLATE_BRIDGE_2AX,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "BR2X",
+ TXT_BRIDGE
+);
+static TemplateTypeClass const Bridge2a(
+ TEMPLATE_BRIDGE_2A,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "BR2A",
+ TXT_BRIDGE
+);
+static TemplateTypeClass const Bridge2b(
+ TEMPLATE_BRIDGE_2B,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "BR2B",
+ TXT_BRIDGE
+);
+static TemplateTypeClass const Bridge2c(
+ TEMPLATE_BRIDGE_2C,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "BR2C",
+ TXT_BRIDGE
+);
+static TemplateTypeClass const Bridge3a(
+ TEMPLATE_BRIDGE_3A,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "BR3A",
+ TXT_BRIDGE
+);
+static TemplateTypeClass const Bridge3b(
+ TEMPLATE_BRIDGE_3B,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "BR3B",
+ TXT_BRIDGE
+);
+static TemplateTypeClass const Bridge3c(
+ TEMPLATE_BRIDGE_3C,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "BR3C",
+ TXT_BRIDGE
+);
+static TemplateTypeClass const Bridge3d(
+ TEMPLATE_BRIDGE_3D,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "BR3D",
+ TXT_BRIDGE
+);
+static TemplateTypeClass const Bridge3e(
+ TEMPLATE_BRIDGE_3E,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "BR3E",
+ TXT_BRIDGE
+);
+static TemplateTypeClass const Bridge3f(
+ TEMPLATE_BRIDGE_3F,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "BR3F",
+ TXT_BRIDGE
+);
+static TemplateTypeClass const ShoreCliff01(
+ TEMPLATE_SHORECLIFF01,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC01",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff02(
+ TEMPLATE_SHORECLIFF02,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC02",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff03(
+ TEMPLATE_SHORECLIFF03,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC03",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff04(
+ TEMPLATE_SHORECLIFF04,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC04",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff05(
+ TEMPLATE_SHORECLIFF05,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC05",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff06(
+ TEMPLATE_SHORECLIFF06,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC06",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff07(
+ TEMPLATE_SHORECLIFF07,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC07",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff08(
+ TEMPLATE_SHORECLIFF08,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC08",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff09(
+ TEMPLATE_SHORECLIFF09,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC09",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff10(
+ TEMPLATE_SHORECLIFF10,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC10",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff11(
+ TEMPLATE_SHORECLIFF11,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC11",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff12(
+ TEMPLATE_SHORECLIFF12,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC12",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff13(
+ TEMPLATE_SHORECLIFF13,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC13",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff14(
+ TEMPLATE_SHORECLIFF14,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC14",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff15(
+ TEMPLATE_SHORECLIFF15,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC15",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff16(
+ TEMPLATE_SHORECLIFF16,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC16",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff17(
+ TEMPLATE_SHORECLIFF17,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC17",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff18(
+ TEMPLATE_SHORECLIFF18,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC18",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff19(
+ TEMPLATE_SHORECLIFF19,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC19",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff20(
+ TEMPLATE_SHORECLIFF20,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC20",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff21(
+ TEMPLATE_SHORECLIFF21,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC21",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff22(
+ TEMPLATE_SHORECLIFF22,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC22",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff23(
+ TEMPLATE_SHORECLIFF23,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC23",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff24(
+ TEMPLATE_SHORECLIFF24,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC24",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff25(
+ TEMPLATE_SHORECLIFF25,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC25",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff26(
+ TEMPLATE_SHORECLIFF26,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC26",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff27(
+ TEMPLATE_SHORECLIFF27,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC27",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff28(
+ TEMPLATE_SHORECLIFF28,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC28",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff29(
+ TEMPLATE_SHORECLIFF29,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC29",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff30(
+ TEMPLATE_SHORECLIFF30,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC30",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff31(
+ TEMPLATE_SHORECLIFF31,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC31",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff32(
+ TEMPLATE_SHORECLIFF32,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC32",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff33(
+ TEMPLATE_SHORECLIFF33,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC33",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff34(
+ TEMPLATE_SHORECLIFF34,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC34",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff35(
+ TEMPLATE_SHORECLIFF35,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC35",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff36(
+ TEMPLATE_SHORECLIFF36,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC36",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff37(
+ TEMPLATE_SHORECLIFF37,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC37",
+ TXT_SHORE
+);
+static TemplateTypeClass const ShoreCliff38(
+ TEMPLATE_SHORECLIFF38,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "WC38",
+ TXT_SHORE
+);
+static TemplateTypeClass const Rough01(
+ TEMPLATE_ROUGH01,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "RF01",
+ TXT_ROCK
+);
+static TemplateTypeClass const Rough02(
+ TEMPLATE_ROUGH02,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "RF02",
+ TXT_ROCK
+);
+static TemplateTypeClass const Rough03(
+ TEMPLATE_ROUGH03,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "RF03",
+ TXT_ROCK
+);
+static TemplateTypeClass const Rough04(
+ TEMPLATE_ROUGH04,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "RF04",
+ TXT_ROCK
+);
+static TemplateTypeClass const Rough05(
+ TEMPLATE_ROUGH05,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "RF05",
+ TXT_ROCK
+);
+static TemplateTypeClass const Rough06(
+ TEMPLATE_ROUGH06,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "RF06",
+ TXT_ROCK
+);
+static TemplateTypeClass const Rough07(
+ TEMPLATE_ROUGH07,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "RF07",
+ TXT_ROCK
+);
+static TemplateTypeClass const Rough08(
+ TEMPLATE_ROUGH08,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "RF08",
+ TXT_ROCK
+);
+static TemplateTypeClass const Rough09(
+ TEMPLATE_ROUGH09,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "RF09",
+ TXT_ROCK
+);
+static TemplateTypeClass const Rough10(
+ TEMPLATE_ROUGH10,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "RF10",
+ TXT_ROCK
+);
+static TemplateTypeClass const Rough11(
+ TEMPLATE_ROUGH11,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "RF11",
+ TXT_ROCK
+);
+static TemplateTypeClass const RiverCliff01(
+ TEMPLATE_RIVERCLIFF01,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "RC01",
+ TXT_RIVER
+);
+static TemplateTypeClass const RiverCliff02(
+ TEMPLATE_RIVERCLIFF02,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "RC02",
+ TXT_RIVER
+);
+static TemplateTypeClass const RiverCliff03(
+ TEMPLATE_RIVERCLIFF03,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "RC03",
+ TXT_RIVER
+);
+static TemplateTypeClass const RiverCliff04(
+ TEMPLATE_RIVERCLIFF04,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "RC04",
+ TXT_RIVER
+);
+
+static TemplateTypeClass const F01(
+ TEMPLATE_F01,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "F01",
+ TXT_RIVER
+);
+static TemplateTypeClass const F02(
+ TEMPLATE_F02,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "F02",
+ TXT_RIVER
+);
+static TemplateTypeClass const F03(
+ TEMPLATE_F03,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "F03",
+ TXT_RIVER
+);
+static TemplateTypeClass const F04(
+ TEMPLATE_F04,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "F04",
+ TXT_RIVER
+);
+static TemplateTypeClass const F05(
+ TEMPLATE_F05,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "F05",
+ TXT_RIVER
+);
+static TemplateTypeClass const F06(
+ TEMPLATE_F06,
+ THEATERF_TEMPERATE|THEATERF_SNOW,
+ "F06",
+ TXT_RIVER
+);
+
+static TemplateTypeClass const ARRO0001(
+ TEMPLATE_ARRO0001,
+ THEATERF_INTERIOR,
+ "ARRO0001",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const ARRO0002(
+ TEMPLATE_ARRO0002,
+ THEATERF_INTERIOR,
+ "ARRO0002",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const ARRO0003(
+ TEMPLATE_ARRO0003,
+ THEATERF_INTERIOR,
+ "ARRO0003",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const ARRO0004(
+ TEMPLATE_ARRO0004,
+ THEATERF_INTERIOR,
+ "ARRO0004",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const ARRO0005(
+ TEMPLATE_ARRO0005,
+ THEATERF_INTERIOR,
+ "ARRO0005",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const ARRO0006(
+ TEMPLATE_ARRO0006,
+ THEATERF_INTERIOR,
+ "ARRO0006",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const ARRO0007(
+ TEMPLATE_ARRO0007,
+ THEATERF_INTERIOR,
+ "ARRO0007",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const ARRO0008(
+ TEMPLATE_ARRO0008,
+ THEATERF_INTERIOR,
+ "ARRO0008",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const ARRO0009(
+ TEMPLATE_ARRO0009,
+ THEATERF_INTERIOR,
+ "ARRO0009",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const ARRO0010(
+ TEMPLATE_ARRO0010,
+ THEATERF_INTERIOR,
+ "ARRO0010",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const ARRO0011(
+ TEMPLATE_ARRO0011,
+ THEATERF_INTERIOR,
+ "ARRO0011",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const ARRO0012(
+ TEMPLATE_ARRO0012,
+ THEATERF_INTERIOR,
+ "ARRO0012",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const ARRO0013(
+ TEMPLATE_ARRO0013,
+ THEATERF_INTERIOR,
+ "ARRO0013",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const ARRO0014(
+ TEMPLATE_ARRO0014,
+ THEATERF_INTERIOR,
+ "ARRO0014",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const ARRO0015(
+ TEMPLATE_ARRO0015,
+ THEATERF_INTERIOR,
+ "ARRO0015",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const FLOR0001(
+ TEMPLATE_FLOR0001,
+ THEATERF_INTERIOR,
+ "FLOR0001",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const FLOR0002(
+ TEMPLATE_FLOR0002,
+ THEATERF_INTERIOR,
+ "FLOR0002",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const FLOR0003(
+ TEMPLATE_FLOR0003,
+ THEATERF_INTERIOR,
+ "FLOR0003",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const FLOR0004(
+ TEMPLATE_FLOR0004,
+ THEATERF_INTERIOR,
+ "FLOR0004",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const FLOR0005(
+ TEMPLATE_FLOR0005,
+ THEATERF_INTERIOR,
+ "FLOR0005",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const FLOR0006(
+ TEMPLATE_FLOR0006,
+ THEATERF_INTERIOR,
+ "FLOR0006",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const FLOR0007(
+ TEMPLATE_FLOR0007,
+ THEATERF_INTERIOR,
+ "FLOR0007",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const GFLR0001(
+ TEMPLATE_GFLR0001,
+ THEATERF_INTERIOR,
+ "GFLR0001",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const GFLR0002(
+ TEMPLATE_GFLR0002,
+ THEATERF_INTERIOR,
+ "GFLR0002",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const GFLR0003(
+ TEMPLATE_GFLR0003,
+ THEATERF_INTERIOR,
+ "GFLR0003",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const GFLR0004(
+ TEMPLATE_GFLR0004,
+ THEATERF_INTERIOR,
+ "GFLR0004",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const GFLR0005(
+ TEMPLATE_GFLR0005,
+ THEATERF_INTERIOR,
+ "GFLR0005",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const GSTR0001(
+ TEMPLATE_GSTR0001,
+ THEATERF_INTERIOR,
+ "GSTR0001",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const GSTR0002(
+ TEMPLATE_GSTR0002,
+ THEATERF_INTERIOR,
+ "GSTR0002",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const GSTR0003(
+ TEMPLATE_GSTR0003,
+ THEATERF_INTERIOR,
+ "GSTR0003",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const GSTR0004(
+ TEMPLATE_GSTR0004,
+ THEATERF_INTERIOR,
+ "GSTR0004",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const GSTR0005(
+ TEMPLATE_GSTR0005,
+ THEATERF_INTERIOR,
+ "GSTR0005",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const GSTR0006(
+ TEMPLATE_GSTR0006,
+ THEATERF_INTERIOR,
+ "GSTR0006",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const GSTR0007(
+ TEMPLATE_GSTR0007,
+ THEATERF_INTERIOR,
+ "GSTR0007",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const GSTR0008(
+ TEMPLATE_GSTR0008,
+ THEATERF_INTERIOR,
+ "GSTR0008",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const GSTR0009(
+ TEMPLATE_GSTR0009,
+ THEATERF_INTERIOR,
+ "GSTR0009",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const GSTR0010(
+ TEMPLATE_GSTR0010,
+ THEATERF_INTERIOR,
+ "GSTR0010",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const GSTR0011(
+ TEMPLATE_GSTR0011,
+ THEATERF_INTERIOR,
+ "GSTR0011",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const LWAL0001(
+ TEMPLATE_LWAL0001,
+ THEATERF_INTERIOR,
+ "LWAL0001",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const LWAL0002(
+ TEMPLATE_LWAL0002,
+ THEATERF_INTERIOR,
+ "LWAL0002",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const LWAL0003(
+ TEMPLATE_LWAL0003,
+ THEATERF_INTERIOR,
+ "LWAL0003",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const LWAL0004(
+ TEMPLATE_LWAL0004,
+ THEATERF_INTERIOR,
+ "LWAL0004",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const LWAL0005(
+ TEMPLATE_LWAL0005,
+ THEATERF_INTERIOR,
+ "LWAL0005",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const LWAL0006(
+ TEMPLATE_LWAL0006,
+ THEATERF_INTERIOR,
+ "LWAL0006",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const LWAL0007(
+ TEMPLATE_LWAL0007,
+ THEATERF_INTERIOR,
+ "LWAL0007",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const LWAL0008(
+ TEMPLATE_LWAL0008,
+ THEATERF_INTERIOR,
+ "LWAL0008",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const LWAL0009(
+ TEMPLATE_LWAL0009,
+ THEATERF_INTERIOR,
+ "LWAL0009",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const LWAL0010(
+ TEMPLATE_LWAL0010,
+ THEATERF_INTERIOR,
+ "LWAL0010",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const LWAL0011(
+ TEMPLATE_LWAL0011,
+ THEATERF_INTERIOR,
+ "LWAL0011",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const LWAL0012(
+ TEMPLATE_LWAL0012,
+ THEATERF_INTERIOR,
+ "LWAL0012",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const LWAL0013(
+ TEMPLATE_LWAL0013,
+ THEATERF_INTERIOR,
+ "LWAL0013",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const LWAL0014(
+ TEMPLATE_LWAL0014,
+ THEATERF_INTERIOR,
+ "LWAL0014",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const LWAL0015(
+ TEMPLATE_LWAL0015,
+ THEATERF_INTERIOR,
+ "LWAL0015",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const LWAL0016(
+ TEMPLATE_LWAL0016,
+ THEATERF_INTERIOR,
+ "LWAL0016",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const LWAL0017(
+ TEMPLATE_LWAL0017,
+ THEATERF_INTERIOR,
+ "LWAL0017",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const LWAL0018(
+ TEMPLATE_LWAL0018,
+ THEATERF_INTERIOR,
+ "LWAL0018",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const LWAL0019(
+ TEMPLATE_LWAL0019,
+ THEATERF_INTERIOR,
+ "LWAL0019",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const LWAL0020(
+ TEMPLATE_LWAL0020,
+ THEATERF_INTERIOR,
+ "LWAL0020",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const LWAL0021(
+ TEMPLATE_LWAL0021,
+ THEATERF_INTERIOR,
+ "LWAL0021",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const LWAL0022(
+ TEMPLATE_LWAL0022,
+ THEATERF_INTERIOR,
+ "LWAL0022",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const LWAL0023(
+ TEMPLATE_LWAL0023,
+ THEATERF_INTERIOR,
+ "LWAL0023",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const LWAL0024(
+ TEMPLATE_LWAL0024,
+ THEATERF_INTERIOR,
+ "LWAL0024",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const LWAL0025(
+ TEMPLATE_LWAL0025,
+ THEATERF_INTERIOR,
+ "LWAL0025",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const LWAL0026(
+ TEMPLATE_LWAL0026,
+ THEATERF_INTERIOR,
+ "LWAL0026",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const LWAL0027(
+ TEMPLATE_LWAL0027,
+ THEATERF_INTERIOR,
+ "LWAL0027",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const STRP0001(
+ TEMPLATE_STRP0001,
+ THEATERF_INTERIOR,
+ "STRP0001",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const STRP0002(
+ TEMPLATE_STRP0002,
+ THEATERF_INTERIOR,
+ "STRP0002",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const STRP0003(
+ TEMPLATE_STRP0003,
+ THEATERF_INTERIOR,
+ "STRP0003",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const STRP0004(
+ TEMPLATE_STRP0004,
+ THEATERF_INTERIOR,
+ "STRP0004",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const STRP0005(
+ TEMPLATE_STRP0005,
+ THEATERF_INTERIOR,
+ "STRP0005",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const STRP0006(
+ TEMPLATE_STRP0006,
+ THEATERF_INTERIOR,
+ "STRP0006",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const STRP0007(
+ TEMPLATE_STRP0007,
+ THEATERF_INTERIOR,
+ "STRP0007",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const STRP0008(
+ TEMPLATE_STRP0008,
+ THEATERF_INTERIOR,
+ "STRP0008",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const STRP0009(
+ TEMPLATE_STRP0009,
+ THEATERF_INTERIOR,
+ "STRP0009",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const STRP0010(
+ TEMPLATE_STRP0010,
+ THEATERF_INTERIOR,
+ "STRP0010",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const STRP0011(
+ TEMPLATE_STRP0011,
+ THEATERF_INTERIOR,
+ "STRP0011",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0001(
+ TEMPLATE_WALL0001,
+ THEATERF_INTERIOR,
+ "WALL0001",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0002(
+ TEMPLATE_WALL0002,
+ THEATERF_INTERIOR,
+ "WALL0002",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0003(
+ TEMPLATE_WALL0003,
+ THEATERF_INTERIOR,
+ "WALL0003",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0004(
+ TEMPLATE_WALL0004,
+ THEATERF_INTERIOR,
+ "WALL0004",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0005(
+ TEMPLATE_WALL0005,
+ THEATERF_INTERIOR,
+ "WALL0005",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0006(
+ TEMPLATE_WALL0006,
+ THEATERF_INTERIOR,
+ "WALL0006",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0007(
+ TEMPLATE_WALL0007,
+ THEATERF_INTERIOR,
+ "WALL0007",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0008(
+ TEMPLATE_WALL0008,
+ THEATERF_INTERIOR,
+ "WALL0008",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0009(
+ TEMPLATE_WALL0009,
+ THEATERF_INTERIOR,
+ "WALL0009",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0010(
+ TEMPLATE_WALL0010,
+ THEATERF_INTERIOR,
+ "WALL0010",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0011(
+ TEMPLATE_WALL0011,
+ THEATERF_INTERIOR,
+ "WALL0011",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0012(
+ TEMPLATE_WALL0012,
+ THEATERF_INTERIOR,
+ "WALL0012",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0013(
+ TEMPLATE_WALL0013,
+ THEATERF_INTERIOR,
+ "WALL0013",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0014(
+ TEMPLATE_WALL0014,
+ THEATERF_INTERIOR,
+ "WALL0014",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0015(
+ TEMPLATE_WALL0015,
+ THEATERF_INTERIOR,
+ "WALL0015",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0016(
+ TEMPLATE_WALL0016,
+ THEATERF_INTERIOR,
+ "WALL0016",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0017(
+ TEMPLATE_WALL0017,
+ THEATERF_INTERIOR,
+ "WALL0017",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0018(
+ TEMPLATE_WALL0018,
+ THEATERF_INTERIOR,
+ "WALL0018",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0019(
+ TEMPLATE_WALL0019,
+ THEATERF_INTERIOR,
+ "WALL0019",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0020(
+ TEMPLATE_WALL0020,
+ THEATERF_INTERIOR,
+ "WALL0020",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0021(
+ TEMPLATE_WALL0021,
+ THEATERF_INTERIOR,
+ "WALL0021",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0022(
+ TEMPLATE_WALL0022,
+ THEATERF_INTERIOR,
+ "WALL0022",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0023(
+ TEMPLATE_WALL0023,
+ THEATERF_INTERIOR,
+ "WALL0023",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0024(
+ TEMPLATE_WALL0024,
+ THEATERF_INTERIOR,
+ "WALL0024",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0025(
+ TEMPLATE_WALL0025,
+ THEATERF_INTERIOR,
+ "WALL0025",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0026(
+ TEMPLATE_WALL0026,
+ THEATERF_INTERIOR,
+ "WALL0026",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0027(
+ TEMPLATE_WALL0027,
+ THEATERF_INTERIOR,
+ "WALL0027",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0028(
+ TEMPLATE_WALL0028,
+ THEATERF_INTERIOR,
+ "WALL0028",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0029(
+ TEMPLATE_WALL0029,
+ THEATERF_INTERIOR,
+ "WALL0029",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0030(
+ TEMPLATE_WALL0030,
+ THEATERF_INTERIOR,
+ "WALL0030",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0031(
+ TEMPLATE_WALL0031,
+ THEATERF_INTERIOR,
+ "WALL0031",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0032(
+ TEMPLATE_WALL0032,
+ THEATERF_INTERIOR,
+ "WALL0032",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0033(
+ TEMPLATE_WALL0033,
+ THEATERF_INTERIOR,
+ "WALL0033",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0034(
+ TEMPLATE_WALL0034,
+ THEATERF_INTERIOR,
+ "WALL0034",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0035(
+ TEMPLATE_WALL0035,
+ THEATERF_INTERIOR,
+ "WALL0035",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0036(
+ TEMPLATE_WALL0036,
+ THEATERF_INTERIOR,
+ "WALL0036",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0037(
+ TEMPLATE_WALL0037,
+ THEATERF_INTERIOR,
+ "WALL0037",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0038(
+ TEMPLATE_WALL0038,
+ THEATERF_INTERIOR,
+ "WALL0038",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0039(
+ TEMPLATE_WALL0039,
+ THEATERF_INTERIOR,
+ "WALL0039",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0040(
+ TEMPLATE_WALL0040,
+ THEATERF_INTERIOR,
+ "WALL0040",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0041(
+ TEMPLATE_WALL0041,
+ THEATERF_INTERIOR,
+ "WALL0041",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0042(
+ TEMPLATE_WALL0042,
+ THEATERF_INTERIOR,
+ "WALL0042",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0043(
+ TEMPLATE_WALL0043,
+ THEATERF_INTERIOR,
+ "WALL0043",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0044(
+ TEMPLATE_WALL0044,
+ THEATERF_INTERIOR,
+ "WALL0044",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0045(
+ TEMPLATE_WALL0045,
+ THEATERF_INTERIOR,
+ "WALL0045",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0046(
+ TEMPLATE_WALL0046,
+ THEATERF_INTERIOR,
+ "WALL0046",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0047(
+ TEMPLATE_WALL0047,
+ THEATERF_INTERIOR,
+ "WALL0047",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0048(
+ TEMPLATE_WALL0048,
+ THEATERF_INTERIOR,
+ "WALL0048",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const WALL0049(
+ TEMPLATE_WALL0049,
+ THEATERF_INTERIOR,
+ "WALL0049",
+ TXT_INTERIOR
+);
+
+static TemplateTypeClass const Xtra0001(
+ TEMPLATE_XTRA0001,
+ THEATERF_INTERIOR,
+ "XTRA0001",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const Xtra0002(
+ TEMPLATE_XTRA0002,
+ THEATERF_INTERIOR,
+ "XTRA0002",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const Xtra0003(
+ TEMPLATE_XTRA0003,
+ THEATERF_INTERIOR,
+ "XTRA0003",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const Xtra0004(
+ TEMPLATE_XTRA0004,
+ THEATERF_INTERIOR,
+ "XTRA0004",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const Xtra0005(
+ TEMPLATE_XTRA0005,
+ THEATERF_INTERIOR,
+ "XTRA0005",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const Xtra0006(
+ TEMPLATE_XTRA0006,
+ THEATERF_INTERIOR,
+ "XTRA0006",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const Xtra0007(
+ TEMPLATE_XTRA0007,
+ THEATERF_INTERIOR,
+ "XTRA0007",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const Xtra0008(
+ TEMPLATE_XTRA0008,
+ THEATERF_INTERIOR,
+ "XTRA0008",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const Xtra0009(
+ TEMPLATE_XTRA0009,
+ THEATERF_INTERIOR,
+ "XTRA0009",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const Xtra0010(
+ TEMPLATE_XTRA0010,
+ THEATERF_INTERIOR,
+ "XTRA0010",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const Xtra0011(
+ TEMPLATE_XTRA0011,
+ THEATERF_INTERIOR,
+ "XTRA0011",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const Xtra0012(
+ TEMPLATE_XTRA0012,
+ THEATERF_INTERIOR,
+ "XTRA0012",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const Xtra0013(
+ TEMPLATE_XTRA0013,
+ THEATERF_INTERIOR,
+ "XTRA0013",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const Xtra0014(
+ TEMPLATE_XTRA0014,
+ THEATERF_INTERIOR,
+ "XTRA0014",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const Xtra0015(
+ TEMPLATE_XTRA0015,
+ THEATERF_INTERIOR,
+ "XTRA0015",
+ TXT_INTERIOR
+);
+static TemplateTypeClass const Xtra0016(
+ TEMPLATE_XTRA0016,
+ THEATERF_INTERIOR,
+ "XTRA0016",
+ TXT_INTERIOR
+);
+
+#ifdef FIXIT_ANTS
+static TemplateTypeClass const AntHill(
+ TEMPLATE_HILL01,
+ THEATERF_TEMPERATE,
+ "HILL01",
+ TXT_ROCK
+);
+#endif
+
+/***********************************************************************************************
+ * TemplateTypeClass::TemplateTypeClass -- Constructor for template type objects. *
+ * *
+ * This is the constructor for the template types. *
+ * *
+ * INPUT: see below... *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/29/1994 JLB : Created. *
+ *=============================================================================================*/
+TemplateTypeClass::TemplateTypeClass(
+ TemplateType iconset,
+ int theater,
+ char const * ininame,
+ int fullname) :
+ ObjectTypeClass(
+ RTTI_TEMPLATETYPE,
+ int(iconset),
+ false,
+ true,
+ false,
+ false,
+ true,
+ true,
+ false,
+ fullname,
+ ininame),
+ Type(iconset),
+ Theater(theater),
+ Width(0),
+ Height(0)
+{
+}
+
+
+/***********************************************************************************************
+ * TemplateTypeClass::operator new -- Allocates a template type from special heap. *
+ * *
+ * This allocates a template type object from the special heap used for that purpose. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with a pointer to the newly allocated template type object. If no object *
+ * could be allocated, then NULL is returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/06/1996 JLB : Created. *
+ *=============================================================================================*/
+void * TemplateTypeClass::operator new(size_t)
+{
+ return(TemplateTypes.Alloc());
+}
+
+
+/***********************************************************************************************
+ * TemplateTypeClass::operator delete -- Deletes a template type object. *
+ * *
+ * This routine will return a template type object back to the special heap it was *
+ * allocated from. *
+ * *
+ * INPUT: ptr -- Pointer to the template type object to free. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/06/1996 JLB : Created. *
+ *=============================================================================================*/
+void TemplateTypeClass::operator delete(void * ptr)
+{
+ TemplateTypes.Free((TemplateTypeClass *)ptr);
+}
+
+
+static void _Watcom_Ugh_Hack(void)
+{
+ (void)new TemplateTypeClass(Road37); // TEMPLATE_ROAD37
+ (void)new TemplateTypeClass(Road38); // TEMPLATE_ROAD38
+ (void)new TemplateTypeClass(Road39); // TEMPLATE_ROAD39
+ (void)new TemplateTypeClass(Road40); // TEMPLATE_ROAD40
+ (void)new TemplateTypeClass(Road41); // TEMPLATE_ROAD41
+ (void)new TemplateTypeClass(Road42); // TEMPLATE_ROAD42
+ (void)new TemplateTypeClass(Road43); // TEMPLATE_ROAD43
+ (void)new TemplateTypeClass(Rough01); // TEMPLATE_ROUGH01
+ (void)new TemplateTypeClass(Rough02); // TEMPLATE_ROUGH02
+ (void)new TemplateTypeClass(Rough03); // TEMPLATE_ROUGH03
+ (void)new TemplateTypeClass(Rough04); // TEMPLATE_ROUGH04
+ (void)new TemplateTypeClass(Rough05); // TEMPLATE_ROUGH05
+ (void)new TemplateTypeClass(Rough06); // TEMPLATE_ROUGH06
+ (void)new TemplateTypeClass(Rough07); // TEMPLATE_ROUGH07
+ (void)new TemplateTypeClass(Rough08); // TEMPLATE_ROUGH08
+ (void)new TemplateTypeClass(Rough09); // TEMPLATE_ROUGH09
+ (void)new TemplateTypeClass(Rough10); // TEMPLATE_ROUGH10
+ (void)new TemplateTypeClass(Rough11); // TEMPLATE_ROUGH11
+ (void)new TemplateTypeClass(Road44); // TEMPLATE_ROAD44
+ (void)new TemplateTypeClass(Road45); // TEMPLATE_ROAD45
+ (void)new TemplateTypeClass(River14); // TEMPLATE_RIVER14
+ (void)new TemplateTypeClass(River15); // TEMPLATE_RIVER15
+ (void)new TemplateTypeClass(RiverCliff01); // TEMPLATE_RIVERCLIFF01
+ (void)new TemplateTypeClass(RiverCliff02); // TEMPLATE_RIVERCLIFF02
+ (void)new TemplateTypeClass(RiverCliff03); // TEMPLATE_RIVERCLIFF03
+ (void)new TemplateTypeClass(RiverCliff04); // TEMPLATE_RIVERCLIFF04
+ (void)new TemplateTypeClass(Bridge1a); // TEMPLATE_BRIDGE_1A
+ (void)new TemplateTypeClass(Bridge1b); // TEMPLATE_BRIDGE_1B
+ (void)new TemplateTypeClass(Bridge1c); // TEMPLATE_BRIDGE_1C
+ (void)new TemplateTypeClass(Bridge2a); // TEMPLATE_BRIDGE_2A
+ (void)new TemplateTypeClass(Bridge2b); // TEMPLATE_BRIDGE_2B
+ (void)new TemplateTypeClass(Bridge2c); // TEMPLATE_BRIDGE_2C
+ (void)new TemplateTypeClass(Bridge3a); // TEMPLATE_BRIDGE_3A
+ (void)new TemplateTypeClass(Bridge3b); // TEMPLATE_BRIDGE_3B
+ (void)new TemplateTypeClass(Bridge3c); // TEMPLATE_BRIDGE_3C
+ (void)new TemplateTypeClass(Bridge3d); // TEMPLATE_BRIDGE_3D
+ (void)new TemplateTypeClass(Bridge3e); // TEMPLATE_BRIDGE_3E
+ (void)new TemplateTypeClass(Bridge3f); // TEMPLATE_BRIDGE_3F
+ (void)new TemplateTypeClass(F01); // TEMPLATE_F01
+ (void)new TemplateTypeClass(F02); // TEMPLATE_F02
+ (void)new TemplateTypeClass(F03); // TEMPLATE_F03
+ (void)new TemplateTypeClass(F04); // TEMPLATE_F04
+ (void)new TemplateTypeClass(F05); // TEMPLATE_F05
+ (void)new TemplateTypeClass(F06); // TEMPLATE_F06
+ (void)new TemplateTypeClass(ARRO0001); // TEMPLATE_ARRO0001
+ (void)new TemplateTypeClass(ARRO0002); // TEMPLATE_ARRO0002
+ (void)new TemplateTypeClass(ARRO0003); // TEMPLATE_ARRO0003
+ (void)new TemplateTypeClass(ARRO0004); // TEMPLATE_ARRO0004
+ (void)new TemplateTypeClass(ARRO0005); // TEMPLATE_ARRO0005
+ (void)new TemplateTypeClass(ARRO0006); // TEMPLATE_ARRO0006
+ (void)new TemplateTypeClass(ARRO0007); // TEMPLATE_ARRO0007
+ (void)new TemplateTypeClass(ARRO0008); // TEMPLATE_ARRO0008
+ (void)new TemplateTypeClass(ARRO0009); // TEMPLATE_ARRO0009
+ (void)new TemplateTypeClass(ARRO0010); // TEMPLATE_ARRO0010
+ (void)new TemplateTypeClass(ARRO0011); // TEMPLATE_ARRO0011
+ (void)new TemplateTypeClass(ARRO0012); // TEMPLATE_ARRO0012
+ (void)new TemplateTypeClass(ARRO0013); // TEMPLATE_ARRO0013
+ (void)new TemplateTypeClass(ARRO0014); // TEMPLATE_ARRO0014
+ (void)new TemplateTypeClass(ARRO0015); // TEMPLATE_ARRO0015
+ (void)new TemplateTypeClass(FLOR0001); // TEMPLATE_FLOR0001
+ (void)new TemplateTypeClass(FLOR0002); // TEMPLATE_FLOR0002
+ (void)new TemplateTypeClass(FLOR0003); // TEMPLATE_FLOR0003
+ (void)new TemplateTypeClass(FLOR0004); // TEMPLATE_FLOR0004
+ (void)new TemplateTypeClass(FLOR0005); // TEMPLATE_FLOR0005
+ (void)new TemplateTypeClass(FLOR0006); // TEMPLATE_FLOR0006
+ (void)new TemplateTypeClass(FLOR0007); // TEMPLATE_FLOR0007
+ (void)new TemplateTypeClass(GFLR0001); // TEMPLATE_GFLR0001
+ (void)new TemplateTypeClass(GFLR0002); // TEMPLATE_GFLR0002
+ (void)new TemplateTypeClass(GFLR0003); // TEMPLATE_GFLR0003
+ (void)new TemplateTypeClass(GFLR0004); // TEMPLATE_GFLR0004
+ (void)new TemplateTypeClass(GFLR0005); // TEMPLATE_GFLR0005
+ (void)new TemplateTypeClass(GSTR0001); // TEMPLATE_GSTR0001
+ (void)new TemplateTypeClass(GSTR0002); // TEMPLATE_GSTR0002
+ (void)new TemplateTypeClass(GSTR0003); // TEMPLATE_GSTR0003
+ (void)new TemplateTypeClass(GSTR0004); // TEMPLATE_GSTR0004
+ (void)new TemplateTypeClass(GSTR0005); // TEMPLATE_GSTR0005
+ (void)new TemplateTypeClass(GSTR0006); // TEMPLATE_GSTR0006
+ (void)new TemplateTypeClass(GSTR0007); // TEMPLATE_GSTR0007
+ (void)new TemplateTypeClass(GSTR0008); // TEMPLATE_GSTR0008
+ (void)new TemplateTypeClass(GSTR0009); // TEMPLATE_GSTR0009
+ (void)new TemplateTypeClass(GSTR0010); // TEMPLATE_GSTR0010
+ (void)new TemplateTypeClass(GSTR0011); // TEMPLATE_GSTR0011
+ (void)new TemplateTypeClass(LWAL0001); // TEMPLATE_LWAL0001
+ (void)new TemplateTypeClass(LWAL0002); // TEMPLATE_LWAL0002
+ (void)new TemplateTypeClass(LWAL0003); // TEMPLATE_LWAL0003
+ (void)new TemplateTypeClass(LWAL0004); // TEMPLATE_LWAL0004
+ (void)new TemplateTypeClass(LWAL0005); // TEMPLATE_LWAL0005
+ (void)new TemplateTypeClass(LWAL0006); // TEMPLATE_LWAL0006
+ (void)new TemplateTypeClass(LWAL0007); // TEMPLATE_LWAL0007
+ (void)new TemplateTypeClass(LWAL0008); // TEMPLATE_LWAL0008
+ (void)new TemplateTypeClass(LWAL0009); // TEMPLATE_LWAL0009
+ (void)new TemplateTypeClass(LWAL0010); // TEMPLATE_LWAL0010
+ (void)new TemplateTypeClass(LWAL0011); // TEMPLATE_LWAL0011
+ (void)new TemplateTypeClass(LWAL0012); // TEMPLATE_LWAL0012
+ (void)new TemplateTypeClass(LWAL0013); // TEMPLATE_LWAL0013
+ (void)new TemplateTypeClass(LWAL0014); // TEMPLATE_LWAL0014
+ (void)new TemplateTypeClass(LWAL0015); // TEMPLATE_LWAL0015
+ (void)new TemplateTypeClass(LWAL0016); // TEMPLATE_LWAL0016
+ (void)new TemplateTypeClass(LWAL0017); // TEMPLATE_LWAL0017
+ (void)new TemplateTypeClass(LWAL0018); // TEMPLATE_LWAL0018
+ (void)new TemplateTypeClass(LWAL0019); // TEMPLATE_LWAL0019
+ (void)new TemplateTypeClass(LWAL0020); // TEMPLATE_LWAL0020
+ (void)new TemplateTypeClass(LWAL0021); // TEMPLATE_LWAL0021
+ (void)new TemplateTypeClass(LWAL0022); // TEMPLATE_LWAL0022
+ (void)new TemplateTypeClass(LWAL0023); // TEMPLATE_LWAL0023
+ (void)new TemplateTypeClass(LWAL0024); // TEMPLATE_LWAL0024
+ (void)new TemplateTypeClass(LWAL0025); // TEMPLATE_LWAL0025
+ (void)new TemplateTypeClass(LWAL0026); // TEMPLATE_LWAL0026
+ (void)new TemplateTypeClass(LWAL0027); // TEMPLATE_LWAL0027
+ (void)new TemplateTypeClass(STRP0001); // TEMPLATE_STRP0001
+ (void)new TemplateTypeClass(STRP0002); // TEMPLATE_STRP0002
+ (void)new TemplateTypeClass(STRP0003); // TEMPLATE_STRP0003
+ (void)new TemplateTypeClass(STRP0004); // TEMPLATE_STRP0004
+ (void)new TemplateTypeClass(STRP0005); // TEMPLATE_STRP0005
+ (void)new TemplateTypeClass(STRP0006); // TEMPLATE_STRP0006
+ (void)new TemplateTypeClass(STRP0007); // TEMPLATE_STRP0007
+ (void)new TemplateTypeClass(STRP0008); // TEMPLATE_STRP0008
+ (void)new TemplateTypeClass(STRP0009); // TEMPLATE_STRP0009
+ (void)new TemplateTypeClass(STRP0010); // TEMPLATE_STRP0010
+ (void)new TemplateTypeClass(STRP0011); // TEMPLATE_STRP0011
+ (void)new TemplateTypeClass(WALL0001); // TEMPLATE_WALL0001
+ (void)new TemplateTypeClass(WALL0002); // TEMPLATE_WALL0002
+ (void)new TemplateTypeClass(WALL0003); // TEMPLATE_WALL0003
+ (void)new TemplateTypeClass(WALL0004); // TEMPLATE_WALL0004
+ (void)new TemplateTypeClass(WALL0005); // TEMPLATE_WALL0005
+ (void)new TemplateTypeClass(WALL0006); // TEMPLATE_WALL0006
+ (void)new TemplateTypeClass(WALL0007); // TEMPLATE_WALL0007
+ (void)new TemplateTypeClass(WALL0008); // TEMPLATE_WALL0008
+ (void)new TemplateTypeClass(WALL0009); // TEMPLATE_WALL0009
+ (void)new TemplateTypeClass(WALL0010); // TEMPLATE_WALL0010
+ (void)new TemplateTypeClass(WALL0011); // TEMPLATE_WALL0011
+ (void)new TemplateTypeClass(WALL0012); // TEMPLATE_WALL0012
+ (void)new TemplateTypeClass(WALL0013); // TEMPLATE_WALL0013
+ (void)new TemplateTypeClass(WALL0014); // TEMPLATE_WALL0014
+ (void)new TemplateTypeClass(WALL0015); // TEMPLATE_WALL0015
+ (void)new TemplateTypeClass(WALL0016); // TEMPLATE_WALL0016
+ (void)new TemplateTypeClass(WALL0017); // TEMPLATE_WALL0017
+ (void)new TemplateTypeClass(WALL0018); // TEMPLATE_WALL0018
+ (void)new TemplateTypeClass(WALL0019); // TEMPLATE_WALL0019
+ (void)new TemplateTypeClass(WALL0020); // TEMPLATE_WALL0020
+ (void)new TemplateTypeClass(WALL0021); // TEMPLATE_WALL0021
+ (void)new TemplateTypeClass(WALL0022); // TEMPLATE_WALL0022
+ (void)new TemplateTypeClass(WALL0023); // TEMPLATE_WALL0023
+ (void)new TemplateTypeClass(WALL0024); // TEMPLATE_WALL0024
+ (void)new TemplateTypeClass(WALL0025); // TEMPLATE_WALL0025
+ (void)new TemplateTypeClass(WALL0026); // TEMPLATE_WALL0026
+ (void)new TemplateTypeClass(WALL0027); // TEMPLATE_WALL0027
+ (void)new TemplateTypeClass(WALL0028); // TEMPLATE_WALL0028
+ (void)new TemplateTypeClass(WALL0029); // TEMPLATE_WALL0029
+ (void)new TemplateTypeClass(WALL0030); // TEMPLATE_WALL0030
+ (void)new TemplateTypeClass(WALL0031); // TEMPLATE_WALL0031
+ (void)new TemplateTypeClass(WALL0032); // TEMPLATE_WALL0032
+ (void)new TemplateTypeClass(WALL0033); // TEMPLATE_WALL0033
+ (void)new TemplateTypeClass(WALL0034); // TEMPLATE_WALL0034
+ (void)new TemplateTypeClass(WALL0035); // TEMPLATE_WALL0035
+ (void)new TemplateTypeClass(WALL0036); // TEMPLATE_WALL0036
+ (void)new TemplateTypeClass(WALL0037); // TEMPLATE_WALL0037
+ (void)new TemplateTypeClass(WALL0038); // TEMPLATE_WALL0038
+ (void)new TemplateTypeClass(WALL0039); // TEMPLATE_WALL0039
+ (void)new TemplateTypeClass(WALL0040); // TEMPLATE_WALL0040
+ (void)new TemplateTypeClass(WALL0041); // TEMPLATE_WALL0041
+ (void)new TemplateTypeClass(WALL0042); // TEMPLATE_WALL0042
+ (void)new TemplateTypeClass(WALL0043); // TEMPLATE_WALL0043
+ (void)new TemplateTypeClass(WALL0044); // TEMPLATE_WALL0044
+ (void)new TemplateTypeClass(WALL0045); // TEMPLATE_WALL0045
+ (void)new TemplateTypeClass(WALL0046); // TEMPLATE_WALL0046
+ (void)new TemplateTypeClass(WALL0047); // TEMPLATE_WALL0047
+ (void)new TemplateTypeClass(WALL0048); // TEMPLATE_WALL0048
+ (void)new TemplateTypeClass(WALL0049); // TEMPLATE_WALL0049
+ (void)new TemplateTypeClass(Bridge1h); // TEMPLATE_BRIDGE1H
+ (void)new TemplateTypeClass(Bridge2h); // TEMPLATE_BRIDGE2H
+
+ (void)new TemplateTypeClass(Bridge1ax); // TEMPLATE_BRIDGE_1AX
+ (void)new TemplateTypeClass(Bridge2ax); // TEMPLATE_BRIDGE_2AX
+ (void)new TemplateTypeClass(Bridge1x); // TEMPLATE_BRIDGE1X
+ (void)new TemplateTypeClass(Bridge2x); // TEMPLATE_BRIDGE2X
+
+ (void)new TemplateTypeClass(Xtra0001); // TEMPLATE_XTRA0001
+ (void)new TemplateTypeClass(Xtra0002); // TEMPLATE_XTRA0002
+ (void)new TemplateTypeClass(Xtra0003); // TEMPLATE_XTRA0003
+ (void)new TemplateTypeClass(Xtra0004); // TEMPLATE_XTRA0004
+ (void)new TemplateTypeClass(Xtra0005); // TEMPLATE_XTRA0005
+ (void)new TemplateTypeClass(Xtra0006); // TEMPLATE_XTRA0006
+ (void)new TemplateTypeClass(Xtra0007); // TEMPLATE_XTRA0007
+ (void)new TemplateTypeClass(Xtra0008); // TEMPLATE_XTRA0008
+ (void)new TemplateTypeClass(Xtra0009); // TEMPLATE_XTRA0009
+ (void)new TemplateTypeClass(Xtra0010); // TEMPLATE_XTRA0010
+ (void)new TemplateTypeClass(Xtra0011); // TEMPLATE_XTRA0011
+ (void)new TemplateTypeClass(Xtra0012); // TEMPLATE_XTRA0012
+ (void)new TemplateTypeClass(Xtra0013); // TEMPLATE_XTRA0013
+ (void)new TemplateTypeClass(Xtra0014); // TEMPLATE_XTRA0014
+ (void)new TemplateTypeClass(Xtra0015); // TEMPLATE_XTRA0015
+ (void)new TemplateTypeClass(Xtra0016); // TEMPLATE_XTRA0016
+
+#ifdef FIXIT_ANTS
+ (void)new TemplateTypeClass(AntHill); // TEMPLATE_ROAD36
+#endif
+}
+
+
+
+void TemplateTypeClass::Init_Heap(void)
+{
+ /*
+ ** These template type class objects must be allocated in the exact order that they
+ ** are specified in the TemplateType enumeration. This is necessary because the heap
+ ** allocation block index serves double duty as the type number index.
+ */
+ (void)new TemplateTypeClass(Clear); // TEMPLATE_CLEAR1
+ (void)new TemplateTypeClass(Water); // TEMPLATE_WATER
+ (void)new TemplateTypeClass(Water2); // TEMPLATE_WATER2
+ (void)new TemplateTypeClass(Shore01); // TEMPLATE_SHORE1
+ (void)new TemplateTypeClass(Shore02); // TEMPLATE_SHORE2
+ (void)new TemplateTypeClass(Shore03); // TEMPLATE_SHORE3
+ (void)new TemplateTypeClass(Shore04); // TEMPLATE_SHORE4
+ (void)new TemplateTypeClass(Shore05); // TEMPLATE_SHORE5
+ (void)new TemplateTypeClass(Shore06); // TEMPLATE_SHORE6
+ (void)new TemplateTypeClass(Shore07); // TEMPLATE_SHORE7
+ (void)new TemplateTypeClass(Shore08); // TEMPLATE_SHORE8
+ (void)new TemplateTypeClass(Shore09); // TEMPLATE_SHORE9
+ (void)new TemplateTypeClass(Shore10); // TEMPLATE_SHORE10
+ (void)new TemplateTypeClass(Shore11); // TEMPLATE_SHORE11
+ (void)new TemplateTypeClass(Shore12); // TEMPLATE_SHORE12
+ (void)new TemplateTypeClass(Shore13); // TEMPLATE_SHORE13
+ (void)new TemplateTypeClass(Shore14); // TEMPLATE_SHORE14
+ (void)new TemplateTypeClass(Shore15); // TEMPLATE_SHORE15
+ (void)new TemplateTypeClass(Shore16); // TEMPLATE_SHORE16
+ (void)new TemplateTypeClass(Shore17); // TEMPLATE_SHORE17
+ (void)new TemplateTypeClass(Shore18); // TEMPLATE_SHORE18
+ (void)new TemplateTypeClass(Shore19); // TEMPLATE_SHORE19
+ (void)new TemplateTypeClass(Shore20); // TEMPLATE_SHORE20
+ (void)new TemplateTypeClass(Shore21); // TEMPLATE_SHORE21
+ (void)new TemplateTypeClass(Shore22); // TEMPLATE_SHORE22
+ (void)new TemplateTypeClass(Shore23); // TEMPLATE_SHORE23
+ (void)new TemplateTypeClass(Shore24); // TEMPLATE_SHORE24
+ (void)new TemplateTypeClass(Shore25); // TEMPLATE_SHORE25
+ (void)new TemplateTypeClass(Shore26); // TEMPLATE_SHORE26
+ (void)new TemplateTypeClass(Shore27); // TEMPLATE_SHORE27
+ (void)new TemplateTypeClass(Shore28); // TEMPLATE_SHORE28
+ (void)new TemplateTypeClass(Shore29); // TEMPLATE_SHORE29
+ (void)new TemplateTypeClass(Shore30); // TEMPLATE_SHORE30
+ (void)new TemplateTypeClass(Shore31); // TEMPLATE_SHORE31
+ (void)new TemplateTypeClass(Shore32); // TEMPLATE_SHORE32
+ (void)new TemplateTypeClass(Shore33); // TEMPLATE_SHORE33
+ (void)new TemplateTypeClass(Shore34); // TEMPLATE_SHORE34
+ (void)new TemplateTypeClass(Shore35); // TEMPLATE_SHORE35
+ (void)new TemplateTypeClass(Shore36); // TEMPLATE_SHORE36
+ (void)new TemplateTypeClass(Shore37); // TEMPLATE_SHORE37
+ (void)new TemplateTypeClass(Shore38); // TEMPLATE_SHORE38
+ (void)new TemplateTypeClass(Shore39); // TEMPLATE_SHORE39
+ (void)new TemplateTypeClass(Shore40); // TEMPLATE_SHORE40
+ (void)new TemplateTypeClass(Shore41); // TEMPLATE_SHORE41
+ (void)new TemplateTypeClass(Shore42); // TEMPLATE_SHORE42
+ (void)new TemplateTypeClass(Shore43); // TEMPLATE_SHORE43
+ (void)new TemplateTypeClass(Shore44); // TEMPLATE_SHORE44
+ (void)new TemplateTypeClass(Shore45); // TEMPLATE_SHORE45
+ (void)new TemplateTypeClass(Shore46); // TEMPLATE_SHORE46
+ (void)new TemplateTypeClass(Shore47); // TEMPLATE_SHORE47
+ (void)new TemplateTypeClass(Shore48); // TEMPLATE_SHORE48
+ (void)new TemplateTypeClass(Shore49); // TEMPLATE_SHORE49
+ (void)new TemplateTypeClass(Shore50); // TEMPLATE_SHORE50
+ (void)new TemplateTypeClass(Shore51); // TEMPLATE_SHORE51
+ (void)new TemplateTypeClass(Shore52); // TEMPLATE_SHORE52
+ (void)new TemplateTypeClass(Shore53); // TEMPLATE_SHORE53
+ (void)new TemplateTypeClass(Shore54); // TEMPLATE_SHORE54
+ (void)new TemplateTypeClass(Shore55); // TEMPLATE_SHORE55
+ (void)new TemplateTypeClass(Shore56); // TEMPLATE_SHORE56
+ (void)new TemplateTypeClass(ShoreCliff01); // TEMPLATE_SHORECLIFF01
+ (void)new TemplateTypeClass(ShoreCliff02); // TEMPLATE_SHORECLIFF02
+ (void)new TemplateTypeClass(ShoreCliff03); // TEMPLATE_SHORECLIFF03
+ (void)new TemplateTypeClass(ShoreCliff04); // TEMPLATE_SHORECLIFF04
+ (void)new TemplateTypeClass(ShoreCliff05); // TEMPLATE_SHORECLIFF05
+ (void)new TemplateTypeClass(ShoreCliff06); // TEMPLATE_SHORECLIFF06
+ (void)new TemplateTypeClass(ShoreCliff07); // TEMPLATE_SHORECLIFF07
+ (void)new TemplateTypeClass(ShoreCliff08); // TEMPLATE_SHORECLIFF08
+ (void)new TemplateTypeClass(ShoreCliff09); // TEMPLATE_SHORECLIFF09
+ (void)new TemplateTypeClass(ShoreCliff10); // TEMPLATE_SHORECLIFF10
+ (void)new TemplateTypeClass(ShoreCliff11); // TEMPLATE_SHORECLIFF11
+ (void)new TemplateTypeClass(ShoreCliff12); // TEMPLATE_SHORECLIFF12
+ (void)new TemplateTypeClass(ShoreCliff13); // TEMPLATE_SHORECLIFF13
+ (void)new TemplateTypeClass(ShoreCliff14); // TEMPLATE_SHORECLIFF14
+ (void)new TemplateTypeClass(ShoreCliff15); // TEMPLATE_SHORECLIFF15
+ (void)new TemplateTypeClass(ShoreCliff16); // TEMPLATE_SHORECLIFF16
+ (void)new TemplateTypeClass(ShoreCliff17); // TEMPLATE_SHORECLIFF17
+ (void)new TemplateTypeClass(ShoreCliff18); // TEMPLATE_SHORECLIFF18
+ (void)new TemplateTypeClass(ShoreCliff19); // TEMPLATE_SHORECLIFF19
+ (void)new TemplateTypeClass(ShoreCliff20); // TEMPLATE_SHORECLIFF20
+ (void)new TemplateTypeClass(ShoreCliff21); // TEMPLATE_SHORECLIFF21
+ (void)new TemplateTypeClass(ShoreCliff22); // TEMPLATE_SHORECLIFF22
+ (void)new TemplateTypeClass(ShoreCliff23); // TEMPLATE_SHORECLIFF23
+ (void)new TemplateTypeClass(ShoreCliff24); // TEMPLATE_SHORECLIFF24
+ (void)new TemplateTypeClass(ShoreCliff25); // TEMPLATE_SHORECLIFF25
+ (void)new TemplateTypeClass(ShoreCliff26); // TEMPLATE_SHORECLIFF26
+ (void)new TemplateTypeClass(ShoreCliff27); // TEMPLATE_SHORECLIFF27
+ (void)new TemplateTypeClass(ShoreCliff28); // TEMPLATE_SHORECLIFF28
+ (void)new TemplateTypeClass(ShoreCliff29); // TEMPLATE_SHORECLIFF29
+ (void)new TemplateTypeClass(ShoreCliff30); // TEMPLATE_SHORECLIFF30
+ (void)new TemplateTypeClass(ShoreCliff31); // TEMPLATE_SHORECLIFF31
+ (void)new TemplateTypeClass(ShoreCliff32); // TEMPLATE_SHORECLIFF32
+ (void)new TemplateTypeClass(ShoreCliff33); // TEMPLATE_SHORECLIFF33
+ (void)new TemplateTypeClass(ShoreCliff34); // TEMPLATE_SHORECLIFF34
+ (void)new TemplateTypeClass(ShoreCliff35); // TEMPLATE_SHORECLIFF35
+ (void)new TemplateTypeClass(ShoreCliff36); // TEMPLATE_SHORECLIFF36
+ (void)new TemplateTypeClass(ShoreCliff37); // TEMPLATE_SHORECLIFF37
+ (void)new TemplateTypeClass(ShoreCliff38); // TEMPLATE_SHORECLIFF38
+ (void)new TemplateTypeClass(Boulder1); // TEMPLATE_BOULDER1
+ (void)new TemplateTypeClass(Boulder2); // TEMPLATE_BOULDER2
+ (void)new TemplateTypeClass(Boulder3); // TEMPLATE_BOULDER3
+ (void)new TemplateTypeClass(Boulder4); // TEMPLATE_BOULDER4
+ (void)new TemplateTypeClass(Boulder5); // TEMPLATE_BOULDER5
+ (void)new TemplateTypeClass(Boulder6); // TEMPLATE_BOULDER6
+ (void)new TemplateTypeClass(Patch01); // TEMPLATE_PATCH1
+ (void)new TemplateTypeClass(Patch02); // TEMPLATE_PATCH2
+ (void)new TemplateTypeClass(Patch03); // TEMPLATE_PATCH3
+ (void)new TemplateTypeClass(Patch04); // TEMPLATE_PATCH4
+ (void)new TemplateTypeClass(Patch07); // TEMPLATE_PATCH7
+ (void)new TemplateTypeClass(Patch08); // TEMPLATE_PATCH8
+ (void)new TemplateTypeClass(Patch13); // TEMPLATE_PATCH13
+ (void)new TemplateTypeClass(Patch14); // TEMPLATE_PATCH14
+ (void)new TemplateTypeClass(Patch15); // TEMPLATE_PATCH15
+ (void)new TemplateTypeClass(River01); // TEMPLATE_RIVER1
+ (void)new TemplateTypeClass(River02); // TEMPLATE_RIVER2
+ (void)new TemplateTypeClass(River03); // TEMPLATE_RIVER3
+ (void)new TemplateTypeClass(River04); // TEMPLATE_RIVER4
+ (void)new TemplateTypeClass(River05); // TEMPLATE_RIVER5
+ (void)new TemplateTypeClass(River06); // TEMPLATE_RIVER6
+ (void)new TemplateTypeClass(River07); // TEMPLATE_RIVER7
+ (void)new TemplateTypeClass(River08); // TEMPLATE_RIVER8
+ (void)new TemplateTypeClass(River09); // TEMPLATE_RIVER9
+ (void)new TemplateTypeClass(River10); // TEMPLATE_RIVER10
+ (void)new TemplateTypeClass(River11); // TEMPLATE_RIVER11
+ (void)new TemplateTypeClass(River12); // TEMPLATE_RIVER12
+ (void)new TemplateTypeClass(River13); // TEMPLATE_RIVER13
+ (void)new TemplateTypeClass(Falls1); // TEMPLATE_FALLS1
+ (void)new TemplateTypeClass(Falls1a); // TEMPLATE_FALLS1A
+ (void)new TemplateTypeClass(Falls2); // TEMPLATE_FALLS2
+ (void)new TemplateTypeClass(Falls2a); // TEMPLATE_FALLS2A
+ (void)new TemplateTypeClass(Ford1); // TEMPLATE_FORD1
+ (void)new TemplateTypeClass(Ford2); // TEMPLATE_FORD2
+ (void)new TemplateTypeClass(Bridge1); // TEMPLATE_BRIDGE1
+ (void)new TemplateTypeClass(Bridge1d); // TEMPLATE_BRIDGE1D
+ (void)new TemplateTypeClass(Bridge2); // TEMPLATE_BRIDGE2
+ (void)new TemplateTypeClass(Bridge2d); // TEMPLATE_BRIDGE2D
+ (void)new TemplateTypeClass(Slope01); // TEMPLATE_SLOPE1
+ (void)new TemplateTypeClass(Slope02); // TEMPLATE_SLOPE2
+ (void)new TemplateTypeClass(Slope03); // TEMPLATE_SLOPE3
+ (void)new TemplateTypeClass(Slope04); // TEMPLATE_SLOPE4
+ (void)new TemplateTypeClass(Slope05); // TEMPLATE_SLOPE5
+ (void)new TemplateTypeClass(Slope06); // TEMPLATE_SLOPE6
+ (void)new TemplateTypeClass(Slope07); // TEMPLATE_SLOPE7
+ (void)new TemplateTypeClass(Slope08); // TEMPLATE_SLOPE8
+ (void)new TemplateTypeClass(Slope09); // TEMPLATE_SLOPE9
+ (void)new TemplateTypeClass(Slope10); // TEMPLATE_SLOPE10
+ (void)new TemplateTypeClass(Slope11); // TEMPLATE_SLOPE11
+ (void)new TemplateTypeClass(Slope12); // TEMPLATE_SLOPE12
+ (void)new TemplateTypeClass(Slope13); // TEMPLATE_SLOPE13
+ (void)new TemplateTypeClass(Slope14); // TEMPLATE_SLOPE14
+ (void)new TemplateTypeClass(Slope15); // TEMPLATE_SLOPE15
+ (void)new TemplateTypeClass(Slope16); // TEMPLATE_SLOPE16
+ (void)new TemplateTypeClass(Slope17); // TEMPLATE_SLOPE17
+ (void)new TemplateTypeClass(Slope18); // TEMPLATE_SLOPE18
+ (void)new TemplateTypeClass(Slope19); // TEMPLATE_SLOPE19
+ (void)new TemplateTypeClass(Slope20); // TEMPLATE_SLOPE20
+ (void)new TemplateTypeClass(Slope21); // TEMPLATE_SLOPE21
+ (void)new TemplateTypeClass(Slope22); // TEMPLATE_SLOPE22
+ (void)new TemplateTypeClass(Slope23); // TEMPLATE_SLOPE23
+ (void)new TemplateTypeClass(Slope24); // TEMPLATE_SLOPE24
+ (void)new TemplateTypeClass(Slope25); // TEMPLATE_SLOPE25
+ (void)new TemplateTypeClass(Slope26); // TEMPLATE_SLOPE26
+ (void)new TemplateTypeClass(Slope27); // TEMPLATE_SLOPE27
+ (void)new TemplateTypeClass(Slope28); // TEMPLATE_SLOPE28
+ (void)new TemplateTypeClass(Slope29); // TEMPLATE_SLOPE29
+ (void)new TemplateTypeClass(Slope30); // TEMPLATE_SLOPE30
+ (void)new TemplateTypeClass(Slope31); // TEMPLATE_SLOPE31
+ (void)new TemplateTypeClass(Slope32); // TEMPLATE_SLOPE32
+ (void)new TemplateTypeClass(Slope33); // TEMPLATE_SLOPE33
+ (void)new TemplateTypeClass(Slope34); // TEMPLATE_SLOPE34
+ (void)new TemplateTypeClass(Slope35); // TEMPLATE_SLOPE35
+ (void)new TemplateTypeClass(Slope36); // TEMPLATE_SLOPE36
+ (void)new TemplateTypeClass(Slope37); // TEMPLATE_SLOPE37
+ (void)new TemplateTypeClass(Slope38); // TEMPLATE_SLOPE38
+ (void)new TemplateTypeClass(Road01); // TEMPLATE_ROAD1
+ (void)new TemplateTypeClass(Road02); // TEMPLATE_ROAD2
+ (void)new TemplateTypeClass(Road03); // TEMPLATE_ROAD3
+ (void)new TemplateTypeClass(Road04); // TEMPLATE_ROAD4
+ (void)new TemplateTypeClass(Road05); // TEMPLATE_ROAD5
+ (void)new TemplateTypeClass(Road06); // TEMPLATE_ROAD6
+ (void)new TemplateTypeClass(Road07); // TEMPLATE_ROAD7
+ (void)new TemplateTypeClass(Road08); // TEMPLATE_ROAD8
+ (void)new TemplateTypeClass(Road09); // TEMPLATE_ROAD9
+ (void)new TemplateTypeClass(Road10); // TEMPLATE_ROAD10
+ (void)new TemplateTypeClass(Road11); // TEMPLATE_ROAD11
+ (void)new TemplateTypeClass(Road12); // TEMPLATE_ROAD12
+ (void)new TemplateTypeClass(Road13); // TEMPLATE_ROAD13
+ (void)new TemplateTypeClass(Road14); // TEMPLATE_ROAD14
+ (void)new TemplateTypeClass(Road15); // TEMPLATE_ROAD15
+ (void)new TemplateTypeClass(Road16); // TEMPLATE_ROAD16
+ (void)new TemplateTypeClass(Road17); // TEMPLATE_ROAD17
+ (void)new TemplateTypeClass(Road18); // TEMPLATE_ROAD18
+ (void)new TemplateTypeClass(Road19); // TEMPLATE_ROAD19
+ (void)new TemplateTypeClass(Road20); // TEMPLATE_ROAD20
+ (void)new TemplateTypeClass(Road21); // TEMPLATE_ROAD21
+ (void)new TemplateTypeClass(Road22); // TEMPLATE_ROAD22
+ (void)new TemplateTypeClass(Road23); // TEMPLATE_ROAD23
+ (void)new TemplateTypeClass(Road24); // TEMPLATE_ROAD24
+ (void)new TemplateTypeClass(Road25); // TEMPLATE_ROAD25
+ (void)new TemplateTypeClass(Road26); // TEMPLATE_ROAD26
+ (void)new TemplateTypeClass(Road27); // TEMPLATE_ROAD27
+ (void)new TemplateTypeClass(Road28); // TEMPLATE_ROAD28
+ (void)new TemplateTypeClass(Road29); // TEMPLATE_ROAD29
+ (void)new TemplateTypeClass(Road30); // TEMPLATE_ROAD30
+ (void)new TemplateTypeClass(Road31); // TEMPLATE_ROAD31
+ (void)new TemplateTypeClass(Road32); // TEMPLATE_ROAD32
+ (void)new TemplateTypeClass(Road33); // TEMPLATE_ROAD33
+ (void)new TemplateTypeClass(Road34); // TEMPLATE_ROAD34
+ (void)new TemplateTypeClass(Road35); // TEMPLATE_ROAD35
+ (void)new TemplateTypeClass(Road36); // TEMPLATE_ROAD36
+
+ /*
+ ** Separate out the list of new operator calls. Watcom bombs
+ ** if they are kept together.
+ */
+ _Watcom_Ugh_Hack();
+}
+
+
+/***********************************************************************************************
+ * TemplateTypeClass::Land_Type -- Determines land type from template and icon number. *
+ * *
+ * This routine will convert the specified icon number into the appropriate land type. The *
+ * land type can be determined from the embedded colors in the "control template" section *
+ * of the original art file. This control information is encoded into the icon data file *
+ * to be retrieved and interpreted as the program sees fit. The engine only recognizes *
+ * the first 16 colors as control colors, so the control map color value serves as an *
+ * index into a simple lookup table. *
+ * *
+ * INPUT: icon -- The icon number within this template that is to be examined and used *
+ * to determine the land type. *
+ * *
+ * OUTPUT: Returns with the land type that corresponds to the icon number specified. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 12/12/1995 JLB : Created. *
+ *=============================================================================================*/
+LandType TemplateTypeClass::Land_Type(int icon) const
+{
+ IconsetClass const * icontrol = (IconsetClass const *)Get_Image_Data();
+
+ if (icontrol != NULL) {
+ unsigned char const * map = icontrol->Control_Map();
+ if (map != NULL) {
+ static LandType _land[16] = {
+ LAND_CLEAR,
+ LAND_CLEAR,
+ LAND_CLEAR,
+ LAND_CLEAR, // Clear
+ LAND_CLEAR,
+ LAND_CLEAR,
+ LAND_BEACH, // Beach
+ LAND_CLEAR,
+ LAND_ROCK, // Rock
+ LAND_ROAD, // Road
+ LAND_WATER, // Water
+ LAND_RIVER, // River
+ LAND_CLEAR,
+ LAND_CLEAR,
+ LAND_ROUGH, // Rough
+ LAND_CLEAR,
+ };
+
+ return(_land[map[icon % (icontrol->Map_Width() * icontrol->Map_Height())]]);
+ }
+ }
+ return(LAND_CLEAR);
+}
+
+
+/***********************************************************************************************
+ * TemplateTypeClass::From_Name -- Determine template from ASCII name. *
+ * *
+ * This routine is used to determine the template number given only *
+ * an ASCII representation. The scenario loader uses this routine *
+ * to construct the map from the INI control file. *
+ * *
+ * INPUT: name -- Pointer to the ASCII name of the template. *
+ * *
+ * OUTPUT: Returns with the template number. If the name had no match, *
+ * then returns with TEMPLATE_NONE. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/23/1994 JLB : Created. *
+ *=============================================================================================*/
+TemplateType TemplateTypeClass::From_Name(char const * name)
+{
+ if (name != NULL) {
+ for (TemplateType index = TEMPLATE_FIRST; index < TEMPLATE_COUNT; index++) {
+ if (stricmp(As_Reference(index).IniName, name) == 0) {
+ return(index);
+ }
+ }
+ }
+ return(TEMPLATE_NONE);
+}
+
+
+/***********************************************************************************************
+ * TemplateTypeClass::Occupy_List -- Determines occupation list. *
+ * *
+ * This routine is used to examine the template map and build an *
+ * occupation list. This list is used to render a template cursor as *
+ * well as placement of icon numbers. *
+ * *
+ * INPUT: placement -- Is this for placement legality checking only? The normal condition *
+ * is for marking occupation flags. *
+ * *
+ * OUTPUT: Returns with a pointer to the template occupation list. *
+ * *
+ * WARNINGS: The return pointer is valid only until the next time that *
+ * this routine is called. *
+ * *
+ * HISTORY: *
+ * 05/23/1994 JLB : Created. *
+ * 12/12/1995 JLB : Optimized for direct access to iconset data. *
+ *=============================================================================================*/
+short const * TemplateTypeClass::Occupy_List(bool) const
+{
+ static short _occupy[13*8+5];
+ short * ptr;
+
+ IconsetClass const * iconset = (IconsetClass const *)Get_Image_Data();
+ unsigned char const * map = iconset->Map_Data();
+
+ ptr = &_occupy[0];
+ for (int index = 0; index < Width * Height; index++) {
+ if (*map++ != 0xFF) {
+ *ptr++ = (index % Width) + ((index / Width)*MAP_CELL_W);
+ }
+ }
+ *ptr = REFRESH_EOL;
+
+ return((short const *)&_occupy[0]);
+}
+
+
+/***********************************************************************************************
+ * TemplateTypeClass::Init -- Loads graphic data for templates. *
+ * *
+ * This routine loads the template graphic data for all the template *
+ * type supported for the specified theater. This routine is called *
+ * whenever the theater for the scenario is first determined. *
+ * *
+ * INPUT: theater -- The theater that the template data is to be *
+ * loaded for. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: This routine goes to disk! *
+ * *
+ * HISTORY: *
+ * 05/23/1994 JLB : Created. *
+ * 06/02/1994 JLB : Only handles iconset loading now (as it should). *
+ *=============================================================================================*/
+void TemplateTypeClass::Init(TheaterType theater)
+{
+ char fullname[_MAX_FNAME+_MAX_EXT]; // Fully constructed iconset name.
+ void const * ptr; // Working loaded iconset pointer.
+
+ for (TemplateType index = TEMPLATE_FIRST; index < TEMPLATE_COUNT; index++) {
+ TemplateTypeClass const & tplate = As_Reference(index);
+
+ ((void const *&)tplate.ImageData) = NULL;
+ if (tplate.Theater & (1< 3 || h > 3);
+ if (scale) {
+ x -= (w * ICON_PIXEL_W) / 4;
+ y -= (h * ICON_PIXEL_H) / 4;
+ } else {
+ x -= (w * ICON_PIXEL_W) / 2;
+ y -= (h * ICON_PIXEL_H) / 2;
+ }
+ x += WindowList[window][WINDOWX];
+ y += WindowList[window][WINDOWY];
+
+ IconsetClass const * iconset = (IconsetClass const *)Get_Image_Data();
+ unsigned char const * map = iconset->Map_Data();
+
+ for (index = 0; index < w*h; index++) {
+ if (map[index] != 0xFF) {
+ HidPage.Draw_Stamp(iconset, index, 0, 0, NULL, WINDOW_MAIN);
+ if (scale) {
+
+ HidPage.Scale((*LogicPage), 0, 0,
+ x + ((index % w)*(ICON_PIXEL_W/2)),
+ y + ((index / w)*(ICON_PIXEL_H/2)),
+ ICON_PIXEL_W, ICON_PIXEL_H,
+ ICON_PIXEL_W/2, ICON_PIXEL_H/2, (char *)NULL);
+
+ } else {
+ HidPage.Blit((*LogicPage), 0, 0, x + ((index % w)*(ICON_PIXEL_W)),
+ y + ((index / w)*(ICON_PIXEL_H)), ICON_PIXEL_W, ICON_PIXEL_H);
+ }
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * TemplateTypeClass::Prep_For_Add -- Prepares to add template to scenario. *
+ * *
+ * This routine prepares a list of template objects so that the *
+ * scenario editor can use this list to display a dialog box. The *
+ * selection of a template object will allow its placement upon the *
+ * map. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/23/1994 JLB : Created. *
+ * 05/28/1994 JLB : Only handles real templates now. *
+ * 06/04/1994 JLB : Uses map editing interface functions. *
+ *=============================================================================================*/
+void TemplateTypeClass::Prep_For_Add(void)
+{
+ for (TemplateType index = TEMPLATE_CLEAR1; index < TEMPLATE_COUNT; index++) {
+ if (As_Reference(index).Get_Image_Data()) {
+ Map.Add_To_List(&As_Reference(index));
+ }
+ }
+}
+#endif
+
+
+/***********************************************************************************************
+ * TemplateTypeClass::Create_And_Place -- Creates and places a template object on the map. *
+ * *
+ * This support routine is used by the scenario editor to add a template object to the map *
+ * and to the game. *
+ * *
+ * INPUT: cell -- The cell to place the template object. *
+ * *
+ * OUTPUT: bool; Was the template object placed successfully? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/28/1994 JLB : Created. *
+ *=============================================================================================*/
+bool TemplateTypeClass::Create_And_Place(CELL cell, HousesType ) const
+{
+ if (new TemplateClass(Type, cell)) {
+ return(true);
+ }
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * TemplateTypeClass::Create_One_Of -- Creates an object of this template type. *
+ * *
+ * This routine will create an object of this type. For certain template objects, such *
+ * as walls, it is actually created as a building. The "building" wall is converted into *
+ * a template at the moment of placing down on the map. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with a pointer to the appropriate object for this template type. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/18/1994 JLB : Created. *
+ *=============================================================================================*/
+ObjectClass * TemplateTypeClass::Create_One_Of(HouseClass *) const
+{
+ return(new TemplateClass(Type, -1));
+}
+
+
+/***********************************************************************************************
+ * TemplateTypeClass::One_Time -- Performs one-time initialization *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/12/1994 JLB : Created. *
+ *=============================================================================================*/
+void TemplateTypeClass::One_Time(void)
+{
+}
+
+
+/***********************************************************************************************
+ * TemplateTypeClass::As_Reference -- Fetches a reference to the template specified. *
+ * *
+ * This will return a reference to the TemplateTypeClass requested. *
+ * *
+ * INPUT: type -- The template type to fetch a reference to. *
+ * *
+ * OUTPUT: Returns with a reference to the template type class specified. *
+ * *
+ * WARNINGS: Be sure to pass a valid type parameter. This routine doesn't check it for *
+ * legality. *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+TemplateTypeClass & TemplateTypeClass::As_Reference(TemplateType type)
+{
+ return(*TemplateTypes.Ptr(type));
+}
+
+
+COORDINATE TemplateTypeClass::Coord_Fixup(COORDINATE coord) const
+{
+ return Coord_Whole(coord);
+}
diff --git a/CODE/CDFILE.CPP b/CODE/CDFILE.CPP
new file mode 100644
index 0000000..eb2faaa
--- /dev/null
+++ b/CODE/CDFILE.CPP
@@ -0,0 +1,720 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CDFILE.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Westwood Library *
+ * *
+ * File Name : CDFILE.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : October 18, 1994 *
+ * *
+ * Last Update : September 22, 1995 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * CDFileClass::Clear_Search_Drives -- Removes all record of a search path. *
+ * CDFileClass::Open -- Opens the file object -- with path search. *
+ * CDFileClass::Open -- Opens the file wherever it can be found. *
+ * CDFileClass::Set_Name -- Performs a multiple directory scan to set the filename. *
+ * CDFileClass::Set_Search_Drives -- Sets a list of search paths for file access. *
+ * Is_Disk_Inserted -- Checks to see if a disk is inserted in specified drive. *
+ * harderr_handler -- Handles hard DOS errors. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#include "cdfile.h"
+#include
+#include
+
+#ifndef WIN32
+#include
+#include
+#endif
+
+/*
+** Pointer to the first search path record.
+*/
+CDFileClass::SearchDriveType * CDFileClass::First = 0;
+
+int CDFileClass::CurrentCDDrive = 0;
+int CDFileClass::LastCDDrive = 0;
+char CDFileClass::RawPath[512] = {0};
+
+CDFileClass::CDFileClass(char const *filename) :
+ IsDisabled(false)
+{
+ CDFileClass::Set_Name(filename);
+// memset (RawPath, 0, sizeof(RawPath));
+}
+
+
+CDFileClass::CDFileClass(void) :
+ IsDisabled(false)
+{
+}
+extern int Get_CD_Index (int cd_drive, int timeout);
+
+/***********************************************************************************************
+ * harderr_handler -- Handles hard DOS errors. *
+ * *
+ * This routine will handle the low level DOS error trapper. Instead of displaying the *
+ * typical "Abort, Retry, Ignore" message, it simply returns with the failure code. The *
+ * cause of the error will fail. The likely case would be with disk I/O. *
+ * *
+ * INPUT: *
+ * *
+ * OUTPUT: Return the failure code. *
+ * *
+ * WARNINGS: Do no processing in this routine that could possibly generate another *
+ * hard error condition. *
+ * *
+ * HISTORY: *
+ * 09/22/1995 JLB : Created. *
+ *=============================================================================================*/
+#ifdef WIN32
+int harderr_handler(unsigned int , unsigned int , unsigned int *)
+#else
+int harderr_handler(unsigned int , unsigned int , unsigned int __far *)
+#endif
+{
+ return(_HARDERR_FAIL);
+}
+
+
+/***********************************************************************************************
+ * Is_Disk_Inserted -- Checks to see if a disk is inserted in specified drive. *
+ * *
+ * This routine will examine the drive specified to see if there is a disk inserted. It *
+ * can be used for floppy drives as well as for the CD-ROM. *
+ * *
+ * INPUT: disk -- The drive number to examine. 0=A, 1=B, etc. *
+ * *
+ * OUTPUT: bool; Is a disk inserted into the specified drive? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 09/20/1995 JLB : Created. *
+ *=============================================================================================*/
+int cdecl Is_Disk_Inserted(int disk)
+{
+#ifndef OBSOLETE
+ struct find_t fb;
+ char scan[] = "?:\\*.*";
+
+ #ifndef WIN32
+ _harderr(harderr_handler); // BG: Install hard error handler
+ #endif
+
+ scan[0] = (char)('A' + disk);
+ return(_dos_findfirst(scan, _A_SUBDIR, &fb) == 0);
+#else
+ struct {
+ struct {
+ char Length;
+ char Unit;
+ char Function;
+ char Status;
+ char Reserved[8];
+ } ReqHdr;
+ char MediaDescriptor; // Media descriptor byte from BPB.
+ void *Transfer; // Pointer to transfer address block.
+ short Length; // Number of bytes to transfer.
+ short Sector; // Starting sector number.
+ void *Volume; // Pointer to requested volume.
+ } IOCTLI;
+ char status[5];
+
+ memset(IOCTLI, 0, sizeof(IOCTLI));
+ IOCTLI.ReqHdr.Length = 26;
+ IOCTLI.ReqHdr.Unit = 0; // First CD-ROM controlled by this driver.
+ //IOCTLI.ReqHdr.Unit = 11; // Hard coded for K:
+ IOCTLI.ReqHdr.Function = 3; // IOCTL read
+ IOCTLI.Transfer = &status[0];
+ IOCTLI.Length = sizeof(status);
+ status[0] = 6; // Fetch device status code.
+ _AX = 0x440D;
+ _CX = 0x0003;
+ geninterrupt(0x21);
+ return(!(_AX & (1<<11)));
+#endif
+}
+
+
+/***********************************************************************************************
+ * CDFileClass::Open -- Opens the file object -- with path search. *
+ * *
+ * This will open the file object, but since the file object could have been constructed *
+ * with a pathname, this routine will try to find the file first. For files opened for *
+ * writing, then use the existing filename without performing a path search. *
+ * *
+ * INPUT: rights -- The access rights to use when opening the file *
+ * *
+ * OUTPUT: bool; Was the open successful? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 10/18/1994 JLB : Created. *
+ *=============================================================================================*/
+int CDFileClass::Open(int rights)
+{
+ return(BufferIOFileClass::Open(rights));
+}
+/***********************************************************************************************
+ * CDFC::Refresh_Search_Drives -- Updates the search path when a CD changes or is added *
+ * *
+ * *
+ * *
+ * INPUT: Nothing *
+ * *
+ * OUTPUT: Nothing *
+ * *
+ * WARNINGS: None *
+ * *
+ * HISTORY: *
+ * 5/22/96 9:01AM ST : Created *
+ *=============================================================================================*/
+void CDFileClass::Refresh_Search_Drives (void)
+{
+ Clear_Search_Drives();
+ Set_Search_Drives(RawPath);
+}
+
+#if 0
+/***********************************************************************************************
+ * CDFileClass::Set_Search_Drives -- Sets a list of search paths for file access. *
+ * *
+ * This routine sets up a list of search paths to use when accessing files. The path list *
+ * is scanned if the file could not be found in the current directory. This is the primary *
+ * method of supporting CD-ROM drives, but is also useful for scanning network and other *
+ * directories. The pathlist as passed to this routine is of the same format as the path *
+ * list used by DOS -- paths are separated by semicolons and need not end in an antivirgule.*
+ * *
+ * If a path entry begins with "?:" then the question mark will be replaced with the first *
+ * CD-ROM drive letter available. If there is no CD-ROM driver detected, then this path *
+ * entry will be ignored. By using this feature, you can always pass the CD-ROM path *
+ * specification to this routine and it will not break if the CD-ROM is not loaded (as in *
+ * the case during development). *
+ * *
+ * Here is an example path specification: *
+ * *
+ * Set_Search_Drives("DATA;?:\DATA;F:\PROJECT\DATA"); *
+ * *
+ * In this example, the current directory will be searched first, followed by a the *
+ * subdirectory "DATA" located off of the current directory. If not found, then the CD-ROM *
+ * will be searched in a directory called "\DATA". If not found or the CD-ROM is not *
+ * present, then it will look to the hard coded path of "F:\PROJECTS\DATA" (maybe a *
+ * network?). If all of these searches fail, the file system will default to the current *
+ * directory and let the normal file error system take over. *
+ * *
+ * INPUT: pathlist -- Pointer to string of path specifications (separated by semicolons) *
+ * that will be used to search for files. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 10/18/1994 JLB : Created. *
+ *=============================================================================================*/
+int CDFileClass::Set_Search_Drives(char * pathlist)
+{
+ int found = false;
+ int empty = false;
+
+ /*
+ ** If there is no pathlist to add, then just return.
+ */
+ if (!pathlist) return(0);
+
+ char const * ptr = strtok(pathlist, ";");
+ while (ptr) {
+ char path[PATH_MAX]; // Working path buffer.
+ SearchDriveType *srch; // Working pointer to path object.
+
+ /*
+ ** Fixup the path to be legal. Legal is defined as all that is necessary to
+ ** create a pathname is to append the actual filename submitted to the
+ ** file system. This means that it must have either a trailing ':' or '\'
+ ** character.
+ */
+ strcpy(path, ptr);
+ switch (path[strlen(path)-1]) {
+ case ':':
+ case '\\':
+ break;
+
+ default:
+ strcat(path, "\\");
+ break;
+ }
+
+ /*
+ ** If there is a drive letter specified, and this drive letter is '?', then it should
+ ** be substituted with the CD-ROM drive letter. In the case of no CD-ROM attached, then
+ ** merely ignore this path entry.
+ */
+ if (strncmp(path, "?:", 2) == 0) {
+ #ifndef WIN32
+ GetCDClass temp;
+ int cd = temp.GetCDDrive();
+ #else
+ int cd = 10;
+ #endif
+ found = cd;
+ empty = !Is_Disk_Inserted(cd);
+ if (!found || empty) goto nextpath;
+ path[0] = (char)('A' + cd);
+ }
+
+ /*
+ ** Allocate a record structure.
+ */
+ srch = new SearchDriveType;
+ if (srch) {
+ found = true;
+
+ /*
+ ** Attach the path to this structure.
+ */
+ srch->Path = strdup(path);
+ srch->Next = NULL;
+
+ /*
+ ** Attach this path record to the end of the path chain.
+ */
+ if (!First) {
+ First = srch;
+ } else {
+ SearchDriveType * chain = First;
+
+ while (chain->Next) {
+ chain = (SearchDriveType *)chain->Next;
+ }
+ chain->Next = srch;
+ }
+ }
+
+ /*
+ ** Find the next path string and resubmit.
+ */
+nextpath:
+ ptr = strtok(NULL, ";");
+ }
+ if (!found) return(1);
+ if (empty) return(2);
+ return(0);
+}
+#endif
+
+
+/***********************************************************************************************
+ * CDFileClass::Set_Search_Drives -- Sets a list of search paths for file access. *
+ * *
+ * This routine sets up a list of search paths to use when accessing files. The path list *
+ * is scanned if the file could not be found in the current directory. This is the primary *
+ * method of supporting CD-ROM drives, but is also useful for scanning network and other *
+ * directories. The pathlist as passed to this routine is of the same format as the path *
+ * list used by DOS -- paths are separated by semicolons and need not end in an antivirgule.*
+ * *
+ * If a path entry begins with "?:" then the question mark will be replaced with the first *
+ * CD-ROM drive letter available. If there is no CD-ROM driver detected, then this path *
+ * entry will be ignored. By using this feature, you can always pass the CD-ROM path *
+ * specification to this routine and it will not break if the CD-ROM is not loaded (as in *
+ * the case during development). *
+ * *
+ * Here is an example path specification: *
+ * *
+ * Set_Search_Drives("DATA;?:\DATA;F:\PROJECT\DATA"); *
+ * *
+ * In this example, the current directory will be searched first, followed by a the *
+ * subdirectory "DATA" located off of the current directory. If not found, then the CD-ROM *
+ * will be searched in a directory called "\DATA". If not found or the CD-ROM is not *
+ * present, then it will look to the hard coded path of "F:\PROJECTS\DATA" (maybe a *
+ * network?). If all of these searches fail, the file system will default to the current *
+ * directory and let the normal file error system take over. *
+ * *
+ * INPUT: pathlist -- Pointer to string of path specifications (separated by semicolons) *
+ * that will be used to search for files. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 10/18/1994 JLB : Created. *
+ * 05/21/1996 ST : Modified to recognise multiple CD drives *
+ *=============================================================================================*/
+int CDFileClass::Set_Search_Drives(char * pathlist)
+{
+ int found = FALSE;
+ int empty = FALSE;
+
+ /*
+ ** If there is no pathlist to add, then just return.
+ */
+ if (!pathlist) return(0);
+
+ /*
+ ** Save the path as it was passed in so we can parse it again later.
+ ** Check for the case where RawPath was passed in.
+ */
+ if (pathlist != RawPath) {
+ strcat (RawPath, ";");
+ strcat (RawPath, pathlist);
+ }
+
+ char const * ptr = strtok(pathlist, ";");
+ while (ptr != NULL) {
+ if (strlen(ptr) > 0) {
+
+ char path[PATH_MAX]; // Working path buffer.
+
+ /*
+ ** Fixup the path to be legal. Legal is defined as all that is necessary to
+ ** create a pathname is to append the actual filename submitted to the
+ ** file system. This means that it must have either a trailing ':' or '\'
+ ** character.
+ */
+ strcpy(path, ptr);
+ switch (path[strlen(path)-1]) {
+ case ':':
+ case '\\':
+ break;
+
+ default:
+ strcat(path, "\\");
+ break;
+ }
+
+ /*
+ ** If there is a drive letter specified, and this drive letter is '?', then it should
+ ** be substituted with the CD-ROM drive letter. In the case of no CD-ROM attached, then
+ ** merely ignore this path entry.
+ ** Adds an extra entry for each CD drive in the system that has a C&C disc inserted.
+ ** ST - 5/21/96 4:40PM
+ */
+ if (strncmp(path, "?:", 2) == 0) {
+ if (CurrentCDDrive) {
+ found = true;
+
+ /*
+ ** If the drive has a C&C CD in it then add it to the path
+ */
+ if (Get_CD_Index(CurrentCDDrive, 2*60) >= 0) {
+ path[0] = (char)(CurrentCDDrive + 'A');
+ Add_Search_Drive(path);
+ }
+ }
+
+ /*
+ ** Find the next path string and resubmit.
+ */
+ ptr = strtok(NULL, ";");
+ continue;
+ }
+
+ found = true;
+ Add_Search_Drive(path);
+ }
+
+ /*
+ ** Find the next path string and resubmit.
+ */
+ ptr = strtok(NULL, ";");
+ }
+ if (!found) return(1);
+ if (empty) return(2);
+ return(0);
+}
+
+
+/***********************************************************************************************
+ * CDFC::Add_Search_Drive -- Add a new path to the search path list *
+ * *
+ * *
+ * *
+ * INPUT: path *
+ * *
+ * OUTPUT: Nothing *
+ * *
+ * WARNINGS: None *
+ * *
+ * HISTORY: *
+ * 5/22/96 10:12AM ST : Created *
+ *=============================================================================================*/
+void CDFileClass::Add_Search_Drive(char *path)
+{
+ SearchDriveType *srch; // Working pointer to path object.
+ /*
+ ** Allocate a record structure.
+ */
+ srch = new SearchDriveType;
+
+ /*
+ ** Attach the path to this structure.
+ */
+ srch->Path = strdup(path);
+ srch->Next = NULL;
+
+ /*
+ ** Attach this path record to the end of the path chain.
+ */
+ if (!First) {
+ First = srch;
+ } else {
+ SearchDriveType * chain = First;
+
+ while (chain->Next) {
+ chain = (SearchDriveType *)chain->Next;
+ }
+ chain->Next = srch;
+ }
+}
+
+
+/***********************************************************************************************
+ * CDFC::Set_CD_Drive -- sets the current CD drive letter *
+ * *
+ * *
+ * *
+ * INPUT: Nothing *
+ * *
+ * OUTPUT: Nothing *
+ * *
+ * WARNINGS: None *
+ * *
+ * HISTORY: *
+ * 5/22/96 9:39AM ST : Created *
+ *=============================================================================================*/
+void CDFileClass::Set_CD_Drive (int drive)
+{
+ LastCDDrive = CurrentCDDrive;
+ CurrentCDDrive = drive;
+}
+
+
+/***********************************************************************************************
+ * CDFileClass::Clear_Search_Drives -- Removes all record of a search path. *
+ * *
+ * Use this routine to clear out any previous path(s) set with Set_Search_Drives() *
+ * function. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 10/18/1994 JLB : Created. *
+ *=============================================================================================*/
+void CDFileClass::Clear_Search_Drives(void)
+{
+ SearchDriveType * chain; // Working pointer to path chain.
+
+ chain = First;
+ while (chain) {
+ SearchDriveType *next;
+
+ next = (SearchDriveType *)chain->Next;
+ if (chain->Path) {
+ free((char *)chain->Path);
+ }
+ delete chain;
+
+ chain = next;
+ }
+ First = 0;
+}
+
+
+/***********************************************************************************************
+ * CDFileClass::Set_Name -- Performs a multiple directory scan to set the filename. *
+ * *
+ * This routine will scan all the directories specified in the path list and if the file *
+ * was found in one of the directories, it will set the filename to a composite of the *
+ * correct directory and the filename. It is used to allow path searching when searching *
+ * for files. Typical use is to support CD-ROM drives. This routine examines the current *
+ * directory first before scanning through the path list. If after scanning the entire *
+ * path list, the file still could not be found, then the file object's name is set with *
+ * just the raw filename as passed to this routine. *
+ * *
+ * INPUT: filename -- Pointer to the filename to set as the name of this file object. *
+ * *
+ * OUTPUT: Returns a pointer to the final and complete filename of this file object. This *
+ * may have a path attached to the file. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 10/18/1994 JLB : Created. *
+ *=============================================================================================*/
+char const * CDFileClass::Set_Name(char const *filename)
+{
+ /*
+ ** Try to find the file in the current directory first. If it can be found, then
+ ** just return with the normal file name setting process. Do the same if there is
+ ** no multi-drive search path.
+ */
+ BufferIOFileClass::Set_Name(filename);
+ if (IsDisabled || !First || BufferIOFileClass::Is_Available()) return(File_Name());
+
+ /*
+ ** Attempt to find the file first. Check the current directory. If not found there, then
+ ** search all the path specifications available. If it still can't be found, then just
+ ** fall into the normal raw file filename setting system.
+ */
+ SearchDriveType * srch = First;
+
+ while (srch) {
+ char path[_MAX_PATH];
+
+ /*
+ ** Build a pathname to search for.
+ */
+ strcpy(path, srch->Path);
+ strcat(path, filename);
+
+ /*
+ ** Check to see if the file could be found. The low level Is_Available logic will
+ ** prompt if necessary when the CD-ROM drive has been removed. In all other cases,
+ ** it will return false and the search process will continue.
+ */
+ BufferIOFileClass::Set_Name(path);
+ if (BufferIOFileClass::Is_Available()) {
+ return(File_Name());
+ }
+
+ /*
+ ** It wasn't found, so try the next path entry.
+ */
+ srch = (SearchDriveType *)srch->Next;
+ }
+
+ /*
+ ** At this point, all path searching has failed. Just set the file name to the
+ ** plain text passed to this routine and be done with it.
+ */
+ BufferIOFileClass::Set_Name(filename);
+ return(File_Name());
+}
+
+
+/***********************************************************************************************
+ * CDFileClass::Open -- Opens the file wherever it can be found. *
+ * *
+ * This routine is similar to the RawFileClass open except that if the file is being *
+ * opened only for READ access, it will search all specified directories looking for the *
+ * file. If after a complete search the file still couldn't be found, then it is opened *
+ * using the normal BufferIOFileClass system -- resulting in normal error procedures. *
+ * *
+ * INPUT: filename -- Pointer to the override filename to supply for this file object. It *
+ * would be the base filename (sans any directory specification). *
+ * *
+ * rights -- The access rights to use when opening the file. *
+ * *
+ * OUTPUT: bool; Was the file opened successfully? If so then the filename may be different *
+ * than requested. The location of the file can be determined by examining the *
+ * filename of this file object. The filename will contain the complete *
+ * pathname used to open the file. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 10/18/1994 JLB : Created. *
+ *=============================================================================================*/
+int CDFileClass::Open(char const *filename, int rights)
+{
+ CDFileClass::Close();
+
+ /*
+ ** Verify that there is a filename associated with this file object. If not, then this is a
+ ** big error condition.
+ */
+ if (!filename) {
+ Error(ENOENT, false);
+ }
+
+ /*
+ ** If writing is requested, then multiple drive searching is not performed.
+ */
+ if (IsDisabled || rights == WRITE) {
+
+ BufferIOFileClass::Set_Name( filename );
+ return( BufferIOFileClass::Open( rights ) );
+ }
+
+ /*
+ ** Perform normal multiple drive searching for the filename and open
+ ** using the normal procedure.
+ */
+ Set_Name(filename);
+ return(BufferIOFileClass::Open(rights));
+}
+
+
+#ifdef NEVER
+/*
+** Get the drive letters if the CD's online */
+*/
+WORD cdecl GetCDDrive(VOID)
+{
+ _ES = FP_SEG(&cdDrive[0]);
+ _BX = FP_OFF(&cdDrive[0]);
+ _AX = 0x150d;
+ geninterrupt(0x2F);
+ return((WORD)(*cdDrive));
+}
+#endif
+
+#if 0
+int Get_CD_Drive(void)
+{
+#ifdef WIN32
+ return(10);
+#else
+
+#ifdef NEVER
+ for (int index = 0; index < 26; index++) {
+ union REGS regs;
+
+ regs.w.ax = 0x150B;
+ regs.w.bx = 0;
+ regs.w.cx = index;
+ int386(0x2F, ®s, ®s);
+ if (regs.w.bx == 0xADAD) {
+ return(index);
+ }
+ }
+ return(0);
+#else
+ GetCDClass temp;
+ return(temp.GetCDDrive());
+#endif
+#endif
+}
+
+#endif
diff --git a/CODE/CDFILE.H b/CODE/CDFILE.H
new file mode 100644
index 0000000..7af0d63
--- /dev/null
+++ b/CODE/CDFILE.H
@@ -0,0 +1,113 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CDFILE.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Westwood LIbrary *
+ * *
+ * File Name : CDFILE.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : October 18, 1994 *
+ * *
+ * Last Update : October 18, 1994 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifndef CDFILE_H
+#define CDFILE_H
+
+#include
+#include "bfiofile.h"
+
+/*
+** This class is derived from the BufferIOFileClass. This class adds the functionality of searching
+** across multiple directories or drives. It is designed for the typical case of a CD-ROM game
+** were some data exists in the current directory (hard drive) and the rest exists on the CD-ROM.
+** Searching for the file occurs by first examining the current directory. If the file does not
+** exist there, then all the paths available are examined in turn until the file can be found.
+** For opening files to write, only the current directory is examined. The directory search order
+** is controlled by the path list as submitted to Set_Search_Drives(). The format of the path
+** string is the same as the DOS path string.
+*/
+class CDFileClass : public BufferIOFileClass
+{
+ public:
+ CDFileClass(char const *filename);
+ CDFileClass(void);
+ virtual ~CDFileClass(void) {};
+
+ virtual char const * Set_Name(char const *filename);
+ virtual int Open(char const *filename, int rights=READ);
+ virtual int Open(int rights=READ);
+
+ void Searching(int on) {IsDisabled = !on;};
+
+ static bool Is_There_Search_Drives(void) {return(First != NULL);};
+ static int Set_Search_Drives(char * pathlist);
+ static void Add_Search_Drive(char *path);
+ static void Clear_Search_Drives(void);
+ static void Refresh_Search_Drives(void);
+ static void Set_CD_Drive(int drive);
+ static int Get_CD_Drive(void) {return(CurrentCDDrive);};
+ static int Get_Last_CD_Drive(void) {return(LastCDDrive);};
+
+ private:
+
+ /*
+ ** Is multi-drive searching disabled for this file object?
+ */
+ unsigned IsDisabled:1;
+
+ /*
+ ** This is the control record for each of the drives specified in the search
+ ** path. There can be many such search paths available.
+ */
+ typedef struct {
+ void * Next; // Pointer to next search record.
+ char const * Path; // Pointer to path string.
+ } SearchDriveType;
+
+ /*
+ ** This points to the first path record.
+ */
+ static SearchDriveType * First;
+ /*
+ ** This is a copy of the unparsed search path list
+ */
+ static char RawPath[512];
+
+ /*
+ ** The drive letter of the current cd drive
+ */
+ static int CurrentCDDrive;
+
+ /*
+ ** The drive letter of the last used CD drive
+ */
+ static int LastCDDrive;
+};
+
+#endif
+
diff --git a/CODE/CELL.CPP b/CODE/CELL.CPP
new file mode 100644
index 0000000..5913f21
--- /dev/null
+++ b/CODE/CELL.CPP
@@ -0,0 +1,3014 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CELL.CPP 4 3/14/97 1:15p Joe_b $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CELL.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : April 29, 1994 *
+ * *
+ * Last Update : October 6, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * CellClass::Adjacent_Cell -- Determines the adjacent cell according to facing. *
+ * CellClass::Adjust_Threat -- Allows adjustment of threat at cell level *
+ * CellClass::Can_Tiberium_Germinate -- Determines if Tiberium can begin growth in the cell. *
+ * CellClass::Can_Tiberium_Grow -- Determines if Tiberium can grow in this cell. *
+ * CellClass::Can_Tiberium_Spread -- Determines if Tiberium can spread from this cell. *
+ * CellClass::CellClass -- Constructor for cell objects. *
+ * CellClass::Cell_Building -- Return with building at specified cell. *
+ * CellClass::Cell_Color -- Determine what radar color to use for this cell. *
+ * CellClass::Cell_Coord -- Returns the coordinate of this cell. *
+ * CellClass::Cell_Find_Object -- Returns ptr to RTTI type occupying cell *
+ * CellClass::Cell_Infantry -- Returns with pointer of first infantry unit. *
+ * CellClass::Cell_Object -- Returns with clickable object in cell. *
+ * CellClass::Cell_Techno -- Return with the unit/building at specified cell. *
+ * CellClass::Cell_Terrain -- Determines terrain object in cell. *
+ * CellClass::Cell_Unit -- Returns with pointer to unit occupying cell. *
+ * CellClass::Cell_Vessel -- Returns with pointer to a vessel located in the cell. *
+ * CellClass::Clear_Icon -- Calculates what the clear icon number should be. *
+ * CellClass::Closest_Free_Spot -- returns free spot closest to given coord *
+ * CellClass::Concrete_Calc -- Calculates the concrete icon to use for the cell. *
+ * CellClass::Draw_It -- Draws the cell imagery at the location specified. *
+ * CellClass::Flag_Place -- Places a house flag down on the cell. *
+ * CellClass::Flag_Remove -- Removes the house flag from the cell. *
+ * CellClass::Goodie_Check -- Performs crate discovery logic. *
+ * CellClass::Grow_Tiberium -- Grows the tiberium in the cell. *
+ * CellClass::Incoming -- Causes objects in cell to "run for cover". *
+ * CellClass::Is_Bridge_Here -- Checks to see if this is a bridge occupied cell. *
+ * CellClass::Is_Clear_To_Build -- Determines if cell can be built upon. *
+ * CellClass::Is_Clear_To_Move -- Determines if the cell is generally clear for travel *
+ * CellClass::Occupy_Down -- Flag occupation of specified cell. *
+ * CellClass::Occupy_Up -- Removes occupation flag from the specified cell. *
+ * CellClass::Overlap_Down -- This routine is used to mark a cell as being spilled over (over*
+ * CellClass::Overlap_Unit -- Marks cell as being overlapped by unit. *
+ * CellClass::Overlap_Up -- Removes overlap flag for the cell. *
+ * CellClass::Read -- Reads a particular cell value from a save game file. *
+ * CellClass::Recalc_Attributes -- Recalculates the ground type attributes for the cell. *
+ * CellClass::Redraw_Objects -- Redraws all objects overlapping this cell. *
+ * CellClass::Reduce_Tiberium -- Reduces the tiberium in the cell by the amount specified. *
+ * CellClass::Reduce_Wall -- Damages a wall, if damage is high enough. *
+ * CellClass::Reserve_Cell -- Marks a cell as being occupied by the specified unit ID. *
+ * CellClass::Shimmer -- Causes all objects in the cell to shimmer. *
+ * CellClass::Spot_Index -- returns cell sub-coord index for given COORDINATE *
+ * CellClass::Spread_Tiberium -- Spread Tiberium from this cell to an adjacent cell. *
+ * CellClass::Tiberium_Adjust -- Adjust the look of the Tiberium for smooth. *
+ * CellClass::Wall_Update -- Updates the imagery for wall objects in cell. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+#include "vortex.h"
+
+/***********************************************************************************************
+ * CellClass::CellClass -- Constructor for cell objects. *
+ * *
+ * A cell object is constructed into an empty state. It contains no specific objects, *
+ * templates, or overlays. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/09/1994 JLB : Created. *
+ * 02/20/1996 JLB : Uses initializer list. *
+ *=============================================================================================*/
+CellClass::CellClass(void) :
+ ID(Map.ID(this)),
+ IsPlot(false),
+ IsCursorHere(false),
+ IsMapped(false),
+ IsVisible(false),
+ IsWaypoint(false),
+ IsRadarCursor(false),
+ IsFlagged(false),
+ IsToShroud(false),
+ Jammed(0),
+ Trigger(NULL),
+ TType(TEMPLATE_NONE),
+ TIcon(0),
+ Overlay(OVERLAY_NONE),
+ OverlayData(0),
+ Smudge(SMUDGE_NONE),
+ SmudgeData(0),
+ Owner(HOUSE_NONE),
+ InfType(HOUSE_NONE),
+ OccupierPtr(0),
+ Land(LAND_CLEAR)
+{
+ for (int zone = MZONE_FIRST; zone < MZONE_COUNT; zone++) {
+ Zones[zone] = 0;
+ }
+ Flag.Composite = 0;
+ for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) {
+ Overlapper[index] = 0;
+ }
+}
+
+
+/***********************************************************************************************
+ * CellClass::Cell_Color -- Determine what radar color to use for this cell. *
+ * *
+ * Use this routine to determine what radar color to render a radar *
+ * pixel with. This routine is called many many times to render the *
+ * radar map, so it must be fast. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the color to display the radar pixel with. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 03/01/1994 JLB : Created. *
+ * 04/30/1994 JLB : Converted to member function. *
+ * 05/31/1994 JLB : Takes into account any stealth characteristics of object. *
+ *=============================================================================================*/
+int CellClass::Cell_Color(bool override) const
+{
+ assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
+
+ BuildingClass * object = Cell_Building();
+ if (object && !object->Class->IsInvisible) {
+ return(ColorRemaps[object->House->RemapColor].Bar);
+ }
+
+ if (override) {
+ return(TBLACK);
+ }
+ if (LastTheater == THEATER_SNOW) {
+ return(::SnowColor[Land_Type()]);
+ } else {
+ return(::GroundColor[Land_Type()]);
+ }
+}
+
+
+/***********************************************************************************************
+ * CellClass::Cell_Techno -- Return with the unit/building at specified cell. *
+ * *
+ * Returns an object located in the cell. If there is a *
+ * building present, it returns a pointer to that, otherwise it returns *
+ * a pointer to one of the units there. If nothing is present in the *
+ * specified cell, then it returns NULL. *
+ * *
+ * INPUT: x,y -- Coordinate offset (from upper left corner) to use as an aid in selecting *
+ * the desired object within the cell. *
+ * *
+ * OUTPUT: Returns a pointer to a building or unit located in cell. If *
+ * nothing present, just returns NULL. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/05/1992 JLB : Created. *
+ * 04/30/1994 JLB : Converted to member function. *
+ *=============================================================================================*/
+TechnoClass * CellClass::Cell_Techno(int x, int y) const
+{
+ assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
+
+ ObjectClass * object;
+ COORDINATE click; // Coordinate of click relative to cell corner.
+ TechnoClass * close = NULL;
+ long distance = 0; // Recorded closest distance.
+
+ /*
+ ** Create a coordinate value that represent the pixel location within the cell. This is
+ ** actually the lower significant bits (leptons) of a regular coordinate value.
+ */
+ click = XY_Coord(Pixel_To_Lepton(x), Pixel_To_Lepton(y));
+
+ if (Cell_Occupier()) {
+ object = Cell_Occupier();
+ while (object) {
+ if (object->Is_Techno()) {
+ COORDINATE coord = Coord_Fraction(object->Center_Coord());
+ long dist = Distance(coord, click);
+ if (!close || dist < distance) {
+ close = (TechnoClass *)object;
+ distance = dist;
+ }
+ }
+ object = object->Next;
+ }
+ }
+ return(close);
+}
+
+
+/***************************************************************************
+ * CellClass::Cell_Find_Object -- Returns ptr to RTTI type occupying cell *
+ * *
+ * INPUT: RTTIType the RTTI type we are searching for *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 03/17/1995 PWG : Created. *
+ * 06/12/1995 JLB : Returns object class pointer. *
+ *=========================================================================*/
+ObjectClass * CellClass::Cell_Find_Object(RTTIType rtti) const
+{
+ assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
+ assert(rtti != RTTI_NONE);
+
+ ObjectClass * object = Cell_Occupier();
+
+ while (object != NULL) {
+ if (object->What_Am_I() == rtti) {
+ return(object);
+ }
+ object = object->Next;
+ }
+ return(NULL);
+}
+
+
+/***********************************************************************************************
+ * CellClass::Cell_Building -- Return with building at specified cell. *
+ * *
+ * Given a cell, determine if there is a building associated *
+ * and return with a pointer to this building. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with a pointer to the building associated with the *
+ * cell. If there is no building associated, then NULL is *
+ * returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/05/1992 JLB : Created. *
+ * 04/30/1994 JLB : Converted to member function. *
+ *=============================================================================================*/
+BuildingClass * CellClass::Cell_Building(void) const
+{
+ assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
+
+ return((BuildingClass *)Cell_Find_Object(RTTI_BUILDING));
+}
+
+
+/***********************************************************************************************
+ * CellClass::Cell_Terrain -- Determines terrain object in cell. *
+ * *
+ * This routine is used to determine the terrain object (if any) that *
+ * overlaps this cell. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with a pointer to the terrain object that overlaps *
+ * this cell. If there is no terrain object present, then NULL *
+ * is returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/18/1994 JLB : Created. *
+ *=============================================================================================*/
+TerrainClass * CellClass::Cell_Terrain(void) const
+{
+ assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
+
+ return((TerrainClass *)Cell_Find_Object(RTTI_TERRAIN));
+}
+
+
+/***********************************************************************************************
+ * CellClass::Cell_Object -- Returns with clickable object in cell. *
+ * *
+ * This routine is used to determine which object is to be selected *
+ * by a player click upon the cell. Not all objects that overlap the *
+ * cell are selectable by the player. This routine sorts out which *
+ * is which and returns with the appropriate object pointer. *
+ * *
+ * INPUT: x,y -- Coordinate (from upper left corner of cell) to use as a guide when *
+ * selecting the object within the cell. This plays a role in those cases *
+ * where several objects (such as infantry) exist within the same cell. *
+ * *
+ * OUTPUT: Returns with pointer to the object clickable within the *
+ * cell. NULL is returned if there is no clickable object *
+ * present. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/13/1994 JLB : Created. *
+ *=============================================================================================*/
+ObjectClass * CellClass::Cell_Object(int x, int y) const
+{
+ assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
+
+ ObjectClass * ptr;
+
+ /*
+ ** Hack so that aircraft landed on helipads can still be selected if directly
+ ** clicked on.
+ */
+ ptr = (ObjectClass *)Cell_Find_Object(RTTI_AIRCRAFT);
+ if (ptr) {
+ return(ptr);
+ }
+
+ ptr = Cell_Techno(x, y);
+ if (ptr) {
+ return(ptr);
+ }
+ ptr = Cell_Terrain();
+ if (ptr) return(ptr);
+ return(ptr);
+}
+
+
+/***********************************************************************************************
+ * CellClass::Redraw_Objects -- Redraws all objects overlapping this cell. *
+ * *
+ * This is a low level routine that marks all objects that overlap this *
+ * cell to be redrawn. It is necessary to call this routine whenever *
+ * the underlying icon has to be redrawn. *
+ * *
+ * INPUT: forced -- Should this redraw be forced even if flags *
+ * indicate that it would be redundant? *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/18/1994 JLB : Created. *
+ * 06/20/1994 JLB : Simplified to use object pointers. *
+ * 12/24/1994 JLB : Only checks if cell is in view and not flagged already. *
+ *=============================================================================================*/
+void CellClass::Redraw_Objects(bool forced)
+{
+ assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
+
+ CELL cell = Cell_Number();
+
+ if (Map.In_View(cell) && (forced || !Map.Is_Cell_Flagged(cell))) {
+
+ /*
+ ** Flag the icon to be redrawn.
+ */
+ Map.Flag_Cell(cell);
+
+ /*
+ ** Flag the main object in the cell to be redrawn.
+ */
+ if (Cell_Occupier() != NULL) {
+ ObjectClass * optr = Cell_Occupier();
+ while (optr != NULL && optr->IsActive) {
+
+#ifdef SORTDRAW
+ if (optr->Is_Techno() && ((TechnoClass *)optr)->Visual_Character() != VISUAL_NORMAL) {
+ optr->Mark(MARK_CHANGE);
+ }
+#else
+ optr->Mark(MARK_CHANGE);
+#endif
+ if (optr->Next != NULL && !optr->Next->IsActive) {
+ optr->Next = NULL;
+ }
+ optr = optr->Next;
+ }
+ }
+
+#ifdef SORTDRAW
+ /*
+ ** Flag any overlapping object in this cell to be redrawn.
+ */
+ for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) {
+ if (Overlapper[index]) {
+ assert(Overlapper[index]->IsActive);
+ if (Overlapper[index]->Is_Techno() && ((TechnoClass *)Overlapper[index])->Visual_Character() != VISUAL_NORMAL) {
+ Overlapper[index]->Mark(MARK_CHANGE);
+ }
+ }
+ }
+#else
+ /*
+ ** Flag any overlapping object in this cell to be redrawn.
+ */
+ for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) {
+ if (Overlapper[index] != NULL) {
+ if (!Overlapper[index]->IsActive) {
+ Overlapper[index] = NULL;
+ } else {
+ Overlapper[index]->Mark(MARK_CHANGE);
+ }
+ }
+ }
+#endif
+ }
+}
+
+
+/***********************************************************************************************
+ * CellClass::Is_Clear_To_Build -- Determines if cell can be built upon. *
+ * *
+ * This determines if the cell can become a proper foundation for *
+ * building placement. *
+ * *
+ * INPUT: loco -- The locomotion of the object trying to consider if this cell is *
+ * generally clear. Buildings use the value of SPEED_NONE. *
+ * *
+ * OUTPUT: bool; Is this cell generally clear (usually for building purposes)? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/18/1994 JLB : Created. *
+ * 06/25/1996 JLB : Handles different locomotion types. *
+ * 10/05/1996 JLB : Checks for crushable walls and crushable object. *
+ *=============================================================================================*/
+bool CellClass::Is_Clear_To_Build(SpeedType loco) const
+{
+ assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
+
+ /*
+ ** During scenario initialization, passability is always guaranteed.
+ */
+ if (ScenarioInit) return(true);
+
+ /*
+ ** If there is an object there, then don't allow building.
+ */
+ if (Cell_Object() != NULL) {
+ return(false);
+ }
+
+ /*
+ ** Prevents a building from being placed over a flag object.
+ */
+#ifdef FIXIT_FLAG_CHECK
+ if (IsFlagged) {
+ return(false);
+ }
+#endif
+
+ /*
+ ** Walls are always considered to block the terrain for general passability
+ ** purposes. In normal game mode, all overlays are not buildable.
+ */
+ if (Overlay != OVERLAY_NONE && (Overlay == OVERLAY_FLAG_SPOT || !Debug_Map || OverlayTypeClass::As_Reference(Overlay).IsWall)) {
+ return(false);
+ }
+
+ /*
+ ** Building over a bib is not allowed.
+ */
+ if (Smudge != SMUDGE_NONE && SmudgeTypeClass::As_Reference(Smudge).IsBib /* && Owner != HOUSE_NONE*/) {
+ return(false);
+ }
+
+ /*
+ ** Building on certain kinds of terrain is prohibited -- bridges in particular.
+ ** If the locomotion type is SPEED_NONE, then this check is presumed to be
+ ** for the purposes of building.
+ */
+ if (loco == SPEED_NONE) {
+ if (Is_Bridge_Here()) {
+ return(false);
+ }
+
+ return(::Ground[Land_Type()].Build);
+
+ } else {
+
+ if (::Ground[Land_Type()].Cost[loco] == fixed(0)) {
+// if (::Ground[Land_Type()].Cost[SPEED_TRACK] == fixed(0)) {
+ return(false);
+ }
+ return(true);
+ }
+}
+
+
+/***********************************************************************************************
+ * CellClass::Recalc_Attributes -- Recalculates the ground type attributes for the cell. *
+ * *
+ * This routine recalculates the ground type in the cell. The speeds the find path *
+ * algorithm and other determinations of the cell type. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/29/1994 JLB : Created. *
+ * 06/20/1994 JLB : Knows about template pointer in cell object. *
+ *=============================================================================================*/
+void CellClass::Recalc_Attributes(void)
+{
+ assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
+
+ /*
+ ** Special override for interior terrain set so that a non-template or a clear template
+ ** is equivalent to impassable rock.
+ */
+ if (LastTheater == THEATER_INTERIOR) {
+ if (TType == TEMPLATE_NONE || TType == TEMPLATE_CLEAR1) {
+ Land = LAND_ROCK;
+ return;
+ }
+ }
+
+ /*
+ ** Check for wall effects.
+ */
+ if (Overlay != OVERLAY_NONE) {
+ Land = OverlayTypeClass::As_Reference(Overlay).Land;
+ if (Land != LAND_CLEAR) return;
+ }
+
+ /*
+ ** If there is a template associated with this cell, then fetch the
+ ** land type given the template type and icon number.
+ */
+ if (TType != TEMPLATE_NONE && TType != 255) {
+ TemplateTypeClass const * ttype = &TemplateTypeClass::As_Reference(TType);
+ Land = ttype->Land_Type(TIcon);
+ return;
+ }
+
+ /*
+ ** No template is the same as clear terrain.
+ */
+ Land = LAND_CLEAR;
+}
+
+
+/***********************************************************************************************
+ * CellClass::Occupy_Down -- Flag occupation of specified cell. *
+ * *
+ * This routine is used to mark the cell as being occupied by the specified object. *
+ * *
+ * INPUT: object -- The object that is to occupy the cell *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/18/1994 JLB : Created. *
+ * 11/29/1994 JLB : Simplified. *
+ *=============================================================================================*/
+void CellClass::Occupy_Down(ObjectClass * object)
+{
+ assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
+ assert(object != NULL && object->IsActive);
+
+ ObjectClass * optr;
+
+ if (object == NULL) return;
+
+ /*
+ ** Always add buildings to the end of the occupation chain. This is necessary because
+ ** the occupation chain is a single list even though buildings occupy more than one
+ ** cell. If more than one building is allowed to occupy the same cell, then this chain
+ ** logic will fail.
+ */
+ if (object->What_Am_I() == RTTI_BUILDING && Cell_Occupier()) {
+ optr = Cell_Occupier();
+ while (optr->Next != NULL) {
+ assert(optr != object);
+ assert(optr->What_Am_I() != RTTI_BUILDING);
+ optr = optr->Next;
+ }
+ optr->Next = object;
+ object->Next = 0;
+ } else {
+ object->Next = Cell_Occupier();
+ OccupierPtr = object;
+ }
+ Map.Radar_Pixel(Cell_Number());
+
+ /*
+ ** If being placed down on a visible square, then flag this
+ ** techno object as being revealed to the player.
+ */
+ if (IsMapped || Session.Type != GAME_NORMAL) {
+ object->Revealed(PlayerPtr);
+ }
+
+ /*
+ ** Special occupy bit set.
+ */
+ switch (object->What_Am_I()) {
+ case RTTI_BUILDING:
+ Flag.Occupy.Building = true;
+ break;
+
+ case RTTI_VESSEL:
+ case RTTI_AIRCRAFT:
+ case RTTI_UNIT:
+ Flag.Occupy.Vehicle = true;
+ break;
+
+ case RTTI_TERRAIN:
+ Flag.Occupy.Monolith = true;
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+/***********************************************************************************************
+ * CellClass::Occupy_Up -- Removes occupation flag from the specified cell. *
+ * *
+ * This routine will lift the object from the cell and free the cell to be occupied by *
+ * another object. Only if the cell was previously marked with the object specified, will *
+ * the object be lifted off. This routine is the counterpart to Occupy_Down(). *
+ * *
+ * INPUT: object -- The object that is being lifted off. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/18/1994 JLB : Created. *
+ * 11/29/1994 JLB : Fixed to handle next pointer in previous object. *
+ *=============================================================================================*/
+void CellClass::Occupy_Up(ObjectClass * object)
+{
+ assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
+ assert(object != NULL && object->IsActive);
+
+ if (object == NULL) return;
+
+ ObjectClass * optr = Cell_Occupier(); // Working pointer to the objects in the chain.
+
+ if (optr == object) {
+ OccupierPtr = object->Next;
+ object->Next = 0;
+ } else {
+ bool found = false;
+ while (optr != NULL) {
+ if (optr->Next == object) {
+ optr->Next = object->Next;
+ object->Next = 0;
+ found = true;
+ break;
+ }
+ optr = optr->Next;
+ }
+// assert(found);
+ }
+ Map.Radar_Pixel(Cell_Number());
+
+ /*
+ ** Special occupy bit clear.
+ */
+ switch (object->What_Am_I()) {
+ case RTTI_BUILDING:
+ Flag.Occupy.Building = false;
+ break;
+
+ case RTTI_VESSEL:
+ case RTTI_AIRCRAFT:
+ case RTTI_UNIT:
+ Flag.Occupy.Vehicle = false;
+ break;
+
+ case RTTI_TERRAIN:
+ Flag.Occupy.Monolith = false;
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+/***********************************************************************************************
+ * CellClass::Overlap_Down -- This routine is used to mark a cell as being spilled over (overla*
+ * *
+ * Most game objects can often have their graphic imagery spill into more than one cell *
+ * even though they are considered to "occupy" only one cell. All cells overlapped are *
+ * flagged by this routine. Using this information it is possible to keep the tactical map *
+ * display correct. *
+ * *
+ * INPUT: object -- The object to mark as overlapping this cell. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/18/1994 JLB : Created. *
+ * 07/04/1995 JLB : Ensures that buildings are always marked down. *
+ *=============================================================================================*/
+void CellClass::Overlap_Down(ObjectClass * object)
+{
+ assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
+ assert(object != NULL && object->IsActive);
+
+ ObjectClass ** ptr = 0;
+
+ if (!object) return;
+
+ for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) {
+ if (Overlapper[index] == object) return;
+ if (!Overlapper[index]) ptr = &Overlapper[index];
+ }
+
+ /*
+ ** Buildings must ALWAYS succeed in marking the cell as overlapped. Bump somebody
+ ** else out in this case.
+ */
+ if (!ptr && object->What_Am_I() == RTTI_BUILDING) {
+ for (index = 0; index < ARRAY_SIZE(Overlapper); index++) {
+ switch (Overlapper[index]->What_Am_I()) {
+ case RTTI_BUILDING:
+ case RTTI_TERRAIN:
+ break;
+
+ default:
+ Overlapper[index] = object;
+ index = ARRAY_SIZE(Overlapper);
+ break;
+ }
+ }
+ }
+ if (ptr) *ptr = object;
+
+ /*
+ ** If being placed down on a visible square, then flag this
+ ** techno object as being revealed to the player.
+ */
+ if (IsMapped) {
+ object->Revealed(PlayerPtr);
+ }
+}
+
+
+/***********************************************************************************************
+ * CellClass::Overlap_Up -- Removes overlap flag for the cell. *
+ * *
+ * This is the counterpart to Overlap_Down and is used to remove the overlap flag for the *
+ * specified unit on the cell. *
+ * *
+ * INPUT: object -- The object to remove the overlap flag for. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/18/1994 JLB : Created. *
+ *=============================================================================================*/
+void CellClass::Overlap_Up(ObjectClass * object)
+{
+ assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
+ assert(object != NULL && object->IsActive);
+
+ for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) {
+ if (Overlapper[index] == object) {
+ Overlapper[index] = 0;
+ break;
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * CellClass::Cell_Unit -- Returns with pointer to unit occupying cell. *
+ * *
+ * This routine will determine if a unit is occupying the cell and if so, return a pointer *
+ * to it. If there is no unit occupying the cell, then NULL is returned. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with pointer to unit occupying cell, else NULL. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/18/1994 JLB : Created. *
+ *=============================================================================================*/
+UnitClass * CellClass::Cell_Unit(void) const
+{
+ assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
+
+ return((UnitClass*)Cell_Find_Object(RTTI_UNIT));
+}
+
+
+/***********************************************************************************************
+ * CellClass::Cell_Vessel -- Returns with pointer to a vessel located in the cell. *
+ * *
+ * Call this routine to query and return a pointer to a vessel located in the cell. If *
+ * there is no vessel present, then this routine will return NULL. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with a pointer to the vessel class object if one is present. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/20/1996 JLB : Created. *
+ *=============================================================================================*/
+VesselClass * CellClass::Cell_Vessel(void) const
+{
+ assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
+
+ return((VesselClass*)Cell_Find_Object(RTTI_VESSEL));
+}
+
+
+/***********************************************************************************************
+ * CellClass::Cell_Infantry -- Returns with pointer of first infantry unit occupying the cell. *
+ * *
+ * This routine examines the cell and returns a pointer to the first infantry unit *
+ * that occupies it. If there is no infantry unit in the cell, then NULL is returned. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with pointer to infantry unit occupying the cell or NULL if none are *
+ * present. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 12/21/1994 JLB : Created. *
+ *=============================================================================================*/
+InfantryClass * CellClass::Cell_Infantry(void) const
+{
+ assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
+
+ return((InfantryClass*)Cell_Find_Object(RTTI_INFANTRY));
+}
+
+
+#ifdef SORTDRAW
+static bool _Calc_Partial_Window(int cellx, int celly, int & drawx, int & drawy)
+{
+ int & px = WindowList[WINDOW_PARTIAL][WINDOWX];
+ int & py = WindowList[WINDOW_PARTIAL][WINDOWY];
+ int & pw = WindowList[WINDOW_PARTIAL][WINDOWWIDTH];
+ int & ph = WindowList[WINDOW_PARTIAL][WINDOWHEIGHT];
+ int & tx = WindowList[WINDOW_TACTICAL][WINDOWX];
+ int & ty = WindowList[WINDOW_TACTICAL][WINDOWY];
+ int & tw = WindowList[WINDOW_TACTICAL][WINDOWWIDTH];
+ int & th = WindowList[WINDOW_TACTICAL][WINDOWHEIGHT];
+
+ px = cellx + tx;
+ py = celly + ty;
+ pw = CELL_PIXEL_W;
+ ph = CELL_PIXEL_H;
+
+ if (px < tx) {
+ pw -= tx - px;
+ px = tx;
+ }
+ if (pw < 1) return(false);
+
+ if (py < ty) {
+ ph -= ty - py;
+ py = ty;
+ }
+ if (ph < 1) return(false);
+
+ if (px + pw > tx + tw) {
+ pw -= (px + pw) - (tx + tw);
+ }
+ if (pw < 1) return(false);
+
+ if (py + ph > ty + th) {
+ ph -= (py + ph) - (ty + th);
+ }
+ if (ph < 1) return(false);
+
+ drawx = drawx - (px-tx);
+ drawy = drawy - (py-ty);
+ return(true);
+}
+
+
+static int _ocompare(const void * left, const void * right)
+{
+ COORDINATE lcoord = (*((ObjectClass **)left))->Sort_Y();
+ COORDINATE rcoord = (*((ObjectClass **)right))->Sort_Y();
+ if (lcoord < rcoord) return(-1);
+ if (lcoord > rcoord) return(1);
+ return(0);
+}
+#endif
+
+
+/***********************************************************************************************
+ * CellClass::Draw_It -- Draws the cell imagery at the location specified. *
+ * *
+ * This is the gruntwork cell rendering code. It draws the cell at the screen location *
+ * specified. This routine doesn't draw any overlapping or occupying units. It only *
+ * deals with the ground (cell) layer -- icon level. *
+ * *
+ * INPUT: x,y -- The screen coordinates to render the cell imagery at. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/18/1994 JLB : Created. *
+ * 08/21/1994 JLB : Revised for simple template objects. *
+ * 11/01/1994 BRR : Updated placement cursor; draws actual object *
+ * 11/14/1994 BRR : Added remapping code to show passable areas *
+ * 12/02/1994 BRR : Added trigger display *
+ * 12/11/1994 JLB : Mixes up clear terrain through pseudo-random table. *
+ * 04/25/1995 JLB : Smudges drawn BELOW overlays. *
+ * 07/22/1996 JLB : Objects added to draw process. *
+ *=============================================================================================*/
+void CellClass::Draw_It(int x, int y, bool objects) const
+{
+ assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
+
+ if (!objects) {
+ BStart(BENCH_CELL);
+
+ TemplateTypeClass const * ttype = 0;
+ int icon; // The icon number to use from the template set.
+ CELL cell = Cell_Number();
+ void * remap = NULL;
+ #ifdef SCENARIO_EDITOR
+ TemplateTypeClass * tptr;
+// TriggerClass * trig;
+ int i;
+ char waypt[3];
+ #endif
+
+ CellCount++;
+
+ /*
+ ** Fetch a pointer to the template type associated with this cell.
+ */
+ if (TType != TEMPLATE_NONE && TType != TEMPLATE_CLEAR1 && TType != 255) {
+ ttype = &TemplateTypeClass::As_Reference(TType);
+ icon = TIcon;
+ } else {
+ ttype = &TemplateTypeClass::As_Reference(TEMPLATE_CLEAR1);
+ icon = Clear_Icon();
+ }
+
+ #ifdef CHEAT_KEYS
+ /*
+ ** Draw the stamp of the template.
+ */
+ if (Debug_Icon) {
+ LogicPage->Fill_Rect(Map.TacPixelX+x, Map.TacPixelY+y, Map.TacPixelX+x+ICON_PIXEL_W-1, Map.TacPixelY+y+ICON_PIXEL_H-1, Sim_Random_Pick(1, 254));
+ FontXSpacing -= 2;
+ Fancy_Text_Print("%02X%02X\r%d%d%d\r%d %d", Map.TacPixelX+x+(ICON_PIXEL_W>>1), Map.TacPixelY+y, &GreyScheme, TBLACK, TPF_EFNT|TPF_CENTER|TPF_BRIGHT_COLOR|TPF_FULLSHADOW,
+ Cell_Y(cell), Cell_X(cell),
+ //(CurrentObject.Count() && CurrentObject[0]->Is_Techno()) ? ((TechnoClass *)CurrentObject[0])->House->Which_Zone(cell) : -1,
+ Zones[MZONE_NORMAL],Zones[MZONE_CRUSHER],Zones[MZONE_DESTROYER],
+ Overlay, OverlayData
+ );
+ FontXSpacing += 2;
+ } else {
+ #endif
+
+ #ifdef SCENARIO_EDITOR
+ /*
+ ** Set up the remap table for this icon.
+ */
+ if (Debug_Map && Debug_Passable) {
+ if (::Ground[Land].Cost[0] == 0 || (Cell_Occupier() != NULL &&
+ Cell_Occupier()->What_Am_I() != RTTI_INFANTRY)) { // impassable
+ remap = DisplayClass::FadingRed;
+ } else {
+ if (::Ground[Land].Cost[0] > fixed(1, 3)) { // pretty passable
+ remap = DisplayClass::FadingGreen;
+ } else {
+ remap = DisplayClass::FadingYellow; // moderately passable
+ }
+ }
+ }
+ #endif
+
+ /*
+ ** This is the underlying terrain icon.
+ */
+ if (ttype->Get_Image_Data()) {
+ LogicPage->Draw_Stamp(ttype->Get_Image_Data(), icon, x, y, NULL, WINDOW_TACTICAL);
+ if (remap) {
+ LogicPage->Remap(x+Map.TacPixelX, y+Map.TacPixelY, ICON_PIXEL_W, ICON_PIXEL_H, remap);
+ }
+ }
+
+ #ifdef SCENARIO_EDITOR
+ /*
+ ** Draw the map editor's "current" cell. This is the cell that can be
+ ** assigned attributes such as tag labels.
+ ** This must be draw before the placement cursor, but after drawing the
+ ** objects in the cell.
+ */
+ if (Debug_Map && CurrentCell == Cell_Number()) {
+ LogicPage->Draw_Rect(x+Map.TacPixelX, y+Map.TacPixelY, Map.TacPixelX + x + CELL_PIXEL_W - 1, Map.TacPixelY + y + CELL_PIXEL_H - 1, YELLOW);
+ }
+ #endif
+
+ /*
+ ** Redraw any smudge.
+ */
+ if (Smudge != SMUDGE_NONE) {
+ SmudgeTypeClass::As_Reference(Smudge).Draw_It(x, y, SmudgeData);
+ }
+
+ /*
+ ** Draw the overlay object.
+ */
+ if (Overlay != OVERLAY_NONE) {
+ OverlayTypeClass const & otype = OverlayTypeClass::As_Reference(Overlay);
+ IsTheaterShape = (bool)otype.IsTheater; //Tell Build_Frame if this overlay is theater specific
+ CC_Draw_Shape(otype.Get_Image_Data(), OverlayData, (x+(CELL_PIXEL_W>>1)), (y+(CELL_PIXEL_H>>1)), WINDOW_TACTICAL, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST, NULL, DisplayClass::UnitShadow);
+ IsTheaterShape = false;
+ }
+
+ #ifdef SCENARIO_EDITOR
+ if (Debug_Map) {
+ /*
+ ** Draw the cell's Trigger mnemonic, if it has a trigger
+ */
+ if (Trigger.Is_Valid()) {
+ Fancy_Text_Print(Trigger->Class->IniName, x+Map.TacPixelX, y+Map.TacPixelY, &ColorRemaps[PCOLOR_RED], TBLACK, TPF_EFNT|TPF_FULLSHADOW);
+ }
+
+ /*
+ ** Draw the cell's Waypoint designation if there is one.
+ */
+ if (IsWaypoint) {
+ for (i = 0; i < WAYPT_HOME; i++) {
+ if (Scen.Waypoint[i] == Cell_Number()) {
+ if (i < 26) {
+ waypt[0] = 'A' + i;
+ waypt[1] = 0;
+ } else {
+ waypt[0] = 'A' + (i/26)-1;
+ waypt[1] = 'A' + (i % 26);
+ waypt[2] = 0;
+ }
+ Fancy_Text_Print(waypt, Map.TacPixelX + x + CELL_PIXEL_W / 2,
+ Map.TacPixelY + y + (CELL_PIXEL_H / 2) - 3,
+ &ColorRemaps[PCOLOR_RED], TBLACK,
+ TPF_EFNT | TPF_CENTER|TPF_FULLSHADOW);
+ break;
+ }
+ }
+ if (Scen.Waypoint[WAYPT_HOME] == Cell_Number()) {
+ Fancy_Text_Print("Home", Map.TacPixelX + x, Map.TacPixelY + y + (CELL_PIXEL_H) - 7,
+ &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_EFNT|TPF_FULLSHADOW);
+ }
+ if (Scen.Waypoint[WAYPT_REINF] == Cell_Number()) {
+ Fancy_Text_Print("Reinf", Map.TacPixelX + x, Map.TacPixelY + y + (CELL_PIXEL_H) - 7,
+ &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_EFNT|TPF_FULLSHADOW);
+ }
+ }
+ }
+ #endif
+
+ /*
+ ** Draw the placement cursor:
+ ** - First, draw the hash-mark cursor, so it will appear underneath
+ ** any cursor being drawn
+ ** - If the PendingObject is a template, overlay, or smudge, draw it
+ ** - Otherwise, it's up to the Display.Refresh_Map() routine to draw it
+ */
+ if (IsCursorHere) {
+ SpeedType loco = SPEED_NONE;
+ if (Map.PendingObjectPtr) {
+ if (Map.PendingObjectPtr->What_Am_I() == RTTI_BUILDING) {
+ BuildingClass * obj = (BuildingClass *)(Map.PendingObjectPtr);
+ loco = obj->Class->Speed;
+ // if (*obj == STRUCT_SUB_PEN || *obj == STRUCT_SHIP_YARD ||
+ // *obj == STRUCT_FAKE_PEN || *obj == STRUCT_FAKE_YARD) loco = SPEED_FLOAT;
+ }
+ }
+
+ /*
+ ** Draw the hash-mark cursor:
+ */
+ if (Map.ProximityCheck && Is_Clear_To_Build(loco)) {
+ LogicPage->Draw_Stamp(DisplayClass::TransIconset, 0, x, y, NULL, WINDOW_TACTICAL);
+ } else {
+ LogicPage->Draw_Stamp(DisplayClass::TransIconset, 2, x, y, NULL, WINDOW_TACTICAL);
+ }
+
+ #ifdef SCENARIO_EDITOR
+ if (Debug_Map && Map.PendingObject) {
+
+ switch (Map.PendingObject->What_Am_I()) {
+
+ /*
+ ** Draw a template:
+ ** - Compute the icon offset of this cell for this template, using
+ ** ZoneCell+ZoneOffset to get the upper-left corner of the placement
+ ** cursor
+ ** - Draw the icon
+ */
+ case RTTI_TEMPLATETYPE:
+ tptr = (TemplateTypeClass *)Map.PendingObject;
+ if (tptr->Get_Image_Data()) {
+ icon = (Cell_X(cell) - Cell_X(Map.ZoneCell + Map.ZoneOffset)) +
+ (Cell_Y(cell) - Cell_Y(Map.ZoneCell + Map.ZoneOffset)) *
+ tptr->Width;
+ LogicPage->Draw_Stamp(tptr->Get_Image_Data(), icon, x, y, NULL, WINDOW_TACTICAL);
+ }
+ break;
+
+ /*
+ ** Draw an overlay; just use the existing 'OverlayData' even though
+ ** it means nothing.
+ */
+ case RTTI_OVERLAYTYPE:
+ OverlayTypeClass::As_Reference(((OverlayTypeClass *)Map.PendingObject)->Type).Draw_It(x, y, OverlayData);
+ break;
+
+ /*
+ ** Draw a smudge
+ */
+ case RTTI_SMUDGETYPE:
+ SmudgeTypeClass::As_Reference(((SmudgeTypeClass *)Map.PendingObject)->Type).Draw_It(x, y, 0);
+ break;
+
+ default:
+ break;
+ }
+ }
+ #endif
+ }
+
+ /*
+ ** Draw the flag if there is one located at this cell.
+ */
+ if (IsFlagged) {
+ void const * flag_remap = HouseClass::As_Pointer(Owner)->Remap_Table(false, REMAP_NORMAL);
+ CC_Draw_Shape(MFCD::Retrieve("FLAGFLY.SHP"), Frame % 14, x+(ICON_PIXEL_W/2), y+(ICON_PIXEL_H/2), WINDOW_TACTICAL, SHAPE_CENTER|SHAPE_GHOST|SHAPE_FADING, flag_remap, DisplayClass::UnitShadow);
+ }
+
+ #ifdef CHEAT_KEYS
+ }
+ #endif
+ BEnd(BENCH_CELL);
+ }
+
+#ifdef SORTDRAW
+ if (objects) {
+ BStart(BENCH_OBJECTS);
+
+ /*
+ ** Build a list of objects to draw into a working buffer. There is a
+ ** big presumption here -- it is presumed that if the cell is to be
+ ** redrawn, then all objects in the cell should properly be flagged to
+ ** be redrawn as well. Normally, this isn't a problem, but for subs
+ ** the IsToDisplay flag MUST REMAIN SET. This is because there is a
+ ** hack overpass after the cells are redrawn so that subs can be
+ ** redrawn separately.
+ */
+ ObjectClass * optr[20 + ARRAY_SIZE(Overlapper)];
+ int count = 0;
+ ObjectClass * object = Cell_Occupier();
+ while (object != NULL) {
+ if (!object->IsActive) break;
+ optr[count] = object;
+ object->IsToDisplay = true;
+ object = object->Next;
+ count++;
+ }
+ for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) {
+ object = Overlapper[index];
+ if (object != NULL && object->IsActive) {
+ object->IsToDisplay = true;
+ optr[count] = object;
+ count++;
+ }
+ }
+
+ /*
+ ** Sort the object list so that objects will be drawn from
+ ** back to front.
+ */
+ switch (count) {
+
+ /*
+ ** If there are zero or one object, then sorting is
+ ** unnecessary.
+ */
+ case 0:
+ case 1:
+ break;
+
+ /*
+ ** Two objects can be sorted with a single compare and swap.
+ */
+ case 2:
+ if (optr[0]->Sort_Y() > optr[1]->Sort_Y()) {
+ swap(optr[0], optr[1]);
+ }
+ break;
+
+ /*
+ ** Three objects can be sorted with three compares and swaps.
+ */
+ case 3:
+ if (optr[0]->Sort_Y() > optr[2]->Sort_Y()) {
+ swap(optr[0], optr[2]);
+ }
+ if (optr[0]->Sort_Y() > optr[1]->Sort_Y()) {
+ swap(optr[0], optr[1]);
+ }
+ if (optr[1]->Sort_Y() > optr[2]->Sort_Y()) {
+ swap(optr[1], optr[2]);
+ }
+ break;
+
+ /*
+ ** Large number of objects can be effeciently sorted by using
+ ** a quicksort.
+ */
+ default:
+ qsort(optr, count, sizeof(optr[0]), _ocompare);
+ break;
+ }
+
+ /*
+ ** Draw any objects that happen to be in or overlapping this cell.
+ */
+ for (index = 0; index < count; index++) {
+ object = optr[index];
+ int xx,yy;
+ if (object->IsToDisplay && (!object->Is_Techno() || ((TechnoClass *)object)->Visual_Character() == VISUAL_NORMAL) && Map.Coord_To_Pixel(object->Render_Coord(), xx, yy)) {
+ if (_Calc_Partial_Window(x, y, xx, yy)) {
+ object->Draw_It(xx, yy, WINDOW_PARTIAL);
+ if (Debug_Map) {
+ object->IsToDisplay = true;
+ } else {
+ object->IsToDisplay = false;
+ }
+ }
+ object->IsToDisplay = false;
+ }
+ }
+ BEnd(BENCH_OBJECTS);
+ }
+#endif
+
+}
+
+
+/***********************************************************************************************
+ * CellClass::Concrete_Calc -- Calculates the concrete icon to use for the cell. *
+ * *
+ * This routine examines the cells around the current one and from this, determines what *
+ * concrete icon shape to use (if any). The cell data is adjusted and the cell is marked *
+ * for redraw if the icon changed. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/01/1994 JLB : Created. *
+ *=============================================================================================*/
+void CellClass::Concrete_Calc(void)
+{
+#ifdef OBSOLETE
+ assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
+
+ static FacingType _even[5] = {FACING_N, FACING_S, FACING_SW, FACING_W, FACING_NW};
+ static FacingType _odd[5] = {FACING_N, FACING_NE, FACING_E, FACING_SE, FACING_S};
+ FacingType * ptr; // Working pointer into adjacent cell list.
+ int index; // Constructed bit index.
+ int icon; // Icon number.
+ bool isodd; // Is this for the odd column?
+
+#define OF_N 0x01
+#define OF_NE 0x02
+#define OF_E 0x04
+#define OF_SE 0x08
+#define OF_S 0x10
+
+#define EF_N 0x01
+#define EF_NW 0x10
+#define EF_W 0x08
+#define EF_SW 0x04
+#define EF_S 0x02
+
+ /*
+ ** Determine if the even or odd row logic is necessary.
+ */
+ isodd = ((Cell_Number() & 0x01) != 0);
+
+ /*
+ ** Fetch correct pointer depending on whether this is for an
+ ** odd or even row.
+ */
+ ptr = (isodd) ? _odd : _even;
+
+ /*
+ ** Build an index according to the presence of concrete in the special
+ ** adjacent cells. This is a short list of adjacent cell flags since
+ ** only 5 adjacent cells need to be examined. The choice of which 5
+ ** depends on whether this is for an even or odd column.
+ */
+ index = 0;
+ for (int i = 0; i < (sizeof(_even)/sizeof(_even[0])); i++) {
+ CellClass & cellptr = Adjacent_Cell(*ptr++);
+
+ if (cellptr.Overlay == OVERLAY_CONCRETE) {
+ index |= (1< 0 && Land == LAND_TIBERIUM) {
+ if (OverlayData+1 > levels) {
+ OverlayData -= levels;
+ reducer = levels;
+ } else {
+ Overlay = OVERLAY_NONE;
+ reducer = OverlayData;
+ OverlayData = 0;
+ Recalc_Attributes();
+ }
+ }
+ return(reducer);
+}
+
+
+/***********************************************************************************************
+ * CellClass::Reduce_Wall -- Damages a wall, if damage is high enough. *
+ * *
+ * This routine will change the wall shape used for a wall if it's damaged. *
+ * *
+ * INPUT: damage -- The number of damage points the wall was hit with. If this value is *
+ * -1, then the entire wall at this cell will be destroyed. *
+ * *
+ * OUTPUT: bool; Was the wall destroyed? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 03/15/1995 BWG : Created. *
+ * 03/19/1995 JLB : Updates cell information if wall was destroyed. *
+ * 10/06/1996 JLB : Updates zone as necessary. *
+ *=============================================================================================*/
+int CellClass::Reduce_Wall(int damage)
+{
+ assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
+
+ if (Overlay != OVERLAY_NONE) {
+ bool destroyed = false;
+ OverlayTypeClass const & wall = OverlayTypeClass::As_Reference(Overlay);
+
+ if (wall.IsWall) {
+
+ /*
+ ** If the damage was great enough to ensure wall destruction, reduce the wall by one
+ ** level (no more). Otherwise determine wall reduction based on a percentage chance
+ ** proportional to the damage received and the wall's strength.
+ */
+ if (damage == -1 || damage >= wall.DamagePoints) {
+ destroyed = true;
+ } else {
+ destroyed = Random_Pick(0, wall.DamagePoints) < damage;
+ }
+
+ /*
+ ** If the wall is destroyed, destroy it and check for any adjustments to
+ ** adjacent walls.
+ */
+ if (destroyed) {
+ OverlayData+=16;
+ if (damage == -1 ||
+ (OverlayData>>4) >= wall.DamageLevels ||
+ ((OverlayData>>4) == wall.DamageLevels-1 && (OverlayData & 0xF)==0) ) {
+
+ Owner = HOUSE_NONE;
+ Overlay = OVERLAY_NONE;
+ OverlayData = 0;
+ Recalc_Attributes();
+ Redraw_Objects();
+ Adjacent_Cell(FACING_N).Wall_Update();
+ Adjacent_Cell(FACING_W).Wall_Update();
+ Adjacent_Cell(FACING_S).Wall_Update();
+ Adjacent_Cell(FACING_E).Wall_Update();
+ Detach_This_From_All(As_Target());
+
+ /*
+ ** The zone calculation changes now for non-crushable zone sensitive
+ ** travellers.
+ */
+ if (wall.IsCrushable) {
+ Map.Zone_Reset(MZONEF_NORMAL);
+ } else {
+ Map.Zone_Reset(MZONEF_CRUSHER|MZONEF_NORMAL);
+ }
+ return(true);
+ }
+ }
+ }
+ }
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * CellClass::Spot_Index -- returns cell sub-coord index for given COORDINATE *
+ * *
+ * INPUT: *
+ * coord COORDINATE to compute index for *
+ * *
+ * OUTPUT: *
+ * index into StoppingCoord that's closest to this coord *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 11/21/1994 BR : Created. *
+ * 12/10/1994 JLB : Uses alternate sub-position algorithm. *
+ *=============================================================================================*/
+int CellClass::Spot_Index(COORDINATE coord)
+{
+ COORDINATE rel = Coord_Fraction(coord); // Sub coordinate value within cell.
+
+ /*
+ ** If the coordinate is close enough to the center of the cell, then return
+ ** the center position index.
+ */
+ if (Distance(rel, (COORDINATE)0x00800080L) < 60) {
+ return(0);
+ }
+
+ /*
+ ** Since the center cell position has been eliminated, a simple comparison
+ ** as related to the center of the cell can be used to determine the sub
+ ** position. Take advantage of the fact that the sub positions are organized
+ ** from left to right, top to bottom.
+ */
+ int index = 0;
+ if (Coord_X(rel) > 0x80) index |= 0x01;
+ if (Coord_Y(rel) > 0x80) index |= 0x02;
+ return(index+1);
+}
+
+
+/***********************************************************************************************
+ * CellClass::Closest_Free_Spot -- returns free spot closest to given coord *
+ * *
+ * Similar to the CellClass::Free_Spot; this routine finds the spot in *
+ * the cell closest to the given coordinate, and returns the COORDINATE of *
+ * that spot if it's available, NULL if it's not. *
+ * *
+ * INPUT: *
+ * coord coordinate to check (only sub cell position examined) *
+ * *
+ * any -- If only the closest spot is desired regardless of whether it is free or *
+ * not, then this parameter will be true. *
+ * *
+ * OUTPUT: *
+ * COORDINATE of free spot, NULL if none. The coordinate return value does not alter the cell *
+ * coordinate data portions of the coordinate passed in. Only the lower sub-cell *
+ * data is altered. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 11/08/1994 BR : Created. *
+ * 12/10/1994 JLB : Picks best of closest stopping positions. *
+ * 12/21/1994 JLB : Adds a mix-up factor if center location is occupied. *
+ *=============================================================================================*/
+COORDINATE CellClass::Closest_Free_Spot(COORDINATE coord, bool any) const
+{
+ assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
+
+ int spot_index = Spot_Index(coord);
+
+ /*
+ ** This precalculated sequence table records the closest spots to any given spot. Sequential
+ ** examination of these spots for availability ensures that the closest available one is
+ ** discovered first.
+ */
+ static unsigned char _sequence[5][4] = {
+ {1,2,3,4},
+ {0,2,3,4},
+ {0,1,4,3},
+ {0,1,4,2},
+ {0,2,3,1}
+ };
+
+ /*
+ ** In the case of the center coordinate being requested, but is occupied, then all other
+ ** sublocations are equidistant. Instead of picking a static sequence of examination, the
+ ** order is mixed up by way of this table.
+ */
+ static unsigned char _alternate[4][4] = {
+ {1,2,3,4},
+ {2,3,4,1},
+ {3,4,1,2},
+ {4,1,2,3},
+ };
+ coord = Coord_Whole(coord);
+
+ /*
+ ** Cells occupied by buildings or vehicles don't have any free spots.
+ */
+ if (!any && (Flag.Occupy.Vehicle || Flag.Occupy.Monolith)) {
+ return(NULL);
+ }
+
+ /*
+ ** If just the nearest position is desired regardless of whether occupied or not,
+ ** then just return with the stopping coordinate value.
+ */
+ if (any || Is_Spot_Free(spot_index)) {
+ return(Coord_Add(coord, StoppingCoordAbs[spot_index]));
+ }
+
+ /*
+ ** Scan through all available sub-locations in the cell in order to determine
+ ** the closest one to the coordinate requested. Use precalculated table so that
+ ** when the first free position is found, bail.
+ */
+ unsigned char * sequence;
+ if (spot_index == 0) {
+ sequence = &_alternate[Random_Pick(0, 3)][0];
+ } else {
+ sequence = &_sequence[spot_index][0];
+ }
+ for (int index = 0; index < 4; index++) {
+ int pos = *sequence++;
+
+ if (Is_Spot_Free(pos)) {
+ return(Coord_Add(coord, StoppingCoordAbs[pos]));
+ }
+ }
+
+ /*
+ ** No free spot could be found so return a NULL coordinate.
+ */
+ return(0x00000000L);
+}
+
+
+/***********************************************************************************************
+ * CellClass::Clear_Icon -- Calculates what the clear icon number should be. *
+ * *
+ * This support routine will determine what the clear icon number would be for the cell. *
+ * The icon number is determined by converting the cell number into an index into a *
+ * lookup table. This yields what appears to be a randomized map without the necessity of *
+ * generating and recording randomized map numbers. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the icon number for clear terrain if it were displayed at the *
+ * cell. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 12/26/1994 JLB : Created. *
+ * 06/09/1995 JLB : Uses 16 entry scramble algorithm. *
+ *=============================================================================================*/
+int CellClass::Clear_Icon(void) const
+{
+ assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
+
+ CELL cell = Cell_Number();
+ return((Cell_X(cell) & 0x03) | ((Cell_Y(cell) & 0x03) << 2));
+// return((cell & 0x03) | ((unsigned(cell)>>5) & 0x0C));
+}
+
+
+/***********************************************************************************************
+ * CellClass::Incoming -- Causes objects in cell to "run for cover". *
+ * *
+ * This routine is called whenever a great, but slow moving, threat is presented to the *
+ * occupants of a cell. The occupants will, in most cases, stop what they are doing and *
+ * try to get out of the way. *
+ * *
+ * INPUT: threat -- The coordinate source of the threat. *
+ * *
+ * forced -- If this threat is so major that the occupants should stop what *
+ * they are doing, then this parameter should be set to true. *
+ * *
+ * nokidding -- Override the scatter to also affect human controlled objects. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 01/10/1995 JLB : Created. *
+ * 08/02/1996 JLB : Added the "nokidding" parameter. *
+ *=============================================================================================*/
+void CellClass::Incoming(COORDINATE threat, bool forced, bool nokidding)
+{
+ assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
+
+ ObjectClass * object = NULL;
+
+ object = Cell_Occupier();
+ while (object != NULL) {
+
+ /*
+ ** Special check to make sure that friendly units never scatter.
+ */
+ if (nokidding || Rule.IsScatter || (object->Is_Techno() && ((TechnoClass *)object)->House->IQ >= Rule.IQScatter)) {
+ object->Scatter(threat, forced, nokidding);
+ }
+ object = object->Next;
+ }
+}
+
+
+/***********************************************************************************************
+ * CellClass::Adjacent_Cell -- Determines the adjacent cell according to facing. *
+ * *
+ * Use this routine to return a reference to the adjacent cell in the direction specified. *
+ * *
+ * INPUT: face -- The direction to use when determining the adjacent cell. *
+ * *
+ * OUTPUT: Returns with a reference to the adjacent cell. *
+ * *
+ * WARNINGS: If the facing value is invalid, then a reference to the same cell is returned. *
+ * *
+ * HISTORY: *
+ * 03/19/1995 JLB : Created. *
+ *=============================================================================================*/
+CellClass const & CellClass::Adjacent_Cell(FacingType face) const
+{
+ assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
+
+ if ((unsigned)face >= FACING_COUNT) {
+ return(*this);
+ }
+
+ CellClass const * ptr = this + AdjacentCell[face];
+ if ((unsigned)ptr->Cell_Number() > MAP_CELL_TOTAL) return(*this);
+ return(*ptr);
+}
+
+
+/***************************************************************************
+ * CellClass::Adjust_Threat -- Allows adjustment of threat at cell level *
+ * *
+ * INPUT: *
+ * *
+ * OUTPUT: *
+ * *
+ * WARNINGS: *
+ * *
+ * HISTORY: *
+ * 04/24/1995 PWG : Created. *
+ *=========================================================================*/
+void CellClass::Adjust_Threat(HousesType house, int threat_value)
+{
+ assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
+
+ int region = Map.Cell_Region(Cell_Number());
+
+ for (HousesType lp = HOUSE_FIRST; lp < HOUSE_COUNT; lp ++) {
+ if (lp == house) continue;
+
+ HouseClass * house_ptr = HouseClass::As_Pointer(lp);
+ if (house_ptr && (!house_ptr->IsHuman || !house_ptr->Is_Ally(house))) {
+ house_ptr->Adjust_Threat(region, threat_value);
+ }
+ }
+ if (Debug_Threat) {
+ Map.Flag_To_Redraw(true);
+ }
+}
+
+
+/***********************************************************************************************
+ * CellClass::Tiberium_Adjust -- Adjust the look of the Tiberium for smoothing purposes. *
+ * *
+ * This routine will adjust the level of the Tiberium in the cell so that it will *
+ * smoothly blend with the adjacent Tiberium. This routine should only be called for *
+ * new Tiberium cells. Existing cells that contain Tiberium follow a different growth *
+ * pattern. *
+ * *
+ * INPUT: pregame -- Is this a pregame call? Such a call will mixup the Tiberium overlay *
+ * used. *
+ * *
+ * OUTPUT: Returns with the added Tiberium value that is now available for harvesting. *
+ * *
+ * WARNINGS: The return value is only valid for the initial placement. Tiberium growth will *
+ * increase the net worth of the existing Tiberium. *
+ * *
+ * HISTORY: *
+ * 05/16/1995 JLB : Created. *
+ * 02/20/1996 JLB : Takes into account the ore type. *
+ *=============================================================================================*/
+long CellClass::Tiberium_Adjust(bool pregame)
+{
+ assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
+ if (Overlay != OVERLAY_NONE) {
+ if (OverlayTypeClass::As_Reference(Overlay).Land == LAND_TIBERIUM) {
+ static int _adj[9] = {0,1,3,4,6,7,8,10,11};
+ static int _adjgem[9] = {0,0,0,1,1,1,2,2,2};
+ int count = 0;
+
+ /*
+ ** Mixup the Tiberium overlays so that they don't look the same.
+ ** Since the type of ore is known, also record the nominal
+ ** value per step of that ore type.
+ */
+ bool gems = false;
+ int value = 0;
+ if (pregame) {
+ switch (Overlay) {
+ case OVERLAY_GOLD1:
+ case OVERLAY_GOLD2:
+ case OVERLAY_GOLD3:
+ case OVERLAY_GOLD4:
+ value = Rule.GoldValue;
+ Overlay = Random_Pick(OVERLAY_GOLD1, OVERLAY_GOLD4);
+ break;
+
+ case OVERLAY_GEMS1:
+ case OVERLAY_GEMS2:
+ case OVERLAY_GEMS3:
+ case OVERLAY_GEMS4:
+ gems = true;
+ value = Rule.GemValue*4;
+ Overlay = Random_Pick(OVERLAY_GEMS1, OVERLAY_GEMS4);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /*
+ ** Add up all adjacent cells that contain tiberium.
+ ** (Skip those cells which aren't on the map)
+ */
+ for (FacingType face = FACING_FIRST; face < FACING_COUNT; face++) {
+ CellClass & adj = Adjacent_Cell(face);
+
+ if (adj.Overlay != OVERLAY_NONE &&
+ OverlayTypeClass::As_Reference(adj.Overlay).Land == LAND_TIBERIUM) {
+ count++;
+ }
+ }
+
+ if (gems) {
+ OverlayData = _adjgem[count];
+ OverlayData = min(OverlayData, 2);
+ } else {
+ OverlayData = _adj[count];
+ }
+ return((OverlayData+1) * value);
+ }
+ }
+ return(0);
+}
+
+
+/***********************************************************************************************
+ * CellClass::Goodie_Check -- Performs crate discovery logic. *
+ * *
+ * Call this routine whenever an object enters a cell. It will check for the existence *
+ * of a crate and generate any "goodie" it might contain. *
+ * *
+ * INPUT: object -- Pointer to the object that is triggering this crate. *
+ * *
+ * OUTPUT: Can the object continue to enter this cell? A false return value means that the *
+ * cell is now occupied and must not be entered. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/22/1995 JLB : Created. *
+ * 07/08/1995 JLB : Added a bunch of goodies to the crates. *
+ * 06/17/1996 JLB : Revamped for Red Alert *
+ *=============================================================================================*/
+bool CellClass::Goodie_Check(FootClass * object)
+{
+ assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
+
+ if (object != NULL && Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Overlay).IsCrate) {
+ bool force_mcv = false;
+ int force_money = 0;
+ int damage;
+ COORDINATE coord;
+
+ /*
+ ** Determine the total number of shares for all the crate powerups. This is used as
+ ** the base pool to determine the odds from.
+ */
+ int total_shares = 0;
+ for (int index = CRATE_FIRST; index < CRATE_COUNT; index++) {
+ total_shares += CrateShares[index];
+ }
+
+ /*
+ ** Pick a random crate powerup according to the shares allotted to each powerup.
+ ** In solo play, the bonus item is dependant upon the rules control.
+ */
+ CrateType powerup;
+ if (Session.Type == GAME_NORMAL) {
+
+ /*
+ ** Solo play has money amount determined by rules.ini file.
+ */
+ force_money = Rule.SoloCrateMoney;
+
+ if (Overlay == OVERLAY_STEEL_CRATE) {
+ powerup = Rule.SilverCrate;
+ }
+
+ if (Overlay == OVERLAY_WOOD_CRATE) {
+ powerup = Rule.WoodCrate;
+ }
+
+ if (Overlay == OVERLAY_WATER_CRATE) {
+//Mono_Printf("%d-%s.\n", __LINE__, __FILE__);
+ powerup = Rule.WaterCrate;
+ }
+
+ } else {
+ int pick = Random_Pick(1, total_shares);
+
+ int share_count = 0;
+ for (powerup = CRATE_FIRST; powerup < CRATE_COUNT; powerup++) {
+ share_count += CrateShares[powerup];
+ if (pick <= share_count) break;
+ }
+ assert(powerup != CRATE_COUNT);
+
+ /*
+ ** Depending on what was picked, there might be an alternate goodie if the selected
+ ** goodie would have no effect.
+ */
+ switch (powerup) {
+ case CRATE_UNIT:
+ if (object->House->CurUnits > 50) powerup = CRATE_MONEY;
+ break;
+
+ case CRATE_SQUAD:
+ if (object->House->CurInfantry > 100) powerup = CRATE_MONEY;
+ break;
+
+ case CRATE_DARKNESS:
+ if (object->House->IsGPSActive) powerup = CRATE_MONEY;
+ break;
+
+ case CRATE_ARMOR:
+ if (object->ArmorBias != 1) powerup = CRATE_MONEY;
+ break;
+
+ case CRATE_SPEED:
+ if (object->SpeedBias != 1 || object->What_Am_I() == RTTI_AIRCRAFT) powerup = CRATE_MONEY;
+ break;
+
+ case CRATE_FIREPOWER:
+ if (object->FirepowerBias != 1 || !object->Is_Weapon_Equipped()) powerup = CRATE_MONEY;
+ break;
+
+ case CRATE_REVEAL:
+ if (object->House->IsVisionary) {
+ if (object->House->IsGPSActive) {
+ powerup = CRATE_MONEY;
+ } else {
+ powerup = CRATE_DARKNESS;
+ }
+ }
+ break;
+
+ case CRATE_CLOAK:
+ if (object->IsCloakable) powerup = CRATE_MONEY;
+ break;
+
+// case CRATE_HEAL_BASE:
+// if (object->House->BScan == 0) powerup = CRATE_UNIT;
+
+ case CRATE_MONEY:
+ break;
+
+ case CRATE_TIMEQUAKE:
+ /*
+ ** For the time quake crate, scan through and count up all the
+ ** units (and infantry and ships and aircraft) and if either
+ ** side has very few, allow the time quake. Otherwise,
+ ** change the crate to money or something. Only do this for
+ ** multiplay - for solo play, they get what they get. First,
+ ** check for time - the chance for getting a time quake crate
+ ** should be very very low when they first start the mission,
+ ** but as time goes on the chance goes up.
+ */
+ if (Session.Type != GAME_NORMAL) {
+ int i,ucount;
+ int minunits = 1000;
+ bool found = false;
+ unsigned long minutes = (Score.ElapsedTime / TIMER_MINUTE);
+ if (minutes > 100) minutes = 100;
+ if (Random_Pick(0,100-(int)minutes) == 0) {
+ for (i=0; i < (Session.Players.Count() + Session.Options.AIPlayers); i++) {
+ ucount = 0;
+ HouseClass * hptr = Houses.Ptr(i + HOUSE_MULTI1);
+ if (hptr != NULL && !hptr->IsDefeated) {
+ int j;
+ for( j=0; j < UNIT_COUNT; j++) {
+ ucount += hptr->QuantityU(j);
+ }
+ for( j=0; j < INFANTRY_COUNT; j++) {
+ ucount += hptr->QuantityI(j);
+ }
+ for( j=0; j < AIRCRAFT_COUNT; j++) {
+ ucount += hptr->QuantityA(j);
+ }
+ for( j=0; j < VESSEL_COUNT; j++) {
+ ucount += hptr->QuantityV(j);
+ }
+ int bcount = 0;
+ for( j=0; j < STRUCT_COUNT; j++) {
+ bcount += hptr->QuantityB(j);
+ }
+ ucount += bcount/2; // weight buildings less
+ minunits = min(minunits, ucount);
+ }
+ }
+ if (Random_Pick(0, minunits) == minunits) {
+ found = true;
+ }
+ }
+
+ if (!found) {
+ powerup = CRATE_MONEY;
+ }
+ }
+ break;
+ }
+ /*
+ ** Possibly force it to be an MCV if there is
+ ** sufficient money and no buildings left.
+ */
+ if ( object->House->BScan == 0 &&
+ object->House->Available_Money() > ( (BuildingTypeClass::As_Reference(STRUCT_REFINERY).Cost + BuildingTypeClass::As_Reference(STRUCT_POWER).Cost) * object->House->CostBias) &&
+ Session.Options.Bases &&
+ !(object->House->UScan & UNITF_MCV)) {
+ powerup = CRATE_UNIT;
+ force_mcv = true;
+ }
+
+ /*
+ ** If the powerup is money but there is insufficient money to build a refinery but there is a construction
+ ** yard available, then force the money to be enough to rebuild the refinery.
+ */
+ if (powerup == CRATE_MONEY && (object->House->BScan & (STRUCTF_CONST|STRUCTF_REFINERY)) == STRUCTF_CONST &&
+ object->House->Available_Money() < BuildingTypeClass::As_Reference(STRUCT_REFINERY).Cost * object->House->CostBias) {
+
+ force_money = BuildingTypeClass::As_Reference(STRUCT_REFINERY).Cost * object->House->CostBias;
+ }
+
+ /*
+ ** Special override for water crates so that illegal goodies items
+ ** won't appear.
+ */
+ if (Overlay == OVERLAY_WATER_CRATE) {
+ switch (powerup) {
+ case CRATE_UNIT:
+ case CRATE_SQUAD:
+ powerup = CRATE_MONEY;
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ /*
+ ** Keep track of the number of each type of crate found
+ */
+ if (Session.Type == GAME_INTERNET) {
+ object->House->TotalCrates->Increment_Unit_Total(powerup);
+ }
+
+ /*
+ ** Remove the crate from the map.
+ */
+ Map.Remove_Crate(Cell_Number());
+// Map[Cell_Number()].Overlay = OVERLAY_NONE;
+
+ if (Session.Type != GAME_NORMAL && Rule.IsMPCrates) {
+ Map.Place_Random_Crate();
+ }
+
+ /*
+ ** Generate any corresponding animation associated with this crate powerup.
+ */
+ if (CrateAnims[powerup] != ANIM_NONE) {
+ new AnimClass(CrateAnims[powerup], Cell_Coord());
+ }
+
+ /*
+ ** Create the effect requested.
+ */
+ bool tospeak = false;
+ switch (powerup) {
+ case CRATE_TIMEQUAKE:
+ TimeQuake = true;
+ break;
+
+ /*
+ ** Give the player money.
+ */
+ case CRATE_MONEY:
+crate_money:
+ if (force_money > 0) {
+ object->House->Refund_Money(force_money);
+ } else {
+ object->House->Refund_Money(Random_Pick(CrateData[powerup], CrateData[powerup]+900));
+ }
+ break;
+
+ /*
+ ** Shroud the world in blackness.
+ */
+ case CRATE_DARKNESS:
+ if (object->House == PlayerPtr) {
+ Map.Shroud_The_Map();
+ }
+ break;
+
+ /*
+ ** Reveal the entire map.
+ */
+ case CRATE_REVEAL:
+ object->House->IsVisionary = true;
+ if (object->House == PlayerPtr) {
+ for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) {
+ Map.Map_Cell(cell, PlayerPtr);
+ }
+ Map.Flag_To_Redraw(true);
+ }
+ break;
+
+ /*
+ ** Try to create a unit where the crate was.
+ */
+ case CRATE_UNIT: {
+ UnitTypeClass const * utp = NULL;
+
+ /*
+ ** Give the player an MCV if he has no base left but does have more than enough
+ ** money to rebuild a new base. Of course, if he already has an MCV, then don't
+ ** give him another one.
+ */
+ if (force_mcv) {
+ utp = &UnitTypeClass::As_Reference(UNIT_MCV);
+ }
+
+ /*
+ ** If the player has a base and a refinery, but no harvester, then give him
+ ** a free one.
+ */
+ if (utp == NULL && (object->House->BScan & STRUCTF_REFINERY) && !(object->House->UScan & UNITF_HARVESTER)) {
+ utp = &UnitTypeClass::As_Reference(UNIT_HARVESTER);
+ }
+
+ /*
+ ** Check for special unit type override value.
+ */
+ if (Rule.UnitCrateType != UNIT_NONE) {
+ utp = &UnitTypeClass::As_Reference(Rule.UnitCrateType);
+ }
+
+ /*
+ ** If no unit type has been determined, then pick one at random.
+ */
+ while (utp == NULL) {
+#ifdef FIXIT_ANTS
+#ifdef FIXIT_CSII // checked - ajw 9/28/98
+ UnitType utype = Random_Pick(UNIT_FIRST, (UnitType)(UNIT_RA_COUNT-1 -3));
+#else
+ UnitType utype = Random_Pick(UNIT_FIRST, (UnitType)(UNIT_COUNT-1 -3));
+#endif
+#else
+ UnitType utype = Random_Pick(UNIT_FIRST, (UnitType)(UNIT_COUNT-1));
+#endif
+ if (utype != UNIT_MCV || Session.Options.Bases) {
+ utp = &UnitTypeClass::As_Reference(utype);
+ if (utp->IsCrateGoodie && (utp->Ownable & (1 << HouseClass::As_Pointer(object->Owner())->ActLike))) {
+ break;
+ }
+ utp = NULL;
+ }
+ }
+
+ if (utp != NULL) {
+ UnitClass * goodie_unit = (UnitClass *)utp->Create_One_Of(object->House);
+ if (goodie_unit != NULL) {
+ if (goodie_unit->Unlimbo(Cell_Coord())) {
+ return(false);
+ }
+
+ /*
+ ** Try to place the object into a nearby cell if something is preventing
+ ** placement at the crate location.
+ */
+ CELL cell = Map.Nearby_Location(Cell_Number(), goodie_unit->Class->Speed);
+ if (goodie_unit->Unlimbo(::Cell_Coord(cell))) {
+ return(false);
+ }
+ delete goodie_unit;
+ goto crate_money;
+ }
+ }
+ }
+ break;
+
+ /*
+ ** Create a squad of miscellaneous composition.
+ */
+ case CRATE_SQUAD:
+ for (index = 0; index < 5; index++) {
+ static InfantryType _inf[] = {
+ INFANTRY_E1,INFANTRY_E1,INFANTRY_E1,INFANTRY_E1,INFANTRY_E1,INFANTRY_E1,
+ INFANTRY_E2,
+ INFANTRY_E3,
+ INFANTRY_RENOVATOR
+ };
+ if (!InfantryTypeClass::As_Reference(_inf[Random_Pick(0, ARRAY_SIZE(_inf)-1)]).Create_And_Place(Cell_Number(), object->Owner())) {
+ if (index == 0) {
+ goto crate_money;
+ }
+ }
+ }
+ return(false);
+
+ /*
+ ** A one para-bomb mission.
+ */
+ case CRATE_PARA_BOMB:
+ if (object->House->SuperWeapon[SPC_PARA_BOMB].Enable(true)) {
+ if (object->IsOwnedByPlayer) {
+ Map.Add(RTTI_SPECIAL, SPC_PARA_BOMB);
+ Map.Column[1].Flag_To_Redraw();
+ }
+ }
+ break;
+
+ /*
+ ** A one time sonar pulse
+ */
+ case CRATE_SONAR:
+ if (object->House->SuperWeapon[SPC_SONAR_PULSE].Enable(true)) {
+ if (object->IsOwnedByPlayer) {
+ Map.Add(RTTI_SPECIAL, SPC_SONAR_PULSE);
+ Map.Column[1].Flag_To_Redraw();
+ }
+ }
+ break;
+
+ /*
+ ** A group of explosions are triggered around the crate.
+ */
+ case CRATE_EXPLOSION:
+ if (object != NULL) {
+ int d = CrateData[powerup];
+ object->Take_Damage(d, 0, WARHEAD_HE, 0, true);
+ }
+ for (index = 0; index < 5; index++) {
+ COORDINATE frag_coord = Coord_Scatter(Cell_Coord(), Random_Pick(0, 0x0200));
+ new AnimClass(ANIM_FBALL1, frag_coord);
+ damage = CrateData[powerup];
+ Explosion_Damage(frag_coord, damage, NULL, WARHEAD_HE);
+ }
+ break;
+
+ /*
+ ** A napalm blast is triggered.
+ */
+ case CRATE_NAPALM:
+ coord = Coord_Mid(Cell_Coord(), object->Center_Coord());
+ new AnimClass(ANIM_NAPALM3, coord);
+ if (object != NULL) {
+ int d = CrateData[powerup];
+ object->Take_Damage(d, 0, WARHEAD_FIRE, 0, true);
+ }
+ damage = CrateData[powerup];
+ Explosion_Damage(coord, damage, NULL, WARHEAD_FIRE);
+ break;
+
+ /*
+ ** All objects within a certain range will gain the ability to cloak.
+ */
+ case CRATE_CLOAK:
+ for (index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) {
+ ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index];
+
+ if (obj && obj->Is_Techno() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius) {
+ ((TechnoClass *)obj)->IsCloakable = true;
+ }
+ }
+ break;
+
+ /*
+ ** All of the player's objects heal up.
+ */
+ case CRATE_HEAL_BASE:
+ if (object->IsOwnedByPlayer) {
+ Sound_Effect(VOC_HEAL, object->Center_Coord());
+ }
+ for (index = 0; index < Logic.Count(); index++) {
+ ObjectClass * obj = Logic[index];
+
+ if (obj && object->Is_Techno() && object->House->Class->House == obj->Owner()) {
+ obj->Strength = obj->Class_Of().MaxStrength;
+ }
+ }
+ break;
+
+
+ case CRATE_ICBM:
+ if (object->House->SuperWeapon[SPC_NUCLEAR_BOMB].Enable(true)) {
+ if (object->IsOwnedByPlayer) {
+ Map.Add(RTTI_SPECIAL, SPC_NUCLEAR_BOMB);
+ Map.Column[1].Flag_To_Redraw();
+ }
+ }
+ break;
+
+ case CRATE_ARMOR:
+ for (index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) {
+ ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index];
+
+ if (obj != NULL && obj->Is_Techno() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius && ((TechnoClass *)obj)->ArmorBias == 1) {
+ fixed val = ((TechnoClass *)obj)->ArmorBias * Inverse(fixed(CrateData[powerup], 256));
+ ((TechnoClass *)obj)->ArmorBias = val;
+ if (obj->Owner() == PlayerPtr->Class->House) tospeak = true;
+ }
+ }
+ if (tospeak) Speak(VOX_UPGRADE_ARMOR);
+ break;
+
+ case CRATE_SPEED:
+ for (index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) {
+ ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index];
+
+ if (obj && obj->Is_Foot() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius && ((FootClass *)obj)->SpeedBias == 1 && obj->What_Am_I() != RTTI_AIRCRAFT) {
+ FootClass * foot = (FootClass *)obj;
+
+ fixed val = foot->SpeedBias * fixed(CrateData[powerup], 256);
+ foot->SpeedBias = val;
+ if (foot->IsOwnedByPlayer) tospeak = true;
+ }
+ }
+ if (tospeak) Speak(VOX_UPGRADE_SPEED);
+ break;
+
+ case CRATE_FIREPOWER:
+ for (index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) {
+ ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index];
+
+ if (obj && obj->Is_Techno() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius && ((TechnoClass *)obj)->FirepowerBias == 1) {
+
+ fixed val = ((TechnoClass *)obj)->FirepowerBias * fixed(CrateData[powerup], 256);
+ ((TechnoClass *)obj)->FirepowerBias = val;
+ if (obj->Owner() == PlayerPtr->Class->House) tospeak = true;
+ }
+ }
+ if (tospeak) Speak(VOX_UPGRADE_FIREPOWER);
+ break;
+
+ case CRATE_INVULN:
+ for (index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) {
+ ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index];
+
+ if (obj && obj->Is_Techno() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius) {
+ ((TechnoClass *)obj)->IronCurtainCountDown = (TICKS_PER_MINUTE * fixed(CrateData[powerup], 256));
+ obj->Mark(MARK_CHANGE);
+ }
+ }
+ break;
+
+ /*
+ ** A chronal vortex appears targetted at the triggering object.
+ */
+ case CRATE_VORTEX:
+ if ( !ChronalVortex.Is_Active()) {
+ ChronalVortex.Appear ( Cell_Coord() );
+ ChronalVortex.Set_Target ( (ObjectClass*) object );
+ Sound_Effect(VOC_TESLA_ZAP, object->Center_Coord());
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ return(true);
+}
+
+
+/***********************************************************************************************
+ * CellClass::Flag_Place -- Places a house flag down on the cell. *
+ * *
+ * This routine will place the house flag at this cell location. *
+ * *
+ * INPUT: house -- The house that is having its flag placed here. *
+ * *
+ * OUTPUT: Was the flag successfully placed here? *
+ * *
+ * WARNINGS: Failure to place means that the cell is impassable for some reason. *
+ * *
+ * HISTORY: *
+ * 05/23/1995 JLB : Created. *
+ *=============================================================================================*/
+bool CellClass::Flag_Place(HousesType house)
+{
+ assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
+
+ if (!IsFlagged && Is_Clear_To_Move(SPEED_TRACK, false, false)) {
+ IsFlagged = true;
+ Owner = house;
+ Redraw_Objects();
+ return(true);
+ }
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * CellClass::Flag_Remove -- Removes the house flag from the cell. *
+ * *
+ * This routine will free the cell of any house flag that may be located there. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Was there a flag here that was removed? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/23/1995 JLB : Created. *
+ *=============================================================================================*/
+bool CellClass::Flag_Remove(void)
+{
+ assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
+
+ if (IsFlagged) {
+ IsFlagged = false;
+ Owner = HOUSE_NONE;
+ Redraw_Objects();
+ return(true);
+ }
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * CellClass::Shimmer -- Causes all objects in the cell to shimmer. *
+ * *
+ * This routine is called when some event would cause a momentary disruption in the *
+ * cloaking device. All objects that are cloaked in the cell will have their cloaking *
+ * device shimmer. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/29/1995 JLB : Created. *
+ *=============================================================================================*/
+void CellClass::Shimmer(void)
+{
+ assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
+
+ ObjectClass * object = Cell_Occupier();
+
+ while (object) {
+ object->Do_Shimmer();
+ object = object->Next;
+ }
+}
+
+
+/***********************************************************************************************
+ * CellClass::Is_Clear_To_Move -- Determines if the cell is generally clear for travel *
+ * *
+ * This routine is called when determining general passability for purposes of zone *
+ * calculation. Only blockages that cannot be circumvented are considered to make a cell *
+ * impassable. All other obstructions can either be destroyed or are temporary. *
+ * *
+ * INPUT: loco -- The locomotion type to use when determining passablility. *
+ * *
+ * ignoreinfantry -- Should infantry in the cell be ignored for movement purposes? *
+ * *
+ * ignorevehicles -- If vehicles should be ignored, then this flag will be true. *
+ * *
+ * zone -- If specified, the zone must match this value or else movement is *
+ * presumed disallowed. *
+ * *
+ * check -- This specifies the zone type that this check applies to. *
+ * *
+ * OUTPUT: Is the cell generally passable to ground targeting? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 09/25/1995 JLB : Created. *
+ * 06/25/1996 JLB : Uses tracked vehicles as a basis for zone check. *
+ * 10/05/1996 JLB : Allows checking for crushable blockages. *
+ *=============================================================================================*/
+bool CellClass::Is_Clear_To_Move(SpeedType loco, bool ignoreinfantry, bool ignorevehicles, int zone, MZoneType check) const
+{
+ assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
+
+ /*
+ ** Flying objects always consider every cell passable since they can fly over everything.
+ */
+ if (loco == SPEED_WINGED) {
+ return(true);
+ }
+
+ /*
+ ** If a zone was specified, then see if the cell is in a legal
+ ** zone to allow movement.
+ */
+ if (zone != -1) {
+ if (zone != Zones[check]) {
+ return(false);
+ }
+ }
+
+ /*
+ ** Check the occupy bits for passable legality. If ignore infantry is true, then
+ ** don't consider infnatry.
+ */
+ int composite = Flag.Composite;
+ if (ignoreinfantry) {
+ composite &= 0xE0; // Drop the infantry occupation bits.
+ }
+ if (ignorevehicles) {
+ composite &= 0x5F; // Drop the vehicle/building bit.
+ }
+ if (composite != 0) {
+ return(false);
+ }
+
+ /*
+ ** Fetch the land type of the cell -- to be modified and used later.
+ */
+ LandType land = Land_Type();
+
+ /*
+ ** Walls are always considered to block the terrain for general passability
+ ** purposes unless this is a wall crushing check or if the checking object
+ ** can destroy walls.
+ */
+ OverlayTypeClass const * overlay = NULL;
+ if (Overlay != OVERLAY_NONE) {
+ overlay = &OverlayTypeClass::As_Reference(Overlay);
+ }
+ if (overlay != NULL && overlay->IsWall) {
+ if (check != MZONE_DESTROYER && (check != MZONE_CRUSHER || !overlay->IsCrushable)) {
+ return(false);
+ }
+
+ /*
+ ** Crushing objects consider crushable walls as clear rather than the
+ ** typical LAND_WALL setting.
+ */
+ land = LAND_CLEAR;
+ }
+
+ /*
+ ** See if the ground type is impassable to this locomotion type and if
+ ** so, return the error condition.
+ */
+ if (::Ground[land].Cost[loco] == 0) {
+ return(false);
+ }
+
+ /*
+ ** All checks passed, so this cell must be passable.
+ */
+ return(true);
+}
+
+
+/***********************************************************************************************
+ * CellClass::Is_Bridge_Here -- Checks to see if this is a bridge occupied cell. *
+ * *
+ * This routine will examine this cell and if there is a bridge here, it will return *
+ * true. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: bool; Is there a bridge located in this cell? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/30/1996 JLB : Created. *
+ *=============================================================================================*/
+bool CellClass::Is_Bridge_Here(void) const
+{
+ switch (TType) {
+ case TEMPLATE_BRIDGE1:
+ case TEMPLATE_BRIDGE1H:
+ case TEMPLATE_BRIDGE1D:
+ case TEMPLATE_BRIDGE2:
+ case TEMPLATE_BRIDGE2H:
+ case TEMPLATE_BRIDGE2D:
+ case TEMPLATE_BRIDGE_1A:
+ case TEMPLATE_BRIDGE_1B:
+ case TEMPLATE_BRIDGE_2A:
+ case TEMPLATE_BRIDGE_2B:
+ case TEMPLATE_BRIDGE_3A:
+ case TEMPLATE_BRIDGE_3B:
+ case TEMPLATE_BRIDGE_3C:
+ case TEMPLATE_BRIDGE_3D:
+ case TEMPLATE_BRIDGE_3E:
+ case TEMPLATE_BRIDGE_3F:
+ return(true);
+ }
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * CellClass::Can_Tiberium_Grow -- Determines if Tiberium can grow in this cell. *
+ * *
+ * This checks the cell to see if Tiberium can grow at least one level in it. Tiberium can *
+ * grow only if there is Tiberium already present. It can only grow to a certain level *
+ * and then all further growth is suspended. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: bool; Can Tiberium grow in this cell? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/14/1996 JLB : Created. *
+ *=============================================================================================*/
+bool CellClass::Can_Tiberium_Grow(void) const
+{
+ if (!Rule.IsTGrowth) return(false);
+
+ if (Session.Type != GAME_NORMAL) {
+ if(!Session.Options.Tiberium) return(false);
+ }
+
+ if (Land_Type() != LAND_TIBERIUM) return(false);
+
+ if (OverlayData >= 11) return(false);
+
+ if (Overlay != OVERLAY_GOLD1 && Overlay != OVERLAY_GOLD2 && Overlay != OVERLAY_GOLD3 && Overlay != OVERLAY_GOLD4) return(false);
+
+ return(true);
+}
+
+
+/***********************************************************************************************
+ * CellClass::Can_Tiberium_Spread -- Determines if Tiberium can spread from this cell. *
+ * *
+ * This routine will examine the cell and determine if there is sufficient Tiberium *
+ * present that Tiberium spores will spread to adjacent cells. If the Tiberium level is *
+ * too low, spreading will not occur. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: bool; Can Tiberium spread from this cell into adjacent cells? *
+ * *
+ * WARNINGS: This routine does not check to see if, in fact, there are any adjacent cells *
+ * available to spread to. *
+ * *
+ * HISTORY: *
+ * 08/14/1996 JLB : Created. *
+ *=============================================================================================*/
+bool CellClass::Can_Tiberium_Spread(void) const
+{
+ if (!Rule.IsTSpread) return(false);
+
+ if (Session.Type != GAME_NORMAL) {
+ if(!Session.Options.Tiberium) return(false);
+ }
+
+ if (Land_Type() != LAND_TIBERIUM) return(false);
+
+ if (OverlayData <= 6) return(false);
+
+ if (Overlay != OVERLAY_GOLD1 && Overlay != OVERLAY_GOLD2 && Overlay != OVERLAY_GOLD3 && Overlay != OVERLAY_GOLD4) return(false);
+
+ return(true);
+}
+
+
+/***********************************************************************************************
+ * CellClass::Grow_Tiberium -- Grows the tiberium in the cell. *
+ * *
+ * This routine will cause the tiberium to grow in the cell. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: bool; Did Tiberium grow in the cell? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/14/1996 JLB : Created. *
+ *=============================================================================================*/
+bool CellClass::Grow_Tiberium(void)
+{
+ if (Can_Tiberium_Grow()) {
+ OverlayData++;
+ Redraw_Objects();
+ return(true);
+ }
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * CellClass::Spread_Tiberium -- Spread Tiberium from this cell to an adjacent cell. *
+ * *
+ * This routine will cause the Tiberium to spread from this cell into an adjacent (random) *
+ * cell. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: bool; Did the Tiberium spread? *
+ * *
+ * WARNINGS: If there are no adjacent cells that the tiberium can spread to, then this *
+ * routine will fail. *
+ * *
+ * HISTORY: *
+ * 08/14/1996 JLB : Created. *
+ *=============================================================================================*/
+bool CellClass::Spread_Tiberium(bool forced)
+{
+ if (!forced) {
+ if (!Can_Tiberium_Spread()) return(false);
+ }
+ FacingType offset = Random_Pick(FACING_N, FACING_NW);
+ for (FacingType index = FACING_N; index < FACING_COUNT; index++) {
+ CellClass * newcell = &Adjacent_Cell(index+offset);
+
+ if (newcell != NULL && newcell->Can_Tiberium_Germinate()) {
+ new OverlayClass(Random_Pick(OVERLAY_GOLD1, OVERLAY_GOLD4), newcell->Cell_Number());
+ newcell->OverlayData = 0;
+ return(true);
+ }
+ }
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * CellClass::Can_Tiberium_Germinate -- Determines if Tiberium can begin growth in the cell. *
+ * *
+ * This routine will examine the cell and determine if Tiberium can start growth in it. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: bool; Can Tiberium grow in this cell? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/14/1996 JLB : Created. *
+ *=============================================================================================*/
+bool CellClass::Can_Tiberium_Germinate(void) const
+{
+ if (!Map.In_Radar(Cell_Number())) return(false);
+
+ if (Is_Bridge_Here()) return(false);
+
+ /*
+ ** Don't allow Tiberium to grow on a cell with a building unless that building is
+ ** invisible. In such a case, the Tiberium must grow or else the location of the
+ ** building will be revealed.
+ */
+ BuildingClass const * building = Cell_Building();
+ if (building != NULL && !building->Class->IsInvisible) return(false);
+
+ if (!Ground[Land_Type()].Build) return(false);
+
+ if (Overlay != OVERLAY_NONE) return(false);
+
+ return(true);
+}
diff --git a/CODE/CELL.H b/CODE/CELL.H
new file mode 100644
index 0000000..8e8aab2
--- /dev/null
+++ b/CODE/CELL.H
@@ -0,0 +1,295 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CELL.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CELL.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : April 29, 1994 *
+ * *
+ * Last Update : April 29, 1994 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifndef CELL_H
+#define CELL_H
+
+#include "building.h"
+#include "unit.h"
+#include "template.h"
+
+
+/****************************************************************************
+** Each cell on the map is controlled by the following structure.
+*/
+class CellClass
+{
+ public:
+ /*
+ ** This is the ID number of this cell. By placing the ID number here, it doesn't have
+ ** be calculated. Calculating this number requires a divide and would occur about
+ ** 5.72031 bijillion times per second.
+ */
+ short ID;
+
+ /*
+ ** Does this cell need to be updated on the radar map? If something changes in the cell
+ ** that might change the radar map imagery, then this flag will be set. It gets cleared
+ ** when the cell graphic is updated to the radar map.
+ */
+ unsigned IsPlot:1;
+
+ /*
+ ** Does this cell contain the special placement cursor graphic? This graphic is
+ ** present when selecting a site for building placement.
+ */
+ unsigned IsCursorHere:1;
+
+ /*
+ ** A mapped cell has some portion of it visible. Maybe it has a shroud piece
+ ** over it and maybe not.
+ */
+ unsigned IsMapped:1;
+
+ /*
+ ** A visible cell means that it is completely visible with no shroud over
+ ** it at all.
+ */
+ unsigned IsVisible:1;
+
+ /*
+ ** Every cell can be assigned a waypoint. A waypoint can only be assigned
+ ** to one cell, and vice-versa. This bit simply indicates whether this
+ ** cell is assigned a waypoint or not.
+ */
+ unsigned IsWaypoint:1;
+
+ /*
+ ** Is this cell currently under the radar map cursor? If so then it
+ ** needs to be updated whenever the map is updated.
+ */
+ unsigned IsRadarCursor:1;
+
+ /*
+ ** If this cell contains a house flag, then this will be true. The actual house
+ ** flag it contains is specified by the Owner field.
+ */
+ unsigned IsFlagged:1;
+
+ /*
+ ** This is a working flag used to help keep track of what cells should be
+ ** shrouded. By using this flag it allows a single pass through the map
+ ** cells for determining shadow regrowth logic.
+ */
+ unsigned IsToShroud:1;
+
+ /*
+ ** This records the movement zone for this map. Movement zones share the
+ ** same number if they are contiguous (terrain consideration only). There
+ ** are basically two kinds of zones. The difference being determined by
+ ** walls that can be crushed by movement. A vehicle that can crush walls
+ ** will only consider the CrushZone. All other terrestrial travellers will
+ ** use the normal Zone.
+ */
+ unsigned char Zones[MZONE_COUNT];
+
+ /*
+ ** This field controls whether an area is being jammed by a gap
+ ** generator.
+ */
+ unsigned short Jammed;
+
+ /*
+ ** This is the trigger ID for any trigger that might be attached to
+ ** this cell.
+ */
+ CCPtr Trigger;
+
+ /*
+ ** This contains the icon number and set to use for the base
+ ** of the terrain. All rendering on an icon occurs AFTER the icon
+ ** specified by this element is rendered. It is the lowest of the low.
+ */
+ TemplateType TType;
+ unsigned char TIcon;
+
+ /*
+ ** The second layer of 'terrain' icons is represented by a simple
+ ** type number and a value byte. This is sufficient for handling
+ ** concrete and walls.
+ */
+ OverlayType Overlay;
+ unsigned char OverlayData;
+
+ /*
+ ** This is used to specify any special 'stain' overlay icon. This
+ ** typically includes infantry bodies or other temporary marks.
+ */
+ SmudgeType Smudge;
+ unsigned char SmudgeData;
+
+ /*
+ ** Smudges and walls need to record ownership values. For walls, this
+ ** allows adjacent building placement logic to work. For smudges, it
+ ** allows building over smudges that are no longer attached to buildings
+ ** in addition to fixing the adjacent placement logic.
+ */
+ HousesType Owner;
+
+ /*
+ ** This flag tells you what type of infantry currently occupy the
+ ** cell or are moving into it.
+ */
+ HousesType InfType;
+
+ /*
+ ** These point to the object(s) that are located in this cell or overlap
+ ** this cell.
+ */
+ private:
+ ObjectClass * OccupierPtr;
+
+ public:
+#ifdef SORTDRAW
+ ObjectClass * Overlapper[10];
+#else
+ ObjectClass * Overlapper[6];
+#endif
+
+ /*
+ ** This array of bit flags is used to indicate which sub positions
+ ** within the cell are either occupied or are soon going to be
+ ** occupied. For vehicles, the cells that the vehicle is passing over
+ ** will be flagged with the vehicle bit. For infantry, the the sub
+ ** position the infantry is stopped at or headed toward will be marked.
+ ** The sub positions it passes over will NOT be marked.
+ */
+ union {
+ struct {
+ unsigned Center:1;
+ unsigned NW:1;
+ unsigned NE:1;
+ unsigned SW:1;
+ unsigned SE:1;
+ unsigned Vehicle:1; // Reserved for vehicle occupation.
+ unsigned Monolith:1; // Some immovable blockage is in cell.
+ unsigned Building:1; // A building of some time (usually blocks movement).
+ } Occupy;
+ unsigned char Composite;
+ } Flag;
+
+ //----------------------------------------------------------------
+ CellClass(void);
+ CellClass(NoInitClass const & x) : Trigger(x) {}
+ ~CellClass(void) {OccupierPtr=0;}
+
+ int operator == (CellClass const & cell) const {return &cell == this;}
+
+ /*
+ ** Query functions.
+ */
+ bool Can_Tiberium_Germinate(void) const;
+ bool Can_Tiberium_Grow(void) const;
+ bool Can_Tiberium_Spread(void) const;
+ bool Is_Bridge_Here(void) const;
+ RTTIType What_Am_I(void) const {return(RTTI_CELL);}
+ BuildingClass * Cell_Building(void) const;
+ CELL Cell_Number(void) const {return(ID);}
+ COORDINATE Cell_Coord(void) const;
+ COORDINATE Closest_Free_Spot(COORDINATE coord, bool any=false) const;
+ COORDINATE Free_Spot(void) const {return Closest_Free_Spot(Cell_Coord());}
+ CellClass & Adjacent_Cell(FacingType face) {return (CellClass &)((*((CellClass const *)this)).Adjacent_Cell(face));}
+ CellClass const & Adjacent_Cell(FacingType face) const;
+ InfantryClass * Cell_Infantry(void) const;
+ LandType Land_Type(void) const {return(Land);}
+ ObjectClass * Cell_Find_Object(RTTIType rtti) const;
+ ObjectClass * Cell_Object(int x=0, int y=0) const;
+ ObjectClass * Cell_Occupier(void) const {return(OccupierPtr);}
+ ObjectClass * Fetch_Occupier(void) const;
+ TARGET As_Target(void) const {return ::As_Target(Cell_Number());}
+ TechnoClass * Cell_Techno(int x=0, int y=0) const;
+ TerrainClass * Cell_Terrain(void) const;
+ UnitClass * Cell_Unit(void) const;
+ VesselClass * Cell_Vessel(void) const;
+ bool Goodie_Check(FootClass * object);
+ bool Is_Clear_To_Build(SpeedType loco = SPEED_TRACK) const;
+ bool Is_Clear_To_Move(SpeedType loco, bool ignoreinfantry, bool ignorevehicles, int zone=-1, MZoneType check=MZONE_NORMAL) const;
+ bool Is_Spot_Free(int spot_index) const {return (! (Flag.Composite & (1 << spot_index)) ); }
+ int Cell_Color(bool override=false) const;
+ int Clear_Icon(void) const;
+ static int Spot_Index(COORDINATE coord);
+
+ /*
+ ** Object placement and removal flag operations.
+ */
+ void Occupy_Down(ObjectClass * object);
+ void Occupy_Up(ObjectClass * object);
+ void Overlap_Down(ObjectClass * object);
+ void Overlap_Up(ObjectClass * object);
+ bool Flag_Place(HousesType house);
+ bool Flag_Remove(void);
+
+ /*
+ ** File I/O.
+ */
+ bool Should_Save(void) const;
+ bool Save(Pipe & file) const;
+ bool Load(Straw & file);
+ void Code_Pointers(void);
+ void Decode_Pointers(void);
+
+ /*
+ ** Display and rendering controls.
+ */
+ void Draw_It(int x, int y, bool objects=false) const;
+ void Redraw_Objects(bool forced=false);
+ void Shimmer(void);
+
+ /*
+ ** Maintenance calculation support.
+ */
+ bool Grow_Tiberium(void);
+ bool Spread_Tiberium(bool forced=false);
+ long Tiberium_Adjust(bool pregame=false);
+ void Wall_Update(void);
+ void Concrete_Calc(void);
+ void Recalc_Attributes(void);
+ int Reduce_Tiberium(int levels);
+ int Reduce_Wall(int damage);
+ void Incoming(COORDINATE threat=0, bool forced=false, bool nokidding=false);
+ void Adjust_Threat(HousesType house, int threat_value);
+
+ int operator != (CellClass const &) const {return 0;}
+
+ private:
+ CellClass (CellClass const &) ;
+
+ LandType Land; // The land type of this cell.
+};
+
+#endif
+
diff --git a/CODE/CHECKBOX.CPP b/CODE/CHECKBOX.CPP
new file mode 100644
index 0000000..cfc7e79
--- /dev/null
+++ b/CODE/CHECKBOX.CPP
@@ -0,0 +1,102 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CHECKBOX.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CHECKBOX.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 05/26/95 *
+ * *
+ * Last Update : July 6, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * CheckBoxClass::Action -- Handles a button action on a checkbox object. *
+ * CheckBoxClass::Draw_Me -- Draws the checkbox imagery. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+#include "checkbox.h"
+
+
+/***********************************************************************************************
+ * CheckBoxClass::Draw_Me -- Draws the checkbox imagery. *
+ * *
+ * This routine will draw the checkbox either filled or empty as necessary. *
+ * *
+ * INPUT: forced -- Should the check box be drawn even if it doesn't think it needs to? *
+ * *
+ * OUTPUT: Was the check box rendered? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/01/1995 JLB : Created. *
+ *=============================================================================================*/
+int CheckBoxClass::Draw_Me(int forced)
+{
+ if (ToggleClass::Draw_Me(forced)) {
+
+ Hide_Mouse();
+ Draw_Box(X, Y, Width, Height, BOXSTYLE_DOWN, false);
+ LogicPage->Fill_Rect(X+1, Y+1, X+Width-2, Y+Height-2, DKGREY);
+ if (IsOn) {
+ LogicPage->Fill_Rect(X+1, Y+1, X+Width-2, Y+Height-2, LTGREEN);
+ }
+ Show_Mouse();
+ return(true);
+ }
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * CheckBoxClass::Action -- Handles a button action on a checkbox object. *
+ * *
+ * This routine will detect if the mouse has been clicked on the checkbox object. If so, *
+ * the check box state will be toggled. *
+ * *
+ * INPUT: flags -- The event flags that resulted in this routine being called. *
+ * *
+ * key -- The key that resulted in this routine being called. *
+ * *
+ * OUTPUT: bool; Should normal processing occur? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/06/1996 JLB : Created. *
+ *=============================================================================================*/
+int CheckBoxClass::Action(unsigned flags, KeyNumType & key)
+{
+ if (flags & LEFTRELEASE) {
+ if (IsOn) {
+ Turn_Off();
+ } else {
+ Turn_On();
+ }
+ }
+ return(ToggleClass::Action(flags, key));
+}
diff --git a/CODE/CHECKBOX.H b/CODE/CHECKBOX.H
new file mode 100644
index 0000000..10d8776
--- /dev/null
+++ b/CODE/CHECKBOX.H
@@ -0,0 +1,56 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CHECKBOX.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CHECKBOX.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 05/26/95 *
+ * *
+ * Last Update : May 26, 1995 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifndef CHECKBOX_H
+#define CHECKBOX_H
+
+#include "toggle.h"
+
+class CheckBoxClass : public ToggleClass
+{
+ public:
+ CheckBoxClass(unsigned id, int x, int y) :
+ ToggleClass(id, x, y, 7, 7)
+ {};
+
+ virtual int Draw_Me(int forced=false);
+ virtual int Action(unsigned flags, KeyNumType & key);
+
+ protected:
+};
+
+#endif
diff --git a/CODE/CHEKLIST.CPP b/CODE/CHEKLIST.CPP
new file mode 100644
index 0000000..c35cc41
--- /dev/null
+++ b/CODE/CHEKLIST.CPP
@@ -0,0 +1,365 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CHEKLIST.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CHEKLIST.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 07/05/96 *
+ * *
+ * Last Update : July 6, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * CheckListClass::Action -- action function for this class *
+ * CheckListClass::Add_Item -- Adds specifies text to check list box. *
+ * CheckListClass::CheckListClass -- constructor *
+ * CheckListClass::Check_Item -- [un]checks an items *
+ * CheckListClass::Draw_Entry -- draws a list box entry *
+ * CheckListClass::Get_Item -- Fetches a pointer to the text associated with the index. *
+ * CheckListClass::Remove_Item -- Remove the item that matches the text pointer specified. *
+ * CheckListClass::Set_Selected_Index -- Set the selected index to match the text pointer spe*
+ * CheckListClass::~CheckListClass -- Destructor for check list object. *
+ * CheckListClass::~CheckListClass -- destructor *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+
+
+/***************************************************************************
+ * CheckListClass::CheckListClass -- constructor *
+ * *
+ * INPUT: *
+ * id control ID for this list box *
+ * x x-coord *
+ * y y-coord *
+ * w width *
+ * h height *
+ * flags mouse event flags *
+ * up ptr to Up-arrow shape *
+ * down ptr to Down-arrow shape *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 02/16/1995 BR : Created. *
+ *=========================================================================*/
+CheckListClass::CheckListClass(int id, int x, int y, int w, int h, TextPrintType flags,
+ void const * up, void const * down) :
+ ListClass (id, x, y, w, h, flags, up, down),
+ IsReadOnly(false)
+{
+}
+
+
+/***********************************************************************************************
+ * CheckListClass::~CheckListClass -- Destructor for check list object. *
+ * *
+ * This destructor will delete all entries attached to it. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/06/1996 JLB : Created. *
+ *=============================================================================================*/
+CheckListClass::~CheckListClass(void)
+{
+ while (CheckListClass::Count()) {
+ CheckObject * obj = (CheckObject *)ListClass::Get_Item(0);
+
+ ListClass::Remove_Item(0);
+ delete obj;
+ }
+}
+
+
+/***********************************************************************************************
+ * CheckListClass::Add_Item -- Adds specifies text to check list box. *
+ * *
+ * This routine will add the specified text string to the check list. *
+ * *
+ * INPUT: text -- Pointer to the text string to add to the list box. *
+ * *
+ * OUTPUT: Returns the index number where the text object was added. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 02/14/1996 JLB : Created. *
+ *=============================================================================================*/
+int CheckListClass::Add_Item(char const * text)
+{
+ CheckObject * obj = new CheckObject(text, false);
+ return(ListClass::Add_Item((char const *)obj));
+}
+
+
+char const * CheckListClass::Current_Item(void) const
+{
+ CheckObject * obj = (CheckObject *)ListClass::Current_Item();
+ if (obj) {
+ return(obj->Text);
+ }
+ return(0);
+}
+
+
+/***********************************************************************************************
+ * CheckListClass::Get_Item -- Fetches a pointer to the text associated with the index. *
+ * *
+ * This routine will find the text associated with the entry specified and return a pointer *
+ * to that text. *
+ * *
+ * INPUT: index -- The entry (index) to fetch a pointer to. *
+ * *
+ * OUTPUT: Returns with the text pointer associated with the index specified. *
+ * *
+ * WARNINGS: If the index is out of range, then NULL is returned. *
+ * *
+ * HISTORY: *
+ * 07/06/1996 JLB : Created. *
+ *=============================================================================================*/
+char const * CheckListClass::Get_Item(int index) const
+{
+ CheckObject * obj = (CheckObject *)ListClass::Get_Item(index);
+ if (obj) {
+ return(obj->Text);
+ }
+ return(0);
+}
+
+
+/***********************************************************************************************
+ * CheckListClass::Remove_Item -- Remove the item that matches the text pointer specified. *
+ * *
+ * This routine will find the entry that matches the text pointer specified and then *
+ * delete that entry. *
+ * *
+ * INPUT: text -- The text pointer to use to find the exact match in the list. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/06/1996 JLB : Created. *
+ *=============================================================================================*/
+void CheckListClass::Remove_Item(char const * text)
+{
+ for (int index = 0; index < Count(); index++) {
+ CheckObject * obj = (CheckObject *)ListClass::Get_Item(index);
+ if (obj && stricmp(obj->Text, text) == 0) {
+ ListClass::Remove_Item(index);
+ delete obj;
+ break;
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * CheckListClass::Set_Selected_Index -- Set the selected index to match the text pointer spec *
+ * *
+ * This routine will find the entry that exactly matches the text pointer specified. If *
+ * found, then that entry will be set as the currently selected index. *
+ * *
+ * INPUT: text -- Pointer to the text string to find the match for. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: If an exact match to the specified text string could not be found, then the *
+ * currently selected index is not changed. *
+ * *
+ * HISTORY: *
+ * 07/06/1996 JLB : Created. *
+ *=============================================================================================*/
+void CheckListClass::Set_Selected_Index(char const * text)
+{
+ for (int index = 0; index < Count(); index++) {
+ CheckObject * obj = (CheckObject *)ListClass::Get_Item(index);
+ if (obj && stricmp(obj->Text, text) == 0) {
+ Set_Selected_Index(index);
+ break;
+ }
+ }
+}
+
+
+/***************************************************************************
+ * CheckListClass::Check_Item -- [un]checks an items *
+ * *
+ * INPUT: *
+ * index index of item to check or uncheck *
+ * checked 0 = uncheck, non-zero = check *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 02/16/1995 BR : Created. *
+ * 02/14/1996 JLB : Revamped. *
+ *=========================================================================*/
+void CheckListClass::Check_Item(int index, bool checked)
+{
+ CheckObject * obj = (CheckObject *)ListClass::Get_Item(index);
+ if (obj && obj->IsChecked != checked) {
+ obj->IsChecked = checked;
+ Flag_To_Redraw();
+ }
+}
+
+
+/***************************************************************************
+ * CheckListClass::Is_Checked -- returns checked state of an item *
+ * *
+ * INPUT: *
+ * index index of item to query *
+ * *
+ * OUTPUT: *
+ * 0 = item is unchecked, 1 = item is checked *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 02/16/1995 BR : Created. *
+ * 02/14/1996 JLB : Revamped. *
+ *=========================================================================*/
+bool CheckListClass::Is_Checked(int index) const
+{
+ CheckObject * obj = (CheckObject *)ListClass::Get_Item(index);
+ if (obj) {
+ return(obj->IsChecked);
+ }
+ return(false);
+}
+
+
+/***************************************************************************
+ * CheckListClass::Action -- action function for this class *
+ * *
+ * INPUT: *
+ * flags the reason we're being called *
+ * key the KN_number that was pressed *
+ * *
+ * OUTPUT: *
+ * true = event was processed, false = event not processed *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 02/16/1995 BR : Created. *
+ *=========================================================================*/
+int CheckListClass::Action(unsigned flags, KeyNumType &key)
+{
+ int rc;
+
+ /*
+ ** If this is a read-only list, it's a display-only device
+ */
+ if (IsReadOnly) {
+ return(false);
+ }
+
+ /*
+ ** Invoke parents Action first, so it can set the SelectedIndex if needed.
+ */
+ rc = ListClass::Action(flags, key);
+
+ /*
+ ** Now, if this event was a left-press, toggle the checked state of the
+ ** current item.
+ */
+ if (flags & LEFTPRESS) {
+ Check_Item(SelectedIndex, !Is_Checked(SelectedIndex));
+ }
+
+ return(rc);
+}
+
+
+/***************************************************************************
+ * CheckListClass::Draw_Entry -- draws a list box entry *
+ * *
+ * INPUT: *
+ * index index into List of item to draw *
+ * x,y x,y coords to draw at *
+ * width maximum width allowed for text *
+ * selected true = this item is selected *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/14/1995 BRR : Created. *
+ *=========================================================================*/
+void CheckListClass::Draw_Entry(int index, int x, int y, int width, int selected)
+{
+ if (index >= Count()) return;
+
+ CheckObject * obj = (CheckObject *)ListClass::Get_Item(index);
+
+ if (obj) {
+ char buffer[100] = "";
+
+ if (obj->IsChecked) {
+ buffer[0] = CHECK_CHAR;
+ } else {
+ buffer[0] = UNCHECK_CHAR;
+ }
+ buffer[1] = ' ';
+ sprintf(&buffer[2], obj->Text);
+
+ TextPrintType flags = TextFlags;
+ RemapControlType * scheme = GadgetClass::Get_Color_Scheme();
+
+ if (selected) {
+ flags = flags | TPF_BRIGHT_COLOR;
+ LogicPage->Fill_Rect (x, y, x + width - 1, y + LineHeight - 1, scheme->Shadow);
+ } else {
+ if (!(flags & TPF_USE_GRAD_PAL)) {
+ flags = flags | TPF_MEDIUM_COLOR;
+ }
+ }
+
+ Conquer_Clip_Text_Print(buffer, x, y, scheme, TBLACK, flags, width, Tabs);
+ }
+}
+
diff --git a/CODE/CHEKLIST.H b/CODE/CHEKLIST.H
new file mode 100644
index 0000000..d123649
--- /dev/null
+++ b/CODE/CHEKLIST.H
@@ -0,0 +1,105 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CHEKLIST.H 1 3/03/97 10:24a Joe_bostic $ */
+/***************************************************************************
+ ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S **
+ ***************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CHEKLIST.H *
+ * *
+ * Programmer : Bill Randolph *
+ * *
+ * Start Date : February 16, 1995 *
+ * *
+ * Last Update : February 16, 1995 [BR] *
+ * *
+ *-------------------------------------------------------------------------*
+ * This class behaves just like the standard list box, except that if the *
+ * first character of a list entry is a space, clicking on it toggles the *
+ * space with a check-mark ('\3'). This makes each entry in the list box *
+ * "toggle-able". *
+ *-------------------------------------------------------------------------*
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifndef CHEKLIST_H
+#define CHEKLIST_H
+
+#include "list.h"
+
+class CheckObject
+{
+ public:
+ CheckObject(char const * text = 0, bool checked=false) :
+ Text(text),
+ IsChecked(checked)
+ {};
+
+ char const * Text;
+ bool IsChecked;
+};
+
+
+class CheckListClass : public ListClass
+{
+ public:
+ /*
+ ** Constructor/Destructor
+ */
+ CheckListClass(int id, int x, int y, int w, int h, TextPrintType flags,
+ void const * up, void const * down);
+ ~CheckListClass(void);
+
+ virtual int Add_Item(int text) {return ListClass::Add_Item(text);}
+ virtual int Add_Item(char const * text);
+ virtual char const * Current_Item(void) const;
+ virtual char const * Get_Item(int index) const;
+ virtual void Remove_Item(char const * text);
+ virtual void Remove_Item(int text) {ListClass::Remove_Item(text);}
+ virtual void Set_Selected_Index(char const * text);
+ virtual void Set_Selected_Index(int index) {ListClass::Set_Selected_Index(index);};
+
+ /*
+ ** Checkmark utility functions
+ */
+ void Check_Item(int index, bool checked); // sets checked state of item
+ bool Is_Checked(int index) const; // gets checked state of item
+
+ void Set_Read_Only(int rdonly) {IsReadOnly = rdonly;}
+
+ /*
+ ** This defines the ASCII value of the checkmark character & non-checkmark
+ ** character.
+ */
+ typedef enum CheckListClassEnum {
+ CHECK_CHAR = '\3',
+ UNCHECK_CHAR = ' '
+ } CheckListClassEnum;
+
+ protected:
+ virtual int Action(unsigned flags, KeyNumType &key);
+ virtual void Draw_Entry(int index, int x, int y, int width, int selected);
+
+ private:
+ bool IsReadOnly;
+};
+
+
+#endif
diff --git a/CODE/CLASS.CPP b/CODE/CLASS.CPP
new file mode 100644
index 0000000..d912187
--- /dev/null
+++ b/CODE/CLASS.CPP
@@ -0,0 +1,19 @@
+/*
+** Command & Conquer Red Alert(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 .
+*/
+
+
diff --git a/CODE/CO-WC32.LNT b/CODE/CO-WC32.LNT
new file mode 100644
index 0000000..e3f749b
--- /dev/null
+++ b/CODE/CO-WC32.LNT
@@ -0,0 +1,67 @@
+// Compiler Options for Watcom C, C++ 32 bit
+
+-cwc
+
+// This file contains options to allow PC-lint to process source
+// files for your compiler. It is used as follows:
+//
+// lint co-wc32.lnt source-file(s)
+//
+-d_M_IX86=200 // assume Intel 80286 architecture -- modify to suit
+-d__declspec()= // ignore this construct
+
+ // additional reserve words needed
++rw(_loadds,_export)
++rw(__interrupt,__near,__far,__huge,__fortran,__pascal,__cdecl)
++rw(__export,__loadds,__saveregs,__asm,__fastcall,__stdcall)
++rw(_export)
+
++fcd // makes cdecl significant -- used for proto generation
++fcu // chars are by default unsigned
++fsu // so are strings
+-d__386__ // pre-defined macro for 386 version, not set by -cwc
+-d__FLAT__ // not set by -cwc
+-si4 // sizeof(int) is 4
+-spN4 // sizeof(near pointer) is 4
+-spF6 // sizeof( far pointer) is 6
+-sld10 // sizeof(long double) is 10.
+-function(exit,_exit) // _exit() is like exit()
+-emacro(734,putc) // don't complain about items being too large.
+-emacro(506,putc) // don't complain about constant Boolean
+-emacro(???,va_arg) // the va_arg() macro can yield 415, 416, 661, 662
+ // 796 and 797 (out-of-bounds errors).
+
+ // While processing compiler (library) header files ...
+-elib(46) // an unsigned short bit field is used as __FILLER__
+-elib(522) // function return value ignored
+-elib(537) // repeated include file (ios.h)
+-elib(641) // converting enum to int
+-elib(652) // suppress message about #define of earlier declared symbols
+-elib(655) // ORing enum's
+-elib(726) // extraneous comma in enumeration
+-elib(760) // suppress message about multiple identical macro defs
+-elib(762) // suppress message about multiple identical declarations and
+-elib(806) // small bit field is signed
+-elib(1053) // prototypes cannot be distinguished
+-elib(1511) // member (rdbuf) hides nonvirtual member
+-elib(1704) // private copy constructor
+-elib(1712) // default constructor missing
+-elib(1717) // empty prototypes
+-elib(1720) // strange argument to assignment operator
+-elib(1721) // unusual operator =() declaration
+-elib(1722) // assignment operator does not return ref to class
+-elib(1724) // strange argument to copy constructor
+
+-esym(1702,operator<<,operator>>)
+
+// 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)
+
diff --git a/CODE/COLRLIST.CPP b/CODE/COLRLIST.CPP
new file mode 100644
index 0000000..0caea38
--- /dev/null
+++ b/CODE/COLRLIST.CPP
@@ -0,0 +1,278 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/COLRLIST.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : COLRLIST.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 01/15/95 *
+ * *
+ * Last Update : April 19, 1995 [BRR] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * ColorListClass::Add_Item -- Adds an item to the list *
+ * ColorListClass::ColorListClass -- Class constructor *
+ * ColorListClass::Draw_Entry -- Draws one text line *
+ * ColorListClass::Remove_Item -- Removes an item from the list *
+ * ColorListClass::Set_Selected_Style -- tells how to draw selected item *
+ * ColorListClass::~ColorListClass -- Class destructor *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+
+
+/***************************************************************************
+ * ColorListClass::ColorListClass -- class constructor *
+ * *
+ * INPUT: *
+ * id button ID *
+ * x,y upper-left corner, in pixels *
+ * w,h width, height, in pixels *
+ * list ptr to array of char strings to list *
+ * flags flags for mouse, style of listbox *
+ * up,down pointers to shapes for up/down buttons *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: 01/05/1995 MML : Created. *
+ *=========================================================================*/
+ColorListClass::ColorListClass (int id, int x, int y, int w, int h,
+ TextPrintType flags, void const * up, void const * down) :
+ ListClass (id, x, y, w, h, flags, up, down),
+ Style(SELECT_HIGHLIGHT),
+ SelectColor(NULL)
+{
+}
+
+
+/***************************************************************************
+ * ColorListClass::~ColorListClass -- Class destructor *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 04/19/1995 BRR : Created. *
+ *=========================================================================*/
+ColorListClass::~ColorListClass(void)
+{
+ Colors.Clear();
+ SelectColor = 0;
+}
+
+
+/***************************************************************************
+ * ColorListClass::Add_Item -- Adds an item to the list *
+ * *
+ * INPUT: *
+ * text text to add to list *
+ * color color for item *
+ * *
+ * OUTPUT: *
+ * position of item in the list *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 04/19/1995 BRR : Created. *
+ *=========================================================================*/
+int ColorListClass::Add_Item(char const * text, RemapControlType * color)
+{
+ Colors.Add(color);
+ return(ListClass::Add_Item(text));
+}
+
+
+/***************************************************************************
+ * ColorListClass::Add_Item -- Adds an item to the list *
+ * *
+ * INPUT: *
+ * text text to add to list *
+ * color color for item *
+ * *
+ * OUTPUT: *
+ * position of item in the list *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 04/19/1995 BRR : Created. *
+ *=========================================================================*/
+int ColorListClass::Add_Item(int text, RemapControlType * color)
+{
+ Colors.Add(color);
+ return(ListClass::Add_Item(text));
+}
+
+
+/***************************************************************************
+ * ColorListClass::Remove_Item -- Removes an item from the list *
+ * *
+ * INPUT: *
+ * text ptr to item to remove *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * *
+ * HISTORY: *
+ * 04/19/1995 BRR : Created. *
+ *=========================================================================*/
+void ColorListClass::Remove_Item(char const * text)
+{
+ int index = List.ID(text);
+ if (index != -1) {
+ Colors.Delete(index);
+ ListClass::Remove_Item(text);
+ }
+}
+
+
+/***************************************************************************
+ * ColorListClass::Set_Selected_Style -- tells how to draw selected item *
+ * *
+ * INPUT: *
+ * style style to draw *
+ * color color to draw the special style in; -1 = use item's color *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 04/19/1995 BRR : Created. *
+ *=========================================================================*/
+void ColorListClass::Set_Selected_Style(SelectStyleType style, RemapControlType * color)
+{
+ Style = style;
+ SelectColor = color;
+}
+
+
+/***************************************************************************
+ * ColorListClass::Draw_Entry -- Draws one text line *
+ * *
+ * INPUT: *
+ * index index into List of item to draw *
+ * x,y x,y coords to draw at *
+ * width maximum width allowed for text *
+ * selected true = this item is selected *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 04/19/1995 BRR : Created. *
+ *=========================================================================*/
+void ColorListClass::Draw_Entry(int index, int x, int y, int width, int selected)
+{
+ RemapControlType * color;
+
+ /*
+ ** Draw a non-selected item in its color
+ */
+ if (!selected) {
+ Conquer_Clip_Text_Print(List[index], x, y, Colors[index], TBLACK, TextFlags, width, Tabs);
+ return;
+ }
+
+ /*
+ ** For selected items, choose the right color & style:
+ */
+ if (SelectColor == NULL) {
+ color = Colors[index];
+ } else {
+ color = SelectColor;
+ }
+
+ switch (Style) {
+ /*
+ ** NONE: Just print the string in its native color
+ */
+ case SELECT_NORMAL:
+ Conquer_Clip_Text_Print(List[index], x, y, Colors[index], TBLACK,
+ TextFlags, width, Tabs);
+ break;
+
+ /*
+ ** HIGHLIGHT: Draw the string in the highlight color (SelectColor must
+ ** be set)
+ */
+ case SELECT_HIGHLIGHT:
+ if (TextFlags & TPF_6PT_GRAD) {
+ Conquer_Clip_Text_Print(List[index], x, y, color, TBLACK, TextFlags | TPF_BRIGHT_COLOR, width, Tabs);
+ } else {
+ Conquer_Clip_Text_Print(List[index], x, y, color, TBLACK, TextFlags, width, Tabs);
+ }
+ break;
+
+ /*
+ ** BOX: Draw a box around the item in the current select color
+ */
+ case SELECT_BOX:
+ LogicPage->Draw_Rect (x, y, x + width - 2, y + LineHeight - 2, color->Color);
+ Conquer_Clip_Text_Print(List[index], x, y, Colors[index], TBLACK, TextFlags, width, Tabs);
+ break;
+
+ /*
+ ** BAR: draw a color bar under the text
+ */
+ case SELECT_BAR:
+ if (TextFlags & TPF_6PT_GRAD) {
+ LogicPage->Fill_Rect(x, y, x + width - 1, y + LineHeight - 1, color->Color);
+ Conquer_Clip_Text_Print(List[index], x, y, Colors[index], TBLACK, TextFlags | TPF_BRIGHT_COLOR, width, Tabs);
+ } else {
+ LogicPage->Fill_Rect(x, y, x + width - 2, y + LineHeight - 2, color->Color);
+ Conquer_Clip_Text_Print(List[index], x, y, Colors[index], TBLACK, TextFlags, width, Tabs);
+ }
+ break;
+
+ /*
+ ** INVERT: Draw text as the background color on foreground color
+ */
+ case SELECT_INVERT:
+ LogicPage->Fill_Rect (x, y, x + width - 1, y + LineHeight - 1, Colors[index]->Color);
+ break;
+ }
+}
diff --git a/CODE/COLRLIST.H b/CODE/COLRLIST.H
new file mode 100644
index 0000000..57f3439
--- /dev/null
+++ b/CODE/COLRLIST.H
@@ -0,0 +1,87 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/COLRLIST.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : COLRLIST.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 01/15/95 *
+ * *
+ * Last Update : January 15, 1995 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifndef COLORLIST_H
+#define COLORLIST_H
+
+#include "list.h"
+
+
+/***************************************************************************
+** This class adds the ability for every list item to have a different color.
+*/
+class ColorListClass : public ListClass
+{
+ public:
+ /*********************************************************************
+ ** These enums are the ways a selected item can be drawn
+ */
+ //lint -esym(578,SELECT_NONE)
+ typedef enum SelectEnum {
+ SELECT_NORMAL, // selected items aren't drawn differently
+ SELECT_HIGHLIGHT, // item is highlighted
+ SELECT_BOX, // draw a box around the item
+ SELECT_BAR, // draw a bar behind the item
+ SELECT_INVERT // draw the string inverted
+ } SelectStyleType;
+
+ ColorListClass(int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down);
+ virtual ~ColorListClass(void);
+
+ virtual int Add_Item(char const * text, RemapControlType * color = NULL);
+ virtual int Add_Item(int text, RemapControlType * color = NULL);
+ virtual void Remove_Item(char const * text);
+
+ virtual void Set_Selected_Style(SelectStyleType style, RemapControlType * color = NULL);
+
+ /*
+ ** This is the list of colors for each item.
+ */
+ DynamicVectorClass Colors;
+
+ protected:
+ virtual void Draw_Entry(int index, int x, int y, int width, int selected);
+
+ /*
+ ** This tells how to draw the selected item.
+ */
+ SelectStyleType Style;
+ RemapControlType * SelectColor;
+
+};
+
+#endif
diff --git a/CODE/COMBAT.CPP b/CODE/COMBAT.CPP
new file mode 100644
index 0000000..1e25864
--- /dev/null
+++ b/CODE/COMBAT.CPP
@@ -0,0 +1,425 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/COMBAT.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : COMBAT.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : September 19, 1994 *
+ * *
+ * Last Update : July 26, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * Combat_Anim -- Determines explosion animation to play. *
+ * Explosion_Damage -- Inflict an explosion damage affect. *
+ * Modify_Damage -- Adjusts damage to reflect the nature of the target. *
+ * Wide_Area_Damage -- Apply wide area damage to the map. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+
+
+/***********************************************************************************************
+ * Modify_Damage -- Adjusts damage to reflect the nature of the target. *
+ * *
+ * This routine is the core of combat tactics. It implements the *
+ * affect various armor types have against various weapon types. By *
+ * careful exploitation of this table, tactical advantage can be *
+ * obtained. *
+ * *
+ * INPUT: damage -- The damage points to process. *
+ * *
+ * warhead -- The source of the damage points. *
+ * *
+ * armor -- The type of armor defending against the damage. *
+ * *
+ * distance -- The distance (in leptons) from the source of the damage. *
+ * *
+ * OUTPUT: Returns with the adjusted damage points to inflict upon the *
+ * target. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 04/16/1994 JLB : Created. *
+ * 04/17/1994 JLB : Always does a minimum of damage. *
+ * 01/01/1995 JLB : Takes into account distance from damage source. *
+ * 04/11/1996 JLB : Changed damage fall-off formula for less damage fall-off. *
+ *=============================================================================================*/
+int Modify_Damage(int damage, WarheadType warhead, ArmorType armor, int distance)
+{
+ if (!damage) return(damage);
+
+ /*
+ ** If there is no raw damage value to start with, then
+ ** there can be no modified damage either.
+ */
+ if (Special.IsInert || !damage || warhead == WARHEAD_NONE) return(0);
+
+ /*
+ ** Negative damage (i.e., heal) is always applied full strength, but only if the heal
+ ** effect is close enough.
+ */
+ if (damage < 0) {
+#ifdef FIXIT_CSII // checked - ajw 9/28/98
+ if (distance < 0x008) {
+ if(warhead != WARHEAD_MECHANICAL && armor == ARMOR_NONE) return(damage);
+ if(warhead == WARHEAD_MECHANICAL && armor != ARMOR_NONE) return(damage);
+ }
+#else
+ if (distance < 0x008 && armor == ARMOR_NONE) return(damage);
+#endif
+ return(0);
+ }
+
+ WarheadTypeClass const * whead = WarheadTypeClass::As_Pointer(warhead);
+// WarheadTypeClass const * whead = &Warheads[warhead];
+
+ damage = damage * whead->Modifier[armor];
+
+ /*
+ ** Reduce damage according to the distance from the impact point.
+ */
+ if (damage) {
+ if (!whead->SpreadFactor) {
+ distance /= PIXEL_LEPTON_W/4;
+ } else {
+ distance /= whead->SpreadFactor * (PIXEL_LEPTON_W/2);
+ }
+ distance = Bound(distance, 0, 16);
+ if (distance) {
+ damage = damage / distance;
+ }
+
+ /*
+ ** Allow damage to drop to zero only if the distance would have
+ ** reduced damage to less than 1/4 full damage. Otherwise, ensure
+ ** that at least one damage point is done.
+ */
+ if (distance < 4) {
+ damage = max(damage, Rule.MinDamage);
+ }
+ }
+
+ damage = min(damage, Rule.MaxDamage);
+ return(damage);
+}
+
+
+/***********************************************************************************************
+ * Explosion_Damage -- Inflict an explosion damage affect. *
+ * *
+ * Processes the collateral damage affects typically caused by an *
+ * explosion. *
+ * *
+ * INPUT: coord -- The coordinate of ground zero. *
+ * *
+ * strength -- Raw damage points at ground zero. *
+ * *
+ * source -- Source of the explosion (who is responsible). *
+ * *
+ * warhead -- The kind of explosion to process. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: This routine can consume some time and will affect the AI *
+ * of nearby enemy units (possibly). *
+ * *
+ * HISTORY: *
+ * 08/16/1991 JLB : Created. *
+ * 11/30/1991 JLB : Uses coordinate system. *
+ * 12/27/1991 JLB : Radius of explosion damage effect. *
+ * 04/13/1994 JLB : Streamlined. *
+ * 04/16/1994 JLB : Warhead damage type modifier. *
+ * 04/17/1994 JLB : Cleaned up. *
+ * 06/20/1994 JLB : Uses object pointers to distribute damage. *
+ * 06/20/1994 JLB : Source is a pointer. *
+ * 06/18/1996 JLB : Strength could be negative for healing effects. *
+ *=============================================================================================*/
+void Explosion_Damage(COORDINATE coord, int strength, TechnoClass * source, WarheadType warhead)
+{
+ CELL cell; // Cell number under explosion.
+ ObjectClass * object; // Working object pointer.
+ ObjectClass * objects[32]; // Maximum number of objects that can be damaged.
+ int distance; // Distance to unit.
+ int range; // Damage effect radius.
+ int count; // Number of vehicle IDs in list.
+
+ if (!strength || Special.IsInert || warhead == WARHEAD_NONE) return;
+
+ WarheadTypeClass const * whead = WarheadTypeClass::As_Pointer(warhead);
+// WarheadTypeClass const * whead = &Warheads[warhead];
+// range = ICON_LEPTON_W*2;
+ range = ICON_LEPTON_W + (ICON_LEPTON_W >> 1);
+ cell = Coord_Cell(coord);
+ if ((unsigned)cell >= MAP_CELL_TOTAL) return;
+
+ CellClass * cellptr = &Map[cell];
+ ObjectClass * impacto = cellptr->Cell_Occupier();
+
+ /*
+ ** Fill the list of unit IDs that will have damage
+ ** assessed upon them. The units can be lifted from
+ ** the cell data directly.
+ */
+ count = 0;
+ for (FacingType i = FACING_NONE; i < FACING_COUNT; i++) {
+ /*
+ ** Fetch a pointer to the cell to examine. This is either
+ ** an adjacent cell or the center cell. Damage never spills
+ ** further than one cell away.
+ */
+ if (i != FACING_NONE) {
+ cellptr = &Map[cell].Adjacent_Cell(i);
+ }
+
+ /*
+ ** Add all objects in this cell to the list of objects to possibly apply
+ ** damage to. The list stops building when the object pointer list becomes
+ ** full. Do not include overlapping objects; selection state can affect
+ ** the overlappers, and this causes multiplayer games to go out of sync.
+ */
+ object = cellptr->Cell_Occupier();
+ while (object) {
+ if (!object->IsToDamage && object != source) {
+ object->IsToDamage = true;
+ objects[count++] = object;
+ if (count >= ARRAY_SIZE(objects)) break;
+ }
+ object = object->Next;
+ }
+ if (count >= ARRAY_SIZE(objects)) break;
+ }
+
+ /*
+ ** Sweep through the units to be damaged and damage them. When damaging
+ ** buildings, consider a hit on any cell the building occupies as if it
+ ** were a direct hit on the building's center.
+ */
+ for (int index = 0; index < count; index++) {
+ object = objects[index];
+
+ object->IsToDamage = false;
+ if (object->IsActive) {
+ if (object->What_Am_I() == RTTI_BUILDING && impacto == object) {
+ distance = 0;
+ } else {
+ distance = Distance(coord, object->Center_Coord());
+ }
+ if (object->IsDown && !object->IsInLimbo && distance < range) {
+ int damage = strength;
+ object->Take_Damage(damage, distance, warhead, source);
+ }
+ }
+ }
+
+ /*
+ ** If there is a wall present at this location, it may be destroyed. Check to
+ ** make sure that the warhead is of the kind that can destroy walls.
+ */
+ cellptr = &Map[cell];
+ if (cellptr->Overlay != OVERLAY_NONE) {
+ OverlayTypeClass const * optr = &OverlayTypeClass::As_Reference(cellptr->Overlay);
+
+ if (optr->IsTiberium && whead->IsTiberiumDestroyer) {
+ cellptr->Reduce_Tiberium(strength / 10);
+ }
+ if (optr->IsWall) {
+ if (whead->IsWallDestroyer || (whead->IsWoodDestroyer && optr->IsWooden)) {
+ Map[cell].Reduce_Wall(strength);
+ }
+ }
+ }
+
+ /*
+ ** If there is a bridge at this location, then it may be destroyed by the
+ ** combat damage.
+ */
+ if (cellptr->TType == TEMPLATE_BRIDGE1 || cellptr->TType == TEMPLATE_BRIDGE2 ||
+ cellptr->TType == TEMPLATE_BRIDGE1H || cellptr->TType == TEMPLATE_BRIDGE2H ||
+ cellptr->TType == TEMPLATE_BRIDGE_1A || cellptr->TType == TEMPLATE_BRIDGE_1B ||
+ cellptr->TType == TEMPLATE_BRIDGE_2A || cellptr->TType == TEMPLATE_BRIDGE_2B ||
+ cellptr->TType == TEMPLATE_BRIDGE_3A || cellptr->TType == TEMPLATE_BRIDGE_3B ) {
+
+ if (((warhead == WARHEAD_AP || warhead == WARHEAD_HE) && Random_Pick(1, Rule.BridgeStrength) < strength)) {
+ Map.Destroy_Bridge_At(cell);
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * Combat_Anim -- Determines explosion animation to play. *
+ * *
+ * This routine is called when a projectile impacts. This routine will determine what *
+ * animation should be played. *
+ * *
+ * INPUT: damage -- The amount of damage this warhead possess (warhead size). *
+ * *
+ * warhead -- The type of warhead. *
+ * *
+ * land -- The land type that this explosion is over. Sometimes, this makes *
+ * a difference (especially over water). *
+ * *
+ * OUTPUT: Returns with the animation to play. If no animation is to be played, then *
+ * ANIM_NONE is returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/19/1996 JLB : Created. *
+ *=============================================================================================*/
+AnimType Combat_Anim(int damage, WarheadType warhead, LandType land)
+{
+ /*
+ ** For cases of no damage or invalid warhead, don't have any
+ ** animation effect at all.
+ */
+ if (damage == 0 || warhead == WARHEAD_NONE) {
+ return(ANIM_NONE);
+ }
+
+ static AnimType _aplist[] = {
+ ANIM_VEH_HIT3, // Small fragment throwing explosion -- burn/exp mix.
+ ANIM_VEH_HIT2, // Small fragment throwing explosion -- pop & sparkles.
+ ANIM_FRAG1, // Medium fragment throwing explosion -- short decay.
+ ANIM_FBALL1, // Large fireball explosion (bulges rightward).
+ };
+
+ static AnimType _helist[] = {
+ ANIM_VEH_HIT1, // Small fireball explosion (bulges rightward).
+ ANIM_VEH_HIT2, // Small fragment throwing explosion -- pop & sparkles.
+ ANIM_ART_EXP1, // Large fragment throwing explosion -- many sparkles.
+ ANIM_FBALL1, // Large fireball explosion (bulges rightward).
+ };
+
+ static AnimType _firelist[] = {
+ ANIM_NAPALM1, // Small napalm burn.
+ ANIM_NAPALM2, // Medium napalm burn.
+ ANIM_NAPALM3, // Large napalm burn.
+ };
+
+ static AnimType _waterlist[] = {
+ ANIM_WATER_EXP3,
+ ANIM_WATER_EXP2,
+ ANIM_WATER_EXP1,
+ };
+
+ WarheadTypeClass const * wptr = WarheadTypeClass::As_Pointer(warhead);
+// WarheadTypeClass const * wptr = &Warheads[warhead];
+ switch (wptr->ExplosionSet) {
+ case 6:
+ return(ANIM_ATOM_BLAST);
+
+ case 2:
+ if (damage > 15) {
+ return(ANIM_PIFFPIFF);
+ }
+ return(ANIM_PIFF);
+
+ case 4:
+ if (land == LAND_NONE) return(ANIM_FLAK);
+// Fixed math error
+ if (land == LAND_WATER) return(_waterlist[(ARRAY_SIZE(_waterlist)-1) * fixed(min(damage, 90), 90)]);
+ return(_aplist[(ARRAY_SIZE(_aplist)-1) * fixed(min(damage, 90), 90)]);
+
+ case 5:
+ if (land == LAND_NONE) return(ANIM_FLAK);
+ if (land == LAND_WATER) return(_waterlist[(ARRAY_SIZE(_waterlist)-1) * fixed(min(damage, 130), 130)]);
+ return(_helist[(ARRAY_SIZE(_helist)-1) * fixed(min(damage, 130), 130)]);
+
+ case 3:
+ if (land == LAND_NONE) return(ANIM_FLAK);
+ if (land == LAND_WATER) return(_waterlist[(ARRAY_SIZE(_waterlist)-1) * fixed(min(damage, 150), 150)]);
+ return(_firelist[(ARRAY_SIZE(_firelist)-1) * fixed(min(damage, 150), 150)]);
+
+ case 1:
+ return(ANIM_PIFF);
+
+ default:
+ break;
+ }
+ return(ANIM_NONE);
+}
+
+
+/***********************************************************************************************
+ * Wide_Area_Damage -- Apply wide area damage to the map. *
+ * *
+ * This routine will apply damage to a very wide area on the map. The damage will be *
+ * spread out from the coordinate specified by the radius specified. The amount of damage *
+ * will attenuate according to the distance from center. *
+ * *
+ * INPUT: coord -- The coordinate that the explosion damage will center about. *
+ * *
+ * radius -- The radius of the explosion. *
+ * *
+ * damage -- The amount of damage to apply at the center location. *
+ * *
+ * source -- Pointer to the purpetrator of the damage (if any). *
+ * *
+ * warhead -- The type of warhead that is causing the damage. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/26/1996 JLB : Created. *
+ *=============================================================================================*/
+void Wide_Area_Damage(COORDINATE coord, LEPTON radius, int rawdamage, TechnoClass * source, WarheadType warhead)
+{
+ int cell_radius = (radius + CELL_LEPTON_W-1) / CELL_LEPTON_W;
+ CELL cell = Coord_Cell(coord);
+
+ for (int x = -cell_radius; x <= cell_radius; x++) {
+ for (int y = -cell_radius; y <= cell_radius; y++) {
+ int xpos = Cell_X(cell) + x;
+ int ypos = Cell_Y(cell) + y;
+
+ /*
+ ** If the potential damage cell is outside of the map bounds,
+ ** then don't process it. This unusual check method ensures that
+ ** damage won't wrap from one side of the map to the other.
+ */
+ if ((unsigned)xpos > MAP_CELL_W) {
+ continue;
+ }
+ if ((unsigned)ypos > MAP_CELL_H) {
+ continue;
+ }
+ CELL tcell = XY_Cell(xpos, ypos);
+ if (!Map.In_Radar(tcell)) continue;
+
+ int dist_from_center = Distance(XY_Coord(x+cell_radius, y+cell_radius), XY_Coord(cell_radius, cell_radius));
+ int damage = rawdamage * Inverse(fixed(cell_radius, dist_from_center));
+ Explosion_Damage(Cell_Coord(tcell), damage, source, warhead);
+ if (warhead == WARHEAD_FIRE && damage > 100) {
+ new SmudgeClass(Random_Pick(SMUDGE_SCORCH1, SMUDGE_SCORCH6), Cell_Coord(tcell));
+ }
+ }
+ }
+}
diff --git a/CODE/COMBUF.CPP b/CODE/COMBUF.CPP
new file mode 100644
index 0000000..c52a995
--- /dev/null
+++ b/CODE/COMBUF.CPP
@@ -0,0 +1,1164 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/COMBUF.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***************************************************************************
+ ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S **
+ ***************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : COMBUF.CPP *
+ * *
+ * Programmer : Bill Randolph *
+ * *
+ * Start Date : December 19, 1994 *
+ * *
+ * Last Update : October 23, 1995 [BRR] *
+ * *
+ *-------------------------------------------------------------------------*
+ * Functions: *
+ * CommBufferClass::CommBufferClass -- class constructor *
+ * CommBufferClass::~CommBufferClass -- class destructor *
+ * CommBufferClass::Init -- initializes this queue *
+ * CommBufferClass::Init_Send_Queue -- Clears the send queue *
+ * CommBufferClass::Queue_Send -- queues a message for sending *
+ * CommBufferClass::UnQueue_Send -- removes next entry from send queue *
+ * CommBufferClass::Get_Send -- gets ptr to queue entry *
+ * CommBufferClass::Queue_Receive -- queues a received message *
+ * CommBufferClass::UnQueue_Receive -- removes next entry from send queue*
+ * CommBufferClass::Get_Receive -- gets ptr to queue entry *
+ * CommBufferClass::Add_Delay -- adds a new delay value for response time*
+ * CommBufferClass::Avg_Response_Time -- returns average response time *
+ * CommBufferClass::Max_Response_Time -- returns max response time *
+ * CommBufferClass::Reset_Response_Time -- resets computations *
+ * Mono_Debug_Print -- Debug output routine *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#include "function.h"
+#include
+#include
+#include "combuf.h"
+#include "connect.h" // for command names for debug output
+#include "wwlib32.h" // to enable mono output
+
+
+/***************************************************************************
+ * CommBufferClass::CommBufferClass -- class constructor *
+ * *
+ * INPUT: *
+ * numsend # queue entries for sending *
+ * numreceive # queue entries for receiving *
+ * maxlen maximum desired packet length, in bytes *
+ * extralen max size of app-specific extra bytes (optional) *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/19/1994 BR : Created. *
+ *=========================================================================*/
+CommBufferClass::CommBufferClass(int numsend, int numreceive, int maxlen,
+ int extralen)
+{
+ int i;
+
+ //------------------------------------------------------------------------
+ // Init variables
+ //------------------------------------------------------------------------
+ MaxSend = numsend;
+ MaxReceive = numreceive;
+ MaxPacketSize = maxlen;
+ MaxExtraSize = extralen;
+
+ //------------------------------------------------------------------------
+ // Allocate the queue entries
+ //------------------------------------------------------------------------
+ SendQueue = new SendQueueType[numsend];
+ ReceiveQueue = new ReceiveQueueType[numreceive];
+
+ SendIndex = new int[numsend];
+ ReceiveIndex = new int[numreceive];
+
+ //------------------------------------------------------------------------
+ // Allocate queue entry buffers
+ //------------------------------------------------------------------------
+ for (i = 0; i < MaxSend; i++) {
+ SendQueue[i].Buffer = new char[maxlen];
+ if (MaxExtraSize > 0) {
+ SendQueue[i].ExtraBuffer = new char[MaxExtraSize];
+ }
+ else {
+ SendQueue[i].ExtraBuffer = NULL;
+ }
+ }
+
+ for (i = 0; i < MaxReceive; i++) {
+ ReceiveQueue[i].Buffer = new char[maxlen];
+ if (MaxExtraSize > 0) {
+ ReceiveQueue[i].ExtraBuffer = new char[MaxExtraSize];
+ }
+ else {
+ ReceiveQueue[i].ExtraBuffer = NULL;
+ }
+ }
+
+ Init();
+
+} /* end of CommBufferClass */
+
+
+/***************************************************************************
+ * CommBufferClass::~CommBufferClass -- class destructor *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/19/1994 BR : Created. *
+ *=========================================================================*/
+CommBufferClass::~CommBufferClass()
+{
+ int i;
+
+ //------------------------------------------------------------------------
+ // Free queue entry buffers
+ //------------------------------------------------------------------------
+ for (i = 0; i < MaxSend; i++) {
+ delete [] SendQueue[i].Buffer;
+ if (SendQueue[i].ExtraBuffer) {
+ delete [] SendQueue[i].ExtraBuffer;
+ }
+ }
+
+ for (i = 0; i < MaxReceive; i++) {
+ delete [] ReceiveQueue[i].Buffer;
+ if (ReceiveQueue[i].ExtraBuffer) {
+ delete [] ReceiveQueue[i].ExtraBuffer;
+ }
+ }
+
+ delete [] SendQueue;
+ delete [] ReceiveQueue;
+
+ delete [] SendIndex;
+ delete [] ReceiveIndex;
+
+} /* end of ~CommBufferClass */
+
+
+/***************************************************************************
+ * CommBufferClass::Init -- initializes this queue *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 01/20/1995 BR : Created. *
+ *=========================================================================*/
+void CommBufferClass::Init(void)
+{
+ int i;
+
+ //------------------------------------------------------------------------
+ // Init data members
+ //------------------------------------------------------------------------
+ SendTotal = 0L;
+ ReceiveTotal = 0L;
+
+ DelaySum = 0L;
+ NumDelay = 0L;
+ MeanDelay = 0L;
+ MaxDelay = 0L;
+
+ SendCount = 0;
+
+ ReceiveCount = 0;
+
+ //------------------------------------------------------------------------
+ // Init the queue entries
+ //------------------------------------------------------------------------
+ for (i = 0; i < MaxSend; i++) {
+ SendQueue[i].IsActive = 0;
+ SendQueue[i].IsACK = 0;
+ SendQueue[i].FirstTime = 0L;
+ SendQueue[i].LastTime = 0L;
+ SendQueue[i].SendCount = 0L;
+ SendQueue[i].BufLen = 0;
+ SendQueue[i].ExtraLen = 0;
+
+ SendIndex[i] = 0;
+ }
+
+ for (i = 0; i < MaxReceive; i++) {
+ ReceiveQueue[i].IsActive = 0;
+ ReceiveQueue[i].IsRead = 0;
+ ReceiveQueue[i].IsACK = 0;
+ ReceiveQueue[i].BufLen = 0;
+ ReceiveQueue[i].ExtraLen = 0;
+
+ ReceiveIndex[i] = 0;
+ }
+
+ //------------------------------------------------------------------------
+ // Init debug values
+ //------------------------------------------------------------------------
+ DebugOffset = 0;
+ DebugSize = 0;
+ DebugNames = NULL;
+ DebugNameCount = 0;
+
+} /* end of Init */
+
+
+/***************************************************************************
+ * CommBufferClass::Init_Send_Queue -- Clears the send queue *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 10/23/1995 BRR : Created. *
+ *=========================================================================*/
+void CommBufferClass::Init_Send_Queue(void)
+{
+ int i;
+
+ //------------------------------------------------------------------------
+ // Init data members
+ //------------------------------------------------------------------------
+ SendCount = 0;
+
+ //------------------------------------------------------------------------
+ // Init the queue entries
+ //------------------------------------------------------------------------
+ for (i = 0; i < MaxSend; i++) {
+ SendQueue[i].IsActive = 0;
+ SendQueue[i].IsACK = 0;
+ SendQueue[i].FirstTime = 0L;
+ SendQueue[i].LastTime = 0L;
+ SendQueue[i].SendCount = 0L;
+ SendQueue[i].BufLen = 0;
+ SendQueue[i].ExtraLen = 0;
+
+ SendIndex[i] = 0;
+ }
+
+} /* end of Init_Send_Queue */
+
+
+/***************************************************************************
+ * CommBufferClass::Queue_Send -- queues a message for sending *
+ * *
+ * INPUT: *
+ * buf buffer containing the message *
+ * buflen length of 'buf' *
+ * extrabuf buffer containing extra data (optional) *
+ * extralen length of extra data (optional) *
+ * *
+ * OUTPUT: *
+ * 1 = OK, 0 = no room in the queue *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/20/1994 BR : Created. *
+ *=========================================================================*/
+int CommBufferClass::Queue_Send(void *buf, int buflen, void *extrabuf,
+ int extralen)
+{
+ int i;
+ int index;
+
+ //------------------------------------------------------------------------
+ // Error if no room in the queue
+ //------------------------------------------------------------------------
+ if (SendCount==MaxSend || buflen > MaxPacketSize)
+ return(0);
+
+ //------------------------------------------------------------------------
+ // Find an empty slot
+ //------------------------------------------------------------------------
+ index = -1;
+ for (i = 0; i < MaxSend; i++) {
+ if (SendQueue[i].IsActive==0) {
+ index = i;
+ break;
+ }
+ }
+ if (index == -1)
+ return (0);
+
+ //------------------------------------------------------------------------
+ // Set entry flags
+ //------------------------------------------------------------------------
+ SendQueue[index].IsActive = 1; // entry is now active
+ SendQueue[index].IsACK = 0; // entry hasn't been ACK'd
+ SendQueue[index].FirstTime = 0L; // filled in by Manager when sent
+ SendQueue[index].LastTime = 0L; // filled in by Manager when sent
+ SendQueue[index].SendCount = 0L; // filled in by Manager when sent
+ SendQueue[index].BufLen = buflen; // save buffer size
+
+ //------------------------------------------------------------------------
+ // Copy the packet data
+ //------------------------------------------------------------------------
+ memcpy(SendQueue[index].Buffer,buf,buflen);
+
+ //------------------------------------------------------------------------
+ // Fill in the extra data, if there is any
+ //------------------------------------------------------------------------
+ if (extrabuf!=NULL && extralen > 0 && extralen <= MaxExtraSize) {
+ memcpy(SendQueue[index].ExtraBuffer,extrabuf,extralen);
+ SendQueue[index].ExtraLen = extralen;
+ }
+ else {
+ SendQueue[index].ExtraLen = 0;
+ }
+
+ //------------------------------------------------------------------------
+ // Save this entry's index
+ //------------------------------------------------------------------------
+ SendIndex[SendCount] = index;
+
+ //------------------------------------------------------------------------
+ // Increment counters & entry ptr
+ //------------------------------------------------------------------------
+ SendCount++;
+ SendTotal++;
+
+ return(1);
+
+} /* end of Queue_Send */
+
+
+/***************************************************************************
+ * CommBufferClass::UnQueue_Send -- removes next entry from send queue *
+ * *
+ * Frees the given entry; the index given by the caller is the "active" *
+ * index value (ie the "nth" active entry), not the actual index in the *
+ * array. *
+ * *
+ * INPUT: *
+ * buf buffer to store entry's data in; if NULL, it's discarded *
+ * buflen filled in with length of entry retrieved *
+ * index "index" of entry to un-queue *
+ * extrabuf buffer for extra data (optional) *
+ * extralen ptr to length of extra data (optional) *
+ * *
+ * OUTPUT: *
+ * 1 = OK, 0 = no entry to retrieve *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/20/1994 BR : Created. *
+ *=========================================================================*/
+int CommBufferClass::UnQueue_Send(void *buf, int *buflen, int index,
+ void *extrabuf, int *extralen)
+{
+ int i;
+
+ //------------------------------------------------------------------------
+ // Error if no entry to retrieve
+ //------------------------------------------------------------------------
+ if (SendCount==0 || SendQueue[SendIndex[index]].IsActive==0) {
+ return(0);
+ }
+
+ //------------------------------------------------------------------------
+ // Copy the data from the entry
+ //------------------------------------------------------------------------
+ if (buf!=NULL) {
+ memcpy(buf,SendQueue[SendIndex[index]].Buffer,
+ SendQueue[SendIndex[index]].BufLen);
+ (*buflen) = SendQueue[SendIndex[index]].BufLen;
+ }
+
+ //------------------------------------------------------------------------
+ // Copy the extra data
+ //------------------------------------------------------------------------
+ if (extrabuf!=NULL && extralen!=NULL) {
+ memcpy(extrabuf,SendQueue[SendIndex[index]].ExtraBuffer,
+ SendQueue[SendIndex[index]].ExtraLen);
+ (*extralen) = SendQueue[SendIndex[index]].ExtraLen;
+ }
+
+ //------------------------------------------------------------------------
+ // Set entry flags
+ //------------------------------------------------------------------------
+ SendQueue[SendIndex[index]].IsActive = 0;
+ SendQueue[SendIndex[index]].IsACK = 0;
+ SendQueue[SendIndex[index]].FirstTime = 0L;
+ SendQueue[SendIndex[index]].LastTime = 0L;
+ SendQueue[SendIndex[index]].SendCount = 0L;
+ SendQueue[SendIndex[index]].BufLen = 0;
+ SendQueue[SendIndex[index]].ExtraLen = 0;
+
+ //------------------------------------------------------------------------
+ // Move Indices back one
+ //------------------------------------------------------------------------
+ for (i = index; i < SendCount - 1; i++) {
+ SendIndex[i] = SendIndex[i + 1];
+ }
+ SendIndex[SendCount - 1] = 0;
+ SendCount--;
+
+ return(1);
+
+} /* end of UnQueue_Send */
+
+
+/***************************************************************************
+ * CommBufferClass::Get_Send -- gets ptr to queue entry *
+ * *
+ * This routine gets a pointer to the indicated queue entry. The index *
+ * value is relative to the next-accessible queue entry; 0 = get the *
+ * next available queue entry, 1 = get the one behind that, etc. *
+ * *
+ * INPUT: *
+ * index index of entry to get (0 = 1st available) *
+ * *
+ * OUTPUT: *
+ * ptr to entry *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/21/1994 BR : Created. *
+ *=========================================================================*/
+SendQueueType * CommBufferClass::Get_Send(int index)
+{
+ if (SendQueue[SendIndex[index]].IsActive==0) {
+ return(NULL);
+ }
+ else {
+ return(&SendQueue[SendIndex[index]]);
+ }
+
+} /* end of Get_Send */
+
+
+/***************************************************************************
+ * CommBufferClass::Queue_Receive -- queues a received message *
+ * *
+ * INPUT: *
+ * buf buffer containing the message *
+ * buflen length of 'buf' *
+ * extrabuf buffer containing extra data (optional) *
+ * extralen length of extra data (optional) *
+ * *
+ * OUTPUT: *
+ * 1 = OK, 0 = no room in the queue *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/20/1994 BR : Created. *
+ *=========================================================================*/
+int CommBufferClass::Queue_Receive(void *buf, int buflen, void *extrabuf,
+ int extralen)
+{
+ int i;
+ int index;
+
+ //------------------------------------------------------------------------
+ // Error if no room in the queue
+ //------------------------------------------------------------------------
+ if (ReceiveCount==MaxReceive || buflen > MaxPacketSize) {
+ return(0);
+ }
+
+ //------------------------------------------------------------------------
+ // Find an empty slot
+ //------------------------------------------------------------------------
+ index = -1;
+ for (i = 0; i < MaxReceive; i++) {
+ if (ReceiveQueue[i].IsActive==0) {
+ index = i;
+ break;
+ }
+ }
+ if (index == -1)
+ return (0);
+
+ //------------------------------------------------------------------------
+ // Set entry flags
+ //------------------------------------------------------------------------
+ ReceiveQueue[index].IsActive = 1;
+ ReceiveQueue[index].IsRead = 0;
+ ReceiveQueue[index].IsACK = 0;
+ ReceiveQueue[index].BufLen = buflen;
+
+ //------------------------------------------------------------------------
+ // Copy the packet data
+ //------------------------------------------------------------------------
+ memcpy(ReceiveQueue[index].Buffer,buf,buflen);
+
+ //------------------------------------------------------------------------
+ // Fill in the extra data, if there is any
+ //------------------------------------------------------------------------
+ if (extrabuf!=NULL && extralen > 0 && extralen <= MaxExtraSize) {
+ memcpy(ReceiveQueue[index].ExtraBuffer,extrabuf,extralen);
+ ReceiveQueue[index].ExtraLen = extralen;
+ }
+ else {
+ ReceiveQueue[index].ExtraLen = 0;
+ }
+
+ //------------------------------------------------------------------------
+ // Save this entry's index
+ //------------------------------------------------------------------------
+ ReceiveIndex[ReceiveCount] = index;
+
+ //------------------------------------------------------------------------
+ // Increment counters & entry ptr
+ //------------------------------------------------------------------------
+ ReceiveCount++;
+ ReceiveTotal++;
+
+ return(1);
+
+} /* end of Queue_Receive */
+
+
+/***************************************************************************
+ * CommBufferClass::UnQueue_Receive -- removes next entry from send queue *
+ * *
+ * Frees the given entry; the index given by the caller is the "active" *
+ * index value (ie the "nth" active entry), not the actual index in the *
+ * array. *
+ * *
+ * INPUT: *
+ * buf buffer to store entry's data in; if NULL, it's discarded *
+ * buflen filled in with length of entry retrieved *
+ * index index of entry to un-queue *
+ * extrabuf buffer for extra data (optional) *
+ * extralen ptr to length of extra data (optional) *
+ * *
+ * OUTPUT: *
+ * 1 = OK, 0 = no entry to retrieve *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/20/1994 BR : Created. *
+ *=========================================================================*/
+int CommBufferClass::UnQueue_Receive(void *buf, int *buflen, int index,
+ void *extrabuf, int *extralen)
+{
+ int i;
+
+ //------------------------------------------------------------------------
+ // Error if no entry to retrieve
+ //------------------------------------------------------------------------
+ if (ReceiveCount==0 || ReceiveQueue[ReceiveIndex[index]].IsActive==0) {
+ return(0);
+ }
+
+ //------------------------------------------------------------------------
+ // Copy the data from the entry
+ //------------------------------------------------------------------------
+ if (buf!=NULL) {
+ memcpy(buf,ReceiveQueue[ReceiveIndex[index]].Buffer,
+ ReceiveQueue[ReceiveIndex[index]].BufLen);
+ (*buflen) = ReceiveQueue[ReceiveIndex[index]].BufLen;
+ }
+
+ //------------------------------------------------------------------------
+ // Copy the extra data
+ //------------------------------------------------------------------------
+ if (extrabuf!=NULL && extralen!=NULL) {
+ memcpy(extrabuf,ReceiveQueue[ReceiveIndex[index]].ExtraBuffer,
+ ReceiveQueue[ReceiveIndex[index]].ExtraLen);
+ (*extralen) = ReceiveQueue[ReceiveIndex[index]].ExtraLen;
+ }
+
+ //------------------------------------------------------------------------
+ // Set entry flags
+ //------------------------------------------------------------------------
+ ReceiveQueue[ReceiveIndex[index]].IsActive = 0;
+ ReceiveQueue[ReceiveIndex[index]].IsRead = 0;
+ ReceiveQueue[ReceiveIndex[index]].IsACK = 0;
+ ReceiveQueue[ReceiveIndex[index]].BufLen = 0;
+ ReceiveQueue[ReceiveIndex[index]].ExtraLen = 0;
+
+ //------------------------------------------------------------------------
+ // Move Indices back one
+ //------------------------------------------------------------------------
+ for (i = index; i < ReceiveCount - 1; i++) {
+ ReceiveIndex[i] = ReceiveIndex[i + 1];
+ }
+ ReceiveIndex[ReceiveCount - 1] = 0;
+ ReceiveCount--;
+
+ return(1);
+
+} /* end of UnQueue_Receive */
+
+
+/***************************************************************************
+ * CommBufferClass::Get_Receive -- gets ptr to queue entry *
+ * *
+ * This routine gets a pointer to the indicated queue entry. The index *
+ * value is relative to the next-accessible queue entry; 0 = get the *
+ * next available queue entry, 1 = get the one behind that, etc. *
+ * *
+ * INPUT: *
+ * index index of entry to get (0 = 1st available) *
+ * *
+ * OUTPUT: *
+ * ptr to entry *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/21/1994 BR : Created. *
+ *=========================================================================*/
+ReceiveQueueType * CommBufferClass::Get_Receive(int index)
+{
+ if (ReceiveQueue[ReceiveIndex[index]].IsActive==0) {
+ return(NULL);
+ }
+ else {
+ return(&ReceiveQueue[ReceiveIndex[index]]);
+ }
+
+} /* end of Get_Receive */
+
+
+/***************************************************************************
+ * CommBufferClass::Add_Delay -- adds a new delay value for response time *
+ * *
+ * This routine updates the average response time for this queue. The *
+ * computation is based on the average of the last 'n' delay values given, *
+ * It computes a running total of the last n delay values, then divides *
+ * that by n to compute the average. *
+ * *
+ * When the number of values given exceeds the max, the mean is subtracted *
+ * off the total, then the new value is added in. Thus, any single delay *
+ * value will have an effect on the total that approaches 0 over time, and *
+ * the new delay value contributes to 1/n of the mean. *
+ * *
+ * INPUT: *
+ * delay value to add into the response time computation *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 01/19/1995 BR : Created. *
+ *=========================================================================*/
+void CommBufferClass::Add_Delay(unsigned long delay)
+{
+ int roundoff = 0;
+
+ if (NumDelay==256) {
+ DelaySum -= MeanDelay;
+ DelaySum += delay;
+ if ( (DelaySum & 0x00ff) > 127)
+ roundoff = 1;
+ MeanDelay = (DelaySum >> 8) + roundoff;
+ }
+ else {
+ NumDelay++;
+ DelaySum += delay;
+ MeanDelay = DelaySum / NumDelay;
+ }
+
+ if (delay > MaxDelay) {
+ MaxDelay = delay;
+ }
+
+} /* end of Add_Delay */
+
+
+/***************************************************************************
+ * CommBufferClass::Avg_Response_Time -- returns average response time *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * latest computed average response time *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 01/19/1995 BR : Created. *
+ *=========================================================================*/
+unsigned long CommBufferClass::Avg_Response_Time(void)
+{
+ return(MeanDelay);
+
+} /* end of Avg_Response_Time */
+
+
+/***************************************************************************
+ * CommBufferClass::Max_Response_Time -- returns max response time *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * latest computed average response time *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 01/19/1995 BR : Created. *
+ *=========================================================================*/
+unsigned long CommBufferClass::Max_Response_Time(void)
+{
+ return(MaxDelay);
+
+} /* end of Max_Response_Time */
+
+
+/***************************************************************************
+ * CommBufferClass::Reset_Response_Time -- resets computations *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 01/19/1995 BR : Created. *
+ *=========================================================================*/
+void CommBufferClass::Reset_Response_Time(void)
+{
+ DelaySum = 0L;
+ NumDelay = 0L;
+ MeanDelay = 0L;
+ MaxDelay = 0L;
+
+} /* end of Reset_Response_Time */
+
+
+/***************************************************************************
+ * CommBufferClass::Configure_Debug -- sets up special debug values *
+ * *
+ * Mono_Debug_Print2() can look into a packet to pull out a particular *
+ * ID, and can print both that ID and a string corresponding to *
+ * that ID. This routine configures these values so it can find *
+ * and decode the ID. This ID is used in addition to the normal *
+ * CommHeaderType values. *
+ * *
+ * INPUT: *
+ * type_offset ID's byte offset into packet *
+ * type_size size of ID, in bytes; 0 if none *
+ * names ptr to array of names; use ID as an index into this *
+ * maxnames max # in the names array; 0 if none. *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * Names shouldn't be longer than 12 characters. *
+ * *
+ * HISTORY: *
+ * 05/31/1995 BRR : Created. *
+ *=========================================================================*/
+void CommBufferClass::Configure_Debug(int type_offset, int type_size,
+ char **names, int namestart, int namecount)
+{
+ DebugOffset = type_offset;
+ DebugSize = type_size;
+ DebugNames = names;
+ DebugNameStart = namestart;
+ DebugNameCount = namecount;
+
+} /* end of Configure_Debug */
+
+
+/***************************************************************************
+ * Mono_Debug_Print -- Debug output routine *
+ * *
+ * This routine leaves 5 lines at the top for the caller's use. *
+ * *
+ * INPUT: *
+ * refresh 1 = clear screen & completely refresh *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 05/02/1995 BRR : Created. *
+ *=========================================================================*/
+void CommBufferClass::Mono_Debug_Print(int refresh)
+{
+#ifdef WWLIB32_H
+ int i; // loop counter
+ static int send_col[] = {1,14,28}; // coords of send queue columns
+ static int receive_col[] = {40,54,68}; // coords of recv queue columns
+ int row,col; // current row,col for printing
+ int num; // max # items to print
+
+ struct CommHdr { // this mirrors the CommHeaderType
+ unsigned short MagicNumber;
+ unsigned char Code;
+ unsigned long PacketID;
+ } *hdr;
+
+ //------------------------------------------------------------------------
+ // If few enough entries, call the verbose debug version
+ //------------------------------------------------------------------------
+ if (MaxSend <= 16) {
+ Mono_Debug_Print2(refresh);
+ return;
+ }
+
+ //------------------------------------------------------------------------
+ // Refresh the screen
+ //------------------------------------------------------------------------
+ if (refresh) {
+ Mono_Clear_Screen ();
+ Mono_Printf("ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\n");
+ Mono_Printf("³ ³\n");
+ Mono_Printf("³ ³\n");
+ Mono_Printf("³ ³\n");
+ Mono_Printf("ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n");
+ Mono_Printf("³ Send Queue ³ Receive Queue ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ID Ct ACK ID Ct ACK ID Ct ACK³ ID Rd ACK ID Rd ACK ID Rd ACK³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ");
+ }
+
+ //------------------------------------------------------------------------
+ // Print Send Queue items
+ //------------------------------------------------------------------------
+ if (MaxSend <= 48) {
+ num = MaxSend;
+ }
+ else {
+ num = 48;
+ }
+ col = 0;
+ row = 0;
+ for (i = 0; i < MaxSend; i++) {
+ Mono_Set_Cursor (send_col[col],row + 8);
+ if (SendQueue[i].IsActive) {
+ hdr = (CommHdr *)SendQueue[i].Buffer;
+ hdr->MagicNumber = hdr->MagicNumber;
+ hdr->Code = hdr->Code;
+ Mono_Printf ("%4d %2d %d",hdr->PacketID, SendQueue[i].SendCount,
+ SendQueue[i].IsACK);
+ }
+ else {
+ Mono_Printf ("____ __ _ ");
+ }
+
+ row++;
+ if (row > 15) {
+ row = 0;
+ col++;
+ }
+ }
+
+ //------------------------------------------------------------------------
+ // Print Receive Queue items
+ //------------------------------------------------------------------------
+ if (MaxReceive <= 48) {
+ num = MaxSend;
+ }
+ else {
+ num = 48;
+ }
+ col = 0;
+ row = 0;
+ for (i = 0; i < MaxReceive; i++) {
+ Mono_Set_Cursor (receive_col[col],row + 8);
+ if (ReceiveQueue[i].IsActive) {
+ hdr = (CommHdr *)ReceiveQueue[i].Buffer;
+ Mono_Printf ("%4d %d %d",hdr->PacketID, ReceiveQueue[i].IsRead,
+ ReceiveQueue[i].IsACK);
+ }
+ else {
+ Mono_Printf ("____ _ _ ");
+ }
+
+ row++;
+ if (row > 15) {
+ row = 0;
+ col++;
+ }
+ }
+
+#else
+ refresh = refresh;
+#endif
+} /* end of Mono_Debug_Print */
+
+
+/***************************************************************************
+ * CommBufferClass::Mono_Debug_Print2 -- Debug output; alternate format *
+ * *
+ * This routine prints more information than the other version; it's *
+ * called only if the number of queue entries is small enough to support *
+ * this format. *
+ * *
+ * INPUT: *
+ * refresh 1 = clear screen & completely refresh *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 05/31/1995 BRR : Created. *
+ *=========================================================================*/
+void CommBufferClass::Mono_Debug_Print2(int refresh)
+{
+#ifdef WWLIB32_H
+ int i; // loop counter
+ char txt[80];
+ int val;
+
+ struct CommHdr { // this mirrors the CommHeaderType
+ unsigned short MagicNumber;
+ unsigned char Code;
+ unsigned long PacketID;
+ } *hdr;
+
+ //------------------------------------------------------------------------
+ // Refresh the screen
+ //------------------------------------------------------------------------
+ if (refresh) {
+ Mono_Clear_Screen ();
+ Mono_Printf("ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\n");
+ Mono_Printf("³ ³\n");
+ Mono_Printf("³ ³\n");
+ Mono_Printf("³ ³\n");
+ Mono_Printf("ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n");
+ Mono_Printf("³ Send Queue ³ Receive Queue ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ID Ct Type Data Name ACK ³ ID Rd Type Data Name ACK ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ");
+ }
+
+ //------------------------------------------------------------------------
+ // Print Send Queue items
+ //------------------------------------------------------------------------
+ for (i = 0; i < MaxSend; i++) {
+ Mono_Set_Cursor (1,8 + i);
+
+ //.....................................................................
+ // Print an active entry
+ //.....................................................................
+ if (SendQueue[i].IsActive) {
+
+ //..................................................................
+ // Get header info
+ //..................................................................
+ hdr = (CommHdr *)SendQueue[i].Buffer;
+ hdr->MagicNumber = hdr->MagicNumber;
+ hdr->Code = hdr->Code;
+ sprintf(txt,"%4d %2d %-5s ",
+ hdr->PacketID,
+ SendQueue[i].SendCount,
+ ConnectionClass::Command_Name(hdr->Code));
+
+ //..................................................................
+ // Decode app's ID & its name
+ //..................................................................
+ if (DebugSize && (DebugOffset + DebugSize) <= SendQueue[i].BufLen) {
+ if (DebugSize==1) {
+ val = *(SendQueue[i].Buffer + DebugOffset);
+
+ }
+ else if (DebugSize==2) {
+ val = *((short *)(SendQueue[i].Buffer + DebugOffset));
+
+ }
+ else if (DebugSize==4) {
+ val = *((int *)(SendQueue[i].Buffer + DebugOffset));
+ }
+ sprintf(txt + strlen(txt),"%4d ",val);
+
+ if (DebugNameCount > 0 && val >= 0 && val < DebugNameCount) {
+ sprintf(txt + strlen(txt),"%-12s %x",
+ DebugNames[val - DebugNameStart], SendQueue[i].IsACK);
+ }
+ else {
+ sprintf(txt + strlen(txt)," %x",
+ SendQueue[i].IsACK);
+ }
+ }
+ else {
+ sprintf(txt + strlen(txt)," %x",
+ SendQueue[i].IsACK);
+ }
+
+ Mono_Printf("%s",txt);
+ }
+ else {
+
+ //..................................................................
+ // Entry isn't active; print blanks
+ //..................................................................
+ Mono_Printf("____ __ _");
+ }
+ }
+
+ //------------------------------------------------------------------------
+ // Print Receive Queue items
+ //------------------------------------------------------------------------
+ for (i = 0; i < MaxReceive; i++) {
+ Mono_Set_Cursor (40,8 + i);
+
+ //.....................................................................
+ // Print an active entry
+ //.....................................................................
+ if (ReceiveQueue[i].IsActive) {
+
+ //..................................................................
+ // Get header info
+ //..................................................................
+ hdr = (CommHdr *)ReceiveQueue[i].Buffer;
+ hdr->MagicNumber = hdr->MagicNumber;
+ hdr->Code = hdr->Code;
+ sprintf(txt,"%4d %2d %-5s ",
+ hdr->PacketID,
+ ReceiveQueue[i].IsRead,
+ ConnectionClass::Command_Name(hdr->Code));
+
+ //..................................................................
+ // Decode app's ID & its name
+ //..................................................................
+ if (DebugSize && (DebugOffset + DebugSize) <= ReceiveQueue[i].BufLen) {
+ if (DebugSize==1) {
+ val = *(ReceiveQueue[i].Buffer + DebugOffset);
+
+ }
+ else if (DebugSize==2) {
+ val = *((short *)(ReceiveQueue[i].Buffer + DebugOffset));
+
+ }
+ else if (DebugSize==4) {
+ val = *((int *)(ReceiveQueue[i].Buffer + DebugOffset));
+ }
+ sprintf(txt + strlen(txt),"%4d ",val);
+
+ if (DebugNameCount > 0 && val >= 0 && val < DebugNameCount) {
+ sprintf(txt + strlen(txt),"%-12s %x",
+ DebugNames[val - DebugNameStart], ReceiveQueue[i].IsACK);
+ }
+ else {
+ sprintf(txt + strlen(txt)," %x",
+ ReceiveQueue[i].IsACK);
+ }
+ }
+ else {
+ sprintf(txt + strlen(txt)," %x",
+ ReceiveQueue[i].IsACK);
+ }
+
+ Mono_Printf("%s",txt);
+ }
+ else {
+
+ //..................................................................
+ // Entry isn't active; print blanks
+ //..................................................................
+ Mono_Printf("____ __ _");
+ }
+ }
+
+#else
+ refresh = refresh;
+#endif
+} /* end of Mono_Debug_Print2 */
+
+
+/************************** end of combuf.cpp ******************************/
+
+
diff --git a/CODE/COMBUF.H b/CODE/COMBUF.H
new file mode 100644
index 0000000..029452e
--- /dev/null
+++ b/CODE/COMBUF.H
@@ -0,0 +1,193 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/COMBUF.H 1 3/03/97 10:24a Joe_bostic $ */
+/***************************************************************************
+ ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S **
+ ***************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : COMBUF.H *
+ * *
+ * Programmer : Bill Randolph *
+ * *
+ * Start Date : December 19, 1994 *
+ * *
+ * Last Update : April 1, 1995 [BR] *
+ * *
+ *-------------------------------------------------------------------------*
+ * *
+ * This class's job is to store outgoing messages & incoming messages, *
+ * and serves as a storage area for various flags for ACK & Retry logic. *
+ * *
+ * This class stores buffers in a non-sequenced order; it allows freeing *
+ * any entry, so the buffers can be kept clear, even if packets come in *
+ * out of order. *
+ * *
+ * The class also contains routines to maintain a cumulative response time *
+ * for this queue. It's up to the caller to call Add_Delay() whenever *
+ * it detects that an outgoing message has been ACK'd; this class adds *
+ * that delay into a computed average delay over the last few message *
+ * delays. *
+ * *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifndef COMBUF_H
+#define COMBUF_H
+
+
+/*
+********************************** Defines **********************************
+*/
+/*---------------------------------------------------------------------------
+This is one output queue entry
+---------------------------------------------------------------------------*/
+typedef struct {
+ unsigned int IsActive : 1; // 1 = this entry is ready to be processed
+ unsigned int IsACK : 1; // 1 = ACK received for this packet
+ unsigned long FirstTime; // time this packet was first sent
+ unsigned long LastTime; // time this packet was last sent
+ unsigned long SendCount; // # of times this packet has been sent
+ int BufLen; // size of the packet stored in this entry
+ char *Buffer; // the data packet
+ int ExtraLen; // size of extra data
+ char *ExtraBuffer; // extra data buffer
+} SendQueueType;
+
+/*---------------------------------------------------------------------------
+This is one input queue entry
+---------------------------------------------------------------------------*/
+typedef struct {
+ unsigned int IsActive : 1; // 1 = this entry is ready to be processed
+ unsigned int IsRead : 1; // 1 = caller has read this entry
+ unsigned int IsACK : 1; // 1 = ACK sent for this packet
+ int BufLen; // size of the packet stored in this entry
+ char *Buffer; // the data packet
+ int ExtraLen; // size of extra data
+ char *ExtraBuffer; // extra data buffer
+} ReceiveQueueType;
+
+/*
+***************************** Class Declaration *****************************
+*/
+class CommBufferClass
+{
+ /*
+ ---------------------------- Public Interface ----------------------------
+ */
+ public:
+ /*
+ ....................... Constructor/Destructor ........................
+ */
+ CommBufferClass(int numsend, int numrecieve, int maxlen,
+ int extralen = 0);
+ virtual ~CommBufferClass();
+ void Init(void);
+ void Init_Send_Queue(void);
+
+ /*
+ ......................... Send Queue routines .........................
+ */
+ int Queue_Send(void *buf, int buflen, void *extrabuf = NULL,
+ int extralen = 0);
+ int UnQueue_Send(void *buf, int *buflen, int index,
+ void *extrabuf = NULL, int *extralen = NULL);
+ int Num_Send(void) {return (SendCount);} // # entries in queue
+ int Max_Send(void) { return (MaxSend);} // max # send queue entries
+ SendQueueType * Get_Send(int index); // random access to queue
+ unsigned long Send_Total(void) {return (SendTotal);}
+
+ /*
+ ....................... Receive Queue routines ........................
+ */
+ int Queue_Receive(void *buf, int buflen, void *extrabuf = NULL,
+ int extralen = 0);
+ int UnQueue_Receive(void *buf, int *buflen, int index,
+ void *extrabuf = NULL, int *extralen = NULL);
+ int Num_Receive(void) {return (ReceiveCount);} // # entries in queue
+ int Max_Receive(void) { return (MaxReceive); } // max # recv queue entries
+ ReceiveQueueType * Get_Receive(int index); // random access to queue
+ unsigned long Receive_Total(void) {return (ReceiveTotal);}
+
+ /*
+ ....................... Response time routines ........................
+ */
+ void Add_Delay(unsigned long delay); // accumulates response time
+ unsigned long Avg_Response_Time(void); // gets mean response time
+ unsigned long Max_Response_Time(void); // gets max response time
+ void Reset_Response_Time(void); // resets computations
+
+ /*
+ ........................ Debug output routines ........................
+ */
+ void Configure_Debug(int type_offset, int type_size, char **names,
+ int namestart, int namecount);
+ void Mono_Debug_Print(int refresh = 0);
+ void Mono_Debug_Print2(int refresh = 0);
+
+ /*
+ --------------------------- Private Interface ----------------------------
+ */
+ private:
+ /*
+ .......................... Limiting variables .........................
+ */
+ int MaxSend; // max # send queue entries
+ int MaxReceive; // max # receive queue entries
+ int MaxPacketSize; // max size of a packet, in bytes
+ int MaxExtraSize; // max size of extra bytes
+
+ /*
+ ....................... Response time variables .......................
+ */
+ unsigned long DelaySum; // sum of last 4 delay times
+ unsigned long NumDelay; // current # delay times summed
+ unsigned long MeanDelay; // current average delay time
+ unsigned long MaxDelay; // max delay ever for this queue
+
+ /*
+ ........................ Send Queue variables .........................
+ */
+ SendQueueType * SendQueue; // incoming packets
+ int SendCount; // # packets in the queue
+ unsigned long SendTotal; // total # added to send queue
+ int *SendIndex; // array of Send entry indices
+
+ /*
+ ....................... Receive Queue variables .......................
+ */
+ ReceiveQueueType * ReceiveQueue; // outgoing packets
+ int ReceiveCount; // # packets in the queue
+ unsigned long ReceiveTotal; // total # added to receive queue
+ int *ReceiveIndex; // array of Receive entry indices
+
+ /*
+ ......................... Debugging Variables .........................
+ */
+ int DebugOffset; // offset into app's packet for ID
+ int DebugSize; // size of app's ID
+ char **DebugNames; // ptr to array of app-specific names
+ int DebugNameStart; // number of 1st ID
+ int DebugNameCount; // # of names in array
+};
+
+#endif
+
+/**************************** end of combuf.h ******************************/
+
diff --git a/CODE/COMINIT.CPP b/CODE/COMINIT.CPP
new file mode 100644
index 0000000..73e6608
--- /dev/null
+++ b/CODE/COMINIT.CPP
@@ -0,0 +1,51 @@
+/*
+** Command & Conquer Red Alert(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 .
+*/
+
+//
+// If you link with this it will automatically call the COM initialization stuff
+//
+
+#include "cominit.h"
+//#include
+//#include
+//#include
+#include
+//#include "externs.h"
+//#include "text.rh"
+
+//#include "WolDebug.h"
+
+ComInit::ComInit()
+{
+ //HRESULT hRes = CoInitialize(NULL);
+// if (SUCCEEDED(hRes)==FALSE)
+// exit(0);
+ HRESULT hRes = OleInitialize(NULL);
+}
+
+ComInit::~ComInit()
+{
+// CoUninitialize();
+// debugprint( "pre OleUninitialize\n" );
+ OleUninitialize();
+// debugprint( "post OleUninitialize\n" );
+}
+
+// Creating this instance will setup all COM stuff & do cleanup on program exit
+ComInit Global_COM_Initializer;
+
diff --git a/CODE/COMINIT.H b/CODE/COMINIT.H
new file mode 100644
index 0000000..172db63
--- /dev/null
+++ b/CODE/COMINIT.H
@@ -0,0 +1,34 @@
+/*
+** Command & Conquer Red Alert(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 .
+*/
+
+#ifndef COMINIT_HEADER
+#define COMINIT_HEADER
+
+//
+// Link with this to automatically initialize COM at startup
+// - See cominit.cpp for more info
+//
+
+class ComInit
+{
+ public:
+ ComInit();
+ ~ComInit();
+};
+
+#endif
diff --git a/CODE/COMPAT.H b/CODE/COMPAT.H
new file mode 100644
index 0000000..528dcb8
--- /dev/null
+++ b/CODE/COMPAT.H
@@ -0,0 +1,217 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/COMPAT.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : COMPAT.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 03/02/95 *
+ * *
+ * Last Update : March 2, 1995 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifndef COMPAT_H
+#define COMPAT_H
+
+
+#define BuffType BufferClass
+//#define movmem(a,b,c) memmove(b,a,c)
+#define ShapeBufferSize _ShapeBufferSize
+
+/*=========================================================================*/
+/* Define some equates for the different graphic routines we will install */
+/* later. */
+/*=========================================================================*/
+#define HIDBUFF ((void *)(0xA0000))
+#define Size_Of_Region(a, b) ((a)*(b))
+
+/*=========================================================================*/
+/* Define some Graphic Routines which will only be fixed by these defines */
+/*=========================================================================*/
+#define Set_Font_Palette(a) Set_Font_Palette_Range(a, 0, 15)
+
+/*
+** These are the Open_File, Read_File, and Seek_File constants.
+*/
+#define READ 1 // Read access.
+#define WRITE 2 // Write access.
+
+#ifndef SEEK_SET
+#define SEEK_SET 0 // Seek from start of file.
+#define SEEK_CUR 1 // Seek relative from current location.
+#define SEEK_END 2 // Seek from end of file.
+#endif
+
+#define ERROR_WINDOW 1
+#define ErrorWindow 1
+
+
+//extern unsigned char *Palette;
+extern unsigned char MDisabled; // Is mouse disabled?
+extern WORD Hard_Error_Occured;
+
+/*
+** This is the menu control structures.
+*/
+typedef enum MenuIndexType {
+ MENUX,
+ MENUY,
+ ITEMWIDTH,
+ ITEMSHIGH,
+ MSELECTED,
+ NORMCOL,
+ HILITE,
+ MENUPADDING=0x1000
+} MenuIndexType;
+
+
+#ifdef NEVER
+#define BITSPERBYTE 8
+#define MAXSHORT 0x7FFF
+#define HIBITS 0x8000
+#define MAXLONG 0x7FFFFFFFL
+#define HIBITL 0x80000000
+
+#define MAXINT MAXLONG
+#define HIBITI HIBITL
+
+#define DMAXEXP 308
+#define FMAXEXP 38
+#define DMINEXP -307
+#define FMINEXP -37
+
+#define MAXDOUBLE 1.797693E+308
+#define MAXFLOAT 3.37E+38F
+#define MINDOUBLE 2.225074E-308
+#define MINFLOAT 8.43E-37F
+
+#define DSIGNIF 53
+#define FSIGNIF 24
+
+#define DMAXPOWTWO 0x3FF
+#define FMAXPOWTWO 0x7F
+#define DEXPLEN 11
+#define FEXPLEN 8
+#define EXPBASE 2
+#define IEEE 1
+#define LENBASE 1
+#define HIDDENBIT 1
+#define LN_MAXDOUBLE 7.0978E+2
+#define LN_MINDOUBLE -7.0840E+2
+#endif
+
+/* These defines handle the various names given to the same color. */
+#define DKGREEN GREEN
+#define DKBLUE BLUE
+#define GRAY GREY
+#define DKGREY GREY
+#define DKGRAY GREY
+#define LTGRAY LTGREY
+
+
+class IconsetClass;
+#ifndef WIN32
+typedef struct {
+ short Width; // Width of icons (pixels).
+ short Height; // Height of icons (pixels).
+ short Count; // Number of (logical) icons in this set.
+ short Allocated; // Was this iconset allocated?
+ short MapWidth; // Width of map (in icons).
+ short MapHeight; // Height of map (in icons).
+ long Size; // Size of entire iconset memory block.
+ long Icons; // Offset from buffer start to icon data.
+// unsigned char * Icons; // Offset from buffer start to icon data.
+ long Palettes; // Offset from buffer start to palette data.
+ long Remaps; // Offset from buffer start to remap index data.
+ long TransFlag; // Offset for transparency flag table.
+ long ColorMap; // Offset for color control value table.
+ long Map; // Icon map offset (if present).
+// unsigned char * Map; // Icon map offset (if present).
+} IControl_Type;
+#endif
+
+inline int Get_IconSet_MapWidth(void const * data)
+{
+ if (data) {
+ return(((IControl_Type *)data)->MapWidth);
+ }
+ return(0);
+}
+
+inline int Get_IconSet_MapHeight(void const * data)
+{
+ if (data) {
+ return(((IControl_Type *)data)->MapHeight);
+ }
+ return(0);
+}
+
+inline unsigned char const * Get_IconSet_ControlMap(void const * data)
+{
+ if (data) {
+ return((unsigned char const *)((char *)data + ((IControl_Type *)data)->ColorMap));
+ }
+ return(0);
+}
+
+class IconsetClass : protected IControl_Type
+{
+ public:
+ /*
+ ** Query functions.
+ */
+ int Map_Width(void) const {return(MapWidth);};
+ int Map_Height(void) const {return(MapHeight);};
+ unsigned char * Control_Map(void) {return((unsigned char *)this + ColorMap);};
+ unsigned char const * Control_Map(void) const {return((unsigned char const *)this + ColorMap);};
+ int Icon_Count(void) const {return(Count);};
+ int Pixel_Width(void) const {return(Width);};
+ int Pixel_Height(void) const {return(Height);};
+ int Total_Size(void) const {return(Size);};
+ unsigned char const * Palette_Data(void) const {return((unsigned char const *)this + Palettes);};
+ unsigned char * Palette_Data(void) {return((unsigned char *)this + Palettes);};
+ unsigned char const * Icon_Data(void) const {return((unsigned char const *)this + Icons);};
+ unsigned char * Icon_Data(void) {return((unsigned char *)this + Icons);};
+ unsigned char const * Map_Data(void) const {return((unsigned char const *)this + Map);};
+ unsigned char * Map_Data(void) {return((unsigned char *)this + Map);};
+ unsigned char const * Remap_Data(void) const {return((unsigned char const *)this + Remaps);};
+ unsigned char * Remap_Data(void) {return((unsigned char *)this + Remaps);};
+ unsigned char const * Trans_Data(void) const {return((unsigned char const *)this + TransFlag);};
+ unsigned char * Trans_Data(void) {return((unsigned char *)this + TransFlag);};
+
+ /*
+ ** Disallow these operations with an IconsetClass object.
+ */
+ private:
+ IconsetClass & operator = (IconsetClass const &);
+ IconsetClass(void);
+ static void * operator new(size_t);
+};
+
+
+#endif
diff --git a/CODE/COMQUEUE.CPP b/CODE/COMQUEUE.CPP
new file mode 100644
index 0000000..5f55194
--- /dev/null
+++ b/CODE/COMQUEUE.CPP
@@ -0,0 +1,1003 @@
+/*
+** Command & Conquer Red Alert(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&c0\vcs\code\comqueue.cpv 4.1 11 Apr 1996 18:28:16 JOE_BOSTIC $ */
+/***************************************************************************
+ ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S **
+ ***************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : COMQUEUE.CPP *
+ * *
+ * Programmer : Bill Randolph *
+ * *
+ * Start Date : December 19, 1994 *
+ * *
+ * Last Update : May 31, 1995 [BRR] *
+ * *
+ *-------------------------------------------------------------------------*
+ * Functions: *
+ * CommQueueClass::Add_Delay -- adds a new delay value for response time *
+ * CommQueueClass::Avg_Response_Time -- returns average response time *
+ * CommQueueClass::CommQueueClass -- class constructor *
+ * CommQueueClass::Configure_Debug -- sets up special debug values *
+ * CommQueueClass::Get_Receive -- gets ptr to queue entry *
+ * CommQueueClass::Get_Send -- gets ptr to queue entry *
+ * CommQueueClass::Init -- initializes this queue *
+ * CommQueueClass::Max_Response_Time -- returns max response time *
+ * CommQueueClass::Mono_Debug_Print -- Debug output routine *
+ * CommQueueClass::Mono_Debug_Print2 -- Debug output; alternate format *
+ * CommQueueClass::Next_Receive -- gets ptr to next entry in send queue *
+ * CommQueueClass::Next_Send -- gets ptr to next entry in send queue *
+ * CommQueueClass::Queue_Receive -- queues a received message *
+ * CommQueueClass::Queue_Send -- queues a message for sending *
+ * CommQueueClass::Reset_Response_Time -- resets computations *
+ * CommQueueClass::UnQueue_Receive -- removes next entry from send queue *
+ * CommQueueClass::UnQueue_Send -- removes next entry from send queue *
+ * CommQueueClass::~CommQueueClass -- class destructor *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+
+
+/***************************************************************************
+ * CommQueueClass::CommQueueClass -- class constructor *
+ * *
+ * INPUT: *
+ * numsend # queue entries for sending *
+ * numreceive # queue entries for receiving *
+ * maxlen maximum desired packet length, in bytes *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/19/1994 BR : Created. *
+ *=========================================================================*/
+CommQueueClass::CommQueueClass(int numsend, int numreceive, int maxlen)
+{
+ int i;
+
+ /*
+ ** Init variables
+ */
+ MaxSend = numsend;
+ MaxReceive = numreceive;
+ MaxPacketSize = maxlen;
+
+ /*
+ ** Allocate the queue entries
+ */
+ SendQueue = new SendQueueType[numsend];
+ ReceiveQueue = new ReceiveQueueType[numreceive];
+
+ /*
+ ** Allocate queue entry buffers
+ */
+ for (i = 0; i < MaxSend; i++) {
+ SendQueue[i].Buffer = new char[maxlen];
+ }
+
+ for (i = 0; i < MaxReceive; i++) {
+ ReceiveQueue[i].Buffer = new char[maxlen];
+ }
+
+ Init();
+}
+
+
+/***************************************************************************
+ * CommQueueClass::~CommQueueClass -- class destructor *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/19/1994 BR : Created. *
+ *=========================================================================*/
+CommQueueClass::~CommQueueClass()
+{
+ int i;
+
+ /*
+ ** Free queue entry buffers
+ */
+ for (i = 0; i < MaxSend; i++) {
+ delete [] SendQueue[i].Buffer;
+ }
+
+ for (i = 0; i < MaxReceive; i++) {
+ delete [] ReceiveQueue[i].Buffer;
+ }
+
+ delete [] SendQueue;
+ delete [] ReceiveQueue;
+}
+
+
+/***************************************************************************
+ * CommQueueClass::Init -- initializes this queue *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 01/20/1995 BR : Created. *
+ *=========================================================================*/
+void CommQueueClass::Init(void)
+{
+ int i;
+
+ /*
+ ** Init data members
+ */
+ SendTotal = 0L;
+ ReceiveTotal = 0L;
+
+ DelaySum = 0L;
+ NumDelay = 0L;
+ MeanDelay = 0L;
+ MaxDelay = 0L;
+
+ SendCount = 0;
+ SendNext = 0;
+ SendEmpty = 0;
+
+ ReceiveCount = 0;
+ ReceiveNext = 0;
+ ReceiveEmpty = 0;
+
+ /*
+ ** Init the queue entries
+ */
+ for (i = 0; i < MaxSend; i++) {
+ SendQueue[i].IsActive = 0;
+ SendQueue[i].IsACK = 0;
+ SendQueue[i].FirstTime = 0L;
+ SendQueue[i].LastTime = 0L;
+ SendQueue[i].SendCount = 0L;
+ ReceiveQueue[i].BufLen = 0;
+ }
+ for (i = 0; i < MaxReceive; i++) {
+ ReceiveQueue[i].IsActive = 0;
+ ReceiveQueue[i].IsRead = 0;
+ ReceiveQueue[i].IsACK = 0;
+ ReceiveQueue[i].BufLen = 0;
+ }
+
+ /*
+ ** Init debug values
+ */
+ DebugOffset = 0;
+ DebugSize = 0;
+ DebugNames = NULL;
+ DebugMaxNames = 0;
+}
+
+
+/***************************************************************************
+ * CommQueueClass::Queue_Send -- queues a message for sending *
+ * *
+ * INPUT: *
+ * buf buffer containing the message *
+ * buflen length of 'buf' *
+ * *
+ * OUTPUT: *
+ * 1 = OK, 0 = no room in the queue *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/20/1994 BR : Created. *
+ *=========================================================================*/
+int CommQueueClass::Queue_Send(void *buf, int buflen)
+{
+ /*
+ ** Error if no room in the queue
+ */
+ if (SendCount==MaxSend || SendQueue[SendEmpty].IsActive!=0) {
+ return(0);
+ }
+
+ /*
+ ** Set entry flags
+ */
+ SendQueue[SendEmpty].IsActive = 1; // entry is now active
+ SendQueue[SendEmpty].IsACK = 0; // entry hasn't been ACK'd
+ SendQueue[SendEmpty].FirstTime = 0L; // filled in by Manager when sent
+ SendQueue[SendEmpty].LastTime = 0L; // filled in by Manager when sent
+ SendQueue[SendEmpty].SendCount = 0L; // filled in by Manager when sent
+ SendQueue[SendEmpty].BufLen = buflen; // save buffer size
+
+ /*
+ ** Copy the packet data
+ */
+ memcpy(SendQueue[SendEmpty].Buffer,buf,buflen);
+
+ /*
+ ** Increment counters & entry ptr
+ */
+ SendCount++;
+ SendEmpty++;
+ if (SendEmpty==MaxSend) {
+ SendEmpty = 0;
+ }
+
+ SendTotal++;
+
+ return(1);
+}
+
+
+/***************************************************************************
+ * CommQueueClass::UnQueue_Send -- removes next entry from send queue *
+ * *
+ * INPUT: *
+ * buf buffer to store entry's data in; if NULL, it's discarded *
+ * buflen filled in with length of entry retrieved *
+ * *
+ * OUTPUT: *
+ * 1 = OK, 0 = no entry to retreive *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/20/1994 BR : Created. *
+ *=========================================================================*/
+int CommQueueClass::UnQueue_Send(void *buf, int *buflen)
+{
+ /*
+ ** Error if no entry to retrieve
+ */
+ if (SendCount==0 || SendQueue[SendNext].IsActive==0) {
+ return(0);
+ }
+
+ /*
+ ** Copy the data from the entry
+ */
+ if (buf!=NULL) {
+ memcpy(buf,SendQueue[SendNext].Buffer,SendQueue[SendNext].BufLen);
+ (*buflen) = SendQueue[SendNext].BufLen;
+ }
+
+ /*
+ ** Set entry flags
+ */
+ SendQueue[SendNext].IsActive = 0;
+ SendQueue[SendNext].IsACK = 0;
+ SendQueue[SendNext].FirstTime = 0L;
+ SendQueue[SendNext].LastTime = 0L;
+ SendQueue[SendNext].SendCount = 0L;
+ SendQueue[SendNext].BufLen = 0;
+ SendCount--;
+ SendNext++;
+ if (SendNext==MaxSend) {
+ SendNext = 0;
+ }
+
+ return(1);
+}
+
+
+/***************************************************************************
+ * CommQueueClass::Next_Send -- gets ptr to next entry in send queue *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * ptr to entry, NULL if there is none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/20/1994 BR : Created. *
+ *=========================================================================*/
+SendQueueType * CommQueueClass::Next_Send(void)
+{
+ if (SendCount==0) {
+ return(NULL);
+ } else {
+ return(&SendQueue[SendNext]);
+ }
+}
+
+
+/***************************************************************************
+ * CommQueueClass::Get_Send -- gets ptr to queue entry *
+ * *
+ * This routine gets a pointer to the indicated queue entry. The index *
+ * value is relative to the next-accessable queue entry; 0 = get the *
+ * next available queue entry, 1 = get the one behind that, etc. *
+ * *
+ * INPUT: *
+ * index index of entry to get (0 = 1st available) *
+ * *
+ * OUTPUT: *
+ * ptr to entry *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/21/1994 BR : Created. *
+ *=========================================================================*/
+SendQueueType * CommQueueClass::Get_Send(int index)
+{
+ int i;
+
+ i = SendNext + index;
+ if (i >= MaxSend) {
+ i -= MaxSend;
+ }
+
+ if (SendQueue[i].IsActive==0) {
+ return(NULL);
+ } else {
+ return(&SendQueue[i]);
+ }
+}
+
+
+/***************************************************************************
+ * CommQueueClass::Queue_Receive -- queues a received message *
+ * *
+ * INPUT: *
+ * buf buffer containing the message *
+ * buflen length of 'buf' *
+ * *
+ * OUTPUT: *
+ * 1 = OK, 0 = no room in the queue *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/20/1994 BR : Created. *
+ *=========================================================================*/
+int CommQueueClass::Queue_Receive(void *buf, int buflen)
+{
+ /*
+ ** Error if no room in the queue
+ */
+ if (ReceiveCount==MaxReceive || ReceiveQueue[ReceiveEmpty].IsActive!=0) {
+ return(0);
+ }
+
+ /*
+ ** Set entry flags
+ */
+ ReceiveQueue[ReceiveEmpty].IsActive = 1;
+ ReceiveQueue[ReceiveEmpty].IsRead = 0;
+ ReceiveQueue[ReceiveEmpty].IsACK = 0;
+ ReceiveQueue[ReceiveEmpty].BufLen = buflen;
+
+ /*
+ ** Copy the packet data
+ */
+ memcpy(ReceiveQueue[ReceiveEmpty].Buffer,buf,buflen);
+
+ /*
+ ** Increment counters & entry ptr
+ */
+ ReceiveCount++;
+ ReceiveEmpty++;
+ if (ReceiveEmpty==MaxReceive) {
+ ReceiveEmpty = 0;
+ }
+
+ ReceiveTotal++;
+
+ return(1);
+}
+
+
+/***************************************************************************
+ * CommQueueClass::UnQueue_Receive -- removes next entry from send queue *
+ * *
+ * INPUT: *
+ * buf buffer to store entry's data in; if NULL, it's discarded *
+ * buflen filled in with length of entry retrieved *
+ * *
+ * OUTPUT: *
+ * 1 = OK, 0 = no entry to retreive *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/20/1994 BR : Created. *
+ *=========================================================================*/
+int CommQueueClass::UnQueue_Receive(void *buf, int *buflen)
+{
+ /*
+ ** Error if no entry to retrieve
+ */
+ if (ReceiveCount==0 || ReceiveQueue[ReceiveNext].IsActive==0) {
+ return(0);
+ }
+
+ /*
+ ** Copy the data from the entry
+ */
+ if (buf!=NULL) {
+ memcpy(buf,ReceiveQueue[ReceiveNext].Buffer,
+ ReceiveQueue[ReceiveNext].BufLen);
+ (*buflen) = ReceiveQueue[ReceiveNext].BufLen;
+ }
+
+ /*
+ ** Set entry flags
+ */
+ ReceiveQueue[ReceiveNext].IsActive = 0;
+ ReceiveQueue[ReceiveNext].IsRead = 0;
+ ReceiveQueue[ReceiveNext].IsACK = 0;
+ ReceiveQueue[ReceiveNext].BufLen = 0;
+ ReceiveCount--;
+ ReceiveNext++;
+ if (ReceiveNext==MaxReceive) {
+ ReceiveNext = 0;
+ }
+
+ return(1);
+}
+
+
+/***************************************************************************
+ * CommQueueClass::Next_Receive -- gets ptr to next entry in send queue *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * ptr to entry, NULL if there is none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/20/1994 BR : Created. *
+ *=========================================================================*/
+ReceiveQueueType * CommQueueClass::Next_Receive(void)
+{
+ if (ReceiveCount==0) {
+ return(NULL);
+ } else {
+ return(&ReceiveQueue[ReceiveNext]);
+ }
+}
+
+
+/***************************************************************************
+ * CommQueueClass::Get_Receive -- gets ptr to queue entry *
+ * *
+ * This routine gets a pointer to the indicated queue entry. The index *
+ * value is relative to the next-accessable queue entry; 0 = get the *
+ * next available queue entry, 1 = get the one behind that, etc. *
+ * *
+ * INPUT: *
+ * index index of entry to get (0 = 1st available) *
+ * *
+ * OUTPUT: *
+ * ptr to entry *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/21/1994 BR : Created. *
+ *=========================================================================*/
+ReceiveQueueType * CommQueueClass::Get_Receive(int index)
+{
+ int i;
+
+ i = ReceiveNext + index;
+ if (i >= MaxReceive) {
+ i -= MaxReceive;
+ }
+
+ if (ReceiveQueue[i].IsActive==0) {
+ return(NULL);
+ } else {
+ return(&ReceiveQueue[i]);
+ }
+}
+
+
+/***************************************************************************
+ * CommQueueClass::Add_Delay -- adds a new delay value for response time *
+ * *
+ * This routine updates the average response time for this queue. The *
+ * computation is based on the average of the last 'n' delay values given, *
+ * It computes a running total of the last n delay values, then divides *
+ * that by n to compute the average. *
+ * *
+ * When the number of values given exceeds the max, the mean is subtracted *
+ * off the total, then the new value is added in. Thus, any single delay *
+ * value will have an effect on the total that approaches 0 over time, and *
+ * the new delay value contributes to 1/n of the mean. *
+ * *
+ * INPUT: *
+ * delay value to add into the response time computation *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 01/19/1995 BR : Created. *
+ *=========================================================================*/
+void CommQueueClass::Add_Delay(unsigned long delay)
+{
+ int roundoff = 0;
+
+ if (NumDelay==256) {
+ DelaySum -= MeanDelay;
+ DelaySum += delay;
+ if ( (DelaySum & 0x00ff) > 127) {
+ roundoff = 1;
+ }
+ MeanDelay = (DelaySum >> 8) + roundoff;
+ } else {
+ NumDelay++;
+ DelaySum += delay;
+ MeanDelay = DelaySum / NumDelay;
+ }
+
+ if (delay > MaxDelay) {
+ MaxDelay = delay;
+ }
+}
+
+
+/***************************************************************************
+ * CommQueueClass::Avg_Response_Time -- returns average response time *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * latest computed average response time *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 01/19/1995 BR : Created. *
+ *=========================================================================*/
+unsigned long CommQueueClass::Avg_Response_Time(void)
+{
+ return(MeanDelay);
+}
+
+
+/***************************************************************************
+ * CommQueueClass::Max_Response_Time -- returns max response time *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * latest computed average response time *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 01/19/1995 BR : Created. *
+ *=========================================================================*/
+unsigned long CommQueueClass::Max_Response_Time(void)
+{
+ return(MaxDelay);
+}
+
+
+/***************************************************************************
+ * CommQueueClass::Reset_Response_Time -- resets computations *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 01/19/1995 BR : Created. *
+ *=========================================================================*/
+void CommQueueClass::Reset_Response_Time(void)
+{
+ DelaySum = 0L;
+ NumDelay = 0L;
+ MeanDelay = 0L;
+ MaxDelay = 0L;
+}
+
+
+/***************************************************************************
+ * CommQueueClass::Configure_Debug -- sets up special debug values *
+ * *
+ * Mono_Debug_Print2() can look into a packet to pull out a particular *
+ * ID, and can print both that ID and a string corresponding to *
+ * that ID. This routine configures these values so it can find *
+ * and decode the ID. This ID is used in addition to the normal *
+ * CommHeaderType values. *
+ * *
+ * INPUT: *
+ * offset ID's byte offset into packet *
+ * size size of ID, in bytes; 0 if none *
+ * names ptr to array of names; use ID as an index into this *
+ * maxnames max # in the names array; 0 if none. *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * Names shouldn't be longer than 12 characters. *
+ * *
+ * HISTORY: *
+ * 05/31/1995 BRR : Created. *
+ *=========================================================================*/
+void CommQueueClass::Configure_Debug(int offset, int size, char **names,
+ int maxnames)
+{
+ DebugOffset = offset;
+ DebugSize = size;
+ DebugNames = names;
+ DebugMaxNames = maxnames;
+}
+
+
+/***************************************************************************
+ * Mono_Debug_Print -- Debug output routine *
+ * *
+ * This routine leaves 5 lines at the top for the caller's use. *
+ * *
+ * INPUT: *
+ * refresh 1 = clear screen & completely refresh *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 05/02/1995 BRR : Created. *
+ *=========================================================================*/
+void CommQueueClass::Mono_Debug_Print(int refresh)
+{
+#ifdef WWLIB32_H
+ int i; // loop counter
+ static int send_col[] = {1,14,28}; // coords of send queue columns
+ static int receive_col[] = {40,54,68}; // coords of recv queue columns
+ int row,col; // current row,col for printing
+ int num; // max # items to print
+
+ struct CommHdr { // this mirrors the CommHeaderType
+ unsigned short MagicNumber;
+ unsigned char Code;
+ unsigned long PacketID;
+ } *hdr;
+
+ /*
+ ** If few enough entries, call the verbose debug version
+ */
+ if (MaxSend <= 16) {
+ Mono_Debug_Print2(refresh);
+ return;
+ }
+
+ /*
+ ** Refresh the screen
+ */
+ if (refresh) {
+ Mono_Clear_Screen ();
+ Mono_Printf("ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\n");
+ Mono_Printf("³ ³\n");
+ Mono_Printf("³ ³\n");
+ Mono_Printf("³ ³\n");
+ Mono_Printf("ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n");
+ Mono_Printf("³ Send Queue ³ Receive Queue ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ID Ct ACK ID Ct ACK ID Ct ACK³ ID Rd ACK ID Rd ACK ID Rd ACK³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ");
+ }
+
+ /*
+ ** Print Send Queue items
+ */
+ if (MaxSend <= 48) {
+ num = MaxSend;
+ } else {
+ num = 48;
+ }
+ col = 0;
+ row = 0;
+ for (i = 0; i < MaxSend; i++) {
+ Mono_Set_Cursor (send_col[col],row + 8);
+ if (SendQueue[i].IsActive) {
+ hdr = (CommHdr *)SendQueue[i].Buffer;
+ hdr->MagicNumber = hdr->MagicNumber;
+ hdr->Code = hdr->Code;
+ Mono_Printf ("%4d %2d %d",hdr->PacketID, SendQueue[i].SendCount,
+ SendQueue[i].IsACK);
+ } else {
+ Mono_Printf ("____ __ _ ");
+ }
+
+ row++;
+ if (row > 15) {
+ row = 0;
+ col++;
+ }
+ }
+
+ /*
+ ** Print Receive Queue items
+ */
+ if (MaxReceive <= 48) {
+ num = MaxSend;
+ } else {
+ num = 48;
+ }
+ col = 0;
+ row = 0;
+ for (i = 0; i < MaxReceive; i++) {
+ Mono_Set_Cursor (receive_col[col],row + 8);
+ if (ReceiveQueue[i].IsActive) {
+ hdr = (CommHdr *)ReceiveQueue[i].Buffer;
+ Mono_Printf ("%4d %d %d",hdr->PacketID, ReceiveQueue[i].IsRead,
+ ReceiveQueue[i].IsACK);
+ } else {
+ Mono_Printf ("____ _ _ ");
+ }
+
+ row++;
+ if (row > 15) {
+ row = 0;
+ col++;
+ }
+ }
+
+#else
+ refresh = refresh;
+#endif
+}
+
+
+/***************************************************************************
+ * CommQueueClass::Mono_Debug_Print2 -- Debug output; alternate format *
+ * *
+ * This routine prints more information than the other version; it's *
+ * called only if the number of queue entries is small enough to support *
+ * this format. *
+ * *
+ * INPUT: *
+ * refresh 1 = clear screen & completely refresh *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 05/31/1995 BRR : Created. *
+ *=========================================================================*/
+void CommQueueClass::Mono_Debug_Print2(int refresh)
+{
+#ifdef WWLIB32_H
+ int i; // loop counter
+ char txt[80];
+ int val;
+
+ struct CommHdr { // this mirrors the CommHeaderType
+ unsigned short MagicNumber;
+ unsigned char Code;
+ unsigned long PacketID;
+ } *hdr;
+
+ /*
+ ** Refresh the screen
+ */
+ if (refresh) {
+ Mono_Clear_Screen ();
+ Mono_Printf("ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\n");
+ Mono_Printf("³ ³\n");
+ Mono_Printf("³ ³\n");
+ Mono_Printf("³ ³\n");
+ Mono_Printf("ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n");
+ Mono_Printf("³ Send Queue ³ Receive Queue ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ID Ct Type Data Name ACK ³ ID Rd Type Data Name ACK ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("³ ³ ³\n");
+ Mono_Printf("ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ");
+ }
+
+ /*
+ ** Print Send Queue items
+ */
+ for (i = 0; i < MaxSend; i++) {
+ Mono_Set_Cursor (1,8 + i);
+
+ /*
+ ** Print an active entry
+ */
+ if (SendQueue[i].IsActive) {
+
+ /*
+ ** Get header info
+ */
+ hdr = (CommHdr *)SendQueue[i].Buffer;
+ hdr->MagicNumber = hdr->MagicNumber;
+ hdr->Code = hdr->Code;
+ sprintf(txt,"%4d %2d %-5s ",
+ hdr->PacketID,
+ SendQueue[i].SendCount,
+ ConnectionClass::Command_Name(hdr->Code));
+
+ /*
+ ** Decode app's ID & its name
+ */
+ if (DebugSize && (DebugOffset + DebugSize) <= SendQueue[i].BufLen) {
+ if (DebugSize==1) {
+ val = *(SendQueue[i].Buffer + DebugOffset);
+ } else {
+ if (DebugSize==2) {
+ val = *((short *)(SendQueue[i].Buffer + DebugOffset));
+ } else {
+ if (DebugSize==4) {
+ val = *((int *)(SendQueue[i].Buffer + DebugOffset));
+ }
+ }
+ }
+ sprintf(txt + strlen(txt),"%4d ",val);
+
+ if (DebugMaxNames && val > 0 && val < DebugMaxNames) {
+ sprintf(txt + strlen(txt),"%-12s %x",
+ DebugNames[val],
+ SendQueue[i].IsACK);
+ } else {
+ sprintf(txt + strlen(txt)," %x",SendQueue[i].IsACK);
+ }
+ }
+ } else {
+
+ /*
+ ** Entry isn't active; print blanks
+ */
+ Mono_Printf("____ __ _");
+ }
+ }
+
+ /*
+ ** Print Receive Queue items
+ */
+ for (i = 0; i < MaxReceive; i++) {
+ Mono_Set_Cursor (40,8 + i);
+
+ /*
+ ** Print an active entry
+ */
+ if (ReceiveQueue[i].IsActive) {
+
+ /*
+ ** Get header info
+ */
+ hdr = (CommHdr *)ReceiveQueue[i].Buffer;
+ hdr->MagicNumber = hdr->MagicNumber;
+ hdr->Code = hdr->Code;
+ sprintf(txt,"%4d %2d %-5s ",
+ hdr->PacketID,
+ ReceiveQueue[i].IsRead,
+ ConnectionClass::Command_Name(hdr->Code));
+
+ /*
+ ** Decode app's ID & its name
+ */
+ if (DebugSize && (DebugOffset + DebugSize) <= SendQueue[i].BufLen) {
+ if (DebugSize==1) {
+ val = *(ReceiveQueue[i].Buffer + DebugOffset);
+ } else {
+ if (DebugSize==2) {
+ val = *((short *)(ReceiveQueue[i].Buffer + DebugOffset));
+ } else {
+ if (DebugSize==4) {
+ val = *((int *)(ReceiveQueue[i].Buffer + DebugOffset));
+ }
+ }
+ }
+ sprintf(txt + strlen(txt),"%4d ",val);
+
+ if (DebugMaxNames && val > 0 && val < DebugMaxNames) {
+ sprintf(txt + strlen(txt),"%-12s %x", DebugNames[val], ReceiveQueue[i].IsACK);
+ } else {
+ sprintf(txt + strlen(txt)," %x",ReceiveQueue[i].IsACK);
+ }
+ }
+ } else {
+
+ /*
+ ** Entry isn't active; print blanks
+ */
+ Mono_Printf("____ __ _");
+ }
+ }
+
+#else
+ refresh = refresh;
+#endif
+}
+
+
diff --git a/CODE/COMQUEUE.H b/CODE/COMQUEUE.H
new file mode 100644
index 0000000..d5d5d9e
--- /dev/null
+++ b/CODE/COMQUEUE.H
@@ -0,0 +1,193 @@
+/*
+** Command & Conquer Red Alert(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&c0\vcs\code\comqueue.h_v 4.1 11 Apr 1996 18:26:02 JOE_BOSTIC $ */
+/***************************************************************************
+ ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S **
+ ***************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : COMQUEUE.H *
+ * *
+ * Programmer : Bill Randolph *
+ * *
+ * Start Date : December 19, 1994 *
+ * *
+ * Last Update : April 1, 1995 [BR] *
+ * *
+ *-------------------------------------------------------------------------*
+ * *
+ * This class's job is to queue up outgoing messages & incoming messages, *
+ * and serves as a storage area for various flags for ACK & Retry logic. *
+ * It allows the application to keep track of how many messages have *
+ * passed through this queue, in & out, so packets can use this as a *
+ * unique ID. (If packets have a unique ID, the application can use this *
+ * to detect re-sends.) *
+ * *
+ * The queues act as FIFO buffers (First-In, First-Out). The first entry *
+ * placed in a queue is the first one read from it, and so on. The *
+ * controlling application must ensure it places the entries on the queue *
+ * in the order it wants to access them. *
+ * *
+ * The queue is implemented as an array of Queue Entries. Index 0 is the *
+ * first element placed on the queue, and is the first retrieved. Index *
+ * 1 is the next, and so on. When Index 0 is retrieved, the next-available*
+ * entry becomes Index 1. The array is circular; when the end is reached, *
+ * the indices wrap around to the beginning. *
+ * *
+ * The class also contains routines to maintain a cumulative response time *
+ * for this queue. It's up to the caller to call Add_Delay() whenever *
+ * it detects that an outgoing message has been ACK'd; this class adds *
+ * that delay into a computed average delay over the last few message *
+ * delays. *
+ * *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifndef COMQUEUE_H
+#define COMQUEUE_H
+
+
+
+/*---------------------------------------------------------------------------
+This is one output queue entry
+---------------------------------------------------------------------------*/
+typedef struct {
+ unsigned int IsActive : 1; // 1 = this entry is ready to be processed
+ unsigned int IsACK : 1; // 1 = ACK received for this packet
+ unsigned long FirstTime; // time this packet was first sent
+ unsigned long LastTime; // time this packet was last sent
+ unsigned long SendCount; // # of times this packet has been sent
+ int BufLen; // size of the packet stored in this entry
+ char *Buffer; // the data packet
+} SendQueueType;
+
+/*---------------------------------------------------------------------------
+This is one input queue entry
+---------------------------------------------------------------------------*/
+typedef struct {
+ unsigned int IsActive : 1; // 1 = this entry is ready to be processed
+ unsigned int IsRead : 1; // 1 = caller has read this entry
+ unsigned int IsACK : 1; // 1 = ACK sent for this packet
+ int BufLen; // size of the packet stored in this entry
+ char *Buffer; // the data packet
+} ReceiveQueueType;
+
+/*
+***************************** Class Declaration *****************************
+*/
+class CommQueueClass
+{
+ /*
+ ---------------------------- Public Interface ----------------------------
+ */
+ public:
+ /*
+ ....................... Constructor/Destructor ........................
+ */
+ CommQueueClass(int numsend, int numrecieve, int maxlen);
+ virtual ~CommQueueClass();
+ void Init(void);
+
+ /*
+ ......................... Send Queue routines .........................
+ */
+ int Queue_Send(void *buf, int buflen); // add to Send queue
+ int UnQueue_Send(void *buf, int *buflen); // remove from Send queue
+ SendQueueType * Next_Send(void); // ptr to next avail entry
+ int Num_Send(void) {return (SendCount);} // # entries in queue
+ int Max_Send(void) { return (MaxSend);} // max # send queue entries
+ SendQueueType * Get_Send(int index); // random access to queue
+ unsigned long Send_Total(void) {return (SendTotal);}
+
+ /*
+ ....................... Receive Queue routines ........................
+ */
+ int Queue_Receive(void *buf, int buflen); // add to Receive queue
+ int UnQueue_Receive(void *buf, int *buflen); // remove from Receive queue
+ ReceiveQueueType * Next_Receive(void); // ptr to next avail entry
+ int Num_Receive(void) {return (ReceiveCount);} // # entries in queue
+ int Max_Receive(void) { return (MaxReceive); } // max # recv queue entries
+ ReceiveQueueType * Get_Receive(int index); // random access to queue
+ unsigned long Receive_Total(void) {return (ReceiveTotal);}
+
+ /*
+ ....................... Response time routines ........................
+ */
+ void Add_Delay(unsigned long delay); // accumulates response time
+ unsigned long Avg_Response_Time(void); // gets mean response time
+ unsigned long Max_Response_Time(void); // gets max response time
+ void Reset_Response_Time(void); // resets computations
+
+ /*
+ ........................ Debug output routines ........................
+ */
+ void Configure_Debug(int offset, int size, char **names, int maxnames);
+ void Mono_Debug_Print(int refresh = 0);
+ void Mono_Debug_Print2(int refresh = 0);
+
+ /*
+ --------------------------- Private Interface ----------------------------
+ */
+ private:
+ /*
+ .......................... Limiting variables .........................
+ */
+ int MaxSend; // max # send queue entries
+ int MaxReceive; // max # receive queue entries
+ int MaxPacketSize; // max size of a packet, in bytes
+
+ /*
+ ....................... Response time variables .......................
+ */
+ unsigned long DelaySum; // sum of last 4 delay times
+ unsigned long NumDelay; // current # delay times summed
+ unsigned long MeanDelay; // current average delay time
+ unsigned long MaxDelay; // max delay ever for this queue
+
+ /*
+ ........................ Send Queue variables .........................
+ */
+ SendQueueType * SendQueue; // incoming packets
+ int SendCount; // # packets in the queue
+ int SendNext; // next entry read from queue
+ int SendEmpty; // next empty spot in queue
+ unsigned long SendTotal; // total # added to send queue
+
+ /*
+ ....................... Receive Queue variables .......................
+ */
+ ReceiveQueueType * ReceiveQueue; // outgoing packets
+ int ReceiveCount; // # packets in the queue
+ int ReceiveNext; // next entry read from queue
+ int ReceiveEmpty; // next empty spot in queue
+ unsigned long ReceiveTotal; // total # added to receive queue
+
+ /*
+ ......................... Debugging Variables .........................
+ */
+ int DebugOffset; // offset into app's packet for ID
+ int DebugSize; // size of app's ID
+ char **DebugNames; // ptr to array of app-specific names
+ int DebugMaxNames; // max # of names in array
+};
+
+#endif
+
+/*************************** end of comqueue.h *****************************/
+
diff --git a/CODE/CONFDLG.CPP b/CODE/CONFDLG.CPP
new file mode 100644
index 0000000..7afacc2
--- /dev/null
+++ b/CODE/CONFDLG.CPP
@@ -0,0 +1,244 @@
+/*
+** Command & Conquer Red Alert(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&c0\vcs\code\confdlg.cpv 4.67 27 Aug 1996 15:46:52 JOE_BOSTIC $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CONFDLG.CPP *
+ * *
+ * Programmer : Maria del Mar McCready Legg *
+ * Joe L. Bostic *
+ * *
+ * Start Date : Jan 30, 1995 *
+ * *
+ * Last Update : Jan 30, 1995 [MML] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * ConfirmationClass::Process -- Handles all the options graphic interface. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+#include "confdlg.h"
+
+
+bool ConfirmationClass::Process(int text)
+{
+ return(Process(Text_String(text)));
+}
+
+
+/***********************************************************************************************
+ * ConfirmationClass::Process -- Handles all the options graphic interface. *
+ * *
+ * This dialog uses an edit box to confirm a deletion. *
+ * *
+ * INPUT: char * string - display in edit box. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: 12/31/1994 MML : Created. *
+ *=============================================================================================*/
+bool ConfirmationClass::Process(char const * string)
+{
+ enum {
+ NUM_OF_BUTTONS = 2
+ };
+
+ char buffer[80*3];
+ int result = true;
+ int width;
+ int bwidth, bheight; // button width and height
+ int height;
+ int selection = 0;
+ bool pressed;
+ int curbutton;
+ TextButtonClass * buttons[NUM_OF_BUTTONS];
+
+ /*
+ ** Set up the window. Window x-coords are in bytes not pixels.
+ */
+ strcpy(buffer, string);
+ Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_6PT_GRAD | TPF_NOSHADOW);
+ Format_Window_String(buffer, 200, width, height);
+ width += 60;
+ height += 60;
+ int x = (320 - width) / 2;
+ int y = (200 - height) / 2;
+
+ Set_Logic_Page(SeenBuff);
+
+ /*
+ ** Create Buttons. Button coords are in pixels, but are window-relative.
+ */
+ bheight = FontHeight + FontYSpacing + 2;
+ bwidth = max( (String_Pixel_Width( Text_String( TXT_YES ) ) + 8), 30);
+
+ TextButtonClass yesbtn(BUTTON_YES, TXT_YES,
+ TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW,
+ x + 10, y + height - (bheight + 5), bwidth );
+
+ TextButtonClass nobtn(BUTTON_NO, TXT_NO,
+ TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW,
+ x + width - (bwidth + 10),
+ y + height - (bheight + 5), bwidth );
+
+ nobtn.Add_Tail(yesbtn);
+
+ curbutton = 1;
+ buttons[0] = &yesbtn;
+ buttons[1] = &nobtn;
+ buttons[curbutton]->Turn_On();
+
+ /*
+ ** This causes left mouse button clicking within the confines of the dialog to
+ ** be ignored if it wasn't recognized by any other button or slider.
+ */
+ GadgetClass dialog(x, y, width, height, GadgetClass::LEFTPRESS);
+ dialog.Add_Tail(yesbtn);
+
+ /*
+ ** This causes a right click anywhere or a left click outside the dialog region
+ ** to be equivalent to clicking on the return to options dialog.
+ */
+ ControlClass background(BUTTON_NO, 0, 0, 320, 200, GadgetClass::LEFTPRESS|GadgetClass::RIGHTPRESS);
+ background.Add_Tail(yesbtn);
+
+ /*
+ ** Main Processing Loop.
+ */
+ bool display = true;
+ bool process = true;
+ pressed = false;
+ while (process) {
+
+ /*
+ ** Invoke game callback.
+ */
+ if (Session.Type == GAME_NORMAL) {
+ Call_Back();
+ } else {
+ if (Main_Loop()) {
+ process = false;
+ result = false;
+ }
+ }
+
+ /*
+ ** Refresh display if needed.
+ */
+ if (display) {
+ Hide_Mouse();
+
+ /*
+ ** Draw the background.
+ */
+ Dialog_Box(x, y, width, height);
+ Draw_Caption(TXT_CONFIRMATION, x, y, width);
+ Fancy_Text_Print(buffer, x+20, y+30, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW);
+
+ /*
+ ** Draw the titles.
+ */
+ yesbtn.Draw_All();
+ Show_Mouse();
+ display = false;
+ }
+
+ /*
+ ** Get user input.
+ */
+ KeyNumType input = yesbtn.Input();
+
+ /*
+ ** Process Input.
+ */
+ switch (input) {
+ case KeyNumType(BUTTON_YES | KN_BUTTON):
+ selection = BUTTON_YES;
+ pressed = true;
+ break;
+
+ case (KN_ESC):
+ case KeyNumType(BUTTON_NO | KN_BUTTON):
+ selection = BUTTON_NO;
+ pressed = true;
+ break;
+
+ case (KN_LEFT):
+ buttons[curbutton]->Turn_Off();
+ buttons[curbutton]->Flag_To_Redraw();
+
+ curbutton--;
+ if (curbutton < 0) {
+ curbutton = NUM_OF_BUTTONS - 1;
+ }
+
+ buttons[curbutton]->Turn_On();
+ buttons[curbutton]->Flag_To_Redraw();
+ break;
+
+ case (KN_RIGHT):
+ buttons[curbutton]->Turn_Off();
+ buttons[curbutton]->Flag_To_Redraw();
+
+ curbutton++;
+ if (curbutton > (NUM_OF_BUTTONS - 1) ) {
+ curbutton = 0;
+ }
+
+ buttons[curbutton]->Turn_On();
+ buttons[curbutton]->Flag_To_Redraw();
+ break;
+
+ case (KN_RETURN):
+ selection = curbutton + BUTTON_YES;
+ pressed = true;
+ break;
+
+ default:
+ break;
+ }
+
+ if (pressed) {
+ switch (selection) {
+ case (BUTTON_YES):
+ result = true;
+ process = false;
+ break;
+
+ case (BUTTON_NO):
+ result = false;
+ process = false;
+ break;
+
+ default:
+ break;
+ }
+
+ pressed = false;
+ }
+ }
+ return(result);
+}
diff --git a/CODE/CONFDLG.H b/CODE/CONFDLG.H
new file mode 100644
index 0000000..ec272d3
--- /dev/null
+++ b/CODE/CONFDLG.H
@@ -0,0 +1,56 @@
+/*
+** Command & Conquer Red Alert(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&c0\vcs\code\confdlg.h_v 4.69 27 Aug 1996 15:43:36 JOE_BOSTIC $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CONFDLG.H *
+ * *
+ * Programmer : Maria del Mar McCready Legg *
+ * Joe L. Bostic *
+ * *
+ * Start Date : Jan 30, 1995 *
+ * *
+ * Last Update : Jan 30, 1995 [MML] *
+ * *
+ *---------------------------------------------------------------------------------------------*/
+
+#ifndef CONFDLG_H
+#define CONFDLG_H
+
+#include "gadget.h"
+
+class ConfirmationClass
+{
+ private:
+ enum ConfirmationClassEnum {
+ BUTTON_YES=1, // Button number for "Options menu"
+ BUTTON_NO // Button number for "Options menu"
+ };
+
+ public:
+ ConfirmationClass(void) { };
+ bool Process(char const * string);
+ bool Process(int text);
+};
+
+#endif
diff --git a/CODE/CONNECT.CPP b/CODE/CONNECT.CPP
new file mode 100644
index 0000000..7e87a65
--- /dev/null
+++ b/CODE/CONNECT.CPP
@@ -0,0 +1,838 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CONNECT.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***************************************************************************
+ ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S **
+ ***************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CONNECT.CPP *
+ * *
+ * Programmer : Bill Randolph *
+ * *
+ * Start Date : December 20, 1994 *
+ * *
+ * Last Update : May 31, 1995 [BRR] *
+ *-------------------------------------------------------------------------*
+ * Functions: *
+ * ConnectionClass::ConnectionClass -- class constructor *
+ * ConnectionClass::~ConnectionClass -- class destructor *
+ * ConnectionClass::Init -- Initializes connection queue to empty *
+ * ConnectionClass::Send_Packet -- adds a packet to the send queue *
+ * ConnectionClass::Receive_Packet -- adds packet to receive queue *
+ * ConnectionClass::Get_Packet -- gets a packet from receive queue *
+ * ConnectionClass::Service -- main polling routine; services packets *
+ * ConnectionClass::Service_Send_Queue -- services the send queue *
+ * ConnectionClass::Service_Receive_Queue -- services receive queue *
+ * ConnectionClass::Time -- gets current time *
+ * ConnectionClass::Command_Name -- returns name for a packet command *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+
+#include "function.h"
+#include
+#include
+#include
+#include "connect.h"
+
+#include "WolDebug.h"
+
+/*
+********************************* Globals ***********************************
+*/
+static char *ConnectionClass::Commands[PACKET_COUNT] = {
+ "ADATA",
+ "NDATA",
+ "ACK"
+};
+
+
+/***************************************************************************
+ * ConnectionClass::ConnectionClass -- class constructor *
+ * *
+ * INPUT: *
+ * numsend desired # of entries for the send queue *
+ * numreceive desired # of entries for the receive queue *
+ * maxlen max length of an application packet *
+ * magicnum the packet "magic number" for this connection *
+ * retry_delta the time to wait between sends *
+ * max_retries the max # of retries allowed for a packet *
+ * (-1 means retry forever, based on this parameter) *
+ * timeout the max amount of time before we give up on a packet *
+ * (-1 means retry forever, based on this parameter) *
+ * extralen max size of app-specific extra bytes (optional) *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/20/1994 BR : Created. *
+ *=========================================================================*/
+ConnectionClass::ConnectionClass (int numsend, int numreceive,
+ int maxlen, unsigned short magicnum, unsigned long retry_delta,
+ unsigned long max_retries, unsigned long timeout, int extralen)
+{
+ /*------------------------------------------------------------------------
+ Compute our maximum packet length
+ ------------------------------------------------------------------------*/
+ MaxPacketLen = maxlen + sizeof(CommHeaderType);
+
+ /*------------------------------------------------------------------------
+ Assign the magic number
+ ------------------------------------------------------------------------*/
+ MagicNum = magicnum;
+
+ /*------------------------------------------------------------------------
+ Initialize the retry time. This is the time that t2 - t1 must be greater
+ than before a retry will occur.
+ ------------------------------------------------------------------------*/
+ RetryDelta = retry_delta;
+
+ /*------------------------------------------------------------------------
+ Set the maximum allowable retries.
+ ------------------------------------------------------------------------*/
+ MaxRetries = max_retries;
+
+ /*------------------------------------------------------------------------
+ Set the timeout for this connection.
+ ------------------------------------------------------------------------*/
+ Timeout = timeout;
+
+ /*------------------------------------------------------------------------
+ Allocate the packet staging buffer. This will be used to
+ ------------------------------------------------------------------------*/
+ PacketBuf = new char[ MaxPacketLen ];
+
+ /*------------------------------------------------------------------------
+ Allocate the packet Queue. This will store incoming packets (placed there
+ by Receive_Packet), and outgoing packets (placed there by Send_Packet).
+ It can optionally store "extra" bytes, which are stored along with each
+ packet, but aren't transmitted as part of the packet. If 'extralen'
+ is 0, the CommBufferClass ignores this parameter.
+ ------------------------------------------------------------------------*/
+ Queue = new CommBufferClass (numsend, numreceive, MaxPacketLen, extralen);
+
+} /* end of ConnectionClass */
+
+
+/***************************************************************************
+ * ConnectionClass::~ConnectionClass -- class destructor *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/20/1994 BR : Created. *
+ *=========================================================================*/
+ConnectionClass::~ConnectionClass ()
+{
+ /*------------------------------------------------------------------------
+ Free memory.
+ ------------------------------------------------------------------------*/
+ delete [] PacketBuf;
+ delete Queue;
+
+} /* end of ~ConnectionClass */
+
+
+/***************************************************************************
+ * ConnectionClass::Init -- Initializes connection queue to empty *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/20/1994 BR : Created. *
+ *=========================================================================*/
+void ConnectionClass::Init (void)
+{
+ NumRecNoAck = 0;
+ NumRecAck = 0;
+ NumSendNoAck = 0;
+ NumSendAck = 0;
+
+ LastSeqID = 0xffffffff;
+ LastReadID = 0xffffffff;
+
+ Queue->Init();
+
+} /* end of Init */
+
+
+/***************************************************************************
+ * ConnectionClass::Send_Packet -- adds a packet to the send queue *
+ * *
+ * This routine prefixes the given buffer with a CommHeaderType and *
+ * queues the resulting packet into the Send Queue. (It's actually the *
+ * Service() routine that handles the hardware-dependent Send of the data).*
+ * The packet's MagicNumber, Code, and PacketID are set here. *
+ * *
+ * INPUT: *
+ * buf buffer to send *
+ * buflen length of buffer *
+ * ack_req 1 = ACK is required for this packet; 0 = isn't *
+ * *
+ * OUTPUT: *
+ * 1 = packet was queue'd OK, 0 = wasn't *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/20/1994 BR : Created. *
+ *=========================================================================*/
+int ConnectionClass::Send_Packet (void * buf, int buflen, int ack_req)
+{
+ /*------------------------------------------------------------------------
+ Set the magic # for the packet
+ ------------------------------------------------------------------------*/
+ ((CommHeaderType *)PacketBuf)->MagicNumber = MagicNum;
+
+ /*------------------------------------------------------------------------
+ Set the packet Code: DATA_ACK if it requires an ACK, NOACK if it doesn't
+ Set the packet ID to the appropriate counter value.
+ ------------------------------------------------------------------------*/
+ if (ack_req) {
+ ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_ACK;
+ ((CommHeaderType *)PacketBuf)->PacketID = NumSendAck;
+ }
+ else {
+ ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_NOACK;
+ ((CommHeaderType *)PacketBuf)->PacketID = NumSendNoAck;
+ }
+
+ /*------------------------------------------------------------------------
+ Now build the packet
+ ------------------------------------------------------------------------*/
+ memcpy(PacketBuf + sizeof(CommHeaderType), buf, buflen);
+
+ /*------------------------------------------------------------------------
+ Add it to the queue; don't add any extra data with it.
+ ------------------------------------------------------------------------*/
+ if (Queue->Queue_Send(PacketBuf,buflen + sizeof(CommHeaderType), NULL, 0)) {
+ if (ack_req) {
+ NumSendAck++;
+ }
+ else {
+ NumSendNoAck++;
+ }
+ return(1);
+ }
+ else {
+ return(0);
+ }
+
+} /* end of Send_Packet */
+
+
+/***************************************************************************
+ * ConnectionClass::Receive_Packet -- adds packet to receive queue *
+ * *
+ * INPUT: *
+ * buf buffer to process (already includes CommHeaderType) *
+ * buflen length of buffer to process *
+ * *
+ * OUTPUT: *
+ * 1 = packet was processed OK, 0 = error *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/20/1994 BR : Created. *
+ *=========================================================================*/
+int ConnectionClass::Receive_Packet (void * buf, int buflen)
+{
+ CommHeaderType *packet; // ptr to packet header
+ SendQueueType *send_entry; // ptr to send entry header
+ ReceiveQueueType *rec_entry; // ptr to recv entry header
+ CommHeaderType *entry_data; // ptr to queue entry data
+ CommHeaderType ackpacket; // ACK packet to send
+ int i;
+ int save_packet = 1; // 0 = this is a resend
+ int found;
+
+ /*------------------------------------------------------------------------
+ Check the magic #
+ ------------------------------------------------------------------------*/
+ packet = (CommHeaderType *)buf;
+ if (packet->MagicNumber != MagicNum) {
+ return(0);
+ }
+
+ /*------------------------------------------------------------------------
+ Handle an incoming ACK
+ ------------------------------------------------------------------------*/
+ if (packet->Code == PACKET_ACK) {
+
+ for (i = 0; i < Queue->Num_Send(); i++) {
+ /*..................................................................
+ Get queue entry ptr
+ ..................................................................*/
+ send_entry = Queue->Get_Send(i);
+
+ /*..................................................................
+ If ptr is valid, get ptr to its data
+ ..................................................................*/
+ if (send_entry != NULL) {
+ entry_data = (CommHeaderType *)send_entry->Buffer;
+
+ /*...............................................................
+ If ACK is for this entry, mark it
+ ...............................................................*/
+ if (packet->PacketID==entry_data->PacketID &&
+ entry_data->Code == PACKET_DATA_ACK) {
+ send_entry->IsACK = 1;
+ break;
+ }
+ }
+ }
+
+ return(1);
+ }
+
+ /*------------------------------------------------------------------------
+ Handle an incoming PACKET_DATA_NOACK packet
+ ------------------------------------------------------------------------*/
+ else if (packet->Code == PACKET_DATA_NOACK) {
+ /*.....................................................................
+ If there's only one slot left, don't tie up the queue with this packet
+ .....................................................................*/
+ if (Queue->Max_Receive() - Queue->Num_Receive() <= 1) {
+ return(0);
+ }
+
+ /*.....................................................................
+ Error if we can't queue the packet
+ .....................................................................*/
+ if (!Queue->Queue_Receive (buf, buflen, NULL, 0)) {
+ return(0);
+ }
+
+ NumRecNoAck++;
+
+ return(1);
+ }
+
+ /*------------------------------------------------------------------------
+ Handle an incoming PACKET_DATA_ACK packet
+ ------------------------------------------------------------------------*/
+ else if (packet->Code == PACKET_DATA_ACK) {
+ /*.....................................................................
+ If this is a packet requires an ACK, and it's ID is older than our
+ "oldest" ID, we know it's a resend; send an ACK, but don't queue it
+ .....................................................................*/
+ if (packet->PacketID <= LastSeqID && LastSeqID != 0xffffffff) {
+ save_packet = 0;
+ }
+
+ /*.....................................................................
+ Otherwise, scan the queue for this entry; if it's found, it's a
+ resend, so don't save it.
+ .....................................................................*/
+ else {
+ save_packet = 1;
+ for (i = 0; i < Queue->Num_Receive(); i++) {
+ rec_entry = Queue->Get_Receive(i);
+
+ if (rec_entry) {
+
+ entry_data = (CommHeaderType *)rec_entry->Buffer;
+
+ /*...........................................................
+ Packet is found; it's a resend
+ ...........................................................*/
+ if (entry_data->Code == PACKET_DATA_ACK &&
+ entry_data->PacketID == packet->PacketID) {
+ save_packet = 0;
+ break;
+ }
+ }
+ }
+ } /* end of scan for resend */
+
+ /*.....................................................................
+ Queue the packet & update our LastSeqID value.
+ .....................................................................*/
+ if (save_packet) {
+ /*..................................................................
+ If there's only one slot left, make sure we only put a packet in it
+ if this packet will let us increment our LastSeqID; otherwise, we'll
+ get stuck, forever unable to increment LastSeqID.
+ ..................................................................*/
+ if (Queue->Max_Receive() - Queue->Num_Receive() <= 1) {
+ if (packet->PacketID != (LastSeqID + 1) ) {
+ return(0);
+ }
+ }
+
+ /*..................................................................
+ If we can't queue the packet, return; don't send an ACK.
+ ..................................................................*/
+ if (!Queue->Queue_Receive (buf, buflen, NULL, 0)) {
+ return(0);
+ }
+
+ NumRecAck++;
+
+ /*..................................................................
+ Update our LastSeqID value if we can. Anything less than LastSeqID
+ we'll know is a resend.
+ ..................................................................*/
+ if (packet->PacketID == (LastSeqID + 1)) {
+ LastSeqID = packet->PacketID;
+ /*...............................................................
+ Now that we have a new 'LastSeqID', search our Queue to see if
+ the next ID is there; if so, keep checking for the next one;
+ break only when the next one isn't found. This forces
+ LastSeqID to be the largest possible value.
+ ...............................................................*/
+ do {
+ found = 0;
+ for (i = 0; i < Queue->Num_Receive(); i++) {
+
+ rec_entry = Queue->Get_Receive(i);
+
+ if (rec_entry) {
+ entry_data = (CommHeaderType *)rec_entry->Buffer;
+
+ /*......................................................
+ Entry is found
+ ......................................................*/
+ if (entry_data->Code == PACKET_DATA_ACK &&
+ entry_data->PacketID == (LastSeqID + 1)) {
+
+ LastSeqID = entry_data->PacketID;
+ found = 1;
+ break;
+ }
+ }
+ }
+ } while (found);
+ }
+ } /* end of save packet */
+
+ /*.....................................................................
+ Send an ACK, regardless of whether this was a resend or not.
+ .....................................................................*/
+ ackpacket.MagicNumber = Magic_Num();
+ ackpacket.Code = PACKET_ACK;
+ ackpacket.PacketID = packet->PacketID;
+ Send ((char *)&ackpacket, sizeof(CommHeaderType), NULL, 0);
+
+ return(1);
+ }
+
+ return(0);
+
+} /* end of Receive_Packet */
+
+
+/***************************************************************************
+ * ConnectionClass::Get_Packet -- gets a packet from receive queue *
+ * *
+ * INPUT: *
+ * buf location to store buffer *
+ * buflen filled in with length of 'buf' *
+ * *
+ * OUTPUT: *
+ * 1 = packet was read, 0 = wasn't *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/20/1994 BR : Created. *
+ *=========================================================================*/
+int ConnectionClass::Get_Packet (void * buf, int *buflen)
+{
+ ReceiveQueueType *rec_entry; // ptr to receive entry header
+ int packetlen; // size of received packet
+ CommHeaderType *entry_data;
+ int i;
+
+ /*------------------------------------------------------------------------
+ Ensure that we read the packets in order. LastReadID is the ID of the
+ last PACKET_DATA_ACK packet we read.
+ ------------------------------------------------------------------------*/
+ for (i = 0; i < Queue->Num_Receive(); i++) {
+
+ rec_entry = Queue->Get_Receive(i);
+
+ /*.....................................................................
+ Only read this entry if it hasn't been yet
+ .....................................................................*/
+ if (rec_entry && rec_entry->IsRead==0) {
+
+ entry_data = (CommHeaderType *)rec_entry->Buffer;
+
+ /*..................................................................
+ If this is a DATA_ACK packet, its ID must be one greater than
+ the last one we read.
+ ..................................................................*/
+ if ( (entry_data->Code == PACKET_DATA_ACK) &&
+ (entry_data->PacketID == (LastReadID + 1))) {
+
+ LastReadID = entry_data->PacketID;
+ rec_entry->IsRead = 1;
+
+ packetlen = rec_entry->BufLen - sizeof(CommHeaderType);
+ if (packetlen > 0) {
+ memcpy(buf, rec_entry->Buffer + sizeof(CommHeaderType),
+ packetlen);
+ }
+ (*buflen) = packetlen;
+ return(1);
+ }
+ /*..................................................................
+ If this is a DATA_NOACK packet, who cares what the ID is?
+ ..................................................................*/
+ else if (entry_data->Code == PACKET_DATA_NOACK) {
+ rec_entry->IsRead = 1;
+
+ packetlen = rec_entry->BufLen - sizeof(CommHeaderType);
+ if (packetlen > 0) {
+ memcpy(buf, rec_entry->Buffer + sizeof(CommHeaderType),
+ packetlen);
+ }
+ (*buflen) = packetlen;
+ return(1);
+ }
+ }
+ }
+
+ return(0);
+
+} /* end of Get_Packet */
+
+
+/***************************************************************************
+ * ConnectionClass::Service -- main polling routine; services packets *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * 1 = OK, 0 = error (connection is broken!) *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/20/1994 BR : Created. *
+ *=========================================================================*/
+int ConnectionClass::Service (void)
+{
+ /*------------------------------------------------------------------------
+ Service the Send Queue: This [re]sends packets in the Send Queue which
+ haven't been ACK'd yet, and if their retry timeout has expired, and
+ updates the FirstTime, LastTime & SendCount values in the Queue entry.
+ Entries that have been ACK'd should be removed.
+
+ Service the Receive Queue: This sends ACKs for packets that haven't
+ been ACK'd yet. Entries that the app has read, and have been ACK'd,
+ should be removed.
+ ------------------------------------------------------------------------*/
+ if ( Service_Send_Queue() && Service_Receive_Queue() ) {
+ return(1);
+ }
+ else {
+ return(0);
+ }
+
+} /* end of Service */
+
+
+/***************************************************************************
+ * ConnectionClass::Service_Send_Queue -- services the send queue *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * 1 = OK, 0 = error *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/20/1994 BR : Created. *
+ *=========================================================================*/
+int ConnectionClass::Service_Send_Queue (void)
+{
+ int i;
+ int num_entries;
+ SendQueueType *send_entry; // ptr to send queue entry
+ CommHeaderType *packet_hdr; // packet header
+ unsigned long curtime; // current time
+ int bad_conn = 0;
+
+ /*------------------------------------------------------------------------
+ Remove any ACK'd packets from the queue
+ ------------------------------------------------------------------------*/
+ for (i = 0; i < Queue->Num_Send(); i++) {
+ /*.....................................................................
+ Get this queue entry
+ .....................................................................*/
+ send_entry = Queue->Get_Send(i);
+
+ /*.....................................................................
+ If ACK has been received, unqueue it
+ .....................................................................*/
+ if (send_entry->IsACK) {
+
+ /*..................................................................
+ Update this queue's response time
+ ..................................................................*/
+ packet_hdr = (CommHeaderType *)send_entry->Buffer;
+ if (packet_hdr->Code == PACKET_DATA_ACK) {
+ Queue->Add_Delay(Time() - send_entry->FirstTime);
+ }
+
+ /*..................................................................
+ Unqueue the packet
+ ..................................................................*/
+ Queue->UnQueue_Send(NULL,NULL,i,NULL,NULL);
+ i--;
+ }
+ }
+
+ /*------------------------------------------------------------------------
+ Loop through all entries in the Send queue. [Re]Send any entries that
+ need it.
+ ------------------------------------------------------------------------*/
+ num_entries = Queue->Num_Send();
+
+ for (i = 0; i < num_entries; i++) {
+ send_entry = Queue->Get_Send(i);
+
+ if (send_entry->IsACK) {
+ continue;
+ }
+
+ /*.....................................................................
+ Only send the message if time has elapsed. (The message's Time
+ fields are init'd to 0 when a message is queue'd or unqueue'd, so the
+ first time through, the delta time will appear large.)
+ .....................................................................*/
+ curtime = Time();
+ if (curtime - send_entry->LastTime > RetryDelta) {
+
+ /*..................................................................
+ Send the message
+ ..................................................................*/
+ Send (send_entry->Buffer, send_entry->BufLen, send_entry->ExtraBuffer,
+ send_entry->ExtraLen);
+
+ /*..................................................................
+ Fill in Time fields
+ ..................................................................*/
+ send_entry->LastTime = curtime;
+ if (send_entry->SendCount==0) {
+ send_entry->FirstTime = curtime;
+
+ /*...............................................................
+ If this is the 1st time we're sending this packet, and it doesn't
+ require an ACK, mark it as ACK'd; then, the next time through,
+ it will just be removed from the queue.
+ ...............................................................*/
+ packet_hdr = (CommHeaderType *)send_entry->Buffer;
+ if (packet_hdr->Code == PACKET_DATA_NOACK) {
+ send_entry->IsACK = 1;
+ }
+ }
+
+ /*..................................................................
+ Update SendCount
+ ..................................................................*/
+ send_entry->SendCount++;
+
+ /*..................................................................
+ Perform error detection, based on either MaxRetries or Timeout
+ ..................................................................*/
+ if (MaxRetries != -1 && send_entry->SendCount > MaxRetries) {
+ bad_conn = 1;
+ }
+
+ if (Timeout != -1 &&
+ (send_entry->LastTime - send_entry->FirstTime) > Timeout) {
+ bad_conn = 1;
+ }
+ }
+ }
+
+ /*------------------------------------------------------------------------
+ If the connection is going bad, return an error
+ ------------------------------------------------------------------------*/
+ if (bad_conn) {
+ return(0);
+ }
+ else {
+ return(1);
+ }
+
+} /* end of Service_Send_Queue */
+
+
+/***************************************************************************
+ * ConnectionClass::Service_Receive_Queue -- services receive queue *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * 1 = OK, 0 = error *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/20/1994 BR : Created. *
+ *=========================================================================*/
+int ConnectionClass::Service_Receive_Queue (void)
+{
+ ReceiveQueueType *rec_entry; // ptr to receive entry header
+ CommHeaderType *packet_hdr; // packet header
+ int i;
+
+ /*------------------------------------------------------------------------
+ Remove all dead packets.
+ PACKET_DATA_NOACK: if it's been read, throw it away.
+ PACKET_DATA_ACK: if it's been read, and its ID is older than LastSeqID,
+ throw it away.
+ ------------------------------------------------------------------------*/
+ for (i = 0; i < Queue->Num_Receive(); i++) {
+ rec_entry = Queue->Get_Receive(i);
+
+ if (rec_entry->IsRead) {
+ packet_hdr = (CommHeaderType *)(rec_entry->Buffer);
+
+ if (packet_hdr->Code == PACKET_DATA_NOACK) {
+ Queue->UnQueue_Receive(NULL,NULL,i,NULL,NULL);
+ i--;
+
+ }
+ else if (packet_hdr->PacketID < LastSeqID) {
+ Queue->UnQueue_Receive(NULL,NULL,i,NULL,NULL);
+ i--;
+ }
+ }
+ }
+
+ return(1);
+
+} /* end of Service_Receive_Queue */
+
+
+/***************************************************************************
+ * ConnectionClass::Time -- gets current time *
+ * *
+ * INPUT: *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/20/1994 BR : Created. *
+ *=========================================================================*/
+unsigned long ConnectionClass::Time (void)
+{
+ static struct timeb mytime; // DOS time
+ unsigned long msec;
+
+#ifdef WWLIB32_H
+
+ /*------------------------------------------------------------------------
+ If the Westwood timer system has been activated, use TickCount's value
+ ------------------------------------------------------------------------*/
+ if (TimerSystemOn) {
+ return(TickCount); // Westwood Library time
+ }
+ /*------------------------------------------------------------------------
+ Otherwise, use the DOS timer
+ ------------------------------------------------------------------------*/
+ else {
+ ftime(&mytime);
+ msec = (unsigned long)mytime.time * 1000L + (unsigned long)mytime.millitm;
+ return((msec / 100) * 6);
+ }
+
+#else
+
+ /*------------------------------------------------------------------------
+ If the Westwood library isn't being used, use the DOS timer.
+ ------------------------------------------------------------------------*/
+ ftime(&mytime);
+ msec = (unsigned long)mytime.time * 1000L + (unsigned long)mytime.millitm;
+ return((msec / 100) * 6);
+
+#endif
+
+} /* end of Time */
+
+
+/***************************************************************************
+ * ConnectionClass::Command_Name -- returns name for given packet command *
+ * *
+ * INPUT: *
+ * command packet Command value to get name for *
+ * *
+ * OUTPUT: *
+ * ptr to command name, NULL if invalid *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 05/31/1995 BRR : Created. *
+ *=========================================================================*/
+char *ConnectionClass::Command_Name(int command)
+{
+ if (command >= 0 && command < PACKET_COUNT) {
+ return(Commands[command]);
+ }
+ else {
+ return(NULL);
+ }
+
+} /* end of Command_Name */
+
+/************************** end of connect.cpp *****************************/
+
+
diff --git a/CODE/CONNECT.CPP.BAK b/CODE/CONNECT.CPP.BAK
new file mode 100644
index 0000000..c0dd50a
--- /dev/null
+++ b/CODE/CONNECT.CPP.BAK
@@ -0,0 +1,833 @@
+/*
+** Command & Conquer Red Alert(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&c0\vcs\code\connect.cpv 4.76 03 Oct 1996 09:20: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 : CONNECT.CPP *
+ * *
+ * Programmer : Bill Randolph *
+ * *
+ * Start Date : December 20, 1994 *
+ * *
+ * Last Update : May 31, 1995 [BRR] *
+ *-------------------------------------------------------------------------*
+ * Functions: *
+ * ConnectionClass::ConnectionClass -- class constructor *
+ * ConnectionClass::~ConnectionClass -- class destructor *
+ * ConnectionClass::Init -- Initializes connection queue to empty *
+ * ConnectionClass::Send_Packet -- adds a packet to the send queue *
+ * ConnectionClass::Receive_Packet -- adds packet to receive queue *
+ * ConnectionClass::Get_Packet -- gets a packet from receive queue *
+ * ConnectionClass::Service -- main polling routine; services packets *
+ * ConnectionClass::Service_Send_Queue -- services the send queue *
+ * ConnectionClass::Service_Receive_Queue -- services receive queue *
+ * ConnectionClass::Time -- gets current time *
+ * ConnectionClass::Command_Name -- returns name for a packet command *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#include "function.h"
+#include
+#include
+#include
+#include "connect.h"
+
+/*
+********************************* Globals ***********************************
+*/
+static char *ConnectionClass::Commands[PACKET_COUNT] = {
+ "ADATA",
+ "NDATA",
+ "ACK"
+};
+
+
+/***************************************************************************
+ * ConnectionClass::ConnectionClass -- class constructor *
+ * *
+ * INPUT: *
+ * numsend desired # of entries for the send queue *
+ * numreceive desired # of entries for the receive queue *
+ * maxlen max length of an application packet *
+ * magicnum the packet "magic number" for this connection *
+ * retry_delta the time to wait between sends *
+ * max_retries the max # of retries allowed for a packet *
+ * (-1 means retry forever, based on this parameter) *
+ * timeout the max amount of time before we give up on a packet *
+ * (-1 means retry forever, based on this parameter) *
+ * extralen max size of app-specific extra bytes (optional) *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/20/1994 BR : Created. *
+ *=========================================================================*/
+ConnectionClass::ConnectionClass (int numsend, int numreceive,
+ int maxlen, unsigned short magicnum, unsigned long retry_delta,
+ unsigned long max_retries, unsigned long timeout, int extralen)
+{
+ /*------------------------------------------------------------------------
+ Compute our maximum packet length
+ ------------------------------------------------------------------------*/
+ MaxPacketLen = maxlen + sizeof(CommHeaderType);
+
+ /*------------------------------------------------------------------------
+ Assign the magic number
+ ------------------------------------------------------------------------*/
+ MagicNum = magicnum;
+
+ /*------------------------------------------------------------------------
+ Initialize the retry time. This is the time that t2 - t1 must be greater
+ than before a retry will occur.
+ ------------------------------------------------------------------------*/
+ RetryDelta = retry_delta;
+
+ /*------------------------------------------------------------------------
+ Set the maximum allowable retries.
+ ------------------------------------------------------------------------*/
+ MaxRetries = max_retries;
+
+ /*------------------------------------------------------------------------
+ Set the timeout for this connection.
+ ------------------------------------------------------------------------*/
+ Timeout = timeout;
+
+ /*------------------------------------------------------------------------
+ Allocate the packet staging buffer. This will be used to
+ ------------------------------------------------------------------------*/
+ PacketBuf = new char[ MaxPacketLen ];
+
+ /*------------------------------------------------------------------------
+ Allocate the packet Queue. This will store incoming packets (placed there
+ by Receive_Packet), and outgoing packets (placed there by Send_Packet).
+ It can optionally store "extra" bytes, which are stored along with each
+ packet, but aren't transmitted as part of the packet. If 'extralen'
+ is 0, the CommBufferClass ignores this parameter.
+ ------------------------------------------------------------------------*/
+ Queue = new CommBufferClass (numsend, numreceive, MaxPacketLen, extralen);
+
+} /* end of ConnectionClass */
+
+
+/***************************************************************************
+ * ConnectionClass::~ConnectionClass -- class destructor *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/20/1994 BR : Created. *
+ *=========================================================================*/
+ConnectionClass::~ConnectionClass ()
+{
+ /*------------------------------------------------------------------------
+ Free memory.
+ ------------------------------------------------------------------------*/
+ delete [] PacketBuf;
+ delete Queue;
+
+} /* end of ~ConnectionClass */
+
+
+/***************************************************************************
+ * ConnectionClass::Init -- Initializes connection queue to empty *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/20/1994 BR : Created. *
+ *=========================================================================*/
+void ConnectionClass::Init (void)
+{
+ NumRecNoAck = 0;
+ NumRecAck = 0;
+ NumSendNoAck = 0;
+ NumSendAck = 0;
+
+ LastSeqID = 0xffffffff;
+ LastReadID = 0xffffffff;
+
+ Queue->Init();
+
+} /* end of Init */
+
+
+/***************************************************************************
+ * ConnectionClass::Send_Packet -- adds a packet to the send queue *
+ * *
+ * This routine prefixes the given buffer with a CommHeaderType and *
+ * queues the resulting packet into the Send Queue. (It's actually the *
+ * Service() routine that handles the hardware-dependent Send of the data).*
+ * The packet's MagicNumber, Code, and PacketID are set here. *
+ * *
+ * INPUT: *
+ * buf buffer to send *
+ * buflen length of buffer *
+ * ack_req 1 = ACK is required for this packet; 0 = isn't *
+ * *
+ * OUTPUT: *
+ * 1 = packet was queue'd OK, 0 = wasn't *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/20/1994 BR : Created. *
+ *=========================================================================*/
+int ConnectionClass::Send_Packet (void * buf, int buflen, int ack_req)
+{
+ /*------------------------------------------------------------------------
+ Set the magic # for the packet
+ ------------------------------------------------------------------------*/
+ ((CommHeaderType *)PacketBuf)->MagicNumber = MagicNum;
+
+ /*------------------------------------------------------------------------
+ Set the packet Code: DATA_ACK if it requires an ACK, NOACK if it doesn't
+ Set the packet ID to the appropriate counter value.
+ ------------------------------------------------------------------------*/
+ if (ack_req) {
+ ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_ACK;
+ ((CommHeaderType *)PacketBuf)->PacketID = NumSendAck;
+ }
+ else {
+ ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_NOACK;
+ ((CommHeaderType *)PacketBuf)->PacketID = NumSendNoAck;
+ }
+
+ /*------------------------------------------------------------------------
+ Now build the packet
+ ------------------------------------------------------------------------*/
+ memcpy(PacketBuf + sizeof(CommHeaderType), buf, buflen);
+
+ /*------------------------------------------------------------------------
+ Add it to the queue; don't add any extra data with it.
+ ------------------------------------------------------------------------*/
+ if (Queue->Queue_Send(PacketBuf,buflen + sizeof(CommHeaderType), NULL, 0)) {
+ if (ack_req) {
+ NumSendAck++;
+ }
+ else {
+ NumSendNoAck++;
+ }
+ return(1);
+ }
+ else {
+ return(0);
+ }
+
+} /* end of Send_Packet */
+
+
+/***************************************************************************
+ * ConnectionClass::Receive_Packet -- adds packet to receive queue *
+ * *
+ * INPUT: *
+ * buf buffer to process (already includes CommHeaderType) *
+ * buflen length of buffer to process *
+ * *
+ * OUTPUT: *
+ * 1 = packet was processed OK, 0 = error *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/20/1994 BR : Created. *
+ *=========================================================================*/
+int ConnectionClass::Receive_Packet (void * buf, int buflen)
+{
+ CommHeaderType *packet; // ptr to packet header
+ SendQueueType *send_entry; // ptr to send entry header
+ ReceiveQueueType *rec_entry; // ptr to recv entry header
+ CommHeaderType *entry_data; // ptr to queue entry data
+ CommHeaderType ackpacket; // ACK packet to send
+ int i;
+ int save_packet = 1; // 0 = this is a resend
+ int found;
+
+ /*------------------------------------------------------------------------
+ Check the magic #
+ ------------------------------------------------------------------------*/
+ packet = (CommHeaderType *)buf;
+ if (packet->MagicNumber != MagicNum) {
+ return(0);
+ }
+
+ /*------------------------------------------------------------------------
+ Handle an incoming ACK
+ ------------------------------------------------------------------------*/
+ if (packet->Code == PACKET_ACK) {
+
+ for (i = 0; i < Queue->Num_Send(); i++) {
+ /*..................................................................
+ Get queue entry ptr
+ ..................................................................*/
+ send_entry = Queue->Get_Send(i);
+
+ /*..................................................................
+ If ptr is valid, get ptr to its data
+ ..................................................................*/
+ if (send_entry != NULL) {
+ entry_data = (CommHeaderType *)send_entry->Buffer;
+
+ /*...............................................................
+ If ACK is for this entry, mark it
+ ...............................................................*/
+ if (packet->PacketID==entry_data->PacketID &&
+ entry_data->Code == PACKET_DATA_ACK) {
+ send_entry->IsACK = 1;
+ break;
+ }
+ }
+ }
+
+ return(1);
+ }
+
+ /*------------------------------------------------------------------------
+ Handle an incoming PACKET_DATA_NOACK packet
+ ------------------------------------------------------------------------*/
+ else if (packet->Code == PACKET_DATA_NOACK) {
+ /*.....................................................................
+ If there's only one slot left, don't tie up the queue with this packet
+ .....................................................................*/
+ if (Queue->Max_Receive() - Queue->Num_Receive() <= 1) {
+ return(0);
+ }
+
+ /*.....................................................................
+ Error if we can't queue the packet
+ .....................................................................*/
+ if (!Queue->Queue_Receive (buf, buflen, NULL, 0)) {
+ return(0);
+ }
+
+ NumRecNoAck++;
+
+ return(1);
+ }
+
+ /*------------------------------------------------------------------------
+ Handle an incoming PACKET_DATA_ACK packet
+ ------------------------------------------------------------------------*/
+ else if (packet->Code == PACKET_DATA_ACK) {
+ /*.....................................................................
+ If this is a packet requires an ACK, and it's ID is older than our
+ "oldest" ID, we know it's a resend; send an ACK, but don't queue it
+ .....................................................................*/
+ if (packet->PacketID <= LastSeqID && LastSeqID != 0xffffffff) {
+ save_packet = 0;
+ }
+
+ /*.....................................................................
+ Otherwise, scan the queue for this entry; if it's found, it's a
+ resend, so don't save it.
+ .....................................................................*/
+ else {
+ save_packet = 1;
+ for (i = 0; i < Queue->Num_Receive(); i++) {
+ rec_entry = Queue->Get_Receive(i);
+
+ if (rec_entry) {
+
+ entry_data = (CommHeaderType *)rec_entry->Buffer;
+
+ /*...........................................................
+ Packet is found; it's a resend
+ ...........................................................*/
+ if (entry_data->Code == PACKET_DATA_ACK &&
+ entry_data->PacketID == packet->PacketID) {
+ save_packet = 0;
+ break;
+ }
+ }
+ }
+ } /* end of scan for resend */
+
+ /*.....................................................................
+ Queue the packet & update our LastSeqID value.
+ .....................................................................*/
+ if (save_packet) {
+ /*..................................................................
+ If there's only one slot left, make sure we only put a packet in it
+ if this packet will let us increment our LastSeqID; otherwise, we'll
+ get stuck, forever unable to increment LastSeqID.
+ ..................................................................*/
+ if (Queue->Max_Receive() - Queue->Num_Receive() <= 1) {
+ if (packet->PacketID != (LastSeqID + 1) ) {
+ return(0);
+ }
+ }
+
+ /*..................................................................
+ If we can't queue the packet, return; don't send an ACK.
+ ..................................................................*/
+ if (!Queue->Queue_Receive (buf, buflen, NULL, 0)) {
+ return(0);
+ }
+
+ NumRecAck++;
+
+ /*..................................................................
+ Update our LastSeqID value if we can. Anything less than LastSeqID
+ we'll know is a resend.
+ ..................................................................*/
+ if (packet->PacketID == (LastSeqID + 1)) {
+ LastSeqID = packet->PacketID;
+ /*...............................................................
+ Now that we have a new 'LastSeqID', search our Queue to see if
+ the next ID is there; if so, keep checking for the next one;
+ break only when the next one isn't found. This forces
+ LastSeqID to be the largest possible value.
+ ...............................................................*/
+ do {
+ found = 0;
+ for (i = 0; i < Queue->Num_Receive(); i++) {
+
+ rec_entry = Queue->Get_Receive(i);
+
+ if (rec_entry) {
+ entry_data = (CommHeaderType *)rec_entry->Buffer;
+
+ /*......................................................
+ Entry is found
+ ......................................................*/
+ if (entry_data->Code == PACKET_DATA_ACK &&
+ entry_data->PacketID == (LastSeqID + 1)) {
+
+ LastSeqID = entry_data->PacketID;
+ found = 1;
+ break;
+ }
+ }
+ }
+ } while (found);
+ }
+ } /* end of save packet */
+
+ /*.....................................................................
+ Send an ACK, regardless of whether this was a resend or not.
+ .....................................................................*/
+ ackpacket.MagicNumber = Magic_Num();
+ ackpacket.Code = PACKET_ACK;
+ ackpacket.PacketID = packet->PacketID;
+ Send ((char *)&ackpacket, sizeof(CommHeaderType), NULL, 0);
+
+ return(1);
+ }
+
+ return(0);
+
+} /* end of Receive_Packet */
+
+
+/***************************************************************************
+ * ConnectionClass::Get_Packet -- gets a packet from receive queue *
+ * *
+ * INPUT: *
+ * buf location to store buffer *
+ * buflen filled in with length of 'buf' *
+ * *
+ * OUTPUT: *
+ * 1 = packet was read, 0 = wasn't *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/20/1994 BR : Created. *
+ *=========================================================================*/
+int ConnectionClass::Get_Packet (void * buf, int *buflen)
+{
+ ReceiveQueueType *rec_entry; // ptr to receive entry header
+ int packetlen; // size of received packet
+ CommHeaderType *entry_data;
+ int i;
+
+ /*------------------------------------------------------------------------
+ Ensure that we read the packets in order. LastReadID is the ID of the
+ last PACKET_DATA_ACK packet we read.
+ ------------------------------------------------------------------------*/
+ for (i = 0; i < Queue->Num_Receive(); i++) {
+
+ rec_entry = Queue->Get_Receive(i);
+
+ /*.....................................................................
+ Only read this entry if it hasn't been yet
+ .....................................................................*/
+ if (rec_entry && rec_entry->IsRead==0) {
+
+ entry_data = (CommHeaderType *)rec_entry->Buffer;
+
+ /*..................................................................
+ If this is a DATA_ACK packet, its ID must be one greater than
+ the last one we read.
+ ..................................................................*/
+ if ( (entry_data->Code == PACKET_DATA_ACK) &&
+ (entry_data->PacketID == (LastReadID + 1))) {
+
+ LastReadID = entry_data->PacketID;
+ rec_entry->IsRead = 1;
+
+ packetlen = rec_entry->BufLen - sizeof(CommHeaderType);
+ if (packetlen > 0) {
+ memcpy(buf, rec_entry->Buffer + sizeof(CommHeaderType),
+ packetlen);
+ }
+ (*buflen) = packetlen;
+ return(1);
+ }
+ /*..................................................................
+ If this is a DATA_NOACK packet, who cares what the ID is?
+ ..................................................................*/
+ else if (entry_data->Code == PACKET_DATA_NOACK) {
+ rec_entry->IsRead = 1;
+
+ packetlen = rec_entry->BufLen - sizeof(CommHeaderType);
+ if (packetlen > 0) {
+ memcpy(buf, rec_entry->Buffer + sizeof(CommHeaderType),
+ packetlen);
+ }
+ (*buflen) = packetlen;
+ return(1);
+ }
+ }
+ }
+
+ return(0);
+
+} /* end of Get_Packet */
+
+
+/***************************************************************************
+ * ConnectionClass::Service -- main polling routine; services packets *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * 1 = OK, 0 = error (connection is broken!) *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/20/1994 BR : Created. *
+ *=========================================================================*/
+int ConnectionClass::Service (void)
+{
+ /*------------------------------------------------------------------------
+ Service the Send Queue: This [re]sends packets in the Send Queue which
+ haven't been ACK'd yet, and if their retry timeout has expired, and
+ updates the FirstTime, LastTime & SendCount values in the Queue entry.
+ Entries that have been ACK'd should be removed.
+
+ Service the Receive Queue: This sends ACKs for packets that haven't
+ been ACK'd yet. Entries that the app has read, and have been ACK'd,
+ should be removed.
+ ------------------------------------------------------------------------*/
+ if ( Service_Send_Queue() && Service_Receive_Queue() ) {
+ return(1);
+ }
+ else {
+ return(0);
+ }
+
+} /* end of Service */
+
+
+/***************************************************************************
+ * ConnectionClass::Service_Send_Queue -- services the send queue *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * 1 = OK, 0 = error *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/20/1994 BR : Created. *
+ *=========================================================================*/
+int ConnectionClass::Service_Send_Queue (void)
+{
+ int i;
+ int num_entries;
+ SendQueueType *send_entry; // ptr to send queue entry
+ CommHeaderType *packet_hdr; // packet header
+ unsigned long curtime; // current time
+ int bad_conn = 0;
+
+ /*------------------------------------------------------------------------
+ Remove any ACK'd packets from the queue
+ ------------------------------------------------------------------------*/
+ for (i = 0; i < Queue->Num_Send(); i++) {
+ /*.....................................................................
+ Get this queue entry
+ .....................................................................*/
+ send_entry = Queue->Get_Send(i);
+
+ /*.....................................................................
+ If ACK has been received, unqueue it
+ .....................................................................*/
+ if (send_entry->IsACK) {
+
+ /*..................................................................
+ Update this queue's response time
+ ..................................................................*/
+ packet_hdr = (CommHeaderType *)send_entry->Buffer;
+ if (packet_hdr->Code == PACKET_DATA_ACK) {
+ Queue->Add_Delay(Time() - send_entry->FirstTime);
+ }
+
+ /*..................................................................
+ Unqueue the packet
+ ..................................................................*/
+ Queue->UnQueue_Send(NULL,NULL,i,NULL,NULL);
+ i--;
+ }
+ }
+
+ /*------------------------------------------------------------------------
+ Loop through all entries in the Send queue. [Re]Send any entries that
+ need it.
+ ------------------------------------------------------------------------*/
+ num_entries = Queue->Num_Send();
+
+ for (i = 0; i < num_entries; i++) {
+ send_entry = Queue->Get_Send(i);
+
+ if (send_entry->IsACK) {
+ continue;
+ }
+
+ /*.....................................................................
+ Only send the message if time has elapsed. (The message's Time
+ fields are init'd to 0 when a message is queue'd or unqueue'd, so the
+ first time through, the delta time will appear large.)
+ .....................................................................*/
+ curtime = Time();
+ if (curtime - send_entry->LastTime > RetryDelta) {
+
+ /*..................................................................
+ Send the message
+ ..................................................................*/
+ Send (send_entry->Buffer, send_entry->BufLen, send_entry->ExtraBuffer,
+ send_entry->ExtraLen);
+
+ /*..................................................................
+ Fill in Time fields
+ ..................................................................*/
+ send_entry->LastTime = curtime;
+ if (send_entry->SendCount==0) {
+ send_entry->FirstTime = curtime;
+
+ /*...............................................................
+ If this is the 1st time we're sending this packet, and it doesn't
+ require an ACK, mark it as ACK'd; then, the next time through,
+ it will just be removed from the queue.
+ ...............................................................*/
+ packet_hdr = (CommHeaderType *)send_entry->Buffer;
+ if (packet_hdr->Code == PACKET_DATA_NOACK) {
+ send_entry->IsACK = 1;
+ }
+ }
+
+ /*..................................................................
+ Update SendCount
+ ..................................................................*/
+ send_entry->SendCount++;
+
+ /*..................................................................
+ Perform error detection, based on either MaxRetries or Timeout
+ ..................................................................*/
+ if (MaxRetries != -1 && send_entry->SendCount > MaxRetries) {
+ bad_conn = 1;
+ }
+
+ if (Timeout != -1 &&
+ (send_entry->LastTime - send_entry->FirstTime) > Timeout) {
+ bad_conn = 1;
+ }
+ }
+ }
+
+ /*------------------------------------------------------------------------
+ If the connection is going bad, return an error
+ ------------------------------------------------------------------------*/
+ if (bad_conn) {
+ return(0);
+ }
+ else {
+ return(1);
+ }
+
+} /* end of Service_Send_Queue */
+
+
+/***************************************************************************
+ * ConnectionClass::Service_Receive_Queue -- services receive queue *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * 1 = OK, 0 = error *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/20/1994 BR : Created. *
+ *=========================================================================*/
+int ConnectionClass::Service_Receive_Queue (void)
+{
+ ReceiveQueueType *rec_entry; // ptr to receive entry header
+ CommHeaderType *packet_hdr; // packet header
+ int i;
+
+ /*------------------------------------------------------------------------
+ Remove all dead packets.
+ PACKET_DATA_NOACK: if it's been read, throw it away.
+ PACKET_DATA_ACK: if it's been read, and its ID is older than LastSeqID,
+ throw it away.
+ ------------------------------------------------------------------------*/
+ for (i = 0; i < Queue->Num_Receive(); i++) {
+ rec_entry = Queue->Get_Receive(i);
+
+ if (rec_entry->IsRead) {
+ packet_hdr = (CommHeaderType *)(rec_entry->Buffer);
+
+ if (packet_hdr->Code == PACKET_DATA_NOACK) {
+ Queue->UnQueue_Receive(NULL,NULL,i,NULL,NULL);
+ i--;
+
+ }
+ else if (packet_hdr->PacketID < LastSeqID) {
+ Queue->UnQueue_Receive(NULL,NULL,i,NULL,NULL);
+ i--;
+ }
+ }
+ }
+
+ return(1);
+
+} /* end of Service_Receive_Queue */
+
+
+/***************************************************************************
+ * ConnectionClass::Time -- gets current time *
+ * *
+ * INPUT: *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 12/20/1994 BR : Created. *
+ *=========================================================================*/
+unsigned long ConnectionClass::Time (void)
+{
+ static struct timeb mytime; // DOS time
+ unsigned long msec;
+
+#ifdef WWLIB32_H
+
+ /*------------------------------------------------------------------------
+ If the Westwood timer system has been activated, use TickCount's value
+ ------------------------------------------------------------------------*/
+ if (TimerSystemOn) {
+ return(TickCount); // Westwood Library time
+ }
+ /*------------------------------------------------------------------------
+ Otherwise, use the DOS timer
+ ------------------------------------------------------------------------*/
+ else {
+ ftime(&mytime);
+ msec = (unsigned long)mytime.time * 1000L + (unsigned long)mytime.millitm;
+ return((msec / 100) * 6);
+ }
+
+#else
+
+ /*------------------------------------------------------------------------
+ If the Westwood library isn't being used, use the DOS timer.
+ ------------------------------------------------------------------------*/
+ ftime(&mytime);
+ msec = (unsigned long)mytime.time * 1000L + (unsigned long)mytime.millitm;
+ return((msec / 100) * 6);
+
+#endif
+
+} /* end of Time */
+
+
+/***************************************************************************
+ * ConnectionClass::Command_Name -- returns name for given packet command *
+ * *
+ * INPUT: *
+ * command packet Command value to get name for *
+ * *
+ * OUTPUT: *
+ * ptr to command name, NULL if invalid *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 05/31/1995 BRR : Created. *
+ *=========================================================================*/
+char *ConnectionClass::Command_Name(int command)
+{
+ if (command >= 0 && command < PACKET_COUNT) {
+ return(Commands[command]);
+ }
+ else {
+ return(NULL);
+ }
+
+} /* end of Command_Name */
+
+/************************** end of connect.cpp *****************************/
diff --git a/CODE/CONNECT.H b/CODE/CONNECT.H
new file mode 100644
index 0000000..b162f8a
--- /dev/null
+++ b/CODE/CONNECT.H
@@ -0,0 +1,281 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CONNECT.H 1 3/03/97 10:24a Joe_bostic $ */
+/***************************************************************************
+ ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S **
+ ***************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CONNECT.H *
+ * *
+ * Programmer : Bill Randolph *
+ * *
+ * Start Date : December 19, 1994 *
+ * *
+ * Last Update : April 1, 1995 [BR] *
+ * *
+ *-------------------------------------------------------------------------*
+ * *
+ * DESCRIPTION: *
+ * This class represents a single "connection" with another system. It's *
+ * a pure virtual base class that acts as a framework for other classes. *
+ * *
+ * This class contains a CommBufferClass member, which stores received *
+ * & transmitted packets. The ConnectionClass has virtual functions to *
+ * handle adding packets to the queue, reading them from the queue, *
+ * a Send routine for actually sending data, and a Receive_Packet function *
+ * which is used to tell the connection that a new packet has come in. *
+ * *
+ * The virtual Service routines handle all ACK & Retry logic for *
+ * communicating between this system & another. Thus, any class derived *
+ * from this class may overload the basic ACK/Retry logic. *
+ * *
+ * THE PACKET HEADER: *
+ * The Connection Classes prefix every packet sent with a header that's *
+ * local to this class. The header contains a "Magic Number" which should *
+ * be unique for each product, and Packet "Code", which will tell the *
+ * receiving end if this is DATA, or an ACK packet, and a packet ID, which *
+ * is a unique numerical ID for this packet (useful for detecting resends).*
+ * The header is stored with each packet in the send & receive Queues; *
+ * it's removed before it's passed back to the application, via *
+ * Get_Packet() *
+ * *
+ * THE CONNECTION MANAGER: *
+ * It is assumed that there will be a "Connection Manager" class which *
+ * will handle parsing incoming packets; it will then tell the connection *
+ * that new packets have come in, and the connection will process them in *
+ * whatever way it needs to for its protocol (check for resends, handle *
+ * ACK packets, etc). The job of the connection manager is to parse *
+ * incoming packets & distribute them to the connections that need to *
+ * store them (for multi-connection protocols). *
+ * *
+ * NOTES ON ACK/RETRY: *
+ * This class provides a "non-sequenced" ACK/Retry approach to packet *
+ * transmission. It sends out as many packets as are in the queue, whose *
+ * resend delta times have expired; and it ACK's any packets its received *
+ * who haven't been ACK'd yet. Thus, order of delivery is NOT guaranteed; *
+ * but, the performance is better than a "sequenced" approach. Also, the *
+ * Packet ID scheme (see below) ensures that the application will read *
+ * the packets in the proper order. Thus, this class guarantees delivery *
+ * and order of deliver. *
+ * *
+ * Each packet has a unique numerical ID; the ID is set to a count of the *
+ * number of packets sent. Different count values are provided, for both *
+ * DATA_ACK & DATA_NOACK packets. This ensures that the counter can be *
+ * used to detect resends of DATA_ACK packets; the counters for DATA_NOACK *
+ * packets aren't currently used. Other counters keep track of the *
+ * last-sequentially-received packet ID (for DATA_ACK packets), so we *
+ * can check for resends & missed packets, and the last-sequentially-read *
+ * packet ID, so we can ensure the app reads the packets in order. *
+ * *
+ * If the protocol being used already guarantees delivery of packets, *
+ * no ACK is required for the packets. In this case, the connection *
+ * class for this protocol can overload the Service routine to avoid *
+ * sending ACK packets, or the Connection Manager can just mark the *
+ * packet as ACK'd when it adds it to the Receive Queue for the connection.*
+ * *
+ * Derived classes must provide: *
+ * - Init: Initialization of any hardware-specific values. *
+ * - Send: a hardware-dependent send routine. *
+ * *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+#ifndef CONNECTION_H
+#define CONNECTION_H
+
+/*
+********************************* Includes **********************************
+*/
+#include "combuf.h"
+
+/*
+********************************** Defines **********************************
+*/
+#define CONN_DEBUG 0
+/*---------------------------------------------------------------------------
+This structure is the header prefixed to any packet sent by the application.
+MagicNumber: This is a number unique to the application; it's up to the
+ Receive_Packet routine to check this value, to be sure we're
+ not getting data from some other product. This value should
+ be unique for each application.
+Code: This will be one of the below-defined codes.
+PacketID: This is a unique numerical ID for this packet. The Connection
+ sets this ID on all packets sent out.
+---------------------------------------------------------------------------*/
+typedef struct {
+ unsigned short MagicNumber;
+ unsigned char Code;
+ unsigned long PacketID;
+} CommHeaderType;
+
+
+/*
+***************************** Class Declaration *****************************
+*/
+class ConnectionClass
+{
+ /*
+ ---------------------------- Public Interface ----------------------------
+ */
+ public:
+ /*.....................................................................
+ These are the possible values for the Code field of the CommHeaderType:
+ .....................................................................*/
+ enum ConnectionEnum {
+ PACKET_DATA_ACK, // this is a data packet requiring an ACK
+ PACKET_DATA_NOACK, // this is a data packet not requiring an ACK
+ PACKET_ACK, // this is an ACK for a packet
+ PACKET_COUNT // for computational purposes
+ };
+
+ /*.....................................................................
+ Constructor/destructor.
+ .....................................................................*/
+ ConnectionClass (int numsend, int numrecieve, int maxlen,
+ unsigned short magicnum, unsigned long retry_delta,
+ unsigned long max_retries, unsigned long timeout, int extralen = 0);
+ virtual ~ConnectionClass ();
+
+ /*.....................................................................
+ Initialization.
+ .....................................................................*/
+ virtual void Init (void);
+
+ /*.....................................................................
+ Send/Receive routines.
+ .....................................................................*/
+ virtual int Send_Packet (void * buf, int buflen, int ack_req);
+ virtual int Receive_Packet (void * buf, int buflen);
+ virtual int Get_Packet (void * buf, int * buflen);
+
+ /*.....................................................................
+ The main polling routine for the connection. Should be called as often
+ as possible.
+ .....................................................................*/
+ virtual int Service (void);
+
+ /*.....................................................................
+ This routine is used by the retry logic; returns the current time in
+ 60ths of a second.
+ .....................................................................*/
+ static unsigned long Time (void);
+
+ /*.....................................................................
+ Utility routines.
+ .....................................................................*/
+ unsigned short Magic_Num (void) { return (MagicNum); }
+ unsigned long Retry_Delta (void) { return (RetryDelta); }
+ void Set_Retry_Delta (unsigned long delta) { RetryDelta = delta;}
+ unsigned long Max_Retries (void) { return (MaxRetries); }
+ void Set_Max_Retries (unsigned long retries) { MaxRetries = retries;}
+ unsigned long Time_Out (void) { return (Timeout); }
+ void Set_TimeOut (unsigned long t) { Timeout = t;}
+ unsigned long Max_Packet_Len (void) { return (MaxPacketLen); }
+ static char * Command_Name(int command);
+
+ /*.....................................................................
+ The packet "queue"; this non-sequenced version isn't really much of
+ a queue, but more of a repository.
+ .....................................................................*/
+ CommBufferClass *Queue;
+
+ /*
+ -------------------------- Protected Interface ---------------------------
+ */
+ protected:
+ /*.....................................................................
+ Routines to service the Send & Receive queues.
+ .....................................................................*/
+ virtual int Service_Send_Queue(void);
+ virtual int Service_Receive_Queue(void);
+
+ /*.....................................................................
+ This routine actually performs a hardware-dependent data send. It's
+ pure virtual, so it must be defined by a derived class. The routine
+ is protected; it's only called by the ACK/Retry logic, not the
+ application.
+ .....................................................................*/
+ virtual int Send(char *buf, int buflen, void *extrabuf,
+ int extralen) = 0;
+
+ /*.....................................................................
+ This is the maximum packet length, including our own internal header.
+ .....................................................................*/
+ int MaxPacketLen;
+
+ /*.....................................................................
+ Packet staging area; this is where the CommHeaderType gets tacked onto
+ the application's packet before it's sent.
+ .....................................................................*/
+ char *PacketBuf;
+
+ /*.....................................................................
+ This is the magic number assigned to this connection. It is the first
+ few bytes of any transmission.
+ .....................................................................*/
+ unsigned short MagicNum;
+
+ /*.....................................................................
+ This value determines the time delay before a packet is re-sent.
+ .....................................................................*/
+ unsigned long RetryDelta;
+
+ /*.....................................................................
+ This is the maximum number of retries allowed for a packet; if this
+ value is exceeded, the connection is probably broken.
+ .....................................................................*/
+ unsigned long MaxRetries;
+
+ /*.....................................................................
+ This is the total timeout for this connection; if this time is exceeded
+ on a packet, the connection is probably broken.
+ .....................................................................*/
+ unsigned long Timeout;
+
+ /*.....................................................................
+ Running totals of # of packets we send & receive which require an ACK,
+ and those that don't.
+ .....................................................................*/
+ unsigned long NumRecNoAck;
+ unsigned long NumRecAck;
+ unsigned long NumSendNoAck;
+ unsigned long NumSendAck;
+
+ /*.....................................................................
+ This is the ID of the last consecutively-received packet; anything older
+ than this, we know is a resend. Anything newer than this MUST be lying
+ around in the Queue for us to detect it as a resend.
+ .....................................................................*/
+ unsigned long LastSeqID;
+
+ /*.....................................................................
+ This is the ID of the PACKET_DATA_ACK packet we read last; it ensures
+ that the application reads that type of packet in order.
+ .....................................................................*/
+ unsigned long LastReadID;
+
+ /*.....................................................................
+ Names of all packet commands
+ .....................................................................*/
+ static char * Commands[PACKET_COUNT];
+};
+
+#endif
+
+/**************************** end of connect.h *****************************/
diff --git a/CODE/CONNMGR.H b/CODE/CONNMGR.H
new file mode 100644
index 0000000..19e298d
--- /dev/null
+++ b/CODE/CONNMGR.H
@@ -0,0 +1,154 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CONNMGR.H 1 3/03/97 10:24a Joe_bostic $ */
+/***************************************************************************
+ ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S **
+ ***************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CONNMGR.H *
+ * *
+ * Programmer : Bill Randolph *
+ * *
+ * Start Date : December 19, 1994 *
+ * *
+ * Last Update : April 3, 1995 [BR] *
+ * *
+ *-------------------------------------------------------------------------*
+ * *
+ * This is the Connection Manager base class. This is an abstract base *
+ * class that's just a shell for more functional derived classes. *
+ * The main job of the Connection Manager classes is to parse a "pool" of *
+ * incoming packets, which may be from different computers, and distribute *
+ * those packets to Connection Classes via their Receive_Packet function. *
+ * *
+ * This class should be the only access to the network/modem for the *
+ * application, so if the app needs any functions to access the *
+ * connections or the queue's, the derived versions of this class should *
+ * provide them. *
+ * *
+ * It's up to the derived class to define: *
+ * - Service: polling routine; should Service each connection *
+ * - Init: initialization; should perform hardware-dependent *
+ * initialization, then Init each connection; this function *
+ * isn't defined in this class, since the parameters will *
+ * be highly protocol-dependent) *
+ * - Send_Message:sends a packet across the connection (this function *
+ * isn't defined in this class, since the parameters will *
+ * be highly protocol-dependent) *
+ * - Get_Message: gets a message from the connection (this function *
+ * isn't defined in this class, since the parameters will *
+ * be highly protocol-dependent) *
+ * *
+ * If the derived class supports multiple connections, it should provide *
+ * functions for creating the connections, associating them with a name *
+ * or ID or both, destroying them, and sending data through all or any *
+ * connection. *
+ * *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifndef CONNMGR_H
+#define CONNMGR_H
+
+
+/*
+***************************** Class Declaration *****************************
+*/
+class ConnManClass
+{
+ /*
+ ---------------------------- Public Interface ----------------------------
+ */
+ public:
+ /*.....................................................................
+ Various useful enums:
+ .....................................................................*/
+ enum IPXConnTag {
+ CONNECTION_NONE = -1 // value of an invalid connection ID
+ };
+
+ /*.....................................................................
+ Constructor/Destructor. These currently do nothing.
+ .....................................................................*/
+ ConnManClass (void) {};
+ virtual ~ConnManClass () {};
+
+ /*.....................................................................
+ The Service routine:
+ - Parses incoming packets, and adds them to the Receive Queue for the
+ Connection Class(s) for this protocol
+ - Invokes each connection's Service routine; returns an error if the
+ connection's Service routine indicates an error.
+ .....................................................................*/
+ virtual int Service (void) = 0;
+
+ /*.....................................................................
+ Sending & receiving data
+ .....................................................................*/
+ virtual int Send_Private_Message (void *buf, int buflen,
+ int ack_req = 1, int conn_id = CONNECTION_NONE) = 0;
+ virtual int Get_Private_Message (void *buf, int *buflen,
+ int *conn_id) = 0;
+
+ /*.....................................................................
+ Connection management
+ .....................................................................*/
+ virtual int Num_Connections(void) = 0;
+ virtual int Connection_ID(int index) = 0;
+ virtual int Connection_Index(int id) = 0;
+
+ /*.....................................................................
+ Queue utility routines
+ .....................................................................*/
+ virtual int Global_Num_Send(void) = 0;
+ virtual int Global_Num_Receive(void) = 0;
+ virtual int Private_Num_Send(int id = CONNECTION_NONE) = 0;
+ virtual int Private_Num_Receive(int id = CONNECTION_NONE) = 0;
+
+ /*.....................................................................
+ Timing management
+ .....................................................................*/
+ virtual void Reset_Response_Time(void) = 0;
+ virtual unsigned long Response_Time(void) = 0;
+ virtual void Set_Timing (unsigned long retrydelta,
+ unsigned long maxretries, unsigned long timeout) = 0;
+
+ /*.....................................................................
+ Debugging
+ .....................................................................*/
+ virtual void Configure_Debug(int index, int type_offset, int type_size,
+ char **names, int namestart, int namecount) = 0;
+#ifdef CHEAT_KEYS
+ virtual void Mono_Debug_Print(int index, int refresh) = 0;
+#endif
+
+ /*
+ --------------------------- Private Interface ----------------------------
+ */
+ private:
+ /*.....................................................................
+ This abstract class contains no data members; but a derived class
+ will contain:
+ - An instance of one or more derived Connection Classes
+ - A buffer to store incoming packets
+ .....................................................................*/
+};
+
+#endif
diff --git a/CODE/CONQUER.CPP b/CODE/CONQUER.CPP
new file mode 100644
index 0000000..e910f3c
--- /dev/null
+++ b/CODE/CONQUER.CPP
@@ -0,0 +1,5558 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CONQUER.CPP 6 3/13/97 2:05p Steve_tall $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CONQUER.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : April 3, 1991 *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * CC_Draw_Shape -- Custom draw shape handler. *
+ * Call_Back -- Main game maintenance callback routine. *
+ * Color_Cycle -- Handle the general palette color cycling. *
+ * Crate_From_Name -- Given a crate name convert it to a crate type. *
+ * Disk_Space_Available -- returns bytes of free disk space *
+ * Do_Record_Playback -- handles saving/loading map pos & current object *
+ * Fading_Table_Name -- Builds a theater specific fading table name. *
+ * Fetch_Techno_Type -- Convert type and ID into TechnoTypeClass pointer. *
+ * Force_CD_Available -- Ensures that specified CD is available. *
+ * Get_Radar_Icon -- Builds and alloc a radar icon from a shape file *
+ * Handle_Team -- Processes team selection command. *
+ * Handle_View -- Either records or restores the tactical view. *
+ * KN_To_Facing -- Converts a keyboard input number into a facing value. *
+ * Keyboard_Process -- Processes the tactical map input codes. *
+ * Language_Name -- Build filename for current language. *
+ * List_Copy -- Makes a copy of a cell offset list. *
+ * Main_Game -- Main game startup routine. *
+ * Main_Loop -- This is the main game loop (as a single loop). *
+ * Map_Edit_Loop -- a mini-main loop for map edit mode only *
+ * Message_Input -- allows inter-player message input processing *
+ * MixFileHandler -- Handles VQ file access. *
+ * Name_From_Source -- retrieves the name for the given SourceType *
+ * Owner_From_Name -- Convert an owner name into a bitfield. *
+ * Play_Movie -- Plays a VQ movie. *
+ * Shake_The_Screen -- Dispatcher that shakes the screen. *
+ * Shape_Dimensions -- Determine the minimum rectangle for the shape. *
+ * Source_From_Name -- Converts ASCII name into SourceType. *
+ * Sync_Delay -- Forces the game into a 15 FPS rate. *
+ * Theater_From_Name -- Converts ASCII name into a theater number. *
+ * Unselect_All -- Causes all selected objects to become unselected. *
+ * VQ_Call_Back -- Maintenance callback used for VQ movies. *
+ * Game_Registry_Key -- Returns pointer to string containing the registry subkey for the game.
+ * Is_Counterstrike_Installed -- Function to determine the availability of the CS expansion.
+ * Is_Aftermath_Installed -- Function to determine the availability of the AM expansion.
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#ifdef TESTCODE
+class A {
+ public:
+ enum {VAR=1};
+};
+
+template
+class B {
+ public:
+ enum {VAR2=T::VAR}; // this is the line in question.
+};
+
+B test;
+#endif
+
+
+
+#include "function.h"
+#ifdef WIN32
+#ifdef WINSOCK_IPX
+#include "WSProto.h"
+#else //WINSOCK_IPX
+#include "tcpip.h"
+#endif //WINSOCK_IPX
+#else
+#include "fakesock.h"
+TcpipManagerClass Winsock;
+#endif
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "ccdde.h"
+#include "vortex.h"
+
+#ifdef WOLAPI_INTEGRATION
+//#include "WolDebug.h"
+#include "WolStrng.h"
+#include "WolapiOb.h"
+extern WolapiObject* pWolapi;
+#define PAGE_RESPOND_KEY KN_RETURN //KN_COMMA
+#endif
+
+#ifdef MPEGMOVIE
+#ifdef MCIMPEG
+#include "mcimovie.h"
+#endif
+#include "movie.h"
+MPG_RESPONSE far __stdcall MpegCallback(MPG_CMD cmd, LPVOID data, LPVOID user);
+#endif
+
+#define SHAPE_TRANS 0x40
+
+void * Get_Shape_Header_Data(void * ptr);
+extern bool Spawn_WChat(bool can_launch);
+
+#ifdef FIXIT_CSII // checked - ajw 9/28/98
+void Enable_Secret_Units(void);
+#endif
+
+extern bool Is_Mission_Aftermath (char *file_name);
+extern bool Is_Mission_Counterstrike (char *file_name);
+
+#ifdef FIXIT_VERSION_3 // Stalemate games.
+extern void Do_Draw(void);
+#endif
+
+#ifdef CHEAT_KEYS
+bool bNoMovies = false;
+#endif
+
+/****************************************
+** Function prototypes for this module **
+*****************************************/
+bool Main_Loop(void);
+void Keyboard_Process(KeyNumType & input);
+static void Message_Input(KeyNumType &input);
+static void Color_Cycle(void);
+bool Map_Edit_Loop(void);
+
+extern "C" {
+ bool UseOldShapeDraw = false;
+}
+
+#ifdef CHEAT_KEYS
+void Dump_Heap_Pointers( void );
+void Error_In_Heap_Pointers( char * string );
+#endif
+static void Do_Record_Playback(void);
+
+void Toggle_Formation(void);
+
+extern "C" {
+ extern char * __nheapbeg;
+}
+
+//
+// Special module globals for recording and playback
+//
+char TeamEvent = 0; // 0 = no event, 1,2,3 = team event type
+char TeamNumber = 0; // which team was selected? (1-9)
+char FormationEvent = 0; // 0 = no event, 1 = formation was toggled
+
+
+ /* -----------------10/14/96 7:29PM------------------
+
+ --------------------------------------------------*/
+
+#if(TEN)
+void TEN_Call_Back(void);
+#endif // TEN
+
+#if(MPATH)
+void MPATH_Call_Back(void);
+#endif // MPATH
+
+/***********************************************************************************************
+ * Main_Game -- Main game startup routine. *
+ * *
+ * This is the first official routine of the game. It handles game initialization and *
+ * the main game loop control. *
+ * *
+ * Initialization: *
+ * - Init_Game handles one-time-only inits *
+ * - Select_Game is responsible for initializations required for each new game played *
+ * (these may be different depending on whether a multiplayer game is selected, and *
+ * other parameters) *
+ * - This routine performs any un-inits required, both for each game played, and one-time *
+ * *
+ * INPUT: argc -- Number of command line arguments (including program name itself). *
+ * *
+ * argv -- Array of command line argument pointers. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 10/01/1994 JLB : Created. *
+ *=============================================================================================*/
+void Main_Game(int argc, char * argv[])
+{
+ static bool fade = true;
+
+ /*
+ ** Perform one-time-only initializations
+ */
+ if (!Init_Game(argc, argv)) {
+ return;
+ }
+
+ /*
+ ** Game processing loop:
+ ** 1) Select which game to play, or whether to exit (don't fade the palette
+ ** on the first game selection, but fade it in on subsequent calls)
+ ** 2) Invoke either the main-loop routine, or the editor-loop routine,
+ ** until they indicate that the user wants to exit the scenario.
+ */
+ while (Select_Game(fade)) {
+ fade = false;
+ ScenarioInit = 0; // Kludge.
+
+ fade = true;
+
+ /*
+ ** Initialise the color lookup tables for the chronal vortex
+ */
+ ChronalVortex.Stop();
+ ChronalVortex.Setup_Remap_Tables(Scen.Theater);
+
+ /*
+ ** Make the game screen visible, clear the keyboard buffer of spurious
+ ** values, and then show the mouse. This PRESUMES that Select_Game() has
+ ** told the map to draw itself.
+ */
+ GamePalette.Set(FADE_PALETTE_MEDIUM);
+ Keyboard->Clear();
+ /*
+ ** Only show the mouse if we're not playing back a recording.
+ */
+ if (Session.Play) {
+ Hide_Mouse();
+ TeamEvent = 0;
+ TeamNumber = 0;
+ FormationEvent = 0;
+ } else {
+ Show_Mouse();
+ }
+
+#ifdef WIN32
+ if (Session.Type == GAME_INTERNET) {
+ Register_Game_Start_Time();
+ GameStatisticsPacketSent = false;
+ PacketLater = NULL;
+ ConnectionLost = false;
+ } else {
+#ifndef WOLAPI_INTEGRATION
+ DDEServer.Disable();
+#endif // !WOLAPI_INTEGRATION
+ }
+#endif //WIN32
+
+#ifdef SCENARIO_EDITOR
+ /*
+ ** Scenario-editor version of main-loop processing
+ */
+ for (;;) {
+ /*
+ ** Non-scenario-editor-mode: call the game's main loop
+ */
+ if (!Debug_Map) {
+#ifdef FIXIT_CSII // checked - ajw 9/28/98
+ TimeQuake = PendingTimeQuake;
+ PendingTimeQuake = false;
+#else
+ TimeQuake = false;
+#endif
+ if (Main_Loop()) {
+ break;
+ }
+
+ if (SpecialDialog != SDLG_NONE) {
+ switch (SpecialDialog) {
+ case SDLG_SPECIAL:
+ Map.Help_Text(TXT_NONE);
+ Map.Override_Mouse_Shape(MOUSE_NORMAL, false);
+ Special_Dialog();
+ Map.Revert_Mouse_Shape();
+ SpecialDialog = SDLG_NONE;
+ break;
+
+ case SDLG_OPTIONS:
+ Map.Help_Text(TXT_NONE);
+ Map.Override_Mouse_Shape(MOUSE_NORMAL, false);
+ Options.Process();
+ Map.Revert_Mouse_Shape();
+ SpecialDialog = SDLG_NONE;
+ break;
+
+ case SDLG_SURRENDER:
+ Map.Help_Text(TXT_NONE);
+ Map.Override_Mouse_Shape(MOUSE_NORMAL, false);
+ if (Surrender_Dialog(TXT_SURRENDER)) {
+ PlayerPtr->Flag_To_Lose();
+ }
+ SpecialDialog = SDLG_NONE;
+ Map.Revert_Mouse_Shape();
+ break;
+
+ default:
+ break;
+ }
+ }
+ } else {
+
+ /*
+ ** Scenario-editor-mode: call the editor's main loop
+ */
+ if (Map_Edit_Loop()) {
+ break;
+ }
+ }
+ }
+#else
+ /*
+ ** Non-editor version of main-loop processing
+ */
+ for (;;) {
+#ifdef FIXIT_CSII // checked - ajw 9/28/98
+ TimeQuake = PendingTimeQuake;
+ PendingTimeQuake = false;
+#else
+ TimeQuake = false;
+#endif
+ /*
+ ** Call the game's main loop
+ */
+ if (Main_Loop()) {
+ break;
+ }
+
+ /*
+ ** If the SpecialDialog flag is set, invoke the given special dialog.
+ ** This must be done outside the main loop, since the dialog will call
+ ** Main_Loop(), allowing the game to run in the background.
+ */
+ if (SpecialDialog != SDLG_NONE) {
+ switch (SpecialDialog) {
+ case SDLG_SPECIAL:
+ Map.Help_Text(TXT_NONE);
+ Map.Override_Mouse_Shape(MOUSE_NORMAL, false);
+ Special_Dialog();
+ Map.Revert_Mouse_Shape();
+ SpecialDialog = SDLG_NONE;
+ break;
+
+ case SDLG_OPTIONS:
+ Map.Help_Text(TXT_NONE);
+ Map.Override_Mouse_Shape(MOUSE_NORMAL, false);
+ Options.Process();
+ Map.Revert_Mouse_Shape();
+ SpecialDialog = SDLG_NONE;
+ break;
+
+ case SDLG_SURRENDER:
+ Map.Help_Text(TXT_NONE);
+ Map.Override_Mouse_Shape(MOUSE_NORMAL, false);
+ if (Surrender_Dialog(TXT_SURRENDER)) {
+ OutList.Add(EventClass(EventClass::DESTRUCT));
+ }
+ SpecialDialog = SDLG_NONE;
+ Map.Revert_Mouse_Shape();
+ break;
+
+/*ifdef FIXIT_VERSION_3 // Stalemate games.
+ case SDLG_PROPOSE_DRAW:
+ Map.Help_Text(TXT_NONE);
+ Map.Override_Mouse_Shape(MOUSE_NORMAL, false);
+ if (Surrender_Dialog(TXT_WOL_PROPOSE_DRAW)) {
+ OutList.Add(EventClass(EventClass::PROPOSE_DRAW));
+ }
+ SpecialDialog = SDLG_NONE;
+ Map.Revert_Mouse_Shape();
+ break;
+
+ case SDLG_ACCEPT_DRAW:
+ Map.Help_Text(TXT_NONE);
+ Map.Override_Mouse_Shape(MOUSE_NORMAL, false);
+ if (Surrender_Dialog(TXT_WOL_ACCEPT_DRAW)) {
+ OutList.Add(EventClass(EventClass::ACCEPT_DRAW));
+ }
+ SpecialDialog = SDLG_NONE;
+ Map.Revert_Mouse_Shape();
+ break;
+#endif
+*/
+ default:
+ break;
+ }
+ }
+ }
+#endif
+
+
+#ifdef WIN32
+ /*
+ ** Send the game stats to WChat if we haven't already done so
+ */
+ if (!GameStatisticsPacketSent && PacketLater) {
+ Send_Statistics_Packet(); // After game sending if PacketLater set.
+ }
+#endif //WIN32
+
+ /*
+ ** Scenario is done; fade palette to black
+ */
+ BlackPalette.Set(FADE_PALETTE_SLOW);
+ VisiblePage.Clear();
+
+ /*
+ ** Un-initialize whatever needs it, for each game played.
+ **
+ ** Shut down either the modem or network; they'll get re-initialized if
+ ** the user selections those options again in Select_Game(). This
+ ** "re-boots" the modem & network code, which I currently feel is safer
+ ** than just letting it hang around.
+ ** (Skip this step if we're in playback mode; the modem or net won't have
+ ** been initialized in that case.)
+ */
+ if (Session.Record || Session.Play) {
+ Session.RecordFile.Close();
+ }
+
+ if (Session.Type == GAME_NULL_MODEM || Session.Type == GAME_MODEM) {
+ if (!Session.Play) {
+ Modem_Signoff();
+ }
+ } else {
+ if (Session.Type == GAME_IPX) {
+ if (!Session.Play) {
+ Shutdown_Network();
+ }
+ }
+ }
+
+#if(TEN)
+
+ if (Session.Type == GAME_TEN) {
+ Shutdown_TEN();
+ //Prog_End();
+ Emergency_Exit(0);
+ }
+#endif // TEN
+
+#if(MPATH)
+ if (Session.Type == GAME_MPATH) {
+ Shutdown_MPATH();
+ //Prog_End();
+ Emergency_Exit(0);
+ }
+#endif // MPATH
+
+ /*
+ ** If we're playing back, the mouse will be hidden; show it.
+ ** Also, set all variables back to normal, to return to the main menu.
+ */
+ if (Session.Play) {
+ Show_Mouse();
+ Session.Type = GAME_NORMAL;
+ Session.Play = 0;
+ }
+#ifndef WOLAPI_INTEGRATION
+#ifdef WIN32
+ if (Special.IsFromWChat) {
+ Shutdown_Network(); // Clear up the pseudo IPX stuff
+#ifndef WINSOCK_IPX
+ Winsock.Close();
+#endif //WINSOCK_IPX
+ Special.IsFromWChat = false;
+ SpawnedFromWChat = false;
+ DDEServer.Delete_MPlayer_Game_Info(); //Make sure we dont use the same start packet twice
+ Session.Type = GAME_NORMAL; //Have to do this or we will got straight to the multiplayer menu
+ Spawn_WChat(false); //Will switch back to Wchat. It must be there because its been poking us
+ }
+#endif //WIN32
+#endif // !WOLAPI_INTEGRATION
+ }
+
+ /*
+ ** Free the scenario description buffers
+ */
+ Session.Free_Scenario_Descriptions();
+}
+
+
+/***********************************************************************************************
+ * Keyboard_Process -- Processes the tactical map input codes. *
+ * *
+ * This routine is used to process the input codes while the player *
+ * has the tactical map displayed. It handles all the keys that *
+ * are appropriate to that mode. *
+ * *
+ * INPUT: input -- Input code as returned from Input_Num(). *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 01/21/1992 JLB : Created. *
+ * 07/04/1995 JLB : Handles team and map control hotkeys. *
+ *=============================================================================================*/
+void Keyboard_Process(KeyNumType & input)
+{
+ ObjectClass * obj;
+ int index;
+
+ /*
+ ** Don't do anything if there is not keyboard event.
+ */
+ if (input == KN_NONE) {
+ return;
+ }
+ /*
+ ** For network & modem, process user input for inter-player messages.
+ */
+ Message_Input(input);
+
+#ifdef WIN32
+ /*
+ ** The VK_BIT must be stripped from the "plain" value of the key so that a comparison to
+ ** KN_1, for example, will yield TRUE if in fact the "1" key was pressed.
+ */
+
+ KeyNumType plain = KeyNumType(input & ~(WWKEY_SHIFT_BIT|WWKEY_ALT_BIT|WWKEY_CTRL_BIT|WWKEY_VK_BIT));
+ KeyNumType key = KeyNumType(input & ~WWKEY_VK_BIT);
+
+
+#else
+ KeyNumType plain = KeyNumType(input & ~(KN_SHIFT_BIT|KN_ALT_BIT|KN_CTRL_BIT));
+ KeyNumType key = plain;
+#endif
+
+#ifdef CHEAT_KEYS
+
+ if (Debug_Flag) {
+ HousesType h;
+
+ switch (int(input)) {
+ case int(int(KN_M) | int(KN_SHIFT_BIT)):
+ case int(int(KN_M) | int(KN_ALT_BIT)):
+ case int(int(KN_M) | int(KN_CTRL_BIT)):
+ for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) {
+ Houses.Ptr(h)->Refund_Money(10000);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+#endif
+
+#ifdef VIRGIN_CHEAT_KEYS
+ if (Debug_Playtest && input == (KN_W|KN_ALT_BIT)) {
+ PlayerPtr->Blockage = false;
+ PlayerPtr->Flag_To_Win();
+ }
+#endif
+
+#ifdef CHEAT_KEYS
+#ifdef WIN32
+ if (Debug_Playtest && input == (KA_W|KN_ALT_BIT)) {
+#else
+ if (Debug_Playtest && input == (KN_W|KN_ALT_BIT)) {
+#endif
+ PlayerPtr->Blockage = false;
+ PlayerPtr->Flag_To_Win();
+ }
+
+ if ((Debug_Flag || Debug_Playtest) && plain == KN_F4) {
+ if (Session.Type == GAME_NORMAL) {
+ Debug_Unshroud = (Debug_Unshroud == false);
+ Map.Flag_To_Redraw(true);
+ }
+ }
+
+ if (Debug_Flag && input == KN_SLASH) {
+ if (Session.Type != GAME_NORMAL) {
+ SpecialDialog = SDLG_SPECIAL;
+ input = KN_NONE;
+ } else {
+ Special_Dialog();
+ }
+ }
+#endif
+
+ /*
+ ** Process prerecorded team selection. This will be an additive select
+ ** if the SHIFT key is held down. It will create the team if the
+ ** CTRL or ALT key is held down.
+ */
+ int action = 0;
+#ifdef WIN32
+ if (input & WWKEY_SHIFT_BIT) action = 1;
+ if (input & WWKEY_ALT_BIT) action = 3;
+ if (input & WWKEY_CTRL_BIT) action = 2;
+#else
+ if (input & KN_SHIFT_BIT) action = 1;
+ if (input & KN_ALT_BIT) action = 3;
+ if (input & KN_CTRL_BIT) action = 2;
+#endif
+
+ /*
+ ** If the "N" key is pressed, then select the next object.
+ */
+ if (key != 0 && key == Options.KeyNext) {
+ if (action) {
+ obj = Map.Prev_Object(CurrentObject.Count() ? CurrentObject[0] : NULL);
+ } else {
+ obj = Map.Next_Object(CurrentObject.Count() ? CurrentObject[0] : NULL);
+ }
+ if (obj != NULL) {
+ Unselect_All();
+ obj->Select();
+ Map.Center_Map();
+ Map.Flag_To_Redraw(true);
+ }
+ input = KN_NONE;
+ }
+ if (key != 0 && key == Options.KeyPrevious) {
+ if (action) {
+ obj = Map.Next_Object(CurrentObject.Count() ? CurrentObject[0] : NULL);
+ } else {
+ obj = Map.Prev_Object(CurrentObject.Count() ? CurrentObject[0] : NULL);
+ }
+ if (obj != NULL) {
+ Unselect_All();
+ obj->Select();
+ Map.Center_Map();
+ Map.Flag_To_Redraw(true);
+ }
+ input = KN_NONE;
+ }
+
+
+ /*
+ ** All selected units will go into idle mode.
+ */
+ if (key != 0 && key == Options.KeyStop) {
+ if (CurrentObject.Count()) {
+ for (index = 0; index < CurrentObject.Count(); index++) {
+ ObjectClass const * tech = CurrentObject[index];
+
+ if (tech != NULL && (tech->Can_Player_Move() || (tech->Can_Player_Fire() && tech->What_Am_I() != RTTI_BUILDING))) {
+ OutList.Add(EventClass(EventClass::IDLE, TargetClass(tech)));
+ }
+ }
+ }
+ input = KN_NONE;
+ }
+
+ /*
+ ** All selected units will attempt to go into guard area mode.
+ */
+ if (key != 0 && key == Options.KeyGuard) {
+ if (CurrentObject.Count()) {
+ for (index = 0; index < CurrentObject.Count(); index++) {
+ ObjectClass const * tech = CurrentObject[index];
+
+ if (tech != NULL && tech->Can_Player_Move() && tech->Can_Player_Fire()) {
+ OutList.Add(EventClass(TargetClass(tech), MISSION_GUARD_AREA));
+ }
+ }
+ }
+ input = KN_NONE;
+ }
+
+ /*
+ ** All selected units will attempt to scatter.
+ */
+ if (key != 0 && key == Options.KeyScatter) {
+ if (CurrentObject.Count()) {
+ for (index = 0; index < CurrentObject.Count(); index++) {
+ ObjectClass const * tech = CurrentObject[index];
+
+ if (tech != NULL && tech->Can_Player_Move()) {
+ OutList.Add(EventClass(EventClass::SCATTER, TargetClass(tech)));
+ }
+ }
+ }
+ input = KN_NONE;
+ }
+
+ /*
+ ** Center the map around the currently selected objects. If no
+ ** objects are selected, then fall into the home case.
+ */
+ if (key != 0 && (key == Options.KeyHome1 || key == Options.KeyHome2)) {
+ if (CurrentObject.Count()) {
+ Map.Center_Map();
+#ifdef WIN32
+ Map.Flag_To_Redraw(true);
+#endif
+ input = KN_NONE;
+ } else {
+ input = Options.KeyBase;
+ }
+ }
+
+ /*
+ ** Center the map about the construction yard or construction vehicle
+ ** if one is present.
+ */
+ if (key != 0 && key == Options.KeyBase) {
+ Unselect_All();
+ if (PlayerPtr->CurBuildings) {
+ for (index = 0; index < Buildings.Count(); index++) {
+ BuildingClass * building = Buildings.Ptr(index);
+
+ if (building != NULL && !building->IsInLimbo && building->House == PlayerPtr && *building == STRUCT_CONST) {
+ Unselect_All();
+ building->Select();
+ if (building->IsLeader) break;
+ }
+ }
+ }
+ if (CurrentObject.Count() == 0 && PlayerPtr->CurUnits) {
+ for (index = 0; index < Units.Count(); index++) {
+ UnitClass * unit = Units.Ptr(index);
+
+ if (unit != NULL && !unit->IsInLimbo && unit->House == PlayerPtr && *unit == UNIT_MCV) {
+ Unselect_All();
+ unit->Select();
+ break;
+ }
+ }
+ }
+ if (CurrentObject.Count()) {
+ Map.Center_Map();
+ } else {
+ if (PlayerPtr->Center != 0) {
+ Map.Center_Map(PlayerPtr->Center);
+ }
+ }
+ Map.Flag_To_Redraw(true);
+ input = KN_NONE;
+ }
+
+ /*
+ ** Toggle the status of formation for the current team
+ */
+ if (key != 0 && key == Options.KeyFormation) {
+ Toggle_Formation();
+ input = KN_NONE;
+ }
+
+#ifdef TOFIX
+ /*
+ ** For multiplayer, 'R' pops up the surrender dialog.
+ */
+ if (input != 0 && input == Options.KeyResign) {
+ if (!PlayerLoses && /*Session.Type != GAME_NORMAL &&*/ !PlayerPtr->IsDefeated) {
+ SpecialDialog = SDLG_SURRENDER;
+ input = KN_NONE;
+ }
+ input = KN_NONE;
+ }
+#endif
+
+ /*
+ ** Handle making and breaking alliances.
+ */
+ if (key != 0 && key == Options.KeyAlliance) {
+ if (Session.Type != GAME_NORMAL || Debug_Flag) {
+ if (CurrentObject.Count() && !PlayerPtr->IsDefeated) {
+ if (CurrentObject[0]->Owner() != PlayerPtr->Class->House) {
+ OutList.Add(EventClass(EventClass::ALLY, CurrentObject[0]->Owner()));
+ }
+ }
+ }
+ input = KN_NONE;
+ }
+
+ /*
+ ** Select all the units on the current display. This is equivalent to
+ ** drag selecting the whole view.
+ */
+ if (key != 0 && key == Options.KeySelectView) {
+ Map.Select_These(0x00000000, XY_Coord(Map.TacLeptonWidth, Map.TacLeptonHeight));
+ input = KN_NONE;
+ }
+
+ /*
+ ** Toggles the repair state similarly to pressing the repair button.
+ */
+ if (key != 0 && key == Options.KeyRepair) {
+ Map.Repair_Mode_Control(-1);
+ input = KN_NONE;
+ }
+
+ /*
+ ** Toggles the sell state similarly to pressing the sell button.
+ */
+ if (key != 0 && key == Options.KeySell) {
+ Map.Sell_Mode_Control(-1);
+ input = KN_NONE;
+ }
+
+ /*
+ ** Toggles the map zoom mode similarly to pressing the map button.
+ */
+ if (key != 0 && key == Options.KeyMap) {
+ Map.Zoom_Mode_Control();
+ input = KN_NONE;
+ }
+
+ /*
+ ** Scrolls the sidebar up one slot.
+ */
+ if (key != 0 && key == Options.KeySidebarUp) {
+ Map.SidebarClass::Scroll(true, -1);
+ input = KN_NONE;
+ }
+
+ /*
+ ** Scrolls the sidebar down one slot.
+ */
+ if (key != 0 && key == Options.KeySidebarDown) {
+ Map.SidebarClass::Scroll(false, -1);
+ input = KN_NONE;
+ }
+
+ /*
+ ** Brings up the options dialog box.
+ */
+ if (key != 0 && (key == Options.KeyOption1 || key == Options.KeyOption2)) {
+ Map.Help_Text(TXT_NONE); // Turns off help text.
+ Queue_Options();
+ input = KN_NONE;
+ }
+
+ /*
+ ** Scrolls the tactical map in the direction specified.
+ */
+ int distance = CELL_LEPTON_W;
+ if (key != 0 && key == Options.KeyScrollLeft) {
+ Map.Scroll_Map(DIR_W, distance, true);
+ input = KN_NONE;
+ }
+ if (key != 0 && key == Options.KeyScrollRight) {
+ Map.Scroll_Map(DIR_E, distance, true);
+ input = KN_NONE;
+ }
+ if (key != 0 && key == Options.KeyScrollUp) {
+ Map.Scroll_Map(DIR_N, distance, true);
+ input = KN_NONE;
+ }
+ if (key != 0 && key == Options.KeyScrollDown) {
+ Map.Scroll_Map(DIR_S, distance, true);
+ input = KN_NONE;
+ }
+
+ /*
+ ** Teams are handled by the 10 special team keys. The manual comparison
+ ** to the KN numbers is because the Windows keyboard driver can vary
+ ** the base code number for the key depending on the shift or alt key
+ ** state!
+ */
+ if (input != 0 && (plain == Options.KeyTeam1 || plain == KN_1)) {
+ Handle_Team(0, action);
+ input = KN_NONE;
+ }
+ if (input != 0 && (plain == Options.KeyTeam2 || plain == KN_2)) {
+ Handle_Team(1, action);
+ input = KN_NONE;
+ }
+ if (input != 0 && (plain == Options.KeyTeam3 || plain == KN_3)) {
+ Handle_Team(2, action);
+ input = KN_NONE;
+ }
+ if (input != 0 && (plain == Options.KeyTeam4 || plain == KN_4)) {
+ Handle_Team(3, action);
+ input = KN_NONE;
+ }
+ if (input != 0 && (plain == Options.KeyTeam5 || plain == KN_5)) {
+ Handle_Team(4, action);
+ input = KN_NONE;
+ }
+ if (input != 0 && (plain == Options.KeyTeam6 || plain == KN_6)) {
+ Handle_Team(5, action);
+ input = KN_NONE;
+ }
+ if (input != 0 && (plain == Options.KeyTeam7 || plain == KN_7)) {
+ Handle_Team(6, action);
+ input = KN_NONE;
+ }
+ if (input != 0 && (plain == Options.KeyTeam8 || plain == KN_8)) {
+ Handle_Team(7, action);
+ input = KN_NONE;
+ }
+ if (input != 0 && (plain == Options.KeyTeam9 || plain == KN_9)) {
+ Handle_Team(8, action);
+ input = KN_NONE;
+ }
+ if (input != 0 && (plain == Options.KeyTeam10 || plain == KN_0)) {
+ Handle_Team(9, action);
+ input = KN_NONE;
+ }
+
+ /*
+ ** Handle the bookmark hotkeys.
+ */
+ if (input != 0 && plain == Options.KeyBookmark1 && !Debug_Map) {
+ Handle_View(0, action);
+ input = KN_NONE;
+ }
+ if (input != 0 && plain == Options.KeyBookmark2 && !Debug_Map) {
+ Handle_View(1, action);
+ input = KN_NONE;
+ }
+ if (input != 0 && plain == Options.KeyBookmark3 && !Debug_Map) {
+ Handle_View(2, action);
+ input = KN_NONE;
+ }
+ if (input != 0 && plain == Options.KeyBookmark4 && !Debug_Map) {
+ Handle_View(3, action);
+ input = KN_NONE;
+ }
+
+#ifdef CHEAT_KEYS
+ if (input != 0 && Debug_Flag && input && (input & KN_RLSE_BIT) == 0) {
+ Debug_Key(input);
+ }
+#endif
+}
+
+
+void Toggle_Formation(void) {
+ int team = -1;
+ long minx = 0x7FFFFFFFL, miny = 0x7FFFFFFFL;
+ long maxx = 0, maxy = 0;
+ int index;
+ bool setform = 0;
+
+ //
+ // Recording support
+ //
+ if (Session.Record) {
+ FormationEvent = 1;
+ }
+
+ /*
+ ** Find the first selected object that is a member of a team, and
+ ** register his group as the team we're using. Once we find the team
+ ** number, update the 'setform' flag to know whether we should be setting
+ ** the formation's offsets, or clearing them. If they currently have
+ ** illegal offsets (as in 0x80000000), then we're setting.
+ */
+ for (index = 0; index < Units.Count(); index++) {
+ UnitClass * obj = Units.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->IsSelected) {
+ team = obj->Group;
+ if (team != -1) {
+ setform = obj->XFormOffset == (int)0x80000000;
+ TeamSpeed[team] = SPEED_WHEEL;
+ TeamMaxSpeed[team] = MPH_LIGHT_SPEED;
+ break;
+ }
+ }
+ }
+ if (team == -1) {
+ for (index = 0; index < Infantry.Count(); index++) {
+ InfantryClass * obj = Infantry.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->IsSelected) {
+ team = obj->Group;
+ if (team != -1) {
+ setform = obj->XFormOffset == (int)0x80000000;
+ TeamSpeed[team] = SPEED_WHEEL;
+ TeamMaxSpeed[team] = MPH_LIGHT_SPEED;
+ break;
+ }
+ }
+ }
+ }
+
+ if (team == -1) {
+ for (index = 0; index < Vessels.Count(); index++) {
+ VesselClass * obj = Vessels.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->IsSelected) {
+ team = obj->Group;
+ if (team != -1) {
+ setform = obj->XFormOffset == 0x80000000UL;
+ TeamSpeed[team] = SPEED_WHEEL;
+ TeamMaxSpeed[team] = MPH_LIGHT_SPEED;
+ break;
+ }
+ }
+ }
+ }
+
+ if (team == -1) return;
+ /*
+ ** Now that we have a team, let's go set (or clear) the formation offsets.
+ */
+ for (index = 0; index < Units.Count(); index++) {
+ UnitClass * obj = Units.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) {
+ obj->Mark(MARK_CHANGE);
+ if (setform) {
+ long xc = Cell_X(Coord_Cell(obj->Center_Coord()));
+ long yc = Cell_Y(Coord_Cell(obj->Center_Coord()));
+ if (xc < minx) minx = xc;
+ if (xc > maxx) maxx = xc;
+ if (yc < miny) miny = yc;
+ if (yc > maxy) maxy = yc;
+ if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) {
+ TeamMaxSpeed[team] = obj->Class->MaxSpeed;
+ TeamSpeed[team] = obj->Class->Speed;
+ }
+ } else {
+ obj->XFormOffset = obj->YFormOffset = (int)0x80000000;
+ }
+ }
+ }
+
+ for (index = 0; index < Infantry.Count(); index++) {
+ InfantryClass * obj = Infantry.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) {
+ obj->Mark(MARK_CHANGE);
+ if (setform) {
+ long xc = Cell_X(Coord_Cell(obj->Center_Coord()));
+ long yc = Cell_Y(Coord_Cell(obj->Center_Coord()));
+ if (xc < minx) minx = xc;
+ if (xc > maxx) maxx = xc;
+ if (yc < miny) miny = yc;
+ if (yc > maxy) maxy = yc;
+ if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) {
+ TeamMaxSpeed[team] = obj->Class->MaxSpeed;
+ }
+ } else {
+ obj->XFormOffset = obj->YFormOffset = (int)0x80000000;
+ }
+ }
+ }
+
+ for (index = 0; index < Vessels.Count(); index++) {
+ VesselClass * obj = Vessels.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) {
+ obj->Mark(MARK_CHANGE);
+ if (setform) {
+ long xc = Cell_X(Coord_Cell(obj->Center_Coord()));
+ long yc = Cell_Y(Coord_Cell(obj->Center_Coord()));
+ if (xc < minx) minx = xc;
+ if (xc > maxx) maxx = xc;
+ if (yc < miny) miny = yc;
+ if (yc > maxy) maxy = yc;
+ if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) {
+ TeamMaxSpeed[team] = obj->Class->MaxSpeed;
+ }
+ } else {
+ obj->XFormOffset = obj->YFormOffset = 0x80000000UL;
+ }
+ }
+ }
+
+ /*
+ ** All the units have been counted to find the bounding rectangle and
+ ** center of the formation, or to clear their offsets. Now, if we're to
+ ** set them into formation, proceed to do so. Otherwise, bail.
+ */
+ if (setform) {
+ int centerx = (int)((maxx - minx)/2)+minx;
+ int centery = (int)((maxy - miny)/2)+miny;
+
+ for (index = 0; index < Units.Count(); index++) {
+ UnitClass * obj = Units.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) {
+ long xc = Cell_X(Coord_Cell(obj->Center_Coord()));
+ long yc = Cell_Y(Coord_Cell(obj->Center_Coord()));
+
+ obj->XFormOffset = xc - centerx;
+ obj->YFormOffset = yc - centery;
+ }
+ }
+
+ for (index = 0; index < Infantry.Count(); index++) {
+ InfantryClass * obj = Infantry.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team ) {
+ long xc = Cell_X(Coord_Cell(obj->Center_Coord()));
+ long yc = Cell_Y(Coord_Cell(obj->Center_Coord()));
+
+ obj->XFormOffset = xc - centerx;
+ obj->YFormOffset = yc - centery;
+ }
+ }
+
+ for (index = 0; index < Vessels.Count(); index++) {
+ VesselClass * obj = Vessels.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team ) {
+ long xc = Cell_X(Coord_Cell(obj->Center_Coord()));
+ long yc = Cell_Y(Coord_Cell(obj->Center_Coord()));
+
+ obj->XFormOffset = xc - centerx;
+ obj->YFormOffset = yc - centery;
+ }
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * Message_Input -- allows inter-player message input processing *
+ * *
+ * INPUT: *
+ * input key value *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 05/22/1995 BRR : Created. *
+ *=============================================================================================*/
+#pragma off (unreferenced)
+static void Message_Input(KeyNumType &input)
+{
+ int rc;
+ char txt[MAX_MESSAGE_LENGTH+32];
+ int id;
+ SerialPacketType * serial_packet;
+ int i;
+ KeyNumType copy_input;
+ //char *msg;
+
+ /*
+ ** Check keyboard input for a request to send a message.
+ ** The 'to' argument for Add_Edit is prefixed to the message buffer; the
+ ** message buffer is big enough for the 'to' field plus MAX_MESSAGE_LENGTH.
+ ** To send the message, calling Get_Edit_Buf retrieves the buffer minus the
+ ** 'to' portion. At the other end, the buffer allocated to display the
+ ** message must be MAX_MESSAGE_LENGTH plus the size of "From: xxx (house)".
+ */
+#ifdef WOLAPI_INTEGRATION
+ if (Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH &&
+ ( ( input >= KN_F1 && input < (KN_F1 + Session.MaxPlayers) ) || input == PAGE_RESPOND_KEY ) &&
+ !Session.Messages.Is_Edit()) {
+#else
+ if (Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH && input >= KN_F1 && input < (KN_F1 + Session.MaxPlayers) && !Session.Messages.Is_Edit()) {
+#endif
+ memset (txt, 0, 40);
+
+ /*
+ ** For a serial game, send a message on F1 or F4; set 'txt' to the
+ ** "Message:" string & add an editable message to the list.
+ */
+ if (Session.Type==GAME_NULL_MODEM || Session.Type==GAME_MODEM) {
+ if (input==KN_F1 || input==(KN_F1 + Session.MaxPlayers - 1)) {
+
+ strcpy(txt, Text_String(TXT_MESSAGE)); // "Message:"
+
+ Session.Messages.Add_Edit (Session.ColorIdx,
+ TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR);
+
+ Map.Flag_To_Redraw(false);
+ }
+ } else if ((Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) && !Session.Messages.Is_Edit()) {
+ /*
+ ** For a network game:
+ ** F1-F7 = "To (house):" (only allowed if we're not in ObiWan mode)
+ ** F8 = "To All:"
+ */
+ if (input==(KN_F1 + Session.MaxPlayers - 1)) {
+
+ Session.MessageAddress = IPXAddressClass(); // set to broadcast
+ strcpy(txt, Text_String(TXT_TO_ALL)); // "To All:"
+
+ Session.Messages.Add_Edit(Session.ColorIdx,
+ TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR);
+
+ Map.Flag_To_Redraw(false);
+
+#ifdef WOLAPI_INTEGRATION
+ } else if ((input - KN_F1) < Ipx.Num_Connections() && !Session.ObiWan && input != PAGE_RESPOND_KEY ) {
+#else
+ } else if ((input - KN_F1) < Ipx.Num_Connections() && !Session.ObiWan) {
+#endif
+ id = Ipx.Connection_ID(input - KN_F1);
+ Session.MessageAddress = (*(Ipx.Connection_Address (id)));
+ sprintf(txt, Text_String(TXT_TO), Ipx.Connection_Name(id));
+
+ Session.Messages.Add_Edit(Session.ColorIdx,
+ TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR);
+
+ Map.Flag_To_Redraw(false);
+ }
+#ifdef WOLAPI_INTEGRATION
+ else if( Session.Type == GAME_INTERNET && pWolapi && !pWolapi->bConnectionDown && input == PAGE_RESPOND_KEY )
+ {
+ if( *pWolapi->szExternalPager )
+ {
+ // Respond to a page from external ww online user that paged me.
+ // Set MessageAddress to all zeroes, as a flag to ourselves later on.
+ NetNumType blip;
+ NetNodeType blop;
+ memset( blip, 0, 4 );
+ memset( blop, 0, 6 );
+ Session.MessageAddress = IPXAddressClass( blip, blop );
+
+ // Tell pWolapi not to reset szExternalPager for the time being.
+ pWolapi->bFreezeExternalPager = true;
+
+ sprintf( txt, Text_String( TXT_TO ), pWolapi->szExternalPager );
+
+ Session.Messages.Add_Edit(Session.ColorIdx,
+ TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR);
+
+ Map.Flag_To_Redraw(false);
+
+ Keyboard->Clear();
+ }
+ else
+ {
+ Session.Messages.Add_Message( NULL, 0, TXT_WOL_NOTPAGED, PCOLOR_GOLD, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE );
+ Sound_Effect( VOC_SYS_ERROR );
+ }
+ }
+#endif
+ }
+#if(TEN)
+ /*
+ ** For a TEN game:
+ ** F1-F7 = "To (house):" (only allowed if we're not in ObiWan mode)
+ ** F8 = "To All:"
+ */
+ else if (Session.Type == GAME_TEN && !Session.Messages.Is_Edit()) {
+ if (input==(KN_F1 + Session.MaxPlayers - 1)) {
+
+ Session.TenMessageAddress = -1; // set to broadcast
+ strcpy(txt,Text_String(TXT_TO_ALL)); // "To All:"
+
+ Session.Messages.Add_Edit(Session.ColorIdx,
+ TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR);
+
+ Map.Flag_To_Redraw(false);
+
+ } else if ((input - KN_F1) < Ten->Num_Connections() && !Session.ObiWan) {
+
+ id = Ten->Connection_ID(input - KN_F1);
+ Session.TenMessageAddress = Ten->Connection_Address(id);
+ sprintf(txt,Text_String(TXT_TO),Ten->Connection_Name(id));
+
+ Session.Messages.Add_Edit(Session.ColorIdx,
+ TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR);
+
+ Map.Flag_To_Redraw(false);
+ }
+ }
+#endif // TEN
+#if(MPATH)
+ /*
+ ** For a MPATH game:
+ ** F1-F7 = "To (house):" (only allowed if we're not in ObiWan mode)
+ ** F8 = "To All:"
+ */
+ else if (Session.Type == GAME_MPATH && !Session.Messages.Is_Edit()) {
+ if (input==(KN_F1 + Session.MaxPlayers - 1)) {
+
+ Session.MPathMessageAddress = 0; // set to broadcast
+ strcpy(txt,Text_String(TXT_TO_ALL)); // "To All:"
+
+ Session.Messages.Add_Edit(Session.ColorIdx,
+ TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR);
+
+ Map.Flag_To_Redraw(false);
+
+ } else if ((input - KN_F1) < MPath->Num_Connections() && !Session.ObiWan) {
+
+ id = MPath->Connection_ID(input - KN_F1);
+ Session.MPathMessageAddress = MPath->Connection_Address(id);
+ sprintf(txt,Text_String(TXT_TO),MPath->Connection_Name(id));
+
+ Session.Messages.Add_Edit(Session.ColorIdx,
+ TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR);
+
+ Map.Flag_To_Redraw(false);
+ }
+ }
+#endif // MPATH
+ }
+
+ /*
+ ** Process message-system input; send the message out if RETURN is hit.
+ */
+ copy_input = input;
+ rc = Session.Messages.Input(input);
+
+ /*
+ ** If a single character has been added to an edit buffer, update the display.
+ */
+ if (rc == 1 && Session.Type != GAME_NORMAL) {
+ Map.Flag_To_Redraw(false);
+ }
+
+ /*
+ ** If backspace was hit, redraw the map. If the edit message was removed,
+ ** the map must be force-drawn, since it won't be able to compute the
+ ** cells to redraw; otherwise, let the map compute the cells to redraw,
+ ** by not force-drawing it, but just setting the IsToRedraw bit.
+ */
+ if (rc==2 && Session.Type != GAME_NORMAL) {
+ if (copy_input==KN_ESC) {
+ Map.Flag_To_Redraw(true);
+#ifdef WOLAPI_INTEGRATION
+ if( pWolapi )
+ // Just in case user was responding to a page from outside the game, and we had frozen the "szExternalPager".
+ pWolapi->bFreezeExternalPager = false;
+#endif
+ } else {
+ Map.Flag_To_Redraw(false);
+ }
+ Map.DisplayClass::IsToRedraw = true;
+ }
+
+ /*
+ ** Send a message
+ */
+ if ((rc==3 || rc==4) && Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH) {
+ /*
+ ** Serial game: fill in a SerialPacketType & send it.
+ ** (Note: The size of the SerialPacketType.Command must be the same as
+ ** the EventClass.Type!)
+ */
+ if (Session.Type==GAME_NULL_MODEM || Session.Type==GAME_MODEM) {
+ serial_packet = (SerialPacketType *)NullModem.BuildBuf;
+
+ serial_packet->Command = SERIAL_MESSAGE;
+ strcpy (serial_packet->Name, Session.Players[0]->Name);
+ serial_packet->ID = Session.ColorIdx;
+
+ if (rc==3) {
+ strcpy (serial_packet->Message.Message, Session.Messages.Get_Edit_Buf());
+ } else {
+ strcpy (serial_packet->Message.Message, Session.Messages.Get_Overflow_Buf());
+ Session.Messages.Clear_Overflow_Buf();
+ }
+
+ /*
+ ** Send the message, and store this message in our LastMessage
+ ** buffer; the computer may send us a version of it later.
+ */
+ NullModem.Send_Message(NullModem.BuildBuf,
+ sizeof(SerialPacketType), 1);
+
+#ifdef FIXIT_CSII // checked - ajw 9/28/98
+ char *ptr = &serial_packet->Message.Message[0];
+ if (!strncmp(ptr,"SECRET UNITS ON ",15) && NewUnitsEnabled) {
+ Enable_Secret_Units();
+ }
+#endif
+ strcpy(Session.LastMessage, serial_packet->Message.Message);
+ } else if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) {
+#ifdef WOLAPI_INTEGRATION
+ NetNumType blip;
+ NetNodeType blop;
+ Session.MessageAddress.Get_Address( blip, blop );
+ if( blip[0] + blip[1] + blip[2] + blip[3] + blop[0] + blop[1] + blop[2] + blop[3] + blop[4] + blop[5] == 0 )
+ {
+ // This message is a response to the last person that paged me.
+ if( pWolapi && !pWolapi->bConnectionDown ) // (As connection may have gone down.)
+ {
+ pWolapi->Page( pWolapi->szExternalPager, Session.Messages.Get_Edit_Buf(), false );
+ pWolapi->bFreezeExternalPager = false;
+ }
+ }
+ else
+#endif
+ {
+
+ /*
+ ** Network game: fill in a GlobalPacketType & send it.
+ */
+ Session.GPacket.Command = NET_MESSAGE;
+ strcpy (Session.GPacket.Name, Session.Players[0]->Name);
+ Session.GPacket.Message.Color = Session.ColorIdx;
+ Session.GPacket.Message.NameCRC = Compute_Name_CRC(Session.GameName);
+
+ if (rc==3) {
+ strcpy (Session.GPacket.Message.Buf, Session.Messages.Get_Edit_Buf());
+ } else {
+ strcpy (Session.GPacket.Message.Buf,
+ Session.Messages.Get_Overflow_Buf());
+ Session.Messages.Clear_Overflow_Buf();
+ }
+
+ /*
+ ** If 'F4' was hit, MessageAddress will be a broadcast address; send
+ ** the message to every player we have a connection with.
+ */
+ if (Session.MessageAddress.Is_Broadcast()) {
+ #ifdef FIXIT_CSII // checked - ajw 9/28/98
+ char *ptr = &Session.GPacket.Message.Buf[0];
+ if (!strncmp(ptr,"SECRET UNITS ON ",15) && NewUnitsEnabled) {
+ *ptr = 'X'; // force it to an odd hack so we know it was broadcast.
+ Enable_Secret_Units();
+ }
+ #endif
+ for (i = 0; i < Ipx.Num_Connections(); i++) {
+ Ipx.Send_Global_Message(&Session.GPacket,
+ sizeof(GlobalPacketType), 1,
+ Ipx.Connection_Address(Ipx.Connection_ID(i)));
+ Ipx.Service();
+ }
+ } else {
+
+ /*
+ ** Otherwise, MessageAddress contains the exact address to send to.
+ ** Send to that address only.
+ */
+ Ipx.Send_Global_Message(&Session.GPacket,
+ sizeof(GlobalPacketType), 1,
+ &Session.MessageAddress);
+ Ipx.Service();
+
+ }
+
+ /*
+ ** Store this message in our LastMessage buffer; the computer may send
+ ** us a version of it later.
+ */
+ strcpy(Session.LastMessage, Session.GPacket.Message.Buf);
+ }
+ }
+
+#if(TEN)
+ /*
+ ** TEN game: fill in a GlobalPacketType & send it.
+ */
+ else if (Session.Type == GAME_TEN) {
+ Session.GPacket.Command = NET_MESSAGE;
+ strcpy (Session.GPacket.Name, Session.Players[0]->Name);
+ Session.GPacket.Message.Color = Session.ColorIdx;
+ Session.GPacket.Message.NameCRC = Compute_Name_CRC(Session.GameName);
+
+ if (rc==3) {
+ strcpy (Session.GPacket.Message.Buf, Session.Messages.Get_Edit_Buf());
+ } else {
+ strcpy (Session.GPacket.Message.Buf,
+ Session.Messages.Get_Overflow_Buf());
+ Session.Messages.Clear_Overflow_Buf();
+ }
+
+ Ten->Send_Global_Message(&Session.GPacket, sizeof(GlobalPacketType),
+ 1, Session.TenMessageAddress);
+ }
+#endif // TEN
+
+#if(MPATH)
+ /*
+ ** MPATH game: fill in a GlobalPacketType & send it.
+ */
+ else if (Session.Type == GAME_MPATH) {
+ Session.GPacket.Command = NET_MESSAGE;
+ strcpy (Session.GPacket.Name, Session.Players[0]->Name);
+ Session.GPacket.Message.Color = Session.ColorIdx;
+ Session.GPacket.Message.NameCRC = Compute_Name_CRC(Session.GameName);
+
+ if (rc==3) {
+ strcpy (Session.GPacket.Message.Buf, Session.Messages.Get_Edit_Buf());
+ } else {
+ strcpy (Session.GPacket.Message.Buf,
+ Session.Messages.Get_Overflow_Buf());
+ Session.Messages.Clear_Overflow_Buf();
+ }
+
+ MPath->Send_Global_Message(&Session.GPacket, sizeof(GlobalPacketType),
+ 1, Session.MPathMessageAddress);
+ }
+#endif // MPATH
+
+ /*
+ ** Tell the map to completely update itself, since a message is now missing.
+ */
+ Map.Flag_To_Redraw(true);
+ }
+}
+#pragma on (unreferenced)
+
+
+/***********************************************************************************************
+ * Color_Cycle -- Handle the general palette color cycling. *
+ * *
+ * This is a maintenance routine that handles the color cycling. It should be called as *
+ * often as necessary to achieve smooth color cycling effects -- at least 8 times a second. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/31/1994 JLB : Created. *
+ * 06/10/1994 JLB : Uses new cycle color values. *
+ * 12/21/1994 JLB : Handles text fade color. *
+ * 07/16/1996 JLB : Faster pulsing of white color. *
+ *=============================================================================================*/
+void Color_Cycle(void)
+{
+ static CDTimerClass _timer;
+ static CDTimerClass _ftimer;
+ static bool _up = false;
+ static int val = 255;
+ bool changed = false;
+
+ if (Options.IsPaletteScroll) {
+ /*
+ ** Process the fading white color. It is used for the radar box and other glowing
+ ** game interface elements.
+ */
+ if (!_ftimer) {
+ _ftimer = TIMER_SECOND/6;
+
+ #define STEP_RATE 20
+ if (_up) {
+ val += STEP_RATE;
+ if (val > 150) {
+ val = 150;
+ _up = false;
+ }
+ } else {
+ val -= STEP_RATE;
+ if (val < 0x20) {
+ val = 0x20;
+ _up = true;
+ }
+ }
+
+ /*
+ ** Set the pulse color as the proportional value between white and the
+ ** minimum value for pulsing.
+ */
+ InGamePalette[CC_PULSE_COLOR] = GamePalette[WHITE];
+ InGamePalette[CC_PULSE_COLOR].Adjust(val, BlackColor);
+
+ /*
+ ** Pulse the glowing embers between medium and dark red.
+ */
+ InGamePalette[CC_EMBER_COLOR] = RGBClass(255, 80, 80);
+ InGamePalette[CC_EMBER_COLOR].Adjust(val, BlackColor);
+
+ changed = true;
+ }
+
+ /*
+ ** Process the color cycling effects -- water.
+ */
+ if (!_timer) {
+ _timer = TIMER_SECOND/4;
+
+ RGBClass first = InGamePalette[CYCLE_COLOR_START+CYCLE_COLOR_COUNT-1];
+ for (int index = CYCLE_COLOR_START+CYCLE_COLOR_COUNT-1; index >= CYCLE_COLOR_START; index--) {
+ InGamePalette[index] = InGamePalette[index-1];
+ }
+ InGamePalette[CYCLE_COLOR_START] = first;
+
+ changed = true;
+ }
+
+ /*
+ ** If any of the processing functions changed the palette, then this palette must be
+ ** passed to the system.
+ */
+ if (changed) {
+ BStart(BENCH_PALETTE);
+ InGamePalette.Set();
+// Set_Palette(InGamePalette);
+ BEnd(BENCH_PALETTE);
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * Call_Back -- Main game maintenance callback routine. *
+ * *
+ * This routine handles all the "real time" processing that needs to *
+ * occur. This includes palette fading and sound updating. It needs *
+ * to be called as often as possible. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 10/07/1992 JLB : Created. *
+ *=============================================================================================*/
+void Call_Back(void)
+{
+ /*
+ ** Music and speech maintenance
+ */
+ if (SampleType) {
+ Sound_Callback();
+ Theme.AI();
+ Speak_AI();
+ }
+
+ /*
+ ** Network maintenance.
+ */
+ if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) {
+ IPX_Call_Back();
+ }
+
+ /*
+ ** Serial game maintenance.
+ */
+ if (Session.Type == GAME_NULL_MODEM || ((Session.Type == GAME_MODEM) && Session.ModemService)) {
+ NullModem.Service();
+ }
+
+#ifdef WOLAPI_INTEGRATION
+ // Wolapi maintenance.
+ if( pWolapi )
+ {
+ if( pWolapi->bInGame )
+ {
+ if( !pWolapi->bConnectionDown && ::timeGetTime() > pWolapi->dwTimeNextWolapiPump )
+ {
+ pWolapi->pChat->PumpMessages();
+ pWolapi->pNetUtil->PumpMessages();
+ pWolapi->dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT + 700; // Slower pump during games.
+ if( pWolapi->bConnectionDown )
+ {
+ // Connection to server lost.
+ Session.Messages.Add_Message( NULL, 0, TXT_WOL_WOLAPIGONE, PCOLOR_GOLD, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE );
+ Sound_Effect( WOLSOUND_LOGOUT );
+// ajw (Wolapi object is now left around, so we can try to send game results.)
+// // Kill wolapi.
+// pWolapi->UnsetupCOMStuff();
+// delete pWolapi;
+// pWolapi = NULL;
+ }
+ }
+ }
+ else
+ {
+ // When showing a modal dialog during chat, this pumping is turned on. It's turned off immediately following.
+ if( pWolapi->bPump_In_Call_Back && ( ::timeGetTime() > pWolapi->dwTimeNextWolapiPump ) )
+ {
+ pWolapi->pChat->PumpMessages();
+ pWolapi->pNetUtil->PumpMessages();
+ pWolapi->dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT;
+ }
+ }
+ }
+#endif
+
+#if(TEN)
+ if (Session.Type == GAME_TEN) {
+ TEN_Call_Back();
+ }
+#endif // TEN
+
+#if(MPATH)
+ if (Session.Type == GAME_MPATH) {
+ MPATH_Call_Back();
+ }
+#endif // MPATH
+}
+
+
+void IPX_Call_Back(void)
+{
+ Ipx.Service();
+
+ /*
+ ** Read packets only if the game is "closed", so we don't steal global
+ ** messages from the connection dialogs.
+ */
+ if (!Session.NetOpen) {
+ if (Ipx.Get_Global_Message (&Session.GPacket, &Session.GPacketlen, &Session.GAddress, &Session.GProductID)) {
+
+ if (Session.GProductID == IPXGlobalConnClass::COMMAND_AND_CONQUER0) {
+
+ /*
+ ** If this is another player signing off, remove the connection &
+ ** mark that player's house as non-human, so the computer will take
+ ** it over.
+ */
+ if (Session.GPacket.Command == NET_SIGN_OFF) {
+ for (int i = 0; i < Ipx.Num_Connections(); i++) {
+
+ int id = Ipx.Connection_ID(i);
+
+ if (Session.GAddress == (*Ipx.Connection_Address(id))) {
+ Destroy_Connection(id, 0);
+ }
+ }
+ } else {
+
+ /*
+ ** Process a message from another user.
+ */
+ if (Session.GPacket.Command == NET_MESSAGE) {
+ bool msg_ok = false;
+
+ /*
+ ** If NetProtect is set, make sure this message came from within
+ ** this game.
+ */
+ if (!Session.NetProtect) {
+ msg_ok = true;
+ } else {
+ if (Session.GPacket.Message.NameCRC ==
+ Compute_Name_CRC(Session.GameName)) {
+ msg_ok = true;
+ } else {
+ msg_ok = false;
+ }
+ }
+
+ if (msg_ok) {
+ if (!Session.Messages.Concat_Message(Session.GPacket.Name,
+ Session.GPacket.Message.Color,
+ Session.GPacket.Message.Buf, Rule.MessageDelay * TICKS_PER_MINUTE)) {
+#ifdef FIXIT_CSII // checked - ajw 9/28/98
+ if (NewUnitsEnabled && !strncmp(Session.GPacket.Message.Buf,"XECRET UNITS ON ",15)) {
+ Session.GPacket.Message.Buf[0]='S';
+ Enable_Secret_Units();
+ }
+#endif
+ Session.Messages.Add_Message (Session.GPacket.Name,
+ Session.GPacket.Message.Color,
+ Session.GPacket.Message.Buf,
+ Session.GPacket.Message.Color,
+ TPF_6PT_GRAD | TPF_USE_GRAD_PAL |
+ TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE);
+
+ Sound_Effect(VOC_INCOMING_MESSAGE);
+ }
+
+ /*
+ ** Tell the map to do a partial update (just to force the messages
+ ** to redraw).
+ */
+ Map.Flag_To_Redraw(true);
+
+ /*
+ ** Save this message in our last-message buffer
+ */
+ strcpy(Session.LastMessage, Session.GPacket.Message.Buf);
+ }
+ } else {
+ Process_Global_Packet(&Session.GPacket, &Session.GAddress);
+ }
+ }
+ }
+ }
+ }
+
+}
+
+
+#if(TEN)
+void TEN_Call_Back(void)
+{
+ int id;
+
+ Ten->Service();
+
+ if (Ten->Get_Global_Message (&Session.GPacket, &Session.GPacketlen,
+ &Session.TenAddress)) {
+
+ //
+ // If this is another player signing off, remove the connection &
+ // mark that player's house as non-human, so the computer will take
+ // it over.
+ //
+ if (Session.GPacket.Command == NET_SIGN_OFF) {
+ for (int i = 0; i < Ten->Num_Connections(); i++) {
+
+ id = Ten->Connection_ID(i);
+
+ if (Session.TenAddress == Ten->Connection_Address(id)) {
+ Destroy_TEN_Connection(id, 0);
+ }
+ }
+ }
+
+ //
+ // Process a message from another user.
+ //
+ else if (Session.GPacket.Command == NET_MESSAGE) {
+ if (!Session.Messages.Concat_Message(Session.GPacket.Name,
+ Session.GPacket.Message.Color,
+ Session.GPacket.Message.Buf, Rule.MessageDelay * TICKS_PER_MINUTE)) {
+
+ Session.Messages.Add_Message (Session.GPacket.Name,
+ Session.GPacket.Message.Color,
+ Session.GPacket.Message.Buf,
+ Session.GPacket.Message.Color,
+ TPF_6PT_GRAD | TPF_USE_GRAD_PAL |
+ TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE);
+
+ Sound_Effect(VOC_INCOMING_MESSAGE);
+
+ /*
+ ** Tell the map to do a partial update (just to force the messages
+ ** to redraw).
+ */
+ Map.Flag_To_Redraw(true);
+
+ /*
+ ** Save this message in our last-message buffer
+ */
+ strcpy(Session.LastMessage, Session.GPacket.Message.Buf);
+ }
+ }
+ }
+}
+#endif // TEN
+
+
+#if(MPATH)
+void MPATH_Call_Back(void)
+{
+ int id;
+
+ MPath->Service();
+
+ if (MPath->Get_Global_Message (&Session.GPacket, &Session.GPacketlen,
+ &Session.MPathAddress)) {
+
+ //
+ // If this is another player signing off, remove the connection &
+ // mark that player's house as non-human, so the computer will take
+ // it over.
+ //
+ if (Session.GPacket.Command == NET_SIGN_OFF) {
+ for (int i = 0; i < MPath->Num_Connections(); i++) {
+
+ id = MPath->Connection_ID(i);
+
+ if (Session.MPathAddress == MPath->Connection_Address(id)) {
+ Destroy_MPATH_Connection(id, 0);
+ }
+ }
+ }
+
+ //
+ // Process a message from another user.
+ //
+ else if (Session.GPacket.Command == NET_MESSAGE) {
+ if (!Session.Messages.Concat_Message(Session.GPacket.Name,
+ Session.GPacket.Message.Color,
+ Session.GPacket.Message.Buf, Rule.MessageDelay * TICKS_PER_MINUTE)) {
+
+ Session.Messages.Add_Message (Session.GPacket.Name,
+ Session.GPacket.Message.Color,
+ Session.GPacket.Message.Buf,
+ Session.GPacket.Message.Color,
+ TPF_6PT_GRAD | TPF_USE_GRAD_PAL |
+ TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE);
+
+ Sound_Effect(VOC_INCOMING_MESSAGE);
+
+ /*
+ ** Tell the map to do a partial update (just to force the messages
+ ** to redraw).
+ */
+ Map.Flag_To_Redraw(true);
+
+ /*
+ ** Save this message in our last-message buffer
+ */
+ strcpy(Session.LastMessage, Session.GPacket.Message.Buf);
+ }
+ }
+ }
+}
+#endif // MPATH
+
+
+/***********************************************************************************************
+ * Language_Name -- Build filename for current language. *
+ * *
+ * This routine attaches a language specific suffix to the base *
+ * filename provided. Typical use of this is when loading language *
+ * specific files at game initialization time. *
+ * *
+ * INPUT: basename -- Base name to append language specific *
+ * extension to. *
+ * *
+ * OUTPUT: Returns with pointer to completed filename. *
+ * *
+ * WARNINGS: The return pointer value is valid only until the next time *
+ * this routine is called. *
+ * *
+ * HISTORY: *
+ * 10/07/1992 JLB : Created. *
+ *=============================================================================================*/
+char const * Language_Name(char const * basename)
+{
+ static char _fullname[_MAX_FNAME+_MAX_EXT];
+
+ if (!basename) return(NULL);
+
+ sprintf(_fullname, "%s.ENG", basename);
+ return(_fullname);
+}
+
+
+/***********************************************************************************************
+ * Source_From_Name -- Converts ASCII name into SourceType. *
+ * *
+ * This routine is used to convert an ASCII name representing a *
+ * SourceType into the actual SourceType value. Typically, this is *
+ * used when processing the scenario INI file. *
+ * *
+ * INPUT: name -- The ASCII source name to process. *
+ * *
+ * OUTPUT: Returns with the SourceType represented by the name *
+ * specified. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 04/17/1994 JLB : Created. *
+ *=============================================================================================*/
+SourceType Source_From_Name(char const * name)
+{
+ if (name) {
+ for (SourceType source = SOURCE_FIRST; source < SOURCE_COUNT; source++) {
+ if (stricmp(SourceName[source], name) == 0) {
+ return(source);
+ }
+ }
+ }
+ return(SOURCE_NONE);
+}
+
+
+/***********************************************************************************************
+ * Name_From_Source -- retrieves the name for the given SourceType *
+ * *
+ * INPUT: *
+ * source SourceType to get the name for *
+ * *
+ * OUTPUT: *
+ * name of SourceType *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 11/15/1994 BR : Created. *
+ *=============================================================================================*/
+char const * Name_From_Source(SourceType source)
+{
+ if ((unsigned)source < SOURCE_COUNT) {
+ return(SourceName[source]);
+ }
+ return("None");
+}
+
+
+/***********************************************************************************************
+ * Theater_From_Name -- Converts ASCII name into a theater number. *
+ * *
+ * This routine converts an ASCII representation of a theater and converts it into a *
+ * matching theater number. If no match was found, then THEATER_NONE is returned. *
+ * *
+ * INPUT: name -- Pointer to ASCII name to convert. *
+ * *
+ * OUTPUT: Returns with the name converted into a theater number. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 10/01/1994 JLB : Created. *
+ *=============================================================================================*/
+TheaterType Theater_From_Name(char const * name)
+{
+ TheaterType index;
+
+ if (name) {
+ for (index = THEATER_FIRST; index < THEATER_COUNT; index++) {
+ if (stricmp(name, Theaters[index].Name) == 0) {
+ return(index);
+ }
+ }
+ }
+ return(THEATER_NONE);
+}
+
+
+/***********************************************************************************************
+ * KN_To_Facing -- Converts a keyboard input number into a facing value. *
+ * *
+ * This routine determine which compass direction is represented by the keyboard value *
+ * provided. It is used for map scrolling and other directional control operations from *
+ * the keyboard. *
+ * *
+ * INPUT: input -- The KN number to convert. *
+ * *
+ * OUTPUT: Returns with the facing type that the keyboard number represents. If it could *
+ * not be translated, then FACING_NONE is returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/28/1994 JLB : Created. *
+ *=============================================================================================*/
+FacingType KN_To_Facing(int input)
+{
+ input &= ~(KN_ALT_BIT|KN_SHIFT_BIT|KN_CTRL_BIT);
+ switch (input) {
+ case KN_LEFT:
+ return(FACING_W);
+
+ case KN_RIGHT:
+ return(FACING_E);
+
+ case KN_UP:
+ return(FACING_N);
+
+ case KN_DOWN:
+ return(FACING_S);
+
+ case KN_UPLEFT:
+ return(FACING_NW);
+
+ case KN_UPRIGHT:
+ return(FACING_NE);
+
+ case KN_DOWNLEFT:
+ return(FACING_SW);
+
+ case KN_DOWNRIGHT:
+ return(FACING_SE);
+
+ default:
+ break;
+ }
+ return(FACING_NONE);
+}
+
+
+/***********************************************************************************************
+ * Sync_Delay -- Forces the game into a 15 FPS rate. *
+ * *
+ * This routine will wait until the timer for the current frame has expired before *
+ * returning. It is called at the end of every game loop in order to force the game loop *
+ * to run at a fixed rate. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: This routine will delay an amount of time according to the game speed setting. *
+ * *
+ * HISTORY: *
+ * 01/04/1995 JLB : Created. *
+ * 03/06/1995 JLB : Fixed. *
+ *=============================================================================================*/
+static void Sync_Delay(void)
+{
+ /*
+ ** Accumulate the number of 'spare' ticks that are frittered away here.
+ */
+ SpareTicks += FrameTimer;
+
+ /*
+ ** Delay until the frame timer expires. This forces the game loop to be regulated to a
+ ** speed controlled by the game options slider.
+ */
+ while (FrameTimer) {
+ Color_Cycle();
+ Call_Back();
+
+ if (SpecialDialog == SDLG_NONE) {
+#ifdef WIN32
+ WWMouse->Erase_Mouse(&HidPage, TRUE);
+#endif //WIN32
+ KeyNumType input = KN_NONE;
+ int x, y;
+ Map.Input(input, x, y);
+ if (input) {
+ Keyboard_Process(input);
+ }
+ Map.Render();
+ }
+ }
+ Color_Cycle();
+ Call_Back();
+}
+
+
+/***********************************************************************************************
+ * Main_Loop -- This is the main game loop (as a single loop). *
+ * *
+ * This function will perform one game loop. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: bool; Should the game end? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 10/01/1994 JLB : Created. *
+ *=============================================================================================*/
+#ifdef WIN32
+extern void Check_For_Focus_Loss(void);
+void Reallocate_Big_Shape_Buffer(void);
+#endif //WIN32
+
+
+bool Main_Loop()
+{
+ KeyNumType input; // Player input.
+ int x;
+ int y;
+ int framedelay;
+
+Mono_Set_Cursor(0,0);
+
+ if (!GameActive) return(!GameActive);
+
+#ifdef WIN32
+ /*
+ ** Call the focus loss handler
+ */
+ Check_For_Focus_Loss();
+
+ /*
+ ** Allocate extra memory for uncompressed shapes as needed
+ */
+ Reallocate_Big_Shape_Buffer();
+#endif
+
+ /*
+ ** Sync-bug trapping code
+ */
+ if (Frame >= Session.TrapFrame) {
+ Session.Trap_Object();
+ }
+
+ //
+ // Initialize our AI processing timer
+ //
+ Session.ProcessTimer = TickCount;
+
+#if 1
+ if (Session.TrapCheckHeap) {
+ Debug_Trap_Check_Heap = true;
+ }
+#endif
+
+#ifdef CHEAT_KEYS
+
+ /*
+ ** Update the running status debug display.
+ */
+ Self_Regulate();
+#endif
+
+ BStart(BENCH_GAME_FRAME);
+
+ /*
+ ** If there is no theme playing, but it looks like one is required, then start one
+ ** playing. This is usually the symptom of there being no transition score.
+ */
+ if (SampleType && Theme.What_Is_Playing() == THEME_NONE) {
+ Theme.Queue_Song(THEME_PICK_ANOTHER);
+ }
+
+ /*
+ ** Setup the timer so that the Main_Loop function processes at the correct rate.
+ */
+ if (Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH &&
+ Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
+
+ //
+ // In playback mode, run as fast as possible.
+ //
+ if (Session.Play) {
+ FrameTimer = 0;
+ } else {
+#ifdef FIXIT_VERSION_3
+ if( !Session.DesiredFrameRate )
+ Session.DesiredFrameRate = 60; // A division by zero was happening (very rare).
+#endif
+ framedelay = TIMER_SECOND / Session.DesiredFrameRate;
+ FrameTimer = framedelay;
+ }
+ } else {
+ if (Options.GameSpeed != 0) {
+ FrameTimer = Options.GameSpeed +
+ (PlayerPtr->Difficulty == DIFF_EASY ? 1 : 0) -
+ (PlayerPtr->Difficulty == DIFF_HARD ? 1 : 0);
+ } else {
+ FrameTimer = Options.GameSpeed + (PlayerPtr->Difficulty == DIFF_EASY ? 1 : 0);
+ }
+ }
+
+ /*
+ ** Update the display, unless we're inside a dialog.
+ */
+ if (!Session.Play) {
+#ifdef WIN32
+ if (SpecialDialog == SDLG_NONE && GameInFocus) {
+ WWMouse->Erase_Mouse(&HidPage, TRUE);
+#else
+ if (SpecialDialog == SDLG_NONE) {
+#endif
+ Map.Input(input, x, y);
+ if (input) {
+ Keyboard_Process(input);
+ }
+ Map.Render();
+ }
+ }
+
+ /*
+ ** Save map's position & selected objects, if we're recording the game.
+ */
+ if (Session.Record || Session.Play) {
+ Do_Record_Playback();
+ }
+
+#ifndef SORTDRAW
+ /*
+ ** Sort the map's ground layer by y-coordinate value. This is done
+ ** outside the IsToRedraw check, for the purposes of game sync'ing
+ ** between machines; this way, all machines will sort the Map's
+ ** layer in the same way, and any processing done that's based on
+ ** the order of this layer will remain in sync.
+ */
+ DisplayClass::Layer[LAYER_GROUND].Sort();
+#endif
+
+ /*
+ ** AI logic operations are performed here.
+ */
+ Logic.AI();
+ TimeQuake = false;
+#ifdef FIXIT_CSII // checked - ajw 9/28/98
+ if (!PendingTimeQuake) {
+ TimeQuakeCenter = 0;
+ }
+#endif
+
+ /*
+ ** Manage the inter-player message list. If Manage() returns true, it means
+ ** a message has expired & been removed, and the entire map must be updated.
+ */
+ if (Session.Messages.Manage()) {
+#ifdef WIN32
+ HiddenPage.Clear();
+#else //WIN32
+ HidPage.Clear();
+#endif //WIN32
+ Map.Flag_To_Redraw(true);
+ }
+
+ //
+ // Measure how long it took to process the AI
+ //
+ Session.ProcessTicks += (TickCount - Session.ProcessTimer);
+ Session.ProcessFrames++;
+
+ /*
+ ** Process all commands that are ready to be processed.
+ */
+ Queue_AI();
+
+ /*
+ ** Keep track of elapsed time in the game.
+ */
+ Score.ElapsedTime += TIMER_SECOND / TICKS_PER_SECOND;
+
+ Call_Back();
+
+ /*
+ ** Check for player wins or loses according to global event flag.
+ */
+ if (PlayerWins) {
+
+#ifdef WIN32
+
+ /*
+ ** Send the game statistics to WChat.
+ */
+ if (Session.Type == GAME_INTERNET && !GameStatisticsPacketSent) {
+ Register_Game_End_Time();
+ Send_Statistics_Packet(); // Player just won.
+ }
+
+ WWMouse->Erase_Mouse(&HidPage, TRUE);
+#endif //WIN32
+ PlayerLoses = false;
+ PlayerWins = false;
+ PlayerRestarts = false;
+ Map.Help_Text(TXT_NONE);
+ Do_Win();
+ return(!GameActive);
+ }
+ if (PlayerLoses) {
+#ifdef WIN32
+ /*
+ ** Send the game statistics to WChat.
+ */
+ if (Session.Type == GAME_INTERNET && !GameStatisticsPacketSent) {
+ Register_Game_End_Time();
+ Send_Statistics_Packet(); // Player just lost.
+ }
+
+ WWMouse->Erase_Mouse(&HidPage, TRUE);
+#endif //WIN32
+ PlayerWins = false;
+ PlayerLoses = false;
+ PlayerRestarts = false;
+ Map.Help_Text(TXT_NONE);
+ Do_Lose();
+ return(!GameActive);
+ }
+ if (PlayerRestarts) {
+#ifdef WIN32
+ WWMouse->Erase_Mouse(&HidPage, TRUE);
+#endif //WIN32
+ PlayerWins = false;
+ PlayerLoses = false;
+ PlayerRestarts = false;
+ Map.Help_Text(TXT_NONE);
+ Do_Restart();
+ return(!GameActive);
+ }
+
+#ifdef FIXIT_VERSION_3 // Stalemate games.
+ if( Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH && Session.Players.Count() == 2 &&
+ Scen.bLocalProposesDraw && Scen.bOtherProposesDraw )
+ {
+ // End game in a draw.
+ if (Session.Type == GAME_INTERNET && !GameStatisticsPacketSent) {
+ Register_Game_End_Time();
+ Send_Statistics_Packet();
+ }
+ WWMouse->Erase_Mouse(&HidPage, TRUE);
+ Map.Help_Text(TXT_NONE);
+ Do_Draw();
+ return(!GameActive);
+ }
+#endif
+
+ /*
+ ** The frame logic has been completed. Increment the frame
+ ** counter.
+ */
+ Frame++;
+
+ /*
+ ** Is there a memory trasher altering the map??
+ */
+ if (Debug_Check_Map) {
+ if (!Map.Validate()) {
+ if (WWMessageBox().Process (TEXT_MAP_ERROR, TEXT_STOP, TEXT_CONTINUE)==0) {
+ GameActive = 0;
+ }
+ Map.Validate(); // give debugger a chance to catch it
+ }
+ }
+
+
+
+#ifdef WIN32
+ if (Debug_MotionCapture) {
+ static void ** _array = 0;
+ static int _sequence = 0;
+ static int _seqsize = Rule.MovieTime * TICKS_PER_MINUTE;
+
+ if (_array == NULL) {
+ _array = new void * [_seqsize];
+ memset(_array, '\0', _seqsize * sizeof(void*));
+ }
+
+ if (_array == NULL) {
+ Debug_MotionCapture = false;
+ }
+
+ static GraphicBufferClass temp_page( SeenBuff.Get_Width(),
+ SeenBuff.Get_Height(),
+ NULL,
+ SeenBuff.Get_Width() * SeenBuff.Get_Height());
+
+ int size = SeenBuff.Get_Width() * SeenBuff.Get_Height();
+
+ if (_sequence < _seqsize) {
+ if (_array[_sequence] == NULL) {
+ _array[_sequence] = new char[size];
+ }
+
+ if (_array[_sequence] != NULL) {
+ SeenBuff.Blit(temp_page);
+ memmove(_array[_sequence], temp_page.Get_Buffer(), size);
+ }
+ _sequence++;
+
+ } else {
+ Debug_MotionCapture = false;
+
+ CDFileClass file;
+ file.Cache(200000);
+ char filename[30];
+
+ for (int index = 0; index < _sequence; index++) {
+ memmove(temp_page.Get_Buffer(), _array[index], size);
+ sprintf(filename, "cap%04d.pcx", index);
+ file.Set_Name(filename);
+
+ Write_PCX_File(file, temp_page, & GamePalette);
+ }
+
+ _sequence = 0;
+ }
+ }
+#endif
+
+ BEnd(BENCH_GAME_FRAME);
+
+ Sync_Delay();
+ return(!GameActive);
+}
+
+
+#ifdef SCENARIO_EDITOR
+/***************************************************************************
+ * Map_Edit_Loop -- a mini-main loop for map edit mode only *
+ * *
+ * INPUT: *
+ * *
+ * OUTPUT: *
+ * *
+ * WARNINGS: *
+ * *
+ * HISTORY: *
+ * 10/19/1994 BR : Created. *
+ *=========================================================================*/
+bool Map_Edit_Loop(void)
+{
+ /*
+ ** Redraw the map.
+ */
+ Map.Render();
+
+ /*
+ ** Get user input (keys, mouse clicks).
+ */
+ KeyNumType input;
+
+#ifdef WIN32
+ WWMouse->Erase_Mouse(&HidPage, TRUE);
+#endif //WIN32
+
+ int x;
+ int y;
+ Map.Input(input, x, y);
+
+ /*
+ ** Process keypress.
+ */
+ if (input) {
+ Keyboard_Process(input);
+ }
+
+ Call_Back(); // maintains Theme.AI() for music
+ Color_Cycle();
+
+ return(!GameActive);
+}
+
+
+/***************************************************************************
+ * Go_Editor -- Enables/disables the map editor *
+ * *
+ * INPUT: *
+ * flag true = go into editor mode; false = go into game mode *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 10/19/1994 BR : Created. *
+ *=========================================================================*/
+void Go_Editor(bool flag)
+{
+ /*
+ ** Go into Scenario Editor mode
+ */
+ if (flag) {
+ Debug_Map = true;
+ Debug_Unshroud = true;
+
+ /*
+ ** Un-select any selected objects
+ */
+ Unselect_All();
+
+ /*
+ ** Turn off the sidebar if it's on
+ */
+ Map.Activate(0);
+
+ /*
+ ** Reset the map's Button list for the new mode
+ */
+ Map.Init_IO();
+
+ /*
+ ** Force a complete redraw of the screen
+ */
+#ifdef WIN32
+ HiddenPage.Clear();
+#else
+ HidPage.Clear();
+#endif
+ Map.Flag_To_Redraw(true);
+ Map.Render();
+
+ } else {
+
+ /*
+ ** Go into normal game mode
+ */
+ Debug_Map = false;
+ Debug_Unshroud = false;
+
+ /*
+ ** Un-select any selected objects
+ */
+ Unselect_All();
+
+ /*
+ ** Reset the map's Button list for the new mode
+ */
+ Map.Init_IO();
+
+ /*
+ ** Force a complete redraw of the screen
+ */
+ HidPage.Clear();
+ Map.Flag_To_Redraw(true);
+ Map.Render();
+ }
+}
+
+#endif
+
+
+/***********************************************************************************************
+ * MixFileHandler -- Handles VQ file access. *
+ * *
+ * This routine is called from the VQ player when it needs to access the source file. By *
+ * using this routine it is possible to virtualize the file system. *
+ * *
+ * INPUT: vqa -- Pointer to the VQA handle for this animation. *
+ * *
+ * action-- The requested action to perform. *
+ * *
+ * buffer-- Optional buffer pointer as needed by the type of action. *
+ * *
+ * nbytes-- The number of bytes (if needed) for this operation. *
+ * *
+ * OUTPUT: Returns a value consistent with the action requested. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/04/1995 JLB : Created. *
+ *=============================================================================================*/
+long MixFileHandler(VQAHandle * vqa, long action, void * buffer, long nbytes)
+{
+ CCFileClass * file;
+ long error;
+
+ file = (CCFileClass *)vqa->VQAio;
+
+ /*
+ ** Perform the action specified by the stream command.
+ */
+ switch (action) {
+
+ /*
+ ** VQACMD_READ means read NBytes from the stream and place it in the
+ ** memory pointed to by Buffer.
+ **
+ ** Any error code returned will be remapped by VQA library into
+ ** VQAERR_READ.
+ */
+ case VQACMD_READ:
+ error = (file->Read(buffer, (unsigned short)nbytes) != (unsigned short)nbytes);
+ break;
+
+ /*
+ ** VQACMD_WRITE is analogous to VQACMD_READ.
+ **
+ ** Writing is not allowed to the VQA file, VQA library will remap the
+ ** error into VQAERR_WRITE.
+ */
+ case VQACMD_WRITE:
+ error = 1;
+ break;
+
+ /*
+ ** VQACMD_SEEK asks that you perform a seek relative to the current
+ ** position. NBytes is a signed number, indicating seek direction
+ ** (positive for forward, negative for backward). Buffer has no meaning
+ ** here.
+ **
+ ** Any error code returned will be remapped by VQA library into
+ ** VQAERR_SEEK.
+ */
+ case VQACMD_SEEK:
+ error = (file->Seek(nbytes, SEEK_CUR) == -1);
+ break;
+
+ /*
+ ** VQACMD_OPEN asks that you open your stream for access.
+ */
+ case VQACMD_OPEN:
+ file = new CCFileClass((char *)buffer);
+
+ if (file != NULL && file->Is_Available()) {
+ error = file->Open((char *)buffer, READ);
+
+ if (error != -1) {
+ vqa->VQAio = (unsigned long)file;
+ error = 0;
+ } else {
+ delete file;
+ file = 0;
+ error = 1;
+ }
+ } else {
+ error = 1;
+ }
+ break;
+
+ case VQACMD_CLOSE:
+ file->Close();
+ delete file;
+ file = 0;
+ vqa->VQAio = 0;
+ error = 0;
+ break;
+
+ /*
+ ** VQACMD_INIT means to prepare your stream for reading. This is used for
+ ** certain streams that can't be read immediately upon opening, and need
+ ** further preparation. This operation is allowed to fail; the error code
+ ** will be returned directly to the client.
+ */
+ case VQACMD_INIT:
+
+ /*
+ ** IFFCMD_CLEANUP means to terminate the transaction with the associated
+ ** stream. This is used for streams that can't simply be closed. This
+ ** operation is not allowed to fail; any error returned will be ignored.
+ */
+ case VQACMD_CLEANUP:
+ error = 0;
+ break;
+
+ default:
+ error = 0;
+ break;
+ }
+
+ return(error);
+}
+
+
+void Rebuild_Interpolated_Palette(unsigned char * interpal)
+{
+ for (int y=0; y<255; y++) {
+ for (int x=y+1; x<256; x++) {
+ *(interpal + (y*256+x)) = *(interpal + (x*256+y));
+ }
+ }
+}
+
+
+unsigned char * InterpolatedPalettes[100];
+BOOL PalettesRead;
+unsigned PaletteCounter;
+
+
+/***********************************************************************************************
+ * Load_Interpolated_Palettes -- Loads in any precalculated palettes for hires VQs *
+ * *
+ * *
+ * *
+ * INPUT: Name of palette file *
+ * *
+ * OUTPUT: Number of palettes loaded *
+ * *
+ * WARNINGS: None *
+ * *
+ * HISTORY: *
+ * 5/7/96 9:49AM ST : Created *
+ *=============================================================================================*/
+int Load_Interpolated_Palettes(char const * filename, BOOL add)
+{
+ int num_palettes=0;
+ int i;
+ int start_palette;
+
+ PalettesRead = FALSE;
+ CCFileClass file(filename);
+
+ if (!add) {
+ for (i=0; i < ARRAY_SIZE(InterpolatedPalettes); i++) {
+ InterpolatedPalettes[i]=NULL;
+ }
+ start_palette=0;
+ } else {
+ for (start_palette = 0; start_palette < ARRAY_SIZE(InterpolatedPalettes); start_palette++) {
+ if (!InterpolatedPalettes[start_palette]) break;
+ }
+ }
+
+ /*
+ ** Hack another interpolated palette if the requested one is
+ ** not present.
+ */
+ if (!file.Is_Available()) {
+ file.Set_Name("AAGUN.VQP");
+ }
+
+ if (file.Is_Available()) {
+
+ file.Open(READ);
+ file.Read(&num_palettes , 4);
+
+ for (i=0; i < num_palettes; i++) {
+ InterpolatedPalettes[i+start_palette] = (unsigned char *)malloc (65536);
+ memset (InterpolatedPalettes[i+start_palette], 0, 65536);
+ for (int y = 0; y < 256; y++) {
+ file.Read (InterpolatedPalettes[i+start_palette] + y*256 , y+1);
+ }
+
+ Rebuild_Interpolated_Palette(InterpolatedPalettes[i+start_palette]);
+ }
+
+ PalettesRead = TRUE;
+ file.Close();
+ }
+ PaletteCounter = 0;
+ return (num_palettes);
+}
+
+
+void Free_Interpolated_Palettes(void)
+{
+ for (int i = 0; i < ARRAY_SIZE(InterpolatedPalettes) ;i++) {
+ if (InterpolatedPalettes[i]) {
+ free(InterpolatedPalettes[i]);
+ InterpolatedPalettes[i]=NULL;
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * Play_Movie -- Plays a VQ movie. *
+ * *
+ * Use this routine to play a VQ movie. It will dispatch the specified movie to the *
+ * VQ player. The routine will not return until the movie has finished playing. *
+ * *
+ * INPUT: name -- The name of the movie file (sans ".VQA"). *
+ * *
+ * theme -- The identifier for an optional theme that should be played in the *
+ * background while this VQ plays. *
+ * *
+ * clrscrn -- 'true' if to clear the screen when the movie is over *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 12/19/1994 JLB : Created. *
+ *=============================================================================================*/
+#ifdef WIN32
+extern void Suspend_Audio_Thread(void);
+extern void Resume_Audio_Thread(void);
+
+#ifdef MOVIE640
+extern GraphicBufferClass VQ640;
+#endif
+#endif
+void Play_Movie(char const * name, ThemeType theme, bool clrscrn)
+ {
+ #ifdef MPEGMOVIE
+ //theme = theme;
+ //clrscrn = clrscrn;
+ if( Using_DVD() )
+ {
+ if (PlayMpegMovie(name))
+ return;
+ }
+ #endif
+
+ #ifdef CHEAT_KEYS
+ // Mono_Printf("Movie: %s\n", name);
+ #endif //CHEAT_KEYS
+ /*
+ ** Don't play movies in editor mode
+ */
+ if (Debug_Map) {
+ return;
+ }
+ #ifdef CHEAT_KEYS
+ // Mono_Printf("A\n");
+ #endif //CHEAT_KEYS
+ /*
+ ** Don't play movies in multiplayer mode
+ */
+ if (Session.Type != GAME_NORMAL) {
+ return;
+ }
+ #ifdef CHEAT_KEYS
+ //Mono_Printf("b\n");
+ #endif //CHEAT_KEYS
+
+ if (name) {
+ char fullname[_MAX_FNAME+_MAX_EXT];
+ _makepath(fullname, NULL, NULL, name, ".VQA");
+ #ifdef WIN32
+ char palname [_MAX_FNAME+_MAX_EXT];
+ _makepath(palname , NULL, NULL, name, ".VQP");
+ #endif //WIN32
+ #ifdef CHEAT_KEYS
+ // Mono_Set_Cursor(0, 0);Mono_Printf("[%s]", fullname);
+ #endif
+
+ if (!CCFileClass(fullname).Is_Available()){
+ #ifdef CHEAT_KEYS
+ // Mono_Printf("fullname: %s\n", fullname);
+ #endif //CHEAT_KEYS
+ return;
+ }
+ /*
+ ** Reset the anim control structure.
+ */
+ Anim_Init();
+
+ /*
+ ** Prepare to play a movie. First hide the mouse and stop any score that is playing.
+ ** While the score (if any) is fading to silence, fade the palette to black as well.
+ ** When the palette has finished fading, wait until the score has finished fading
+ ** before launching the movie.
+ */
+ Hide_Mouse();
+ Theme.Queue_Song(theme);
+ if (PreserveVQAScreen == 0 && !clrscrn) {
+ BlackPalette.Set(FADE_PALETTE_MEDIUM);
+ VisiblePage.Clear();
+ BlackPalette.Adjust(0x08, WhitePalette);
+ BlackPalette.Set();
+ BlackPalette.Adjust(0xFF);
+ BlackPalette.Set();
+ }
+ PreserveVQAScreen = 0;
+ Keyboard->Clear();
+
+ VQAHandle * vqa = NULL;
+
+
+ #ifdef WIN32
+ #ifdef MOVIE640
+ if(IsVQ640) {
+ AnimControl.ImageWidth = 640;
+ AnimControl.ImageHeight = 400;
+ AnimControl.ImageBuf = (unsigned char *)VQ640.Get_Offset();
+ } else {
+ AnimControl.ImageWidth = 320;
+ AnimControl.ImageHeight = 200;
+ AnimControl.ImageBuf = (unsigned char *)SysMemPage.Get_Offset();
+ }
+ #endif
+ #endif
+
+ if (!Debug_Quiet && Get_Digi_Handle() != -1) {
+ AnimControl.OptionFlags |= VQAOPTF_AUDIO;
+ } else {
+ AnimControl.OptionFlags &= ~VQAOPTF_AUDIO;
+ }
+
+ if ((vqa = VQA_Alloc()) != NULL) {
+ VQA_Init(vqa, MixFileHandler);
+
+ if (VQA_Open(vqa, fullname, &AnimControl) == 0) {
+ Brokeout = false;
+ #ifdef WIN32
+ //Suspend_Audio_Thread();
+ #ifdef MOVIE640
+ if(!IsVQ640) {
+ Load_Interpolated_Palettes(palname);
+ }
+ #else
+ Load_Interpolated_Palettes(palname);
+ #endif
+ //Set_Palette(BlackPalette);
+ SysMemPage.Clear();
+ InMovie = true;
+ #endif //WIN32
+ VQA_Play(vqa, VQAMODE_RUN);
+ VQA_Close(vqa);
+ #ifdef WIN32
+ //Resume_Audio_Thread();
+ InMovie = FALSE;
+ #ifdef MOVIE640
+ if(!IsVQ640) {
+ Free_Interpolated_Palettes();
+ }
+ #else
+ Free_Interpolated_Palettes();
+ #endif
+ IsVQ640 = false;
+ Set_Primary_Buffer_Format();
+ #endif //WIN32
+
+ /*
+ ** Any movie that ends prematurely must have the screen
+ ** cleared to avoid any unexpected palette glitches.
+ */
+ if (Brokeout) {
+ clrscrn = true;
+ VisiblePage.Clear();
+ Brokeout = false;
+ }
+ } else {
+ #ifndef NDEBUG
+ bool error = true;
+ assert(error);
+ #endif
+ }
+
+ VQA_Free(vqa);
+ } else {
+ assert(vqa != NULL);
+ }
+ #ifdef CHEAT_KEYS
+ //Mono_Printf("d");
+ #endif //CHEAT_KEYS
+ /*
+ ** Presume that the screen is left in a garbage state as well as the palette
+ ** being in an unknown condition. Recover from this by clearing the screen and
+ ** forcing the palette to black.
+ */
+ if (clrscrn) {
+ VisiblePage.Clear();
+ BlackPalette.Adjust(0x08, WhitePalette);
+ BlackPalette.Set();
+ BlackPalette.Adjust(0xFF);
+ BlackPalette.Set();
+ }
+ Show_Mouse();
+ }
+}
+
+
+void Play_Movie(VQType name, ThemeType theme, bool clrscrn)
+{
+ if (name != VQ_NONE) {
+ if (name == VQ_REDINTRO) {
+ IsVQ640 = true;
+ }
+ Play_Movie(VQName[name], theme, clrscrn);
+ IsVQ640 = false;
+ }
+}
+
+
+// Denzil 5/18/98 - Mpeg movie playback
+#ifdef MPEGMOVIE
+extern LPDIRECTDRAWPALETTE PalettePtr;
+
+bool PlayMpegMovie(const char* name)
+ {
+ char path[MAX_PATH];
+ CCFileClass file;
+ const char* filename;
+
+#ifdef CHEAT_KEYS
+ if( bNoMovies )
+ return true;
+#endif
+
+ sprintf(path, "movies\\%.8s.%.3s", name, "mpg");
+ filename = file.Set_Name(path);
+
+ if (!file.Is_Available())
+ {
+ #if(1)
+ VisiblePage.Clear();
+ GamePalette.Set();
+ Show_Mouse();
+ sprintf(path, "Couldn't find %s\n", filename);
+ WWMessageBox().Process(path);
+ #endif
+ return false;
+ }
+
+ // Stop theme music
+ if (Misc_Focus_Loss_Function)
+ Misc_Focus_Loss_Function();
+
+ // Release primary surface
+ VisiblePage.Un_Init();
+
+ #ifdef MCIMPEG
+ if (MciMovie && MpgSettings && (MpgSettings->GetDeviceName() != NULL))
+ {
+ DirectDrawObject->SetCooperativeLevel(MainWindow, DDSCL_NORMAL);
+
+ if (!MciMovie->Open(filename, MpgSettings->GetDeviceName()))
+ {
+ WWMessageBox().Process("Couldn't open movie.\n");
+ }
+ else if (!MciMovie->Play(MainWindow))
+ {
+ WWMessageBox().Process("Couldn't play movie.\n");
+ }
+
+ DirectDrawObject->SetCooperativeLevel(MainWindow, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
+ }
+ else
+ #endif
+ {
+ DDSURFACEDESC ddsd;
+ IDirectDrawSurface* primary = NULL;
+ bool modeChange = false;
+ RECT rect;
+
+ if (FAILED(DirectDrawObject->SetDisplayMode(ScreenWidth, ScreenHeight, 16)))
+ {
+ WWMessageBox().Process("Couldn't change display mode.\n");
+ }
+ else
+ {
+ // Create primary surface reference
+ memset(&ddsd, 0, sizeof(ddsd));
+ ddsd.dwSize = sizeof(ddsd);
+ ddsd.dwFlags = DDSD_CAPS;
+ ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
+
+ if (FAILED(DirectDrawObject->CreateSurface(&ddsd, &primary, NULL)))
+ {
+ WWMessageBox().Process("Couldn't create primary movie surface.\n");
+ }
+ else
+ {
+ rect.top = rect.left = 0;
+ rect.bottom = ScreenHeight;
+ rect.right = ScreenWidth;
+
+ MpgSetCallback(MpegCallback, NULL);
+ MpgPlay(filename, DirectDrawObject, primary, &rect);
+
+ if (primary)
+ primary->Release();
+
+ }
+
+ DirectDrawObject->SetDisplayMode(ScreenWidth, ScreenHeight, 8);
+ }
+ }
+
+ // Restore surfaces
+ VisiblePage.Init(ScreenWidth, ScreenHeight, NULL, 0, (GBC_Enum)(GBC_VISIBLE|GBC_VIDEOMEM));
+ PaletteSurface->SetPalette(PalettePtr);
+ AllSurfaces.Set_Surface_Focus(true);
+ AllSurfaces.Restore_Surfaces();
+ return true;
+ }
+
+
+MPG_RESPONSE far __stdcall MpegCallback(MPG_CMD cmd, LPVOID data, LPVOID user)
+ {
+ static IDirectDrawPalette* _palette = NULL;
+
+ user = user;
+
+ switch (cmd)
+ {
+ case MPGCMD_ERROR:
+ WWMessageBox().Process((char const *)data);
+ break;
+
+ case MPGCMD_INIT:
+ VisiblePage.Clear();
+ break;
+
+ case MPGCMD_CLEANUP:
+ VisiblePage.Clear();
+
+ if (_palette != NULL)
+ {
+ PaletteSurface->SetPalette(_palette);
+ _palette->Release();
+ _palette = NULL;
+ }
+ break;
+
+ case MPGCMD_PALETTE:
+ if (FAILED(PaletteSurface->GetPalette(&_palette)))
+ {
+ WWMessageBox().Process("Couldn't get primary palette.\n");
+ }
+ else
+ {
+ if (FAILED(PaletteSurface->SetPalette((IDirectDrawPalette*)data)))
+ {
+ WWMessageBox().Process("Couldn't set movie palette.\n");
+ }
+ }
+ break;
+
+ case MPGCMD_UPDATE:
+ if ((BreakoutAllowed || Debug_Flag) && Keyboard->Check())
+ {
+ if (Keyboard->Get() == KN_ESC)
+ {
+ Keyboard->Clear();
+ return MPGRES_QUIT;
+ }
+
+ Keyboard->Clear();
+ }
+
+ if (!GameInFocus)
+ {
+ MpgPause();
+
+ while (!GameInFocus)
+ {
+ Check_For_Focus_Loss();
+ }
+
+ MpgResume();
+ return MPGRES_LOSTFOCUS;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return MPGRES_CONTINUE;
+ }
+#endif
+
+/***********************************************************************************************
+ * Unselect_All -- Causes all selected objects to become unselected. *
+ * *
+ * This routine will unselect all objects that are currently selected. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 01/19/1995 JLB : Created. *
+ *=============================================================================================*/
+void Unselect_All(void)
+{
+ while (CurrentObject.Count()) {
+ CurrentObject[0]->Unselect();
+ }
+}
+
+
+/***********************************************************************************************
+ * Fading_Table_Name -- Builds a theater specific fading table name. *
+ * *
+ * This routine builds a standard fading table name. This name is dependant on the theater *
+ * being played, since each theater has its own palette. *
+ * *
+ * INPUT: base -- The base name of this fading table. The base name can be no longer than *
+ * seven characters. *
+ * *
+ * theater -- The theater that this fading table is specific to. *
+ * *
+ * OUTPUT: Returns with a pointer to the constructed fading table filename. This pointer is *
+ * valid until this function is called again. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 01/19/1995 JLB : Created. *
+ *=============================================================================================*/
+char const * Fading_Table_Name(char const * base, TheaterType theater)
+{
+ static char _buffer[_MAX_FNAME+_MAX_EXT];
+ char root[_MAX_FNAME];
+
+ sprintf(root, "%1.1s%s", Theaters[theater].Root, base);
+ _makepath(_buffer, NULL, NULL, root, ".MRF");
+ return(_buffer);
+}
+
+
+/***********************************************************************************************
+ * Get_Radar_Icon -- Builds and alloc a radar icon from a shape file *
+ * *
+ * INPUT: void const * shapefile - pointer to a key framed shapefile *
+ * int shapenum - shape to extract from shapefile *
+ * *
+ * OUTPUT: void const * - 3/3 icon set of shape from file *
+ * *
+ * HISTORY: *
+ * 04/12/1995 PWG : Created. *
+ * 05/10/1995 JLB : Handles a null shapefile pointer. *
+ *=============================================================================================*/
+void const * Get_Radar_Icon(void const * shapefile, int shapenum, int frames, int zoomfactor)
+{
+ static int _offx[]={ 0, 0, -1, 1, 0, -1, 1, -1, 1};
+ static int _offy[]={ 0, 0, -1, 1, 0, -1, 1, -1, 1};
+ int lp,framelp;
+ char pixel;
+
+ char * retval = NULL;
+ char * buffer = NULL;
+
+ /*
+ ** If there is no shape file, then there can be no radar icon imagery.
+ */
+ if (!shapefile) return(NULL);
+
+#if (0)
+ CCPalette.Set();
+ Set_Logic_Page(SeenBuff);
+ CC_Draw_Shape(shapefile, shapenum, 64, 64, WINDOW_MAIN, SHAPE_WIN_REL);
+#endif
+
+ /*
+ ** Get the pixel width and height of the frame we built. This will
+ ** be used to extract icons and build pixels.
+ */
+ int pixel_width = Get_Build_Frame_Width( shapefile );
+ int pixel_height = Get_Build_Frame_Height( shapefile );
+
+ /*
+ ** Find the width and height in icons, adjust these by half an
+ ** icon because the artists may be sloppy and miss the edge of an
+ ** icon one way or the other.
+ */
+ int icon_width = (pixel_width + 12) / 24;
+ int icon_height = (pixel_height + 12) / 24;
+
+ /*
+ ** If we have been told to build as many frames as possible, then
+ ** find out how many frames there are to build.
+ */
+ if (frames == -1) frames = Get_Build_Frame_Count( shapefile );
+
+ /*
+ ** Allocate a position to store our icons. If the alloc fails then
+ ** we don't add these icons to the set.
+ **/
+ buffer = new char[(icon_width * icon_height * 9 * frames)+2];
+ if (!buffer) return(NULL);
+
+ /*
+ ** Save off the return value so that we can return it to the calling
+ ** function.
+ */
+ retval = (char *)buffer;
+ *buffer++ = (char)icon_width;
+ *buffer++ = (char)icon_height;
+ int val = 24/zoomfactor;
+
+ for (framelp = 0; framelp < frames; framelp ++) {
+ /*
+ ** Build the current frame. If the frame can not be built then we
+ ** just need to skip past this set of icons and try to build the
+ ** next frame.
+ */
+#ifdef WIN32
+ void * ptr;
+ if ((ptr = (void *)(Build_Frame(shapefile, shapenum + framelp, SysMemPage.Get_Buffer()))) != NULL) {
+ ptr = Get_Shape_Header_Data(ptr);
+#else //WIN#@
+ if (Build_Frame(shapefile, shapenum + framelp, HidPage.Get_Buffer()) <= (unsigned long)HidPage.Get_Size() ) {
+#endif //WIN32
+
+ /*
+ ** Loop through the icon width and the icon height building icons
+ ** into the buffer pointer. When the getx or gety falls outside of
+ ** the width and height of the shape, just insert transparent pixels.
+ */
+ for (int icony = 0; icony < icon_height; icony ++) {
+ for (int iconx = 0; iconx < icon_width; iconx ++) {
+#ifdef WIN32
+
+ for (int y = 0; y < zoomfactor; y++) {
+ for (int x = 0; x < zoomfactor; x++) {
+ int getx = (iconx * 24) + (x * val) + (zoomfactor / 2);
+ int gety = (icony * 24) + (y * val) + (zoomfactor / 2);
+ if ((getx < pixel_width) && (gety < pixel_height)) {
+ for (lp = 0; lp < 9; lp ++) {
+ pixel = *(char *)((char *)ptr + ((gety - _offy[lp]) * pixel_width) + getx-_offx[lp]);
+
+#else //WIN32
+ for (int y = 0; y < 3; y++) {
+ for (int x = 0; x < 3; x++) {
+ int getx = (iconx * 24) + (x << 3) + 4;
+ int gety = (icony * 24) + (y << 3) + 4;
+ if ((getx < pixel_width) && (gety < pixel_height)) {
+ for (lp = 0; lp < 9; lp ++) {
+ pixel = *(char *)((char *)HidPage.Get_Buffer(), ((gety - _offy[lp]) * pixel_width) + getx-_offx[lp]);
+#endif //WIN32
+ if (pixel == LTGREEN) pixel = 0;
+ if (pixel) {
+ break;
+ }
+ }
+ *buffer++ = pixel;
+ } else {
+ *buffer++ = 0;
+ }
+ }
+ }
+ }
+ }
+ } else {
+ buffer += icon_width * icon_height * 9;
+ }
+ }
+ return(retval);
+}
+
+
+/***********************************************************************************************
+ * CC_Draw_Shape -- Custom draw shape handler. *
+ * *
+ * All draw shape calls will route through this function. It handles all draws for *
+ * C&C. Such draws always occur to the logical page and assume certain things about *
+ * the parameters passed. *
+ * *
+ * INPUT: shapefile -- Pointer to the shape data file. This data file contains all the *
+ * embedded shapes. *
+ * *
+ * shapenum -- The shape number within the shapefile that will be drawn. *
+ * *
+ * x,y -- The pixel coordinates to draw the shape. *
+ * *
+ * window -- The clipping window to use. *
+ * *
+ * flags -- The custom draw shape flags. This controls how the parameters *
+ * are used (if any). *
+ * *
+ * fadingdata -- If SHAPE_FADING is desired, then this points to the fading *
+ * data table. *
+ * *
+ * ghostdata -- If SHAPE_GHOST is desired, then this points to the ghost remap *
+ * table. *
+ * *
+ * rotation -- Rotation to apply to the shape (DIR_N = no rotation at all). *
+ * *
+ * scale -- 24.8 fixed point scale factor. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 02/21/1995 JLB : Created. *
+ *=============================================================================================*/
+void CC_Draw_Shape(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata, void const * ghostdata, DirType rotation, long scale)
+{
+ int predoffset;
+#ifdef WIN32
+ unsigned long shape_pointer;
+#endif //WIN32
+
+
+ /*
+ ** Special kludge for E3 to prevent crashes
+ */
+ if ((flags & SHAPE_GHOST) && (!ghostdata)) {
+ ghostdata = DisplayClass::SpecialGhost;
+ }
+ if ((flags & SHAPE_FADING) && (!fadingdata)) {
+ fadingdata = DisplayClass::FadingShade;
+ }
+
+ static unsigned char * _xbuffer = 0;
+
+ if (!_xbuffer) {
+ _xbuffer = new unsigned char[SHAPE_BUFFER_SIZE];
+ }
+
+ if (shapefile != NULL && shapenum != -1) {
+
+ int width = Get_Build_Frame_Width(shapefile);
+ int height = Get_Build_Frame_Height(shapefile);
+
+#ifdef NEVER
+ /*
+ ** Perform a quick clip check against the destination rectangle.
+ */
+ if (flags & SHAPE_CENTER) {
+ if (x-width/2 >= WindowList[window][WINDOWWIDTH]) return;
+ if (y-width/2 >= WindowList[window][WINDOWHEIGHT]) return;
+ if (x+width/2 < 0) return;
+ if (y+height/2 < 0) return;
+
+ } else {
+ if (x >= WindowList[window][WINDOWWIDTH]) return;
+ if (y >= WindowList[window][WINDOWHEIGHT]) return;
+ if (x+width < 0) return;
+ if (y+height < 0) return;
+ }
+#endif
+
+
+#ifdef WIN32
+ /*
+ ** In WIn95, build shape returns a pointer to the shape not its size
+ */
+ shape_pointer = Build_Frame(shapefile, shapenum, _ShapeBuffer);
+ if (shape_pointer) {
+ GraphicViewPortClass draw_window(LogicPage->Get_Graphic_Buffer(),
+ WindowList[window][WINDOWX] + LogicPage->Get_XPos(),
+ WindowList[window][WINDOWY] + LogicPage->Get_YPos(),
+ WindowList[window][WINDOWWIDTH],
+ WindowList[window][WINDOWHEIGHT]);
+ unsigned char * buffer = (unsigned char *) shape_pointer; //Get_Shape_Header_Data((void*)shape_pointer);
+
+#else //WIN32
+ if ( Build_Frame(shapefile, shapenum, _ShapeBuffer ) <= (unsigned long)_ShapeBufferSize) {
+ GraphicViewPortClass draw_window(LogicPage,
+ WindowList[window][WINDOWX],
+ WindowList[window][WINDOWY],
+ WindowList[window][WINDOWWIDTH],
+ WindowList[window][WINDOWHEIGHT]);
+ unsigned char * buffer = (unsigned char *)_ShapeBuffer;
+#endif //WIN32
+
+ UseOldShapeDraw = false;
+ /*
+ ** Rotation and scale handler.
+ */
+ if (rotation != DIR_N || scale != 0x0100) {
+
+ /*
+ ** Get the raw shape data without the new header and flag to use the old shape drawing
+ */
+ UseOldShapeDraw = true;
+#ifdef WIN32
+ buffer = (unsigned char *) Get_Shape_Header_Data((void*)shape_pointer);
+#endif
+
+ if (Debug_Rotate) {
+
+ GraphicBufferClass src(width, height, buffer);
+ width *= 2;
+ height *= 2;
+ memset(_xbuffer, '\0', SHAPE_BUFFER_SIZE);
+ GraphicBufferClass dst(width, height, _xbuffer);
+ Rotate_Bitmap(&src, &dst, rotation);
+ buffer = _xbuffer;
+
+ } else {
+
+ BitmapClass bm(width, height, buffer);
+ width *= 2;
+ height *= 2;
+ memset(_xbuffer, '\0', SHAPE_BUFFER_SIZE);
+ GraphicBufferClass gb(width, height, _xbuffer);
+ TPoint2D pt(width/2, height/2);
+
+ gb.Scale_Rotate(bm, pt, scale, (256-(rotation-64)));
+ buffer = _xbuffer;
+ }
+ }
+
+ /*
+ ** Special shadow drawing code (used for aircraft and bullets).
+ */
+ if ((flags & (SHAPE_FADING|SHAPE_PREDATOR)) == (SHAPE_FADING|SHAPE_PREDATOR)) {
+ flags = flags & ~(SHAPE_FADING|SHAPE_PREDATOR);
+ flags = flags | SHAPE_GHOST;
+ ghostdata = DisplayClass::SpecialGhost;
+ }
+
+ predoffset = Frame;
+
+ if (x > ( WindowList[window][WINDOWWIDTH] << 2)) {
+ predoffset = -predoffset;
+ }
+
+ if (draw_window.Lock()) {
+ if ((flags & (SHAPE_GHOST|SHAPE_FADING)) == (SHAPE_GHOST|SHAPE_FADING)) {
+ Buffer_Frame_To_Page(x, y, width, height, buffer, draw_window, flags | SHAPE_TRANS, ghostdata, fadingdata, 1, predoffset);
+ } else {
+ if (flags & SHAPE_FADING) {
+ Buffer_Frame_To_Page(x, y, width, height, buffer, draw_window, flags | SHAPE_TRANS, fadingdata, 1, predoffset);
+ } else {
+ if (flags & SHAPE_PREDATOR) {
+ Buffer_Frame_To_Page(x, y, width, height, buffer, draw_window, flags | SHAPE_TRANS, predoffset);
+ } else {
+ Buffer_Frame_To_Page(x, y, width, height, buffer, draw_window, flags | SHAPE_TRANS, ghostdata, predoffset);
+ }
+ }
+ }
+ draw_window.Unlock();
+ }
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * Shape_Dimensions -- Determine the minimum rectangle for the shape. *
+ * *
+ * This routine will calculate (using brute forced) the minimum rectangle that will *
+ * enclose the pixels of the shape. This rectangle will be relative to the upper left *
+ * corner of the maximum shape size. By using this minimum rectangle, it is possible to *
+ * greatly optimize the map 'dirty rectangle' logic. *
+ * *
+ * INPUT: shapedata -- Pointer to the shape data block. *
+ * *
+ * shapenum -- The shape number to examine. Each shape would have a different *
+ * dimension rectangle. *
+ * *
+ * OUTPUT: Returns with the rectangle that encloses the shape. *
+ * *
+ * WARNINGS: This routine uses brute force and is slow. It is presumed that the results *
+ * will be cached for subsiquent reuse. *
+ * *
+ * HISTORY: *
+ * 07/22/1996 JLB : Created. *
+ *=============================================================================================*/
+Rect const Shape_Dimensions(void const * shapedata, int shapenum)
+{
+ Rect rect;
+
+ if (shapedata == NULL || shapenum < 0 || shapenum > Get_Build_Frame_Count(shapedata)) {
+ return(rect);
+ }
+
+ char * shape;
+#ifdef WIN32
+ void * sh = (void *)Build_Frame(shapedata, shapenum, _ShapeBuffer);
+ if (sh == NULL) return(rect);
+// shape = (char *)sh;
+ shape = (char *)Get_Shape_Header_Data(sh);
+#else
+ Build_Frame(shapedata, shapenum, _ShapeBuffer);
+ shape = (char *)_ShapeBuffer;
+#endif
+
+ int width = Get_Build_Frame_Width(shapedata);
+ int height = Get_Build_Frame_Height(shapedata);
+
+ rect.X = 0;
+ rect.Y = 0;
+ int xlimit = width-1;
+ int ylimit = height-1;
+
+ /*
+ ** Find top edge of the shape.
+ */
+ for (int y = 0; y <= ylimit; y++) {
+ for (int x = 0; x <= xlimit; x++) {
+ if (shape[y*width + x] != 0) {
+ rect.Y = y;
+ rect.X = x;
+ y = ylimit+1;
+ break;
+ }
+ }
+ }
+
+ /*
+ ** Find bottom edge of the shape.
+ */
+ for (y = ylimit; y >= rect.Y; y--) {
+ for (int x = xlimit; x >= 0; x--) {
+ if (shape[y*width + x] != 0) {
+ rect.Height = (y-rect.Y)+1;
+ xlimit = x;
+ y = rect.Y-1;
+ break;
+ }
+ }
+ }
+
+ /*
+ ** Find left edge of the shape.
+ */
+ for (int x = 0; x < rect.X; x++) {
+ for (y = rect.Y; y < rect.Y+rect.Height; y++) {
+ if (shape[y*width + x] != 0) {
+ rect.X = x;
+ x = rect.X;
+ break;
+ }
+ }
+ }
+
+ /*
+ ** Find the right edge of the shape.
+ */
+ for (x = width-1; x >= xlimit; x--) {
+ for (y = rect.Y; y < rect.Y+rect.Height; y++) {
+ if (shape[y*width + x] != 0) {
+ rect.Width = (x-rect.X)+1;
+ x = xlimit-1;
+ break;
+ }
+ }
+ }
+
+ /*
+ ** Normalize the rectangle around the center of the shape.
+ */
+ rect.X -= width / 2;
+ rect.Y -= height / 2;
+
+ /*
+ ** Return with the minimum rectangle that encloses the shape.
+ */
+ return(rect);
+}
+
+
+/***********************************************************************************************
+ * Fetch_Techno_Type -- Convert type and ID into TechnoTypeClass pointer. *
+ * *
+ * This routine will convert the supplied RTTI type number and the ID value into a valid *
+ * TechnoTypeClass pointer. If there is an error in conversion, then NULL is returned. *
+ * *
+ * INPUT: type -- RTTI type of the techno class object. *
+ * *
+ * id -- Integer representation of the techno sub type number. *
+ * *
+ * OUTPUT: Returns with a pointer to the techno type class object specified or NULL if the *
+ * conversion could not occur. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/08/1995 JLB : Created. *
+ *=============================================================================================*/
+TechnoTypeClass const * Fetch_Techno_Type(RTTIType type, int id)
+{
+ switch (type) {
+ case RTTI_UNITTYPE:
+ case RTTI_UNIT:
+ return(&UnitTypeClass::As_Reference(UnitType(id)));
+
+ case RTTI_VESSELTYPE:
+ case RTTI_VESSEL:
+ return(&VesselTypeClass::As_Reference(VesselType(id)));
+
+ case RTTI_BUILDINGTYPE:
+ case RTTI_BUILDING:
+ return(&BuildingTypeClass::As_Reference(StructType(id)));
+
+ case RTTI_INFANTRYTYPE:
+ case RTTI_INFANTRY:
+ return(&InfantryTypeClass::As_Reference(InfantryType(id)));
+
+ case RTTI_AIRCRAFTTYPE:
+ case RTTI_AIRCRAFT:
+ return(&AircraftTypeClass::As_Reference(AircraftType(id)));
+
+ default:
+ break;
+ }
+ return(NULL);
+}
+
+
+/***********************************************************************************************
+ * VQ_Call_Back -- Maintenance callback used for VQ movies. *
+ * *
+ * This routine is called every frame of the VQ movie as it is being played. If this *
+ * routine returns non-zero, then the movie will stop. *
+ * *
+ * INPUT: buffer -- Pointer to the image buffer for the current frame. *
+ * *
+ * frame -- The frame number about to be displayed. *
+ * *
+ * OUTPUT: Should the movie be stopped? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/24/1995 JLB : Created. *
+ *=============================================================================================*/
+#ifdef WIN32
+void VQA_PauseAudio(void);
+void Check_VQ_Palette_Set(void);
+
+extern GraphicBufferClass VQ640;
+extern bool IsVQ640;
+long VQ_Call_Back(unsigned char *, long )
+{
+ int key = 0;
+ if (Keyboard->Check()) {
+ key = Keyboard->Get();
+ Keyboard->Clear();
+ }
+ Check_VQ_Palette_Set();
+#ifdef MOVIE640
+ if(IsVQ640) {
+ VQ640.Blit(SeenBuff);
+ } else {
+ Interpolate_2X_Scale(&SysMemPage, &SeenBuff, NULL);
+ }
+#else
+ Interpolate_2X_Scale(&SysMemPage, &SeenBuff, NULL);
+#endif
+ //Call_Back();
+
+ if ((BreakoutAllowed || Debug_Flag) && key == KN_ESC) {
+ Keyboard->Clear();
+ Brokeout = true;
+ return(true);
+ }
+
+ if (!GameInFocus) {
+ VQA_PauseAudio();
+ while (!GameInFocus) {
+ Check_For_Focus_Loss();
+ }
+ }
+ return(false);
+}
+
+#else //WIN32
+
+long VQ_Call_Back(unsigned char *, long )
+{
+ Call_Back();
+ if ((BreakoutAllowed || Debug_Flag) && Keyboard->Check()) {
+ if (Keyboard->Get() == KN_ESC) {
+ Keyboard->Clear();
+ Brokeout = true;
+ return(true);
+ }
+ Keyboard->Clear();
+ }
+ return(false);
+}
+#endif //WIN32
+
+
+/***********************************************************************************************
+ * Handle_Team -- Processes team selection command. *
+ * *
+ * This routine will handle creation and selection of pseudo teams that the player can *
+ * create or control. A team in this sense is an arbitrary grouping of units such that *
+ * rapid selection control is allowed. *
+ * *
+ * INPUT: team -- The logical team number to process. *
+ * *
+ * action-- The action to perform on this team: *
+ * 0 - Toggle the select state for all members of this team. *
+ * 1 - Select the members of this team. *
+ * 2 - Make all selected objects members of this team. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/27/1995 JLB : Created. *
+ *=============================================================================================*/
+void Handle_Team(int team, int action)
+{
+ int index;
+
+ //
+ // Recording support
+ //
+ if (Session.Record) {
+ TeamNumber = (char)team;
+ TeamEvent = (char)action + 1;
+ }
+
+ AllowVoice = true;
+ switch (action) {
+
+ /*
+ ** Toggle the team selection. If the team is selected, then merely unselect it. If the
+ ** team is not selected, then unselect all others before selecting this team.
+ */
+ case 3:
+ case 0:
+
+ /*
+ ** If a non team member is currently selected, then deselect all objects
+ ** before selecting this team.
+ */
+ if (CurrentObject.Count()) {
+ if (CurrentObject[0]->Is_Foot() && ((FootClass *)CurrentObject[0])->Group != team) {
+ Unselect_All();
+ }
+ }
+ for (index = 0; index < Vessels.Count(); index++) {
+ VesselClass * obj = Vessels.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) {
+ if (!obj->IsSelected) {
+ obj->Select();
+ AllowVoice = false;
+ }
+ }
+ }
+ for (index = 0; index < Units.Count(); index++) {
+ UnitClass * obj = Units.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) {
+ if (!obj->IsSelected) {
+ obj->Select();
+ AllowVoice = false;
+ }
+ }
+ }
+ for (index = 0; index < Infantry.Count(); index++) {
+ InfantryClass * obj = Infantry.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) {
+ if (!obj->IsSelected) {
+ obj->Select();
+ AllowVoice = false;
+ }
+ }
+ }
+ for (index = 0; index < Aircraft.Count(); index++) {
+ AircraftClass * obj = Aircraft.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) {
+ if (!obj->IsSelected) {
+ obj->Select();
+ AllowVoice = false;
+ }
+ }
+ }
+
+ /*
+ ** Center the map around the team if the ALT key was pressed too.
+ */
+ if (action == 3) {
+ Map.Center_Map();
+#ifdef WIN32
+ Map.Flag_To_Redraw(true);
+#endif //WIn32
+ }
+ break;
+
+ /*
+ ** Additive selection of team.
+ */
+ case 1:
+ for (index = 0; index < Units.Count(); index++) {
+ UnitClass * obj = Units.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) {
+ if (!obj->IsSelected) {
+ obj->Select();
+ AllowVoice = false;
+ }
+ }
+ }
+ for (index = 0; index < Vessels.Count(); index++) {
+ VesselClass * obj = Vessels.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) {
+ if (!obj->IsSelected) {
+ obj->Select();
+ AllowVoice = false;
+ }
+ }
+ }
+ for (index = 0; index < Infantry.Count(); index++) {
+ InfantryClass * obj = Infantry.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) {
+ if (!obj->IsSelected) {
+ obj->Select();
+ AllowVoice = false;
+ }
+ }
+ }
+ for (index = 0; index < Aircraft.Count(); index++) {
+ AircraftClass * obj = Aircraft.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) {
+ if (!obj->IsSelected) {
+ obj->Select();
+ AllowVoice = false;
+ }
+ }
+ }
+ break;
+
+ /*
+ ** Create the team.
+ */
+ case 2: {
+ long minx = 0x7FFFFFFFL, miny = 0x7FFFFFFFL;
+ long maxx = 0, maxy = 0;
+ TeamSpeed[team] = SPEED_WHEEL;
+ TeamMaxSpeed[team] = MPH_LIGHT_SPEED;
+ for (index = 0; index < Units.Count(); index++) {
+ UnitClass * obj = Units.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) {
+ if (obj->Group == team) obj->Group = 0xFF;
+ if (obj->IsSelected) {
+ obj->Group = team;
+ obj->Mark(MARK_CHANGE);
+ long xc = Cell_X(Coord_Cell(obj->Center_Coord()));
+ long yc = Cell_Y(Coord_Cell(obj->Center_Coord()));
+ if (xc < minx) minx = xc;
+ if (xc > maxx) maxx = xc;
+ if (yc < miny) miny = yc;
+ if (yc > maxy) maxy = yc;
+ if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) {
+ TeamMaxSpeed[team] = obj->Class->MaxSpeed;
+ TeamSpeed[team] = obj->Class->Speed;
+ }
+ }
+ }
+ }
+
+ for (index = 0; index < Vessels.Count(); index++) {
+ VesselClass * obj = Vessels.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) {
+ if (obj->Group == team) obj->Group = -1;
+ if (obj->IsSelected) {
+ obj->Group = team;
+ obj->Mark(MARK_CHANGE);
+ long xc = Cell_X(Coord_Cell(obj->Center_Coord()));
+ long yc = Cell_Y(Coord_Cell(obj->Center_Coord()));
+ if (xc < minx) minx = xc;
+ if (xc > maxx) maxx = xc;
+ if (yc < miny) miny = yc;
+ if (yc > maxy) maxy = yc;
+ if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) {
+ TeamMaxSpeed[team] = obj->Class->MaxSpeed;
+ TeamSpeed[team] = obj->Class->Speed;
+ }
+ }
+ }
+ }
+
+ for (index = 0; index < Infantry.Count(); index++) {
+ InfantryClass * obj = Infantry.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) {
+ if (obj->Group == team) obj->Group = 0xFF;
+ if (obj->IsSelected) {
+ obj->Group = team;
+ obj->Mark(MARK_CHANGE);
+ long xc = Cell_X(Coord_Cell(obj->Center_Coord()));
+ long yc = Cell_Y(Coord_Cell(obj->Center_Coord()));
+ if (xc < minx) minx = xc;
+ if (xc > maxx) maxx = xc;
+ if (yc < miny) miny = yc;
+ if (yc > maxy) maxy = yc;
+ if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) {
+ TeamMaxSpeed[team] = obj->Class->MaxSpeed;
+ }
+ }
+ }
+ }
+ for (index = 0; index < Aircraft.Count(); index++) {
+ AircraftClass * obj = Aircraft.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) {
+ if (obj->Group == team) obj->Group = 0xFF;
+ if (obj->IsSelected) {
+ obj->Group = team;
+ obj->Mark(MARK_CHANGE);
+ }
+ }
+ }
+
+ for (index = 0; index < Units.Count(); index++) {
+ UnitClass * obj = Units.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl &&
+ (obj->Group == team) && (obj->IsSelected) ) {
+
+ /*
+ ** When a team is first created, they're created without a
+ ** formation offset, so they will not be created in
+ ** formation. Later, if they're assigned a formation, the
+ ** XFormOffset & YFormOffset numbers will change to valid
+ ** offsets, and they'll be formationed.
+ */
+#if(1)
+ obj->XFormOffset = obj->YFormOffset = (int)0x80000000;
+#else
+#if(1)
+// Old always-north formation stuff
+ long xc = Cell_X(Coord_Cell(obj->Center_Coord()));
+ long yc = Cell_Y(Coord_Cell(obj->Center_Coord()));
+
+ obj->XFormOffset = xc - centerx;
+ obj->YFormOffset = yc - centery;
+#else
+// New method: save direction and distance rather than x & y offset
+ obj->XFormOffset = ::Direction(As_Coord(center), obj->Center_Coord());
+ obj->YFormOffset = ::Distance (As_Coord(center), obj->Center_Coord());
+#endif
+#endif
+ }
+ }
+
+ for (index = 0; index < Infantry.Count(); index++) {
+ InfantryClass * obj = Infantry.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) {
+ if (obj->Group == team) obj->Group = 0xFF;
+ if (obj->IsSelected) obj->Group = team;
+ if (obj->Group == team && obj->IsSelected) {
+#if(1)
+ obj->XFormOffset = obj->YFormOffset = (int)0x80000000;
+#else
+#if(1)
+// Old always-north formation stuff
+ long xc = Cell_X(Coord_Cell(obj->Center_Coord()));
+ long yc = Cell_Y(Coord_Cell(obj->Center_Coord()));
+
+ obj->XFormOffset = xc - centerx;
+ obj->YFormOffset = yc - centery;
+#else
+// New method: save direction and distance rather than x & y offset
+ obj->XFormOffset = ::Direction(As_Coord(center), obj->Center_Coord());
+ obj->YFormOffset = ::Distance (As_Coord(center), obj->Center_Coord());
+#endif
+#endif
+ }
+ }
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ AllowVoice = true;
+}
+
+
+/***********************************************************************************************
+ * Handle_View -- Either records or restores the tactical view. *
+ * *
+ * This routine is used to record or restore the current map tactical view. *
+ * *
+ * INPUT: view -- The view number to work with. *
+ * *
+ * action-- The action to perform with this view number. *
+ * 0 = Restore the view to this previously remembered location. *
+ * 1 = Record the current view location. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/04/1995 JLB : Created. *
+ *=============================================================================================*/
+void Handle_View(int view, int action)
+{
+ if ((unsigned)view < ARRAY_SIZE(Scen.Views)) {
+ if (action == 0) {
+
+ Map.Set_Tactical_Position(Coord_Whole(Cell_Coord(Scen.Views[view] - (MAP_CELL_W * 4 * RESFACTOR) - (5*RESFACTOR))));
+
+#ifdef WIN32
+ /*
+ ** Win95 scrolling logic cant handle just jumps in screen position so redraw the lot.
+ */
+ Map.Flag_To_Redraw (true);
+#endif //WIN32
+ } else {
+ Scen.Views[view] = Coord_Cell(Map.TacticalCoord) + (MAP_CELL_W*4*RESFACTOR) + (5*RESFACTOR);
+ }
+ }
+}
+
+
+#ifndef ROR_NOT_READY
+#define ROR_NOT_READY 21
+#endif
+
+static char * _CD_Volume_Label[] = {
+ "CD1",
+ "CD2",
+ "CD3",
+ "CD4",
+ // Denzil 4/15/98
+ #ifdef DVD
+ "CD1", // ajw - Pushes RADVD to position 5, to match enum in Force_CD_Available(). 4 will never be returned here.
+ "RADVD",
+ #endif
+};
+static int _Num_Volumes = ARRAY_SIZE(_CD_Volume_Label);
+
+
+#ifdef WIN32
+/***********************************************************************************************
+ * Get_CD_Index -- returns the volume type of the CD in the given drive *
+ * *
+ * *
+ * *
+ * INPUT: drive number *
+ * timeout *
+ * *
+ * OUTPUT: 0 = gdi *
+ * 1 = nod *
+ * 2 = covert or CS *
+ * 3 = Aftermath
+ * 5 = DVD
+ * -1 = non C&C *
+ * *
+ * WARNINGS: None *
+ * *
+ * HISTORY: *
+ * 5/21/96 5:27PM ST : Created *
+ * 01/20/97 V.Grippi added CS support *
+ *=============================================================================================*/
+int Get_CD_Index (int cd_drive, int timeout)
+ {
+ char volume_name[128];
+ char buffer[128];
+ unsigned filename_length;
+ unsigned misc_dword;
+ int count = 0;
+
+ CountDownTimerClass timer;
+
+ timer.Set(timeout);
+
+ /*
+ ** Get the volume label. If we get a 'not ready' error then retry for the timeout
+ ** period.
+ */
+ for (;;)
+ {
+ sprintf(buffer, "%c:\\", 'A' + cd_drive);
+
+ if (GetVolumeInformation ((char const *)buffer, &volume_name[0] ,
+ (unsigned long)sizeof(volume_name), (unsigned long *)NULL ,
+ (unsigned long *)&filename_length, (unsigned long *)&misc_dword,
+ (char *)NULL, (unsigned long)0))
+ {
+ /*
+ ** Try opening 'movies.mix' to verify that the CD is really there and is what
+ ** it says it is.
+ */
+ sprintf(buffer, "%c:\\main.mix", 'A' + cd_drive);
+
+ HANDLE handle = CreateFile(buffer, GENERIC_READ, FILE_SHARE_READ,NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (handle != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(handle);
+
+ /*
+ ** Match the volume label to the list of known C&C volume labels.
+ */
+ for (int i = 0 ; i < _Num_Volumes; i++)
+ {
+ if (!stricmp(_CD_Volume_Label[i], volume_name))
+ return(i);
+ }
+ }
+ else
+ {
+ if (!count)
+ count++;
+ else
+ return -1;
+ }
+ }
+ else
+ {
+ /*
+ ** Failed to get the volume label on a known CD drive.
+ ** If this is a CD changer it may require time to swap the disks so dont return
+ ** immediately if the error is ROR_NOT_READY
+ */
+ if (!timer.Time())
+ return -1;
+
+ int val = GetLastError();
+
+ if (val != ROR_NOT_READY)
+ return -1;
+ }
+ }
+ }
+#else
+int Get_CD_Index(int cd_drive, int)
+ {
+ char buffer[128];
+
+ /*
+ ** We need to do this twice because of the possibilities of a directory
+ ** being cached. If this is so, it will only be discovered when we
+ ** actually attempt to read a file from the drive.
+ */
+ if(cd_drive) for (int count = 0; count < 2; count ++)
+ {
+ struct find_t ft;
+ int file;
+ int open_failed;
+
+ /*
+ ** Create a path for the cd drive and attempt to read the volume label from
+ ** it.
+ */
+ sprintf(buffer, "%c:\\", 'A' + cd_drive);
+
+ /*
+ ** If we are able to read the volume label, this is good but not enough.
+ ** Further verification must be done.
+ */
+ if (!_dos_findfirst(buffer, _A_VOLID, &ft))
+ {
+ /*
+ ** Since some versions of disk cacheing software will cache the CD's
+ ** directory tracks, we may think the CD is in the drive when it is
+ ** actually not. To resolve this we must attempt to open a file on
+ ** the cd. Opening a file will always update the directory tracks
+ ** (suposedly).
+ */
+ sprintf(buffer, "%c:\\main.mix", 'A' + cd_drive);
+ open_failed = _dos_open(buffer, O_RDONLY|SH_DENYNO, &file);
+
+ if (!open_failed)
+ {
+ _dos_close(file);
+
+ /*
+ ** Hey some times the stupid dos driver appends a period to the
+ ** name if it is eight characters long. If the last char is a
+ ** period then erase it.
+ */
+ if (ft.name[strlen(ft.name)-1] == '.')
+ {
+ ft.name[strlen(ft.name)-1] = 0;
+ }
+
+ /*
+ ** Match the volume label to the list of known C&C volume labels.
+ */
+ for (int i = 0 ; i < _Num_Volumes; i++)
+ {
+ if (!stricmp(_CD_Volume_Label[i], ft.name))
+ return (i);
+ }
+ }
+ }
+ }
+
+ return -1;
+ }
+#endif
+
+
+/***********************************************************************************************
+ * Force_CD_Available -- Ensures that specified CD is available. *
+ * *
+ * Call this routine when you need to ensure that the specified CD is actually in the *
+ * CD-ROM drive. *
+ * *
+ * INPUT: cd -- The CD that must be available. This will either be "0" for the GDI CD, or *
+ * "1" for the Nod CD. If either CD will qualify, then pass in "-1". *
+ * 0 = CD1
+ * 1 = CD2
+ * 2 = Counterstrike
+ * 3 = Aftermath
+ * 4 = Counterstrike or Aftermath
+ * 5 = DVD
+ * -1 = Any CD
+ * -2 = Local Harddisk
+ * *
+ * OUTPUT: Is the CD inserted and available? If false is returned, then this indicates that *
+ * the player pressed . *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/11/1995 JLB : Created. *
+ * 05/22/1996 ST : Handles multiple CD drives / CD changers *
+ * 01/20/1997 V.Grippi added expansion cd message
+ *=============================================================================================*/
+
+typedef enum {
+ CD_LOCAL = -2,
+ CD_ANY = -1,
+ CD_SOVIET = 0,
+ CD_ALLIED,
+ CD_COUNTERSTRIKE,
+ CD_AFTERMATH,
+ CD_CS_OR_AM,
+ CD_DVD
+} CD_VOLUME;
+
+#ifdef FIXIT_VERSION_3
+
+#ifndef DVD
+#error DVD must be defined!
+#endif
+
+bool Force_CD_Available( int cd_desired ) // ajw
+{
+ static int _last = -1;
+ static void *font;
+#ifdef FRENCH
+ static char * _cd_name[] = {
+ "ALERTE ROUGE CD1",
+ "ALERTE ROUGE CD2",
+ "CD Missions Taiga",
+ "CD Missions M.A.D.",
+ "ALERTE ROUGE DVD",
+};
+#endif
+#ifdef GERMAN
+ static char * _cd_name[] = {
+ "ALARMSTUFE ROT CD1",
+ "ALARMSTUFE ROT CD2",
+ "CD Gegenangriff einlegen",
+ "CD TRANS einlegen",
+ "ALARMSTUFE ROT DVD",
+ };
+#endif
+#ifdef ENGLISH
+ static char * _cd_name[] = {
+ "RED ALERT DISK 1",
+ "RED ALERT DISK 2",
+ "CounterStrike CD",
+ "Aftermath CD",
+ "RED ALERT DVD",
+ };
+#endif
+
+ int new_cd_drive = 0;
+ int cd_current;
+ int current_drive;
+
+ ThemeType theme_playing = THEME_NONE;
+
+ /*
+ ** If the required CD is set to -2 then it means that the file is present
+ ** on the local hard drive and we shouldn't have to worry about it.
+ */
+ if (cd_desired == CD_LOCAL) return(true);
+
+ /*
+ ** Find out if the CD in the current drive is the one we are looking for
+ */
+ current_drive = CCFileClass::Get_CD_Drive();
+ cd_current = Get_CD_Index(current_drive, 1*60);
+
+// debugprint("Get_CD_Index just returned %d\n", cd_current);
+// debugprint("We are checking for %d\n", cd_desired);
+// debugprint("current_drive = %d\n", current_drive);
+
+ if( Using_DVD() )
+ {
+ // All requested cd indexes get rerouted to the DVD.
+ cd_desired = CD_DVD;
+// if( RequiredCD != -1 )
+// RequiredCD = CD_DVD; // Just seems like a good idea. Not sure if necessary. ajw
+ }
+
+ if (cd_current >= 0 )
+ {
+ if( cd_desired == CD_CS_OR_AM )
+ {
+ // If the current cd is CS or AM then change request to whatever
+ // is present.
+ if( cd_current == CD_COUNTERSTRIKE || cd_current == CD_AFTERMATH )
+ cd_desired = cd_current;
+ }
+ // If the current CD is requested or any CD will work
+ if( cd_desired == cd_current || cd_desired == CD_ANY )
+ {
+ /*
+ ** The required CD is still in the CD drive we used last time
+ */
+ new_cd_drive = current_drive;
+ }
+ }
+
+ /*
+ ** Flag that we will have to restart the theme
+ */
+ theme_playing = Theme.What_Is_Playing();
+ Theme.Stop();
+
+ // Check the last drive
+ if (!new_cd_drive)
+ {
+ /*
+ ** Check the last CD drive we used if it's different from the current one
+ */
+ int last_drive = CCFileClass::Get_Last_CD_Drive();
+
+ /*
+ ** Make sure the last drive is valid and it isn't the current drive
+ */
+ if (last_drive && last_drive != CCFileClass::Get_CD_Drive()) // Else we have already checked this cd.
+ {
+ /*
+ ** Find out if there is a C&C cd in the last drive and if so is it the one we are looking for
+ ** Give it a nice big timeout so the CD changer has time to swap the discs
+ */
+ cd_current = Get_CD_Index(last_drive, 10*60);
+
+ if (cd_current >= 0 )
+ {
+ if( cd_desired == CD_CS_OR_AM )
+ {
+ // If the cd is CS or AM then change request to whatever
+ // is present.
+ if( cd_current == CD_COUNTERSTRIKE || cd_current == CD_AFTERMATH )
+ cd_desired = cd_current;
+ }
+ // If the cd is present or any cd will work
+ if( cd_desired == cd_current || cd_desired == CD_ANY )
+ {
+ /*
+ ** The required CD is in the CD drive we used last time
+ */
+ new_cd_drive = last_drive;
+ }
+ }
+ }
+ }
+
+ /*
+ ** Lordy. No sign of that blimming CD anywhere. Search all the CD drives
+ ** then if we still can't find it prompt the user to insert it.
+ */
+ if (!new_cd_drive)
+ {
+ /*
+ ** Small timeout for the first pass through the drives
+ */
+ int drive_search_timeout = 2*60;
+
+ for (;;)
+ {
+ char buffer[128];
+ /*
+ ** Search all present CD drives for the required disc.
+ */
+ for (int i = 0 ; i < CDList.Get_Number_Of_Drives(); i++)
+ {
+ int cd_drive = CDList.Get_Next_CD_Drive();
+ cd_current = Get_CD_Index(cd_drive, drive_search_timeout);
+
+ if (cd_current >= 0)
+ {
+ /*
+ ** We found a C&C cd - lets see if it was the one we were looking for
+ */
+ // Require CS or AM
+ if( cd_desired == CD_CS_OR_AM )
+ {
+ // If the cd is CS or AM then change request to whatever
+ // is present.
+ if( cd_current == CD_COUNTERSTRIKE || cd_current == CD_AFTERMATH )
+ cd_desired = cd_current;
+ }
+
+ if( cd_desired == cd_current || cd_desired == CD_ANY )
+ {
+ /*
+ ** Woohoo! The disk was in a different cd drive. Refresh the search path list
+ * and return.
+ */
+ new_cd_drive = cd_drive;
+ break;
+ }
+ }
+ }
+
+ /*
+ ** A new disc has become available so break
+ */
+ if (new_cd_drive) break;
+
+ /*
+ ** Increase the timeout for subsequent drive searches.
+ */
+ drive_search_timeout = 5*60;
+
+ /*
+ ** Prompt to insert the CD into the drive.
+ */
+ //V.Grippi
+ if( cd_desired == CD_CS_OR_AM )
+ cd_desired = CD_AFTERMATH;
+
+ if( cd_desired == CD_DVD )
+ {
+ #ifdef FRENCH
+ sprintf(buffer, "InsŠrez le %s", _cd_name[4]);
+ #else
+ #ifdef GERMAN
+ sprintf(buffer, "Bitte %s", _cd_name[4]);
+ #else
+ sprintf(buffer, "Please insert the %s", _cd_name[4]);
+ #endif
+ #endif
+ }
+ else if( cd_desired == CD_COUNTERSTRIKE || cd_desired == CD_AFTERMATH )
+ {
+ #ifdef FRENCH
+ sprintf(buffer, "InsŠrez le %s", _cd_name[cd_desired]);
+ #else
+ #ifdef GERMAN
+ sprintf(buffer, "Bitte %s", _cd_name[cd_desired]);
+ #else
+ sprintf(buffer, "Please insert the %s", _cd_name[cd_desired]);
+ #endif
+ #endif
+ }
+ else if( cd_desired == CD_ANY )
+ {
+ sprintf(buffer, Text_String(TXT_CD_DIALOG_1), cd_desired+1, _cd_name[cd_desired]);
+ }
+ else // 0 or 1
+ {
+ sprintf(buffer, Text_String(TXT_CD_DIALOG_2), cd_desired+1, _cd_name[cd_desired]);
+ }
+
+ GraphicViewPortClass * oldpage = Set_Logic_Page(SeenBuff);
+ theme_playing = Theme.What_Is_Playing();
+ Theme.Stop();
+ int hidden = Get_Mouse_State();
+ font = (void *)FontPtr;
+
+ /*
+ ** Only set the palette if necessary.
+ */
+ if (PaletteClass::CurrentPalette[1].Red_Component() +
+ PaletteClass::CurrentPalette[1].Blue_Component() +
+ PaletteClass::CurrentPalette[1].Green_Component() == 0)
+ {
+ GamePalette.Set();
+ }
+
+ Keyboard->Clear();
+
+ while (Get_Mouse_State()) Show_Mouse();
+
+ if (WWMessageBox().Process(buffer, TXT_OK, TXT_CANCEL, TXT_NONE, TRUE) == 1)
+ {
+ Set_Logic_Page(oldpage);
+#ifdef FIXIT_VERSION_3
+ while (hidden--) Hide_Mouse();
+#else
+ Hide_Mouse();
+#endif
+ return(false);
+ }
+
+ while (hidden--) Hide_Mouse();
+ Set_Font(font);
+ Set_Logic_Page(oldpage);
+ }
+ }
+
+ CurrentCD = cd_current;
+
+ CCFileClass::Set_CD_Drive(new_cd_drive);
+ CCFileClass::Refresh_Search_Drives();
+
+ /*
+ ** If it broke out of the query for CD-ROM loop, then this means that the
+ ** CD-ROM has been inserted.
+ */
+ if (cd_desired == 4) cd_desired--;
+
+ // ajw - Added condition of cd_desired != 5 to the following if.
+ // Reason: This was triggering before Init_Secondary_Mixfiles(), which was screwing up the mixfile system somehow.
+ //
+ // Since the DVD is the only disk that can possibly be required when Using_DVD(), I never have to reload the mix
+ // files here, because no other disk could ever have been asked for. And if not Using_DVD(), cd_desired will never
+ // be equal to 5. So this is safe.
+ if (cd_desired > -1 && _last != cd_desired && cd_desired != 5)
+ {
+ _last = cd_desired;
+
+ Theme.Stop();
+
+// if (ConquerMix) delete ConquerMix;
+ if (MoviesMix) delete MoviesMix;
+ if (GeneralMix) delete GeneralMix;
+ if (ScoreMix) delete ScoreMix;
+ if (MainMix) delete MainMix;
+
+ MainMix = new MFCD("MAIN.MIX", &FastKey);
+ assert(MainMix != NULL);
+// ConquerMix = new MFCD("CONQUER.MIX", &FastKey);
+ if (CCFileClass("MOVIES1.MIX").Is_Available())
+ MoviesMix = new MFCD("MOVIES1.MIX", &FastKey);
+ else
+ MoviesMix = new MFCD("MOVIES2.MIX", &FastKey);
+ assert(MoviesMix != NULL);
+ GeneralMix = new MFCD("GENERAL.MIX", &FastKey);
+ ScoreMix = new MFCD("SCORES.MIX", &FastKey);
+ ThemeClass::Scan();
+ }
+
+ return(true);
+}
+
+#else // FIXIT_VERSION_3 not defined
+
+bool Force_CD_Available(int cd)
+{
+ static int _last = -1;
+// static char _palette[768];
+// static char _hold[256];
+ static void *font;
+#ifdef FRENCH
+ static char * _cd_name[] = {
+ "ALERTE ROUGE CD1",
+ "ALERTE ROUGE CD2",
+ "CD Missions Taiga",
+ "CD Missions M.A.D.",
+
+ // Denzil 4/15/98
+ #ifdef DVD
+ "ALERTE ROUGE DVD",
+ #endif
+};
+
+#endif
+#ifdef GERMAN
+ static char * _cd_name[] = {
+ "ALARMSTUFE ROT CD1 einlegen",
+ "ALARMSTUFE ROT CD2 einlegen",
+ "CD Gegenangriff einlegen",
+ "CD TRANS einlegen",
+
+ // Denzil 4/15/98
+ #ifdef DVD
+ "ALARMSTUFE ROT DVD einlegen",
+ #endif
+ };
+#endif
+#ifdef ENGLISH
+ static char * _cd_name[] = {
+ "RED ALERT DISK 1",
+ "RED ALERT DISK 2",
+ "CounterStrike CD",
+ "Aftermath CD",
+
+ // Denzil 4/15/98
+ #ifdef DVD
+ "RED ALERT DVD",
+ #endif
+ };
+#endif
+
+ int new_cd_drive = 0;
+ int cd_index;
+ char buffer[128];
+ int cd_drive;
+ int current_drive;
+ int drive_search_timeout;
+
+ ThemeType theme_playing = THEME_NONE;
+
+//#ifdef FIXIT_ANTS
+// if(Scen.ScenarioName[2] == 'A')
+// cd = 2;
+//#endif
+ /*
+ ** If the required CD is set to -2 then it means that the file is present
+ ** on the local hard drive and we shouldn't have to worry about it.
+ */
+ if (cd == CD_LOCAL) return(true);
+
+ /*
+ ** Find out if the CD in the current drive is the one we are looking for
+ */
+ current_drive = CCFileClass::Get_CD_Drive();
+ cd_index = Get_CD_Index(current_drive, 1*60);
+
+ #ifdef CHEAT_KEYS
+ // Mono_Printf("Get_CD_Index just returned %d\n", cd_index);
+ // Mono_Printf("We are checking for %d\n", cd);
+ // Mono_Printf("current_drive = %d\n", current_drive);
+ #endif //CHEAT_KEYS
+
+ #ifdef DVD // Denzil
+ // CD1 and CD2 are ignored, force the DVD
+ if (cd_index == 0 || cd_index == 1)
+ cd_index = -1;
+ #endif
+
+ if (cd_index >= 0 )
+ {
+ #ifdef FIXIT_CSII // checked - ajw 9/28/98
+ // Require CS or AM
+ if (cd == CD_CS_OR_AM)
+ {
+ // If the current cd is CS or AM then change request to whatever
+ // is present.
+ if (cd_index == 2 || cd_index == 3)
+ {
+ cd = cd_index;
+ }
+ }
+ #endif
+
+ #ifdef DVD // Denzil
+ // If the current drive is the DVD then requests for CD1 and CD2 are okay
+ if (cd_index == 4)
+ {
+ // CD1, CD2 & DVD requests
+ if (cd == 0 || cd == 1 || cd == 5)
+ {
+ cd_index = cd;
+ }
+ }
+ #endif
+
+ // If the current CD is requested or any CD will work
+ if (cd == cd_index || cd == -1 )
+ {
+ /*
+ ** The required CD is still in the CD drive we used last time
+ */
+ new_cd_drive = current_drive;
+ }
+ }
+
+ /*
+ ** Flag that we will have to restart the theme
+ */
+ theme_playing = Theme.What_Is_Playing();
+ Theme.Stop();
+
+ // Check the last drive
+ if (!new_cd_drive)
+ {
+ /*
+ ** Check the last CD drive we used if it's different from the current one
+ */
+ int last_drive = CCFileClass::Get_Last_CD_Drive();
+
+ /*
+ ** Make sure the last drive is valid and it isn't the current drive
+ */
+ if (last_drive && last_drive != CCFileClass::Get_CD_Drive())
+ {
+ /*
+ ** Find out if there is a C&C cd in the last drive and if so is it the one we are looking for
+ ** Give it a nice big timeout so the CD changer has time to swap the discs
+ */
+ cd_index = Get_CD_Index(last_drive, 10*60);
+
+ #ifdef DVD // Denzil
+ // Ignore CD1 and CD2 disks, force DVD
+ if (cd_index == 0 || cd_index == 1)
+ cd_index = -1;
+ #endif
+
+ if (cd_index >= 0 )
+ {
+ #ifdef FIXIT_CSII // checked - ajw 9/28/98
+ // Require CS or AM
+ if (cd == 4)
+ {
+ // If CS or AM was the last drive then use it
+ if (cd_index == 2 || cd_index == 3)
+ {
+ cd = cd_index;
+ }
+ }
+ #endif
+
+ #ifdef DVD // Denzil
+ // If DVD is in drive
+ if (cd_index == 4)
+ {
+ // CD1, CD2 and DVD requests are all on the DVD
+ if ((cd == 0) || (cd == 1) || (cd == 5))
+ {
+ cd_index = cd;
+ }
+ }
+ #endif
+
+ // If the cd is present or any cd will work
+ if (cd == cd_index || cd == -1 )
+ {
+ /*
+ ** The required CD is in the CD drive we used last time
+ */
+ new_cd_drive = last_drive;
+ }
+ }
+ }
+ }
+
+ /*
+ ** Lordy. No sign of that blimming CD anywhere. Search all the CD drives
+ ** then if we still can't find it prompt the user to insert it.
+ */
+ if (!new_cd_drive)
+ {
+ /*
+ ** Small timeout for the first pass through the drives
+ */
+ drive_search_timeout = 2*60;
+
+ for (;;)
+ {
+ /*
+ ** Search all present CD drives for the required disc.
+ */
+ for (int i = 0 ; i < CDList.Get_Number_Of_Drives(); i++)
+ {
+ cd_drive = CDList.Get_Next_CD_Drive();
+ cd_index = Get_CD_Index(cd_drive, drive_search_timeout);
+
+ #ifdef DVD // Denzil
+ // Ignore CD1 and CD2, force the DVD
+ if (cd_index == 0 || cd_index == 1)
+ cd_index = -1;
+ #endif
+
+ if (cd_index >= 0)
+ {
+ /*
+ ** We found a C&C cd - lets see if it was the one we were looking for
+ */
+ #ifdef FIXIT_CSII // checked - ajw 9/28/98
+ // Require CS or AM
+ if (cd == 4)
+ {
+ // If the disk is CS or AM then request it
+ if (cd_index == 2 || cd_index == 3)
+ {
+ cd = cd_index;
+ }
+ }
+ #endif
+
+ #ifdef DVD // Denzil
+ if (cd_index == 4)
+ {
+ if ((cd == 0) || (cd == 1) || (cd == 5))
+ {
+ cd_index = cd;
+ }
+ }
+ #endif
+
+ if (cd == cd_index || cd == -1 || cd == -2 )
+ {
+ /*
+ ** Woohoo! The disk was in a different cd drive. Refresh the search path list
+ * and return.
+ */
+ new_cd_drive = cd_drive;
+ break;
+ }
+ }
+ }
+
+ /*
+ ** A new disc has become available so break
+ */
+ if (new_cd_drive) break;
+
+ /*
+ ** Increase the timeout for subsequent drive searches.
+ */
+ drive_search_timeout = 5*60;
+
+ /*
+ ** Prompt to insert the CD into the drive.
+ */
+ //V.Grippi
+ #ifdef FIXIT_CSII // checked - ajw 9/28/98
+ if(cd == 4) cd--;
+
+ // CS or AM
+ if(cd == 2 || cd == 3)
+ {
+ #else
+ if(cd == 2)
+ {
+ #endif
+
+ #ifdef FRENCH
+ sprintf(buffer, "InsŠrez le %s", _cd_name[cd]);
+ #else
+ #ifdef GERMAN
+ sprintf(buffer, "Bitte %s", _cd_name[cd]);
+ #else
+ sprintf(buffer, "Please insert the %s", _cd_name[cd]);
+ #endif
+ #endif
+ }
+ else
+ {
+ #ifdef DVD
+ #ifdef FRENCH
+ sprintf(buffer, "InsŠrez le %s", _cd_name[4]);
+ #else
+ #ifdef GERMAN
+ sprintf(buffer, "Bitte %s", _cd_name[4]);
+ #else
+ sprintf(buffer, "Please insert the %s", _cd_name[4]);
+ #endif
+ #endif
+ #else
+ if (cd == -1 )
+ {
+ sprintf(buffer, Text_String(TXT_CD_DIALOG_1), cd+1, _cd_name[cd]);
+ }
+ else
+ {
+ sprintf(buffer, Text_String(TXT_CD_DIALOG_2), cd+1, _cd_name[cd]);
+ }
+ #endif
+ }
+
+ #ifdef WIN32
+ GraphicViewPortClass * oldpage = Set_Logic_Page(SeenBuff);
+ #else
+ GraphicBufferClass * oldpage = Set_Logic_Page(SeenBuff);
+ #endif
+ theme_playing = Theme.What_Is_Playing();
+ Theme.Stop();
+ int hidden = Get_Mouse_State();
+ font = (void *)FontPtr;
+
+ /*
+ ** Only set the palette if necessary.
+ */
+ if (PaletteClass::CurrentPalette[1].Red_Component() +
+ PaletteClass::CurrentPalette[1].Blue_Component() +
+ PaletteClass::CurrentPalette[1].Green_Component() == 0)
+ {
+ GamePalette.Set();
+ }
+
+ Keyboard->Clear();
+
+ while (Get_Mouse_State()) Show_Mouse();
+
+ if (WWMessageBox().Process(buffer, TXT_OK, TXT_CANCEL, TXT_NONE, TRUE) == 1)
+ {
+ Set_Logic_Page(oldpage);
+ Hide_Mouse();
+ return(false);
+ }
+
+ while (hidden--) Hide_Mouse();
+ Set_Font(font);
+ Set_Logic_Page(oldpage);
+ }
+ }
+
+ CurrentCD = cd_index;
+
+ CCFileClass::Set_CD_Drive(new_cd_drive);
+ CCFileClass::Refresh_Search_Drives();
+
+ /*
+ ** If it broke out of the query for CD-ROM loop, then this means that the
+ ** CD-ROM has been inserted.
+ */
+ #ifdef FIXIT_CSII // checked - ajw 9/28/98
+ if (cd == 4) cd--;
+ #endif
+// if (cd > -3 && _last != cd) {
+ if (cd > -1 && _last != cd)
+ {
+ _last = cd;
+
+ Theme.Stop();
+
+// if (ConquerMix) delete ConquerMix;
+ if (MoviesMix) delete MoviesMix;
+ if (GeneralMix) delete GeneralMix;
+ if (ScoreMix) delete ScoreMix;
+ if (MainMix) delete MainMix;
+
+ MainMix = new MFCD("MAIN.MIX", &FastKey);
+
+ assert(MainMix != NULL);
+// ConquerMix = new MFCD("CONQUER.MIX", &FastKey);
+
+ if (CCFileClass("MOVIES1.MIX").Is_Available())
+ {
+ MoviesMix = new MFCD("MOVIES1.MIX", &FastKey);
+ }
+ else
+ {
+ MoviesMix = new MFCD("MOVIES2.MIX", &FastKey);
+ }
+ assert(MoviesMix != NULL);
+ GeneralMix = new MFCD("GENERAL.MIX", &FastKey);
+ ScoreMix = new MFCD("SCORES.MIX", &FastKey);
+ ThemeClass::Scan();
+ }
+
+ if (theme_playing != THEME_NONE)
+ {
+ Theme.Queue_Song(theme_playing);
+ }
+
+ return(true);
+ }
+
+
+#endif // FIXIT_VERSION_3
+
+/***************************************************************************
+ * DISK_SPACE_AVAILABLE -- returns bytes of free disk space *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: returns amount of free disk space *
+ * *
+ * HISTORY: *
+ * 08/11/1995 PWG : Created. *
+ *=========================================================================*/
+unsigned long Disk_Space_Available(void)
+{
+ struct diskfree_t diskdata;
+ unsigned drive;
+
+ _dos_getdrive(&drive);
+ _dos_getdiskfree(drive, &diskdata);
+
+ return(diskdata.avail_clusters * diskdata.sectors_per_cluster * diskdata.bytes_per_sector);
+}
+
+
+/***********************************************************************************************
+ * Do_Record_Playback -- handles saving/loading map pos & current object *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 08/15/1995 BRR : Created. *
+ *=============================================================================================*/
+static void Do_Record_Playback(void)
+{
+ int count;
+ TARGET tgt;
+ int i;
+ COORDINATE coord;
+ ObjectClass * obj;
+ unsigned long sum;
+ unsigned long sum2;
+ unsigned long ltgt;
+
+ /*
+ ** Record a game
+ */
+ if (Session.Record) {
+
+ /*
+ ** Save the map's location
+ */
+ Session.RecordFile.Write(&Map.DesiredTacticalCoord,
+ sizeof (Map.DesiredTacticalCoord));
+
+ /*
+ ** Save the current object list count
+ */
+ count = CurrentObject.Count();
+ Session.RecordFile.Write(&count, sizeof(count));
+
+ /*
+ ** Save a CRC of the selected-object list.
+ */
+ sum = 0;
+ for (i = 0; i < count; i++) {
+ ltgt = (unsigned long)(CurrentObject[i]->As_Target());
+ sum += ltgt;
+ }
+ Session.RecordFile.Write (&sum, sizeof(sum));
+
+ /*
+ ** Save all selected objects.
+ */
+ for (i = 0; i < count; i++) {
+ tgt = CurrentObject[i]->As_Target();
+ Session.RecordFile.Write (&tgt, sizeof(tgt));
+ }
+
+ //
+ // Save team-selection and formation events
+ //
+ Session.RecordFile.Write (&TeamEvent, sizeof(TeamEvent));
+ Session.RecordFile.Write (&TeamNumber, sizeof(TeamNumber));
+ Session.RecordFile.Write (&FormationEvent, sizeof(FormationEvent));
+ Session.RecordFile.Write (TeamMaxSpeed, sizeof(TeamMaxSpeed));
+ Session.RecordFile.Write (TeamSpeed, sizeof(TeamSpeed));
+ Session.RecordFile.Write (&FormMove, sizeof(FormMove));
+ Session.RecordFile.Write (&FormSpeed, sizeof(FormSpeed));
+ Session.RecordFile.Write (&FormMaxSpeed, sizeof(FormMaxSpeed));
+ TeamEvent = 0;
+ TeamNumber = 0;
+ FormationEvent = 0;
+ }
+
+ /*
+ ** Play back a game ("attract" mode)
+ */
+ if (Session.Play) {
+
+ /*
+ ** Read & set the map's location.
+ */
+ if (Session.RecordFile.Read(&coord, sizeof(coord))==sizeof(coord)) {
+ if (coord != Map.DesiredTacticalCoord) {
+ Map.Set_Tactical_Position(coord);
+ }
+ }
+
+ if (Session.RecordFile.Read(&count, sizeof(count))==sizeof(count)) {
+ /*
+ ** Compute a CRC of the current object-selection list.
+ */
+ sum = 0;
+ for (i = 0; i < CurrentObject.Count(); i++) {
+ ltgt = (unsigned long)(CurrentObject[i]->As_Target());
+ sum += ltgt;
+ }
+
+ /*
+ ** Load the CRC of the objects on disk; if it doesn't match, select
+ ** all objects as they're loaded.
+ */
+ Session.RecordFile.Read (&sum2, sizeof(sum2));
+ if (sum2 != sum) {
+ Unselect_All();
+ }
+
+ AllowVoice = true;
+
+ for (i = 0; i < count; i++) {
+ if (Session.RecordFile.Read (&tgt, sizeof(tgt))==sizeof(tgt)) {
+ obj = As_Object(tgt);
+ if (obj && (sum2 != sum)) {
+ obj->Select();
+ AllowVoice = false;
+ }
+ }
+ }
+
+ AllowVoice = true;
+
+ }
+
+ //
+ // Save team-selection and formation events
+ //
+ Session.RecordFile.Read (&TeamEvent, sizeof(TeamEvent));
+ Session.RecordFile.Read (&TeamNumber, sizeof(TeamNumber));
+ Session.RecordFile.Read (&FormationEvent, sizeof(FormationEvent));
+ if (TeamEvent) {
+ Handle_Team(TeamNumber, TeamEvent - 1);
+ }
+ if (FormationEvent) {
+ Toggle_Formation();
+ }
+
+Session.RecordFile.Read (TeamMaxSpeed, sizeof(TeamMaxSpeed));
+Session.RecordFile.Read (TeamSpeed, sizeof(TeamSpeed));
+Session.RecordFile.Read (&FormMove, sizeof(FormMove));
+Session.RecordFile.Read (&FormSpeed, sizeof(FormSpeed));
+Session.RecordFile.Read (&FormMaxSpeed, sizeof(FormMaxSpeed));
+ /*
+ ** The map isn't drawn in playback mode, so draw it here.
+ */
+ Map.Render();
+ }
+}
+
+
+/***********************************************************************************************
+ * Hires_Load -- Allocates memory for, and loads, a resolution dependant file. *
+ * *
+ * *
+ * *
+ * INPUT: Name of file to load *
+ * *
+ * OUTPUT: Ptr to loaded file *
+ * *
+ * WARNINGS: Caller is responsible for releasing the memory allocated *
+ * *
+ * *
+ * HISTORY: *
+ * 5/13/96 3:20PM ST : Created *
+ *=============================================================================================*/
+void * Hires_Load(char * name)
+{
+ char filename[30];
+ int length;
+ void * return_ptr;
+
+#ifdef WIN32
+ sprintf(filename, "H%s", name);
+#else
+ strcpy(filename, name);
+#endif
+ CCFileClass file (filename);
+
+ if (file.Is_Available()) {
+
+ length = file.Size();
+ return_ptr = new char[length];
+ file.Read(return_ptr, length);
+ return (return_ptr);
+
+ } else {
+ return (NULL);
+ }
+}
+
+
+/***********************************************************************************************
+ * Crate_From_Name -- Given a crate name convert it to a crate type. *
+ * *
+ * Use this routine to convert an ASCII crate name into a crate type. If no match could *
+ * be found, then CRATE_MONEY is assumed. *
+ * *
+ * INPUT: name -- Pointer to the crate name text to convert into a crate type. *
+ * *
+ * OUTPUT: Returns with the crate name converted into a crate type. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/12/1996 JLB : Created. *
+ *=============================================================================================*/
+CrateType Crate_From_Name(char const * name)
+{
+ if (name != NULL) {
+ for (CrateType crate = CRATE_FIRST; crate < CRATE_COUNT; crate++) {
+ if (stricmp(name, CrateNames[crate]) == 0) return(crate);
+ }
+ }
+ return(CRATE_MONEY);
+}
+
+
+/***********************************************************************************************
+ * Owner_From_Name -- Convert an owner name into a bitfield. *
+ * *
+ * This will take an owner specification and convert it into a bitfield that represents *
+ * it. Sometimes this will be just a single house bit, but other times it could be *
+ * all the allies or soviet house bits combined. *
+ * *
+ * INPUT: text -- Pointer to the text to convert into a house bitfield. *
+ * *
+ * OUTPUT: Returns with the houses specified. The value is in the form of a bit field with *
+ * one bit per house type. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/12/1996 JLB : Created. *
+ *=============================================================================================*/
+int Owner_From_Name(char const * text)
+{
+ int ownable = 0;
+ if (stricmp(text, "soviet") == 0) {
+ ownable |= HOUSEF_SOVIET;
+ } else {
+ if (stricmp(text, "allies") == 0 || stricmp(text, "allied") == 0) {
+ ownable |= HOUSEF_ALLIES;
+ } else {
+ HousesType h = HouseTypeClass::From_Name(text);
+ if (h != HOUSE_NONE && (h < HOUSE_MULTI1 || h > HOUSE_MULTI8)) {
+ ownable |= (1 << h);
+ }
+ }
+ }
+ return(ownable);
+}
+
+
+/***********************************************************************************************
+ * Shake_The_Screen -- Dispatcher that shakes the screen. *
+ * *
+ * This routine will shake the game screen the number of shakes requested. *
+ * *
+ * INPUT: shakes -- The number of shakes to shake the screen. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 09/04/1996 BWG : Created. *
+ *=============================================================================================*/
+void Shake_The_Screen(int shakes)
+{
+#ifdef WIN32
+ shakes += shakes;
+
+ Hide_Mouse();
+ SeenPage.Blit(HidPage);
+ int oldyoff = 0;
+ int newyoff = 0;
+ while(shakes--) {
+ int x = TickCount;
+// CountDownTimer = 1;
+ do {
+ newyoff = Sim_Random_Pick(0,2) - 1;
+ } while (newyoff == oldyoff);
+ switch (newyoff) {
+ case -1:
+ HidPage.Blit(SeenPage, 0,2, 0,0, 640,398);
+ break;
+ case 0:
+ HidPage.Blit(SeenPage);
+ break;
+ case 1:
+ HidPage.Blit(SeenPage, 0,0, 0,2, 640,398);
+ break;
+ } while (x == TickCount);
+// } while (CountDownTimer != 0) ;
+ }
+ HidPage.Blit(SeenPage);
+ Show_Mouse();
+#else
+ Shake_Screen(shakes);
+#endif
+}
+
+
+/***********************************************************************************************
+ * List_Copy -- Makes a copy of a cell offset list. *
+ * *
+ * This routine will make a copy of a cell offset list. It will only copy the significant *
+ * elements of the list limited by the maximum length specified. *
+ * *
+ * INPUT: source -- Pointer to a cell offset list. *
+ * *
+ * len -- The maximum number of cell offset elements to store in to the *
+ * destination list pointer. *
+ * *
+ * dest -- Pointer to the destination list to store the copy into. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: Ensure that the destination list is large enough to hold the list copy. *
+ * *
+ * HISTORY: *
+ * 09/04/1996 JLB : Created. *
+ *=============================================================================================*/
+void List_Copy(short const * source, int len, short * dest)
+{
+ if (dest == NULL || dest == NULL) {
+ return;
+ }
+
+ while (len > 0) {
+ *dest = *source;
+ if (*dest == REFRESH_EOL) break;
+ dest++;
+ source++;
+ len--;
+ }
+}
+
+
+
+#if 0
+//
+// Boy, this function sure is crummy
+//
+void Crummy(int crumb1, int crumb2)
+{
+ if (Debug_Check_Map && Debug_Heap_Dump) {
+ Mono_Printf("Hi, I'm Crummy. And so are these: %d, %d\n",crumb1,crumb2);
+ }
+}
+#endif
+
+
+
+/***********************************************************************************************
+ * Game_Registry_Key -- Returns pointer to string containing the registry subkey for the game.
+ * This is located under HKEY_LOCAL_MACHINE.
+ * HISTORY:
+ * 11/19/98 ajw : Created
+ *=============================================================================================*/
+const char* Game_Registry_Key()
+{
+#ifdef ENGLISH
+ static char szKey[] = "SOFTWARE\\Westwood\\Red Alert Windows 95 Edition";
+#else
+#ifdef GERMAN
+ static char szKey[] = "SOFTWARE\\Westwood\\Alarmstufe Rot Windows 95 Edition";
+#else
+ static char szKey[] = "SOFTWARE\\Westwood\\Alerte Rouge version Windows 95";
+#endif
+#endif
+ return szKey;
+}
+
+
+/***********************************************************************************************
+ * Is_Counterstrike_Installed -- Function to determine the availability of the CS expansion *
+ * *
+ * *
+ * *
+ * INPUT: Nothing *
+ * *
+ * OUTPUT: true if Counterstrike is present *
+ * *
+ * WARNINGS: None *
+ * *
+ * HISTORY: *
+ * 4/1/97 11:39PM ST : Created *
+ *=============================================================================================*/
+bool Is_Counterstrike_Installed (void)
+{
+ // ajw 9/29/98
+ static bool bAlreadyChecked = false;
+ static bool bInstalled = false;
+
+ if( !bAlreadyChecked )
+ {
+ HKEY hKey;
+ if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, Game_Registry_Key(), 0, KEY_READ, &hKey ) != ERROR_SUCCESS )
+ return false;
+ DWORD dwValue;
+ DWORD dwBufSize = sizeof( DWORD );
+ if( RegQueryValueEx( hKey, "CStrikeInstalled", 0, NULL, (LPBYTE)&dwValue, &dwBufSize ) != ERROR_SUCCESS )
+ bInstalled = false;
+ else
+ bInstalled = (bool)dwValue; // (Presumably true, if it's there...)
+
+ RegCloseKey( hKey );
+ bAlreadyChecked = true;
+ }
+ return bInstalled;
+
+// RawFileClass file("EXPAND.MIX");
+// return(file.Is_Available());
+}
+
+#ifdef FIXIT_CSII // checked - ajw 9/28/98
+/***********************************************************************************************
+ *=============================================================================================*/
+bool Is_Aftermath_Installed (void)
+{
+ // ajw 9/29/98
+ static bool bAlreadyChecked = false;
+ static bool bInstalled = false;
+
+ if( !bAlreadyChecked )
+ {
+ HKEY hKey;
+ if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, Game_Registry_Key(), 0, KEY_READ, &hKey ) != ERROR_SUCCESS )
+ return false;
+ DWORD dwValue;
+ DWORD dwBufSize = sizeof( DWORD );
+ if( RegQueryValueEx( hKey, "AftermathInstalled", 0, NULL, (LPBYTE)&dwValue, &dwBufSize ) != ERROR_SUCCESS )
+ bInstalled = false;
+ else
+ bInstalled = (bool)dwValue; // (Presumably true, if it's there...)
+
+ RegCloseKey( hKey );
+ bAlreadyChecked = true;
+ }
+ return bInstalled;
+
+// RawFileClass file("EXPAND2.MIX");
+// return(file.Is_Available());
+}
+#endif
+
+#ifdef FIXIT_CSII // checked - ajw 9/28/98
+void Enable_Secret_Units(void)
+{
+#if 0
+ SecretUnitsEnabled=true;
+ UnitTypeClass::As_Reference(UNIT_PHASE).Level=10;
+ VesselTypeClass::As_Reference(VESSEL_CARRIER).Level=10;
+ for (int index = 0; index < Buildings.Count(); index++) {
+ Buildings.Ptr(index)->Update_Buildables();
+ }
+#endif
+}
+#endif
+
+#ifdef FIXIT_VERSION_3
+bool Force_Scenario_Available( const char* szName )
+{
+ // Calls Force_CD_Available based on type of scenario. szName is assumed to be an official scenario here.
+ if( Is_Mission_Counterstrike( (char*)szName ) )
+ {
+// debugprint( "Force_Scenario_Available requiring disk 4...\n" );
+ return Force_CD_Available( 4 );
+ }
+ else if( Is_Mission_Aftermath( (char*)szName ) )
+ {
+// debugprint( "Force_Scenario_Available requiring disk 3...\n" );
+ return Force_CD_Available( 3 );
+ }
+ return true;
+}
+#endif
diff --git a/CODE/CONQUER.CPP.BAK b/CODE/CONQUER.CPP.BAK
new file mode 100644
index 0000000..891c8b9
--- /dev/null
+++ b/CODE/CONQUER.CPP.BAK
@@ -0,0 +1,4185 @@
+/*
+** Command & Conquer Red Alert(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&c0\vcs\code\conquer.cpv 4.83 24 Oct 1996 12:55: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 : CONQUER.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : April 3, 1991 *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * CC_Draw_Shape -- Custom draw shape handler. *
+ * Call_Back -- Main game maintenance callback routine. *
+ * Color_Cycle -- Handle the general palette color cycling. *
+ * Crate_From_Name -- Given a crate name convert it to a crate type. *
+ * Disk_Space_Available -- returns bytes of free disk space *
+ * Do_Record_Playback -- handles saving/loading map pos & current object *
+ * Fading_Table_Name -- Builds a theater specific fading table name. *
+ * Fetch_Techno_Type -- Convert type and ID into TechnoTypeClass pointer. *
+ * Force_CD_Available -- Ensures that specified CD is available. *
+ * Get_Radar_Icon -- Builds and alloc a radar icon from a shape file *
+ * Handle_Team -- Processes team selection command. *
+ * Handle_View -- Either records or restores the tactical view. *
+ * KN_To_Facing -- Converts a keyboard input number into a facing value. *
+ * Keyboard_Process -- Processes the tactical map input codes. *
+ * Language_Name -- Build filename for current language. *
+ * List_Copy -- Makes a copy of a cell offset list. *
+ * Main_Game -- Main game startup routine. *
+ * Main_Loop -- This is the main game loop (as a single loop). *
+ * Map_Edit_Loop -- a mini-main loop for map edit mode only *
+ * Message_Input -- allows inter-player message input processing *
+ * MixFileHandler -- Handles VQ file access. *
+ * Name_From_Source -- retrieves the name for the given SourceType *
+ * Owner_From_Name -- Convert an owner name into a bitfield. *
+ * Play_Movie -- Plays a VQ movie. *
+ * Shake_The_Screen -- Dispatcher that shakes the screen. *
+ * Shape_Dimensions -- Determine the minimum rectangle for the shape. *
+ * Source_From_Name -- Converts ASCII name into SourceType. *
+ * Sync_Delay -- Forces the game into a 15 FPS rate. *
+ * Theater_From_Name -- Converts ASCII name into a theater number. *
+ * Unselect_All -- Causes all selected objects to become unselected. *
+ * VQ_Call_Back -- Maintenance callback used for VQ movies. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#ifdef TESTCODE
+class A {
+ public:
+ enum {VAR=1};
+};
+
+template
+class B {
+ public:
+ enum {VAR2=T::VAR}; // this is the line in question.
+};
+
+B test;
+#endif
+
+
+
+#include "function.h"
+#ifdef WIN32
+#include "tcpip.h"
+#else
+#include "fakesock.h"
+TcpipManagerClass Winsock;
+#endif
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "ccdde.h"
+#include "vortex.h"
+
+#define SHAPE_TRANS 0x40
+
+void * Get_Shape_Header_Data(void * ptr);
+extern bool Spawn_WChat(void);
+
+/****************************************
+** Function prototypes for this module **
+*****************************************/
+bool Main_Loop(void);
+void Keyboard_Process(KeyNumType & input);
+static void Message_Input(KeyNumType &input);
+static void Color_Cycle(void);
+bool Map_Edit_Loop(void);
+
+extern "C" {
+ bool UseOldShapeDraw = false;
+}
+
+#ifdef CHEAT_KEYS
+void Dump_Heap_Pointers( void );
+void Error_In_Heap_Pointers( char * string );
+#endif
+static void Do_Record_Playback(void);
+
+void Toggle_Formation(void);
+
+extern "C" {
+ extern char * __nheapbeg;
+}
+
+//
+// Special module globals for recording and playback
+//
+char TeamEvent = 0; // 0 = no event, 1,2,3 = team event type
+char TeamNumber = 0; // which team was selected? (1-9)
+char FormationEvent = 0; // 0 = no event, 1 = formation was toggled
+
+
+ /* -----------------10/14/96 7:29PM------------------
+
+ --------------------------------------------------*/
+
+/***********************************************************************************************
+ * Main_Game -- Main game startup routine. *
+ * *
+ * This is the first official routine of the game. It handles game initialization and *
+ * the main game loop control. *
+ * *
+ * Initialization: *
+ * - Init_Game handles one-time-only inits *
+ * - Select_Game is responsible for initializations required for each new game played *
+ * (these may be different depending on whether a multiplayer game is selected, and *
+ * other parameters) *
+ * - This routine performs any un-inits required, both for each game played, and one-time *
+ * *
+ * INPUT: argc -- Number of command line arguments (including program name itself). *
+ * *
+ * argv -- Array of command line argument pointers. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 10/01/1994 JLB : Created. *
+ *=============================================================================================*/
+void Main_Game(int argc, char * argv[])
+{
+ static bool fade = true;
+
+ /*
+ ** Perform one-time-only initializations
+ */
+ if (!Init_Game(argc, argv)) {
+ return;
+ }
+
+ /*
+ ** Game processing loop:
+ ** 1) Select which game to play, or whether to exit (don't fade the palette
+ ** on the first game selection, but fade it in on subsequent calls)
+ ** 2) Invoke either the main-loop routine, or the editor-loop routine,
+ ** until they indicate that the user wants to exit the scenario.
+ */
+ while (Select_Game(fade)) {
+ fade = false;
+ ScenarioInit = 0; // Kludge.
+
+ fade = true;
+
+ /*
+ ** Initialise the color lookup tables for the chronal vortex
+ */
+ ChronalVortex.Stop();
+ ChronalVortex.Setup_Remap_Tables(Scen.Theater);
+
+ /*
+ ** Make the game screen visible, clear the keyboard buffer of spurious
+ ** values, and then show the mouse. This PRESUMES that Select_Game() has
+ ** told the map to draw itself.
+ */
+ GamePalette.Set(FADE_PALETTE_MEDIUM);
+ Keyboard->Clear();
+
+ /*
+ ** Only show the mouse if we're not playing back a recording.
+ */
+ if (Session.Play) {
+ Hide_Mouse();
+ TeamEvent = 0;
+ TeamNumber = 0;
+ FormationEvent = 0;
+ } else {
+ Show_Mouse();
+ }
+
+#ifdef WIN32
+ if (Session.Type == GAME_INTERNET) {
+ Register_Game_Start_Time();
+ GameStatisticsPacketSent = false;
+ PacketLater = NULL;
+ ConnectionLost = false;
+ } else {
+ DDEServer.Disable();
+ }
+#endif //WIN32
+
+#ifdef SCENARIO_EDITOR
+ /*
+ ** Scenario-editor version of main-loop processing
+ */
+ for (;;) {
+ /*
+ ** Non-scenario-editor-mode: call the game's main loop
+ */
+ if (!Debug_Map) {
+ TimeQuake = false;
+ if (Main_Loop()) {
+ break;
+ }
+
+ if (SpecialDialog != SDLG_NONE) {
+ switch (SpecialDialog) {
+ case SDLG_SPECIAL:
+ Map.Help_Text(TXT_NONE);
+ Map.Override_Mouse_Shape(MOUSE_NORMAL, false);
+ Special_Dialog();
+ Map.Revert_Mouse_Shape();
+ SpecialDialog = SDLG_NONE;
+ break;
+
+ case SDLG_OPTIONS:
+ Map.Help_Text(TXT_NONE);
+ Map.Override_Mouse_Shape(MOUSE_NORMAL, false);
+ Options.Process();
+ Map.Revert_Mouse_Shape();
+ SpecialDialog = SDLG_NONE;
+ break;
+
+ case SDLG_SURRENDER:
+ Map.Help_Text(TXT_NONE);
+ Map.Override_Mouse_Shape(MOUSE_NORMAL, false);
+ if (Surrender_Dialog(TXT_SURRENDER)) {
+ PlayerPtr->Flag_To_Lose();
+ }
+ SpecialDialog = SDLG_NONE;
+ Map.Revert_Mouse_Shape();
+ break;
+
+ default:
+ break;
+ }
+ }
+ } else {
+
+ /*
+ ** Scenario-editor-mode: call the editor's main loop
+ */
+ if (Map_Edit_Loop()) {
+ break;
+ }
+ }
+ }
+#else
+ /*
+ ** Non-editor version of main-loop processing
+ */
+ for (;;) {
+ TimeQuake = false;
+ /*
+ **call the game's main loop
+ */
+ //VG_MONO
+ Mono_Print("About to call Main Loop in Main Game/n/n");
+
+ if (Main_Loop()) {
+ break;
+ }
+
+ /*
+ ** If the SpecialDialog flag is set, invoke the given special dialog.
+ ** This must be done outside the main loop, since the dialog will call
+ ** Main_Loop(), allowing the game to run in the background.
+ */
+ if (SpecialDialog != SDLG_NONE) {
+ switch (SpecialDialog) {
+ case SDLG_SPECIAL:
+ Map.Help_Text(TXT_NONE);
+ Map.Override_Mouse_Shape(MOUSE_NORMAL, false);
+ Special_Dialog();
+ Map.Revert_Mouse_Shape();
+ SpecialDialog = SDLG_NONE;
+ break;
+
+ case SDLG_OPTIONS:
+ Map.Help_Text(TXT_NONE);
+ Map.Override_Mouse_Shape(MOUSE_NORMAL, false);
+ Options.Process();
+ Map.Revert_Mouse_Shape();
+ SpecialDialog = SDLG_NONE;
+ break;
+
+ case SDLG_SURRENDER:
+ Map.Help_Text(TXT_NONE);
+ Map.Override_Mouse_Shape(MOUSE_NORMAL, false);
+ if (Surrender_Dialog(TXT_SURRENDER)) {
+ OutList.Add(EventClass(EventClass::DESTRUCT));
+ }
+ SpecialDialog = SDLG_NONE;
+ Map.Revert_Mouse_Shape();
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+#endif
+
+
+#ifdef WIN32
+ /*
+ ** Send the game stats to WChat if we havnt already done so
+ */
+ if (!GameStatisticsPacketSent && PacketLater) {
+ Send_Statistics_Packet();
+ }
+#endif //WIN32
+
+ /*
+ ** Scenario is done; fade palette to black
+ */
+ BlackPalette.Set(FADE_PALETTE_SLOW);
+ VisiblePage.Clear();
+
+ /*
+ ** Un-initialize whatever needs it, for each game played.
+ **
+ ** Shut down either the modem or network; they'll get re-initialized if
+ ** the user selections those options again in Select_Game(). This
+ ** "re-boots" the modem & network code, which I currently feel is safer
+ ** than just letting it hang around.
+ ** (Skip this step if we're in playback mode; the modem or net won't have
+ ** been initialized in that case.)
+ */
+ if (Session.Record || Session.Play) {
+ Session.RecordFile.Close();
+ }
+
+ if (Session.Type == GAME_NULL_MODEM || Session.Type == GAME_MODEM) {
+ if (!Session.Play) {
+ Modem_Signoff();
+ }
+ } else {
+ if (Session.Type == GAME_IPX) {
+ if (!Session.Play) {
+ Shutdown_Network();
+ }
+ }
+ }
+
+ /*
+ ** If we're playing back, the mouse will be hidden; show it.
+ ** Also, set all variables back to normal, to return to the main menu.
+ */
+ if (Session.Play) {
+ Show_Mouse();
+ Session.Type = GAME_NORMAL;
+ Session.Play = 0;
+ }
+#ifdef WIN32
+ if (Special.IsFromWChat) {
+ Shutdown_Network(); // Clear up the pseudo IPX stuff
+ Winsock.Close();
+ Special.IsFromWChat = false;
+ SpawnedFromWChat = false;
+ DDEServer.Delete_MPlayer_Game_Info(); //Make sure we dont use the same start packet twice
+ Session.Type = GAME_NORMAL; //Have to do this or we will got straight to the multiplayer menu
+ Spawn_WChat(); //Will switch back to Wchat. It must be there because its been poking us
+ }
+#endif //WIN32
+ }
+
+ /*
+ ** Free the scenario description buffers
+ */
+ Session.Free_Scenario_Descriptions();
+}
+
+
+/***********************************************************************************************
+ * Keyboard_Process -- Processes the tactical map input codes. *
+ * *
+ * This routine is used to process the input codes while the player *
+ * has the tactical map displayed. It handles all the keys that *
+ * are appropriate to that mode. *
+ * *
+ * INPUT: input -- Input code as returned from Input_Num(). *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 01/21/1992 JLB : Created. *
+ * 07/04/1995 JLB : Handles team and map control hotkeys. *
+ *=============================================================================================*/
+void Keyboard_Process(KeyNumType & input)
+{
+ ObjectClass * obj;
+ int index;
+
+ /*
+ ** Don't do anything if there is not keyboard event.
+ */
+ if (input == KN_NONE) {
+ return;
+ }
+ /*
+ ** For network & modem, process user input for inter-player messages.
+ */
+ Message_Input(input);
+
+#ifdef WIN32
+ /*
+ ** The VK_BIT must be stripped from the "plain" value of the key so that a comparison to
+ ** KN_1, for example, will yield TRUE if in fact the "1" key was pressed.
+ */
+
+ KeyNumType plain = KeyNumType(input & ~(WWKEY_SHIFT_BIT|WWKEY_ALT_BIT|WWKEY_CTRL_BIT|WWKEY_VK_BIT));
+ KeyNumType key = KeyNumType(input & ~WWKEY_VK_BIT);
+
+
+#else
+ KeyNumType plain = KeyNumType(input & ~(KN_SHIFT_BIT|KN_ALT_BIT|KN_CTRL_BIT));
+ KeyNumType key = plain;
+#endif
+
+#ifdef CHEAT_KEYS
+
+ if (Debug_Flag) {
+ HousesType h;
+
+ switch (int(input)) {
+ case int(int(KN_M) | int(KN_SHIFT_BIT)):
+ case int(int(KN_M) | int(KN_ALT_BIT)):
+ case int(int(KN_M) | int(KN_CTRL_BIT)):
+ for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) {
+ Houses.Ptr(h)->Refund_Money(10000);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+#endif
+
+#ifdef VIRGIN_CHEAT_KEYS
+ if (Debug_Playtest && input == (KN_W|KN_ALT_BIT)) {
+ PlayerPtr->Blockage = false;
+ PlayerPtr->Flag_To_Win();
+ }
+#endif
+
+#ifdef CHEAT_KEYS
+#ifdef WIN32
+ if (Debug_Playtest && input == (KA_W|KN_ALT_BIT)) {
+#else
+ if (Debug_Playtest && input == (KN_W|KN_ALT_BIT)) {
+#endif
+ PlayerPtr->Blockage = false;
+ PlayerPtr->Flag_To_Win();
+ }
+
+ if ((Debug_Flag || Debug_Playtest) && plain == KN_F4) {
+ if (Session.Type == GAME_NORMAL) {
+ Debug_Unshroud = (Debug_Unshroud == false);
+ Map.Flag_To_Redraw(true);
+ }
+ }
+
+ if (Debug_Flag && input == KN_SLASH) {
+ if (Session.Type != GAME_NORMAL) {
+ SpecialDialog = SDLG_SPECIAL;
+ input = KN_NONE;
+ } else {
+ Special_Dialog();
+ }
+ }
+#endif
+
+ /*
+ ** Process prerecorded team selection. This will be an additive select
+ ** if the SHIFT key is held down. It will create the team if the
+ ** CTRL or ALT key is held down.
+ */
+ int action = 0;
+#ifdef WIN32
+ if (input & WWKEY_SHIFT_BIT) action = 1;
+ if (input & WWKEY_ALT_BIT) action = 3;
+ if (input & WWKEY_CTRL_BIT) action = 2;
+#else
+ if (input & KN_SHIFT_BIT) action = 1;
+ if (input & KN_ALT_BIT) action = 3;
+ if (input & KN_CTRL_BIT) action = 2;
+#endif
+
+ /*
+ ** If the "N" key is pressed, then select the next object.
+ */
+ if (key != 0 && key == Options.KeyNext) {
+ if (action) {
+ obj = Map.Prev_Object(CurrentObject.Count() ? CurrentObject[0] : NULL);
+ } else {
+ obj = Map.Next_Object(CurrentObject.Count() ? CurrentObject[0] : NULL);
+ }
+ if (obj != NULL) {
+ Unselect_All();
+ obj->Select();
+ Map.Center_Map();
+ Map.Flag_To_Redraw(true);
+ }
+ input = KN_NONE;
+ }
+ if (key != 0 && key == Options.KeyPrevious) {
+ if (action) {
+ obj = Map.Next_Object(CurrentObject.Count() ? CurrentObject[0] : NULL);
+ } else {
+ obj = Map.Prev_Object(CurrentObject.Count() ? CurrentObject[0] : NULL);
+ }
+ if (obj != NULL) {
+ Unselect_All();
+ obj->Select();
+ Map.Center_Map();
+ Map.Flag_To_Redraw(true);
+ }
+ input = KN_NONE;
+ }
+
+
+ /*
+ ** All selected units will go into idle mode.
+ */
+ if (key != 0 && key == Options.KeyStop) {
+ if (CurrentObject.Count()) {
+ for (index = 0; index < CurrentObject.Count(); index++) {
+ ObjectClass const * tech = CurrentObject[index];
+
+ if (tech != NULL && (tech->Can_Player_Move() || (tech->Can_Player_Fire() && tech->What_Am_I() != RTTI_BUILDING))) {
+ OutList.Add(EventClass(EventClass::IDLE, TargetClass(tech)));
+ }
+ }
+ }
+ input = KN_NONE;
+ }
+
+ /*
+ ** All selected units will attempt to go into guard area mode.
+ */
+ if (key != 0 && key == Options.KeyGuard) {
+ if (CurrentObject.Count()) {
+ for (index = 0; index < CurrentObject.Count(); index++) {
+ ObjectClass const * tech = CurrentObject[index];
+
+ if (tech != NULL && tech->Can_Player_Move() && tech->Can_Player_Fire()) {
+ OutList.Add(EventClass(TargetClass(tech), MISSION_GUARD_AREA));
+ }
+ }
+ }
+ input = KN_NONE;
+ }
+
+ /*
+ ** All selected units will attempt to scatter.
+ */
+ if (key != 0 && key == Options.KeyScatter) {
+ if (CurrentObject.Count()) {
+ for (index = 0; index < CurrentObject.Count(); index++) {
+ ObjectClass const * tech = CurrentObject[index];
+
+ if (tech != NULL && tech->Can_Player_Move()) {
+ OutList.Add(EventClass(EventClass::SCATTER, TargetClass(tech)));
+ }
+ }
+ }
+ input = KN_NONE;
+ }
+
+ /*
+ ** Center the map around the currently selected objects. If no
+ ** objects are selected, then fall into the home case.
+ */
+ if (key != 0 && (key == Options.KeyHome1 || key == Options.KeyHome2)) {
+ if (CurrentObject.Count()) {
+ Map.Center_Map();
+#ifdef WIN32
+ Map.Flag_To_Redraw(true);
+#endif
+ input = KN_NONE;
+ } else {
+ input = Options.KeyBase;
+ }
+ }
+
+ /*
+ ** Center the map about the construction yard or construction vehicle
+ ** if one is present.
+ */
+ if (key != 0 && key == Options.KeyBase) {
+ Unselect_All();
+ if (PlayerPtr->CurBuildings) {
+ for (index = 0; index < Buildings.Count(); index++) {
+ BuildingClass * building = Buildings.Ptr(index);
+
+ if (building != NULL && !building->IsInLimbo && building->House == PlayerPtr && *building == STRUCT_CONST) {
+ Unselect_All();
+ building->Select();
+ if (building->IsLeader) break;
+ }
+ }
+ }
+ if (CurrentObject.Count() == 0 && PlayerPtr->CurUnits) {
+ for (index = 0; index < Units.Count(); index++) {
+ UnitClass * unit = Units.Ptr(index);
+
+ if (unit != NULL && !unit->IsInLimbo && unit->House == PlayerPtr && *unit == UNIT_MCV) {
+ Unselect_All();
+ unit->Select();
+ break;
+ }
+ }
+ }
+ if (CurrentObject.Count()) {
+ Map.Center_Map();
+ } else {
+ if (PlayerPtr->Center != 0) {
+ Map.Center_Map(PlayerPtr->Center);
+ }
+ }
+ Map.Flag_To_Redraw(true);
+ input = KN_NONE;
+ }
+
+ /*
+ ** Toggle the status of formation for the current team
+ */
+ if (key != 0 && key == Options.KeyFormation) {
+ Toggle_Formation();
+ input = KN_NONE;
+ }
+
+#ifdef TOFIX
+ /*
+ ** For multiplayer, 'R' pops up the surrender dialog.
+ */
+ if (input != 0 && input == Options.KeyResign) {
+ if (!PlayerLoses && /*Session.Type != GAME_NORMAL &&*/ !PlayerPtr->IsDefeated) {
+ SpecialDialog = SDLG_SURRENDER;
+ input = KN_NONE;
+ }
+ input = KN_NONE;
+ }
+#endif
+
+ /*
+ ** Handle making and breaking alliances.
+ */
+ if (key != 0 && key == Options.KeyAlliance) {
+ if (Session.Type != GAME_NORMAL || Debug_Flag) {
+ if (CurrentObject.Count() && !PlayerPtr->IsDefeated) {
+ if (CurrentObject[0]->Owner() != PlayerPtr->Class->House) {
+ OutList.Add(EventClass(EventClass::ALLY, CurrentObject[0]->Owner()));
+ }
+ }
+ }
+ input = KN_NONE;
+ }
+
+ /*
+ ** Select all the units on the current display. This is equivalent to
+ ** drag selecting the whole view.
+ */
+ if (key != 0 && key == Options.KeySelectView) {
+ Map.Select_These(0x00000000, XY_Coord(Map.TacLeptonWidth, Map.TacLeptonHeight));
+ input = KN_NONE;
+ }
+
+ /*
+ ** Toggles the repair state similarly to pressing the repair button.
+ */
+ if (key != 0 && key == Options.KeyRepair) {
+ Map.Repair_Mode_Control(-1);
+ input = KN_NONE;
+ }
+
+ /*
+ ** Toggles the sell state similarly to pressing the sell button.
+ */
+ if (key != 0 && key == Options.KeySell) {
+ Map.Sell_Mode_Control(-1);
+ input = KN_NONE;
+ }
+
+ /*
+ ** Toggles the map zoom mode similarly to pressing the map button.
+ */
+ if (key != 0 && key == Options.KeyMap) {
+ Map.Zoom_Mode_Control();
+ input = KN_NONE;
+ }
+
+ /*
+ ** Scrolls the sidebar up one slot.
+ */
+ if (key != 0 && key == Options.KeySidebarUp) {
+ Map.SidebarClass::Scroll(true, -1);
+ input = KN_NONE;
+ }
+
+ /*
+ ** Scrolls the sidebar down one slot.
+ */
+ if (key != 0 && key == Options.KeySidebarDown) {
+ Map.SidebarClass::Scroll(false, -1);
+ input = KN_NONE;
+ }
+
+ /*
+ ** Brings up the options dialog box.
+ */
+ if (key != 0 && (key == Options.KeyOption1 || key == Options.KeyOption2)) {
+ Map.Help_Text(TXT_NONE); // Turns off help text.
+ Queue_Options();
+ input = KN_NONE;
+ }
+
+ /*
+ ** Scrolls the tactical map in the direction specified.
+ */
+ int distance = CELL_LEPTON_W;
+ if (key != 0 && key == Options.KeyScrollLeft) {
+ Map.Scroll_Map(DIR_W, distance, true);
+ input = KN_NONE;
+ }
+ if (key != 0 && key == Options.KeyScrollRight) {
+ Map.Scroll_Map(DIR_E, distance, true);
+ input = KN_NONE;
+ }
+ if (key != 0 && key == Options.KeyScrollUp) {
+ Map.Scroll_Map(DIR_N, distance, true);
+ input = KN_NONE;
+ }
+ if (key != 0 && key == Options.KeyScrollDown) {
+ Map.Scroll_Map(DIR_S, distance, true);
+ input = KN_NONE;
+ }
+
+ /*
+ ** Teams are handled by the 10 special team keys. The manual comparison
+ ** to the KN numbers is because the Windows keyboard driver can vary
+ ** the base code number for the key depending on the shift or alt key
+ ** state!
+ */
+ if (input != 0 && (plain == Options.KeyTeam1 || plain == KN_1)) {
+ Handle_Team(0, action);
+ input = KN_NONE;
+ }
+ if (input != 0 && (plain == Options.KeyTeam2 || plain == KN_2)) {
+ Handle_Team(1, action);
+ input = KN_NONE;
+ }
+ if (input != 0 && (plain == Options.KeyTeam3 || plain == KN_3)) {
+ Handle_Team(2, action);
+ input = KN_NONE;
+ }
+ if (input != 0 && (plain == Options.KeyTeam4 || plain == KN_4)) {
+ Handle_Team(3, action);
+ input = KN_NONE;
+ }
+ if (input != 0 && (plain == Options.KeyTeam5 || plain == KN_5)) {
+ Handle_Team(4, action);
+ input = KN_NONE;
+ }
+ if (input != 0 && (plain == Options.KeyTeam6 || plain == KN_6)) {
+ Handle_Team(5, action);
+ input = KN_NONE;
+ }
+ if (input != 0 && (plain == Options.KeyTeam7 || plain == KN_7)) {
+ Handle_Team(6, action);
+ input = KN_NONE;
+ }
+ if (input != 0 && (plain == Options.KeyTeam8 || plain == KN_8)) {
+ Handle_Team(7, action);
+ input = KN_NONE;
+ }
+ if (input != 0 && (plain == Options.KeyTeam9 || plain == KN_9)) {
+ Handle_Team(8, action);
+ input = KN_NONE;
+ }
+ if (input != 0 && (plain == Options.KeyTeam10 || plain == KN_0)) {
+ Handle_Team(9, action);
+ input = KN_NONE;
+ }
+
+ /*
+ ** Handle the bookmark hotkeys.
+ */
+ if (input != 0 && plain == Options.KeyBookmark1 && !Debug_Map) {
+ Handle_View(0, action);
+ input = KN_NONE;
+ }
+ if (input != 0 && plain == Options.KeyBookmark2 && !Debug_Map) {
+ Handle_View(1, action);
+ input = KN_NONE;
+ }
+ if (input != 0 && plain == Options.KeyBookmark3 && !Debug_Map) {
+ Handle_View(2, action);
+ input = KN_NONE;
+ }
+ if (input != 0 && plain == Options.KeyBookmark4 && !Debug_Map) {
+ Handle_View(3, action);
+ input = KN_NONE;
+ }
+
+#ifdef CHEAT_KEYS
+ if (input != 0 && Debug_Flag && input && (input & KN_RLSE_BIT) == 0) {
+ Debug_Key(input);
+ }
+#endif
+}
+
+
+void Toggle_Formation(void) {
+ int team = -1;
+ long minx = 0x7FFFFFFFL, miny = 0x7FFFFFFFL;
+ long maxx = 0, maxy = 0;
+ int index;
+ bool setform = 0;
+
+ //
+ // Recording support
+ //
+ if (Session.Record) {
+ FormationEvent = 1;
+ }
+
+ /*
+ ** Find the first selected object that is a member of a team, and
+ ** register his group as the team we're using. Once we find the team
+ ** number, update the 'setform' flag to know whether we should be setting
+ ** the formation's offsets, or clearing them. If they currently have
+ ** illegal offsets (as in 0x80000000), then we're setting.
+ */
+ for (index = 0; index < Units.Count(); index++) {
+ UnitClass * obj = Units.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->IsSelected) {
+ team = obj->Group;
+ if (team != -1) {
+ setform = obj->XFormOffset == (int)0x80000000;
+ TeamSpeed[team] = SPEED_WHEEL;
+ TeamMaxSpeed[team] = MPH_LIGHT_SPEED;
+ break;
+ }
+ }
+ }
+ if (team == -1) {
+ for (index = 0; index < Infantry.Count(); index++) {
+ InfantryClass * obj = Infantry.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->IsSelected) {
+ team = obj->Group;
+ if (team != -1) {
+ setform = obj->XFormOffset == (int)0x80000000;
+ TeamSpeed[team] = SPEED_WHEEL;
+ TeamMaxSpeed[team] = MPH_LIGHT_SPEED;
+ break;
+ }
+ }
+ }
+ }
+
+ if (team == -1) {
+ for (index = 0; index < Vessels.Count(); index++) {
+ VesselClass * obj = Vessels.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->IsSelected) {
+ team = obj->Group;
+ if (team != -1) {
+ setform = obj->XFormOffset == 0x80000000UL;
+ TeamSpeed[team] = SPEED_WHEEL;
+ TeamMaxSpeed[team] = MPH_LIGHT_SPEED;
+ break;
+ }
+ }
+ }
+ }
+
+ if (team == -1) return;
+ /*
+ ** Now that we have a team, let's go set (or clear) the formation offsets.
+ */
+ for (index = 0; index < Units.Count(); index++) {
+ UnitClass * obj = Units.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) {
+ obj->Mark(MARK_CHANGE);
+ if (setform) {
+ long xc = Cell_X(Coord_Cell(obj->Center_Coord()));
+ long yc = Cell_Y(Coord_Cell(obj->Center_Coord()));
+ if (xc < minx) minx = xc;
+ if (xc > maxx) maxx = xc;
+ if (yc < miny) miny = yc;
+ if (yc > maxy) maxy = yc;
+ if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) {
+ TeamMaxSpeed[team] = obj->Class->MaxSpeed;
+ TeamSpeed[team] = obj->Class->Speed;
+ }
+ } else {
+ obj->XFormOffset = obj->YFormOffset = (int)0x80000000;
+ }
+ }
+ }
+
+ for (index = 0; index < Infantry.Count(); index++) {
+ InfantryClass * obj = Infantry.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) {
+ obj->Mark(MARK_CHANGE);
+ if (setform) {
+ long xc = Cell_X(Coord_Cell(obj->Center_Coord()));
+ long yc = Cell_Y(Coord_Cell(obj->Center_Coord()));
+ if (xc < minx) minx = xc;
+ if (xc > maxx) maxx = xc;
+ if (yc < miny) miny = yc;
+ if (yc > maxy) maxy = yc;
+ if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) {
+ TeamMaxSpeed[team] = obj->Class->MaxSpeed;
+ }
+ } else {
+ obj->XFormOffset = obj->YFormOffset = (int)0x80000000;
+ }
+ }
+ }
+
+ for (index = 0; index < Vessels.Count(); index++) {
+ VesselClass * obj = Vessels.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) {
+ obj->Mark(MARK_CHANGE);
+ if (setform) {
+ long xc = Cell_X(Coord_Cell(obj->Center_Coord()));
+ long yc = Cell_Y(Coord_Cell(obj->Center_Coord()));
+ if (xc < minx) minx = xc;
+ if (xc > maxx) maxx = xc;
+ if (yc < miny) miny = yc;
+ if (yc > maxy) maxy = yc;
+ if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) {
+ TeamMaxSpeed[team] = obj->Class->MaxSpeed;
+ }
+ } else {
+ obj->XFormOffset = obj->YFormOffset = 0x80000000UL;
+ }
+ }
+ }
+
+ /*
+ ** All the units have been counted to find the bounding rectangle and
+ ** center of the formation, or to clear their offsets. Now, if we're to
+ ** set them into formation, proceed to do so. Otherwise, bail.
+ */
+ if (setform) {
+ int centerx = (int)((maxx - minx)/2)+minx;
+ int centery = (int)((maxy - miny)/2)+miny;
+
+ for (index = 0; index < Units.Count(); index++) {
+ UnitClass * obj = Units.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) {
+ long xc = Cell_X(Coord_Cell(obj->Center_Coord()));
+ long yc = Cell_Y(Coord_Cell(obj->Center_Coord()));
+
+ obj->XFormOffset = xc - centerx;
+ obj->YFormOffset = yc - centery;
+ }
+ }
+
+ for (index = 0; index < Infantry.Count(); index++) {
+ InfantryClass * obj = Infantry.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team ) {
+ long xc = Cell_X(Coord_Cell(obj->Center_Coord()));
+ long yc = Cell_Y(Coord_Cell(obj->Center_Coord()));
+
+ obj->XFormOffset = xc - centerx;
+ obj->YFormOffset = yc - centery;
+ }
+ }
+
+ for (index = 0; index < Vessels.Count(); index++) {
+ VesselClass * obj = Vessels.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team ) {
+ long xc = Cell_X(Coord_Cell(obj->Center_Coord()));
+ long yc = Cell_Y(Coord_Cell(obj->Center_Coord()));
+
+ obj->XFormOffset = xc - centerx;
+ obj->YFormOffset = yc - centery;
+ }
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * Message_Input -- allows inter-player message input processing *
+ * *
+ * INPUT: *
+ * input key value *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 05/22/1995 BRR : Created. *
+ *=============================================================================================*/
+#pragma off (unreferenced)
+static void Message_Input(KeyNumType &input)
+{
+ int rc;
+ char txt[MAX_MESSAGE_LENGTH+32];
+ int id;
+ SerialPacketType * serial_packet;
+ int i;
+ KeyNumType copy_input;
+ //char *msg;
+
+ /*
+ ** Check keyboard input for a request to send a message.
+ ** The 'to' argument for Add_Edit is prefixed to the message buffer; the
+ ** message buffer is big enough for the 'to' field plus MAX_MESSAGE_LENGTH.
+ ** To send the message, calling Get_Edit_Buf retrieves the buffer minus the
+ ** 'to' portion. At the other end, the buffer allocated to display the
+ ** message must be MAX_MESSAGE_LENGTH plus the size of "From: xxx (house)".
+ */
+ if (Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH && input >= KN_F1 && input < (KN_F1 + Session.MaxPlayers) && !Session.Messages.Is_Edit()) {
+ memset (txt, 0, 40);
+
+ /*
+ ** For a serial game, send a message on F1 or F4; set 'txt' to the
+ ** "Message:" string & add an editable message to the list.
+ */
+ if (Session.Type==GAME_NULL_MODEM || Session.Type==GAME_MODEM) {
+ if (input==KN_F1 || input==(KN_F1 + Session.MaxPlayers - 1)) {
+
+ strcpy(txt, Text_String(TXT_MESSAGE)); // "Message:"
+
+ Session.Messages.Add_Edit (Session.ColorIdx,
+ TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR);
+
+ Map.Flag_To_Redraw(false);
+ }
+ } else if ((Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) && !Session.Messages.Is_Edit()) {
+ /*
+ ** For a network game:
+ ** F1-F7 = "To (house):" (only allowed if we're not in ObiWan mode)
+ ** F8 = "To All:"
+ */
+ if (input==(KN_F1 + Session.MaxPlayers - 1)) {
+
+ Session.MessageAddress = IPXAddressClass(); // set to broadcast
+ strcpy(txt, Text_String(TXT_TO_ALL)); // "To All:"
+
+ Session.Messages.Add_Edit(Session.ColorIdx,
+ TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR);
+
+ Map.Flag_To_Redraw(false);
+ } else if ((input - KN_F1) < Ipx.Num_Connections() && !Session.ObiWan) {
+
+ id = Ipx.Connection_ID(input - KN_F1);
+ Session.MessageAddress = (*(Ipx.Connection_Address (id)));
+ sprintf(txt, Text_String(TXT_TO), Ipx.Connection_Name(id));
+
+ Session.Messages.Add_Edit(Session.ColorIdx,
+ TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR);
+
+ Map.Flag_To_Redraw(false);
+ }
+ }
+ }
+
+ /*
+ ** Process message-system input; send the message out if RETURN is hit.
+ */
+ copy_input = input;
+ rc = Session.Messages.Input(input);
+
+ /*
+ ** If a single character has been added to an edit buffer, update the display.
+ */
+ if (rc == 1 && Session.Type != GAME_NORMAL) {
+ Map.Flag_To_Redraw(false);
+ }
+
+ /*
+ ** If backspace was hit, redraw the map. If the edit message was removed,
+ ** the map must be force-drawn, since it won't be able to compute the
+ ** cells to redraw; otherwise, let the map compute the cells to redraw,
+ ** by not force-drawing it, but just setting the IsToRedraw bit.
+ */
+ if (rc==2 && Session.Type != GAME_NORMAL) {
+ if (copy_input==KN_ESC) {
+ Map.Flag_To_Redraw(true);
+ } else {
+ Map.Flag_To_Redraw(false);
+ }
+ Map.DisplayClass::IsToRedraw = true;
+ }
+
+ /*
+ ** Send a message
+ */
+ if ((rc==3 || rc==4) && Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH) {
+ /*
+ ** Serial game: fill in a SerialPacketType & send it.
+ ** (Note: The size of the SerialPacketType.Command must be the same as
+ ** the EventClass.Type!)
+ */
+ if (Session.Type==GAME_NULL_MODEM || Session.Type==GAME_MODEM) {
+ serial_packet = (SerialPacketType *)NullModem.BuildBuf;
+
+ serial_packet->Command = SERIAL_MESSAGE;
+ strcpy (serial_packet->Name, Session.Players[0]->Name);
+ serial_packet->ID = Session.ColorIdx;
+
+ if (rc==3) {
+ strcpy (serial_packet->Message.Message, Session.Messages.Get_Edit_Buf());
+ } else {
+ strcpy (serial_packet->Message.Message, Session.Messages.Get_Overflow_Buf());
+ Session.Messages.Clear_Overflow_Buf();
+ }
+
+ /*
+ ** Send the message, and store this message in our LastMessage
+ ** buffer; the computer may send us a version of it later.
+ */
+ NullModem.Send_Message(NullModem.BuildBuf,
+ sizeof(SerialPacketType), 1);
+
+ strcpy(Session.LastMessage, serial_packet->Message.Message);
+ } else if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) {
+ /*
+ ** Network game: fill in a GlobalPacketType & send it.
+ */
+ Session.GPacket.Command = NET_MESSAGE;
+ strcpy (Session.GPacket.Name, Session.Players[0]->Name);
+ Session.GPacket.Message.Color = Session.ColorIdx;
+ Session.GPacket.Message.NameCRC = Compute_Name_CRC(Session.GameName);
+
+ if (rc==3) {
+ strcpy (Session.GPacket.Message.Buf, Session.Messages.Get_Edit_Buf());
+ } else {
+ strcpy (Session.GPacket.Message.Buf,
+ Session.Messages.Get_Overflow_Buf());
+ Session.Messages.Clear_Overflow_Buf();
+ }
+
+ /*
+ ** If 'F4' was hit, MessageAddress will be a broadcast address; send
+ ** the message to every player we have a connection with.
+ */
+ if (Session.MessageAddress.Is_Broadcast()) {
+ for (i = 0; i < Ipx.Num_Connections(); i++) {
+ Ipx.Send_Global_Message(&Session.GPacket,
+ sizeof(GlobalPacketType), 1,
+ Ipx.Connection_Address(Ipx.Connection_ID(i)));
+ Ipx.Service();
+ }
+ } else {
+
+ /*
+ ** Otherwise, MessageAddress contains the exact address to send to.
+ ** Send to that address only.
+ */
+ Ipx.Send_Global_Message(&Session.GPacket,
+ sizeof(GlobalPacketType), 1,
+ &Session.MessageAddress);
+ Ipx.Service();
+
+ }
+
+ /*
+ ** Store this message in our LastMessage buffer; the computer may send
+ ** us a version of it later.
+ */
+ strcpy(Session.LastMessage, Session.GPacket.Message.Buf);
+ }
+
+ /*
+ ** Tell the map to completely update itself, since a message is now missing.
+ */
+ Map.Flag_To_Redraw(true);
+ }
+}
+#pragma on (unreferenced)
+
+
+/***********************************************************************************************
+ * Color_Cycle -- Handle the general palette color cycling. *
+ * *
+ * This is a maintenance routine that handles the color cycling. It should be called as *
+ * often as necessary to achieve smooth color cycling effects -- at least 8 times a second. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/31/1994 JLB : Created. *
+ * 06/10/1994 JLB : Uses new cycle color values. *
+ * 12/21/1994 JLB : Handles text fade color. *
+ * 07/16/1996 JLB : Faster pulsing of white color. *
+ *=============================================================================================*/
+void Color_Cycle(void)
+{
+ static CDTimerClass _timer;
+ static CDTimerClass _ftimer;
+ static bool _up = false;
+ static int val = 255;
+ bool changed = false;
+
+ if (Options.IsPaletteScroll) {
+ /*
+ ** Process the fading white color. It is used for the radar box and other glowing
+ ** game interface elements.
+ */
+ if (!_ftimer) {
+ _ftimer = TIMER_SECOND/6;
+
+ #define STEP_RATE 20
+ if (_up) {
+ val += STEP_RATE;
+ if (val > 150) {
+ val = 150;
+ _up = false;
+ }
+ } else {
+ val -= STEP_RATE;
+ if (val < 0x20) {
+ val = 0x20;
+ _up = true;
+ }
+ }
+
+ /*
+ ** Set the pulse color as the proportional value between white and the
+ ** minimum value for pulsing.
+ */
+ InGamePalette[CC_PULSE_COLOR] = GamePalette[WHITE];
+ InGamePalette[CC_PULSE_COLOR].Adjust(val, BlackColor);
+
+ /*
+ ** Pulse the glowing embers between medium and dark red.
+ */
+ InGamePalette[CC_EMBER_COLOR] = RGBClass(255, 80, 80);
+ InGamePalette[CC_EMBER_COLOR].Adjust(val, BlackColor);
+
+ changed = true;
+ }
+
+ /*
+ ** Process the color cycling effects -- water.
+ */
+ if (!_timer) {
+ _timer = TIMER_SECOND/4;
+
+ RGBClass first = InGamePalette[CYCLE_COLOR_START+CYCLE_COLOR_COUNT-1];
+ for (int index = CYCLE_COLOR_START+CYCLE_COLOR_COUNT-1; index >= CYCLE_COLOR_START; index--) {
+ InGamePalette[index] = InGamePalette[index-1];
+ }
+ InGamePalette[CYCLE_COLOR_START] = first;
+
+ changed = true;
+ }
+
+ /*
+ ** If any of the processing functions changed the palette, then this palette must be
+ ** passed to the system.
+ */
+ if (changed) {
+ BStart(BENCH_PALETTE);
+ InGamePalette.Set();
+// Set_Palette(InGamePalette);
+ BEnd(BENCH_PALETTE);
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * Call_Back -- Main game maintenance callback routine. *
+ * *
+ * This routine handles all the "real time" processing that needs to *
+ * occur. This includes palette fading and sound updating. It needs *
+ * to be called as often as possible. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 10/07/1992 JLB : Created. *
+ *=============================================================================================*/
+void Call_Back(void)
+{
+ /*
+ ** Music and speech maintenance
+ */
+ if (SampleType) {
+ Sound_Callback();
+ Theme.AI();
+ Speak_AI();
+ }
+
+ /*
+ ** Network maintenance.
+ */
+ if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) {
+ IPX_Call_Back();
+ }
+
+ /*
+ ** Serial game maintenance.
+ */
+ if (Session.Type == GAME_NULL_MODEM || ((Session.Type == GAME_MODEM) && Session.ModemService)) {
+ NullModem.Service();
+ }
+}
+
+
+void IPX_Call_Back(void)
+{
+ Ipx.Service();
+
+ /*
+ ** Read packets only if the game is "closed", so we don't steal global
+ ** messages from the connection dialogs.
+ */
+ if (!Session.NetOpen) {
+ if (Ipx.Get_Global_Message (&Session.GPacket, &Session.GPacketlen, &Session.GAddress, &Session.GProductID)) {
+
+ if (Session.GProductID == IPXGlobalConnClass::COMMAND_AND_CONQUER0) {
+
+ /*
+ ** If this is another player signing off, remove the connection &
+ ** mark that player's house as non-human, so the computer will take
+ ** it over.
+ */
+ if (Session.GPacket.Command == NET_SIGN_OFF) {
+ for (int i = 0; i < Ipx.Num_Connections(); i++) {
+
+ int id = Ipx.Connection_ID(i);
+
+ if (Session.GAddress == (*Ipx.Connection_Address(id))) {
+
+ Destroy_Connection(id, 0);
+ }
+ }
+ } else {
+
+ /*
+ ** Process a message from another user.
+ */
+ if (Session.GPacket.Command == NET_MESSAGE) {
+ bool msg_ok = false;
+
+ /*
+ ** If NetProtect is set, make sure this message came from within
+ ** this game.
+ */
+ if (!Session.NetProtect) {
+ msg_ok = true;
+ } else {
+ if (Session.GPacket.Message.NameCRC ==
+ Compute_Name_CRC(Session.GameName)) {
+ msg_ok = true;
+ } else {
+ msg_ok = false;
+ }
+ }
+
+ if (msg_ok) {
+ if (!Session.Messages.Concat_Message(Session.GPacket.Name,
+ Session.GPacket.Message.Color,
+ Session.GPacket.Message.Buf, Rule.MessageDelay * TICKS_PER_MINUTE)) {
+
+ Session.Messages.Add_Message (Session.GPacket.Name,
+ Session.GPacket.Message.Color,
+ Session.GPacket.Message.Buf,
+ Session.GPacket.Message.Color,
+ TPF_6PT_GRAD | TPF_USE_GRAD_PAL |
+ TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE);
+
+ Sound_Effect(VOC_INCOMING_MESSAGE);
+ }
+
+ /*
+ ** Tell the map to do a partial update (just to force the messages
+ ** to redraw).
+ */
+ Map.Flag_To_Redraw(true);
+
+ /*
+ ** Save this message in our last-message buffer
+ */
+ strcpy(Session.LastMessage, Session.GPacket.Message.Buf);
+ }
+ } else {
+ Process_Global_Packet(&Session.GPacket, &Session.GAddress);
+ }
+ }
+ }
+ }
+ }
+
+}
+
+
+/***********************************************************************************************
+ * Language_Name -- Build filename for current language. *
+ * *
+ * This routine attaches a language specific suffix to the base *
+ * filename provided. Typical use of this is when loading language *
+ * specific files at game initialization time. *
+ * *
+ * INPUT: basename -- Base name to append language specific *
+ * extension to. *
+ * *
+ * OUTPUT: Returns with pointer to completed filename. *
+ * *
+ * WARNINGS: The return pointer value is valid only until the next time *
+ * this routine is called. *
+ * *
+ * HISTORY: *
+ * 10/07/1992 JLB : Created. *
+ *=============================================================================================*/
+char const * Language_Name(char const * basename)
+{
+ static char _fullname[_MAX_FNAME+_MAX_EXT];
+
+ if (!basename) return(NULL);
+
+ sprintf(_fullname, "%s.ENG", basename);
+ return(_fullname);
+}
+
+
+/***********************************************************************************************
+ * Source_From_Name -- Converts ASCII name into SourceType. *
+ * *
+ * This routine is used to convert an ASCII name representing a *
+ * SourceType into the actual SourceType value. Typically, this is *
+ * used when processing the scenario INI file. *
+ * *
+ * INPUT: name -- The ASCII source name to process. *
+ * *
+ * OUTPUT: Returns with the SourceType represented by the name *
+ * specified. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 04/17/1994 JLB : Created. *
+ *=============================================================================================*/
+SourceType Source_From_Name(char const * name)
+{
+ if (name) {
+ for (SourceType source = SOURCE_FIRST; source < SOURCE_COUNT; source++) {
+ if (stricmp(SourceName[source], name) == 0) {
+ return(source);
+ }
+ }
+ }
+ return(SOURCE_NONE);
+}
+
+
+/***********************************************************************************************
+ * Name_From_Source -- retrieves the name for the given SourceType *
+ * *
+ * INPUT: *
+ * source SourceType to get the name for *
+ * *
+ * OUTPUT: *
+ * name of SourceType *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 11/15/1994 BR : Created. *
+ *=============================================================================================*/
+char const * Name_From_Source(SourceType source)
+{
+ if ((unsigned)source < SOURCE_COUNT) {
+ return(SourceName[source]);
+ }
+ return("None");
+}
+
+
+/***********************************************************************************************
+ * Theater_From_Name -- Converts ASCII name into a theater number. *
+ * *
+ * This routine converts an ASCII representation of a theater and converts it into a *
+ * matching theater number. If no match was found, then THEATER_NONE is returned. *
+ * *
+ * INPUT: name -- Pointer to ASCII name to convert. *
+ * *
+ * OUTPUT: Returns with the name converted into a theater number. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 10/01/1994 JLB : Created. *
+ *=============================================================================================*/
+TheaterType Theater_From_Name(char const * name)
+{
+ TheaterType index;
+
+ if (name) {
+ for (index = THEATER_FIRST; index < THEATER_COUNT; index++) {
+ if (stricmp(name, Theaters[index].Name) == 0) {
+ return(index);
+ }
+ }
+ }
+ return(THEATER_NONE);
+}
+
+
+/***********************************************************************************************
+ * KN_To_Facing -- Converts a keyboard input number into a facing value. *
+ * *
+ * This routine determine which compass direction is represented by the keyboard value *
+ * provided. It is used for map scrolling and other directional control operations from *
+ * the keyboard. *
+ * *
+ * INPUT: input -- The KN number to convert. *
+ * *
+ * OUTPUT: Returns with the facing type that the keyboard number represents. If it could *
+ * not be translated, then FACING_NONE is returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/28/1994 JLB : Created. *
+ *=============================================================================================*/
+FacingType KN_To_Facing(int input)
+{
+ input &= ~(KN_ALT_BIT|KN_SHIFT_BIT|KN_CTRL_BIT);
+ switch (input) {
+ case KN_LEFT:
+ return(FACING_W);
+
+ case KN_RIGHT:
+ return(FACING_E);
+
+ case KN_UP:
+ return(FACING_N);
+
+ case KN_DOWN:
+ return(FACING_S);
+
+ case KN_UPLEFT:
+ return(FACING_NW);
+
+ case KN_UPRIGHT:
+ return(FACING_NE);
+
+ case KN_DOWNLEFT:
+ return(FACING_SW);
+
+ case KN_DOWNRIGHT:
+ return(FACING_SE);
+
+ default:
+ break;
+ }
+ return(FACING_NONE);
+}
+
+
+/***********************************************************************************************
+ * Sync_Delay -- Forces the game into a 15 FPS rate. *
+ * *
+ * This routine will wait until the timer for the current frame has expired before *
+ * returning. It is called at the end of every game loop in order to force the game loop *
+ * to run at a fixed rate. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: This routine will delay an amount of time according to the game speed setting. *
+ * *
+ * HISTORY: *
+ * 01/04/1995 JLB : Created. *
+ * 03/06/1995 JLB : Fixed. *
+ *=============================================================================================*/
+static void Sync_Delay(void)
+{
+ /*
+ ** Accumulate the number of 'spare' ticks that are frittered away here.
+ */
+ SpareTicks += FrameTimer;
+
+ /*
+ ** Delay until the frame timer expires. This forces the game loop to be regulated to a
+ ** speed controlled by the game options slider.
+ */
+ while (FrameTimer) {
+ Color_Cycle();
+ Call_Back();
+
+ if (SpecialDialog == SDLG_NONE) {
+#ifdef WIN32
+ WWMouse->Erase_Mouse(&HidPage, TRUE);
+#endif //WIN32
+ KeyNumType input = KN_NONE;
+ int x, y;
+ Map.Input(input, x, y);
+ if (input) {
+ Keyboard_Process(input);
+ }
+ Map.Render();
+ }
+ }
+ Color_Cycle();
+ Call_Back();
+}
+
+
+/***********************************************************************************************
+ * Main_Loop -- This is the main game loop (as a single loop). *
+ * *
+ * This function will perform one game loop. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: bool; Should the game end? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 10/01/1994 JLB : Created. *
+ *=============================================================================================*/
+#ifdef WIN32
+extern void Check_For_Focus_Loss(void);
+void Reallocate_Big_Shape_Buffer(void);
+#endif //WIN32
+
+
+bool Main_Loop()
+{
+ KeyNumType input; // Player input.
+ int x;
+ int y;
+ int framedelay;
+
+ if (!GameActive) return(!GameActive);
+
+#ifdef WIN32
+ /*
+ ** Call the focus loss handler
+ */
+ Check_For_Focus_Loss();
+
+ /*
+ ** Allocate extra memory for uncompressed shapes as needed
+ */
+ Reallocate_Big_Shape_Buffer();
+#endif
+
+ /*
+ ** Sync-bug trapping code
+ */
+ if (Frame >= Session.TrapFrame) {
+ Session.Trap_Object();
+ }
+
+ //
+ // Initialize our AI processing timer
+ //
+ Session.ProcessTimer = TickCount;
+
+#if 1
+ if (Session.TrapCheckHeap) {
+ Debug_Trap_Check_Heap = true;
+ }
+#endif
+
+#ifdef CHEAT_KEYS
+
+ /*
+ ** Update the running status debug display.
+ */
+ Self_Regulate();
+#endif
+
+ BStart(BENCH_GAME_FRAME);
+
+ /*
+ ** If there is no theme playing, but it looks like one is required, then start one
+ ** playing. This is usually the symptom of there being no transition score.
+ */
+ if (SampleType && Theme.What_Is_Playing() == THEME_NONE) {
+ Theme.Queue_Song(THEME_PICK_ANOTHER);
+ }
+
+ /*
+ ** Setup the timer so that the Main_Loop function processes at the correct rate.
+ */
+ if (Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH &&
+ Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
+
+ //
+ // In playback mode, run as fast as possible.
+ //
+ if (Session.Play) {
+ FrameTimer = 0;
+ } else {
+ framedelay = TIMER_SECOND / Session.DesiredFrameRate;
+ FrameTimer = framedelay;
+ }
+ } else {
+ if (Options.GameSpeed != 0) {
+ FrameTimer = Options.GameSpeed +
+ (PlayerPtr->Difficulty == DIFF_EASY ? 1 : 0) -
+ (PlayerPtr->Difficulty == DIFF_HARD ? 1 : 0);
+ } else {
+ FrameTimer = Options.GameSpeed + (PlayerPtr->Difficulty == DIFF_EASY ? 1 : 0);
+ }
+ }
+
+ /*
+ ** Update the display, unless we're inside a dialog.
+ */
+ if (!Session.Play) {
+#ifdef WIN32
+ if (SpecialDialog == SDLG_NONE && GameInFocus) {
+ WWMouse->Erase_Mouse(&HidPage, TRUE);
+#else
+ if (SpecialDialog == SDLG_NONE) {
+#endif
+ Map.Input(input, x, y);
+ if (input) {
+ Keyboard_Process(input);
+ }
+ Map.Render();
+ }
+ }
+
+ /*
+ ** Save map's position & selected objects, if we're recording the game.
+ */
+ if (Session.Record || Session.Play) {
+ Do_Record_Playback();
+ }
+
+#ifndef SORTDRAW
+ /*
+ ** Sort the map's ground layer by y-coordinate value. This is done
+ ** outside the IsToRedraw check, for the purposes of game sync'ing
+ ** between machines; this way, all machines will sort the Map's
+ ** layer in the same way, and any processing done that's based on
+ ** the order of this layer will remain in sync.
+ */
+ DisplayClass::Layer[LAYER_GROUND].Sort();
+#endif
+
+ /*
+ ** AI logic operations are performed here.
+ */
+ Logic.AI();
+ TimeQuake = false;
+
+ /*
+ ** Manage the inter-player message list. If Manage() returns true, it means
+ ** a message has expired & been removed, and the entire map must be updated.
+ */
+ if (Session.Messages.Manage()) {
+#ifdef WIN32
+ HiddenPage.Clear();
+#else //WIN32
+ HidPage.Clear();
+#endif //WIN32
+ Map.Flag_To_Redraw(true);
+ }
+
+ //
+ // Measure how long it took to process the AI
+ //
+ Session.ProcessTicks += (TickCount - Session.ProcessTimer);
+ Session.ProcessFrames++;
+
+ /*
+ ** Process all commands that are ready to be processed.
+ */
+ Queue_AI();
+
+ /*
+ ** Keep track of elapsed time in the game.
+ */
+ Score.ElapsedTime += TIMER_SECOND / TICKS_PER_SECOND;
+
+ Call_Back();
+
+ /*
+ ** Check for player wins or loses according to global event flag.
+ */
+ if (PlayerWins) {
+
+#ifdef WIN32
+
+ /*
+ ** Send the game statistics to WChat.
+ */
+ if (Session.Type == GAME_INTERNET && !GameStatisticsPacketSent) {
+ Register_Game_End_Time();
+ Send_Statistics_Packet();
+ }
+
+ WWMouse->Erase_Mouse(&HidPage, TRUE);
+#endif //WIN32
+ PlayerLoses = false;
+ PlayerWins = false;
+ PlayerRestarts = false;
+ Map.Help_Text(TXT_NONE);
+ Do_Win();
+ return(!GameActive);
+ }
+ if (PlayerLoses) {
+#ifdef WIN32
+ /*
+ ** Send the game statistics to WChat.
+ */
+ if (Session.Type == GAME_INTERNET && !GameStatisticsPacketSent) {
+ Register_Game_End_Time();
+ Send_Statistics_Packet();
+ }
+
+ WWMouse->Erase_Mouse(&HidPage, TRUE);
+#endif //WIN32
+ PlayerWins = false;
+ PlayerLoses = false;
+ PlayerRestarts = false;
+ Map.Help_Text(TXT_NONE);
+ Do_Lose();
+ return(!GameActive);
+ }
+ if (PlayerRestarts) {
+#ifdef WIN32
+ WWMouse->Erase_Mouse(&HidPage, TRUE);
+#endif //WIN32
+ PlayerWins = false;
+ PlayerLoses = false;
+ PlayerRestarts = false;
+ Map.Help_Text(TXT_NONE);
+ Do_Restart();
+ return(!GameActive);
+ }
+
+ /*
+ ** The frame logic has been completed. Increment the frame
+ ** counter.
+ */
+ Frame++;
+
+ /*
+ ** Is there a memory trasher altering the map??
+ */
+ if (Debug_Check_Map) {
+ if (!Map.Validate()) {
+ if (WWMessageBox().Process (TEXT_MAP_ERROR, TEXT_STOP, TEXT_CONTINUE)==0) {
+ GameActive = 0;
+ }
+ Map.Validate(); // give debugger a chance to catch it
+ }
+ }
+
+
+#ifdef WIN32
+ if (Debug_MotionCapture) {
+ static void ** _array = 0;
+ static int _sequence = 0;
+ static int _seqsize = Rule.MovieTime * TICKS_PER_MINUTE;
+
+ if (_array == NULL) {
+ _array = new void * [_seqsize];
+ memset(_array, '\0', _seqsize * sizeof(void*));
+ }
+
+ if (_array == NULL) {
+ Debug_MotionCapture = false;
+ }
+
+ static GraphicBufferClass temp_page( SeenBuff.Get_Width(),
+ SeenBuff.Get_Height(),
+ NULL,
+ SeenBuff.Get_Width() * SeenBuff.Get_Height());
+
+ int size = SeenBuff.Get_Width() * SeenBuff.Get_Height();
+
+ if (_sequence < _seqsize) {
+ if (_array[_sequence] == NULL) {
+ _array[_sequence] = new char[size];
+ }
+
+ if (_array[_sequence] != NULL) {
+ SeenBuff.Blit(temp_page);
+ memmove(_array[_sequence], temp_page.Get_Buffer(), size);
+ }
+ _sequence++;
+
+ } else {
+ Debug_MotionCapture = false;
+
+ CDFileClass file;
+ file.Cache(200000);
+ char filename[30];
+
+ for (int index = 0; index < _sequence; index++) {
+ memmove(temp_page.Get_Buffer(), _array[index], size);
+ sprintf(filename, "cap%04d.pcx", index);
+ file.Set_Name(filename);
+
+ Write_PCX_File(file, temp_page, & GamePalette);
+ }
+
+ _sequence = 0;
+ }
+ }
+#endif
+
+ BEnd(BENCH_GAME_FRAME);
+
+ Sync_Delay();
+ return(!GameActive);
+}
+
+
+#ifdef SCENARIO_EDITOR
+/***************************************************************************
+ * Map_Edit_Loop -- a mini-main loop for map edit mode only *
+ * *
+ * INPUT: *
+ * *
+ * OUTPUT: *
+ * *
+ * WARNINGS: *
+ * *
+ * HISTORY: *
+ * 10/19/1994 BR : Created. *
+ *=========================================================================*/
+bool Map_Edit_Loop(void)
+{
+ /*
+ ** Redraw the map.
+ */
+ Map.Render();
+
+ /*
+ ** Get user input (keys, mouse clicks).
+ */
+ KeyNumType input;
+
+#ifdef WIN32
+ WWMouse->Erase_Mouse(&HidPage, TRUE);
+#endif //WIN32
+
+ int x;
+ int y;
+ Map.Input(input, x, y);
+
+ /*
+ ** Process keypress.
+ */
+ if (input) {
+ Keyboard_Process(input);
+ }
+
+ Call_Back(); // maintains Theme.AI() for music
+ Color_Cycle();
+
+ return(!GameActive);
+}
+
+
+/***************************************************************************
+ * Go_Editor -- Enables/disables the map editor *
+ * *
+ * INPUT: *
+ * flag true = go into editor mode; false = go into game mode *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 10/19/1994 BR : Created. *
+ *=========================================================================*/
+void Go_Editor(bool flag)
+{
+ /*
+ ** Go into Scenario Editor mode
+ */
+ if (flag) {
+ Debug_Map = true;
+ Debug_Unshroud = true;
+
+ /*
+ ** Un-select any selected objects
+ */
+ Unselect_All();
+
+ /*
+ ** Turn off the sidebar if it's on
+ */
+ Map.Activate(0);
+
+ /*
+ ** Reset the map's Button list for the new mode
+ */
+ Map.Init_IO();
+
+ /*
+ ** Force a complete redraw of the screen
+ */
+#ifdef WIN32
+ HiddenPage.Clear();
+#else
+ HidPage.Clear();
+#endif
+ Map.Flag_To_Redraw(true);
+ Map.Render();
+
+ } else {
+
+ /*
+ ** Go into normal game mode
+ */
+ Debug_Map = false;
+ Debug_Unshroud = false;
+
+ /*
+ ** Un-select any selected objects
+ */
+ Unselect_All();
+
+ /*
+ ** Reset the map's Button list for the new mode
+ */
+ Map.Init_IO();
+
+ /*
+ ** Force a complete redraw of the screen
+ */
+ HidPage.Clear();
+ Map.Flag_To_Redraw(true);
+ Map.Render();
+ }
+}
+
+#endif
+
+
+/***********************************************************************************************
+ * MixFileHandler -- Handles VQ file access. *
+ * *
+ * This routine is called from the VQ player when it needs to access the source file. By *
+ * using this routine it is possible to virtualize the file system. *
+ * *
+ * INPUT: vqa -- Pointer to the VQA handle for this animation. *
+ * *
+ * action-- The requested action to perform. *
+ * *
+ * buffer-- Optional buffer pointer as needed by the type of action. *
+ * *
+ * nbytes-- The number of bytes (if needed) for this operation. *
+ * *
+ * OUTPUT: Returns a value consistent with the action requested. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/04/1995 JLB : Created. *
+ *=============================================================================================*/
+long MixFileHandler(VQAHandle * vqa, long action, void * buffer, long nbytes)
+{
+ CCFileClass * file;
+ long error;
+
+ file = (CCFileClass *)vqa->VQAio;
+
+ /*
+ ** Perform the action specified by the stream command.
+ */
+ switch (action) {
+
+ /*
+ ** VQACMD_READ means read NBytes from the stream and place it in the
+ ** memory pointed to by Buffer.
+ **
+ ** Any error code returned will be remapped by VQA library into
+ ** VQAERR_READ.
+ */
+ case VQACMD_READ:
+ error = (file->Read(buffer, (unsigned short)nbytes) != (unsigned short)nbytes);
+ break;
+
+ /*
+ ** VQACMD_WRITE is analogous to VQACMD_READ.
+ **
+ ** Writing is not allowed to the VQA file, VQA library will remap the
+ ** error into VQAERR_WRITE.
+ */
+ case VQACMD_WRITE:
+ error = 1;
+ break;
+
+ /*
+ ** VQACMD_SEEK asks that you perform a seek relative to the current
+ ** position. NBytes is a signed number, indicating seek direction
+ ** (positive for forward, negative for backward). Buffer has no meaning
+ ** here.
+ **
+ ** Any error code returned will be remapped by VQA library into
+ ** VQAERR_SEEK.
+ */
+ case VQACMD_SEEK:
+ error = (file->Seek(nbytes, SEEK_CUR) == -1);
+ break;
+
+ /*
+ ** VQACMD_OPEN asks that you open your stream for access.
+ */
+ case VQACMD_OPEN:
+ file = new CCFileClass((char *)buffer);
+
+ if (file != NULL && file->Is_Available()) {
+ error = file->Open((char *)buffer, READ);
+
+ if (error != -1) {
+ vqa->VQAio = (unsigned long)file;
+ error = 0;
+ } else {
+ delete file;
+ file = 0;
+ error = 1;
+ }
+ } else {
+ error = 1;
+ }
+ break;
+
+ case VQACMD_CLOSE:
+ file->Close();
+ delete file;
+ file = 0;
+ vqa->VQAio = 0;
+ error = 0;
+ break;
+
+ /*
+ ** VQACMD_INIT means to prepare your stream for reading. This is used for
+ ** certain streams that can't be read immediately upon opening, and need
+ ** further preparation. This operation is allowed to fail; the error code
+ ** will be returned directly to the client.
+ */
+ case VQACMD_INIT:
+
+ /*
+ ** IFFCMD_CLEANUP means to terminate the transaction with the associated
+ ** stream. This is used for streams that can't simply be closed. This
+ ** operation is not allowed to fail; any error returned will be ignored.
+ */
+ case VQACMD_CLEANUP:
+ error = 0;
+ break;
+
+ default:
+ error = 0;
+ break;
+ }
+
+ return(error);
+}
+
+
+void Rebuild_Interpolated_Palette(unsigned char * interpal)
+{
+ for (int y=0; y<255; y++) {
+ for (int x=y+1; x<256; x++) {
+ *(interpal + (y*256+x)) = *(interpal + (x*256+y));
+ }
+ }
+}
+
+
+unsigned char * InterpolatedPalettes[50];
+BOOL PalettesRead;
+unsigned PaletteCounter;
+
+
+/***********************************************************************************************
+ * Load_Interpolated_Palettes -- Loads in any precalculated palettes for hires VQs *
+ * *
+ * *
+ * *
+ * INPUT: Name of palette file *
+ * *
+ * OUTPUT: Number of palettes loaded *
+ * *
+ * WARNINGS: None *
+ * *
+ * HISTORY: *
+ * 5/7/96 9:49AM ST : Created *
+ *=============================================================================================*/
+int Load_Interpolated_Palettes(char const * filename, BOOL add)
+{
+ int num_palettes=0;
+ int i;
+ int start_palette;
+
+ PalettesRead = FALSE;
+ CCFileClass file(filename);
+
+ if (!add) {
+ for (i=0; i < 50; i++) {
+ InterpolatedPalettes[i]=NULL;
+ }
+ start_palette=0;
+ } else {
+ for (start_palette = 0; start_palette < 50; start_palette++) {
+ if (!InterpolatedPalettes[start_palette]) break;
+ }
+ }
+
+ /*
+ ** Hack another interpolated palette if the requested one is
+ ** not present.
+ */
+ if (!file.Is_Available()) {
+ file.Set_Name("AAGUN.VQP");
+ }
+
+ if (file.Is_Available()) {
+
+ file.Open(READ);
+ file.Read(&num_palettes , 4);
+
+ for (i=0; i < num_palettes; i++) {
+ InterpolatedPalettes[i+start_palette] = (unsigned char *)malloc (65536);
+ memset (InterpolatedPalettes[i+start_palette], 0, 65536);
+ for (int y = 0; y < 256; y++) {
+ file.Read (InterpolatedPalettes[i+start_palette] + y*256 , y+1);
+ }
+
+ Rebuild_Interpolated_Palette(InterpolatedPalettes[i+start_palette]);
+ }
+
+ PalettesRead = TRUE;
+ file.Close();
+ }
+ PaletteCounter = 0;
+ return (num_palettes);
+}
+
+
+void Free_Interpolated_Palettes(void)
+{
+ for (int i = 0; i < 50 ;i++) {
+ if (InterpolatedPalettes[i]) {
+ free(InterpolatedPalettes[i]);
+ InterpolatedPalettes[i]=NULL;
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * Play_Movie -- Plays a VQ movie. *
+ * *
+ * Use this routine to play a VQ movie. It will dispatch the specified movie to the *
+ * VQ player. The routine will not return until the movie has finished playing. *
+ * *
+ * INPUT: name -- The name of the movie file (sans ".VQA"). *
+ * *
+ * theme -- The identifier for an optional theme that should be played in the *
+ * background while this VQ plays. *
+ * *
+ * clrscrn -- 'true' if to clear the screen when the movie is over *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 12/19/1994 JLB : Created. *
+ *=============================================================================================*/
+#ifdef WIN32
+extern void Suspend_Audio_Thread(void);
+extern void Resume_Audio_Thread(void);
+
+#ifdef MOVIE640
+extern GraphicBufferClass VQ640;
+#endif
+#endif
+void Play_Movie(char const * name, ThemeType theme, bool clrscrn)
+{
+ /*
+ ** Don't play movies in editor mode
+ */
+ if (Debug_Map) {
+ return;
+ }
+
+ /*
+ ** Don't play movies in multiplayer mode
+ */
+ if (Session.Type != GAME_NORMAL) {
+ return;
+ }
+
+ if (name) {
+ char fullname[_MAX_FNAME+_MAX_EXT];
+ _makepath(fullname, NULL, NULL, name, ".VQA");
+#ifdef WIN32
+ char palname [_MAX_FNAME+_MAX_EXT];
+ _makepath(palname , NULL, NULL, name, ".VQP");
+#endif //WIN32
+ #ifdef CHEAT_KEYS
+ Mono_Set_Cursor(0, 0);Mono_Printf("[%s]", fullname);
+ #endif
+
+ if (!CCFileClass(fullname).Is_Available()) return;
+
+ /*
+ ** Reset the anim control structure.
+ */
+ Anim_Init();
+
+ /*
+ ** Prepare to play a movie. First hide the mouse and stop any score that is playing.
+ ** While the score (if any) is fading to silence, fade the palette to black as well.
+ ** When the palette has finished fading, wait until the score has finished fading
+ ** before launching the movie.
+ */
+ Hide_Mouse();
+ Theme.Queue_Song(theme);
+ if (PreserveVQAScreen == 0 && !clrscrn) {
+ BlackPalette.Set(FADE_PALETTE_MEDIUM);
+ VisiblePage.Clear();
+ BlackPalette.Adjust(0x08, WhitePalette);
+ BlackPalette.Set();
+ BlackPalette.Adjust(0xFF);
+ BlackPalette.Set();
+ }
+ PreserveVQAScreen = 0;
+ Keyboard->Clear();
+
+ VQAHandle * vqa = NULL;
+
+
+#ifdef WIN32
+#ifdef MOVIE640
+ if(IsVQ640) {
+ AnimControl.ImageWidth = 640;
+ AnimControl.ImageHeight = 400;
+ AnimControl.ImageBuf = (unsigned char *)VQ640.Get_Offset();
+ } else {
+ AnimControl.ImageWidth = 320;
+ AnimControl.ImageHeight = 200;
+ AnimControl.ImageBuf = (unsigned char *)SysMemPage.Get_Offset();
+ }
+#endif
+#endif
+
+ if (!Debug_Quiet && Get_Digi_Handle() != -1) {
+ AnimControl.OptionFlags |= VQAOPTF_AUDIO;
+ } else {
+ AnimControl.OptionFlags &= ~VQAOPTF_AUDIO;
+ }
+
+ if ((vqa = VQA_Alloc()) != NULL) {
+ VQA_Init(vqa, MixFileHandler);
+
+ if (VQA_Open(vqa, fullname, &AnimControl) == 0) {
+ Brokeout = false;
+#ifdef WIN32
+ //Suspend_Audio_Thread();
+#ifdef MOVIE640
+ if(!IsVQ640) {
+ Load_Interpolated_Palettes(palname);
+ }
+#else
+ Load_Interpolated_Palettes(palname);
+#endif
+ //Set_Palette(BlackPalette);
+ SysMemPage.Clear();
+ InMovie = true;
+#endif //WIN32
+ VQA_Play(vqa, VQAMODE_RUN);
+ VQA_Close(vqa);
+#ifdef WIN32
+ //Resume_Audio_Thread();
+ InMovie = FALSE;
+#ifdef MOVIE640
+ if(!IsVQ640) {
+ Free_Interpolated_Palettes();
+ }
+#else
+ Free_Interpolated_Palettes();
+#endif
+ IsVQ640 = false;
+ Set_Primary_Buffer_Format();
+#endif //WIN32
+
+ /*
+ ** Any movie that ends prematurely must have the screen
+ ** cleared to avoid any unexpected palette glitches.
+ */
+ if (Brokeout) {
+ clrscrn = true;
+ VisiblePage.Clear();
+ Brokeout = false;
+ }
+ } else {
+ #ifndef NDEBUG
+ bool error = true;
+ assert(error);
+ #endif
+ }
+
+ VQA_Free(vqa);
+ } else {
+ assert(vqa != NULL);
+ }
+
+ /*
+ ** Presume that the screen is left in a garbage state as well as the palette
+ ** being in an unknown condition. Recover from this by clearing the screen and
+ ** forcing the palette to black.
+ */
+ if (clrscrn) {
+ VisiblePage.Clear();
+ BlackPalette.Adjust(0x08, WhitePalette);
+ BlackPalette.Set();
+ BlackPalette.Adjust(0xFF);
+ BlackPalette.Set();
+ }
+ Show_Mouse();
+ }
+}
+
+
+void Play_Movie(VQType name, ThemeType theme, bool clrscrn)
+{
+ if (name != VQ_NONE) {
+ if (name == VQ_REDINTRO) {
+ IsVQ640 = true;
+ }
+ Play_Movie(VQName[name], theme, clrscrn);
+ IsVQ640 = false;
+ }
+}
+
+
+/***********************************************************************************************
+ * Unselect_All -- Causes all selected objects to become unselected. *
+ * *
+ * This routine will unselect all objects that are currently selected. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 01/19/1995 JLB : Created. *
+ *=============================================================================================*/
+void Unselect_All(void)
+{
+ while (CurrentObject.Count()) {
+ CurrentObject[0]->Unselect();
+ }
+}
+
+
+/***********************************************************************************************
+ * Fading_Table_Name -- Builds a theater specific fading table name. *
+ * *
+ * This routine builds a standard fading table name. This name is dependant on the theater *
+ * being played, since each theater has its own palette. *
+ * *
+ * INPUT: base -- The base name of this fading table. The base name can be no longer than *
+ * seven characters. *
+ * *
+ * theater -- The theater that this fading table is specific to. *
+ * *
+ * OUTPUT: Returns with a pointer to the constructed fading table filename. This pointer is *
+ * valid until this function is called again. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 01/19/1995 JLB : Created. *
+ *=============================================================================================*/
+char const * Fading_Table_Name(char const * base, TheaterType theater)
+{
+ static char _buffer[_MAX_FNAME+_MAX_EXT];
+ char root[_MAX_FNAME];
+
+ sprintf(root, "%1.1s%s", Theaters[theater].Root, base);
+ _makepath(_buffer, NULL, NULL, root, ".MRF");
+ return(_buffer);
+}
+
+
+/***********************************************************************************************
+ * Get_Radar_Icon -- Builds and alloc a radar icon from a shape file *
+ * *
+ * INPUT: void const * shapefile - pointer to a key framed shapefile *
+ * int shapenum - shape to extract from shapefile *
+ * *
+ * OUTPUT: void const * - 3/3 icon set of shape from file *
+ * *
+ * HISTORY: *
+ * 04/12/1995 PWG : Created. *
+ * 05/10/1995 JLB : Handles a null shapefile pointer. *
+ *=============================================================================================*/
+void const * Get_Radar_Icon(void const * shapefile, int shapenum, int frames, int zoomfactor)
+{
+ static int _offx[]={ 0, 0, -1, 1, 0, -1, 1, -1, 1};
+ static int _offy[]={ 0, 0, -1, 1, 0, -1, 1, -1, 1};
+ int lp,framelp;
+ char pixel;
+
+ char * retval = NULL;
+ char * buffer = NULL;
+
+ /*
+ ** If there is no shape file, then there can be no radar icon imagery.
+ */
+ if (!shapefile) return(NULL);
+
+#if (0)
+ CCPalette.Set();
+ Set_Logic_Page(SeenBuff);
+ CC_Draw_Shape(shapefile, shapenum, 64, 64, WINDOW_MAIN, SHAPE_WIN_REL);
+#endif
+
+ /*
+ ** Get the pixel width and height of the frame we built. This will
+ ** be used to extract icons and build pixels.
+ */
+ int pixel_width = Get_Build_Frame_Width( shapefile );
+ int pixel_height = Get_Build_Frame_Height( shapefile );
+
+ /*
+ ** Find the width and height in icons, adjust these by half an
+ ** icon because the artists may be sloppy and miss the edge of an
+ ** icon one way or the other.
+ */
+ int icon_width = (pixel_width + 12) / 24;
+ int icon_height = (pixel_height + 12) / 24;
+
+ /*
+ ** If we have been told to build as many frames as possible, then
+ ** find out how many frames there are to build.
+ */
+ if (frames == -1) frames = Get_Build_Frame_Count( shapefile );
+
+ /*
+ ** Allocate a position to store our icons. If the alloc fails then
+ ** we don't add these icons to the set.
+ **/
+ buffer = new char[(icon_width * icon_height * 9 * frames)+2];
+ if (!buffer) return(NULL);
+
+ /*
+ ** Save off the return value so that we can return it to the calling
+ ** function.
+ */
+ retval = (char *)buffer;
+ *buffer++ = (char)icon_width;
+ *buffer++ = (char)icon_height;
+ int val = 24/zoomfactor;
+
+ for (framelp = 0; framelp < frames; framelp ++) {
+ /*
+ ** Build the current frame. If the frame can not be built then we
+ ** just need to skip past this set of icons and try to build the
+ ** next frame.
+ */
+#ifdef WIN32
+ void * ptr;
+ if ((ptr = (void *)(Build_Frame(shapefile, shapenum + framelp, SysMemPage.Get_Buffer()))) != NULL) {
+ ptr = Get_Shape_Header_Data(ptr);
+#else //WIN#@
+ if (Build_Frame(shapefile, shapenum + framelp, HidPage.Get_Buffer()) <= (unsigned long)HidPage.Get_Size() ) {
+#endif //WIN32
+
+ /*
+ ** Loop through the icon width and the icon height building icons
+ ** into the buffer pointer. When the getx or gety falls outside of
+ ** the width and height of the shape, just insert transparent pixels.
+ */
+ for (int icony = 0; icony < icon_height; icony ++) {
+ for (int iconx = 0; iconx < icon_width; iconx ++) {
+#ifdef WIN32
+
+ for (int y = 0; y < zoomfactor; y++) {
+ for (int x = 0; x < zoomfactor; x++) {
+ int getx = (iconx * 24) + (x * val) + (zoomfactor / 2);
+ int gety = (icony * 24) + (y * val) + (zoomfactor / 2);
+ if ((getx < pixel_width) && (gety < pixel_height)) {
+ for (lp = 0; lp < 9; lp ++) {
+ pixel = *(char *)((char *)ptr + ((gety - _offy[lp]) * pixel_width) + getx-_offx[lp]);
+
+#else //WIN32
+ for (int y = 0; y < 3; y++) {
+ for (int x = 0; x < 3; x++) {
+ int getx = (iconx * 24) + (x << 3) + 4;
+ int gety = (icony * 24) + (y << 3) + 4;
+ if ((getx < pixel_width) && (gety < pixel_height)) {
+ for (lp = 0; lp < 9; lp ++) {
+ pixel = *(char *)((char *)HidPage.Get_Buffer(), ((gety - _offy[lp]) * pixel_width) + getx-_offx[lp]);
+#endif //WIN32
+ if (pixel == LTGREEN) pixel = 0;
+ if (pixel) {
+ break;
+ }
+ }
+ *buffer++ = pixel;
+ } else {
+ *buffer++ = 0;
+ }
+ }
+ }
+ }
+ }
+ } else {
+ buffer += icon_width * icon_height * 9;
+ }
+ }
+ return(retval);
+}
+
+
+/***********************************************************************************************
+ * CC_Draw_Shape -- Custom draw shape handler. *
+ * *
+ * All draw shape calls will route through this function. It handles all draws for *
+ * C&C. Such draws always occur to the logical page and assume certain things about *
+ * the parameters passed. *
+ * *
+ * INPUT: shapefile -- Pointer to the shape data file. This data file contains all the *
+ * embedded shapes. *
+ * *
+ * shapenum -- The shape number within the shapefile that will be drawn. *
+ * *
+ * x,y -- The pixel coordinates to draw the shape. *
+ * *
+ * window -- The clipping window to use. *
+ * *
+ * flags -- The custom draw shape flags. This controls how the parameters *
+ * are used (if any). *
+ * *
+ * fadingdata -- If SHAPE_FADING is desired, then this points to the fading *
+ * data table. *
+ * *
+ * ghostdata -- If SHAPE_GHOST is desired, then this points to the ghost remap *
+ * table. *
+ * *
+ * rotation -- Rotation to apply to the shape (DIR_N = no rotation at all). *
+ * *
+ * scale -- 24.8 fixed point scale factor. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 02/21/1995 JLB : Created. *
+ *=============================================================================================*/
+void CC_Draw_Shape(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata, void const * ghostdata, DirType rotation, long scale)
+{
+ int predoffset;
+#ifdef WIN32
+ unsigned long shape_pointer;
+#endif //WIN32
+
+
+ /*
+ ** Special kludge for E3 to prevent crashes
+ */
+ if ((flags & SHAPE_GHOST) && (!ghostdata)) {
+ ghostdata = DisplayClass::SpecialGhost;
+ }
+ if ((flags & SHAPE_FADING) && (!fadingdata)) {
+ fadingdata = DisplayClass::FadingShade;
+ }
+
+ static unsigned char * _xbuffer = 0;
+
+ if (!_xbuffer) {
+ _xbuffer = new unsigned char[SHAPE_BUFFER_SIZE];
+ }
+
+ if (shapefile != NULL && shapenum != -1) {
+
+ int width = Get_Build_Frame_Width(shapefile);
+ int height = Get_Build_Frame_Height(shapefile);
+
+#ifdef NEVER
+ /*
+ ** Perform a quick clip check against the destination rectangle.
+ */
+ if (flags & SHAPE_CENTER) {
+ if (x-width/2 >= WindowList[window][WINDOWWIDTH]) return;
+ if (y-width/2 >= WindowList[window][WINDOWHEIGHT]) return;
+ if (x+width/2 < 0) return;
+ if (y+height/2 < 0) return;
+
+ } else {
+ if (x >= WindowList[window][WINDOWWIDTH]) return;
+ if (y >= WindowList[window][WINDOWHEIGHT]) return;
+ if (x+width < 0) return;
+ if (y+height < 0) return;
+ }
+#endif
+
+
+#ifdef WIN32
+ /*
+ ** In WIn95, build shape returns a pointer to the shape not its size
+ */
+ shape_pointer = Build_Frame(shapefile, shapenum, _ShapeBuffer);
+ if (shape_pointer) {
+ GraphicViewPortClass draw_window(LogicPage->Get_Graphic_Buffer(),
+ WindowList[window][WINDOWX] + LogicPage->Get_XPos(),
+ WindowList[window][WINDOWY] + LogicPage->Get_YPos(),
+ WindowList[window][WINDOWWIDTH],
+ WindowList[window][WINDOWHEIGHT]);
+ unsigned char * buffer = (unsigned char *) shape_pointer; //Get_Shape_Header_Data((void*)shape_pointer);
+
+#else //WIN32
+ if ( Build_Frame(shapefile, shapenum, _ShapeBuffer ) <= (unsigned long)_ShapeBufferSize) {
+ GraphicViewPortClass draw_window(LogicPage,
+ WindowList[window][WINDOWX],
+ WindowList[window][WINDOWY],
+ WindowList[window][WINDOWWIDTH],
+ WindowList[window][WINDOWHEIGHT]);
+ unsigned char * buffer = (unsigned char *)_ShapeBuffer;
+#endif //WIN32
+
+ UseOldShapeDraw = false;
+ /*
+ ** Rotation and scale handler.
+ */
+ if (rotation != DIR_N || scale != 0x0100) {
+
+ /*
+ ** Get the raw shape data without the new header and flag to use the old shape drawing
+ */
+ UseOldShapeDraw = true;
+#ifdef WIN32
+ buffer = (unsigned char *) Get_Shape_Header_Data((void*)shape_pointer);
+#endif
+
+ if (Debug_Rotate) {
+
+ GraphicBufferClass src(width, height, buffer);
+ width *= 2;
+ height *= 2;
+ memset(_xbuffer, '\0', SHAPE_BUFFER_SIZE);
+ GraphicBufferClass dst(width, height, _xbuffer);
+ Rotate_Bitmap(&src, &dst, rotation);
+ buffer = _xbuffer;
+
+ } else {
+
+ BitmapClass bm(width, height, buffer);
+ width *= 2;
+ height *= 2;
+ memset(_xbuffer, '\0', SHAPE_BUFFER_SIZE);
+ GraphicBufferClass gb(width, height, _xbuffer);
+ TPoint2D pt(width/2, height/2);
+
+ gb.Scale_Rotate(bm, pt, scale, (256-(rotation-64)));
+ buffer = _xbuffer;
+ }
+ }
+
+ /*
+ ** Special shadow drawing code (used for aircraft and bullets).
+ */
+ if ((flags & (SHAPE_FADING|SHAPE_PREDATOR)) == (SHAPE_FADING|SHAPE_PREDATOR)) {
+ flags = flags & ~(SHAPE_FADING|SHAPE_PREDATOR);
+ flags = flags | SHAPE_GHOST;
+ ghostdata = DisplayClass::SpecialGhost;
+ }
+
+ predoffset = Frame;
+
+ if (x > ( WindowList[window][WINDOWWIDTH] << 2)) {
+ predoffset = -predoffset;
+ }
+
+ if (draw_window.Lock()) {
+ if ((flags & (SHAPE_GHOST|SHAPE_FADING)) == (SHAPE_GHOST|SHAPE_FADING)) {
+ Buffer_Frame_To_Page(x, y, width, height, buffer, draw_window, flags | SHAPE_TRANS, ghostdata, fadingdata, 1, predoffset);
+ } else {
+ if (flags & SHAPE_FADING) {
+ Buffer_Frame_To_Page(x, y, width, height, buffer, draw_window, flags | SHAPE_TRANS, fadingdata, 1, predoffset);
+ } else {
+ if (flags & SHAPE_PREDATOR) {
+ Buffer_Frame_To_Page(x, y, width, height, buffer, draw_window, flags | SHAPE_TRANS, predoffset);
+ } else {
+ Buffer_Frame_To_Page(x, y, width, height, buffer, draw_window, flags | SHAPE_TRANS, ghostdata, predoffset);
+ }
+ }
+ }
+ draw_window.Unlock();
+ }
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * Shape_Dimensions -- Determine the minimum rectangle for the shape. *
+ * *
+ * This routine will calculate (using brute forced) the minimum rectangle that will *
+ * enclose the pixels of the shape. This rectangle will be relative to the upper left *
+ * corner of the maximum shape size. By using this minimum rectangle, it is possible to *
+ * greatly optimize the map 'dirty rectangle' logic. *
+ * *
+ * INPUT: shapedata -- Pointer to the shape data block. *
+ * *
+ * shapenum -- The shape number to examine. Each shape would have a different *
+ * dimension rectangle. *
+ * *
+ * OUTPUT: Returns with the rectangle that encloses the shape. *
+ * *
+ * WARNINGS: This routine uses brute force and is slow. It is presumed that the results *
+ * will be cached for subsiquent reuse. *
+ * *
+ * HISTORY: *
+ * 07/22/1996 JLB : Created. *
+ *=============================================================================================*/
+Rect const Shape_Dimensions(void const * shapedata, int shapenum)
+{
+ Rect rect;
+
+ if (shapedata == NULL || shapenum < 0 || shapenum > Get_Build_Frame_Count(shapedata)) {
+ return(rect);
+ }
+
+ char * shape;
+#ifdef WIN32
+ void * sh = (void *)Build_Frame(shapedata, shapenum, _ShapeBuffer);
+ if (sh == NULL) return(rect);
+// shape = (char *)sh;
+ shape = (char *)Get_Shape_Header_Data(sh);
+#else
+ Build_Frame(shapedata, shapenum, _ShapeBuffer);
+ shape = (char *)_ShapeBuffer;
+#endif
+
+ int width = Get_Build_Frame_Width(shapedata);
+ int height = Get_Build_Frame_Height(shapedata);
+
+ rect.X = 0;
+ rect.Y = 0;
+ int xlimit = width-1;
+ int ylimit = height-1;
+
+ /*
+ ** Find top edge of the shape.
+ */
+ for (int y = 0; y <= ylimit; y++) {
+ for (int x = 0; x <= xlimit; x++) {
+ if (shape[y*width + x] != 0) {
+ rect.Y = y;
+ rect.X = x;
+ y = ylimit+1;
+ break;
+ }
+ }
+ }
+
+ /*
+ ** Find bottom edge of the shape.
+ */
+ for (y = ylimit; y >= rect.Y; y--) {
+ for (int x = xlimit; x >= 0; x--) {
+ if (shape[y*width + x] != 0) {
+ rect.Height = (y-rect.Y)+1;
+ xlimit = x;
+ y = rect.Y-1;
+ break;
+ }
+ }
+ }
+
+ /*
+ ** Find left edge of the shape.
+ */
+ for (int x = 0; x < rect.X; x++) {
+ for (y = rect.Y; y < rect.Y+rect.Height; y++) {
+ if (shape[y*width + x] != 0) {
+ rect.X = x;
+ x = rect.X;
+ break;
+ }
+ }
+ }
+
+ /*
+ ** Find the right edge of the shape.
+ */
+ for (x = width-1; x >= xlimit; x--) {
+ for (y = rect.Y; y < rect.Y+rect.Height; y++) {
+ if (shape[y*width + x] != 0) {
+ rect.Width = (x-rect.X)+1;
+ x = xlimit-1;
+ break;
+ }
+ }
+ }
+
+ /*
+ ** Normalize the rectangle around the center of the shape.
+ */
+ rect.X -= width / 2;
+ rect.Y -= height / 2;
+
+ /*
+ ** Return with the minimum rectangle that encloses the shape.
+ */
+ return(rect);
+}
+
+
+/***********************************************************************************************
+ * Fetch_Techno_Type -- Convert type and ID into TechnoTypeClass pointer. *
+ * *
+ * This routine will convert the supplied RTTI type number and the ID value into a valid *
+ * TechnoTypeClass pointer. If there is an error in conversion, then NULL is returned. *
+ * *
+ * INPUT: type -- RTTI type of the techno class object. *
+ * *
+ * id -- Integer representation of the techno sub type number. *
+ * *
+ * OUTPUT: Returns with a pointer to the techno type class object specified or NULL if the *
+ * conversion could not occur. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/08/1995 JLB : Created. *
+ *=============================================================================================*/
+TechnoTypeClass const * Fetch_Techno_Type(RTTIType type, int id)
+{
+ switch (type) {
+ case RTTI_UNITTYPE:
+ case RTTI_UNIT:
+ return(&UnitTypeClass::As_Reference(UnitType(id)));
+
+ case RTTI_VESSELTYPE:
+ case RTTI_VESSEL:
+ return(&VesselTypeClass::As_Reference(VesselType(id)));
+
+ case RTTI_BUILDINGTYPE:
+ case RTTI_BUILDING:
+ return(&BuildingTypeClass::As_Reference(StructType(id)));
+
+ case RTTI_INFANTRYTYPE:
+ case RTTI_INFANTRY:
+ return(&InfantryTypeClass::As_Reference(InfantryType(id)));
+
+ case RTTI_AIRCRAFTTYPE:
+ case RTTI_AIRCRAFT:
+ return(&AircraftTypeClass::As_Reference(AircraftType(id)));
+
+ default:
+ break;
+ }
+ return(NULL);
+}
+
+
+/***********************************************************************************************
+ * VQ_Call_Back -- Maintenance callback used for VQ movies. *
+ * *
+ * This routine is called every frame of the VQ movie as it is being played. If this *
+ * routine returns non-zero, then the movie will stop. *
+ * *
+ * INPUT: buffer -- Pointer to the image buffer for the current frame. *
+ * *
+ * frame -- The frame number about to be displayed. *
+ * *
+ * OUTPUT: Should the movie be stopped? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/24/1995 JLB : Created. *
+ *=============================================================================================*/
+#ifdef WIN32
+void VQA_PauseAudio(void);
+void Check_VQ_Palette_Set(void);
+
+extern GraphicBufferClass VQ640;
+extern bool IsVQ640;
+long VQ_Call_Back(unsigned char *, long )
+{
+ int key = 0;
+ if (Keyboard->Check()) {
+ key = Keyboard->Get();
+ Keyboard->Clear();
+ }
+ Check_VQ_Palette_Set();
+#ifdef MOVIE640
+ if(IsVQ640) {
+ VQ640.Blit(SeenBuff);
+ } else {
+ Interpolate_2X_Scale(&SysMemPage, &SeenBuff, NULL);
+ }
+#else
+ Interpolate_2X_Scale(&SysMemPage, &SeenBuff, NULL);
+#endif
+ //Call_Back();
+
+ if ((BreakoutAllowed || Debug_Flag) && key == KN_ESC) {
+ Keyboard->Clear();
+ Brokeout = true;
+ return(true);
+ }
+
+ if (!GameInFocus) {
+ VQA_PauseAudio();
+ while (!GameInFocus) {
+ Check_For_Focus_Loss();
+ }
+ }
+ return(false);
+}
+
+#else //WIN32
+
+long VQ_Call_Back(unsigned char *, long )
+{
+ Call_Back();
+ if ((BreakoutAllowed || Debug_Flag) && Keyboard->Check()) {
+ if (Keyboard->Get() == KN_ESC) {
+ Keyboard->Clear();
+ Brokeout = true;
+ return(true);
+ }
+ Keyboard->Clear();
+ }
+ return(false);
+}
+#endif //WIN32
+
+
+/***********************************************************************************************
+ * Handle_Team -- Processes team selection command. *
+ * *
+ * This routine will handle creation and selection of pseudo teams that the player can *
+ * create or control. A team in this sense is an arbitrary grouping of units such that *
+ * rapid selection control is allowed. *
+ * *
+ * INPUT: team -- The logical team number to process. *
+ * *
+ * action-- The action to perform on this team: *
+ * 0 - Toggle the select state for all members of this team. *
+ * 1 - Select the members of this team. *
+ * 2 - Make all selected objects members of this team. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/27/1995 JLB : Created. *
+ *=============================================================================================*/
+void Handle_Team(int team, int action)
+{
+ int index;
+
+ //
+ // Recording support
+ //
+ if (Session.Record) {
+ TeamNumber = (char)team;
+ TeamEvent = (char)action + 1;
+ }
+
+ AllowVoice = true;
+ switch (action) {
+
+ /*
+ ** Toggle the team selection. If the team is selected, then merely unselect it. If the
+ ** team is not selected, then unselect all others before selecting this team.
+ */
+ case 3:
+ case 0:
+
+ /*
+ ** If a non team member is currently selected, then deselect all objects
+ ** before selecting this team.
+ */
+ if (CurrentObject.Count()) {
+ if (CurrentObject[0]->Is_Foot() && ((FootClass *)CurrentObject[0])->Group != team) {
+ Unselect_All();
+ }
+ }
+ for (index = 0; index < Vessels.Count(); index++) {
+ VesselClass * obj = Vessels.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) {
+ if (!obj->IsSelected) {
+ obj->Select();
+ AllowVoice = false;
+ }
+ }
+ }
+ for (index = 0; index < Units.Count(); index++) {
+ UnitClass * obj = Units.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) {
+ if (!obj->IsSelected) {
+ obj->Select();
+ AllowVoice = false;
+ }
+ }
+ }
+ for (index = 0; index < Infantry.Count(); index++) {
+ InfantryClass * obj = Infantry.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) {
+ if (!obj->IsSelected) {
+ obj->Select();
+ AllowVoice = false;
+ }
+ }
+ }
+ for (index = 0; index < Aircraft.Count(); index++) {
+ AircraftClass * obj = Aircraft.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) {
+ if (!obj->IsSelected) {
+ obj->Select();
+ AllowVoice = false;
+ }
+ }
+ }
+
+ /*
+ ** Center the map around the team if the ALT key was pressed too.
+ */
+ if (action == 3) {
+ Map.Center_Map();
+#ifdef WIN32
+ Map.Flag_To_Redraw(true);
+#endif //WIn32
+ }
+ break;
+
+ /*
+ ** Additive selection of team.
+ */
+ case 1:
+ for (index = 0; index < Units.Count(); index++) {
+ UnitClass * obj = Units.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) {
+ if (!obj->IsSelected) {
+ obj->Select();
+ AllowVoice = false;
+ }
+ }
+ }
+ for (index = 0; index < Vessels.Count(); index++) {
+ VesselClass * obj = Vessels.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) {
+ if (!obj->IsSelected) {
+ obj->Select();
+ AllowVoice = false;
+ }
+ }
+ }
+ for (index = 0; index < Infantry.Count(); index++) {
+ InfantryClass * obj = Infantry.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) {
+ if (!obj->IsSelected) {
+ obj->Select();
+ AllowVoice = false;
+ }
+ }
+ }
+ for (index = 0; index < Aircraft.Count(); index++) {
+ AircraftClass * obj = Aircraft.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) {
+ if (!obj->IsSelected) {
+ obj->Select();
+ AllowVoice = false;
+ }
+ }
+ }
+ break;
+
+ /*
+ ** Create the team.
+ */
+ case 2: {
+ long minx = 0x7FFFFFFFL, miny = 0x7FFFFFFFL;
+ long maxx = 0, maxy = 0;
+ TeamSpeed[team] = SPEED_WHEEL;
+ TeamMaxSpeed[team] = MPH_LIGHT_SPEED;
+ for (index = 0; index < Units.Count(); index++) {
+ UnitClass * obj = Units.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) {
+ if (obj->Group == team) obj->Group = 0xFF;
+ if (obj->IsSelected) {
+ obj->Group = team;
+ obj->Mark(MARK_CHANGE);
+ long xc = Cell_X(Coord_Cell(obj->Center_Coord()));
+ long yc = Cell_Y(Coord_Cell(obj->Center_Coord()));
+ if (xc < minx) minx = xc;
+ if (xc > maxx) maxx = xc;
+ if (yc < miny) miny = yc;
+ if (yc > maxy) maxy = yc;
+ if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) {
+ TeamMaxSpeed[team] = obj->Class->MaxSpeed;
+ TeamSpeed[team] = obj->Class->Speed;
+ }
+ }
+ }
+ }
+
+ for (index = 0; index < Vessels.Count(); index++) {
+ VesselClass * obj = Vessels.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) {
+ if (obj->Group == team) obj->Group = -1;
+ if (obj->IsSelected) {
+ obj->Group = team;
+ obj->Mark(MARK_CHANGE);
+ long xc = Cell_X(Coord_Cell(obj->Center_Coord()));
+ long yc = Cell_Y(Coord_Cell(obj->Center_Coord()));
+ if (xc < minx) minx = xc;
+ if (xc > maxx) maxx = xc;
+ if (yc < miny) miny = yc;
+ if (yc > maxy) maxy = yc;
+ if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) {
+ TeamMaxSpeed[team] = obj->Class->MaxSpeed;
+ TeamSpeed[team] = obj->Class->Speed;
+ }
+ }
+ }
+ }
+
+ for (index = 0; index < Infantry.Count(); index++) {
+ InfantryClass * obj = Infantry.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) {
+ if (obj->Group == team) obj->Group = 0xFF;
+ if (obj->IsSelected) {
+ obj->Group = team;
+ obj->Mark(MARK_CHANGE);
+ long xc = Cell_X(Coord_Cell(obj->Center_Coord()));
+ long yc = Cell_Y(Coord_Cell(obj->Center_Coord()));
+ if (xc < minx) minx = xc;
+ if (xc > maxx) maxx = xc;
+ if (yc < miny) miny = yc;
+ if (yc > maxy) maxy = yc;
+ if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) {
+ TeamMaxSpeed[team] = obj->Class->MaxSpeed;
+ }
+ }
+ }
+ }
+ for (index = 0; index < Aircraft.Count(); index++) {
+ AircraftClass * obj = Aircraft.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) {
+ if (obj->Group == team) obj->Group = 0xFF;
+ if (obj->IsSelected) {
+ obj->Group = team;
+ obj->Mark(MARK_CHANGE);
+ }
+ }
+ }
+
+ for (index = 0; index < Units.Count(); index++) {
+ UnitClass * obj = Units.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl &&
+ (obj->Group == team) && (obj->IsSelected) ) {
+
+ /*
+ ** When a team is first created, they're created without a
+ ** formation offset, so they will not be created in
+ ** formation. Later, if they're assigned a formation, the
+ ** XFormOffset & YFormOffset numbers will change to valid
+ ** offsets, and they'll be formationed.
+ */
+#if(1)
+ obj->XFormOffset = obj->YFormOffset = (int)0x80000000;
+#else
+#if(1)
+// Old always-north formation stuff
+ long xc = Cell_X(Coord_Cell(obj->Center_Coord()));
+ long yc = Cell_Y(Coord_Cell(obj->Center_Coord()));
+
+ obj->XFormOffset = xc - centerx;
+ obj->YFormOffset = yc - centery;
+#else
+// New method: save direction and distance rather than x & y offset
+ obj->XFormOffset = ::Direction(As_Coord(center), obj->Center_Coord());
+ obj->YFormOffset = ::Distance (As_Coord(center), obj->Center_Coord());
+#endif
+#endif
+ }
+ }
+
+ for (index = 0; index < Infantry.Count(); index++) {
+ InfantryClass * obj = Infantry.Ptr(index);
+ if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) {
+ if (obj->Group == team) obj->Group = 0xFF;
+ if (obj->IsSelected) obj->Group = team;
+ if (obj->Group == team && obj->IsSelected) {
+#if(1)
+ obj->XFormOffset = obj->YFormOffset = (int)0x80000000;
+#else
+#if(1)
+// Old always-north formation stuff
+ long xc = Cell_X(Coord_Cell(obj->Center_Coord()));
+ long yc = Cell_Y(Coord_Cell(obj->Center_Coord()));
+
+ obj->XFormOffset = xc - centerx;
+ obj->YFormOffset = yc - centery;
+#else
+// New method: save direction and distance rather than x & y offset
+ obj->XFormOffset = ::Direction(As_Coord(center), obj->Center_Coord());
+ obj->YFormOffset = ::Distance (As_Coord(center), obj->Center_Coord());
+#endif
+#endif
+ }
+ }
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ AllowVoice = true;
+}
+
+
+/***********************************************************************************************
+ * Handle_View -- Either records or restores the tactical view. *
+ * *
+ * This routine is used to record or restore the current map tactical view. *
+ * *
+ * INPUT: view -- The view number to work with. *
+ * *
+ * action-- The action to perform with this view number. *
+ * 0 = Restore the view to this previously remembered location. *
+ * 1 = Record the current view location. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/04/1995 JLB : Created. *
+ *=============================================================================================*/
+void Handle_View(int view, int action)
+{
+ if ((unsigned)view < sizeof(Scen.Views)/sizeof(Scen.Views[0])) {
+ if (action == 0) {
+ Map.Set_Tactical_Position(Coord_Whole(Cell_Coord(Scen.Views[view])));
+#ifdef WIN32
+ /*
+ ** Win95 scrolling logic cant handle just jumps in screen position so redraw the lot.
+ */
+ Map.Flag_To_Redraw (true);
+#endif //WIN32
+ } else {
+ Scen.Views[view] = Coord_Cell(Map.TacticalCoord);
+ }
+ }
+}
+
+
+#ifndef ROR_NOT_READY
+#define ROR_NOT_READY 21
+#endif
+
+static char * _CD_Volume_Label[] = {
+ "CD1",
+ "CD2",
+};
+static int _Num_Volumes = ARRAY_SIZE(_CD_Volume_Label);
+
+
+#ifdef WIN32
+/***********************************************************************************************
+ * Get_CD_Index -- returns the volume type of the CD in the given drive *
+ * *
+ * *
+ * *
+ * INPUT: drive number *
+ * timeout *
+ * *
+ * OUTPUT: 0 = gdi *
+ * 1 = nod *
+ * 2 = covert *
+ * -1 = non C&C *
+ * *
+ * WARNINGS: None *
+ * *
+ * HISTORY: *
+ * 5/21/96 5:27PM ST : Created *
+ *=============================================================================================*/
+int Get_CD_Index (int cd_drive, int timeout)
+{
+ char volume_name[128];
+ char buffer[128];
+ unsigned filename_length;
+ unsigned misc_dword;
+ int count = 0;
+
+ CountDownTimerClass timer;
+
+
+
+ timer.Set(timeout);
+
+
+ /*
+ ** Get the volume label. If we get a 'not ready' error then retry for the timeout
+ ** period.
+ */
+ for (;;) {
+ sprintf(buffer, "%c:\\", 'A' + cd_drive);
+ if (GetVolumeInformation ((char const *)buffer,
+ &volume_name[0] ,
+ (unsigned long)sizeof(volume_name),
+ (unsigned long *)NULL ,
+ (unsigned long *)&filename_length,
+ (unsigned long *)&misc_dword ,
+ (char *)NULL ,
+ (unsigned long)0)) {
+
+ /*
+ ** Try opening 'movies.mix' to verify that the CD is really there and is what
+ ** it says it is.
+ */
+ sprintf(buffer, "%c:\\main.mix", 'A' + cd_drive);
+
+ HANDLE handle = CreateFile(buffer, GENERIC_READ, FILE_SHARE_READ,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (handle != INVALID_HANDLE_VALUE) {
+ CloseHandle(handle);
+
+ /*
+ ** Match the volume label to the list of known C&C volume labels.
+ */
+ for (int i=0 ; i<_Num_Volumes ; i++) {
+ if (!stricmp(_CD_Volume_Label[i], volume_name)) return (i);
+ }
+ } else {
+ if (!count)
+ count++;
+ else return -1;
+ }
+ } else {
+ /*
+ ** Failed to get the volume label on a known CD drive.
+ ** If this is a CD changer it may require time to swap the disks so dont return
+ ** immediately if the error is ROR_NOT_READY
+ */
+ if (!timer.Time()) return -1;
+ int val = GetLastError();
+ if (val != ROR_NOT_READY) return -1;
+ }
+ }
+}
+#else
+int Get_CD_Index (int cd_drive, int )
+{
+ char buffer[128];
+
+ /*
+ ** We need to do this twice because of the possibilities of a directory
+ ** being cached. If this is so, it will only be discovered when we
+ ** actually attempt to read a file from the drive.
+ */
+ if(cd_drive) for (int count = 0; count < 2; count ++) {
+ struct find_t ft;
+ int file;
+ int open_failed;
+
+ /*
+ ** Create a path for the cd drive and attempt to read the volume label from
+ ** it.
+ */
+ sprintf(buffer, "%c:\\", 'A' + cd_drive);
+
+ /*
+ ** If we are able to read the volume label, this is good but not enough.
+ ** Further verification must be done.
+ */
+ if (!_dos_findfirst(buffer, _A_VOLID, &ft)) {
+
+ /*
+ ** Since some versions of disk cacheing software will cache the CD's
+ ** directory tracks, we may think the CD is in the drive when it is
+ ** actually not. To resolve this we must attempt to open a file on
+ ** the cd. Opening a file will always update the directory tracks
+ ** (suposedly).
+ */
+ sprintf(buffer, "%c:\\main.mix", 'A' + cd_drive);
+ open_failed = _dos_open(buffer, O_RDONLY|SH_DENYNO, &file);
+ if (!open_failed) {
+ _dos_close(file);
+
+ /*
+ ** Hey some times the stupid dos driver appends a period to the
+ ** name if it is eight characters long. If the last char is a
+ ** period then erase it.
+ */
+ if (ft.name[strlen(ft.name)-1] == '.') {
+ ft.name[strlen(ft.name)-1] = 0;
+ }
+
+ /*
+ ** Match the volume label to the list of known C&C volume labels.
+ */
+ for (int i=0 ; i < _Num_Volumes ; i++) {
+ if (!stricmp(_CD_Volume_Label[i], ft.name)) return (i);
+ }
+ }
+ }
+ }
+ return -1;
+}
+#endif
+
+
+/***********************************************************************************************
+ * Force_CD_Available -- Ensures that specified CD is available. *
+ * *
+ * Call this routine when you need to ensure that the specified CD is actually in the *
+ * CD-ROM drive. *
+ * *
+ * INPUT: cd -- The CD that must be available. This will either be "0" for the GDI CD, or *
+ * "1" for the Nod CD. If either CD will qualify, then pass in "-1". *
+ * *
+ * OUTPUT: Is the CD inserted and available? If false is returned, then this indicates that *
+ * the player pressed . *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/11/1995 JLB : Created. *
+ * 05/22/1996 ST : Handles multiple CD drives / CD changers *
+ *=============================================================================================*/
+bool Force_CD_Available(int cd)
+{
+#ifndef DEMO
+ static int _last = -1;
+#endif
+// static char _palette[768];
+// static char _hold[256];
+ static void *font;
+ static char * _cd_name[] = {
+ "RED ALERT DISK 1",
+ "RED ALERT DISK 2",
+ };
+
+ int new_cd_drive = 0;
+ int cd_index;
+ char buffer[128];
+ int cd_drive;
+ int current_drive;
+ int drive_search_timeout;
+
+ ThemeType theme_playing = THEME_NONE;
+
+ /*
+ ** If the required CD is set to -2 then it means that the file is present
+ ** on the local hard drive and we shouldn't have to worry about it.
+ */
+ if (cd == -2) return(true);
+
+ /*
+ ** Find out if the CD in the current drive is the one we are looking for
+ */
+ current_drive = CCFileClass::Get_CD_Drive();
+ cd_index = Get_CD_Index(current_drive, 1*60);
+ if (cd_index >= 0) {
+ if (cd == cd_index || cd == -1) {
+ /*
+ ** The required CD is still in the CD drive we used last time
+ */
+ new_cd_drive = current_drive;
+ }
+ }
+
+ /*
+ ** Flag that we will have to restart the theme
+ */
+ theme_playing = Theme.What_Is_Playing();
+ Theme.Stop();
+
+
+ if (!new_cd_drive) {
+ /*
+ ** Check the last CD drive we used if its different from the current one
+ */
+ int last_drive = CCFileClass::Get_Last_CD_Drive();
+
+ /*
+ ** Make sure the last drive is valid and it isnt the current drive
+ */
+ if (last_drive && last_drive != CCFileClass::Get_CD_Drive()) {
+ /*
+ ** Find out if there is a C&C cd in the last drive and if so is it the one we are looking for
+ ** Give it a nice big timeout so the CD changer has time to swap the discs
+ */
+ cd_index = Get_CD_Index(last_drive, 10*60);
+ if (cd_index >= 0) {
+ if (cd == cd_index || cd == -1) {
+ /*
+ ** The required CD is in the CD drive we used last time
+ */
+ new_cd_drive = last_drive;
+ }
+ }
+ }
+ }
+
+ /*
+ ** Lordy. No sign of that blimming CD anywhere. Search all the CD drives
+ ** then if we still cant find it prompt the user to insert it.
+ */
+ if (!new_cd_drive) {
+
+ /*
+ ** Small timeout for the first pass through the drives
+ */
+ drive_search_timeout = 2*60;
+
+ for (;;) {
+ /*
+ ** Search all present CD drives for the required disc.
+ */
+ for (int i=0 ; i=0) {
+ /*
+ ** We found a C&C cd - lets see if it was the one we were looking for
+ */
+ if (cd == cd_index || cd == -1 || cd == -2) {
+ /*
+ ** Woohoo! The disk was in a different cd drive. Refresh the search path list
+ * and return.
+ */
+ new_cd_drive = cd_drive;
+ break;
+ }
+ }
+ }
+
+ /*
+ ** A new disc has become available so break
+ */
+ if (new_cd_drive) break;
+
+ /*
+ ** Increase the timeout for subsequent drive searches.
+ */
+ drive_search_timeout = 5*60;
+
+ /*
+ ** Prompt to insert the CD into the drive.
+ */
+ if (cd == -1) {
+ sprintf(buffer, Text_String(TXT_CD_DIALOG_1), cd+1, _cd_name[cd]);
+ } else {
+ sprintf(buffer, Text_String(TXT_CD_DIALOG_2), cd+1, _cd_name[cd]);
+ }
+#ifdef WIN32
+ GraphicViewPortClass * oldpage = Set_Logic_Page(SeenBuff);
+#else
+ GraphicBufferClass * oldpage = Set_Logic_Page(SeenBuff);
+#endif
+ theme_playing = Theme.What_Is_Playing();
+ Theme.Stop();
+ int hidden = Get_Mouse_State();
+ font = (void *)FontPtr;
+
+ /*
+ ** Only set the palette if necessary.
+ */
+ if (PaletteClass::CurrentPalette[1].Red_Component() +
+ PaletteClass::CurrentPalette[1].Blue_Component() +
+ PaletteClass::CurrentPalette[1].Green_Component() == 0) {
+
+ GamePalette.Set();
+ }
+
+ Keyboard->Clear();
+
+ while (Get_Mouse_State()) Show_Mouse();
+
+ if (WWMessageBox().Process(buffer, TXT_OK, TXT_CANCEL, TXT_NONE, TRUE) == 1) {
+ Set_Logic_Page(oldpage);
+ Hide_Mouse();
+ return(false);
+ }
+ while (hidden--) Hide_Mouse();
+ Set_Font(font);
+ Set_Logic_Page(oldpage);
+ }
+ }
+
+ CurrentCD = cd_index;
+
+#ifndef DEMO
+
+ CCFileClass::Set_CD_Drive(new_cd_drive);
+ CCFileClass::Refresh_Search_Drives();
+
+ /*
+ ** If it broke out of the query for CD-ROM loop, then this means that the
+ ** CD-ROM has been inserted.
+ */
+// if (cd > -3 && _last != cd) {
+ if (cd > -1 && _last != cd) {
+ _last = cd;
+
+ Theme.Stop();
+
+// if (ConquerMix) delete ConquerMix;
+ if (MoviesMix) delete MoviesMix;
+ if (GeneralMix) delete GeneralMix;
+ if (ScoreMix) delete ScoreMix;
+ if (MainMix) delete MainMix;
+
+ MainMix = new MFCD("MAIN.MIX", &FastKey);
+ assert(MainMix != NULL);
+// ConquerMix = new MFCD("CONQUER.MIX", &FastKey);
+ if (CCFileClass("MOVIES1.MIX").Is_Available()) {
+ MoviesMix = new MFCD("MOVIES1.MIX", &FastKey);
+ } else {
+ MoviesMix = new MFCD("MOVIES2.MIX", &FastKey);
+ }
+ assert(MoviesMix != NULL);
+ GeneralMix = new MFCD("GENERAL.MIX", &FastKey);
+ ScoreMix = new MFCD("SCORES.MIX", &FastKey);
+ ThemeClass::Scan();
+ }
+#endif
+
+ if (theme_playing != THEME_NONE) {
+ Theme.Queue_Song(theme_playing);
+ }
+
+ return(true);
+}
+
+
+/***************************************************************************
+ * DISK_SPACE_AVAILABLE -- returns bytes of free disk space *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: returns amount of free disk space *
+ * *
+ * HISTORY: *
+ * 08/11/1995 PWG : Created. *
+ *=========================================================================*/
+unsigned long Disk_Space_Available(void)
+{
+ struct diskfree_t diskdata;
+ unsigned drive;
+
+ _dos_getdrive(&drive);
+ _dos_getdiskfree(drive, &diskdata);
+
+ return(diskdata.avail_clusters * diskdata.sectors_per_cluster * diskdata.bytes_per_sector);
+}
+
+
+/***********************************************************************************************
+ * Do_Record_Playback -- handles saving/loading map pos & current object *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 08/15/1995 BRR : Created. *
+ *=============================================================================================*/
+static void Do_Record_Playback(void)
+{
+ int count;
+ TARGET tgt;
+ int i;
+ COORDINATE coord;
+ ObjectClass * obj;
+ unsigned long sum;
+ unsigned long sum2;
+ unsigned long ltgt;
+
+ /*
+ ** Record a game
+ */
+ if (Session.Record) {
+
+ /*
+ ** Save the map's location
+ */
+ Session.RecordFile.Write(&Map.DesiredTacticalCoord,
+ sizeof (Map.DesiredTacticalCoord));
+
+ /*
+ ** Save the current object list count
+ */
+ count = CurrentObject.Count();
+ Session.RecordFile.Write(&count, sizeof(count));
+
+ /*
+ ** Save a CRC of the selected-object list.
+ */
+ sum = 0;
+ for (i = 0; i < count; i++) {
+ ltgt = (unsigned long)(CurrentObject[i]->As_Target());
+ sum += ltgt;
+ }
+ Session.RecordFile.Write (&sum, sizeof(sum));
+
+ /*
+ ** Save all selected objects.
+ */
+ for (i = 0; i < count; i++) {
+ tgt = CurrentObject[i]->As_Target();
+ Session.RecordFile.Write (&tgt, sizeof(tgt));
+ }
+
+ //
+ // Save team-selection and formation events
+ //
+ Session.RecordFile.Write (&TeamEvent, sizeof(TeamEvent));
+ Session.RecordFile.Write (&TeamNumber, sizeof(TeamNumber));
+ Session.RecordFile.Write (&FormationEvent, sizeof(FormationEvent));
+Session.RecordFile.Write (TeamMaxSpeed, sizeof(TeamMaxSpeed));
+Session.RecordFile.Write (TeamSpeed, sizeof(TeamSpeed));
+Session.RecordFile.Write (&FormMove, sizeof(FormMove));
+Session.RecordFile.Write (&FormSpeed, sizeof(FormSpeed));
+Session.RecordFile.Write (&FormMaxSpeed, sizeof(FormMaxSpeed));
+ TeamEvent = 0;
+ TeamNumber = 0;
+ FormationEvent = 0;
+ }
+
+ /*
+ ** Play back a game ("attract" mode)
+ */
+ if (Session.Play) {
+
+ /*
+ ** Read & set the map's location.
+ */
+ if (Session.RecordFile.Read(&coord, sizeof(coord))==sizeof(coord)) {
+ if (coord != Map.DesiredTacticalCoord) {
+ Map.Set_Tactical_Position(coord);
+ }
+ }
+
+ if (Session.RecordFile.Read(&count, sizeof(count))==sizeof(count)) {
+ /*
+ ** Compute a CRC of the current object-selection list.
+ */
+ sum = 0;
+ for (i = 0; i < CurrentObject.Count(); i++) {
+ ltgt = (unsigned long)(CurrentObject[i]->As_Target());
+ sum += ltgt;
+ }
+
+ /*
+ ** Load the CRC of the objects on disk; if it doesn't match, select
+ ** all objects as they're loaded.
+ */
+ Session.RecordFile.Read (&sum2, sizeof(sum2));
+ if (sum2 != sum) {
+ Unselect_All();
+ }
+
+ AllowVoice = true;
+
+ for (i = 0; i < count; i++) {
+ if (Session.RecordFile.Read (&tgt, sizeof(tgt))==sizeof(tgt)) {
+ obj = As_Object(tgt);
+ if (obj && (sum2 != sum)) {
+ obj->Select();
+ AllowVoice = false;
+ }
+ }
+ }
+
+ AllowVoice = true;
+
+ }
+
+ //
+ // Save team-selection and formation events
+ //
+ Session.RecordFile.Read (&TeamEvent, sizeof(TeamEvent));
+ Session.RecordFile.Read (&TeamNumber, sizeof(TeamNumber));
+ Session.RecordFile.Read (&FormationEvent, sizeof(FormationEvent));
+ if (TeamEvent) {
+ Handle_Team(TeamNumber, TeamEvent - 1);
+ }
+ if (FormationEvent) {
+ Toggle_Formation();
+ }
+
+Session.RecordFile.Read (TeamMaxSpeed, sizeof(TeamMaxSpeed));
+Session.RecordFile.Read (TeamSpeed, sizeof(TeamSpeed));
+Session.RecordFile.Read (&FormMove, sizeof(FormMove));
+Session.RecordFile.Read (&FormSpeed, sizeof(FormSpeed));
+Session.RecordFile.Read (&FormMaxSpeed, sizeof(FormMaxSpeed));
+ /*
+ ** The map isn't drawn in playback mode, so draw it here.
+ */
+ Map.Render();
+ }
+}
+
+
+/***********************************************************************************************
+ * Hires_Load -- Allocates memory for, and loads, a resolution dependant file. *
+ * *
+ * *
+ * *
+ * INPUT: Name of file to load *
+ * *
+ * OUTPUT: Ptr to loaded file *
+ * *
+ * WARNINGS: Caller is responsible for releasing the memory allocated *
+ * *
+ * *
+ * HISTORY: *
+ * 5/13/96 3:20PM ST : Created *
+ *=============================================================================================*/
+void * Hires_Load(char * name)
+{
+ char filename[30];
+ int length;
+ void * return_ptr;
+
+#ifdef WIN32
+ sprintf(filename, "H%s", name);
+#else
+ strcpy(filename, name);
+#endif
+ CCFileClass file (filename);
+
+ if (file.Is_Available()) {
+
+ length = file.Size();
+ return_ptr = new char[length];
+ file.Read(return_ptr, length);
+ return (return_ptr);
+
+ } else {
+ return (NULL);
+ }
+}
+
+
+/***********************************************************************************************
+ * Crate_From_Name -- Given a crate name convert it to a crate type. *
+ * *
+ * Use this routine to convert an ASCII crate name into a crate type. If no match could *
+ * be found, then CRATE_MONEY is assumed. *
+ * *
+ * INPUT: name -- Pointer to the crate name text to convert into a crate type. *
+ * *
+ * OUTPUT: Returns with the crate name converted into a crate type. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/12/1996 JLB : Created. *
+ *=============================================================================================*/
+CrateType Crate_From_Name(char const * name)
+{
+ if (name != NULL) {
+ for (CrateType crate = CRATE_FIRST; crate < CRATE_COUNT; crate++) {
+ if (stricmp(name, CrateNames[crate]) == 0) return(crate);
+ }
+ }
+ return(CRATE_MONEY);
+}
+
+
+/***********************************************************************************************
+ * Owner_From_Name -- Convert an owner name into a bitfield. *
+ * *
+ * This will take an owner specification and convert it into a bitfield that represents *
+ * it. Sometimes this will be just a single house bit, but other times it could be *
+ * all the allies or soviet house bits combined. *
+ * *
+ * INPUT: text -- Pointer to the text to convert into a house bitfield. *
+ * *
+ * OUTPUT: Returns with the houses specified. The value is in the form of a bit field with *
+ * one bit per house type. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/12/1996 JLB : Created. *
+ *=============================================================================================*/
+int Owner_From_Name(char const * text)
+{
+ int ownable = 0;
+ if (stricmp(text, "soviet") == 0) {
+ ownable |= HOUSEF_SOVIET;
+ } else {
+ if (stricmp(text, "allies") == 0 || stricmp(text, "allied") == 0) {
+ ownable |= HOUSEF_ALLIES;
+ } else {
+ HousesType h = HouseTypeClass::From_Name(text);
+ if (h != HOUSE_NONE && (h < HOUSE_MULTI1 || h > HOUSE_MULTI8)) {
+ ownable |= (1 << h);
+ }
+ }
+ }
+ return(ownable);
+}
+
+
+/***********************************************************************************************
+ * Shake_The_Screen -- Dispatcher that shakes the screen. *
+ * *
+ * This routine will shake the game screen the number of shakes requested. *
+ * *
+ * INPUT: shakes -- The number of shakes to shake the screen. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 09/04/1996 BWG : Created. *
+ *=============================================================================================*/
+void Shake_The_Screen(int shakes)
+{
+#ifdef WIN32
+ shakes += shakes;
+
+ Hide_Mouse();
+ SeenPage.Blit(HidPage);
+ int oldyoff = 0;
+ int newyoff = 0;
+ while(shakes--) {
+ int x = TickCount;
+// CountDownTimer = 1;
+ do {
+ newyoff = Sim_Random_Pick(0,2) - 1;
+ } while (newyoff == oldyoff);
+ switch (newyoff) {
+ case -1:
+ HidPage.Blit(SeenPage, 0,2, 0,0, 640,398);
+ break;
+ case 0:
+ HidPage.Blit(SeenPage);
+ break;
+ case 1:
+ HidPage.Blit(SeenPage, 0,0, 0,2, 640,398);
+ break;
+ } while (x == TickCount);
+// } while (CountDownTimer != 0) ;
+ }
+ HidPage.Blit(SeenPage);
+ Show_Mouse();
+#else
+ Shake_Screen(shakes);
+#endif
+}
+
+
+/***********************************************************************************************
+ * List_Copy -- Makes a copy of a cell offset list. *
+ * *
+ * This routine will make a copy of a cell offset list. It will only copy the significant *
+ * elements of the list limited by the maximum length specified. *
+ * *
+ * INPUT: source -- Pointer to a cell offset list. *
+ * *
+ * len -- The maximum number of cell offset elements to store in to the *
+ * destination list pointer. *
+ * *
+ * dest -- Pointer to the destination list to store the copy into. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: Ensure that the destination list is large enough to hold the list copy. *
+ * *
+ * HISTORY: *
+ * 09/04/1996 JLB : Created. *
+ *=============================================================================================*/
+void List_Copy(short const * source, int len, short * dest)
+{
+ if (dest == NULL || dest == NULL) {
+ return;
+ }
+
+ while (len > 0) {
+ *dest = *source;
+ if (*dest == REFRESH_EOL) break;
+ dest++;
+ source++;
+ len--;
+ }
+}
+
+
+
+#if 0
+//
+// Boy, this function sure is crummy
+//
+void Crummy(int crumb1, int crumb2)
+{
+ if (Debug_Check_Map && Debug_Heap_Dump) {
+ Mono_Printf("Hi, I'm Crummy. And so are these: %d, %d\n",crumb1,crumb2);
+ }
+}
+#endif
diff --git a/CODE/CONQUER.H b/CODE/CONQUER.H
new file mode 100644
index 0000000..ab194da
--- /dev/null
+++ b/CODE/CONQUER.H
@@ -0,0 +1,573 @@
+/*
+** Command & Conquer Red Alert(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 0 //
+#define TXT_CREDIT_FORMAT 1 // %3d.%02d
+#define TXT_TIME_FORMAT_HOURS 2 // Temps:%02d:%02d:%02d
+#define TXT_TIME_FORMAT_NO_HOURS 3 // Temps:%02d:%02d
+#define TXT_BUTTON_SELL 4 // Vente
+#define TXT_SELL 5 // Vente structure
+#define TXT_BUTTON_REPAIR 6 // R‚paration
+#define TXT_YOU 7 // Vous :
+#define TXT_ENEMY 8 // Ennemi :
+#define TXT_BUILD_DEST 9 // Bƒtiments d‚truits par
+#define TXT_UNIT_DEST 10 // Unit‚s d‚truites par
+#define TXT_TIB_HARV 11 // Minerai r‚colt‚ par
+#define TXT_SCORE_1 12 // Score: %d
+#define TXT_YES 13 // Oui
+#define TXT_NO 14 // Non
+#define TXT_SCENARIO_WON 15 // Mission Accomplie
+#define TXT_SCENARIO_LOST 16 // Mission Echou‚e
+#define TXT_START_NEW_GAME 17 // Nouvelle partie
+#define TXT_INTRO 18 // Intro/Preview
+#define TXT_CANCEL 19 // Annuler
+#define TXT_ROCK 20 // Rocher
+#define TXT_CIVILIAN 21 // Civil
+#define TXT_JP 22 // Equipe de confinement
+#define TXT_OK 23 // OK
+#define TXT_TREE 24 // Arbre
+#define TXT_LEFT 25 //
+#define TXT_RIGHT 26 //
+#define TXT_UP 27 //
+#define TXT_DOWN 28 //
+#define TXT_CLEAR 29 // Effacer
+#define TXT_WATER 30 // Eau
+#define TXT_ROAD 31 // Route
+#define TXT_SLOPE 32 // Pente
+#define TXT_PATCH 33 // Patch
+#define TXT_RIVER 34 // RiviŠre
+#define TXT_LOAD_MISSION 35 // Charger Mission
+#define TXT_SAVE_MISSION 36 // Sauvegarder Mission
+#define TXT_DELETE_MISSION 37 // Effacer Mission
+#define TXT_LOAD_BUTTON 38 // Charger
+#define TXT_SAVE_BUTTON 39 // Sauvegarder
+#define TXT_DELETE_BUTTON 40 // Effacer
+#define TXT_GAME_CONTROLS 41 // Contr“les
+#define TXT_SOUND_CONTROLS 42 // Son
+#define TXT_RESUME_MISSION 43 // Reprendre Mission
+#define TXT_VISUAL_CONTROLS 44 // Affichage
+#define TXT_QUIT_MISSION 45 // Abandonner Mission
+#define TXT_EXIT_GAME 46 // Quitter le jeu
+#define TXT_OPTIONS 47 // Options
+#define TXT_SQUISH 48 // D‚bris humains
+#define TXT_CRATER 49 // CratŠre
+#define TXT_SCORCH 50 // Marque de br–lure
+#define TXT_BRIGHTNESS 51 // Luminosit‚ :
+#define TXT_MUSIC 52 // Musique
+#define TXT_VOLUME 53 // Effets sonores
+#define TXT_TINT 54 // Teintes :
+#define TXT_CONTRAST 55 // Contraste :
+#define TXT_SPEED 56 // Vitesse du jeu :
+#define TXT_SCROLLRATE 57 // Vitesse d‚filement :
+#define TXT_COLOR 58 // Couleur :
+#define TXT_RETURN_TO_GAME 59 // Revenir au jeu
+#define TXT_ENEMY_SOLDIER 60 // Soldat ennemi
+#define TXT_ENEMY_VEHICLE 61 // V‚hicule ennemi
+#define TXT_ENEMY_STRUCTURE 62 // Structure ennemie
+#define TXT_LTANK 63 // Tank l‚ger
+#define TXT_MTANK 64 // Tank lourd
+#define TXT_MTANK2 65 // Tank moyen
+#define TXT_HTANK 66 // Tank Mammouth
+#define TXT_SAM 67 // Missiles SAM
+#define TXT_JEEP 68 // Ranger
+#define TXT_TRANS 69 // H‚licoptŠre Chinook
+#define TXT_HARVESTER 70 // Collecteur minerai
+#define TXT_ARTY 71 // Artillerie
+#define TXT_E1 72 // Mitrailleurs
+#define TXT_E2 73 // Grenadiers
+#define TXT_E3 74 // Bazookas
+#define TXT_E4 75 // Lance-flammes
+#define TXT_HELI 76 // H‚licoptŠre d'assaut
+#define TXT_ORCA 77 // Hind
+#define TXT_APC 78 // VBT
+#define TXT_GUARD_TOWER 79 // Tour de garde
+#define TXT_COMMAND 80 // D“me radar
+#define TXT_HELIPAD 81 // H‚liport
+#define TXT_AIRSTRIP 82 // Piste d'atterrissage
+#define TXT_STORAGE 83 // Silo minerai
+#define TXT_CONST_YARD 84 // Chantier de construction
+#define TXT_REFINERY 85 // Raffinerie de minerai
+#define TXT_CIV1 86 // Eglise
+#define TXT_CIV2 87 // Chez Hans et Gretel
+#define TXT_CIV3 88 // Manoir d'Hewitt
+#define TXT_CIV4 89 // Maison de Ricktor
+#define TXT_CIV5 90 // Maison de Gretchin
+#define TXT_CIV6 91 // La grange
+#define TXT_CIV7 92 // Pub Damon
+#define TXT_CIV8 93 // Maison de Fran
+#define TXT_CIV9 94 // Usine d'instruments
+#define TXT_CIV10 95 // Fabricant de jouets
+#define TXT_CIV11 96 // Maison de Ludwig
+#define TXT_CIV12 97 // Meules de foin
+#define TXT_CIV13 98 // Meule de foin
+#define TXT_CIV14 99 // Champ de bl‚
+#define TXT_CIV15 100 // Champ en friche
+#define TXT_CIV16 101 // Champ de ma‹s
+#define TXT_CIV17 102 // Champ de c‚leri
+#define TXT_CIV18 103 // Champ de pommes de terre
+#define TXT_CIV20 104 // Maison de Sala
+#define TXT_CIV21 105 // Maison d'Abdul
+#define TXT_CIV22 106 // Le Pub Barjo de Pablo
+#define TXT_CIV23 107 // Puits du village
+#define TXT_CIV24 108 // Marchand de chameaux
+#define TXT_CIV25 109 // Eglise
+#define TXT_CIV26 110 // Maison d'Ali
+#define TXT_CIV27 111 // Ted le Marchand
+#define TXT_CIV28 112 // Maison de Menelik
+#define TXT_CIV29 113 // Maison du pasteur John
+#define TXT_CIV30 114 // Puits du village
+#define TXT_CIV31 115 // Hutte du gu‚risseur
+#define TXT_CIV32 116 // Hutte de Rikitikitembo
+#define TXT_CIV33 117 // Hutte de Roarke
+#define TXT_CIV34 118 // Hutte de Moubasa'
+#define TXT_CIV35 119 // Hutte d'Aksoum
+#define TXT_CIV36 120 // Hutte de Mambo
+#define TXT_CIV37 121 // Le studio
+#define TXT_CIVMISS 122 // Centre technologique
+#define TXT_TURRET 123 // Tourelle
+#define TXT_GUNBOAT 124 // Aviso-torpilleur
+#define TXT_MCV 125 // V‚hicule de construction
+#define TXT_POWER 126 // Centrale ‚lectrique
+#define TXT_ADVANCED_POWER 127 // Centrale ‚lectrique avanc‚e
+#define TXT_HOSPITAL 128 // H“pital
+#define TXT_BARRACKS 129 // Caserne
+#define TXT_PUMP 130 // Pompe
+#define TXT_TANKER 131 // P‚trolier
+#define TXT_SANDBAG_WALL 132 // Sacs de sable
+#define TXT_CYCLONE_WALL 133 // Cl“ture grillag‚e
+#define TXT_BRICK_WALL 134 // Mur de b‚ton
+#define TXT_BARBWIRE_WALL 135 // Cl“ture barbel‚e
+#define TXT_WOOD_WALL 136 // BarriŠre de bois
+#define TXT_WEAPON_FACTORY 137 // Usine d'armement
+#define TXT_AGUARD_TOWER 138 // Tour de garde avanc‚e
+#define TXT_BIO_LAB 139 // Laboratoire biologique
+#define TXT_FIX_IT 140 // Centre de service
+#define TXT_TAB_SIDEBAR 141 // Contr“les
+#define TXT_TAB_BUTTON_CONTROLS 142 // Options
+#define TXT_TAB_BUTTON_DATABASE 143 // Base de donn‚es
+#define TXT_SHADOW 144 // Terrain inconnu
+#define TXT_OPTIONS_MENU 145 // Menu des options
+#define TXT_STOP 146 // Stop
+#define TXT_PLAY 147 // Lect
+#define TXT_SHUFFLE 148 // Al‚at.
+#define TXT_REPEAT 149 // R‚p‚ter
+#define TXT_MUSIC_VOLUME 150 // Musique :
+#define TXT_SOUND_VOLUME 151 // Effets sonores :
+#define TXT_ON 152 // Oui
+#define TXT_OFF 153 // Non
+#define TXT_MULTIPLAYER_GAME 154 // Jeu Multijoueurs
+#define TXT_NO_FILES 155 // Pas de fichiers disponibles
+#define TXT_DELETE_SINGLE_FILE 156 // Voulez-vous effacer ce
+#define TXT_DELETE_MULTIPLE_FILES 157 // Voulez-vous effacer %d
+#define TXT_RESET_MENU 158 // D‚faut
+#define TXT_CONFIRM_EXIT 159 // Voulez-vous abandonner la
+#define TXT_MISSION_DESCRIPTION 160 // Description de la mission
+#define TXT_C1 161 // Joe
+#define TXT_C2 162 // Barry
+#define TXT_C3 163 // Shelly
+#define TXT_C4 164 // Maria
+#define TXT_C5 165 // Karen
+#define TXT_C6 166 // Steve
+#define TXT_C7 167 // Phil
+#define TXT_C8 168 // Dwight
+#define TXT_C9 169 // Erik
+#define TXT_EINSTEIN 170 // Prof. Einstein
+#define TXT_BIB 171 // Cour
+#define TXT_FASTER 172 // Rapide
+#define TXT_SLOWER 173 // Lent
+#define TXT_AIR_STRIKE 174 // Attaque a‚rienne
+#define TXT_STEEL_CRATE 175 // Caisse d'acier
+#define TXT_WOOD_CRATE 176 // Caisse de bois
+#define TXT_WATER_CRATE 177 // Caisse flottante
+#define TXT_FLAG_SPOT 178 // Emplacement du drapeau
+#define TXT_UNABLE_READ_SCENARIO 179 // Lecture sc‚nario impossible
+#define TXT_ERROR_LOADING_GAME 180 // Erreur de chargement !
+#define TXT_OBSOLETE_SAVEGAME 181 // Sauvegarde obsolŠte
+#define TXT_MUSTENTER_DESCRIPTION 182 // Vous devez entrer une
+#define TXT_ERROR_SAVING_GAME 183 // Erreur de sauvegarde !
+#define TXT_DELETE_FILE_QUERY 184 // Effacer ce fichier ?
+#define TXT_EMPTY_SLOT 185 // [EMPLACEMENT VIDE]
+#define TXT_SELECT_MPLAYER_GAME 186 // Choix du jeu Multijoueurs
+#define TXT_MODEM_SERIAL 187 // Modem/S‚rie
+#define TXT_NETWORK 188 // R‚seau
+#define TXT_INIT_NET_ERROR 189 // Initialisation r‚seau
+#define TXT_JOIN_NETWORK_GAME 190 // Rejoindre jeu en r‚seau
+#define TXT_NEW 191 // Nouveau
+#define TXT_JOIN 192 // Joindre
+#define TXT_SEND_MESSAGE 193 // Envoi Message
+#define TXT_YOUR_NAME 194 // Votre nom :
+#define TXT_SIDE_COLON 195 // Camp :
+#define TXT_COLOR_COLON 196 // Couleur :
+#define TXT_GAMES 197 // Parties
+#define TXT_PLAYERS 198 // Joueurs
+#define TXT_SCENARIO_COLON 199 // Sc‚nario :
+#define TXT_NOT_FOUND 200 // >> NON TROUVE <<
+#define TXT_START_CREDITS_COLON 201 // Cr‚dits :
+#define TXT_BASES_COLON 202 // Bases :
+#define TXT_TIBERIUM_COLON 203 // Minerai :
+#define TXT_CRATES_COLON 204 // Caisses :
+#define TXT_AI_PLAYERS_COLON 205 // Joueurs IA :
+#define TXT_REQUEST_DENIED 206 // Demande refus‚e.
+#define TXT_UNABLE_PLAY_WAAUGH 207 // Impossible de jouer,
+#define TXT_NOTHING_TO_JOIN 208 // Aucune partie disponible !
+#define TXT_NAME_ERROR 209 // Vous devez entrer un nom !
+#define TXT_DUPENAMES_NOTALLOWED 210 // Les noms identiques ne sont
+#define TXT_YOURGAME_OUTDATED 211 // La version de votre jeu est
+#define TXT_DESTGAME_OUTDATED 212 // Version du jeu de
+#define TXT_THATGUYS_GAME 213 // Partie de %s
+#define TXT_THATGUYS_GAME_BRACKET 214 // [Partie de %s]
+#define TXT_NETGAME_SETUP 215 // Configuration du jeu en
+#define TXT_REJECT 216 // Rejeter
+#define TXT_CANT_REJECT_SELF 217 // Vous ne pouvez pas vous
+#define TXT_SELECT_PLAYER_REJECT 218 // Vous devez s‚lectionner un
+#define TXT_BASES 219 // Bases
+#define TXT_CRATES 220 // Caisses
+#define TXT_AI_PLAYERS 221 // Joueur IA
+#define TXT_SCENARIOS 222 // Sc‚narios
+#define TXT_CREDITS_COLON 223 // Cr‚dits :
+#define TXT_ONLY_ONE 224 // Un seul joueur ?
+#define TXT_OOPS 225 // Oops !
+#define TXT_TO 226 // Pour %s :
+#define TXT_TO_ALL 227 // Pour tous
+#define TXT_MESSAGE 228 // Message :
+#define TXT_CONNECTION_LOST 229 // Perte de connexion avec %s
+#define TXT_LEFT_GAME 230 // %s a quitt‚ le jeu.
+#define TXT_PLAYER_DEFEATED 231 // %s a ‚t‚ vaincu !
+#define TXT_WAITING_CONNECT 232 // Attente de connexion...
+#define TXT_NULL_CONNERR_CHECK_CABLES 233 // Erreur de connexion !
+#define TXT_MODEM_CONNERR_REDIALING 234 // Erreur de connexion !
+#define TXT_MODEM_CONNERR_WAITING 235 // Erreur de connexion !
+#define TXT_SELECT_SERIAL_GAME 236 // Choix du jeu en s‚rie
+#define TXT_DIAL_MODEM 237 // Appel
+#define TXT_ANSWER_MODEM 238 // Attente appel
+#define TXT_NULL_MODEM 239 // Null Modem
+#define TXT_SETTINGS 240 // ParamŠtres
+#define TXT_PORT_COLON 241 // Port :
+#define TXT_IRQ_COLON 242 // IRQ :
+#define TXT_BAUD_COLON 243 // Bauds :
+#define TXT_INIT_STRING 244 // ChaŒne d'initialisation
+#define TXT_CWAIT_STRING 245 // ChaŒne d'attente d'appel
+#define TXT_TONE_BUTTON 246 // Tonalit‚
+#define TXT_PULSE_BUTTON 247 // Impulsions
+#define TXT_HOST_SERIAL_GAME 248 // H“te du jeu en s‚rie
+#define TXT_OPPONENT_COLON 249 // Adversaire :
+#define TXT_USER_SIGNED_OFF 250 // Utilisateur reparti !
+#define TXT_JOIN_SERIAL_GAME 251 // Rejoindre jeu en s‚rie
+#define TXT_PHONE_LIST 252 // R‚pertoire
+#define TXT_ADD 253 // Ajouter
+#define TXT_EDIT 254 // Editer
+#define TXT_DIAL 255 // Appel
+#define TXT_DEFAULT 256 // D‚faut
+#define TXT_DEFAULT_SETTINGS 257 // D‚faut
+#define TXT_CUSTOM_SETTINGS 258 // Autres paramŠtres
+#define TXT_PHONE_LISTING 259 // R‚pertoire
+#define TXT_NAME_COLON 260 // Nom :
+#define TXT_NUMBER_COLON 261 // Num‚ro :
+#define TXT_UNABLE_FIND_MODEM 262 // Modem non d‚tect‚. V‚rifiez
+#define TXT_NO_CARRIER 263 // Pas de porteuse.
+#define TXT_LINE_BUSY 264 // Ligne occup‚e.
+#define TXT_NUMBER_INVALID 265 // Num‚ro incorrect.
+#define TXT_SYSTEM_NOT_RESPONDING 266 // L'autre systŠme ne r‚pond
+#define TXT_OUT_OF_SYNC 267 // Mauvaise synchronisation !
+#define TXT_PACKET_TOO_LATE 268 // Paquet re‡u trop tard !
+#define TXT_PLAYER_LEFT_GAME 269 // L'autre joueur a quitt‚ le
+#define TXT_FROM 270 // De %s:%s
+#define TXT_SCORE_TIME 271 // TEMPS :
+#define TXT_SCORE_LEAD 272 // COMMANDEMENT :
+#define TXT_SCORE_EFFI 273 // EFFICACITE :
+#define TXT_SCORE_TOTA 274 // SCORE TOTAL :
+#define TXT_SCORE_CASU 275 // PERTES :
+#define TXT_SCORE_NEUT 276 // NEUTRE :
+#define TXT_SCORE_BUIL 277 // BATIMENTS PERDUS
+#define TXT_SCORE_BUIL1 278 // BATIMENTS
+#define TXT_SCORE_BUIL2 279 // PERDUS :
+#define TXT_SCORE_TOP 280 // MEILLEURS SCORES
+#define TXT_SCORE_ENDCRED 281 // CREDITS DE FIN :
+#define TXT_SCORE_TIMEFORMAT1 282 // %dh %dm
+#define TXT_SCORE_TIMEFORMAT2 283 // %dm
+#define TXT_DIALING 284 // Appel...
+#define TXT_DIALING_CANCELED 285 // Appel annul‚
+#define TXT_WAITING_FOR_CALL 286 // Attente d'appel...
+#define TXT_ANSWERING_CANCELED 287 // Attente d'appel annul‚e
+#define TXT_E6 288 // Ing‚nieurs
+#define TXT_E8 289 // Espion
+#define TXT_MODEM_OR_LOOPBACK 290 // Pas de cƒble Null Modem
+#define TXT_MAP 291 // Carte
+#define TXT_BLOSSOM_TREE 292 // Arbre en fleurs
+#define TXT_RESTATE_MISSION 293 // Briefing
+#define TXT_COMPUTER 294 // Joueur IA
+#define TXT_COUNT 295 // Nombre :
+#define TXT_LEVEL 296 // Niveau :
+#define TXT_OPPONENT 297 // Adversaire
+#define TXT_KILLS_COLON 298 // Vict.:
+#define TXT_VIDEO 299 // Vid‚o
+#define TXT_C10 300 // Scientifique
+#define TXT_CAPTURE_THE_FLAG 301 // Capture drapeau
+#define TXT_OBJECTIVE 302 // Objectifs de mission
+#define TXT_MISSION 303 // Mission
+#define TXT_NO_SAVES 304 // Pas de sauvegardes
+#define TXT_CIVILIAN_BUILDING 305 // Bƒtiment civil
+#define TXT_TECHNICIAN 306 // Technicien
+#define TXT_NO_SAVELOAD 307 // Sauvegarde interdite en
+#define TXT_DELPHI 308 // Agent Sp‚cial 1
+#define TXT_TO_REPLAY 309 // Voulez-vous recommencer
+#define TXT_RECONN_TO 310 // Reconnexion vers %s.
+#define TXT_PLEASE_WAIT 311 // Attendez %02d secondes.
+#define TXT_SURRENDER 312 // Voulez-vous vous rendre ?
+#define TXT_SEL_TRANS 313 // CHOIX DE LA TRANSMISSION
+#define TXT_GAMENAME_MUSTBE_UNIQUE 314 // Les sauvegardes ne peuvent
+#define TXT_GAME_IS_CLOSED 315 // Partie ferm‚e.
+#define TXT_NAME_MUSTBE_UNIQUE 316 // Les noms doivent ˆtre tous
+#define TXT_RECONNECTING_TO 317 // Reconnexion vers %s
+#define TXT_WAITING_FOR_CONNECTIONS 318 // Attente de connexions...
+#define TXT_TIME_ALLOWED 319 // Temps autoris‚ : %02d
+#define TXT_PRESS_ESC 320 // Appuyez sur Echap pour
+#define TXT_JUST_YOU_AND_ME 321 // De l'ordinateur : Il ne
+#define TXT_CAPTURE_THE_FLAG_COLON 322 // Capture du drapeau :
+#define TXT_CHAN 323 // Agent Sp‚cial 2
+#define TXT_HAS_ALLIED 324 // %s s'est alli‚(e) avec %s
+#define TXT_AT_WAR 325 // %s d‚clare la guerre … %s
+#define TXT_SEL_TARGET 326 // Choisissez un cible
+#define TXT_RESIGN 327 // Abandonner
+#define TXT_TIBERIUM_FAST 328 // Le minerai pousse trŠs
+#define TXT_ANSWERING 329 // R‚ponse en cours...
+#define TXT_INITIALIZING_MODEM 330 // Initialisation Modem...
+#define TXT_SCENARIOS_DO_NOT_MATCH 331 // Les sc‚narios ne
+#define TXT_POWER_OUTPUT 332 // Production d'‚nergie
+#define TXT_POWER_OUTPUT_LOW 333 // Production d'‚nergie
+#define TXT_CONTINUE 334 // Continuer
+#define TXT_QUEUE_FULL 335 // Saturation des donn‚es …
+#define TXT_SPECIAL_WARNING 336 // %s a modifi‚ les options de
+#define TXT_CD_DIALOG_1 337 // Placez un CD d'Alerte Rouge
+#define TXT_CD_DIALOG_2 338 // Placez le CD %d (%s) dans
+#define TXT_CD_ERROR1 339 // Alerte Rouge n'a pas
+#define TXT_NO_SOUND_CARD 340 // Pas de carte sonore
+#define TXT_UNKNOWN 341 // INCONNU
+#define TXT_OLD_GAME 342 // (ancien)
+#define TXT_NO_SPACE 343 // Espace disque insuffisant
+#define TXT_MUST_HAVE_SPACE 344 // Vous devez disposer de %d
+#define TXT_RUN_SETUP 345 // Lancez d'abord le programme
+#define TXT_WAITING_FOR_OPPONENT 346 // Attente adversaire
+#define TXT_SELECT_SETTINGS 347 // Choisissez l'option
+#define TXT_PRISON 348 // Prison
+#define TXT_GAME_WAS_SAVED 349 // Mission sauvegard‚e
+#define TXT_SPACE_CANT_SAVE 350 // Espace disque insuffisant
+#define TXT_INVALID_PORT_ADDRESS 351 // Port/Adresse invalide. COM
+#define TXT_INVALID_SETTINGS 352 // paramŠtres Port et/ou IRQ
+#define TXT_IRQ_ALREADY_IN_USE 353 // IRQ d‚j… utilis‚
+#define TXT_ABORT 354 // Oui
+#define TXT_RESTART 355 // Recommencer
+#define TXT_RESTARTING 356 // Mission relanc‚e. Attendez
+#define TXT_LOADING 357 // Chargement de la mission.
+#define TXT_ERROR_IN_INITSTRING 358 // Erreur chaŒne
+#define TXT_SHADOW_COLON 359 // Ombre
+#define TXT_AVMINE 360 // Mine Anti-V‚hicule
+#define TXT_APMINE 361 // Mine Anti-Personnel
+#define TXT_NEW_MISSIONS 362 // Nouvelles missions
+#define TXT_THIEF 363 // Voleur
+#define TXT_MRJ 364 // Brouilleur de radar
+#define TXT_GAP_GENERATOR 365 // G‚n‚rateur d'ombre
+#define TXT_PILLBOX 366 // Bunker
+#define TXT_CAMOPILLBOX 367 // Bunker camoufl‚
+#define TXT_CHRONOSPHERE 368 // ChronosphŠre
+#define TXT_ENGLAND 369 // Roy. Uni
+#define TXT_GERMANY 370 // Allemagne
+#define TXT_SPAIN 371 // Espagne
+#define TXT_USSR 372 // URSS
+#define TXT_UKRAINE 373 // Ukraine
+#define TXT_GREECE 374 // GrŠce
+#define TXT_FRANCE 375 // France
+#define TXT_TURKEY 376 // Turquie
+#define TXT_SHORE 377 // Rivage
+#define TXT_PLACE_OBJECT 378 // Choisir objet
+#define TXT_SS 379 // Sous-marin
+#define TXT_DD 380 // Contre-torpilleur
+#define TXT_CA 381 // Croiseur
+#define TXT_TRANSPORT 382 // Transport
+#define TXT_PT 383 // Aviso-torpilleur
+#define TXT_LOBBY 384 // Hall
+#define TXT_CHANNEL_GAMES 385 // Parties
+#define TXT_SAVING_GAME 386 // Sauvegarder partie...
+#define TXT_GAME_FULL 387 // La partie est au complet.
+#define TXT_MUST_SELECT_GAME 388 // Vous devez s‚lectionner une
+#define TXT_S_PLAYING_S 389 // %s joue contre %s
+#define TXT_ONLY_HOST_CAN_MODIFY 390 // Seul l'h“te peut modifier
+#define TXT_GAME_CANCELLED 391 // La partie a ‚t‚ annul‚e.
+#define TXT_S_FORMED_NEW_GAME 392 // %s a initi‚ une nouvelle
+#define TXT_GAME_NOW_IN_PROGRESS 393 // La partie de %s est
+#define TXT_TESLA 394 // Bobine de Tesla
+#define TXT_MGG 395 // G‚n‚rateur d'ombre mobile
+#define TXT_FLAME_TURRET 396 // Tour lance-flammes
+#define TXT_AAGUN 397 // Canon Anti-Avion
+#define TXT_KENNEL 398 // Niche
+#define TXT_SOVIET_TECH 399 // Centre Technique
+#define TXT_BADGER 400 // Bombardier
+#define TXT_MIG 401 // Mig
+#define TXT_YAK 402 // Yak
+#define TXT_FENCE 403 // Barbel‚s
+#define TXT_MEDIC 404 // M‚decin
+#define TXT_SABOTEUR 405 // Saboteur
+#define TXT_GENERAL 406 // G‚n‚ral
+#define TXT_E7 407 // Tanya
+#define TXT_PARA_BOMB 408 // Parabombes
+#define TXT_PARA_INFANTRY 409 // Parachutistes
+#define TXT_PARA_SABOTEUR 410 // Saboteur parachutiste
+#define TXT_SHIP_YARD 411 // Chantier naval
+#define TXT_SUB_PEN 412 // Port sous-marin
+#define TXT_SCENARIO_OPTIONS 413 // Options Sc‚nario
+#define TXT_SPY_MISSION 414 // Avion espion
+#define TXT_U2 415 // Avion espion
+#define TXT_GUARD_DOG 416 // Chien d'attaque
+#define TXT_SPY_INFO 417 // Info Espion
+#define TXT_BUILDNGS 418 // Bƒtiments
+#define TXT_UNITS 419 // Unit‚s
+#define TXT_INFANTRY 420 // Infanterie
+#define TXT_AIRCRAFT 421 // Avion
+#define TXT_TRUCK 422 // Camion d'approvisionnement
+#define TXT_INVUL 423 // Module d'invuln‚rabilit‚
+#define TXT_IRON_CURTAIN 424 // Rideau de Fer
+#define TXT_ADVANCED_TECH 425 // Centre technique avanc‚
+#define TXT_V2_LAUNCHER 426 // Lance-roquettes V2
+#define TXT_FORWARD_COM 427 // Poste de commandement
+#define TXT_DEMOLITIONER 428 // Bombardeur
+#define TXT_MINE_LAYER 429 // Poseur de mines
+#define TXT_FAKE_CONST 430 // Chantier de construction
+#define TXT_FAKE_WEAP 431 // Usine d'armement leurre
+#define TXT_FAKE_YARD 432 // Chantier naval leurre
+#define TXT_FAKE_PEN 433 // Port sous-marin leurre
+#define TXT_FAKE_RADAR 434 // D“me radar leurre
+#define TXT_THEME_BIGF 435 // Bigfoot
+#define TXT_THEME_CRUS 436 // La r‚volte
+#define TXT_THEME_FAC1 437 // A l'attaque 1
+#define TXT_THEME_FAC2 438 // A l'attaque 2
+#define TXT_THEME_HELL 439 // Marche de l'enfer
+#define TXT_THEME_RUN1 440 // Sauve-qui-peut
+#define TXT_THEME_SMSH 441 // La d‚bƒcle
+#define TXT_THEME_TREN 442 // Tranch‚es
+#define TXT_THEME_WORK 443 // Les professionnels
+#define TXT_THEME_AWAIT 444 // Attente
+#define TXT_THEME_DENSE_R 445 // Dense
+#define TXT_THEME_MAP 446 // S‚lection carte
+#define TXT_THEME_FOGGER1A 447 // Fogger
+#define TXT_THEME_MUD1A 448 // Boue
+#define TXT_THEME_RADIO2 449 // Radio 2
+#define TXT_THEME_ROLLOUT 450 // Laminage
+#define TXT_THEME_SNAKE 451 // Serpent
+#define TXT_THEME_TERMINAT 452 // Extermination
+#define TXT_THEME_TWIN 453 // Jumeau
+#define TXT_THEME_VECTOR1A 454 // Vecteur
+#define TXT_TEAM_MEMBERS 455 // Equipiers
+#define TXT_BRIDGE 456 // Pont
+#define TXT_BARREL 457 // Baril
+#define TXT_GOODGUY 458 // Amical
+#define TXT_BADGUY 459 // Ennemi
+#define TXT_GOLD 460 // Or
+#define TXT_GEMS 461 // Gemmes
+#define TXT_TEASER 462 // Film titre
+#define TXT_MOVIES 463 // Films
+#define TXT_INTERIOR 464 // Int‚rieur
+#define TXT_SONAR_PULSE 465 // Signal sonar
+#define TXT_MSLO 466 // Silo de missiles
+#define TXT_GPS_SATELLITE 467 // Satellite GPS
+#define TXT_NUCLEAR_BOMB 468 // Bombe atomique
+#define TXT_EASY 469 // Facile
+#define TXT_HARD 470 // Difficile
+#define TXT_NORMAL 471 // Normal
+#define TXT_DIFFICULTY 472 // S‚lectionnez un niveau de
+#define TXT_ALLIES 473 // Alli‚s
+#define TXT_SOVIET 474 // Soviets
+#define TXT_THEME_INTRO 475 // ThŠme Intro
+#define TXT_SHADOW_REGROWS 476 // Progr. ombre
+#define TXT_ORE_SPREADS 477 // Progr. minerai
+#define TXT_THEME_SCORE 478 // Musiques
+#define TXT_INTERNET 479 // Internet
+#define TXT_ICE 480 // Glace
+#define TXT_CRATE 481 // Caisses
+#define TXT_SKIRMISH 482 // Escarmouche
+#define TXT_CHOOSE 483 // Choisissez votre camp.
+#define TXT_MINERALS 484 // Min‚raux pr‚cieux
+#define TXT_IGNORE 485 // Ignorer
+#define TXT_ERROR_NO_RESP 486 // Erreur - le modem ne r‚pond
+#define TXT_ERROR_NO_RESCODE 487 // Erreur - Le modem n'a pas
+#define TXT_ERROR_NO_INIT 488 // Erreur - Le modem n'a pas
+#define TXT_ERROR_NO_VERB 489 // Erreur - Le modem n'a pas
+#define TXT_ERROR_NO_ECHO 490 // Erreur - Le modem n'a pas
+#define TXT_ERROR_NO_DISABLE 491 // Erreur - Impossible de
+#define TXT_ERROR_TOO_MANY 492 // Erreur - Trop d'erreurs
+#define TXT_ERROR_ERROR 493 // Erreur - Le modem a
+#define TXT_ERROR_TIMEOUT 494 // Erreur - Temps d'attente de
+#define TXT_ACCOMPLISHED 495 // Accompli
+#define TXT_CLICK_CONTINUE 496 // Cliquez pour continuer
+#define TXT_RECEIVING_SCENARIO 497 // R‚ception du sc‚nario de
+#define TXT_SENDING_SCENARIO 498 // Envoi du sc‚nario aux
+#define TXT_NO_FLOW_CONTROL_RESPONSE 499 // Erreur - Le modem n'a pas
+#define TXT_NO_COMPRESSION_RESPONSE 500 // Erreur - Le modem n'a pas
+#define TXT_NO_ERROR_CORRECTION_RESPONSE 501 // Erreur - Le modem n'a pas
+#define TXT_EXPLAIN_REGISTRATION 502 // Pour jouer … Alerte Rouge
+#define TXT_ERROR_UNABLE_TO_RUN_WCHAT 503 // Erreur - Impossible
+#define TXT_REGISTER 504 // Enregistrer
+#define TXT_ORE_MINE 505 // Gisement Minerai
+#define TXT_NO_REGISTERED_MODEM 506 // Aucun modem configur‚
+#define TXT_CHRONOSHIFT 507 // D‚placement chronoporte
+#define TXT_UNABLE_TO_OPEN_PORT 508 // Adresse invalide ou en
+#define TXT_NO_DIAL_TONE 509 // Pas de tonalit‚.
+#define TXT_NO_EXPANSION_SCENARIO 510 // Erreur - L'autre joueur n'a
+#define TXT_STAND_BY 511 // Patientez SVP...
+#define TXT_THEME_CREDITS 512 // Musique du g‚n‚rique de fin
+#define TXT_POWER_AAGUN 513 // Puissance faible: Canon(s)
+#define TXT_POWER_TESLA 514 // Puissance faible: Bobine(s)
+#define TXT_LOW_POWER 515 // Puissance Faible
+#define TXT_COMMANDER 516 // Commandant:
+#define TXT_BATTLES_WON 517 // Parties gagn‚es:
+#define TXT_MISMATCH 518 // Fichier de donn‚es du jeu
+#define TXT_SCENARIO_ERROR 519 // Votre version de jeu
+#define TXT_CONNECTING 520 // Connecting
+#define TXT_MODEM_INITIALISATION 521 // Initialisation du Modem
+#define TXT_DATA_COMPRESSION 522 // Compression des Donn‚es
+#define TXT_ERROR_CORRECTION 523 // Correction d'Erreur
+#define TXT_HARDWARE_FLOW_CONTROL 524 // Contr“le Mat‚riel du Flux
+#define TXT_ADVANCED 525 // Avanc‚
+#define TXT_THEME_2ND_HAND 526 // Seconde main
+#define TXT_THEME_ARAZOID 527 // Arazo‹de
+#define TXT_THEME_BACKSTAB 528 // Retour … l'envoyeur
+#define TXT_THEME_CHAOS2 529 // Chaos2
+#define TXT_THEME_SHUT_IT 530 // Fermez-la !
+#define TXT_THEME_TWINMIX1 531 // Visite de courtoisie
+#define TXT_THEME_UNDER3 532 // A couvert
+#define TXT_THEME_VR2 533 // VR2
+#define TXT_ASK_EMERGENCY_SAVE_NOT_RESPONDING 534 // L'autre systŠme ne r‚pond
+#define TXT_ASK_EMERGENCY_SAVE_HUNG_UP 535 // L'autre systŠme a
+#define TXT_NO_REG_APP 536 // Alerte Rouge n'a pu
+#define TXT_NO_CS_SCENARIOS 537 // Un joueur de la partie n'a
+#define TXT_MISSILESUB 538 // Sous-marin MS
+#define TXT_SHOCKTROOPER 539 // Electrocuteur
+#define TXT_MECHANIC 540 // M‚canicien
+#define TXT_CHRONOTANK 541 // Chrono Tank
+#define TXT_TESLATANK 542 // Tank Tesla
+#define TXT_MAD 543 // Tank M.A.D.
+#define TXT_DEMOTRUCK 544 // Camion de d‚molition
+#define TXT_PHASETRANSPORT 545 // Transport Cam‚l‚on
+#define TXT_THEME_BOG 546 // Mar‚cages
+#define TXT_THEME_FLOAT_V2 547 // Volutes
+#define TXT_THEME_GLOOM 548 // T‚nŠbres
+#define TXT_THEME_GRNDWIRE 549 // Terrain min‚
+#define TXT_THEME_RPT 550 // M‚caniciens 2
+#define TXT_THEME_SEARCH 551 // Battue
+#define TXT_THEME_TRACTION 552 // Traction
+#define TXT_THEME_WASTELND 553 // Chaos
+#define TXT_CARRIER 554 // H‚liport Mobile
diff --git a/CODE/CONQUER.IDE b/CODE/CONQUER.IDE
new file mode 100644
index 0000000..66bf576
Binary files /dev/null and b/CODE/CONQUER.IDE differ
diff --git a/CODE/CONQUER.LNT b/CODE/CONQUER.LNT
new file mode 100644
index 0000000..7bb2410
--- /dev/null
+++ b/CODE/CONQUER.LNT
@@ -0,0 +1,79 @@
+// Watcom C, C++ (32 bit), -si4 -sp4,
+// Standard lint options
+
+// Compiler Options for Watcom C, C++ 32 bit
+
+-cwc
+
+// This file contains options to allow PC-lint to process source
+// files for your compiler. It is used as follows:
+//
+// lint co-wc32.lnt source-file(s)
+//
+-d_M_IX86=200 // assume Intel 80286 architecture -- modify to suit
+-d__declspec()= // ignore this construct
+
+ // additional reserve words needed
++rw(_loadds,_export)
++rw(__interrupt,__near,__far,__huge,__fortran,__pascal,__cdecl)
++rw(__export,__loadds,__saveregs,__asm,__fastcall,__stdcall)
++rw(_export)
+
++fcd // makes cdecl significant -- used for proto generation
++fcu // chars are by default unsigned
++fsu // so are strings
+-d__386__ // pre-defined macro for 386 version, not set by -cwc
+-d__FLAT__ // not set by -cwc
+-si4 // sizeof(int) is 4
+-spN4 // sizeof(near pointer) is 4
+-spF6 // sizeof( far pointer) is 6
+-sld10 // sizeof(long double) is 10.
+-function(exit,_exit) // _exit() is like exit()
+-emacro(734,putc) // don't complain about items being too large.
+-emacro(506,putc) // don't complain about constant Boolean
+-emacro(???,va_arg) // the va_arg() macro can yield 415, 416, 661, 662
+ // 796 and 797 (out-of-bounds errors).
+
+ // While processing compiler (library) header files ...
+-elib(46) // an unsigned short bit field is used as __FILLER__
+-elib(522) // function return value ignored
+-elib(537) // repeated include file (ios.h)
+-elib(641) // converting enum to int
+-elib(652) // suppress message about #define of earlier declared symbols
+-elib(655) // ORing enum's
+-elib(726) // extraneous comma in enumeration
+-elib(760) // suppress message about multiple identical macro defs
+-elib(762) // suppress message about multiple identical declarations and
+-elib(806) // small bit field is signed
+-elib(1053) // prototypes cannot be distinguished
+-elib(1511) // member (rdbuf) hides nonvirtual member
+-elib(1704) // private copy constructor
+-elib(1712) // default constructor missing
+-elib(1717) // empty prototypes
+-elib(1720) // strange argument to assignment operator
+-elib(1721) // unusual operator =() declaration
+-elib(1722) // assignment operator does not return ref to class
+-elib(1724) // strange argument to copy constructor
+
+-esym(1702,operator<<,operator>>)
+
+// 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/CODE/CONST.CPP b/CODE/CONST.CPP
new file mode 100644
index 0000000..52adc51
--- /dev/null
+++ b/CODE/CONST.CPP
@@ -0,0 +1,820 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CONST.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CONST.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : September 20, 1993 *
+ * *
+ * Last Update : September 20, 1993 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+
+
+/***************************************************************************
+** These are the access passwords used to activate cheat mode, editor mode,
+** and special game options.
+*/
+long const PlayCodes[] = {
+ 0xE0792D6D, // Dwight Okahara
+ 0x90046ECF, // Paul S. Mudra
+ 0xC3EE9A26, // Frank Klepaki
+ 0xED382178, // Ed Del Castillo
+ 0L
+};
+
+long const CheatCodes[] = {
+ 0xA0E2AB53, // Joseph Hewitt
+ 0x00532693, // Mike Lightner
+ 0x7DDFF824, // Joe Bostic
+ 0x2CB5CF01, // Phil Gorrow
+ 0xB5B63531, // Bill Randolph
+ 0xDFABC23A, // Adam Isgreen
+ 0x52B19A22, // Erik Yeo
+ 0xBE79088C, // David Dettmer
+ 0xB216AE7E, // Barry Green
+ 0x0E07B213, // Steve Tall
+ 0L
+};
+
+
+long const EditorCodes[] = {
+ 0xA2C09326, // Erik Yeo
+ 0x1F944BB3, // Mike Lightner
+ 0xDE07154D, // Adam Isgreen
+ 0x0E07B213, // Steve Tall
+ 0x16B170B1, // Joe Bostic
+ 0L
+};
+
+
+/***********************************************************************************************
+** Unit order names. These names correspond to the player selectable orders
+** a unit can have. The system initiated orders have no use for the ASCII name
+** associated, but they are listed here for completeness sake.
+*/
+char const * Missions[MISSION_COUNT] = {
+ "Sleep",
+ "Attack",
+ "Move",
+ "QMove",
+ "Retreat",
+ "Guard",
+ "Sticky",
+ "Enter",
+ "Capture",
+ "Harvest",
+ "Area Guard",
+ "Return",
+ "Stop",
+ "Ambush",
+ "Hunt",
+ "Unload",
+ "Sabotage",
+ "Construction",
+ "Selling",
+ "Repair",
+ "Rescue",
+ "Missile",
+ "Harmless"
+};
+
+
+/***************************************************************************
+** Special weapon names.
+*/
+#ifdef SCENARIO_EDITOR
+char const * const SpecialWeaponName[SPC_COUNT] = {
+ "Sonar Pulse",
+ "Nuclear Missile",
+ "Chronosphere",
+ "Parachute Bomb",
+ "Paratroopers",
+ "Recon Plane",
+ "Iron Curtain",
+ "GPS Satellite"
+};
+#endif
+int const SpecialWeaponHelp[SPC_COUNT] = {
+ TXT_SONAR_PULSE,
+ TXT_NUCLEAR_BOMB,
+ TXT_CHRONOSHIFT,
+ TXT_PARA_BOMB,
+ TXT_PARA_INFANTRY,
+ TXT_SPY_MISSION,
+ TXT_INVUL,
+ TXT_GPS_SATELLITE
+};
+char const * const SpecialWeaponFile[SPC_COUNT] = {
+ "SONR",
+ "ATOM",
+ "WARP",
+ "PBMB",
+ "PINF",
+ "CAM",
+ "INFX",
+ "GPSS"
+};
+
+
+/***************************************************************************
+** Type of quarry to search out and attack. These values are used for team
+** attack missions.
+*/
+char const * const QuarryName[QUARRY_COUNT] = {
+ "N/A",
+ "Anything",
+ "Buildings - any",
+ "Harvesters",
+ "Infantry",
+ "Vehicles - any",
+ "Ships - any",
+ "Factories",
+ "Base Defenses",
+ "Base Threats",
+ "Power Facilities",
+ "Fake Buildings"
+};
+
+
+/***************************************************************************
+** These are the text names for the formation types.
+*/
+char const * const FormationName[FORMATION_COUNT] = {
+ "None",
+
+ "Tight",
+ "Loose",
+ "Wedge North",
+ "Wedge East",
+ "Wedge South",
+ "Wedge West",
+ "Line N/S",
+ "Line E/W"
+};
+
+
+/***************************************************************************
+** These are the ASCII names for the reinforcement sources.
+*/
+char const * const SourceName[SOURCE_COUNT] =
+{
+ "North",
+ "East",
+ "South",
+ "West",
+ "Air"
+};
+
+
+/***************************************************************************
+** These are the text names for the various armor types a unit may possess.
+*/
+char const * const ArmorName[ARMOR_COUNT] = {
+ "none",
+ "wood",
+ "light",
+ "heavy",
+ "concrete"
+};
+
+
+// HACK ALERT! This unused text string is here to stop Watcom from crashing. There is some
+// magic text heap length that causes a crash before the code executes. This dummy string
+// changes the text heap length enough to stop the crash. Who knows why, but it works.
+char * __test__ = "alskdfjlasdfjkajsdfkja;sldjfklasj9awutreqjfnfdkvnldzlknvadsjgflkasdjfkajsdfas";
+
+
+/***************************************************************************
+** The list of VQ filenames.
+*/
+char const * const VQName[VQ_COUNT] = {
+ "AAGUN",
+ "MIG",
+ "SFROZEN",
+ "AIRFIELD",
+ "BATTLE",
+ "BMAP",
+ "BOMBRUN",
+ "DPTHCHRG",
+ "GRVESTNE",
+ "MONTPASS",
+ "MTNKFACT",
+ "CRONTEST",
+ "OILDRUM",
+ "ALLYEND",
+ "RADRRAID",
+ "SHIPYARD",
+ "SHORBOMB",
+ "SITDUCK",
+ "SLNTSRVC",
+ "SNOWBASE",
+ "EXECUTE",
+ "REDINTRO", // low res.
+ "NUKESTOK",
+ "V2ROCKET",
+ "SEARCH",
+ "BINOC",
+ "ELEVATOR",
+ "FROZEN",
+ "MCV",
+ "SHIPSINK",
+ "SOVMCV",
+ "TRINITY",
+ "ALLYMORF",
+ "APCESCPE",
+ "BRDGTILT",
+ "CRONFAIL",
+ "STRAFE",
+ "DESTROYR",
+ "DOUBLE",
+ "FLARE",
+ "SNSTRAFE",
+ "LANDING",
+ "ONTHPRWL",
+ "OVERRUN",
+ "SNOWBOMB",
+ "SOVCEMET",
+ "TAKE_OFF",
+ "TESLA",
+ "SOVIET8",
+ "SPOTTER",
+ "ALLY1",
+ "ALLY2",
+ "ALLY4",
+ "SOVFINAL",
+ "ASSESS",
+ "SOVIET10",
+ "DUD",
+ "MCV_LAND",
+ "MCVBRDGE",
+ "PERISCOP",
+ "SHORBOM1",
+ "SHORBOM2",
+ "SOVBATL",
+ "SOVTSTAR",
+ "AFTRMATH",
+ "SOVIET11",
+ "MASASSLT",
+ "ENGLISH", // High res.
+ "SOVIET1",
+ "SOVIET2",
+ "SOVIET3",
+ "SOVIET4",
+ "SOVIET5",
+ "SOVIET6",
+ "SOVIET7",
+ "PROLOG",
+ "AVERTED",
+ "COUNTDWN",
+ "MOVINGIN",
+ "ALLY10",
+ "ALLY12",
+ "ALLY5",
+ "ALLY6",
+ "ALLY8",
+ "TANYA1",
+ "TANYA2",
+ "ALLY10B",
+ "ALLY11",
+ "ALLY14",
+ "ALLY9",
+ "SPY",
+ "TOOFAR",
+ "SOVIET12",
+ "SOVIET13",
+ "SOVIET9",
+ "BEACHEAD",
+ "SOVIET14",
+ "SIZZLE",
+ "SIZZLE2",
+ "ANTEND",
+ "ANTINTRO"
+};
+
+
+/***************************************************************************
+** Relative coordinate offsets from the center of a cell for each
+** of the legal positions that an object in a cell may stop at. Only infantry
+** are allowed to stop at other than the center of the cell.
+*/
+COORDINATE const StoppingCoordAbs[5] = {
+ 0x00800080L, // center
+ 0x00400040L, // upper left
+ 0x004000C0L, // upper right
+ 0x00C00040L, // lower left
+ 0x00C000C0L // lower right
+};
+
+
+/***************************************************************************
+** Converts pixel values (cell relative) into the appropriate lepton (sub cell)
+** value. This is used to convert pixel (screen) coordinates into the underlying
+** coordinate system.
+*/
+unsigned char const Pixel2Lepton[24] = {
+ 0x00,0x0B,0x15,0x20,0x2B,0x35,0x40,0x4B,
+ 0x55,0x60,0x6B,0x75,0x80,0x8B,0x95,0xA0,
+ 0xAB,0xB5,0xC0,0xCB,0xD5,0xE0,0xEB,0xF5
+};
+
+
+/***************************************************************************
+** This array is used to index a facing in order to retrieve a cell
+** offset that, when added to another cell, will achieve the adjacent cell
+** in the indexed direction.
+*/
+CELL const AdjacentCell[FACING_COUNT] = {
+ -(MAP_CELL_W), // North
+ -(MAP_CELL_W-1), // North East
+ 1, // East
+ MAP_CELL_W+1, // South East
+ MAP_CELL_W, // South
+ MAP_CELL_W-1, // South West
+ -1, // West
+ -(MAP_CELL_W+1) // North West
+};
+
+COORDINATE const AdjacentCoord[FACING_COUNT] = {
+ 0xFF000000L,
+ 0xFF000100L,
+ 0x00000100L,
+ 0x01000100L,
+ 0x01000000L,
+ 0x0100FF00L,
+ 0x0000FF00L,
+ 0xFF00FF00L
+};
+
+
+/***************************************************************************
+** This specifies the odds of receiving the various random crate power
+** ups. The odds are expressed as "shares" of 100 percent.
+*/
+int CrateShares[CRATE_COUNT] = {
+ 50, // CRATE_MONEY
+ 20, // CRATE_UNIT
+ 3, // CRATE_PARA_BOMB
+ 1, // CRATE_HEAL_BASE
+ 3, // CRATE_CLOAK
+ 5, // CRATE_EXPLOSION
+ 5, // CRATE_NAPALM
+ 20, // CRATE_SQUAD
+ 1, // CRATE_DARKNESS
+ 1, // CRATE_REVEAL
+ 3, // CRATE_SONAR
+ 10, // CRATE_ARMOR
+ 10, // CRATE_SPEED
+ 10, // CRATE_FIREPOWER
+ 1, // CRATE_ICBM
+ 1, // CRATE_TIMEQUAKE
+ 3, // CRATE_INVULN
+ 5 // CRATE_VORTEX
+};
+
+AnimType CrateAnims[CRATE_COUNT] = {
+ ANIM_NONE, // CRATE_MONEY
+ ANIM_NONE, // CRATE_UNIT
+ ANIM_NONE, // CRATE_PARA_BOMB
+ ANIM_NONE, // CRATE_HEAL_BASE
+ ANIM_NONE, // CRATE_CLOAK
+ ANIM_NONE, // CRATE_EXPLOSION
+ ANIM_NONE, // CRATE_NAPALM
+ ANIM_NONE, // CRATE_SQUAD
+ ANIM_NONE, // CRATE_DARKNESS
+ ANIM_NONE, // CRATE_REVEAL
+ ANIM_NONE, // CRATE_SONAR
+ ANIM_NONE, // CRATE_ARMOR
+ ANIM_NONE, // CRATE_SPEED
+ ANIM_NONE, // CRATE_FIREPOWER
+ ANIM_NONE, // CRATE_ICBM
+ ANIM_NONE, // CRATE_TIMEQUAKE
+ ANIM_NONE, // CRATE_INVULN
+ ANIM_NONE // CRATE_VORTEX
+};
+
+int CrateData[CRATE_COUNT] = {
+ 0, // CRATE_MONEY
+ 0, // CRATE_UNIT
+ 0, // CRATE_PARA_BOMB
+ 0, // CRATE_HEAL_BASE
+ 0, // CRATE_CLOAK
+ 0, // CRATE_EXPLOSION
+ 0, // CRATE_NAPALM
+ 0, // CRATE_SQUAD
+ 0, // CRATE_DARKNESS
+ 0, // CRATE_REVEAL
+ 0, // CRATE_SONAR
+ 0, // CRATE_ARMOR
+ 0, // CRATE_SPEED
+ 0, // CRATE_FIREPOWER
+ 0, // CRATE_ICBM
+ 0, // CRATE_TIMEQUAKE
+ 0, // CRATE_INVULN
+ 0 // CRATE_VORTEX
+};
+
+char const * const CrateNames[CRATE_COUNT] = {
+ "Money",
+ "Unit",
+ "ParaBomb",
+ "HealBase",
+ "Cloak",
+ "Explosion",
+ "Napalm",
+ "Squad",
+ "Darkness",
+ "Reveal",
+ "Sonar",
+ "Armor",
+ "Speed",
+ "Firepower",
+ "ICBM",
+ "TimeQuake",
+ "Invulnerability",
+ "ChronalVortex"
+};
+
+
+/***************************************************************************
+** This converts 0..255 facing values into either 8, 16, or 32 facing values.
+** Note: a simple shift won't suffice because 0..255 facing values should
+** be converted to the CLOSEST appropriate facing, NOT rounded down to the
+** nearest facing.
+*/
+unsigned char const Facing8[256] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+
+
+unsigned char const Facing16[256] = {
+ 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,
+ 2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,
+ 4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,
+ 10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,
+ 12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,
+ 14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,0,0,0,0,0,0,0,0
+};
+
+
+signed char const Rotation16[256] = {
+ 0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,
+ 0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,
+ 0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,
+ 0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,
+ 0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,
+ 0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,
+ 0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,
+ 0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1
+};
+
+
+/*
+** This table incorporates a compensating factor for the distortion caused
+** by 3D-Studio when it tries to render 45% angles.
+*/
+unsigned char const Facing32[256] = {
+ 0,0,0,0,0,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,
+ 3,4,4,4,4,4,4,5,5,5,5,5,5,5,6,6,6,6,6,6,6,7,7,7,7,7,7,7,8,8,8,8,
+ 8,8,8,9,9,9,9,9,9,9,10,10,10,10,10,10,10,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,
+ 13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16,
+ 16,16,16,16,16,17,17,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,
+ 19,20,20,20,20,20,20,21,21,21,21,21,21,21,22,22,22,22,22,22,22,23,23,23,23,23,23,23,24,24,24,24,
+ 24,24,24,25,25,25,25,25,25,25,26,26,26,26,26,26,26,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,
+ 29,29,29,29,29,29,29,29,30,30,30,30,30,30,30,30,30,31,31,31,31,31,31,31,31,31,0,0,0,0,0,0
+};
+
+
+#ifdef OBSOLETE
+unsigned char const Facing32[256] = {
+ 0,0,0,0,
+ 1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2,
+ 3,3,3,3,3,3,3,3,
+ 4,4,4,4,4,4,4,4,
+ 5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,
+ 8,8,8,8,8,8,8,8,
+ 9,9,9,9,9,9,9,9,
+ 10,10,10,10,10,10,10,10,
+ 11,11,11,11,11,11,11,11,
+ 12,12,12,12,12,12,12,12,
+ 13,13,13,13,13,13,13,13,
+ 14,14,14,14,14,14,14,14,
+ 15,15,15,15,15,15,15,15,
+ 16,16,16,16,16,16,16,16,
+ 17,17,17,17,17,17,17,17,
+ 18,18,18,18,18,18,18,18,
+ 19,19,19,19,19,19,19,19,
+ 20,20,20,20,20,20,20,20,
+ 21,21,21,21,21,21,21,21,
+ 22,22,22,22,22,22,22,22,
+ 23,23,23,23,23,23,23,23,
+ 24,24,24,24,24,24,24,24,
+ 25,25,25,25,25,25,25,25,
+ 26,26,26,26,26,26,26,26,
+ 27,27,27,27,27,27,27,27,
+ 28,28,28,28,28,28,28,28,
+ 29,29,29,29,29,29,29,29,
+ 30,30,30,30,30,30,30,30,
+ 31,31,31,31,31,31,31,31,
+ 0,0,0,0
+};
+#endif
+
+
+/***************************************************************************
+** These are the movement costs (in ticks at fastest speed) to enter each
+** of the given terrain cells.
+*/
+
+int const GroundColor[LAND_COUNT] = {
+ 141, // "Clear" terrain.
+ 141, // Road terrain.
+ 172, // Water.
+ 21, // Impassable rock.
+ 21, // Wall (blocks movement).
+ 158, // Tiberium field.
+ 141, // Beach terrain.
+ 141, // Rocky terrain.
+ 174 // Rocky riverbed.
+};
+
+int const SnowColor[LAND_COUNT] = {
+ 141, // "Clear" terrain.
+ 141, // Road terrain.
+ 172, // Water.
+ 21, // Impassable rock.
+ 21, // Wall (blocks movement).
+ 158, // Tiberium field.
+ 141, // Beach terrain.
+ 141, // Rocky terrain.
+ 174 // Rocky riverbed.
+};
+
+#ifdef NEVER
+int const GroundColor[LAND_COUNT] = {
+ 46, // "Clear" terrain.
+ 44, // Road terrain.
+ BLUE, // Water.
+ DKGREY, // Impassable rock.
+ DKGREY, // Wall (blocks movement).
+ 158, // Tiberium field.
+ 64, // Beach terrain.
+ DKGREY, // Rocky terrain.
+ DKGREY // Rocky riverbed.
+};
+
+int const SnowColor[LAND_COUNT] = {
+ WHITE, // "Clear" terrain.
+ LTGRAY, // Road terrain.
+ BLUE, // Water.
+ DKGREY, // Impassable rock.
+ DKGREY, // Wall (blocks movement).
+ 158, // Tiberium field.
+ LTGRAY, // Beach terrain.
+ DKGREY, // Rocky terrain.
+ DKGREY // Rocky riverbed.
+};
+#endif
+
+GroundType Ground[LAND_COUNT];
+
+
+/***************************************************************************
+** These are the names of the theaters.
+*/
+TheaterDataType const Theaters[THEATER_COUNT] = {
+ {"TEMPERATE","TEMPERAT","TEM"},
+ {"SNOW","SNOW","SNO"},
+ {"INTERIOR","INTERIOR","INT"},
+};
+
+
+unsigned char const RemapCiv2[256] = {
+ 0,1,2,3,4,5,6,209,8,9,10,11,12,13,12,15, // 0..15
+ 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31
+ 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47
+ 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63
+ 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79
+ 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95
+ 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111
+ 112,113,114,115,116,117,187,188,120,121,122,123,124,125,126,127, // 112..127
+ 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143
+ 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,209, // 144..159
+ 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175
+ 176,177,178,179,180,181,182,183,184,185,186,167, 13,189,190,191, // 176..191
+ 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207
+ 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223
+ 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239
+ 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255
+};
+
+unsigned char const RemapCiv4[256] = {
+ 0,1,2,3,4,5,6,187,8,9,10,11,12,13,14,15, // 0..15
+ 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31
+ 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47
+ 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63
+ 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79
+ 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95
+ 96,97,98,99,100,101,102,103,104,105,106,107,108,118,110,119, // 96..111
+ 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, // 112..127
+ 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143
+ 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 144..159
+ 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175
+ 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // 176..191
+ 192,193,194,195,196,197,198,199,200,201,202,203,204,205,188,207, // 192..207
+ 208,209,182,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223
+ 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239
+ 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255
+};
+
+unsigned char const RemapCiv5[256] = {
+ 0,1,2,3,4,5,6,109,8,9,10,11,131,13,14,15, // 0..15
+ 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31
+ 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47
+ 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63
+ 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79
+ 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95
+ 96,97,98,99,100,101,102,103,104,105,106,107,108,177,110,178, // 96..111
+ 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, // 112..127
+ 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143
+ 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 144..159
+ 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175
+ 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // 176..191
+ 192,193,194,195,196,197,198,199,111,201,202,203,204,205,111,207, // 192..207
+ 208,209,182,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223
+ 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239
+ 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255
+};
+
+unsigned char const RemapCiv6[256] = {
+ 0,1,2,3,4,5,6,120,8,9,10,11,12,13,238,15, // 0..15
+ 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31
+ 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47
+ 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63
+ 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79
+ 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95
+ 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111
+ 112,113,114,115,116,117,236,206,120,121,122,123,124,125,126,127, // 112..127
+ 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143
+ 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,111, // 144..159
+ 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175
+ 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // 176..191
+ 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207
+ 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223
+ 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239
+ 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255
+};
+
+unsigned char const RemapCiv7[256] = {
+ 0,1,2,3,4,5,6,7,8,9,10,11,12,13,131,15, // 0..15
+ 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31
+ 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47
+ 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63
+ 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79
+ 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95
+ 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111
+ 112,113,114,115,116,117,157,212,120,121,122,123,124,125,126,127, // 112..127
+ 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143
+ 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,7, // 144..159
+ 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175
+ 176,177,178,179,180,181,182,183,184,185,186,118,119,189,190,191, // 176..191
+ 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207
+ 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223
+ 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239
+ 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255
+};
+
+unsigned char const RemapCiv8[256] = {
+ 0,1,2,3,4,5,6,182,8,9,10,11,12,13,131,15, // 0..15
+ 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31
+ 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47
+ 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63
+ 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79
+ 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95
+ 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111
+ 112,113,114,115,116,117,215,7,120,121,122,123,124,125,126,127, // 112..127
+ 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143
+ 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,182, // 144..159
+ 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175
+ 176,177,178,179,180,181,182,183,184,185,186,198,199,189,190,191, // 176..191
+ 192,193,194,195,196,197,198,199,111,201,202,203,204,205,206,207, // 192..207
+ 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223
+ 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239
+ 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255
+};
+
+unsigned char const RemapCiv9[256] = {
+ 0,1,2,3,4,5,6,7,8,9,10,11,12,13,7,15, // 0..15
+ 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31
+ 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47
+ 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63
+ 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79
+ 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95
+ 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111
+ 112,113,114,115,116,117,163,165,120,121,122,123,124,125,126,127, // 112..127
+ 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143
+ 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,200, // 144..159
+ 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175
+ 176,177,178,179,180,181,182,183,184,185,186,111,13,189,190,191, // 176..191
+ 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207
+ 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223
+ 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239
+ 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255
+};
+
+unsigned char const RemapCiv10[256] = {
+ 0,1,2,3,4,5,6,137,8,9,10,11,12,13,15,15, // 0..15
+ 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31
+ 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47
+ 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63
+ 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79
+ 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95
+ 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111
+ 112,113,114,115,116,117,129,131,120,121,122,123,124,125,126,127, // 112..127
+ 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143
+ 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,137, // 144..159
+ 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175
+ 176,177,178,179,180,181,182,183,184,185,186,163,165,189,190,191, // 176..191
+ 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207
+ 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223
+ 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239
+ 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255
+};
+
+unsigned char const RemapEmber[256] = {
+#define CEC CC_EMBER_COLOR
+ 0,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,BLACK,CEC,CEC,CEC,
+ CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,
+ CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,
+ CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,
+ CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,
+ CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,
+ CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,
+ CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,
+ CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,
+ CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,
+ CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,
+ CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,
+ CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,
+ CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,
+ CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,
+ CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC
+};
+
+
+//char const Keys[] =
+// "[PublicKey]\n"
+// "1=AgkCbXo9sKMHOBk=\n"
+//#ifdef CHEAT_KEYS
+// "[PrivateKey]\n"
+// "1=AggxFU55vc7LYQ==\n"
+//#endif
+// "\n";
+
+char const Keys[] =
+"[PublicKey]\n"
+"1=AihRvNoIbTn85FZRYNZRcT+i6KpU+maCsEqr3Q5q+LDB5tH7Tz2qQ38V\n"
+#ifdef CHEAT_KEYS
+"[PrivateKey]\n"
+"1=AigKVje8mROcR8QixnxUEF5b29Curkq01DNDWCdOG99XBqH79OaCiTCB\n"
+#endif
+"\n";
diff --git a/CODE/CONTROL.CPP b/CODE/CONTROL.CPP
new file mode 100644
index 0000000..6fa9224
--- /dev/null
+++ b/CODE/CONTROL.CPP
@@ -0,0 +1,226 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CONTROL.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CONTROL.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 01/15/95 *
+ * *
+ * Last Update : December 5, 1995 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * ControlClass::Action -- Normal action for control gadget objects. *
+ * ControlClass::ControlClass -- Constructor for control class objects. *
+ * ControlClass::ControlClass -- Copy constructor for control gadget. *
+ * ControlClass::Draw_Me -- Draw logic for the control class object. *
+ * ControlClass::Get_ID -- Gets the ID number for this gadget. *
+ * ControlClass::Make_Peer -- Assigns a peer gadget to this gadget. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+
+
+/***********************************************************************************************
+ * ControlClass::ControlClass -- Constructor for control class objects. *
+ * *
+ * This is the normal constructor for control class objects. At this level, it only needs *
+ * to record the ID number assigned to this button. *
+ * *
+ * INPUT: id -- The ID number for this gadget. If the ID number specified is 0, then *
+ * this tells the system that no special ID code should be returned. *
+ * *
+ * x,y -- Pixel coordinate of upper left corner of gadget's region. *
+ * *
+ * w,h -- Pixel dimensions of the gadget's region. *
+ * *
+ * flags -- The input event flags that this gadget recognizes. *
+ * *
+ * sticky-- This this a "sticky" gadget? A sticky gadget is one that takes over the *
+ * gadget list while the mouse button is held down, if the mouse button was *
+ * initially clicked over its region. This is the behavior of "normal" *
+ * buttons in Windows. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 01/15/1995 JLB : Created. *
+ *=============================================================================================*/
+ControlClass::ControlClass(unsigned id, int x, int y, int w, int h, unsigned flags, int sticky) :
+ GadgetClass(x, y, w, h, flags, sticky),
+ ID(id),
+ Peer(0)
+{
+}
+
+
+/***********************************************************************************************
+ * ControlClass::ControlClass -- Copy constructor for control gadget. *
+ * *
+ * This copy constructor for a control gadget is used create a duplicate gadget that *
+ * is functionally similar. *
+ * *
+ * INPUT: control -- Reference to the gadget that is to be copied. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 12/05/1995 JLB : Created. *
+ *=============================================================================================*/
+ControlClass::ControlClass(ControlClass const & control) :
+ GadgetClass(control),
+ ID(control.ID),
+ Peer(control.Peer)
+{
+}
+
+/***********************************************************************************************
+ * ControlClass::Action -- Normal action for control gadget objects. *
+ * *
+ * This function gets called when the input event that this control gadget is looking for *
+ * occurs. In such a case, the return key code value is changed to the gadget's ID number *
+ * with the special button bit flag attached. *
+ * *
+ * INPUT: flags -- The event that triggered this function call. If this value is NULL, then *
+ * this is a forced (probably due to the sticky flag) call and the key code *
+ * is not altered. *
+ * *
+ * key -- Reference to the key code that will be returned by the controlling *
+ * Input() function. *
+ * *
+ * OUTPUT: bool; Should further list processing be aborted? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 01/15/1995 JLB : Created. *
+ *=============================================================================================*/
+int ControlClass::Action(unsigned flags, KeyNumType & key)
+{
+
+ /*
+ ** Only if the flags indicate that a recognized action has occurred, do the
+ ** normal processing of this gadget and set return value to the gadget ID.
+ */
+ if (flags) {
+ if (ID) {
+ key = (KeyNumType)(ID | KN_BUTTON);
+ } else {
+ key = KN_NONE;
+ }
+ }
+
+ /*
+ ** If there is a peer link established, inform that gadget of this
+ ** action call.
+ */
+ if (Peer) {
+ Peer->Peer_To_Peer(flags, key, *this);
+ }
+
+ return(GadgetClass::Action(flags, key));
+}
+
+
+/***********************************************************************************************
+ * ControlClass::Make_Peer -- Assigns a peer gadget to this gadget. *
+ * *
+ * This function will assign another gadget to this one. That other gadget will receive *
+ * notification of any Action() call to this gadget. Presumably, this is how one gadget *
+ * can automatically adapt to changes in another. Say for example, a slider bar can affect *
+ * the list box it is attached to. *
+ * *
+ * INPUT: gadget -- The gadget to inform when any Action() function is called. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 01/16/1995 JLB : Created. *
+ *=============================================================================================*/
+void ControlClass::Make_Peer(GadgetClass & gadget)
+{
+ Peer = &gadget;
+}
+
+
+/***********************************************************************************************
+ * ControlClass::Get_ID -- Gets the ID number for this gadget. *
+ * *
+ * This function will query and return with the ID number for this gadget. It is primarily *
+ * used by the Extract_Gadget() function. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the ID number for this gadget. If zero is returned, this means that *
+ * no ID was assigned to this gadget. This is a special case since a zero value will *
+ * never be returned as a pseudo-key as is done with non-zero values. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 01/16/1995 JLB : Created. *
+ *=============================================================================================*/
+unsigned ControlClass::Get_ID(void) const
+{
+ return(ID);
+}
+
+
+/***********************************************************************************************
+ * ControlClass::Draw_Me -- Draw logic for the control class object. *
+ * *
+ * This is called when the control object might need to be redrawn or when redrawing is *
+ * necessary. Since at this level of the class hierarchy, no actual drawing occurs, this *
+ * routine doesn't perform any rendering. It does, however, inform any peer attached *
+ * object that a Draw_Me function has been called. Presumably, the attached peer gadget *
+ * might very well need to be redrawn as a result of some action by this gadget. Since this *
+ * gadget might, more than likely, be of the "sticky" variety, a normal call to Draw_Me *
+ * for the other gadget will not occur. It must rely on the call by this routine in order *
+ * to update correctly. A typical example of this would be a slider that is attached to *
+ * a list box. As the slider is being drug around, the attached list box must be redrawn. *
+ * *
+ * INPUT: forced -- Should the redraw be forced regardless of the redraw flag? *
+ * *
+ * OUTPUT: bool; Was the gadget redrawn? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 01/16/1995 JLB : Created. *
+ *=============================================================================================*/
+int ControlClass::Draw_Me(int forced)
+{
+ if (Peer) {
+ Peer->Draw_Me();
+ }
+ return(GadgetClass::Draw_Me(forced));
+}
diff --git a/CODE/CONTROL.H b/CODE/CONTROL.H
new file mode 100644
index 0000000..078db39
--- /dev/null
+++ b/CODE/CONTROL.H
@@ -0,0 +1,90 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CONTROL.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CONTROL.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 01/15/95 *
+ * *
+ * Last Update : January 15, 1995 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifndef CONTROL_H
+#define CONTROL_H
+
+#include "gadget.h"
+
+/***************************************************************************
+ * ControlClass -- Region tracking class *
+ * *
+ * INPUT: int x -- x position of gadget *
+ * int y -- y position of gadget *
+ * int w -- width of gadget *
+ * int h -- height of gadget *
+ * UWORD flags -- see enumeration choices *
+ * *
+ * OUTPUT: 0 = new scenario created, -1 = not *
+ * WARNINGS: This class is Abstract (cannot make an instance of it) *
+ * *
+ * HISTORY: *
+ * 01/03/1995 MML : Created. *
+ *=========================================================================*/
+class ControlClass : public GadgetClass
+{
+ public:
+ ControlClass(NoInitClass const & x) : GadgetClass(x) {};
+ ControlClass(unsigned id, int x, int y, int w, int h, unsigned flags=LEFTPRESS|RIGHTPRESS, int sticky=false);
+ ControlClass(ControlClass const & control);
+
+ virtual void Make_Peer(GadgetClass & gadget);
+
+ /*
+ ** Render support function.
+ */
+ virtual int Draw_Me(int forced=false);
+
+ /*
+ ** This is the ID number for this control gadget. This number is used to generate
+ ** a special pseudo-key when the gadget detects valid input.
+ */
+ unsigned ID;
+
+ protected:
+ virtual unsigned Get_ID(void) const;
+ virtual int Action(unsigned flags, KeyNumType & key);
+
+ /*
+ ** This points to the peer button to inform when something happens to this
+ ** gadget.
+ */
+ GadgetClass * Peer;
+};
+
+#endif
+
diff --git a/CODE/COORD.CPP b/CODE/COORD.CPP
new file mode 100644
index 0000000..b33da6b
--- /dev/null
+++ b/CODE/COORD.CPP
@@ -0,0 +1,652 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/COORD.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : COORD.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : September 10, 1993 *
+ * *
+ * Last Update : July 22, 1996 [JLB] *
+ * *
+ * Support code to handle the coordinate system is located in this module. *
+ * Routines here will be called QUITE frequently during play and must be *
+ * as efficient as possible. *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * Cardinal_To_Fixed -- Converts cardinal numbers into a fixed point number. *
+ * Coord_Cell -- Convert a coordinate into a cell number. *
+ * Coord_Move -- Moves a coordinate an arbitrary direction for an arbitrary distance *
+ * Coord_Scatter -- Determines a random coordinate from an anchor point. *
+ * Coord_Spillage_List -- Calculate a spillage list for the dirty rectangle specified. *
+ * Coord_Spillage_List -- Determines the offset list for cell spillage/occupation. *
+ * Distance -- Determines the cell distance between two cells. *
+ * Distance -- Determines the lepton distance between two coordinates. *
+ * Distance -- Fetch distance between two target values. *
+ * Fixed_To_Cardinal -- Converts a fixed point number into a cardinal number. *
+ * Normal_Move_Point -- Moves point with tilt compensation. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+
+
+/***********************************************************************************************
+ * Coord_Cell -- Convert a coordinate into a cell number. *
+ * *
+ * This routine will convert the specified coordinate value into a cell number. This is *
+ * useful to determine the map index number into the cell array that corresponds to a *
+ * particular coordinate. *
+ * *
+ * INPUT: coord -- The coordinate to convert into a cell number. *
+ * *
+ * OUTPUT: Returns with the cell number that corresponds to the coordinate specified. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/17/1996 JLB : Created. *
+ *=============================================================================================*/
+CELL Coord_Cell(COORDINATE coord)
+{
+ CELL_COMPOSITE cell;
+ cell.Cell = 0;
+ cell.Sub.X = ((COORD_COMPOSITE &)coord).Sub.X.Sub.Cell;
+ cell.Sub.Y = ((COORD_COMPOSITE &)coord).Sub.Y.Sub.Cell;
+ return(cell.Cell);
+// return(XY_Cell(((COORD_COMPOSITE)coord).Sub.X, ((COORD_COMPOSITE)composite).Sub.Y));
+}
+
+
+/***********************************************************************************************
+ * Distance -- Fetch distance between two target values. *
+ * *
+ * This routine will determine the lepton distance between the two specified target *
+ * values. *
+ * *
+ * INPUT: target1 -- First target value. *
+ * *
+ * target2 -- Second target value. *
+ * *
+ * OUTPUT: Returns with the lepton distance between the two target values. *
+ * *
+ * WARNINGS: Be sure that the targets are legal before calling this routine. Otherwise, the *
+ * return value is meaningless. *
+ * *
+ * HISTORY: *
+ * 06/17/1996 JLB : Created. *
+ *=============================================================================================*/
+int Distance(TARGET target1, TARGET target2)
+{
+ return(Distance(As_Coord(target1), As_Coord(target2)));
+}
+
+
+/***********************************************************************************************
+ * Distance -- Determines the lepton distance between two coordinates. *
+ * *
+ * This routine is used to determine the distance between two coordinates. It uses the *
+ * Dragon Strike method of distance determination and thus it is very fast. *
+ * *
+ * INPUT: coord1 -- First coordinate. *
+ * *
+ * coord2 -- Second coordinate. *
+ * *
+ * OUTPUT: Returns the lepton distance between the two coordinates. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/27/1994 JLB : Created. *
+ *=============================================================================================*/
+int Distance(COORDINATE coord1, COORDINATE coord2)
+{
+ int diff1, diff2;
+
+ diff1 = Coord_Y(coord1) - Coord_Y(coord2);
+ if (diff1 < 0) diff1 = -diff1;
+ diff2 = Coord_X(coord1) - Coord_X(coord2);
+ if (diff2 < 0) diff2 = -diff2;
+ if (diff1 > diff2) {
+ return(diff1 + ((unsigned)diff2 / 2));
+ }
+ return(diff2 + ((unsigned)diff1 / 2));
+}
+
+
+/***********************************************************************************************
+ * Coord_Spillage_List -- Determines the offset list for cell spillage/occupation. *
+ * *
+ * This routine will take an arbitrary position and object size and return with a list of *
+ * cell offsets from the current cell for all cells that are overlapped by the object. The *
+ * first cell offset is always zero, so to just get the adjacent spill cell list, add one *
+ * to the return pointer. *
+ * *
+ * INPUT: coord -- The coordinate to examine. *
+ * *
+ * maxsize -- The maximum width/height of the object (pixels). *
+ * *
+ * OUTPUT: Returns with a pointer to a spillage list. *
+ * *
+ * WARNINGS: The algorithm is limited to working with a maxsize of 48 or less. Larger values *
+ * will generate an incomplete overlap list. *
+ * *
+ * HISTORY: *
+ * 11/06/1993 JLB : Created. *
+ * 03/25/1994 JLB : Added width optimization. *
+ * 04/29/1994 JLB : Converted to C. *
+ * 06/03/1994 JLB : Converted to general purpose spillage functionality. *
+ * 01/07/1995 JLB : Manually calculates spillage list for large objects. *
+ *=============================================================================================*/
+short const * Coord_Spillage_List(COORDINATE coord, int maxsize)
+{
+ static short const _MoveSpillage[(int)FACING_COUNT+1][5] = {
+ {0, -MAP_CELL_W, REFRESH_EOL, 0, 0}, // N
+ {0, -MAP_CELL_W, 1, -(MAP_CELL_W-1), REFRESH_EOL}, // NE
+ {0, 1, REFRESH_EOL, 0, 0}, // E
+ {0, 1, MAP_CELL_W, MAP_CELL_W+1, REFRESH_EOL}, // SE
+ {0, MAP_CELL_W, REFRESH_EOL, 0, 0}, // S
+ {0, -1, MAP_CELL_W, MAP_CELL_W-1, REFRESH_EOL}, // SW
+ {0, -1, REFRESH_EOL, 0, 0}, // W
+ {0, -1, -MAP_CELL_W, -(MAP_CELL_W+1), REFRESH_EOL}, // NW
+ {0, REFRESH_EOL, 0, 0, 0} // non-moving.
+ };
+ static short _manual[10];
+//; 00 = on axis
+//; 01 = below axis
+//; 10 = above axis
+//; 11 = undefined
+ static signed char const _SpillTable[16] = {8,6,2,-1,0,7,1,-1,4,5,3,-1,-1,-1,-1,-1};
+ int index=0;
+ int x,y;
+
+ /*
+ ** For mondo-enourmo-gigundo objects, use a prebuilt mammoth table
+ ** that covers a 5x5 square region.
+ */
+ if (maxsize > ICON_PIXEL_W * 2) {
+ static short const _gigundo[] = {
+ -((2*MAP_CELL_W)-2),-((2*MAP_CELL_W)-1),-((2*MAP_CELL_W)),-((2*MAP_CELL_W)+1),-((2*MAP_CELL_W)+2),
+ -((1*MAP_CELL_W)-2),-((1*MAP_CELL_W)-1),-((1*MAP_CELL_W)),-((1*MAP_CELL_W)+1),-((1*MAP_CELL_W)+2),
+ -((0*MAP_CELL_W)-2),-((0*MAP_CELL_W)-1),-((0*MAP_CELL_W)),-((0*MAP_CELL_W)+1),-((0*MAP_CELL_W)+2),
+ ((1*MAP_CELL_W)-2),((1*MAP_CELL_W)-1),((1*MAP_CELL_W)),((1*MAP_CELL_W)+1),((1*MAP_CELL_W)+2),
+ +((2*MAP_CELL_W)-2),+((2*MAP_CELL_W)-1),+((2*MAP_CELL_W)),+((2*MAP_CELL_W)+1),+((2*MAP_CELL_W)+2),
+ REFRESH_EOL
+ };
+ return(&_gigundo[0]);
+ }
+
+ /*
+ ** For very large objects, build the overlap list by hand. This is time consuming, but
+ ** not nearly as time consuming as drawing even a single cell unnecessarily.
+ */
+ if (maxsize > ICON_PIXEL_W) {
+ maxsize = min(maxsize, (ICON_PIXEL_W*2))/2;
+
+ x = (ICON_PIXEL_W * Coord_XLepton(coord)) / ICON_LEPTON_W;
+ y = (ICON_PIXEL_H * Coord_YLepton(coord)) / ICON_LEPTON_H;
+ int left = x-maxsize;
+ int right = x+maxsize;
+ int top = y-maxsize;
+ int bottom = y+maxsize;
+
+ _manual[index++] = 0;
+ if (left < 0) _manual[index++] = -1;
+ if (right >= ICON_PIXEL_W) _manual[index++] = 1;
+ if (top < 0) _manual[index++] = -MAP_CELL_W;
+ if (bottom >= ICON_PIXEL_H) _manual[index++] = MAP_CELL_W;
+ if (left < 0 && top < 0) _manual[index++] = -(MAP_CELL_W+1);
+ if (right >= ICON_PIXEL_W && bottom >= ICON_PIXEL_H) _manual[index++] = MAP_CELL_W+1;
+ if (left < 0 && bottom >= ICON_PIXEL_H) _manual[index++] = MAP_CELL_W-1;
+ if (right >= ICON_PIXEL_H && top < 0) _manual[index++] = -(MAP_CELL_W-1);
+ _manual[index] = REFRESH_EOL;
+ return(&_manual[0]);
+ }
+
+ /*
+ ** Determine the number of leptons "leeway" allowed this unit.
+ */
+ int posval = Pixel2Lepton[(ICON_PIXEL_W-maxsize)/2];
+
+ x = Coord_XLepton(coord) - 0x0080;
+ y = Coord_YLepton(coord) - 0x0080;
+ if (y > posval) index |= 0x08; // Spilling South.
+ if (y < -posval) index |= 0x04; // Spilling North.
+ if (x > posval) index |= 0x02; // Spilling East.
+ if (x < -posval) index |= 0x01; // Spilling West.
+
+ return(&_MoveSpillage[_SpillTable[index]][0]);
+}
+
+
+/***********************************************************************************************
+ * Coord_Spillage_List -- Calculate a spillage list for the dirty rectangle specified. *
+ * *
+ * Given a center coordinate and a dirty rectangle, calcuate a cell offset list for *
+ * determining such things as overlap and redraw logic. Optionally, the center cell *
+ * location will not be part of the list. *
+ * *
+ * INPUT: coord -- The center coordinate that the dirty rectangle is based off of. *
+ * *
+ * rect -- Reference to the dirty rectangle. *
+ * *
+ * nocenter -- If true, then the center cell offset will not be part of the spillage *
+ * list returned. This is handy when the center cell is known to be *
+ * processed by some other method and it can be safely and efficiently *
+ * ignored by the list generated. *
+ * *
+ * OUTPUT: Returns with a pointer to the spillage list that corresponds to the data *
+ * specified. This is a pointer to a static buffer and as such it will only be valid *
+ * until the next time that this routine is called. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/22/1996 JLB : Created. *
+ *=============================================================================================*/
+short const * Coord_Spillage_List(COORDINATE coord, Rect const & rect, bool nocenter)
+{
+ if (!rect.Is_Valid()) {
+ static short const _list[] = {REFRESH_EOL};
+ return(_list);
+ }
+
+ CELL coordcell = Coord_Cell(coord);
+ LEPTON x = Coord_X(coord);
+ LEPTON y = Coord_Y(coord);
+
+ /*
+ ** Add the rectangle values to the coordinate in order to normalize the start and end
+ ** corners of the rectangle. The values are now absolute to the real game world rather
+ ** than relative to the coordinate.
+ */
+ LEPTON_COMPOSITE startx;
+ LEPTON_COMPOSITE starty;
+ LEPTON_COMPOSITE endx;
+ LEPTON_COMPOSITE endy;
+ startx.Raw = (int)x + (short)Pixel_To_Lepton(rect.X);
+ starty.Raw = (int)y + (short)Pixel_To_Lepton(rect.Y);
+ endx.Raw = startx.Raw + Pixel_To_Lepton(rect.Width-1);
+ endy.Raw = starty.Raw + Pixel_To_Lepton(rect.Height-1);
+
+ /*
+ ** Determine the upper left and lower right cell indexes. This is a simple conversion from
+ ** their lepton counterpart. These cells values are used to form the bounding box for the
+ ** map offset list.
+ */
+ int cellx = startx.Sub.Cell;
+ int cellx2 = endx.Sub.Cell;
+ int celly = starty.Sub.Cell;
+ int celly2 = endy.Sub.Cell;
+
+ /*
+ ** Generate the spillage list by counting off the rows and colums of the cells
+ ** that are affected. This is easy since the upper left and lower right corner cells
+ ** are known.
+ */
+ int count = 0;
+ static short _spillagelist[128];
+ short * ptr = _spillagelist;
+ for (int yy = celly; yy <= celly2; yy++) {
+ for (int xx = cellx; xx <= cellx2; xx++) {
+ short offset = (XY_Cell(xx, yy) - coordcell);
+ if (!nocenter || offset != 0) {
+ *ptr++ = offset;
+ count++;
+ if (count+2 >= ARRAY_SIZE(_spillagelist)) break;
+ }
+ }
+ if (count+2 >= ARRAY_SIZE(_spillagelist)) break;
+ }
+
+ /*
+ ** Cap the list with the end of list marker and then return a pointer
+ ** to the completed list.
+ */
+ *ptr = REFRESH_EOL;
+ return(_spillagelist);
+}
+
+
+/***********************************************************************************************
+ * Coord_Move -- Moves a coordinate an arbitrary direction for an arbitrary distance *
+ * *
+ * This function will move a coordinate in a using SIN and COS arithmetic. *
+ * *
+ * INPUT: start -- The starting coordinate. *
+ * *
+ * dir -- The direction to move the coordinate. *
+ * *
+ * distance -- The distance to move the coordinate position (in leptons). *
+ * *
+ * OUTPUT: Returns the new coordinate position. *
+ * *
+ * WARNINGS: This routine uses multiplies -- use with caution. *
+ * *
+ * HISTORY: *
+ * 05/27/1994 JLB : Created. *
+ *=============================================================================================*/
+COORDINATE Coord_Move(COORDINATE start, register DirType dir, unsigned short distance)
+{
+#ifdef NEVER
+ short x = Coord_X(start);
+ short y = Coord_Y(start);
+
+ Move_Point(x, y, dir, distance);
+ return(XY_Coord(x,y));
+#endif
+
+ Move_Point(*(short *)&start, *(((short *)&start)+1), dir, distance);
+ return(start);
+}
+
+
+/***********************************************************************************************
+ * Coord_Scatter -- Determines a random coordinate from an anchor point. *
+ * *
+ * This routine will perform a scatter algorithm on the specified *
+ * anchor point in order to return with another coordinate that is *
+ * randomly nearby the original. Typical use of this would be for *
+ * missile targeting. *
+ * *
+ * INPUT: coord -- This is the anchor coordinate. *
+ * *
+ * distance -- This is the distance in pixels that the scatter *
+ * should fall within. *
+ * *
+ * lock -- bool; Convert the new coordinate into a center *
+ * cell based coordinate? *
+ * *
+ * OUTPUT: Returns with a new coordinate that is nearby the original. *
+ * *
+ * WARNINGS: Maximum pixel scatter distance is 255. *
+ * *
+ * HISTORY: *
+ * 02/01/1992 JLB : Created. *
+ * 05/13/1992 JLB : Only uses Random(). *
+ *=============================================================================================*/
+COORDINATE Coord_Scatter(COORDINATE coord, unsigned distance, bool lock)
+{
+ COORDINATE newcoord;
+
+ newcoord = Coord_Move(coord, Random_Pick(DIR_N, DIR_MAX), distance);
+
+ if (newcoord & HIGH_COORD_MASK) newcoord = coord;
+
+ if (lock) {
+ newcoord = Coord_Snap(newcoord);
+ }
+
+ return(newcoord);
+}
+
+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] = {
+ 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.
+
+#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
+
+}
+
+
+/***********************************************************************************************
+ * Normal_Move_Point -- Moves point with tilt compensation. *
+ * *
+ * This routine will move the point in the direction and distance specified but it will *
+ * take into account the tilt of the playing field. Typical use of this routine is to *
+ * determine positioning as it relates to the playfield. Turrets are a good example of *
+ * this. *
+ * *
+ * INPUT: x,y -- References to the coordinates to adjust. *
+ * *
+ * dir -- The direction of the desired movement. *
+ * *
+ * distance -- The distance (in coordinate units) to move the point. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 12/19/1995 JLB : Created. *
+ *=============================================================================================*/
+// Loss of precision in initializations (8 bits to 7 bits) warning. Hmmm.. can this be fixed?
+//lint -e569
+void Normal_Move_Point(short &x, short &y, register DirType dir, unsigned short distance)
+{
+ static signed 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 signed 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.
+
+ x += calcx(CosTable[dir], distance);
+
+ y += calcy(SinTable[dir] / 2, distance);
+}
diff --git a/CODE/COORDA.ASM b/CODE/COORDA.ASM
new file mode 100644
index 0000000..f8e279f
--- /dev/null
+++ b/CODE/COORDA.ASM
@@ -0,0 +1,134 @@
+;
+; Command & Conquer Red Alert(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 Cardinal_To_Fixed :NEAR
+GLOBAL 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/CODE/CPUID.ASM b/CODE/CPUID.ASM
new file mode 100644
index 0000000..4f58f13
--- /dev/null
+++ b/CODE/CPUID.ASM
@@ -0,0 +1,185 @@
+;
+; Command & Conquer Red Alert(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&c0\vcs\code\cpuid.asv 5.0 11 Nov 1996 09:40:28 JOE_BOSTIC $
+;***************************************************************************
+;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S I N C **
+;***************************************************************************
+;* *
+;* Project Name : Command & Conquer *
+;* *
+;* File Name : MMX.ASM *
+;* *
+;* Programmer : Steve Tall *
+;* *
+;* Start Date : May 19th, 1996 *
+;* *
+;* Last Update : May 19th 1996 [ST] *
+;* *
+;*-------------------------------------------------------------------------*
+;* Functions: *
+;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *
+
+
+ .586
+ .model flat
+
+;
+; Variables externs
+;
+GLOBAL C CPUType:byte
+;externdef C CPUType:byte
+GLOBAL C VendorID:byte
+;externdef C VendorID:byte
+
+;
+; Function externs
+;
+GLOBAL C Detect_MMX_Availability:near
+;externdef C Detect_MMX_Availability:near
+
+
+ .code
+
+
+;*********************************************************************************************
+;* Detect_MMX_Availability -- Detect the presence of MMX technology. *
+;* *
+;* *
+;* INPUT: Nothing *
+;* *
+;* OUTPUT: True if MMX technology is available. *
+;* *
+;* Warnings: *
+;* *
+;* Note: Based in part on CPUID32.ASM by Intel *
+;* *
+;* HISTORY: *
+;* 05/19/96 ST : Created. *
+;*===========================================================================================*
+
+Detect_MMX_Availability proc C
+
+ local idflag:byte
+ local cputype:byte
+
+;assume processor is at least 386
+;
+;check whether AC bit in eflags can be toggled.
+;If not then processor is 386
+
+ mov [idflag],0
+
+ pushfd ;get Eflags in EAX
+ pop eax
+ mov ecx,eax ;save eflags
+ xor eax,40000h ;toggle AC bit in eflags
+ push eax ;new eflags on stack
+ popfd ;move new value into eflags
+ pushfd ;get new eflags back into eax
+ pop eax
+ xor eax,ecx ;if AC bit not toggled then CPU=386
+ mov [cputype],3
+ jz @@end_get_cpu ;cpu is 386
+
+ push ecx
+ popfd ;restore AC bit in eflags
+
+
+;processor is at least 486
+;
+;Check for ability to set/clear ID flag in EFLAGS
+;ID flag indicates ability of processor to execute the CPUID instruction.
+;486 not guaranteed to have CPUID inst?
+;
+ mov [cputype],4
+ mov eax,ecx ;original EFLAGS
+ xor eax,200000h ;toggle ID bit
+ push eax
+ popfd
+ pushfd
+ pop eax
+ xor eax,ecx ;check if still toggled
+ jz @@end_get_cpu
+
+
+; Execute CPUID instruction to determine vendor, family,
+; model and stepping.
+;
+
+ mov [idflag],1 ;flag ID is available
+
+ xor eax,eax
+ cpuid
+
+ mov dword ptr [VendorID],ebx
+ mov dword ptr [VendorID+4],edx
+ mov dword ptr [VendorID+8],ecx
+ mov dword ptr [VendorID+12]," "
+
+ cmp eax,1 ;check if 1 is valid
+ jl @@end_get_cpu ;inp for cpuid inst.
+
+ xor eax,eax
+ inc eax
+
+ cpuid ;get stepping, model and family
+
+ and ax,0f00H
+ shr ax,08H
+
+ mov [cputype],al
+
+@@end_get_cpu: mov al,[cputype]
+ mov [CPUType],al
+
+
+;
+; We have the CPU type in al now.
+; If we arent on at least a pentium then we can assume there is no MMX
+;
+ cmp al,5
+ jl @@no_mmx
+
+ mov eax,1
+ cpuid
+ test edx,00800000h
+ jz @@no_mmx
+
+;
+; MMX detected - return true
+;
+ mov eax,1
+ ret
+
+
+@@no_mmx: xor eax,eax
+ ret
+
+
+Detect_MMX_Availability endp
+
+
+ .data
+
+CPUType db 0
+VendorID db "Not available",0,0,0,0,0,0
+
+
+end
+
diff --git a/CODE/CRATE.CPP b/CODE/CRATE.CPP
new file mode 100644
index 0000000..bb17217
--- /dev/null
+++ b/CODE/CRATE.CPP
@@ -0,0 +1,185 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CRATE.CPP 3 3/04/97 3:12p Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CRATE.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 08/26/96 *
+ * *
+ * Last Update : October 14, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * CrateClass::Create_Crate -- Create a crate in the cell specified. *
+ * CrateClass::Get_Crate -- Pick up a crate from the cell specified. *
+ * CrateClass::Put_Crate -- Generates crate overlay at cell specified. *
+ * CrateClass::Remove_It -- Removes the crate from wherever it is. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+
+
+/***********************************************************************************************
+ * CrateClass::Remove_It -- Removes the crate from wherever it is. *
+ * *
+ * This routine will remove the crate from whereever it happens to be. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: bool; Was the crate found and removed? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/26/1996 JLB : Created. *
+ *=============================================================================================*/
+bool CrateClass::Remove_It(void)
+{
+ if (Is_Valid()) {
+ Get_Crate(Cell);
+ Make_Invalid();
+ return(true);
+ }
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * CrateClass::Create_Crate -- Create a crate in the cell specified. *
+ * *
+ * This will create a crate in the cell specified. If the crate could not be crated there *
+ * then 'false' will be returned. *
+ * *
+ * INPUT: cell -- The desired cell to place the crate in. *
+ * *
+ * OUTPUT: bool; Was the crate created and placed in the cell? *
+ * *
+ * WARNINGS: It is quite possible for the crate not to have been placed. Only the most clear *
+ * locations are valid for crate placement. *
+ * *
+ * HISTORY: *
+ * 08/26/1996 JLB : Created. *
+ *=============================================================================================*/
+bool CrateClass::Create_Crate(CELL cell)
+{
+ /*
+ ** Remove any existing crate that this crate class is tracking.
+ */
+ Remove_It();
+
+ /*
+ ** Try to place a new crate at the cell specified.
+ */
+ if (Put_Crate(cell)) {
+ Cell = cell;
+ Timer = Random_Pick(Rule.CrateTime * (TICKS_PER_MINUTE/2), Rule.CrateTime * (TICKS_PER_MINUTE*2));
+ Timer.Start();
+ return(true);
+ }
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * CrateClass::Put_Crate -- Generates crate overlay at cell specified. *
+ * *
+ * This helpter routine will examine the cell and place the appropriate crate type into *
+ * the cell specified. If the overlay could not be generated, then 'false' is returned. *
+ * *
+ * INPUT: cell -- The cell to generate the crate overlay in. *
+ * *
+ * OUTPUT: bool; Was the crate overlay generated? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/26/1996 JLB : Created. *
+ * 10/14/1996 JLB : Takes reference to cell so that tracking can occur. *
+ *=============================================================================================*/
+bool CrateClass::Put_Crate(CELL & cell)
+{
+ int old = ScenarioInit;
+ ScenarioInit = 0;
+
+ if (Map.In_Radar(cell)) {
+ CellClass * cellptr = &Map[cell];
+
+ while (cellptr->Overlay != OVERLAY_NONE && !cellptr->Is_Clear_To_Build(SPEED_FLOAT) && !cellptr->Is_Clear_To_Build(SPEED_FOOT)) {
+ cell = Map.Pick_Random_Location();
+
+ if (Percent_Chance(100 * Rule.WaterCrateChance)) {
+ cell = Map.Nearby_Location(cell, SPEED_FLOAT);
+ } else {
+ cell = Map.Nearby_Location(cell, SPEED_TRACK);
+ }
+ cellptr = &Map[cell];
+ }
+
+ if (cellptr->Is_Clear_To_Build(SPEED_FLOAT)) {
+ new OverlayClass(OVERLAY_WATER_CRATE, cell);
+ } else {
+ new OverlayClass(OVERLAY_WOOD_CRATE, cell);
+ }
+ ScenarioInit = old;
+ return(true);
+ }
+
+ ScenarioInit = old;
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * CrateClass::Get_Crate -- Pick up a crate from the cell specified. *
+ * *
+ * This will remove the crate from the cell specified. *
+ * *
+ * INPUT: cell -- The cell to examine and remove any crate overlays present. *
+ * *
+ * OUTPUT: bool; Was a crate overlay found and removed? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/26/1996 JLB : Created. *
+ *=============================================================================================*/
+bool CrateClass::Get_Crate(CELL cell)
+{
+ if (Map.In_Radar(cell)) {
+ CellClass * cellptr = &Map[cell];
+
+ if (cellptr->Overlay == OVERLAY_WOOD_CRATE ||
+ cellptr->Overlay == OVERLAY_STEEL_CRATE ||
+ cellptr->Overlay == OVERLAY_WATER_CRATE) {
+
+ cellptr->Overlay = OVERLAY_NONE;
+ cellptr->OverlayData = 0;
+ cellptr->Redraw_Objects();
+ return(true);
+ }
+ }
+ return(false);
+}
diff --git a/CODE/CRATE.H b/CODE/CRATE.H
new file mode 100644
index 0000000..97b55b6
--- /dev/null
+++ b/CODE/CRATE.H
@@ -0,0 +1,79 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CRATE.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CRATE.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 08/26/96 *
+ * *
+ * Last Update : August 26, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifndef CRATE_H
+#define CRATE_H
+
+#include "ftimer.h"
+#include "jshell.h"
+
+
+/*
+** The "bool" integral type was defined by the C++ comittee in
+** November of '94. Until the compiler supports this, use the following
+** definition.
+*/
+#ifndef __BORLANDC__
+#ifndef TRUE_FALSE_DEFINED
+#define TRUE_FALSE_DEFINED
+enum {false=0,true=1};
+typedef int bool;
+#endif
+#endif
+
+class CrateClass {
+ public:
+ CrateClass(void) : Timer(NoInitClass()), Cell(-1) {}
+ void Init(void) {Make_Invalid();}
+ bool Create_Crate(CELL cell);
+ bool Is_Here(CELL cell) const {return(Is_Valid() && cell == Cell);}
+ bool Remove_It(void);
+ bool Is_Expired(void) const {return(Is_Valid() && Timer == 0);}
+ bool Is_Valid(void) const {return(Cell != -1);}
+
+ private:
+ static bool Put_Crate(CELL & cell);
+ static bool Get_Crate(CELL cell);
+
+ void Make_Invalid(void) {Cell = -1;Timer.Stop();}
+
+ CDTimerClass Timer;
+ CELL Cell;
+};
+
+
+#endif
diff --git a/CODE/CRC.CPP b/CODE/CRC.CPP
new file mode 100644
index 0000000..9b4e448
--- /dev/null
+++ b/CODE/CRC.CPP
@@ -0,0 +1,136 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CRC.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CRC.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 03/02/96 *
+ * *
+ * Last Update : March 2, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * CRCEngine::operator() -- Submits one byte of data to the CRC engine. *
+ * CRCEngine::operator() -- Submits an arbitrary data block to the CRC engine. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "crc.h"
+
+
+/***********************************************************************************************
+ * CRCEngine::operator() -- Submits one byte of data to the CRC engine. *
+ * *
+ * This routine will take the specified byte of data and submit it to the CRC engine *
+ * for processing. This routine is designed to be as fast as possible since the typical *
+ * use of this routine is to feed one of presumably many byte sized chunks of data to the *
+ * CRC engine. *
+ * *
+ * INPUT: datum -- One byte of data to submit to the CRC engine. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: If possible, use the buffer/size operator to submit data rather than repeated *
+ * calls to this routine. *
+ * *
+ * HISTORY: *
+ * 03/02/1996 JLB : Created. *
+ *=============================================================================================*/
+void CRCEngine::operator() (char datum)
+{
+ StagingBuffer.Buffer[Index++] = datum;
+
+ if (Index == sizeof(long)) {
+ CRC = Value();
+ StagingBuffer.Composite = 0;
+ Index = 0;
+ }
+}
+
+
+/***********************************************************************************************
+ * CRCEngine::operator() -- Submits an arbitrary data block to the CRC engine. *
+ * *
+ * This routine will submit the specified block to the CRC engine. The block can be of *
+ * arbitrary length. *
+ * *
+ * INPUT: buffer -- Pointer to the buffer that contains the data. The buffer will not *
+ * be modified. *
+ * *
+ * length -- The length of the buffer (in bytes). *
+ * *
+ * OUTPUT: Returns with the current CRC value accumulated so far. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 03/02/1996 JLB : Created. *
+ *=============================================================================================*/
+long CRCEngine::operator() (void const * buffer, int length)
+{
+ if (buffer != NULL && length > 0) {
+ char const * dataptr = (char const *)buffer;
+ int bytes_left = length;
+
+ /*
+ ** If there are any leader bytes (needed to fill the staging buffer)
+ ** then process those by first using them to fill up the staging
+ ** buffer. The bulk of the data block will be processed by the high
+ ** speed longword processing loop.
+ */
+ while (bytes_left && Buffer_Needs_Data()) {
+ operator()(*dataptr);
+ dataptr++;
+ bytes_left--;
+ }
+
+ /*
+ ** Perform the fast 'bulk' processing by reading long word sized
+ ** data blocks.
+ */
+ long const * longptr = (long const *)dataptr;
+ int longcount = bytes_left / sizeof(long); // Whole 'long' elements remaining.
+ while (longcount--) {
+ CRC = _lrotl(CRC, 1) + *longptr++;
+ bytes_left -= sizeof(long);
+ }
+
+ /*
+ ** If there are remainder bytes, then process these by adding them
+ ** to the staging buffer.
+ */
+ dataptr = (char const *)longptr;
+ while (bytes_left) {
+ operator()(*dataptr);
+ dataptr++;
+ bytes_left--;
+ }
+ }
+
+ /*
+ ** Return the current CRC value.
+ */
+ return(Value());
+}
diff --git a/CODE/CRC.H b/CODE/CRC.H
new file mode 100644
index 0000000..7b758fb
--- /dev/null
+++ b/CODE/CRC.H
@@ -0,0 +1,121 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CRC.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CRC.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 03/02/96 *
+ * *
+ * Last Update : March 2, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#ifndef CRC_H
+#define CRC_H
+
+#include
+
+/*
+** The "bool" integral type was defined by the C++ comittee in
+** November of '94. Until the compiler supports this, use the following
+** definition.
+*/
+#ifndef __BORLANDC__
+#ifndef TRUE_FALSE_DEFINED
+#define TRUE_FALSE_DEFINED
+enum {false=0,true=1};
+typedef int bool;
+#endif
+#endif
+
+/*
+** This is a CRC engine class. It will process submitted data and generate a CRC from it.
+** Well, actually, the value returned is not a true CRC. However, it shares the same strength
+** characteristic and is faster to generate than the traditional CRC. This object is treated like
+** a method class. If it is called as a function (using the function operator), it will return
+** the CRC value. There are other function operators to submit data for processing.
+*/
+class CRCEngine {
+ public:
+
+ // Constructor for CRC engine (it can have an override initial CRC value).
+ CRCEngine(long initial=0) : CRC(initial), Index(0) {
+ StagingBuffer.Composite = 0;
+ };
+
+ // Fetches CRC value.
+ long operator() (void) const {return(Value());};
+
+ // Submits one byte sized datum to the CRC accumulator.
+ void operator() (char datum);
+
+ // Submits an arbitrary buffer to the CRC accumulator.
+ long operator() (void const * buffer, int length);
+
+ // Implicit conversion operator so this object appears like a 'long integer'.
+ operator long(void) const {return(Value());};
+
+ protected:
+
+ bool Buffer_Needs_Data(void) const {
+ return(Index != 0);
+ };
+
+ long Value(void) const {
+ if (Buffer_Needs_Data()) {
+ return(_lrotl(CRC, 1) + StagingBuffer.Composite);
+ }
+ return(CRC);
+ };
+
+ /*
+ ** Current accumulator of the CRC value. This value doesn't take into
+ ** consideration any pending data in the staging buffer.
+ */
+ long CRC;
+
+ /*
+ ** This is the sub index into the staging buffer used to keep track of
+ ** partial data blocks as they are submitted to the CRC engine.
+ */
+ int Index;
+
+ /*
+ ** This is the buffer that holds the incoming partial data. When the buffer
+ ** is filled, the value is transformed into the CRC and the buffer is flushed
+ ** in preparation for additional data.
+ */
+ union {
+ long Composite;
+ char Buffer[sizeof(long)];
+ } StagingBuffer;
+};
+
+#endif
+
diff --git a/CODE/CRCPIPE.CPP b/CODE/CRCPIPE.CPP
new file mode 100644
index 0000000..5b0c2fd
--- /dev/null
+++ b/CODE/CRCPIPE.CPP
@@ -0,0 +1,90 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CRCPIPE.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CRCPIPE.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 06/30/96 *
+ * *
+ * Last Update : July 3, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * CRCPipe::Result -- Fetches the current CRC of the data. *
+ * CRCPipe::Put -- Retrieves the data bytes specified and calculates CRC on it. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#include "crcpipe.h"
+
+
+/***********************************************************************************************
+ * CRCPipe::Put -- Retrieves the data bytes specified and calculates CRC on it. *
+ * *
+ * This routine will fetch the number of bytes requested from the straw. The data is *
+ * not modified by this straw segment, but it is examined by the CRC engine in order to *
+ * keep an accurate CRC of the data that passes through this routine. *
+ * *
+ * INPUT: source -- Pointer to the buffer that will hold the data requested. *
+ * *
+ * length -- The number of bytes requested. *
+ * *
+ * OUTPUT: Returns with the actual number of bytes stored into the buffer. If this number is *
+ * less than the number requested, then this indicates that the data stream has been *
+ * exhausted. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+int CRCPipe::Put(void const * source, int slen)
+{
+ CRC(source, slen);
+ return(Pipe::Put(source, slen));
+}
+
+
+/***********************************************************************************************
+ * CRCPipe::Result -- Fetches the current CRC of the data. *
+ * *
+ * This routine will return the CRC of the data that has passed through the pipe up to *
+ * this time. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the CRC value. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+long CRCPipe::Result(void) const
+{
+ return(CRC());
+}
+
diff --git a/CODE/CRCPIPE.H b/CODE/CRCPIPE.H
new file mode 100644
index 0000000..d5c9b16
--- /dev/null
+++ b/CODE/CRCPIPE.H
@@ -0,0 +1,66 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CRCPIPE.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CRCPIPE.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 06/30/96 *
+ * *
+ * Last Update : June 30, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#ifndef CRCPIPE_H
+#define CRCPIPE_H
+
+#include "pipe.h"
+#include "crc.h"
+
+/*
+** This class doesn't modify the data being piped through, but it does examine it and build
+** a CRC value from the data.
+*/
+class CRCPipe : public Pipe
+{
+ public:
+ CRCPipe(void) {}
+ virtual int Put(void const * source, int slen);
+
+ // Fetch the CRC value.
+ long Result(void) const;
+
+ protected:
+ CRCEngine CRC;
+
+ private:
+ CRCPipe(CRCPipe & rvalue);
+ CRCPipe & operator = (CRCPipe const & pipe);
+};
+
+#endif
diff --git a/CODE/CRCSTRAW.CPP b/CODE/CRCSTRAW.CPP
new file mode 100644
index 0000000..04de153
--- /dev/null
+++ b/CODE/CRCSTRAW.CPP
@@ -0,0 +1,94 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CRCSTRAW.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CRCSTRAW.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 07/02/96 *
+ * *
+ * Last Update : July 3, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * CRCStraw::Get -- Fetch the data requested and calculate CRC on it. *
+ * CRCStraw::Result -- Returns with the CRC of all data passed through the straw. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#include "crcstraw.h"
+
+
+/***********************************************************************************************
+ * CRCStraw::Get -- Fetch the data requested and calculate CRC on it. *
+ * *
+ * This routine will fetch the number of bytes requested. The data will not be modified *
+ * by this straw segment, but the CRC engine will examine the data so as to keep an *
+ * accurate CRC value. *
+ * *
+ * INPUT: source -- Pointer to the buffer to hold the data requested. *
+ * *
+ * length -- The number of bytes requested. *
+ * *
+ * OUTPUT: Returns with the actual number of bytes stored in the buffer. If this number is *
+ * less than that requested, then this indicates that the data stream has been *
+ * exhausted. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+int CRCStraw::Get(void * source, int slen)
+{
+ if (source == NULL || slen < 1) {
+ return(0);
+ }
+
+ int counter = Straw::Get(source, slen);
+ CRC(source, counter);
+ return(counter);
+}
+
+
+/***********************************************************************************************
+ * CRCStraw::Result -- Returns with the CRC of all data passed through the straw. *
+ * *
+ * This routine will return the CRC value of the data that has passed through this straw *
+ * segment. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the CRC value of the data this straw segment has seen. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+long CRCStraw::Result(void) const
+{
+ return(CRC());
+}
diff --git a/CODE/CRCSTRAW.H b/CODE/CRCSTRAW.H
new file mode 100644
index 0000000..da9fb9f
--- /dev/null
+++ b/CODE/CRCSTRAW.H
@@ -0,0 +1,67 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CRCSTRAW.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CRCSTRAW.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 07/02/96 *
+ * *
+ * Last Update : July 2, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#ifndef CRCSTRAW_H
+#define CRCSTRAW_H
+
+#include "straw.h"
+#include "crc.h"
+
+/*
+** This class will build a CRC value from the data stream that is drawn through this class.
+** The data is not modified, but it is examined as it passes through.
+*/
+class CRCStraw : public Straw
+{
+ public:
+ CRCStraw(void) {}
+ virtual int Get(void * source, int slen);
+
+ // Calculate and return the CRC value.
+ long Result(void) const;
+
+ protected:
+ CRCEngine CRC;
+
+ private:
+ CRCStraw(CRCStraw & rvalue);
+ CRCStraw & operator = (CRCStraw const & pipe);
+};
+
+
+#endif
diff --git a/CODE/CREDITS.CPP b/CODE/CREDITS.CPP
new file mode 100644
index 0000000..cd25f3f
--- /dev/null
+++ b/CODE/CREDITS.CPP
@@ -0,0 +1,245 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CREDITS.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CREDITS.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : April 17, 1994 *
+ * *
+ * Last Update : March 13, 1995 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * CreditClass::AI -- Handles updating the credit display. *
+ * CreditClass::CreditClass -- Default constructor for the credit class object. *
+ * CreditClass::Graphic_Logic -- Handles the credit redraw logic. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+
+
+/***********************************************************************************************
+ * CreditClass::CreditClass -- Default constructor for the credit class object. *
+ * *
+ * This is the constructor for the credit class object. It merely sets the credit display *
+ * state to null. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 03/13/1995 JLB : Created. *
+ *=============================================================================================*/
+CreditClass::CreditClass(void) :
+ Credits(0),
+ Current(0),
+ IsToRedraw(false),
+ IsUp(false),
+ IsAudible(false),
+ Countdown(0)
+{
+}
+
+
+/***********************************************************************************************
+ * CreditClass::Graphic_Logic -- Handles the credit redraw logic. *
+ * *
+ * This routine should be called whenever the main game screen is to be updated. It will *
+ * check to see if the credit display should be redrawn. If so, it will redraw it. *
+ * *
+ * INPUT: forced -- Should the credit display be redrawn regardless of whether the redraw *
+ * flag is set? This is typically the case when the screen needs to be *
+ * redrawn from scratch. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 03/13/1995 JLB : Created. *
+ *=============================================================================================*/
+//#define XX (320-120)
+//#define WW 50
+void CreditClass::Graphic_Logic(bool forced)
+{
+ if (forced || IsToRedraw) {
+ BStart(BENCH_TABS);
+
+ int xx = SeenBuff.Get_Width() - (120 * RESFACTOR);
+
+ /*
+ ** Adjust the credits display to be above the sidebar for 640x400
+ */
+#ifdef WIN32
+ xx += 80 * RESFACTOR;
+#endif
+
+ /*
+ ** Play a sound effect when the money display changes, but only if a sound
+ ** effect was requested.
+ */
+ if (IsAudible) {
+ if (IsUp) {
+ Sound_Effect(VOC_MONEY_UP, fixed(1, 2));
+ } else {
+ Sound_Effect(VOC_MONEY_DOWN, fixed(1, 2));
+ }
+ }
+
+ /*
+ ** Display the new current value.
+ */
+ TabClass::Draw_Credits_Tab();
+#ifdef WIN32
+ Fancy_Text_Print("%ld", xx, 0, &MetalScheme, TBLACK, TPF_METAL12 | TPF_CENTER | TPF_USE_GRAD_PAL, Current);
+#else
+ Fancy_Text_Print("%ld", xx, 0, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_NOSHADOW|TPF_6PT_GRAD|TPF_CENTER|TPF_BRIGHT_COLOR, Current);
+#endif //WIN32
+
+ if (Scen.MissionTimer.Is_Active()) {
+ long secs = Scen.MissionTimer / TICKS_PER_SECOND;
+ long mins = secs / 60;
+ long hours = mins / 60;
+ secs %= 60;
+ mins %= 60;
+
+ /*
+ ** Speak mission timer reminders.
+ */
+ VoxType vox = VOX_NONE;
+ if (Scen.MissionTimer == (1 * TICKS_PER_MINUTE)) vox = VOX_TIME_1;
+ if (Scen.MissionTimer == (2 * TICKS_PER_MINUTE)) vox = VOX_TIME_2;
+ if (Scen.MissionTimer == (3 * TICKS_PER_MINUTE)) vox = VOX_TIME_3;
+ if (Scen.MissionTimer == (4 * TICKS_PER_MINUTE)) vox = VOX_TIME_4;
+ if (Scen.MissionTimer == (5 * TICKS_PER_MINUTE)) vox = VOX_TIME_5;
+ if (Scen.MissionTimer == (10 * TICKS_PER_MINUTE)) vox = VOX_TIME_10;
+ if (Scen.MissionTimer == (20 * TICKS_PER_MINUTE)) vox = VOX_TIME_20;
+ if (Scen.MissionTimer == (30 * TICKS_PER_MINUTE)) vox = VOX_TIME_30;
+ if (Scen.MissionTimer == (40 * TICKS_PER_MINUTE)) vox = VOX_TIME_40;
+ if (vox != VOX_NONE) {
+ Speak(vox);
+ Map.FlasherTimer = 7;
+ }
+
+#ifdef WIN32
+ if (hours) {
+ Fancy_Text_Print(TXT_TIME_FORMAT_HOURS, 200 * RESFACTOR, 0, &MetalScheme, TBLACK, TPF_METAL12|TPF_CENTER|TPF_USE_GRAD_PAL, hours, mins, secs);
+ } else {
+ Fancy_Text_Print(TXT_TIME_FORMAT_NO_HOURS, 200 * RESFACTOR, 0, &MetalScheme, TBLACK, TPF_METAL12|TPF_CENTER|TPF_USE_GRAD_PAL, mins, secs);
+ }
+#else
+ if (hours) {
+ Fancy_Text_Print("%02d:%02d:%02d", 120 * RESFACTOR, 0, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_NOSHADOW|TPF_6PT_GRAD|TPF_CENTER|TPF_BRIGHT_COLOR, hours, mins, secs);
+ } else {
+ Fancy_Text_Print("%02d:%02d", 120 * RESFACTOR, 0, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_NOSHADOW|TPF_6PT_GRAD|TPF_CENTER|TPF_BRIGHT_COLOR, mins, secs);
+ }
+#endif //WIN32
+ }
+
+ IsToRedraw = false;
+ IsAudible = false;
+ BEnd(BENCH_TABS);
+ }
+}
+
+
+/***********************************************************************************************
+ * CreditClass::AI -- Handles updating the credit display. *
+ * *
+ * This routine handles the logic that controls the rate of credit change in the credit *
+ * display. It doesn't actually redraw the credit display, but will flag it to be redrawn *
+ * if it detects that a change is to occur. *
+ * *
+ * INPUT: forced -- Should the credit display immediately reflect the current credit *
+ * total for the player? This is usually desired when initially loading *
+ * a scenario or saved game. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 03/13/1995 JLB : Created. *
+ *=============================================================================================*/
+void CreditClass::AI(bool forced)
+{
+ static int _last = 0;
+
+ if (!forced && Frame == _last) return;
+ _last = Frame;
+
+ Credits = PlayerPtr->Available_Money();
+
+ /*
+ ** Make sure that the credit counter doesn't drop below zero.
+ */
+ Credits = max(Credits, 0L);
+
+ if (Scen.MissionTimer.Is_Active() || Scen.MissionTimer) {
+ IsToRedraw = true;
+ Map.Flag_To_Redraw(false);
+ }
+
+ if (Current == Credits) return;
+
+ if (forced) {
+ IsAudible = false;
+ Current = Credits;
+ } else {
+
+ if (Countdown) Countdown--;
+ if (Countdown) return;
+
+ /*
+ ** Determine the amount to change the display toward the
+ ** desired value.
+ */
+ int adder = Credits - Current;
+
+ if (adder > 0) {
+ Countdown = 1;
+ } else {
+ Countdown = 3;
+ }
+
+ adder = ABS(adder);
+ adder >>= 3;
+// adder >>= 4;
+// adder >>= 5;
+ adder = Bound(adder, 1, 71+72);
+ if (Current > Credits) adder = -adder;
+ Current += adder;
+ if (Current-adder != Current) {
+ IsAudible = true;
+ IsUp = (adder > 0);
+ }
+ }
+ IsToRedraw = true;
+ Map.Flag_To_Redraw(false);
+}
diff --git a/CODE/CREDITS.H b/CODE/CREDITS.H
new file mode 100644
index 0000000..c79aa17
--- /dev/null
+++ b/CODE/CREDITS.H
@@ -0,0 +1,73 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CREDITS.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CREDIT.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : April 19, 1994 *
+ * *
+ * Last Update : April 19, 1994 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifndef CREDITS_H
+#define CREDITS_H
+
+/****************************************************************************
+** The animating credit counter display is controlled by this class.
+*/
+class CreditClass {
+ public:
+ long Credits; // Value of credits trying to update display to.
+
+ /*---------------------------------------------------------------------
+ ** Constructors, Destructors, and overloaded operators.
+ */
+ CreditClass(void);
+ CreditClass(NoInitClass const & ) {};
+
+ /*---------------------------------------------------------------------
+ ** Member function prototypes.
+ */
+ void Update(bool forced=false, bool redraw=false);
+
+ void Graphic_Logic(bool forced=false);
+ void AI(bool forced=false);
+
+ 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/CODE/CREW.CPP b/CODE/CREW.CPP
new file mode 100644
index 0000000..c9fe7e9
--- /dev/null
+++ b/CODE/CREW.CPP
@@ -0,0 +1,39 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CREW.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CREW.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : April 23, 1994 *
+ * *
+ * Last Update : April 23, 1994 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+
diff --git a/CODE/CREW.H b/CODE/CREW.H
new file mode 100644
index 0000000..e8470fa
--- /dev/null
+++ b/CODE/CREW.H
@@ -0,0 +1,70 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CREW.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CREW.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : April 23, 1994 *
+ * *
+ * Last Update : April 23, 1994 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifndef CREW_H
+#define CREW_H
+
+
+/****************************************************************************
+** This class handles the basic crew logic. This includes hero tracking,
+** crew bail-out, and attached object logic.
+*/
+class CrewClass
+{
+ public:
+ /*
+ ** This keeps track of the number of "kills" the unit as accumulated.
+ ** When it reaches a certain point, the unit improves.
+ */
+ unsigned short Kills;
+
+ /*
+ ** Constructors, Destructors, and overloaded operators.
+ */
+ CrewClass(void) : Kills(0) {};
+ CrewClass(NoInitClass const &) {};
+ ~CrewClass(void) {};
+
+ int Made_A_Kill(void) {
+ Kills++;
+ return(Kills);
+ };
+
+ private:
+};
+
+#endif
diff --git a/CODE/CSTRAW.CPP b/CODE/CSTRAW.CPP
new file mode 100644
index 0000000..250c188
--- /dev/null
+++ b/CODE/CSTRAW.CPP
@@ -0,0 +1,100 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CSTRAW.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CSTRAW.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 11/10/96 *
+ * *
+ * Last Update : November 10, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * CacheStraw::Get -- Fetch data from the data source. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "cstraw.h"
+#include
+
+
+/***********************************************************************************************
+ * CacheStraw::Get -- Fetch data from the data source. *
+ * *
+ * This will supply the data quantity requested. It performs a regulating influence on the *
+ * data requests passed through it. The data is requested from the next straw in the *
+ * chain such that the data stream is requested in chunks. This serves to lessen the *
+ * impact of multiple small data requests. *
+ * *
+ * INPUT: source -- Pointer to the buffer to hold the data. *
+ * *
+ * slen -- The number of data bytes requested. *
+ * *
+ * OUTPUT: Returns with the number of data bytes stored into the buffer specified. If this *
+ * number is less than that requested, it indicates that the data source has been *
+ * exhausted. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 11/10/1996 JLB : Created. *
+ *=============================================================================================*/
+int CacheStraw::Get(void * source, int slen)
+{
+ int total = 0;
+
+ if (Is_Valid() && source != NULL && slen > 0) {
+
+ /*
+ ** Keep processing the data request until there is no more data to supply or the request
+ ** has been fulfilled.
+ */
+ while (slen > 0) {
+
+ /*
+ ** First try to fetch the data from data previously loaded into the buffer.
+ */
+ if (Length > 0) {
+ int tocopy = (Length < slen) ? Length : slen;
+ memmove(source, ((char *)BufferPtr.Get_Buffer()) + Index, tocopy);
+ slen -= tocopy;
+ Index += tocopy;
+ total += tocopy;
+ Length -= tocopy;
+ source = (char*)source + tocopy;
+ }
+ if (slen == 0) break;
+
+ /*
+ ** Since there is more to be fulfilled yet the holding buffer is empty,
+ ** refill the buffer with a fresh block of data from the source.
+ */
+ Length = Straw::Get(BufferPtr, BufferPtr.Get_Size());
+ Index = 0;
+ if (Length == 0) break;
+ }
+ }
+ return(total);
+}
diff --git a/CODE/CSTRAW.H b/CODE/CSTRAW.H
new file mode 100644
index 0000000..8d7c65b
--- /dev/null
+++ b/CODE/CSTRAW.H
@@ -0,0 +1,71 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/CSTRAW.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : CSTRAW.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 11/10/96 *
+ * *
+ * Last Update : November 10, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#ifndef CSTRAW_H
+#define CSTRAW_H
+
+#include "straw.h"
+#include "buff.h"
+
+/*
+** This class handles transfer of data by perform regulated requests for data from the next
+** class in the chain. It performs no translation on the data. By using this segment in a
+** straw chain, data throughput can be regulated. This can yield great performance increases
+** when dealing with a file source.
+*/
+class CacheStraw : public Straw
+{
+ public:
+ CacheStraw(Buffer const & buffer) : BufferPtr(buffer), Index(0), Length(0) {}
+ CacheStraw(int length=4096) : BufferPtr(length), Index(0), Length(0) {}
+ virtual int Get(void * source, int slen);
+
+ private:
+ Buffer BufferPtr;
+ int Index;
+ int Length;
+
+ bool Is_Valid(void) {return(BufferPtr.Is_Valid());}
+ CacheStraw(CacheStraw & rvalue);
+ CacheStraw & operator = (CacheStraw const & pipe);
+};
+
+
+
+#endif
+
diff --git a/CODE/CWSTUB.C b/CODE/CWSTUB.C
new file mode 100644
index 0000000..2af8ae0
--- /dev/null
+++ b/CODE/CWSTUB.C
@@ -0,0 +1,71 @@
+/*
+** Command & Conquer Red Alert(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/CODE/DDE.CPP b/CODE/DDE.CPP
new file mode 100644
index 0000000..ec6d293
--- /dev/null
+++ b/CODE/DDE.CPP
@@ -0,0 +1,452 @@
+/*
+** Command & Conquer Red Alert(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 *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifdef 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;
+ }
+}
+
+#endif //WIN32
diff --git a/CODE/DDE.H b/CODE/DDE.H
new file mode 100644
index 0000000..1554a9a
--- /dev/null
+++ b/CODE/DDE.H
@@ -0,0 +1,175 @@
+/*
+** Command & Conquer Red Alert(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/CODE/DEBUG.CPP b/CODE/DEBUG.CPP
new file mode 100644
index 0000000..2631bf7
--- /dev/null
+++ b/CODE/DEBUG.CPP
@@ -0,0 +1,560 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/DEBUG.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : DEBUG.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : September 10, 1993 *
+ * *
+ * Last Update : July 18, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * Self_Regulate -- Regulates the logic timer to result in smooth animation. *
+ * Debug_Key -- Debug mode keyboard processing. *
+ * Bench_Time -- Convert benchmark timer into descriptive string. *
+ * Benchmarks -- Display the performance tracking benchmarks. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+#include "vortex.h"
+#include
+
+
+#ifdef CHEAT_KEYS
+
+
+static CDTimerClass DebugTimer;
+
+int VortexFrame = -1;
+
+/***********************************************************************************************
+ * Debug_Key -- Debug mode keyboard processing. *
+ * *
+ * If debugging is enabled, then this routine will be called for every keystroke that the *
+ * game doesn't recognize. These extra keys usually perform some debugging function. *
+ * *
+ * INPUT: input -- The key code that was pressed. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 10/07/1992 JLB : Created. *
+ *=============================================================================================*/
+void Debug_Key(unsigned input)
+{
+ static int map_x = -1;
+ static int map_y = -1;
+ static int map_width = -1;
+ static int map_height = -1;
+
+ if (!input || input & KN_BUTTON) return;
+
+ /*
+ ** Processing of normal keystrokes.
+ */
+ if (Debug_Flag) {
+
+ switch (input) {
+
+ case KN_BACKSPACE:
+
+ if (ChronalVortex.Is_Active()) {
+ ChronalVortex.Disappear();
+ } else {
+ int xxxx = Get_Mouse_X() + Map.TacPixelX;
+ int yyyy = Get_Mouse_Y() + Map.TacPixelY;
+ CELL cell = Map.DisplayClass::Click_Cell_Calc(xxxx,yyyy);
+ ChronalVortex.Appear ( Cell_Coord (cell) );
+ }
+ break;
+
+#ifdef WIN32
+ case KN_J:
+ Debug_MotionCapture = true;
+ break;
+
+#ifdef OBSOLETE
+ case KN_K:
+ /*
+ ** time to create a screen shot using the PCX code (if it works)
+ */
+ if (!Debug_MotionCapture) {
+ GraphicBufferClass temp_page( SeenBuff.Get_Width(),
+ SeenBuff.Get_Height(),
+ NULL,
+ SeenBuff.Get_Width() * SeenBuff.Get_Height());
+ CDFileClass file;
+ char filename[30];
+
+ SeenBuff.Blit(temp_page);
+ for (int lp = 0; lp < 99; lp ++) {
+ sprintf(filename, "scrsht%02d.pcx", lp);
+ file.Set_Name(filename);
+ if (!file.Is_Available()) break;
+ }
+
+ file.Cache(200000);
+ Write_PCX_File(file, temp_page, & GamePalette);
+ Sound_Effect(VOC_BEEP);
+ }
+ break;
+#endif
+#endif
+
+ case KN_P:
+ {
+ for (SpecialWeaponType spc = SPC_FIRST; spc < SPC_COUNT; spc++) {
+ PlayerPtr->SuperWeapon[spc].Enable(true, true);
+ PlayerPtr->SuperWeapon[spc].Forced_Charge(true);
+ Map.Add(RTTI_SPECIAL, spc);
+ Map.Column[1].Flag_To_Redraw();
+ }
+ }
+ break;
+
+ case KN_I:
+ {
+ Map.Flash_Power();
+ Map.Flash_Money();
+ }
+ break;
+
+ case KN_O:
+ {
+ AircraftClass * air = new AircraftClass(AIRCRAFT_HIND, PlayerPtr->Class->House);
+ if (air) {
+ air->Height = 0;
+ air->Unlimbo(Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()), DIR_N);
+ }
+ }
+ break;
+
+ case KN_B:
+ {
+ AircraftClass * air = new AircraftClass(AIRCRAFT_LONGBOW, PlayerPtr->Class->House);
+ if (air) {
+ air->Height = 0;
+ air->Unlimbo(Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()), DIR_N);
+ }
+ }
+ break;
+
+ case KN_GRAVE:
+ {
+ WarheadType warhead = Random_Pick(WARHEAD_HE, WARHEAD_FIRE);
+ COORDINATE coord = Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y());
+ int damage = 1000;
+ new AnimClass(Combat_Anim(damage, warhead, Map[coord].Land_Type()), coord);
+ Explosion_Damage(coord, damage, NULL, warhead);
+ }
+ break;
+
+ case KN_C:
+ Debug_Cheat = (Debug_Cheat == false);
+ PlayerPtr->IsRecalcNeeded = true;
+
+ /*
+ ** This placement might affect any prerequisite requirements for construction
+ ** lists. Update the buildable options accordingly.
+ */
+ if (!ScenarioInit) {
+ Map.Recalc();
+ for (int index = 0; index < Buildings.Count(); index++) {
+ Buildings.Ptr(index)->Update_Buildables();
+ }
+ }
+ break;
+
+ case (int)KN_Z|(int)KN_ALT_BIT:
+ if (map_x == -1) {
+ map_x = Map.MapCellX;
+ map_y = Map.MapCellY;
+ map_width = Map.MapCellWidth;
+ map_height = Map.MapCellHeight;
+ Map.MapCellX = 1;
+ Map.MapCellY = 1;
+ Map.MapCellWidth = MAP_CELL_W-2;
+ Map.MapCellHeight = MAP_CELL_H-2;
+ } else {
+ Map.MapCellX = map_x;
+ Map.MapCellY = map_y;
+ Map.MapCellWidth = map_width;
+ Map.MapCellHeight = map_height;
+ map_x = -1;
+ map_y = -1;
+ map_width = -1;
+ map_height = -1;
+ }
+ break;
+
+ case KN_M:
+ if (Debug_Flag) {
+ if (MonoClass::Is_Enabled()) {
+ MonoClass::Disable();
+ } else {
+ MonoClass::Enable();
+ }
+ }
+ break;
+
+ case (int)KN_W|(int)KN_ALT_BIT:
+ PlayerPtr->Flag_To_Win();
+ break;
+
+ case (int)KN_L|(int)KN_ALT_BIT:
+ PlayerPtr->Flag_To_Lose();
+ break;
+
+ case KN_DELETE:
+ if (CurrentObject.Count()) {
+ Map.Recalc();
+ //CurrentObject[0]->Detach_All();
+ if (CurrentObject[0]->What_Am_I() == RTTI_BUILDING) {
+ ((BuildingClass *)CurrentObject[0])->Sell_Back(1);
+ } else {
+ ObjectClass * object = CurrentObject[0];
+ object->Unselect();
+ object->Limbo();
+ delete object;
+ }
+ }
+ break;
+
+ case (int)KN_DELETE|(int)KN_SHIFT_BIT:
+ if (CurrentObject.Count()) {
+ Map.Recalc();
+ int damage = 50;
+ CurrentObject[0]->Take_Damage(damage, 0, WARHEAD_SA);
+ }
+ break;
+
+ case KN_INSERT:
+ if (CurrentObject.Count()) {
+ Map.PendingObject = &CurrentObject[0]->Class_Of();
+ if (Map.PendingObject) {
+ Map.PendingHouse = CurrentObject[0]->Owner();
+ Map.PendingObjectPtr = Map.PendingObject->Create_One_Of(HouseClass::As_Pointer(Map.PendingHouse));
+ if (Map.PendingObjectPtr) {
+ Map.Set_Cursor_Pos();
+ Map.Set_Cursor_Shape(Map.PendingObject->Occupy_List());
+ }
+ }
+ }
+ break;
+
+ case KN_LBRACKET:
+ case KN_F11:
+ if (MonoPage == DMONO_FIRST) {
+ MonoPage = DMonoType(DMONO_COUNT-1);
+ } else {
+ MonoPage = DMonoType(MonoPage - 1);
+ }
+ DebugTimer = 0;
+ break;
+
+ case KN_RBRACKET:
+ case KN_F12:
+ MonoPage = DMonoType(MonoPage + 1);
+ if (MonoPage == DMONO_COUNT) {
+ MonoPage = DMONO_FIRST;
+ }
+ DebugTimer = 0;
+ break;
+
+ case KN_V:
+ case KN_F3:
+ Debug_Icon = (Debug_Icon == false);
+ Map.Flag_To_Redraw(true);
+ break;
+
+ /*
+ ** Reveal entire map to player.
+ */
+// case KN_F4:
+// if (Session.Type == GAME_NORMAL) {
+// Debug_Unshroud = (Debug_Unshroud == false);
+// Map.Flag_To_Redraw(true);
+// }
+// break;
+
+ /*
+ ** Shows sight and fire range in the form of circles emanating from the currently
+ ** selected unit. The white circle is for sight range, the red circle is for
+ ** fire range.
+ */
+ case KN_F7:
+ if (CurrentObject.Count() && CurrentObject[0]->Is_Techno()) {
+ TechnoTypeClass const & ttype = (TechnoTypeClass const &)CurrentObject[0]->Class_Of();
+ int sight = ((int)ttype.SightRange)<<8;
+ int weapon = 0;
+ if (ttype.PrimaryWeapon != NULL) weapon = ttype.PrimaryWeapon->Range;
+ Set_Logic_Page(SeenBuff);
+ COORDINATE center = CurrentObject[0]->Center_Coord();
+ COORDINATE center2 = CurrentObject[0]->Fire_Coord(0);
+
+ for (int r = 0; r < 255; r += 10) {
+ int x,y,x1,y1;
+ DirType r1 = (DirType)r;
+ DirType r2 = (DirType)((r+10) & 0xFF);
+
+ if (Map.Coord_To_Pixel(Coord_Move(center, r1, sight), x, y)) {
+ Map.Coord_To_Pixel(Coord_Move(center, r2, sight), x1, y1);
+ LogicPage->Draw_Line(x, y+8, x1, y1+8, WHITE);
+ }
+ if (Map.Coord_To_Pixel(Coord_Move(center2, r1, weapon), x, y)) {
+ Map.Coord_To_Pixel(Coord_Move(center2, r2, weapon), x1, y1);
+ LogicPage->Draw_Line(x, y+8, x1, y1+8, RED);
+ }
+ }
+ }
+ break;
+
+ case ((int)KN_F4 | (int)KN_CTRL_BIT):
+ Debug_Unshroud = (Debug_Unshroud == false);
+ Map.Flag_To_Redraw(true);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * Bench_Time -- Convert benchmark timer into descriptive string. *
+ * *
+ * This routine will take the values of the benchmark timer specified and build a string *
+ * that displays the average time each event consumed as well as the ranking of how much *
+ * time that event took (total) during the tracking duration (one second?). *
+ * *
+ * INPUT: btype -- The benchmark to convert to a descriptive string. *
+ * *
+ * OUTPUT: Returns with a pointer to the descriptive string of the benchmark specified. *
+ * *
+ * WARNINGS: The value returned is a pointer to a static buffer. As such, it is only valid *
+ * until the next time that this routine is called. *
+ * *
+ * HISTORY: *
+ * 07/18/1996 JLB : Created. *
+ *=============================================================================================*/
+static char const * Bench_Time(BenchType btype)
+{
+ static char buffer[32];
+
+ int rootcount = Benches[BENCH_GAME_FRAME].Count();
+ if (rootcount == 0) rootcount = 1;
+ int roottime = Benches[BENCH_GAME_FRAME].Value();
+ int count = Benches[btype].Count();
+ int time = Benches[btype].Value();
+ if (count > 0 && count * time > roottime * rootcount) time = roottime / count;
+ int percent = 0;
+ if (roottime != 0 && rootcount != 0) {
+ percent = ((count * time) * 99) / (roottime * rootcount);
+ }
+ if (percent > 99) percent = 99;
+ sprintf(buffer, "%-2d%% %7d", percent, time);
+ return(buffer);
+}
+
+
+/***********************************************************************************************
+ * Benchmarks -- Display the performance tracking benchmarks. *
+ * *
+ * This will display the benchmarks for the various processes that are being tracked. The *
+ * display will indicate the fraction that each process is consuming out of the entire *
+ * process time as well as the time consumed by each individual event. The total fraction *
+ * is useful for determing what should be optimized. The individual time is useful for *
+ * guaging the effectiveness of optimization changes. *
+ * *
+ * INPUT: mono -- Pointer to the monochrome screen that the display will use. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/18/1996 JLB : Created. *
+ *=============================================================================================*/
+static void Benchmarks(MonoClass * mono)
+{
+ static bool _first = true;
+ if (_first) {
+ _first = false;
+ mono->Clear();
+ mono->Set_Cursor(0, 0);
+ mono->Print(Text_String(TXT_DEBUG_PERFORMANCE));
+ if (Benches == NULL) {
+ mono->Set_Cursor(20, 15);
+ mono->Printf(TXT_NO_PENTIUM);
+ }
+ }
+
+ if (Benches != NULL) {
+ mono->Set_Cursor(1, 2);mono->Printf("%s", Bench_Time(BENCH_FINDPATH));
+ mono->Set_Cursor(1, 4);mono->Printf("%s", Bench_Time(BENCH_GREATEST_THREAT));
+ mono->Set_Cursor(1, 6);mono->Printf("%s", Bench_Time(BENCH_AI));
+ mono->Set_Cursor(1, 8);mono->Printf("%s", Bench_Time(BENCH_PCP));
+ mono->Set_Cursor(1, 10);mono->Printf("%s", Bench_Time(BENCH_EVAL_OBJECT));
+ mono->Set_Cursor(1, 12);mono->Printf("%s", Bench_Time(BENCH_EVAL_CELL));
+ mono->Set_Cursor(1, 14);mono->Printf("%s", Bench_Time(BENCH_EVAL_WALL));
+ mono->Set_Cursor(1, 16);mono->Printf("%s", Bench_Time(BENCH_MISSION));
+
+ mono->Set_Cursor(14, 2);mono->Printf("%s", Bench_Time(BENCH_CELL));
+ mono->Set_Cursor(14, 4);mono->Printf("%s", Bench_Time(BENCH_OBJECTS));
+ mono->Set_Cursor(14, 6);mono->Printf("%s", Bench_Time(BENCH_ANIMS));
+
+ mono->Set_Cursor(27, 2);mono->Printf("%s", Bench_Time(BENCH_PALETTE));
+
+ mono->Set_Cursor(40, 2);mono->Printf("%s", Bench_Time(BENCH_GSCREEN_RENDER));
+ mono->Set_Cursor(40, 4);mono->Printf("%s", Bench_Time(BENCH_SIDEBAR));
+ mono->Set_Cursor(40, 6);mono->Printf("%s", Bench_Time(BENCH_RADAR));
+ mono->Set_Cursor(40, 8);mono->Printf("%s", Bench_Time(BENCH_TACTICAL));
+ mono->Set_Cursor(40, 10);mono->Printf("%s", Bench_Time(BENCH_POWER));
+ mono->Set_Cursor(40, 12);mono->Printf("%s", Bench_Time(BENCH_SHROUD));
+ mono->Set_Cursor(40, 14);mono->Printf("%s", Bench_Time(BENCH_TABS));
+ mono->Set_Cursor(40, 16);mono->Printf("%s", Bench_Time(BENCH_BLIT_DISPLAY));
+
+ mono->Set_Cursor(66, 2);mono->Printf("%7d", Benches[BENCH_RULES].Value());
+ mono->Set_Cursor(66, 4);mono->Printf("%7d", Benches[BENCH_SCENARIO].Value());
+
+ for (BenchType index = BENCH_FIRST; index < BENCH_COUNT; index++) {
+ if (index != BENCH_RULES && index != BENCH_SCENARIO) Benches[index].Reset();
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * Self_Regulate -- Regulates the logic timer to result in smooth animation *
+ * *
+ * The self regulation process checks the number of frames displayed *
+ * per second and from this determines the amount of time to devote *
+ * to internal logic processing. By adjusting the time allotted to *
+ * internal processing, smooth animation can be maintained. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: In order for this routine to work properly it MUST be *
+ * called every display loop. *
+ * *
+ * HISTORY: *
+ * 07/31/1991 JLB : Created. *
+ * 07/05/1994 JLB : Handles new monochrome system. *
+ *=============================================================================================*/
+#define UPDATE_INTERVAL TIMER_SECOND
+void Self_Regulate(void)
+{
+ static ObjectClass * _lastobject = 0;
+ static bool _first=true;
+
+ if (DebugTimer == 0) {
+ DebugTimer = UPDATE_INTERVAL;
+
+ if (MonoClass::Is_Enabled()) {
+ if (_first) {
+ _first = false;
+ for (DMonoType index = DMONO_FIRST; index < DMONO_COUNT; index++) {
+ MonoArray[index].Clear();
+ }
+ }
+
+ /*
+ ** Always update the stress tracking mono display even if it
+ ** currently isn't visible.
+ */
+ Logic.Debug_Dump(&MonoArray[DMONO_STRESS]);
+
+ MonoClass * mono = &MonoArray[MonoPage];
+ mono->Set_Default_Attribute(MonoClass::NORMAL);
+ mono->View();
+
+ switch (MonoPage) {
+ case DMONO_EVENTS:
+ Benchmarks(mono);
+ break;
+
+ case DMONO_OBJECT:
+ mono->Clear();
+
+ /*
+ ** Display the status of the currently selected object.
+ */
+ if (CurrentObject.Count()) {
+ _lastobject = CurrentObject[0];
+ }
+ if (_lastobject && !_lastobject->IsActive) {
+ _lastobject = 0;
+ }
+ if (_lastobject) {
+ _lastobject->Debug_Dump(mono);
+ }
+ break;
+
+ case DMONO_STRESS:
+#ifdef OBSOLETE
+ mono->Set_Cursor(0, 20);
+ mono->Printf(
+ "Heap size:%10ld \r"
+ "Largest: %10ld \r"
+ "Ttl Free: %10ld \r"
+ "Frag: %10ld \r",
+ Heap_Size(MEM_NORMAL),
+ Ram_Free(MEM_NORMAL),
+ Total_Ram_Free(MEM_NORMAL),
+ Total_Ram_Free(MEM_NORMAL)-Ram_Free(MEM_NORMAL)
+ );
+#endif
+ break;
+
+ case DMONO_HOUSE:
+ mono->Clear();
+
+ if (CurrentObject.Count()) {
+ _lastobject = CurrentObject[0];
+ }
+ if (_lastobject && !_lastobject->IsActive) {
+ _lastobject = 0;
+ }
+ if (_lastobject && _lastobject->Is_Techno()) {
+ ((TechnoClass *)_lastobject)->House->Debug_Dump(mono);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ mono->Set_Cursor(0, 0);
+ }
+ }
+}
+#endif
diff --git a/CODE/DEBUG.H b/CODE/DEBUG.H
new file mode 100644
index 0000000..b18ada0
--- /dev/null
+++ b/CODE/DEBUG.H
@@ -0,0 +1,61 @@
+/*
+** Command & Conquer Red Alert(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_CLEAR_MAP 0x3e8 // Clear the map
+#define TXT_INHERIT_MAP 0x3e9 // Inherit previous map
+#define TXT_SPECIAL_OPTIONS 0x3ea // Select Special Options
+#define TXT_VISIBLE_TARGET 0x3eb // Targeting flash visible to
+#define TXT_TREE_TARGET 0x3ec // Allow targeting of trees.
+#define TXT_MCV_DEPLOY 0x3ed // Allow undeploy of
+#define TXT_SMART_DEFENCE 0x3ee // Employ smarter self defense
+#define TXT_SLOW_BUILD 0x3ef // Moderate production speed.
+#define TXT_THREE_POINT 0x3f0 // Use three point turn logic.
+#define TXT_TIBERIUM_GROWTH 0x3f1 // Ore will grow.
+#define TXT_TIBERIUM_SPREAD 0x3f2 // Ore will spread.
+#define TXT_ROAD_PIECES 0x3f3 // Disable building "bib"
+#define TXT_SCATTER 0x3f4 // Allow running from
+#define TXT_SHOW_NAMES 0x3f5 // Show true object names.
+#define TXT_DEFENDER_ADVANTAGE 0x3f6 // Defender has the advantage.
+#define TXT_SEPARATE_HELIPAD 0x3f7 // Allow separate helipad
+#define TXT_PASSWORD_CAPTION 0x3f8 // Password Request
+#define TXT_PASSWORD_MESSAGE 0x3f9 // Enter Red Alert access code
+#define TXT_PASSWORD_ERROR 0x3fa // Access code error detected.
+#define TXT_TRY_AGAIN 0x3fb // Try Again
+#define TXT_MINE_AWARE 0x3fc // Friendly units avoid
+#define TXT_TRIGGER_EDITOR 0x3fd // Trigger Editor
+#define TXT_TRIGGER_JUST_EVENT 0x3fe // Just This Event
+#define TXT_TRIGGER_AND 0x3ff // ... and ...
+#define TXT_TRIGGER_OR 0x400 // ... or ...
+#define TXT_TRIGGER_LINKED 0x401 // ... linked ...
+#define TXT_TRIGGER_JUST_ACTION 0x402 // Just This Action
+#define TXT_TEAM_EDIT 0x403 // Team Editor
+#define TXT_SELLABLE 0x404 // Sellable
+#define TXT_REBUILD 0x405 // Rebuild
+#define TXT_SPEED_BUILD 0x406 // Building constructin time
+#define TXT_SCENARIO_ERRORx 0x407 // Scenario authentication
+#define TXT_DEBUG_STRESS 0x408 // ÚFrames:ÄÂF/R:ÂCPU:ÄÄÂF/R:ÄÄ
+#define TXT_DEBUG_VEHICLE 0x409 // ÚFull
+#define TXT_DEBUG_INFANTRY 0x40a // ÚFull
+#define TXT_DEBUG_SHIP 0x40b // ÚFull
+#define TXT_DEBUG_BUILDING 0x40c // ÚFull
+#define TXT_DEBUG_PERFORMANCE 0x40d // Game Objects³ Drawing
+#define TXT_DEBUG_AIRCRAFT 0x40e // ÚFull
+#define TXT_DEBUG_HOUSE 0x40f // ÚFull Name:ÄÄÄÄÄÄÄÄÂAct
+#define TXT_NO_PENTIUM 0x410 // ****************************
+#define TXT_SIZE_MAP 0x411 // Size Map
+#define TXT_TRUCK_CRATE 0x412 // Trucks drop crate when
diff --git a/CODE/DEFINES.H b/CODE/DEFINES.H
new file mode 100644
index 0000000..de0009b
--- /dev/null
+++ b/CODE/DEFINES.H
@@ -0,0 +1,3555 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/DEFINES.H 4 3/07/97 9:55a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : DEFINES.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : September 10, 1993 *
+ * *
+ * Last Update : September 10, 1993 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+#ifndef DEFINES_H
+#define DEFINES_H
+
+/**********************************************************************
+** Language control: define the desired language for this build.
+*/
+//#define ENGLISH 1
+//#define FRENCH 1
+//#define GERMAN 1
+//#define SPAIN 1 (never used)
+// - Language define is now passed in from the makefile. -
+
+/**********************************************************************
+** Controls the nature of the game and its abilities. Only define
+** one of these values.
+**
+** Internal version -- complete with scenario editor.
+** Playtest version -- no editor but does have minimal cheat keys.
+** Release version -- no editor or cheat keys -- all debugging info removed.
+*/
+//#define INTERNAL_VERSION
+//#define PLAYTEST_VERSION
+#define RELEASE_VERSION
+
+/**********************************************************************
+** ColinM
+** Set this to enable dongle protection
+*/
+//#define DONGLE
+
+
+// Enable 640x400 VQ movie capability in WIN32 mode
+#define MOVIE640
+
+
+//#if (GERMAN | FRENCH)
+//#define BOGUSCD
+//#endif
+
+#define FIXIT_SCORE_CRASH // Fixes score screen crash
+#define FIXIT_MULTI_SAVE // Fixes multiplayer save/load
+#define FIXIT_NO_COMP_ALLY // Prevent ally with computer
+#define FIXIT_DESTNET // Fixes -destnet parameter in Win95
+#define FIXIT_RANDOM_GAME // Fixes random seed at start of multiplayer games
+#define FIXIT_FORCE_CD // Forces correct CD load after scenario #1
+#define FIXIT_IP_CRASH // Fixes crash if internet game aborts too quickly
+#define FIXIT_IP_STATS // Fixes so vessels show up in internet stat info
+#define FIXIT_NAME_OVERRIDE // Allows changing of unit names
+#define FIXIT_RADAR_JAMMED // Fixes unjamming by merely starting to build a radar facility
+#define FIXIT_CAPTURE_BIB // Fixes so that if fake is captured, you still can't build off of it.
+#define FIXIT_BASE_ANNOUNCE // Fixes so player controlled buildings count as base when attacked.
+#define FIXIT_APTIVA_MODEM // Fixes crash with Aptiva modem.
+#define FIXIT_FLAG_CHECK // Disable placing building over a flag.
+
+#define FIXIT_ANTS // Adds Ant Units
+
+#define FIXIT_CSII // Adds Aftermath CounterStrike II units
+// ajw 9/28/98 - Note about FIXIT_CSII. Changes seem to have been made for Aftermath ("Counterstrike II") that: a) were
+// bug fixes that should never be rolled back, b) change the nature of the game, at least in multi-player. This meant
+// that the "Red Alert" executable ( == Counterstrike executable ) could no longer be built. Apparently, at the time,
+// this was justified, as it was believed that no further patches to the RA executable would ever be necessary.
+// Given that Denzil's DVD changes and my WOLAPI integration are essentially a patch, we've got a problem.
+// We've decided to level the field and make sure every who gets or patches to the new version of Red Alert, CS, AM, (and
+// their DVD equivalent(s)) will have the same executable. So we're assuming that all of the FIXIT_CSII changes are
+// permanent (as, in fact, all prior FIXIT_'s are - makes me wonder why the old non-compiling code has to hang around
+// forever), and fixing the code so that the assumption "this is an Aftermath game" is no longer hard-coded, but can
+// change at runtime. (Which is what should have been done when Aftermath was created.)
+//
+#define FIXIT_CARRIER // Adds Aftermath aircraft carrier
+#define FIXIT_PHASETRANSPORT // Adds Aftermath cloaking APC
+// ajw - Discovered that engineer changing fields were specifically left out of aftrmath.ini, thus this has no effect.
+// Engineer changes (and other game rule changes) are in mplayer.ini, which was loaded before aftermath-only mplayer games.
+#define FIXIT_ENGINEER // Adds Engineer rules.ini overrides
+
+//#define FIXIT_FAST_LOAD // Enables faster INI loading
+
+// These fixes will cause the game to go out of sync.
+//#define FIXIT_ENGINEER_CAPTURE // If building not allied, will still capture if engineer not allied with building.
+//#define FIXIT_HELI_LANDING // Fixes so new helicopters land at free helipad
+//#define FIXIT_MINE_PASSABLE // Fixes units not driving onto mines
+
+/* Turn on these changes for the 1.08 patch */
+#define FIXIT_PATCH_108
+
+#ifdef FIXIT_PATCH_108
+#define STEVES_LOAD_OVERRIDE // Allows loading of CONQUER.ENG instead of from mix file.
+#define FIXIT_DIFFICULTY // Fixes no difficulty level for CStrike missions
+#define FIXIT_VERSION // Fixes version playability for 1.04, 1.07 & 1.08
+#define FIXIT_MODEM_LOAD_CRASH // Fixes crash after loading a modem game when names are the same
+#define FIXIT_PHONELIST_CRASH // Fixes crash when clicking on an empty phonelist
+#endif
+
+// Denotes changes made for version 3 - reunification of all existing versions and undoing of Aftermath divergence. - ajw
+#define FIXIT_VERSION_3
+#define DVD
+
+// Define DVD to turn on RADVD additions/changes - Denzil
+#ifdef DVD
+//#define INTERNET_OFF
+#define MPEGMOVIE
+//#define MCIMPEG
+#endif
+
+// Test to see if partial object drawing is any faster.
+#define PARTIAL
+//#define SORTDRAW
+
+/**********************************************************************
+** If the scenario editor to to be active in this build then uncomment
+** the following #define line.
+*/
+#ifdef INTERNAL_VERSION
+#define SCENARIO_EDITOR
+#endif
+
+
+/**********************************************************************
+** This define enables the full set of cheat keys and special
+** command line options.
+*/
+#if defined(INTERNAL_VERSION) || defined(PLAYTEST_VERSION)
+#define CHEAT_KEYS
+#endif
+
+
+/**********************************************************************
+** If this is defined, the special Virgin limited cheat keys
+** are enabled. This allows the "cheat" parameter and then only
+** allows the ALT-W to win the mission.
+*/
+#ifdef PLAYTEST_VERSION
+#define VIRGIN_CHEAT_KEYS
+#endif
+
+
+/**********************************************************************
+** If this is defined, then the network code will be enabled.
+*/
+#define NETWORK
+#define TIMING_FIX 1
+
+
+/**********************************************************************
+** Define this to 1 to enable MPath-specific code. Do not define
+** TEN at the same time.
+*/
+#define MPATH 0
+
+
+/**********************************************************************
+** Define this to 1 to enable TEN-specific code. Do not define
+** MPATH at the same time.
+*/
+#define TEN 0
+
+
+/**********************************************************************
+** If this is defined, the DoList is "mirrored", for memory trasher
+** detection.
+*/
+#ifdef CHEAT_KEYS
+//#define MIRROR_QUEUE
+#endif
+
+
+/**********************************************************************
+** This define tells the Version Number class to use the date/time-based
+** version numbering system. If this define is not set, the actual
+** major/minor version numbers will be used.
+*/
+//#define DEV_VERSION
+//#define DEV_VER_NAME
+
+
+/**********************************************************************
+** This define enables a special additional foreign-version-number
+** after the other version number, for display purposes only.
+*/
+#if !defined(ENGLISH)
+#define FOREIGN_VERSION
+#define FOREIGN_VERSION_NUMBER 7
+#endif
+
+
+/**********************************************************************
+** This is the multiplier factor to convert low resution coordinates
+** into their actual resolution counterparts.
+*/
+#ifdef WIN32
+#define RESFACTOR 2
+#else
+//#undef SCENARIO_EDITOR
+#define RESFACTOR 1
+#endif
+
+
+#define SIDEBAR_WID 80
+
+
+/**********************************************************************
+** Optional parameter control for special options.
+*/
+
+/*
+** Enable the set of limited cheat key options.
+*/
+#ifdef VIRGIN_CHEAT_KEYS
+#define PARM_PLAYTEST 0xF7DDC227 // "PLAYTEST"
+#endif
+
+/*
+** Enable the full set of cheat key options.
+*/
+#ifdef CHEAT_KEYS
+#ifndef PARM_PLAYTEST
+#define PARM_PLAYTEST 0xF7DDC227 // "PLAYTEST"
+#endif
+#endif
+
+
+#define PARM_INSTALL 0xD95C68A2 // "FROMINSTALL"
+
+
+//
+// Allow normal game play in the MPath version
+//
+#if(MPATH)
+#define PARM_ALLOW_SOLO 0xc901c9db // AllowSoloPlayOptions
+#endif
+
+//
+// Allow normal game play in the TEN version
+//
+#if(TEN)
+#define PARM_ALLOW_SOLO 0xc901c9db // AllowSoloPlayOptions
+#endif
+
+/**********************************************************************
+** Defines for verifying free disk space
+*/
+#define INIT_FREE_DISK_SPACE 8388608
+#define SAVE_GAME_DISK_SPACE (INIT_FREE_DISK_SPACE - (1024*4096))
+//#define SAVE_GAME_DISK_SPACE 100000
+
+
+/**********************************************************************
+** This is the complete list of VQs allowed to be played in the game.
+*/
+typedef enum VQType {
+ VQ_NONE=-1,
+ VQ_AAGUN,
+ VQ_MIG,
+ VQ_SFROZEN,
+ VQ_AIRFIELD,
+ VQ_BATTLE,
+ VQ_BMAP,
+ VQ_BOMBRUN,
+ VQ_DPTHCHRG,
+ VQ_GRVESTNE,
+ VQ_MONTPASS,
+ VQ_MTNKFACT,
+ VQ_CRONTEST,
+ VQ_OILDRUM,
+ VQ_ALLYEND,
+ VQ_RADRRAID,
+ VQ_SHIPYARD,
+ VQ_SHORBOMB,
+ VQ_SITDUCK,
+ VQ_SLNTSRVC,
+ VQ_SNOWBASE,
+ VQ_EXECUTE,
+ VQ_TITLE, // Low res.
+ VQ_NUKESTOK,
+ VQ_V2ROCKET,
+ VQ_SEARCH,
+ VQ_BINOC,
+ VQ_ELEVATOR,
+ VQ_FROZEN,
+ VQ_MCV,
+ VQ_SHIPSINK,
+ VQ_SOVMCV,
+ VQ_TRINITY,
+ VQ_ALLYMORF,
+ VQ_APCESCPE,
+ VQ_BRDGTILT,
+ VQ_CRONFAIL,
+ VQ_STRAFE,
+ VQ_DESTROYR,
+ VQ_DOUBLE,
+ VQ_FLARE,
+ VQ_SNSTRAFE,
+ VQ_LANDING,
+ VQ_ONTHPRWL,
+ VQ_OVERRUN,
+ VQ_SNOWBOMB,
+ VQ_SOVCEMET,
+ VQ_TAKE_OFF,
+ VQ_TESLA,
+ VQ_SOVIET8,
+ VQ_SPOTTER,
+ VQ_SCENE1,
+ VQ_SCENE2,
+ VQ_SCENE4,
+ VQ_SOVFINAL,
+ VQ_ASSESS,
+ VQ_SOVIET10,
+ VQ_DUD,
+ VQ_MCV_LAND,
+ VQ_MCVBRDGE,
+ VQ_PERISCOP,
+ VQ_SHORBOM1,
+ VQ_SHORBOM2,
+ VQ_SOVBATL,
+ VQ_SOVTSTAR,
+ VQ_AFTRMATH,
+ VQ_SOVIET11,
+ VQ_MASASSLT,
+ VQ_REDINTRO, // High res
+ VQ_SOVIET1,
+ VQ_SOVIET2,
+ VQ_SOVIET3,
+ VQ_SOVIET4,
+ VQ_SOVIET5,
+ VQ_SOVIET6,
+ VQ_SOVIET7,
+ VQ_INTRO_MOVIE,
+ VQ_AVERTED,
+ VQ_COUNTDWN,
+ VQ_MOVINGIN,
+ VQ_ALLIED10,
+ VQ_ALLIED12,
+ VQ_ALLIED5,
+ VQ_ALLIED6,
+ VQ_ALLIED8,
+ VQ_TANYA1,
+ VQ_TANYA2,
+ VQ_ALLY10B,
+ VQ_ALLY11,
+ VQ_ALLY14,
+ VQ_ALLY9,
+ VQ_SPY,
+ VQ_TOOFAR,
+ VQ_SOVIET12,
+ VQ_SOVIET13,
+ VQ_SOVIET9,
+ VQ_BEACHEAD,
+ VQ_SOVIET14,
+ VQ_SIZZLE,
+ VQ_SIZZLE2,
+ VQ_ANTEND,
+ VQ_ANTINTRO,
+
+ VQ_COUNT,
+ VQ_FIRST=0
+} VQType;
+
+
+/**********************************************************************
+** These enumerations are used to implement RTTI. The target system
+** uses these and thus there can be no more RTTI types than can fit
+** in the exponent of a target value.
+*/
+typedef enum RTTIType {
+ RTTI_NONE=0,
+ RTTI_AIRCRAFT,
+ RTTI_AIRCRAFTTYPE,
+ RTTI_ANIM,
+ RTTI_ANIMTYPE,
+ RTTI_BUILDING,
+ RTTI_BUILDINGTYPE,
+ RTTI_BULLET,
+ RTTI_BULLETTYPE,
+ RTTI_CELL,
+ RTTI_FACTORY,
+ RTTI_HOUSE,
+ RTTI_HOUSETYPE,
+ RTTI_INFANTRY,
+ RTTI_INFANTRYTYPE,
+ RTTI_OVERLAY,
+ RTTI_OVERLAYTYPE,
+ RTTI_SMUDGE,
+ RTTI_SMUDGETYPE,
+ RTTI_SPECIAL,
+ RTTI_TEAM,
+ RTTI_TEAMTYPE,
+ RTTI_TEMPLATE,
+ RTTI_TEMPLATETYPE,
+ RTTI_TERRAIN,
+ RTTI_TERRAINTYPE,
+ RTTI_TRIGGER,
+ RTTI_TRIGGERTYPE,
+ RTTI_UNIT,
+ RTTI_UNITTYPE,
+ RTTI_VESSEL,
+ RTTI_VESSELTYPE,
+
+ RTTI_COUNT
+} RTTIType;
+
+
+/**********************************************************************
+** These are the difficulty settings of the game.
+*/
+typedef enum DiffType {
+ DIFF_EASY,
+ DIFF_NORMAL,
+ DIFF_HARD,
+
+ DIFF_COUNT,
+ DIFF_FIRST=0
+} DiffType;
+
+
+/**********************************************************************
+** This is the size of the speech buffer. This value should be as large
+** as the largest speech sample, plus a few bytes for overhead
+** (16 bytes is sufficient).
+*/
+#define SPEECH_BUFFER_SIZE 50000L
+
+
+/**********************************************************************
+** The theater mixfiles are cached into a buffer of this size. Ensure
+** that the size specified is at least as large as the largest
+** theater mixfile data block.
+*/
+#define THEATER_BUFFER_SIZE 1100000L
+
+
+/**********************************************************************
+** This is the size of the shape buffer. This buffer is used as a staging
+** buffer for the shape drawing technology. It MUST be as big as the
+** largest shape (uncompressed) that will be drawn. If this value is
+** changed, be sure to update the makefile and rebuild all of the shape
+** data files.
+*/
+#define SHAPE_BUFFER_SIZE 65000L
+
+
+/**********************************************************************
+** Filenames of the data files it can create at run time.
+*/
+#define FAME_FILE_NAME "HALLFAME.DAT"
+#define NET_SAVE_FILE_NAME "SAVEGAME.NET"
+#define CONFIG_FILE_NAME "REDALERT.INI"
+
+
+/**********************************************************************
+** Map controls. The map is composed of square elements called 'cells'.
+** All larger elements are build upon these.
+*/
+
+#define HIGH_COORD_MASK 0x80008000L
+
+// Size of the map in cells.
+#define MAP_CELL_W 128
+#define MAP_CELL_H 128
+#define MAP_CELL_TOTAL (MAP_CELL_W*MAP_CELL_H)
+
+#define REFRESH_EOL 32767 // This number ends a refresh/occupy offset list.
+#define REFRESH_SIDEBAR 32766 // This number flags that sidebar needs refreshing.
+
+
+/****************************************************************************
+** These are custom C&C specific types. The CELL is used for map coordinate
+** with cell resolution. The COORDINATE type is used for map coordinates that
+** have a lepton resolution. CELL is more efficient when indexing into the map
+** and when size is critical. COORDINATE is more efficient when dealing with
+** accuracy and object movement.
+*/
+typedef unsigned short LEPTON;
+typedef union {
+ LEPTON Raw;
+ struct {
+#ifdef BIG_ENDIAN
+ unsigned char Cell;
+ unsigned char Lepton;
+#else
+ unsigned char Lepton;
+ unsigned char Cell;
+#endif
+ } Sub;
+} LEPTON_COMPOSITE;
+
+typedef unsigned long COORDINATE;
+typedef union {
+ COORDINATE Coord;
+ struct {
+#ifdef BIG_ENDIAN
+ LEPTON_COMPOSITE Y;
+ LEPTON_COMPOSITE X;
+#else
+ LEPTON_COMPOSITE X;
+ LEPTON_COMPOSITE Y;
+#endif
+ } Sub;
+} COORD_COMPOSITE;
+
+typedef signed short CELL;
+#define SLUFF_BITS (sizeof(CELL)*CHAR_BIT)-(14)
+typedef union {
+ CELL Cell;
+ struct {
+#ifdef BIG_ENDIAN
+#if SLUFF_BITS
+ /*
+ ** Unused upper bits will cause problems on a big-endian machine unless they
+ ** are deliberately accounted for.
+ */
+ unsigned sluff:SLUF_BITS;
+#endif
+ unsigned Y:7;
+ unsigned X:7;
+#else
+ unsigned X:7;
+ unsigned Y:7;
+#endif
+ } Sub;
+} CELL_COMPOSITE;
+
+typedef int WAYPOINT;
+
+
+/**********************************************************************
+** This is the target composit information. Notice that with an RTTI_NONE
+** and an index value of 0, the target value returned is identical with
+** TARGET_NONE. This is by design and is necessary.
+*/
+typedef long TARGET;
+
+#define TARGET_MANTISSA 24 // Bits of value precision.
+#define TARGET_EXPONENT 8
+typedef union {
+ TARGET Target;
+ struct {
+#ifdef BIG_ENDIAN
+ unsigned Exponent:TARGET_EXPONENT;
+ unsigned Mantissa:TARGET_MANTISSA;
+#else
+ unsigned Mantissa:TARGET_MANTISSA;
+ unsigned Exponent:TARGET_EXPONENT;
+#endif
+ } Sub;
+} TARGET_COMPOSITE;
+
+
+inline TARGET Build_Target(RTTIType kind, int value)
+{
+ TARGET_COMPOSITE target;
+
+ target.Target = 0;
+ target.Sub.Exponent = kind;
+ target.Sub.Mantissa = value;
+ return(target.Target);
+}
+
+
+#define TARGET_NONE ((TARGET)0)
+
+
+/*
+** The map is broken down into regions of this specified dimensions.
+*/
+#define REGION_WIDTH 4
+#define REGION_HEIGHT 4
+#define MAP_REGION_WIDTH (((MAP_CELL_W + (REGION_WIDTH -1)) / REGION_WIDTH)+2)
+#define MAP_REGION_HEIGHT (((MAP_CELL_H + (REGION_WIDTH -1)) / REGION_HEIGHT)+2)
+#define MAP_TOTAL_REGIONS (MAP_REGION_WIDTH * MAP_REGION_HEIGHT)
+
+
+/**********************************************************************
+** This enumerates the various known fear states for infantry units.
+** At these stages, certain events or recovery actions are performed.
+*/
+typedef enum FearType {
+ FEAR_NONE=0, // No fear at all (default state).
+ FEAR_ANXIOUS=10, // Something makes them scared.
+ FEAR_SCARED=100, // Scared enough to take cover.
+ FEAR_PANIC=200, // Run away! Run away!
+ FEAR_MAXIMUM=255 // Scared to death.
+} FearType;
+
+
+/**********************************************************************
+** When a moving object moves, the Per_Cell_Process function is called
+** at various times during the move. Certain operations must be
+** performed at different stages of the move. This enum specifies the
+** different conditions under which the Per_Cell_Process function is
+** called.
+*/
+typedef enum PCPType {
+ PCP_ROTATION, // When sitting in place and performing rotations.
+ PCP_DURING, // While moving between two cells.
+ PCP_END, // When the 'center' of a cell is reached during movement.
+} PCPType;
+
+
+/**********************************************************************
+** A base is broken up into several zones. This type enumerates the
+** various zones.
+*/
+typedef enum ZoneType {
+ ZONE_CORE, // Center of base.
+ ZONE_NORTH, // North section.
+ ZONE_EAST, // East section.
+ ZONE_SOUTH, // South section.
+ ZONE_WEST, // West section.
+
+ ZONE_COUNT,
+ ZONE_FIRST=0,
+ ZONE_NONE=-1
+} ZoneType;
+
+
+/**********************************************************************
+** The map is prescanned to mark of movement zones according to certain
+** movement characteristics. This enum specifies those characteristics
+** and movement zones kept track of.
+*/
+typedef enum MZoneType {
+ MZONE_NORMAL, // Normal terrestrial objects (can't crush walls).
+ MZONE_CRUSHER, // Can crush crushable wall types.
+ MZONE_DESTROYER, // Can destroy walls.
+ MZONE_WATER, // Water based objects.
+
+ MZONE_COUNT,
+ MZONE_FIRST=0
+} MZoneType;
+
+#define MZONEF_NORMAL (1<APC or vehicle->Repair facility.
+ ACTION_SELF, // Self select special case.
+ ACTION_ATTACK, // Can attack or fire upon it in some fashion.
+ ACTION_HARVEST, // Special harvest mode.
+ ACTION_SELECT, // Would change selection to specified object.
+ ACTION_TOGGLE_SELECT,// Toggles select state of the object.
+ ACTION_CAPTURE, // The unit will try to capture the object.
+ ACTION_REPAIR, // The target object should be repaired.
+ ACTION_SELL, // The target building should be sold back.
+ ACTION_SELL_UNIT, // The target unit should be sold back.
+ ACTION_NO_SELL, // No sell or no repair.
+ ACTION_NO_REPAIR, // No sell or no repair.
+ ACTION_SABOTAGE, // The unit will try to sabotage/destroy the object.
+ ACTION_PARA_BOMB, // Parachute bomb strike.
+ ACTION_PARA_INFANTRY,// Parachute infantry strike.
+ ACTION_PARA_SABOTEUR,// Parachute saboteur strike.
+ ACTION_NUKE_BOMB, // That target object should be blasted.
+ ACTION_AIR_STRIKE, // That target object should be blasted.
+ ACTION_CHRONOSPHERE, // That target object should be teleported.
+ ACTION_CHRONO2, // Teleport it to the given coordinates now.
+ ACTION_IRON_CURTAIN, // That target object should be invulnerable.
+ ACTION_SPY_MISSION, // Photo recon mission.
+ ACTION_GUARD_AREA, // Guard the area/object clicked on.
+ ACTION_HEAL, // Heal the infantryman clicked on.
+ ACTION_DAMAGE, // Enter and damage building.
+ ACTION_GREPAIR, // Enter and complete repair building.
+ ACTION_NO_DEPLOY,
+ ACTION_NO_ENTER,
+ ACTION_NO_GREPAIR,
+
+ ACTION_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;
+
+
+#ifdef OBSOLETE
+/**********************************************************************
+** These are the special concrete control defines. They enumerate the
+** sequence order of the concrete icons in the concrete art file.
+*/
+// DEBUG === convert this to be zero based so that a nulled cell is the
+// default cell.
+enum ConcreteEnum {
+ C_NONE=-1,
+ C_LEFT=0,
+ C_RIGHT=1,
+ C_RIGHT_UPDOWN=2,
+ C_LEFT_UPDOWN=3,
+ C_UP_RIGHT=4,
+ C_UP_LEFT=5,
+ C_DOWN_RIGHT=6,
+ C_DOWN_LEFT=7,
+ C_RIGHT_DOWN=8,
+ C_LEFT_DOWN=9,
+ C_RIGHT_UP=10,
+ C_LEFT_UP=11,
+ C_UPDOWN_RIGHT=12,
+ C_UPDOWN_LEFT=13
+};
+#endif
+
+
+/**********************************************************************
+** Units that move can move at different speeds. These enumerate the
+** different speeds that a unit can move.
+*/
+typedef enum MPHType{
+ MPH_IMMOBILE=0,
+ MPH_VERY_SLOW=5, // 2
+ MPH_KINDA_SLOW=6, // 3
+ MPH_SLOW=8, // 4
+ MPH_SLOW_ISH=10, // 5
+ MPH_MEDIUM_SLOW=12, // 6
+ MPH_MEDIUM=18, // 9
+ MPH_MEDIUM_FAST=30, // 12
+ MPH_MEDIUM_FASTER=35, // 14
+ MPH_FAST=40, // 16
+ MPH_ROCKET=60, // 24
+ MPH_VERY_FAST=100, // 40
+ MPH_LIGHT_SPEED=255 // 100
+} MPHType;
+
+
+/**********************************************************************
+** The houses that can be played are listed here. Each has their own
+** personality and strengths.
+*/
+typedef enum HousesType {
+ HOUSE_NONE=-1,
+ HOUSE_SPAIN, // Gold (unremapped)
+ HOUSE_GREECE, // LtBlue
+ HOUSE_USSR, // Red
+ HOUSE_ENGLAND, // Green
+ HOUSE_UKRAINE, // Orange
+ HOUSE_GERMANY, // Grey
+ HOUSE_FRANCE, // Blue
+ HOUSE_TURKEY, // Brown
+ HOUSE_GOOD, // Global Defense Initiative
+ HOUSE_BAD, // Brotherhood of Nod
+ HOUSE_NEUTRAL, // Civilians
+ HOUSE_JP, // Disaster Containment Team
+ HOUSE_MULTI1, // Multi-Player house #1
+ HOUSE_MULTI2, // Multi-Player house #2
+ HOUSE_MULTI3, // Multi-Player house #3
+ HOUSE_MULTI4, // Multi-Player house #4
+ HOUSE_MULTI5, // Multi-Player house #5
+ HOUSE_MULTI6, // Multi-Player house #6
+ HOUSE_MULTI7, // Multi-Player house #7
+ HOUSE_MULTI8, // Multi-Player house #8
+ HOUSE_COUNT,
+ HOUSE_FIRST=0
+} HousesType;
+
+inline HousesType operator++(HousesType &, int);
+
+#define HOUSEF_ALLIES (HOUSEF_ENGLAND|HOUSEF_SPAIN|HOUSEF_GREECE|HOUSEF_GERMANY|HOUSEF_FRANCE|HOUSEF_TURKEY|HOUSEF_GOOD)
+#define HOUSEF_SOVIET (HOUSEF_USSR|HOUSEF_UKRAINE|HOUSEF_BAD)
+#define HOUSEF_OTHERS (HOUSEF_NEUTRAL|HOUSEF_JP|HOUSEF_MULTI1|HOUSEF_MULTI2|HOUSEF_MULTI3|HOUSEF_MULTI4|HOUSEF_MULTI5|HOUSEF_MULTI6|HOUSEF_MULTI7|HOUSEF_MULTI8)
+#define HOUSEF_NONE 0
+
+#define HOUSEF_ENGLAND (1L< 2 players.
+*/
+typedef enum ScenarioPlayerEnum
+{
+ SCEN_PLAYER_NONE = -1,
+ SCEN_PLAYER_SPAIN,
+ SCEN_PLAYER_GREECE,
+ SCEN_PLAYER_USSR,
+ SCEN_PLAYER_JP,
+ SCEN_PLAYER_2PLAYER,
+ SCEN_PLAYER_MPLAYER,
+ SCEN_PLAYER_COUNT,
+ SCEN_PLAYER_FIRST = 0
+} ScenarioPlayerType;
+
+inline ScenarioPlayerType operator++(ScenarioPlayerType &, int);
+
+
+/**********************************************************************
+** These are the directional parameters for a scenario.
+*/
+typedef enum ScenarioDirEnum
+{
+ 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_SURFACE, // Flat on the ground (no sorting or apparent vertical height).
+ LAYER_GROUND, // Touching the ground type object (units & buildings).
+ LAYER_AIR, // Flying above the ground (explosions & flames).
+ LAYER_TOP, // Topmost layer (aircraft & bullets).
+
+ LAYER_COUNT,
+ LAYER_FIRST=0
+} LayerType;
+
+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_INVISIBLE,
+ BULLET_CANNON,
+ BULLET_ACK,
+ BULLET_TORPEDO,
+ BULLET_FROG,
+ BULLET_HEAT_SEEKER,
+ BULLET_LASER_GUIDED,
+ BULLET_LOBBED,
+ BULLET_BOMBLET,
+ BULLET_BALLISTIC,
+ BULLET_PARACHUTE,
+ BULLET_FIREBALL,
+ BULLET_DOG,
+ BULLET_CATAPULT,
+ BULLET_AAMISSILE,
+ BULLET_GPS_SATELLITE,
+ BULLET_NUKE_UP,
+ BULLET_NUKE_DOWN,
+
+ BULLET_COUNT,
+ BULLET_FIRST=0
+} BulletType;
+
+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_ADVANCED_TECH,
+ STRUCT_IRON_CURTAIN,
+ STRUCT_WEAP,
+ STRUCT_CHRONOSPHERE,
+ STRUCT_PILLBOX,
+ STRUCT_CAMOPILLBOX,
+ STRUCT_RADAR,
+ STRUCT_GAP,
+ STRUCT_TURRET,
+ STRUCT_AAGUN,
+ STRUCT_FLAME_TURRET,
+ STRUCT_CONST,
+ STRUCT_REFINERY,
+ STRUCT_STORAGE,
+ STRUCT_HELIPAD,
+ STRUCT_SAM,
+ STRUCT_AIRSTRIP,
+ STRUCT_POWER,
+ STRUCT_ADVANCED_POWER,
+ STRUCT_SOVIET_TECH,
+ STRUCT_HOSPITAL,
+ STRUCT_BARRACKS,
+ STRUCT_TENT,
+ STRUCT_KENNEL,
+ STRUCT_REPAIR,
+ STRUCT_BIO_LAB,
+ STRUCT_MISSION,
+ STRUCT_SHIP_YARD,
+ STRUCT_SUB_PEN,
+ STRUCT_MSLO,
+ STRUCT_FORWARD_COM,
+ STRUCT_TESLA,
+
+ /*
+ ** All buildings that are never used as a prerequisite
+ ** for construction, follow this point. Typically, this is
+ ** limited to civilian structures. Also, the following
+ ** buildings are NEVER used in the availability bit field
+ ** record that each house maintains. i.e., STRUCTF_????
+ ** bit checking will never occur with the following
+ ** building types.
+ */
+ STRUCT_FAKEWEAP,
+ STRUCT_FAKECONST,
+ STRUCT_FAKE_YARD,
+ STRUCT_FAKE_PEN,
+ STRUCT_FAKE_RADAR,
+
+ STRUCT_SANDBAG_WALL,
+ STRUCT_CYCLONE_WALL,
+ STRUCT_BRICK_WALL,
+ STRUCT_BARBWIRE_WALL,
+ STRUCT_WOOD_WALL,
+ STRUCT_FENCE,
+
+ STRUCT_AVMINE,
+ STRUCT_APMINE,
+ STRUCT_V01,
+ STRUCT_V02,
+ STRUCT_V03,
+ STRUCT_V04,
+ STRUCT_V05,
+ STRUCT_V06,
+ STRUCT_V07,
+ STRUCT_V08,
+ STRUCT_V09,
+ STRUCT_V10,
+ STRUCT_V11,
+ STRUCT_V12,
+ STRUCT_V13,
+ STRUCT_V14,
+ STRUCT_V15,
+ STRUCT_V16,
+ STRUCT_V17,
+ STRUCT_V18,
+ STRUCT_PUMP,
+ STRUCT_V20,
+ STRUCT_V21,
+ STRUCT_V22,
+ STRUCT_V23,
+ STRUCT_V24,
+ STRUCT_V25,
+ STRUCT_V26,
+ STRUCT_V27,
+ STRUCT_V28,
+ STRUCT_V29,
+ STRUCT_V30,
+ STRUCT_V31,
+ STRUCT_V32,
+ STRUCT_V33,
+ STRUCT_V34,
+ STRUCT_V35,
+ STRUCT_V36,
+ STRUCT_V37,
+ STRUCT_BARREL,
+ STRUCT_BARREL3,
+
+#ifdef FIXIT_ANTS
+ STRUCT_QUEEN,
+ STRUCT_LARVA1,
+ STRUCT_LARVA2,
+#endif
+
+ STRUCT_COUNT,
+ STRUCT_FIRST=0
+} StructType;
+
+inline StructType operator++(StructType &, int);
+
+#define STRUCTF_NONE 0L
+#define STRUCTF_ADVANCED_TECH (1L << STRUCT_ADVANCED_TECH)
+#define STRUCTF_IRON_CURTAIN (1L << STRUCT_IRON_CURTAIN)
+#define STRUCTF_WEAP (1L << STRUCT_WEAP)
+#define STRUCTF_CHRONOSPHERE (1L << STRUCT_CHRONOSPHERE)
+#define STRUCTF_PILLBOX (1L << STRUCT_PILLBOX)
+#define STRUCTF_CAMOPILLBOX (1L << STRUCT_CAMOPILLBOX)
+#define STRUCTF_RADAR (1L << STRUCT_RADAR)
+#define STRUCTF_GAP (1L << STRUCT_GAP)
+#define STRUCTF_TURRET (1L << STRUCT_TURRET)
+#define STRUCTF_AAGUN (1L << STRUCT_AAGUN)
+#define STRUCTF_FLAME_TURRET (1L << STRUCT_FLAME_TURRET)
+#define STRUCTF_CONST (1L << STRUCT_CONST)
+#define STRUCTF_REFINERY (1L << STRUCT_REFINERY)
+#define STRUCTF_STORAGE (1L << STRUCT_STORAGE)
+#define STRUCTF_HELIPAD (1L << STRUCT_HELIPAD)
+#define STRUCTF_SAM (1L << STRUCT_SAM)
+#define STRUCTF_AIRSTRIP (1L << STRUCT_AIRSTRIP)
+#define STRUCTF_POWER (1L << STRUCT_POWER)
+#define STRUCTF_ADVANCED_POWER (1L << STRUCT_ADVANCED_POWER)
+#define STRUCTF_SOVIET_TECH (1L << STRUCT_SOVIET_TECH)
+#define STRUCTF_HOSPITAL (1L << STRUCT_HOSPITAL)
+#define STRUCTF_BARRACKS (1L << STRUCT_BARRACKS)
+#define STRUCTF_TENT (1L << STRUCT_TENT)
+#define STRUCTF_KENNEL (1L << STRUCT_KENNEL)
+#define STRUCTF_REPAIR (1L << STRUCT_REPAIR)
+#define STRUCTF_BIO_LAB (1L << STRUCT_BIO_LAB)
+#define STRUCTF_MISSION (1L << STRUCT_MISSION)
+#define STRUCTF_SHIP_YARD (1L << STRUCT_SHIP_YARD)
+#define STRUCTF_SUB_PEN (1L << STRUCT_SUB_PEN)
+#define STRUCTF_MSLO (1L << STRUCT_MSLO)
+#define STRUCTF_FAKECONST (1L << STRUCT_FAKECONST)
+#define STRUCTF_FAKEWEAP (1L << STRUCT_FAKEWEAP)
+
+
+/**********************************************************************
+** The overlays are enumerated here. An overlay functions similarly to
+** a transparent icon. It is placed over the terrain but usually falls
+** "under" buildings, trees, and units.
+*/
+typedef enum OverlayType {
+ OVERLAY_NONE=-1,
+ OVERLAY_SANDBAG_WALL, // Piled sandbags.
+ OVERLAY_CYCLONE_WALL, // Chain-link fence.
+ OVERLAY_BRICK_WALL, // Solid concrete wall.
+ OVERLAY_BARBWIRE_WALL, // Barbed-wire wall.
+ OVERLAY_WOOD_WALL, // Wooden fence.
+ OVERLAY_GOLD1,
+ OVERLAY_GOLD2,
+ OVERLAY_GOLD3,
+ OVERLAY_GOLD4,
+ OVERLAY_GEMS1,
+ OVERLAY_GEMS2,
+ OVERLAY_GEMS3,
+ OVERLAY_GEMS4,
+ OVERLAY_V12, // Haystacks
+ OVERLAY_V13, // Haystack
+ OVERLAY_V14, // Wheat field
+ OVERLAY_V15, // Fallow field
+ OVERLAY_V16, // Corn field
+ OVERLAY_V17, // Celery field
+ OVERLAY_V18, // Potato field
+ OVERLAY_FLAG_SPOT, // Flag start location.
+ OVERLAY_WOOD_CRATE, // Wooden goodie crate.
+ OVERLAY_STEEL_CRATE, // Steel goodie crate.
+ OVERLAY_FENCE, // New fangled fence.
+ OVERLAY_WATER_CRATE, // Water goodie crate.
+
+ OVERLAY_COUNT,
+ OVERLAY_FIRST=0
+} OverlayType;
+
+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_RENOVATOR, // Engineer.
+ INFANTRY_TANYA, // Saboteur.
+ INFANTRY_SPY, // Spy.
+ INFANTRY_THIEF, // Thief.
+ INFANTRY_MEDIC, // Field Medic.
+ INFANTRY_GENERAL, // Field Marshal.
+ INFANTRY_DOG, // Soviet attack dog
+
+ INFANTRY_C1, // Civilian
+ INFANTRY_C2, // Civilian
+ INFANTRY_C3, // Civilian
+ INFANTRY_C4, // Civilian
+ INFANTRY_C5, // Civilian
+ INFANTRY_C6, // Civilian
+ INFANTRY_C7, // Civilian
+ INFANTRY_C8, // Civilian
+ INFANTRY_C9, // Civilian
+ INFANTRY_C10, // Nikumba
+ INFANTRY_EINSTEIN, // Einstein
+ INFANTRY_DELPHI, // Agent "Delphi"
+ INFANTRY_CHAN, // Dr. Chan
+
+#ifdef FIXIT_CSII // checked - ajw 9/28/98
+// CounterStrike II only!
+ INFANTRY_SHOCK, // Shock Trooper
+ INFANTRY_MECHANIC,
+#endif
+
+ INFANTRY_COUNT,
+ INFANTRY_FIRST=0
+#ifdef FIXIT_CSII // checked - ajw 9/28/98
+,
+ INFANTRY_RA_COUNT = INFANTRY_SHOCK
+#endif
+} InfantryType;
+
+#define INFANTRYF_DOG (1L << INFANTRY_DOG)
+
+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, // Mammoth tank.
+ UNIT_MTANK, // Heavy tank.
+ UNIT_MTANK2, // Medium tank.
+ UNIT_LTANK, // Light tank ('Bradly').
+ UNIT_APC, // APC.
+ UNIT_MINELAYER, // Mine-laying vehicle.
+ UNIT_JEEP, // 4x4 jeep replacement.
+ UNIT_HARVESTER, // Resource gathering vehicle.
+ UNIT_ARTY, // Artillery unit.
+ UNIT_MRJ, // Mobile Radar Jammer.
+ UNIT_MGG, // Mobile Gap Generator
+ UNIT_MCV, // Mobile construction vehicle.
+ UNIT_V2_LAUNCHER, // V2 rocket launcher.
+ UNIT_TRUCK, // Convoy truck
+
+#ifdef FIXIT_ANTS
+ UNIT_ANT1, // Warrior ant.
+ UNIT_ANT2, // Warrior ant.
+ UNIT_ANT3, // Warrior ant.
+#endif
+
+#ifdef FIXIT_CSII // checked - ajw 9/28/98
+// CS II ONLY!
+ UNIT_CHRONOTANK, // Chrono-shifting tank
+ UNIT_TESLATANK, // Tesla-equipped tank
+ UNIT_MAD, // Timequake tank
+ UNIT_DEMOTRUCK, // Jihad truck
+#ifdef FIXIT_PHASETRANSPORT // checked - ajw 9/28/98
+ UNIT_PHASE, // cloaking APC for special missions
+#endif
+#endif
+
+ UNIT_COUNT,
+ UNIT_FIRST=0
+#ifdef FIXIT_CSII // checked - ajw 9/28/98
+,
+ UNIT_RA_COUNT = UNIT_CHRONOTANK
+#endif
+} UnitType;
+
+inline UnitType operator++(UnitType &, int);
+
+#define UNITF_HTANK (1L<id)
+
+
+#define MAX_LOG_LEVEL 10
+
+// Maximum number of multi players possible.
+#define MAX_PLAYERS 8 // max # of players we can have
+
+
+
+#endif
diff --git a/CODE/DESCDLG.CPP b/CODE/DESCDLG.CPP
new file mode 100644
index 0000000..3700a78
--- /dev/null
+++ b/CODE/DESCDLG.CPP
@@ -0,0 +1,163 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/DESCDLG.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : DESCDLG.CPP *
+ * *
+ * Programmer : Maria del Mar McCready Legg *
+ * Joe L. Bostic *
+ * *
+ * Start Date : Jan 26, 1995 *
+ * *
+ * Last Update : Jan 26, 1995 [MML] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * DescriptionClass::Process -- Handles all the options graphic interface. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+#include "descdlg.h"
+
+
+/***********************************************************************************************
+ * DescriptionClass::Process -- Handles all the options graphic interface. *
+ * *
+ * This dialog uses an edit box to "fill-out" a description. *
+ * *
+ * INPUT: char *string - return answer here. *
+ * OUTPUT: none *
+ * WARNINGS: none *
+ * HISTORY: 12/31/1994 MML : Created. *
+ *=============================================================================================*/
+void DescriptionClass::Process(char * string)
+{
+ /*
+ ** Set up the window. Window x-coords are in bytes not pixels.
+ */
+ Set_Window(WINDOW_EDITOR, OPTION_X, OPTION_Y, OPTION_WIDTH, OPTION_HEIGHT);
+ Set_Logic_Page(SeenBuff);
+
+ /*
+ ** Create Buttons. Button coords are in pixels, but are window-relative.
+ */
+ TextButtonClass optionsbtn(BUTTON_OPTIONS, TXT_OK, TPF_BUTTON, 0, BUTTON_Y);
+ TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, 0, BUTTON_Y);
+
+ cancelbtn.X = OPTION_X + ((OPTION_WIDTH - optionsbtn.Width)/3)*2;
+ optionsbtn.X = OPTION_X + ((OPTION_WIDTH - optionsbtn.Width)/3);
+ optionsbtn.Add_Tail(cancelbtn);
+
+ EditClass edit(
+ BUTTON_EDIT,
+ string,
+ 31,
+ TPF_6PT_GRAD,
+ 0,
+ EDIT_Y,
+ EDIT_W);
+
+ edit.Set_Focus();
+ edit.X = OPTION_X + (OPTION_WIDTH - edit.Width)/2,
+ optionsbtn.Add_Tail(edit);
+
+ /*
+ ** This causes left mouse button clicking within the confines of the dialog to
+ ** be ignored if it wasn't recognized by any other button or slider.
+ */
+ GadgetClass dialog(OPTION_X, OPTION_Y, OPTION_WIDTH, OPTION_HEIGHT, GadgetClass::LEFTPRESS);
+ optionsbtn.Add_Tail(dialog);
+
+ /*
+ ** This causes a right click anywhere or a left click outside the dialog region
+ ** to be equivalent to clicking on the return to options dialog.
+ */
+ ControlClass background(BUTTON_OPTIONS, 0, 0, 320, 200, GadgetClass::LEFTPRESS|GadgetClass::RIGHTPRESS);
+ optionsbtn.Add_Tail(background);
+
+ /*
+ ** Main Processing Loop
+ */
+ bool display = true;
+ bool process = true;
+ while (process) {
+
+ /*
+ ** Invoke game callback
+ */
+ Call_Back();
+
+ /*
+ ** Refresh display if needed
+ */
+ if (display) {
+
+ Window_Hide_Mouse(WINDOW_EDITOR);
+
+ /*
+ ** Draw the background
+ */
+ Window_Box (WINDOW_EDITOR, BOXSTYLE_BORDER); // has border, raised up
+ Draw_Caption(TXT_MISSION_DESCRIPTION, OPTION_X, OPTION_Y, OPTION_WIDTH);
+
+ /*
+ ** Draw the titles
+ */
+ optionsbtn.Draw_All();
+ Window_Show_Mouse();
+ display = false;
+ }
+
+ /*
+ ** Get user input
+ */
+ KeyNumType input = optionsbtn.Input();
+
+ /*
+ ** Process Input
+ */
+ switch (input) {
+
+ case KN_RETURN:
+ case KeyNumType(BUTTON_OPTIONS|KN_BUTTON):
+ strtrim(string);
+ process = false;
+ break;
+
+ case KN_ESC:
+ case KeyNumType(BUTTON_CANCEL|KN_BUTTON):
+ string[0]= NULL;
+ strtrim(string);
+ process = false;
+ break;
+
+ case KeyNumType(BUTTON_EDIT|KN_BUTTON):
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
diff --git a/CODE/DESCDLG.H b/CODE/DESCDLG.H
new file mode 100644
index 0000000..484b4ce
--- /dev/null
+++ b/CODE/DESCDLG.H
@@ -0,0 +1,69 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/DESCDLG.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : DESCDLG.H *
+ * *
+ * Programmer : Maria del Mar McCready Legg *
+ * Joe L. Bostic *
+ * *
+ * Start Date : Jan 26, 1995 *
+ * *
+ * Last Update : Jan 26, 1995 [MML] *
+ * *
+ *---------------------------------------------------------------------------------------------*/
+
+#ifndef DESCDLG_H
+#define DESCDLG_H
+
+#include "gadget.h"
+
+class DescriptionClass
+{
+ private:
+
+ enum DescriptionClassEnum {
+ OPTION_WIDTH=216, // Width of dialog box.
+ OPTION_HEIGHT=122, // Height of dialog box.
+ OPTION_X=(((320 - OPTION_WIDTH) / 2) & ~7),
+ OPTION_Y=((200 - OPTION_HEIGHT) / 2),
+ TEXT_X=OPTION_X+32, // Title's x pos
+ TEXT_Y=OPTION_Y+32, // Add 11 for each following line
+ BUTTON_OPTIONS=1, // Button number for "Ok"
+ BUTTON_CANCEL,
+ BUTTON_EDIT,
+ BUTTON_X=OPTION_X+63, // Options button x pos
+ BUTTON_Y=OPTION_Y+102, // Options button y pos
+ EDIT_Y =OPTION_Y+50,
+ EDIT_W =180 //204,
+ };
+
+ public:
+ DescriptionClass(void) {};
+ void Process(char *string);
+};
+
+#endif
+
+
diff --git a/CODE/DIAL8.CPP b/CODE/DIAL8.CPP
new file mode 100644
index 0000000..c201fc8
--- /dev/null
+++ b/CODE/DIAL8.CPP
@@ -0,0 +1,319 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/DIAL8.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : DIAL8.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 07/05/96 *
+ * *
+ * Last Update : July 5, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * Dial8Class::Action -- action routine for Dial8Class *
+ * Dial8Class::Dial8Class -- constructor for the facing dial *
+ * Dial8Class::Draw_Me -- render routine for Dial8Class *
+ * Dial8Class::Get_Direction -- retrieves direction (0-255) of dial *
+ * Dial8Class::Set_Direction -- sets current direction (0-255) of dial *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+
+
+/***************************************************************************
+ * Dial8Class::Dial8Class -- constructor for the facing dial *
+ * *
+ * INPUT: *
+ * id button ID *
+ * x,y,w,h dimensions in window-relative pixels *
+ * dir numerical initial facing value (0-255); this is the *
+ * value returned by WWLIB Desired_Facing8() *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 11/16/1994 BR : Created. *
+ *=========================================================================*/
+Dial8Class::Dial8Class(int id, int x, int y, int w, int h, DirType dir) :
+ ControlClass(id, x, y, w, h, LEFTPRESS | LEFTHELD | LEFTRELEASE, true)
+{
+ /*
+ ** Center coordinates.
+ */
+ FaceX = X + (Width / 2);
+ FaceY = Y + (Height / 2);
+
+ /*
+ ** Init directions.
+ */
+ Direction = dir; // 0 - 255
+ Facing = Dir_Facing(Direction); // 0 - 7
+ OldFacing = Facing; // 0 - 7
+
+ /*
+ ** Compute the drawing dimensions: a 45-degree angle intersects a unity-
+ ** radius circle at (.707,.707). Make the decorations 8/10 of the radius,
+ ** and the line extend to 6/10 of the radius. Use Width/2 for x-radius,
+ ** Height/2 for y-radius.
+ */
+ FacePoint[0][0] = FaceX;
+ FacePoint[0][1] = FaceY - (h * 8 / 2) / 10;
+
+ FacePoint[1][0] = FaceX + (w * 7 * 8 / 2) / 100;
+ FacePoint[1][1] = FaceY - (h * 7 * 8 / 2) / 100;
+
+ FacePoint[2][0] = FaceX + (w * 8 / 2) / 10;
+ FacePoint[2][1] = FaceY;
+
+ FacePoint[3][0] = FaceX + (w * 7 * 8 / 2) / 100;
+ FacePoint[3][1] = FaceY + (h * 7 * 8 / 2) / 100;
+
+ FacePoint[4][0] = FaceX;
+ FacePoint[4][1] = FaceY + (h * 8 / 2) / 10;
+
+ FacePoint[5][0] = FaceX - (w * 7 * 8 / 2) / 100;
+ FacePoint[5][1] = FaceY + (h * 7 * 8 / 2) / 100;
+
+ FacePoint[6][0] = FaceX - (w * 8 / 2) / 10;
+ FacePoint[6][1] = FaceY;
+
+ FacePoint[7][0] = FaceX - (w * 7 * 8 / 2) / 100;
+ FacePoint[7][1] = FaceY - (h * 7 * 8 / 2) / 100;
+
+ FaceLine[0][0] = FaceX;
+ FaceLine[0][1] = FaceY - (h * 6 / 2) / 10;
+
+ FaceLine[1][0] = FaceX + (w * 7 * 6 / 2) / 100;
+ FaceLine[1][1] = FaceY - (h * 7 * 6 / 2) / 100;
+
+ FaceLine[2][0] = FaceX + (w * 6 / 2) / 10;
+ FaceLine[2][1] = FaceY;
+
+ FaceLine[3][0] = FaceX + (w * 7 * 6 / 2) / 100;
+ FaceLine[3][1] = FaceY + (h * 7 * 6 / 2) / 100;
+
+ FaceLine[4][0] = FaceX;
+ FaceLine[4][1] = FaceY + (h * 6 / 2) / 10;
+
+ FaceLine[5][0] = FaceX - (w * 7 * 6 / 2) / 100;
+ FaceLine[5][1] = FaceY + (h * 7 * 6 / 2) / 100;
+
+ FaceLine[6][0] = FaceX - (w * 6 / 2) / 10;
+ FaceLine[6][1] = FaceY;
+
+ FaceLine[7][0] = FaceX - (w * 7 * 6 / 2) / 100;
+ FaceLine[7][1] = FaceY - (h * 7 * 6 / 2) / 100;
+}
+
+
+/***************************************************************************
+ * Dial8Class::Action -- activation function for Dial8Class *
+ * *
+ * INPUT: *
+ * flags the reason we're being called *
+ * key the KN_number that was pressed *
+ * *
+ * OUTPUT: *
+ * true = event was processed, false = event not processed *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 02/06/1995 BR : Created. *
+ *=========================================================================*/
+int Dial8Class::Action(unsigned flags, KeyNumType &key)
+{
+ static int is_sel = 0;
+
+ /*
+ ** We might end up clearing the event bits. Make sure that the sticky
+ ** process is properly updated anyway.
+ */
+ Sticky_Process(flags);
+
+ if (flags & LEFTPRESS) {
+ is_sel = 1;
+ }
+
+ /*
+ ** If left mouse is clicked or held, and the dial has changed its direction,
+ ** invoke the parent Action routine:
+ ** GadgetClass::Action handles Sticky processing, & sets IsToRepaint if any
+ ** flag bits are set.
+ ** ControlClass::Action handles Peer_To_Peer notification, and substitutes
+ ** 'key' with the button ID if any flags are set, or 0 if no flags are set
+ */
+ if (flags & LEFTPRESS || ((flags & LEFTHELD) && is_sel)) {
+ /*
+ ** Get new dial position (0-255)
+ */
+ Direction = (DirType)Desired_Facing8(FaceX, FaceY, Get_Mouse_X(), Get_Mouse_Y());
+
+ /*
+ ** Convert to Facing value (0-7).
+ */
+ Facing = Dir_Facing(Direction);
+
+ /*
+ ** If it's moved, redraw.
+ */
+ if (Facing!=OldFacing) {
+ OldFacing = Facing;
+ ControlClass::Action(flags, key);
+ return(true);
+
+ } else {
+
+ /*
+ ** Dial hasn't moved; kill the event & return
+ */
+ key = KN_NONE;
+ ControlClass::Action(0, key);
+ return(true);
+ }
+
+ } else {
+
+ /*
+ ** Otherwise, no events have occurred; kill the event if it's a LEFTRELEASE,
+ ** and return
+ */
+ if (flags & LEFTRELEASE) {
+ key = KN_NONE;
+ is_sel = 0;
+ }
+ return(ControlClass::Action(0, key));
+ }
+}
+
+
+/***************************************************************************
+ * Dial8Class::Draw_Me -- custom render routine for Dial8Class *
+ * *
+ * INPUT: *
+ * forced true = draw regardless of the current redraw flag state*
+ * *
+ * OUTPUT: *
+ * true = gadget was redrawn, false = wasn't *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 02/06/1995 BR : Created. *
+ *=========================================================================*/
+int Dial8Class::Draw_Me(int forced)
+{
+ RemapControlType * scheme = GadgetClass::Get_Color_Scheme();
+
+ /*
+ ** Redraw if parent indicates a redraw is needed
+ */
+ if (ControlClass::Draw_Me(forced)) {
+ /*
+ ** Hide the mouse.
+ */
+
+ if (LogicPage == &SeenBuff) {
+ Hide_Mouse();
+ }
+
+ /*
+ ** Draw background & decorations.
+ */
+ Draw_Box(X, Y, Width, Height, BOXSTYLE_DOWN, true);
+ for (int i=0; i<8; i++) {
+ Draw_Box(FacePoint[i][0] - 1, FacePoint[i][1] -1, 3, 3, BOXSTYLE_RAISED, false);
+ }
+
+ /*
+ ** Draw the hand & its shadow.
+ */
+ LogicPage->Draw_Line(FaceX+1, FaceY+1, FaceLine[Facing][0]+1, FaceLine[Facing][1]+1, scheme->Shadow);
+ LogicPage->Draw_Line(FaceX, FaceY, FaceLine[Facing][0], FaceLine[Facing][1], scheme->Highlight);
+
+ /*
+ ** Restore the mouse.
+ */
+ if (LogicPage == &SeenBuff) {
+ Show_Mouse();
+ }
+
+ return(true);
+ }
+
+ return(false);
+}
+
+
+/***************************************************************************
+ * Dial8Class::Get_Direction -- retrieves direction (0-255) of dial *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * DirType dial is pointing to *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 11/17/1994 BR : Created. *
+ *=========================================================================*/
+DirType Dial8Class::Get_Direction(void) const
+{
+ return(Direction);
+}
+
+
+/***************************************************************************
+ * Dial8Class::Set_Direction -- sets current direction (0-255) of dial *
+ * *
+ * INPUT: *
+ * DirType to set dial to *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 11/17/1994 BR : Created. *
+ *=========================================================================*/
+void Dial8Class::Set_Direction(DirType dir)
+{
+ Direction = dir;
+ Facing = Dir_Facing(Direction);
+ OldFacing = Facing;
+ Flag_To_Redraw();
+}
diff --git a/CODE/DIAL8.H b/CODE/DIAL8.H
new file mode 100644
index 0000000..4fdb8c2
--- /dev/null
+++ b/CODE/DIAL8.H
@@ -0,0 +1,76 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/DIAL8.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : DIAL8.H *
+ * *
+ * Programmer : Bill Randolph *
+ * *
+ * Start Date : 02/06/95 *
+ * *
+ * Last Update : February 6, 1995 [BR] *
+ * *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifndef DIAL8_H
+#define DIAL8_H
+
+class Dial8Class : public ControlClass
+{
+ public:
+ /*
+ ** Constructor/Destructor
+ */
+ Dial8Class(int id, int x, int y, int w, int h, DirType dir);
+
+ /*
+ ** Get/Set the direction the dial is currently pointing
+ */
+ DirType Get_Direction(void) const;
+ void Set_Direction(DirType dir);
+
+ /*
+ ** Overloaded draw routine
+ */
+ virtual int Draw_Me(int forced = false);
+
+ protected:
+ /*
+ ** Overloaded event processing routine
+ */
+ virtual int Action(unsigned flags, KeyNumType &key);
+
+ private:
+ int FaceX; // x-coord of center of face
+ int FaceY; // y-coord of center of face
+ int FacePoint[8][2]; // coords of the little dial decorations
+ int FaceLine[8][2]; // coords for drawing the dial hand
+ DirType Direction; // 0-255 numerical direction of dial
+ FacingType Facing; // numerical facing direction of dial (0 - 7)
+ FacingType OldFacing; // previous Facing value
+
+};
+
+#endif
+
diff --git a/CODE/DIALOG.CPP b/CODE/DIALOG.CPP
new file mode 100644
index 0000000..1c5e93c
--- /dev/null
+++ b/CODE/DIALOG.CPP
@@ -0,0 +1,998 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/DIALOG.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : DIALOG.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : September 10, 1993 *
+ * *
+ * Last Update : July 31, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * Clip_Text_Print -- Prints text with clipping and support. *
+ * Dialog_Box -- draws a dialog background box *
+ * Display_Place_Building -- Displays the "place building" dialog box. *
+ * Display_Select_Target -- Displays the "choose target" prompt. *
+ * Display_Status -- Display the player scenario status box. *
+ * Draw_Box -- Displays a highlighted box. *
+ * Draw_Caption -- Draws a caption on a dialog box. *
+ * Fancy_Text_Print -- Prints text with a drop shadow. *
+ * Plain_Text_Print -- Prints text without using a color scheme *
+ * Redraw_Needed -- Determine if sidebar needs to be redrawn. *
+ * Render_Bar_Graph -- Renders a specified bargraph. *
+ * Simple_Text_Print -- Prints text with a drop shadow. *
+ * Window_Box -- Draws a fancy box over the specified window. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+
+#include "defines.h" //VG 10/17/96
+
+unsigned char * Font_Palette(int color);
+
+
+/***********************************************************************************************
+ * Dialog_Box -- draws a dialog background box *
+ * *
+ * INPUT: *
+ * x,y,w,h the usual *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 01/26/1995 BR : Created. *
+ * 07/31/1996 JLB : Uses shapes to draw the box. *
+ *=============================================================================================*/
+void Dialog_Box(int x, int y, int w, int h)
+{
+// Try to expand the box a little taller and a little wider to make room for
+// the dialog box graphics in the DOS version.
+#ifndef WIN32
+ x = max(0, x-4);
+ y = max(0, y-4);
+ w = min(w+8, 320-x);
+ h = min(h+8, 200-y);
+#endif
+
+ WindowList[WINDOW_PARTIAL][WINDOWX] = x;
+ WindowList[WINDOW_PARTIAL][WINDOWY] = y;
+ WindowList[WINDOW_PARTIAL][WINDOWWIDTH] = w;
+ WindowList[WINDOW_PARTIAL][WINDOWHEIGHT] = h;
+
+ /*
+ ** Always draw to the hidpage and then blit forward.
+ */
+#ifdef WIN32
+ GraphicViewPortClass * oldpage = Set_Logic_Page(HidPage);
+#else
+ GraphicBufferClass * oldpage = Set_Logic_Page(HidPage);
+#endif
+
+ /*
+ ** Draw the background block.
+ */
+ int cx = w/2;
+ int cy = h/2;
+ void const * shapedata = MFCD::Retrieve("DD-BKGND.SHP");
+#ifdef WIN32
+ CC_Draw_Shape(shapedata, 0, cx-312, cy-192, WINDOW_PARTIAL, SHAPE_WIN_REL);
+ CC_Draw_Shape(shapedata, 1, cx, cy-192, WINDOW_PARTIAL, SHAPE_WIN_REL);
+ CC_Draw_Shape(shapedata, 2, cx-312, cy, WINDOW_PARTIAL, SHAPE_WIN_REL);
+ CC_Draw_Shape(shapedata, 3, cx, cy, WINDOW_PARTIAL, SHAPE_WIN_REL);
+#else
+ CC_Draw_Shape(shapedata, 0, cx-156, cy-96, WINDOW_PARTIAL, SHAPE_WIN_REL);
+#endif
+ /*
+ ** Draw the side strips.
+ */
+ shapedata = MFCD::Retrieve("DD-EDGE.SHP");
+ for (int yy = 0; yy < h; yy += 6) {
+ CC_Draw_Shape(shapedata, 0, 7*RESFACTOR, yy, WINDOW_PARTIAL, SHAPE_WIN_REL);
+ CC_Draw_Shape(shapedata, 1, w-((7+8)*RESFACTOR), yy, WINDOW_PARTIAL, SHAPE_WIN_REL);
+ }
+
+ /*
+ ** Draw the border bars.
+ */
+ shapedata = MFCD::Retrieve("DD-LEFT.SHP");
+ CC_Draw_Shape(shapedata, 0, 0, cy-100*RESFACTOR, WINDOW_PARTIAL, SHAPE_WIN_REL);
+ CC_Draw_Shape(shapedata, 0, 0, cy, WINDOW_PARTIAL, SHAPE_WIN_REL);
+
+ shapedata = MFCD::Retrieve("DD-RIGHT.SHP");
+ int rightx = w - (7*RESFACTOR);
+#ifndef WIN32
+ rightx--;
+#endif
+ CC_Draw_Shape(shapedata, 0, rightx, cy-100*RESFACTOR, WINDOW_PARTIAL, SHAPE_WIN_REL);
+ CC_Draw_Shape(shapedata, 0, rightx, cy, WINDOW_PARTIAL, SHAPE_WIN_REL);
+
+ shapedata = MFCD::Retrieve("DD-BOTM.SHP");
+ CC_Draw_Shape(shapedata, 0, cx-160*RESFACTOR, h-8*RESFACTOR, WINDOW_PARTIAL, SHAPE_WIN_REL);
+ CC_Draw_Shape(shapedata, 0, cx, h-8*RESFACTOR, WINDOW_PARTIAL, SHAPE_WIN_REL);
+
+ shapedata = MFCD::Retrieve("DD-TOP.SHP");
+ CC_Draw_Shape(shapedata, 0, cx-160*RESFACTOR, 0, WINDOW_PARTIAL, SHAPE_WIN_REL);
+ CC_Draw_Shape(shapedata, 0, cx, 0, WINDOW_PARTIAL, SHAPE_WIN_REL);
+
+ /*
+ ** Draw the corner caps.
+ */
+ shapedata = MFCD::Retrieve("DD-CRNR.SHP");
+ CC_Draw_Shape(shapedata, 0, 0, 0, WINDOW_PARTIAL, SHAPE_WIN_REL);
+ CC_Draw_Shape(shapedata, 1, w-(12*RESFACTOR-1), 0, WINDOW_PARTIAL, SHAPE_WIN_REL);
+ CC_Draw_Shape(shapedata, 2, 0, h-(12*RESFACTOR), WINDOW_PARTIAL, SHAPE_WIN_REL);
+ CC_Draw_Shape(shapedata, 3, w-(12*RESFACTOR-1), h-(12*RESFACTOR), WINDOW_PARTIAL, SHAPE_WIN_REL);
+
+#ifdef WIN32
+ WWMouse->Draw_Mouse(&HidPage);
+ HidPage.Blit(SeenBuff, x, y, x, y, w, h, false);
+ WWMouse->Erase_Mouse(&HidPage, FALSE);
+#else
+// Shadow_Blit(0, 0, 320, 200, HidPage, SeenPage, Map.ShadowPage->Get_Buffer());
+ Hide_Mouse();
+ HidPage.Blit(SeenBuff);
+ Show_Mouse();
+// Shadow_Blit(0, 0, 320, 200, HidPage, SeenPage, ((GraphicBufferClass*)Map.Shadow_Address())->Get_Buffer());
+#endif
+ Set_Logic_Page(oldpage);
+}
+
+
+/***********************************************************************************************
+ * Draw_Box -- Displays a highlighted box. *
+ * *
+ * This will draw a highlighted box to the logicpage. It can *
+ * optionally fill the box with a color as well. This is a low level *
+ * function and thus, it doesn't do any graphic mode color adjustments. *
+ * *
+ * INPUT: x,y -- Upper left corner of the box to be drawn (pixels). *
+ * *
+ * w,h -- Width and height of box (in pixels). *
+ * *
+ * up -- Is the box rendered in the "up" stated? *
+ * *
+ * filled-- Is the box to be filled. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/28/1991 JLB : Created. *
+ * 05/30/1992 JLB : Embedded color codes. *
+ * 07/31/1992 JLB : Depressed option added. *
+ *=============================================================================================*/
+void Draw_Box(int x, int y, int w, int h, BoxStyleEnum up, bool filled)
+{
+ RemapControlType * scheme = GadgetClass::Get_Color_Scheme();
+
+ // Filler, Shadow, Hilite, Corner colors
+
+ BoxStyleType const ButtonColors[BOXSTYLE_COUNT] = {
+ { scheme->Background, scheme->Highlight, scheme->Shadow, scheme->Corners}, // Down
+ { scheme->Background, scheme->Shadow, scheme->Highlight, scheme->Corners}, // Raised
+ { DKGREY, WHITE, BLACK, DKGREY}, // Disabled down
+ { DKGREY, BLACK, LTGREY, DKGREY}, // Disabled up
+ { BLACK, scheme->Box, scheme->Box, BLACK}, // List box
+ { BLACK, scheme->Box, scheme->Box, BLACK}, // Dialog box
+ };
+
+ w--;
+ h--;
+ BoxStyleType const &style = ButtonColors[up];
+
+ if (filled) {
+ LogicPage->Fill_Rect( x, y, x+w, y+h, style.Filler);
+ }
+
+ switch (up) {
+ case (BOXSTYLE_BOX):
+ LogicPage->Draw_Rect(x, y, x+w, y+h, style.Highlight);
+ break;
+
+ case (BOXSTYLE_BORDER):
+ LogicPage->Draw_Rect(x+1, y+1, x+w-1, y+h-1, style.Highlight);
+ break;
+
+ default:
+ LogicPage->Draw_Line(x, y+h, x+w, y+h, style.Shadow);
+ LogicPage->Draw_Line(x+w, y, x+w, y+h, style.Shadow);
+
+ LogicPage->Draw_Line(x, y, x+w, y, style.Highlight);
+ LogicPage->Draw_Line(x, y, x, y+h, style.Highlight);
+
+ LogicPage->Put_Pixel(x, y+h, style.Corner);
+ LogicPage->Put_Pixel(x+w, y, style.Corner);
+ break;
+ }
+}
+
+
+/***********************************************************************************************
+ * Format_Window_String -- Separates a String into Lines. *
+ * This function will take a long string and break it up into lines *
+ * which are not longer then the window width. Any character < ' ' is *
+ * considered a new line marker and will be replaced by a NULL. *
+ * *
+ * INPUT: char *String - string to be formated. *
+ * int maxlinelen - Max length of any line in pixels. *
+ * *
+ * OUTPUT: int - number of lines string is. *
+ * *
+ * WARNINGS: The string passed in will be modified - NULLs will be put *
+ * into each position that will be a new line. *
+ * *
+ * HISTORY: *
+ * 03/27/1992 SB : Created. *
+ * 05/18/1995 JLB : Greatly revised for new font system. *
+ * 09/04/1996 BWG : Added '@' is treated as a carriage return for width calculations. *
+ *=============================================================================================*/
+int Format_Window_String(char * string, int maxlinelen, int & width, int & height)
+{
+ int linelen;
+ int lines = 0;
+ width = 0;
+ height = 0;
+
+ // In no string was passed in, then there are no lines.
+ if (!string) return(0);
+
+ // While there are more letters left divide the line up.
+ while (*string) {
+ linelen = 0;
+ height += FontHeight + FontYSpacing;
+ lines++;
+
+ /*
+ ** Look for special line break character and force a line break when it is
+ ** discovered.
+ */
+ if (*string == '@') {
+ *string = '\r';
+ }
+
+ // While the current line is less then the max length...
+ while (linelen < maxlinelen && *string != '\r' && *string != '\0' && *string != '@') {
+ linelen += Char_Pixel_Width(*string++);
+ }
+
+ // if the line is to long...
+ if (linelen >= maxlinelen) {
+
+ /*
+ ** Back up to an appropriate location to break.
+ */
+ while (*string != ' ' && *string != '\r' && *string != '\0' && *string != '@') {
+ linelen -= Char_Pixel_Width(*string--);
+ }
+
+ }
+
+ /*
+ ** Record the largest width of the worst case string.
+ */
+ if (linelen > width) {
+ width = linelen;
+ }
+
+ /*
+ ** Force a break at the end of the line.
+ */
+ if (*string) {
+ *string++ = '\r';
+ }
+ }
+ return(lines);
+}
+
+
+/***********************************************************************************************
+ * Window_Box -- Draws a fancy box over the specified window. *
+ * *
+ * This routine will draw a fancy (shaded) box over the specified *
+ * window. This is the effect used to give the polished look to *
+ * screen rectangles without having to use art. *
+ * *
+ * INPUT: window -- Specified window to fill and border. *
+ * *
+ * style -- The style to render the window. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: The rendering is done to the LogicPage. *
+ * *
+ * HISTORY: *
+ * 03/03/1992 JLB : Created. *
+ * 07/31/1992 JLB : Cool raised border effect. *
+ * 06/08/1994 JLB : Takes appropriate enumeration parameters. *
+ *=============================================================================================*/
+void Window_Box(WindowNumberType window, BoxStyleEnum style)
+{
+ int x = WindowList[window][WINDOWX];
+ int y = WindowList[window][WINDOWY];
+ int w = WindowList[window][WINDOWWIDTH];
+ int h = WindowList[window][WINDOWHEIGHT];
+
+ /*
+ ** If it is to be rendered to the seenpage, then
+ ** hide the mouse.
+ */
+ if (LogicPage == (&SeenBuff)) Conditional_Hide_Mouse(x ,y, x+w, y+h);
+
+ Draw_Box(x, y, w, h, style, true);
+
+ /*
+ ** Restore the mouse if it has been hidden and return.
+ */
+ if (LogicPage == &SeenBuff) Conditional_Show_Mouse();
+}
+
+
+/***********************************************************************************************
+ * Simple_Text_Print -- Prints text with a drop shadow. *
+ * *
+ * This routine functions like Text_Print, but will render a drop *
+ * shadow (in black). *
+ * *
+ * The C&C gradient font colors are as follows: *
+ * 0 transparent (background) *
+ * 1 foreground color for mono-color fonts only *
+ * 2 shadow under characters ("drop shadow") *
+ * 3 shadow all around characters ("full shadow") *
+ * 4-10 unused *
+ * 11 top row *
+ * 12 next row *
+ * 13 next row *
+ * 14 next row *
+ * 15 bottom row *
+ * *
+ * INPUT: text -- Pointer to text to render. *
+ * *
+ * x,y -- Pixel coordinate for to print text. *
+ * *
+ * fore -- Foreground color. *
+ * *
+ * back -- Background color. *
+ * *
+ * flag -- Text print control flags. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 12/24/1991 JLB : Created. *
+ * 10/26/94 JLB : Handles font X spacing in a more friendly manner. *
+ *=============================================================================================*/
+void Simple_Text_Print(char const * text, unsigned x, unsigned y, RemapControlType * fore, unsigned back, TextPrintType flag)
+{
+ static int yspace=0; // Y spacing adjustment for font.
+ static int xspace=0; // Spacing adjustment for font.
+ void const * font=0; // Font to use.
+ int shadow; // Requested shadow value.
+ unsigned char fontpalette[16]; // Working font palette array.
+ int forecolor;
+
+ if (fore == NULL) {
+ fore = &ColorRemaps[PCOLOR_RED];
+ }
+
+ /*
+ ** Init the font palette to the given background color
+ */
+ memset(&fontpalette[0], back, 16);
+
+ forecolor = fore->Color;
+
+ /*
+ ** A gradient font always requires special fixups for the palette
+ */
+ int point = (flag & (TextPrintType)0x000F);
+ if (point == TPF_VCR || point == TPF_6PT_GRAD || point == TPF_METAL12 || point == TPF_EFNT || point == TPF_TYPE) {
+
+ /*
+ ** If a gradient palette is specified, copy the remap table directly, otherwise
+ ** use the foreground color as the entire font remap color.
+ */
+ if (flag & TPF_USE_GRAD_PAL) {
+ memcpy(fontpalette, fore->FontRemap, 16);
+ forecolor = fore->Color;
+ if (point == TPF_TYPE) {
+ forecolor = fontpalette[1];
+ }
+ } else {
+ memset(&fontpalette[4], fore->Color, 12);
+ forecolor = fore->Color;
+ }
+
+ /*
+ ** Medium color: set all font colors to a medium value. This flag
+ ** overrides any gradient effects.
+ */
+ if (flag & TPF_MEDIUM_COLOR) {
+ forecolor = fore->Color;
+ memset(&fontpalette[4], fore->Color, 12);
+ }
+
+ /*
+ ** Bright color: set all font colors to a bright value. This flag
+ ** overrides any gradient effects.
+ */
+ if (flag & TPF_BRIGHT_COLOR) {
+ forecolor = fore->Bright;
+ memset(&fontpalette[4], fore->BrightColor, 12);
+ }
+ }
+
+ /*
+ ** Change the current font if it differs from the font desired.
+ */
+#ifdef WIN32
+ xspace = 1;
+#else
+ xspace = 0;
+#endif
+ yspace = 0;
+
+ switch (point) {
+ case TPF_SCORE:
+ font = ScoreFontPtr;
+ break;
+
+ case TPF_METAL12:
+ font = Metal12FontPtr;
+ //xspace += 1;
+ break;
+
+ case TPF_MAP:
+ font = MapFontPtr;
+ xspace -= 1;
+ break;
+
+ case TPF_VCR:
+ font = VCRFontPtr;
+ break;
+
+ case TPF_6PT_GRAD:
+ font = GradFont6Ptr;
+ xspace -= 1;
+ break;
+
+ case TPF_3POINT:
+ xspace += 1;
+ font = Font3Ptr;
+ flag = flag & ~(TPF_DROPSHADOW|TPF_FULLSHADOW|TPF_NOSHADOW);
+ break;
+
+ case TPF_6POINT:
+ font = Font6Ptr;
+ xspace -= 1;
+ break;
+
+ case TPF_EFNT:
+ font = EditorFont;
+#ifdef WIN32
+ yspace += 1;
+ xspace -= 1;
+#endif
+ xspace -= 1;
+ break;
+
+ case TPF_8POINT:
+ font = Font8Ptr;
+#ifdef WIN32
+ xspace -= 2;
+ yspace -= 4;
+#else
+ xspace -= 1;
+ yspace -= 2;
+#endif
+ break;
+
+ case TPF_LED:
+#ifdef WIN32
+ xspace -= 4;
+#else
+ xspace -= 2;
+#endif
+ font = FontLEDPtr;
+ break;
+
+ case TPF_TYPE:
+ font = TypeFontPtr;
+ xspace -= 1;
+
+#ifdef WOLAPI_INTEGRATION
+ xspace -= 2;
+ yspace += 2;
+#else // I am implicitly assuming that TPF_TYPE was no longer being used, before I came along, despite the following. ajw
+#ifdef GERMAN
+ yspace += 4; //VG 10/17/96
+#endif
+#endif
+
+ break;
+
+ default:
+ font = FontPtr;
+ break;
+ }
+
+ /*
+ ** Change the current font palette according to the dropshadow flags.
+ */
+ shadow = (flag & (TPF_NOSHADOW|TPF_DROPSHADOW|TPF_FULLSHADOW|TPF_LIGHTSHADOW));
+ switch (shadow) {
+
+ /*
+ ** The text is rendered plain.
+ */
+ case TPF_NOSHADOW:
+ fontpalette[2] = back;
+ fontpalette[3] = back;
+ xspace -= 1;
+#ifdef WIN32
+ yspace -= 2;
+#else
+ yspace -= 1;
+#endif
+ break;
+
+ /*
+ ** The text is rendered with a simple
+ ** drop shadow.
+ */
+ case TPF_DROPSHADOW:
+ fontpalette[2] = BLACK;
+ fontpalette[3] = back;
+ xspace -= 1;
+ break;
+
+ /*
+ ** Special engraved text look for the options
+ ** dialog system.
+ */
+ case TPF_LIGHTSHADOW:
+ fontpalette[2] = ((14 * 16) + 7)+1;
+ fontpalette[3] = back;
+ xspace -= 1;
+ break;
+
+ /*
+ ** Each letter is surrounded by black. This is used
+ ** when the text will be over a non-plain background.
+ */
+ case TPF_FULLSHADOW:
+ fontpalette[2] = BLACK;
+ fontpalette[3] = BLACK;
+ xspace -= 1;
+ break;
+
+ default:
+ break;
+ }
+ if (point != TPF_TYPE) {
+ fontpalette[0] = back;
+ fontpalette[1] = fore->Color;
+ }
+
+ /*
+ ** Set the font and spacing according to the values they should be.
+ */
+ FontXSpacing = xspace;
+ FontYSpacing = yspace;
+ Set_Font(font);
+ Set_Font_Palette(fontpalette);
+
+ /*
+ ** Display the (centered) message if there is one.
+ */
+ if (text && *text) {
+ switch (flag & (TPF_CENTER|TPF_RIGHT)) {
+ case TPF_CENTER:
+ x -= String_Pixel_Width(text)>>1;
+ break;
+
+ case TPF_RIGHT:
+ x -= String_Pixel_Width(text);
+ break;
+
+ default:
+ break;
+ }
+
+ if ((unsigned)x < LogicPage->Get_Width() && (unsigned)y < LogicPage->Get_Height()) {
+ LogicPage->Print(text, x, y, forecolor, back);
+// LogicPage->Print(text, x, y, fore->Color, back);
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * Fancy_Text_Print -- Prints text with a drop shadow. *
+ * *
+ * This routine functions like Text_Print, but will render a drop *
+ * shadow (in black). *
+ * *
+ * INPUT: text -- Text number to print. *
+ * *
+ * x,y -- Pixel coordinate for to print text. *
+ * *
+ * fore -- Foreground color. *
+ * *
+ * back -- Background color. *
+ * *
+ * flag -- Text print control flags. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: This routine is much slower than normal text print and *
+ * if rendered to the SEENPAGE, the intermediate rendering *
+ * steps could be visible. *
+ * *
+ * HISTORY: *
+ * 11/29/1994 JLB : Created *
+ *=============================================================================================*/
+void Fancy_Text_Print(int text, unsigned x, unsigned y, RemapControlType * fore, unsigned back, TextPrintType flag, ...)
+{
+ char buffer[512]; // Working staging buffer.
+ va_list arg; // Argument list var.
+
+ /*
+ ** If the text number is valid, then process it.
+ */
+ if (text != TXT_NONE) {
+ va_start(arg, flag);
+
+ /*
+ ** The text string must be locked since the vsprintf function doesn't know
+ ** how to handle EMS pointers.
+ */
+ char const * tptr = Text_String(text);
+ vsprintf(buffer, tptr, arg);
+ va_end(arg);
+
+ Simple_Text_Print(buffer, x, y, fore, back, flag);
+ } else {
+
+ /*
+ ** Just the flags are to be changed, since the text number is TXT_NONE.
+ */
+ Simple_Text_Print((char const *)0, x, y, fore, back, flag);
+ }
+}
+
+
+/***********************************************************************************************
+ * Fancy_Text_Print -- Prints text with a drop shadow. *
+ * *
+ * This routine functions like Text_Print, but will render a drop *
+ * shadow (in black). *
+ * *
+ * INPUT: text -- Pointer to text to render. *
+ * *
+ * x,y -- Pixel coordinate for to print text. *
+ * *
+ * fore -- Foreground color. *
+ * *
+ * back -- Background color. *
+ * *
+ * flag -- Text print control flags. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: This routine is much slower than normal text print and *
+ * if rendered to the SEENPAGE, the intermediate rendering *
+ * steps could be visible. *
+ * *
+ * HISTORY: *
+ * 12/24/1991 JLB : Created. *
+ * 10/26/94 JLB : Handles font X spacing in a more friendly manner. *
+ * 11/29/1994 JLB : Separated actual draw action. *
+ *=============================================================================================*/
+void Fancy_Text_Print(char const * text, unsigned x, unsigned y, RemapControlType * fore, unsigned back, TextPrintType flag, ...)
+{
+ char buffer[512]; // Working staging buffer.
+ va_list arg; // Argument list var.
+
+ /*
+ ** If there is a valid text string pointer then build the final string into the
+ ** working buffer before sending it to the simple string printing routine.
+ */
+ if (text) {
+
+ /*
+ ** Since vsprintf doesn't know about EMS pointers, be sure to surround this
+ ** call with locking code.
+ */
+ va_start(arg, flag);
+ vsprintf(buffer, text, arg);
+ va_end(arg);
+
+ Simple_Text_Print(buffer, x, y, fore, back, flag);
+ } else {
+
+ /*
+ ** Just the flags are desired to be changed, so call the simple print routine with
+ ** a NULL text pointer.
+ */
+ Simple_Text_Print((char const *)0, x, y, fore, back, flag);
+ }
+}
+
+
+/***********************************************************************************************
+ * Clip_Text_Print -- Prints text with clipping and support. *
+ * *
+ * Use this routine to print text that that should be clipped at an arbitrary right margin *
+ * as well as possibly recognizing characters. Typical users of this routine would *
+ * be list boxes. *
+ * *
+ * INPUT: text -- Reference to the text to print. *
+ * *
+ * x,y -- Pixel coordinate of the upper left corner of the text position. *
+ * *
+ * fore -- The foreground color to use. *
+ * *
+ * back -- The background color to use. *
+ * *
+ * flag -- The text print flags to use. *
+ * *
+ * width -- The maximum pixel width to draw the text. Extra characters beyond this *
+ * point will not be printed. *
+ * *
+ * tabs -- Optional pointer to a series of pixel tabstop positions. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 01/21/1995 JLB : Created. *
+ *=============================================================================================*/
+void Conquer_Clip_Text_Print(char const * text, unsigned x, unsigned y, RemapControlType * fore, unsigned back, TextPrintType flag, int width, int const * tabs)
+{
+ char buffer[512];
+
+ if (text) {
+ strcpy(buffer, text);
+
+ /*
+ ** Set the font and spacing characteristics according to the flag
+ ** value passed in.
+ */
+ 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;
+ }
+ }
+ }
+}
+
+/***************************************************************************
+ * Plain_Text_Print -- Prints text without using a color scheme *
+ * *
+ * INPUT: *
+ * text text to print *
+ * x,y coords to print at *
+ * fore desired foreground color *
+ * back desired background color *
+ * flag text print control flags *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * Do not use the gradient control flag with this routine! For *
+ * a gradient appearance, use Fancy_Text_Print. *
+ * Despite this routine's name, it is actually faster to call *
+ * Fancy_Text_Print than this routine. *
+ * *
+ * HISTORY: *
+ * 01/05/1996 BRR : Created. *
+ *=========================================================================*/
+void Plain_Text_Print(int text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag, ...)
+{
+ RemapControlType scheme;
+
+ memset(&scheme, 0, sizeof(RemapControlType));
+ memset(&(scheme.FontRemap[4]), fore, 12);
+
+ scheme.BrightColor = fore;
+ scheme.Color = fore;
+ scheme.Shadow = fore;
+ scheme.Background = fore;
+ scheme.Corners = fore;
+ scheme.Highlight = fore;
+ scheme.Box = fore;
+ scheme.Bright = fore;
+ scheme.Underline = fore;
+ scheme.Bar = fore;
+
+ Fancy_Text_Print(text, x, y, &scheme, back, flag);
+}
+
+
+/***************************************************************************
+ * Plain_Text_Print -- Prints text without using a color scheme *
+ * *
+ * INPUT: *
+ * text text to print *
+ * x,y coords to print at *
+ * fore desired foreground color *
+ * back desired background color *
+ * flag text print control flags *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * Do not use the gradient control flag with this routine! For *
+ * a gradient appearance, use Fancy_Text_Print. *
+ * Despite this routine's name, it is actually faster to call *
+ * Fancy_Text_Print than this routine. *
+ * *
+ * HISTORY: *
+ * 01/05/1996 BRR : Created. *
+ *=========================================================================*/
+void Plain_Text_Print(char const * text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag, ...)
+{
+ RemapControlType scheme;
+
+ memset(&scheme, 0, sizeof(RemapControlType));
+ memset(&(scheme.FontRemap[4]), fore, 12);
+
+ scheme.BrightColor = fore;
+ scheme.Color = fore;
+ scheme.Shadow = fore;
+ scheme.Background = fore;
+ scheme.Corners = fore;
+ scheme.Highlight = fore;
+ scheme.Box = fore;
+ scheme.Bright = fore;
+ scheme.Underline = fore;
+ scheme.Bar = fore;
+
+ Fancy_Text_Print(text, x, y, &scheme, back, flag);
+}
+
+
+
+unsigned char * Font_Palette(int color)
+{
+ static unsigned char _fpalette[16];
+
+ memset(_fpalette, '\0', sizeof(_fpalette));
+ memset(&_fpalette[11], color, 5);
+ return(_fpalette);
+}
+
+
+
+/***********************************************************************************************
+ * Draw_Caption -- Draws a caption on a dialog box. *
+ * *
+ * This routine draws the caption text and any fancy filigree that the dialog may require. *
+ * *
+ * INPUT: text -- The text of the caption. This is the text number. *
+ * *
+ * x,y -- The dialog box X and Y pixel coordinate of the upper left corner. *
+ * *
+ * w -- The width of the dialog box (in pixels). *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/23/1995 JLB : Created. *
+ *=============================================================================================*/
+void Draw_Caption(int text, int x, int y, int w)
+{
+ Draw_Caption(Text_String(text), x, y, w);
+}
+
+
+void Draw_Caption(char const * text, int x, int y, int w)
+{
+ /*
+ ** Draw the caption.
+ */
+ if (text != NULL && *text != '\0') {
+ if (Debug_Map) {
+ Fancy_Text_Print(text, w/2 + x, (2 * RESFACTOR) + y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_CENTER|TPF_EFNT|TPF_USE_GRAD_PAL|TPF_NOSHADOW);
+ } else {
+ Fancy_Text_Print(text, w/2 + x, (8 * RESFACTOR) + y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_CENTER|TPF_TEXT);
+ int length = String_Pixel_Width(text);
+ LogicPage->Draw_Line((x+(w/2))-(length/2), y+FontHeight+FontYSpacing + (8 * RESFACTOR), (x+(w/2))+(length/2),
+ y+FontHeight+FontYSpacing + (8 * RESFACTOR), GadgetClass::Get_Color_Scheme()->Box);
+ }
+ }
+}
diff --git a/CODE/DIBAPI.H b/CODE/DIBAPI.H
new file mode 100644
index 0000000..59bb133
--- /dev/null
+++ b/CODE/DIBAPI.H
@@ -0,0 +1,153 @@
+/*
+** Command & Conquer Red Alert(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 .
+*/
+
+#ifndef DIBAPI_H
+#define DIBAPI_H
+/*
+ * dibapi.h
+ *
+ * Copyright (c) 1991 Microsoft Corporation. All rights reserved
+ *
+ * Header file for Device-Independent Bitmap (DIB) API. Provides
+ * function prototypes and constants for the following functions:
+ *
+ * BitmapToDIB() - Creates a DIB from a bitmap
+ * ChangeBitmapFormat() - Changes a bitmap to a specified DIB format
+ * ChangeDIBFormat() - Changes a DIB's BPP and/or compression format
+ * CopyScreenToBitmap() - Copies entire screen to a standard Bitmap
+ * CopyScreenToDIB() - Copies entire screen to a DIB
+ * CopyWindowToBitmap() - Copies a window to a standard Bitmap
+ * CopyWindowToDIB() - Copies a window to a DIB
+ * CreateDIBPalette() - Creates a palette from a DIB
+ * CreateDIB() - Creates a new DIB
+ * DestroyDIB() - Deletes DIB when finished using it
+ * DIBError() - Displays message box with error message
+ * DIBHeight() - Gets the DIB height
+ * DIBNumColors() - Calculates number of colors in the DIB's color table
+ * DIBToBitmap() - Creates a bitmap from a DIB
+ * DIBWidth() - Gets the DIB width
+ * FindDIBBits() - Sets pointer to the DIB bits
+ * GetSystemPalette() - Gets the current palette
+ * LoadDIB() - Loads a DIB from a file
+ * PaintBitmap() - Displays standard bitmap in the specified DC
+ * PaintDIB() - Displays DIB in the specified DC
+ * PalEntriesOnDevice() - Gets the number of palette entries
+ * PaletteSize() - Calculates the buffer size required by a palette
+ * PrintDIB() - Prints the specified DIB
+ * PrintScreen() - Prints the entire screen
+ * PrintWindow() - Prints all or part of a window
+ * SaveDIB() - Saves the specified dib in a file
+ *
+ * See the file DIBAPI.TXT for more information about these functions.
+ *
+ * ajw added
+ * LoadDIB_FromMemory() - Loads a DIB from BMP file data located at a location in memory.
+ *
+ */
+
+
+/* Handle to a DIB */
+#define HDIB HANDLE
+
+
+/* Print Area selection */
+#define PW_WINDOW 1
+#define PW_CLIENT 2
+
+
+/* Print Options selection */
+#define PW_BESTFIT 1
+#define PW_STRETCHTOPAGE 2
+#define PW_SCALE 3
+
+/* DIB Macros*/
+
+// WIDTHBYTES performs DWORD-aligning of DIB scanlines. The "bits"
+// parameter is the bit count for the scanline (biWidth * biBitCount),
+// and this macro returns the number of DWORD-aligned bytes needed
+// to hold those bits.
+
+#define WIDTHBYTES(bits) (((bits) + 31) / 32 * 4)
+
+/* Error constants */
+enum {
+ ERR_MIN = 0, // All error #s >= this value
+ ERR_NOT_DIB = 0, // Tried to load a file, NOT a DIB!
+ ERR_MEMORY, // Not enough memory!
+ ERR_READ, // Error reading file!
+ ERR_LOCK, // Error on a GlobalLock()!
+ ERR_OPEN, // Error opening a file!
+ ERR_CREATEPAL, // Error creating palette.
+ ERR_GETDC, // Couldn't get a DC.
+ ERR_CREATEDDB, // Error create a DDB.
+ ERR_STRETCHBLT, // StretchBlt() returned failure.
+ ERR_STRETCHDIBITS, // StretchDIBits() returned failure.
+ ERR_SETDIBITSTODEVICE, // SetDIBitsToDevice() failed.
+ ERR_STARTDOC, // Error calling StartDoc().
+ ERR_NOGDIMODULE, // Couldn't find GDI module in memory.
+ ERR_SETABORTPROC, // Error calling SetAbortProc().
+ ERR_STARTPAGE, // Error calling StartPage().
+ ERR_NEWFRAME, // Error calling NEWFRAME escape.
+ ERR_ENDPAGE, // Error calling EndPage().
+ ERR_ENDDOC, // Error calling EndDoc().
+ ERR_SETDIBITS, // Error calling SetDIBits().
+ ERR_FILENOTFOUND, // Error opening file in GetDib()
+ ERR_INVALIDHANDLE, // Invalid Handle
+ ERR_DIBFUNCTION, // Error on call to DIB function
+ ERR_MAX // All error #s < this value
+ };
+
+
+
+/* Function prototypes */
+
+HDIB FAR BitmapToDIB (HBITMAP hBitmap, HPALETTE hPal);
+HDIB FAR ChangeBitmapFormat (HBITMAP hBitmap,
+ WORD wBitCount,
+ DWORD dwCompression,
+ HPALETTE hPal);
+HDIB FAR ChangeDIBFormat (HDIB hDIB, WORD wBitCount,
+ DWORD dwCompression);
+HBITMAP FAR CopyScreenToBitmap (LPRECT);
+HDIB FAR CopyScreenToDIB (LPRECT);
+HBITMAP FAR CopyWindowToBitmap (HWND, WORD);
+HDIB FAR CopyWindowToDIB (HWND, WORD);
+HPALETTE FAR CreateDIBPalette (HDIB hDIB);
+HDIB FAR CreateDIB(DWORD, DWORD, WORD);
+WORD FAR DestroyDIB (HDIB);
+void FAR DIBError (int ErrNo);
+DWORD FAR DIBHeight (LPCSTR lpDIB);
+WORD FAR DIBNumColors (LPCSTR lpDIB);
+HBITMAP FAR DIBToBitmap (HDIB hDIB, HPALETTE hPal);
+DWORD FAR DIBWidth (LPCSTR lpDIB);
+LPSTR FAR FindDIBBits (LPCSTR lpDIB);
+HPALETTE FAR GetSystemPalette (void);
+HDIB FAR LoadDIB (LPSTR);
+BOOL FAR PaintBitmap (HDC, LPRECT, HBITMAP, LPRECT, HPALETTE);
+BOOL FAR PaintDIB (HDC, LPRECT, HDIB, LPRECT, HPALETTE);
+int FAR PalEntriesOnDevice (HDC hDC);
+WORD FAR PaletteSize (LPCSTR lpDIB);
+WORD FAR PrintDIB (HDIB, WORD, WORD, WORD, LPSTR);
+WORD FAR PrintScreen (LPRECT, WORD, WORD, WORD, LPSTR);
+WORD FAR PrintWindow (HWND, WORD, WORD, WORD, WORD, LPSTR);
+WORD FAR SaveDIB (HDIB, LPSTR);
+
+// ajw added
+HDIB LoadDIB_FromMemory( const unsigned char* pData, DWORD dwBitsSize );
+
+#endif
diff --git a/CODE/DIBFILE.CPP b/CODE/DIBFILE.CPP
new file mode 100644
index 0000000..ddd1f49
--- /dev/null
+++ b/CODE/DIBFILE.CPP
@@ -0,0 +1,703 @@
+/*
+** Command & Conquer Red Alert(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 .
+*/
+
+//*******************************************************************
+//
+// file.c
+//
+// Source file for Device-Independent Bitmap (DIB) API. Provides
+// the following functions:
+//
+// SaveDIB() - Saves the specified dib in a file
+// LoadDIB() - Loads a DIB from a file
+// DestroyDIB() - Deletes DIB when finished using it
+//
+// Development Team: Mark Bader
+// Patrick Schreiber
+// Garrett McAuliffe
+// Eric Flo
+// Tony Claflin
+//
+// Written by Microsoft Product Support Services, Developer Support.
+// COPYRIGHT:
+//
+// (C) Copyright Microsoft Corp. 1993. All rights reserved.
+//
+// You have a royalty-free right to use, modify, reproduce and
+// distribute the Sample Files (and/or any modified version) in
+// any way you find useful, provided that you agree that
+// Microsoft has no warranty obligations or liability for any
+// Sample Application Files which are modified.
+//
+//*******************************************************************
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "dibutil.h"
+#include "dibapi.h"
+
+//#include "WolDebug.h"
+
+/*
+ * Dib Header Marker - used in writing DIBs to files
+ */
+#define DIB_HEADER_MARKER ((WORD) ('M' << 8) | 'B')
+
+/*********************************************************************
+ *
+ * Local Function Prototypes
+ *
+ *********************************************************************/
+
+
+HANDLE ReadDIBFile(int);
+BOOL MyRead(int, LPSTR, DWORD);
+BOOL SaveDIBFile(void);
+BOOL WriteDIB(LPSTR, HANDLE);
+DWORD PASCAL MyWrite(int, VOID FAR *, DWORD);
+
+/*************************************************************************
+ *
+ * LoadDIB()
+ *
+ * Loads the specified DIB from a file, allocates memory for it,
+ * and reads the disk file into the memory.
+ *
+ *
+ * Parameters:
+ *
+ * LPSTR lpFileName - specifies the file to load a DIB from
+ *
+ * Returns: A handle to a DIB, or NULL if unsuccessful.
+ *
+ * NOTE: The DIB API were not written to handle OS/2 DIBs; This
+ * function will reject any file that is not a Windows DIB.
+ *
+ * History: Date Author Reason
+ * 9/15/91 Mark Bader Based on DIBVIEW
+ *
+ *************************************************************************/
+
+
+HDIB FAR LoadDIB(LPSTR lpFileName)
+{
+ HDIB hDIB;
+ int hFile;
+ OFSTRUCT ofs;
+
+ /*
+ * Set the cursor to a hourglass, in case the loading operation
+ * takes more than a sec, the user will know what's going on.
+ */
+
+ SetCursor(LoadCursor(NULL, IDC_WAIT));
+ if ((hFile = OpenFile(lpFileName, &ofs, OF_READ)) != -1)
+ {
+ hDIB = ReadDIBFile(hFile);
+ _lclose(hFile);
+ SetCursor(LoadCursor(NULL, IDC_ARROW));
+ return hDIB;
+ }
+ else
+ {
+// DIBError(ERR_FILENOTFOUND);
+ SetCursor(LoadCursor(NULL, IDC_ARROW));
+ return NULL;
+ }
+}
+
+
+/*************************************************************************
+ *
+ * SaveDIB()
+ *
+ * Saves the specified DIB into the specified file name on disk. No
+ * error checking is done, so if the file already exists, it will be
+ * written over.
+ *
+ * Parameters:
+ *
+ * HDIB hDib - Handle to the dib to save
+ *
+ * LPSTR lpFileName - pointer to full pathname to save DIB under
+ *
+ * Return value: 0 if successful, or one of:
+ * ERR_INVALIDHANDLE
+ * ERR_OPEN
+ * ERR_LOCK
+ *
+ * History:
+ *
+ * NOTE: The DIB API were not written to handle OS/2 DIBs, so this
+ * function will not save a file if it is not a Windows DIB.
+ *
+ * History: Date Author Reason
+ * 9/15/91 Mark Bader Taken from DIBVIEW (which was taken
+ * from SHOWDIB)
+ * 1/30/92 Mark Bader Fixed problem of writing too many
+ * bytes to the file
+ * 6/24/92 Mark Bader Added check for OS/2 DIB
+ *
+ *************************************************************************/
+
+
+WORD FAR SaveDIB(HDIB hDib, LPSTR lpFileName)
+{
+ BITMAPFILEHEADER bmfHdr; // Header for Bitmap file
+ LPBITMAPINFOHEADER lpBI; // Pointer to DIB info structure
+ int fh; // file handle for opened file
+ OFSTRUCT of; // OpenFile structure
+ DWORD dwDIBSize;
+ DWORD dwError; // Error return from MyWrite
+
+ if (!hDib)
+ return ERR_INVALIDHANDLE;
+ fh = OpenFile(lpFileName, &of, OF_CREATE | OF_READWRITE);
+ if (fh == -1)
+ return ERR_OPEN;
+
+ /*
+ * Get a pointer to the DIB memory, the first of which contains
+ * a BITMAPINFO structure
+ */
+ lpBI = (LPBITMAPINFOHEADER)GlobalLock(hDib);
+ if (!lpBI)
+ return ERR_LOCK;
+
+ // Check to see if we're dealing with an OS/2 DIB. If so, don't
+ // save it because our functions aren't written to deal with these
+ // DIBs.
+
+ if (lpBI->biSize != sizeof(BITMAPINFOHEADER))
+ {
+ GlobalUnlock(hDib);
+ return ERR_NOT_DIB;
+ }
+
+ /*
+ * Fill in the fields of the file header
+ */
+
+ /* Fill in file type (first 2 bytes must be "BM" for a bitmap) */
+ bmfHdr.bfType = DIB_HEADER_MARKER; // "BM"
+
+ // Calculating the size of the DIB is a bit tricky (if we want to
+ // do it right). The easiest way to do this is to call GlobalSize()
+ // on our global handle, but since the size of our global memory may have
+ // been padded a few bytes, we may end up writing out a few too
+ // many bytes to the file (which may cause problems with some apps,
+ // like HC 3.0).
+ //
+ // So, instead let's calculate the size manually.
+ //
+ // To do this, find size of header plus size of color table. Since the
+ // first DWORD in both BITMAPINFOHEADER and BITMAPCOREHEADER conains
+ // the size of the structure, let's use this.
+
+ dwDIBSize = *(LPDWORD)lpBI + PaletteSize((LPSTR)lpBI); // Partial Calculation
+
+ // Now calculate the size of the image
+
+ if ((lpBI->biCompression == BI_RLE8) || (lpBI->biCompression == BI_RLE4)) {
+
+ // It's an RLE bitmap, we can't calculate size, so trust the
+ // biSizeImage field
+
+ dwDIBSize += lpBI->biSizeImage;
+ }
+ else {
+ DWORD dwBmBitsSize; // Size of Bitmap Bits only
+
+ // It's not RLE, so size is Width (DWORD aligned) * Height
+
+ dwBmBitsSize = WIDTHBYTES((lpBI->biWidth)*((DWORD)lpBI->biBitCount)) * lpBI->biHeight;
+
+ dwDIBSize += dwBmBitsSize;
+
+ // Now, since we have calculated the correct size, why don't we
+ // fill in the biSizeImage field (this will fix any .BMP files which
+ // have this field incorrect).
+
+ lpBI->biSizeImage = dwBmBitsSize;
+ }
+
+
+ // Calculate the file size by adding the DIB size to sizeof(BITMAPFILEHEADER)
+
+ bmfHdr.bfSize = dwDIBSize + sizeof(BITMAPFILEHEADER);
+ bmfHdr.bfReserved1 = 0;
+ bmfHdr.bfReserved2 = 0;
+
+ /*
+ * Now, calculate the offset the actual bitmap bits will be in
+ * the file -- It's the Bitmap file header plus the DIB header,
+ * plus the size of the color table.
+ */
+ bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + lpBI->biSize +
+ PaletteSize((LPSTR)lpBI);
+
+ /* Write the file header */
+ _lwrite(fh, (LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER));
+
+ /*
+ * Write the DIB header and the bits -- use local version of
+ * MyWrite, so we can write more than 32767 bytes of data
+ */
+ dwError = MyWrite(fh, (LPSTR)lpBI, dwDIBSize);
+ GlobalUnlock(hDib);
+ _lclose(fh);
+
+ if (dwError == 0)
+ return ERR_OPEN; // oops, something happened in the write
+ else
+ return 0; // Success code
+}
+
+
+/*************************************************************************
+ *
+ * DestroyDIB ()
+ *
+ * Purpose: Frees memory associated with a DIB
+ *
+ * Returns: Nothing
+ *
+ * History: Date Author Reason
+ * 9/15/91 Mark Bader Created
+ *
+ *************************************************************************/
+
+
+WORD FAR DestroyDIB(HDIB hDib)
+{
+ GlobalFree(hDib);
+ return 0;
+}
+
+
+//************************************************************************
+//
+// Auxiliary Functions which the above procedures use
+//
+//************************************************************************
+
+
+/*************************************************************************
+ *
+ * Function: ReadDIBFile (int)
+ *
+ * Purpose: Reads in the specified DIB file into a global chunk of
+ * memory.
+ *
+ * Returns: A handle to a dib (hDIB) if successful.
+ * NULL if an error occurs.
+ *
+ * Comments: BITMAPFILEHEADER is stripped off of the DIB. Everything
+ * from the end of the BITMAPFILEHEADER structure on is
+ * returned in the global memory handle.
+ *
+ *
+ * NOTE: The DIB API were not written to handle OS/2 DIBs, so this
+ * function will reject any file that is not a Windows DIB.
+ *
+ * History: Date Author Reason
+ * 9/15/91 Mark Bader Based on DIBVIEW
+ * 6/25/92 Mark Bader Added check for OS/2 DIB
+ * 7/21/92 Mark Bader Added code to deal with bfOffBits
+ * field in BITMAPFILEHEADER
+ * 9/11/92 Mark Bader Fixed Realloc Code to free original mem
+ *
+ *************************************************************************/
+
+HANDLE ReadDIBFile(int hFile)
+{
+ BITMAPFILEHEADER bmfHeader;
+ DWORD dwBitsSize;
+ UINT nNumColors; // Number of colors in table
+ HANDLE hDIB;
+ HANDLE hDIBtmp; // Used for GlobalRealloc() //MPB
+ LPBITMAPINFOHEADER lpbi;
+ DWORD offBits;
+
+ /*
+ * get length of DIB in bytes for use when reading
+ */
+
+ dwBitsSize = filelength(hFile);
+
+ // Allocate memory for header & color table. We'll enlarge this
+ // memory as needed.
+
+ hDIB = GlobalAlloc(GMEM_MOVEABLE,
+ (DWORD)(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD)));
+
+ if (!hDIB) return NULL;
+
+ lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB);
+ if (!lpbi)
+ {
+ GlobalFree(hDIB);
+ return NULL;
+ }
+
+ // read the BITMAPFILEHEADER from our file
+
+ if (sizeof (BITMAPFILEHEADER) != _lread (hFile, (LPSTR)&bmfHeader, sizeof (BITMAPFILEHEADER)))
+ goto ErrExit;
+
+ if (bmfHeader.bfType != 0x4d42) /* 'BM' */
+ goto ErrExit;
+
+ // read the BITMAPINFOHEADER
+
+ if (sizeof(BITMAPINFOHEADER) != _lread (hFile, (LPSTR)lpbi, sizeof(BITMAPINFOHEADER)))
+ goto ErrExit;
+
+ // Check to see that it's a Windows DIB -- an OS/2 DIB would cause
+ // strange problems with the rest of the DIB API since the fields
+ // in the header are different and the color table entries are
+ // smaller.
+ //
+ // If it's not a Windows DIB (e.g. if biSize is wrong), return NULL.
+
+ if (lpbi->biSize == sizeof(BITMAPCOREHEADER))
+ goto ErrExit;
+
+ // Now determine the size of the color table and read it. Since the
+ // bitmap bits are offset in the file by bfOffBits, we need to do some
+ // special processing here to make sure the bits directly follow
+ // the color table (because that's the format we are susposed to pass
+ // back)
+ nNumColors = (UINT)lpbi->biClrUsed;
+ if (!nNumColors)
+ {
+ // no color table for 24-bit, default size otherwise
+ if (lpbi->biBitCount != 24)
+ nNumColors = 1 << lpbi->biBitCount; /* standard size table */
+ }
+
+ // fill in some default values if they are zero
+ if (lpbi->biClrUsed == 0)
+ lpbi->biClrUsed = nNumColors;
+
+ if (lpbi->biSizeImage == 0)
+ {
+ lpbi->biSizeImage = ((((lpbi->biWidth * (DWORD)lpbi->biBitCount) + 31) & ~31) >> 3)
+ * lpbi->biHeight;
+ }
+
+ // get a proper-sized buffer for header, color table and bits
+ GlobalUnlock(hDIB);
+ hDIBtmp = GlobalReAlloc(hDIB, lpbi->biSize +
+ nNumColors * sizeof(RGBQUAD) +
+ lpbi->biSizeImage, 0);
+
+ if (!hDIBtmp) // can't resize buffer for loading
+ goto ErrExitNoUnlock; //MPB
+ else
+ hDIB = hDIBtmp;
+
+ lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB);
+
+ // read the color table
+ _lread (hFile, (LPSTR)(lpbi) + lpbi->biSize, nNumColors * sizeof(RGBQUAD));
+
+ // offset to the bits from start of DIB header
+ offBits = lpbi->biSize + nNumColors * sizeof(RGBQUAD);
+
+ // If the bfOffBits field is non-zero, then the bits might *not* be
+ // directly following the color table in the file. Use the value in
+ // bfOffBits to seek the bits.
+
+ if (bmfHeader.bfOffBits != 0L)
+ _llseek(hFile, bmfHeader.bfOffBits, SEEK_SET);
+
+ if (MyRead(hFile, (LPSTR)lpbi + offBits, lpbi->biSizeImage))
+ goto OKExit;
+
+
+ErrExit:
+ GlobalUnlock(hDIB);
+ErrExitNoUnlock:
+ GlobalFree(hDIB);
+ return NULL;
+
+OKExit:
+ GlobalUnlock(hDIB);
+ return hDIB;
+}
+
+/*************************************************************************
+
+ Function: MyRead (int, LPSTR, DWORD)
+
+ Purpose: Routine to read files greater than 64K in size.
+
+ Returns: TRUE if successful.
+ FALSE if an error occurs.
+
+
+ History: Date Author Reason
+ 9/15/91 Mark Bader Based on DIBVIEW
+
+*************************************************************************/
+
+
+BOOL MyRead(int hFile, LPSTR lpBuffer, DWORD dwSize)
+{
+ char huge *lpInBuf = (char huge *)lpBuffer;
+ int nBytes;
+
+ /*
+ * Read in the data in 32767 byte chunks (or a smaller amount if it's
+ * the last chunk of data read)
+ */
+
+ while (dwSize)
+ {
+ nBytes = (int)(dwSize > (DWORD)32767 ? 32767 : LOWORD (dwSize));
+ if (_lread(hFile, (LPSTR)lpInBuf, nBytes) != (WORD)nBytes)
+ return FALSE;
+ dwSize -= nBytes;
+ lpInBuf += nBytes;
+ }
+ return TRUE;
+}
+
+
+/****************************************************************************
+
+ FUNCTION : MyWrite(int fh, VOID FAR *pv, DWORD ul)
+
+ PURPOSE : Writes data in steps of 32k till all the data is written.
+ Normal _lwrite uses a WORD as 3rd parameter, so it is
+ limited to 32767 bytes, but this procedure is not.
+
+ RETURNS : 0 - If write did not proceed correctly.
+ number of bytes written otherwise.
+
+ History: Date Author Reason
+ 9/15/91 Mark Bader Based on DIBVIEW
+
+ ****************************************************************************/
+
+
+DWORD PASCAL MyWrite(int iFileHandle, VOID FAR *lpBuffer, DWORD dwBytes)
+{
+ DWORD dwBytesTmp = dwBytes; // Save # of bytes for return value
+ BYTE huge *hpBuffer = (BYTE huge *)lpBuffer; // make a huge pointer to the data
+
+ /*
+ * Write out the data in 32767 byte chunks.
+ */
+
+ while (dwBytes > 32767)
+ {
+ if (_lwrite(iFileHandle, (LPSTR)hpBuffer, (WORD)32767) != 32767)
+ return 0;
+ dwBytes -= 32767;
+ hpBuffer += 32767;
+ }
+
+ /* Write out the last chunk (which is < 32767 bytes) */
+ if (_lwrite(iFileHandle, (LPSTR)hpBuffer, (WORD)dwBytes) != (WORD)dwBytes)
+ return 0;
+ return dwBytesTmp;
+}
+
+// ajw added
+// Added to allow "loading" from a location in memory.
+// A modification of ReadDIBFile(), above.
+//***********************************************************************************************
+HDIB LoadDIB_FromMemory( const unsigned char* pData, DWORD dwBitsSize )
+{
+ BITMAPFILEHEADER bmfHeader;
+ UINT nNumColors; // Number of colors in table
+ HANDLE hDIB;
+ HANDLE hDIBtmp; // Used for GlobalRealloc() //MPB
+ LPBITMAPINFOHEADER lpbi;
+ DWORD offBits;
+
+ const unsigned char* const pDataStart = pData;
+ const unsigned char* pDataEnd = pData + dwBitsSize; // One char past end of "file".
+
+ // Allocate memory for header & color table. We'll enlarge this
+ // memory as needed.
+
+// debugprint( "LoadDIB_FromMemory, GlobalAlloc\n" );
+ hDIB = GlobalAlloc(GMEM_MOVEABLE, (DWORD)(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD)));
+// debugprint( "hDIB from GlobalALloc is %i\n", hDIB );
+
+ if (!hDIB)
+ {
+// debugprint( "LoadDIB_FromMemory error: failed alloc\n" );
+ return NULL;
+ }
+
+// debugprint( "LoadDIB_FromMemory, lpbi Lock\n" );
+ lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB);
+// debugprint( "lpbi is %i\n", lpbi );
+ if (!lpbi)
+ {
+// debugprint( "LoadDIB_FromMemory error: failed lock\n" );
+ GlobalFree(hDIB);
+ return NULL;
+ }
+
+ // read the BITMAPFILEHEADER from our file
+// if (sizeof (BITMAPFILEHEADER) != _lread (hFile, (LPSTR)&bmfHeader, sizeof (BITMAPFILEHEADER)))
+// goto ErrExit;
+ if( pData + sizeof( BITMAPFILEHEADER ) >= pDataEnd )
+ {
+// debugprint( "LoadDIB_FromMemory error: bad size\n" );
+ goto ErrExit;
+ }
+// debugprint( "LoadDIB_FromMemory, memcpy BITMAPFILEHEADER %i bytes\n", sizeof( BITMAPFILEHEADER ) );
+ memcpy( &bmfHeader, pData, sizeof( BITMAPFILEHEADER ) );
+ pData += sizeof( BITMAPFILEHEADER );
+
+ if (bmfHeader.bfType != 0x4d42) /* 'BM' */
+ {
+// debugprint( "LoadDIB_FromMemory error: no BM\n" );
+ goto ErrExit;
+ }
+
+ // read the BITMAPINFOHEADER
+// if (sizeof(BITMAPINFOHEADER) != _lread (hFile, (LPSTR)lpbi, sizeof(BITMAPINFOHEADER)))
+// goto ErrExit;
+ if( pData + sizeof( BITMAPINFOHEADER ) >= pDataEnd )
+ {
+// debugprint( "LoadDIB_FromMemory error: bad size 2\n" );
+ goto ErrExit;
+ }
+// debugprint( "LoadDIB_FromMemory, memcpy BITMAPINFOHEADER %i bytes\n", sizeof( BITMAPINFOHEADER ) );
+ memcpy( lpbi, pData, sizeof( BITMAPINFOHEADER ) );
+ pData += sizeof( BITMAPINFOHEADER );
+
+ // Check to see that it's a Windows DIB -- an OS/2 DIB would cause
+ // strange problems with the rest of the DIB API since the fields
+ // in the header are different and the color table entries are
+ // smaller.
+ //
+ // If it's not a Windows DIB (e.g. if biSize is wrong), return NULL.
+
+ if (lpbi->biSize == sizeof(BITMAPCOREHEADER))
+ {
+// debugprint( "LoadDIB_FromMemory error: lpbi->biSize bad\n" );
+ goto ErrExit;
+ }
+
+ if( lpbi->biCompression != BI_RGB )
+ {
+// debugprint( "LoadDIB_FromMemory error: Image is compressed\n" );
+ goto ErrExit;
+ }
+
+ // Now determine the size of the color table and read it. Since the
+ // bitmap bits are offset in the file by bfOffBits, we need to do some
+ // special processing here to make sure the bits directly follow
+ // the color table (because that's the format we are susposed to pass
+ // back)
+ nNumColors = (UINT)lpbi->biClrUsed;
+ if (!nNumColors)
+ {
+ // no color table for 24-bit, default size otherwise
+ if (lpbi->biBitCount != 24)
+ nNumColors = 1 << lpbi->biBitCount; /* standard size table */
+ }
+
+ // fill in some default values if they are zero
+ if (lpbi->biClrUsed == 0)
+ lpbi->biClrUsed = nNumColors;
+
+// debugprint( "biSizeImage is %i. I would say it was %i, because the bpp is %i.\n", lpbi->biSizeImage, ((((lpbi->biWidth * (DWORD)lpbi->biBitCount) + 31) & ~31) >> 3) * lpbi->biHeight, lpbi->biBitCount );
+ if (lpbi->biSizeImage == 0)
+ {
+ lpbi->biSizeImage = ((((lpbi->biWidth * (DWORD)lpbi->biBitCount) + 31) & ~31) >> 3) * lpbi->biHeight;
+ }
+
+ // get a proper-sized buffer for header, color table and bits
+ GlobalUnlock(hDIB);
+// debugprint( "LoadDIB_FromMemory, GlobalReAlloc: lpbi->biSize=%i, nNumColors=%i, lpbi->biSizeImage=%i\n", lpbi->biSize, nNumColors,lpbi->biSizeImage );
+ hDIBtmp = GlobalReAlloc(hDIB, lpbi->biSize + nNumColors * sizeof(RGBQUAD) + lpbi->biSizeImage, 0);
+
+ if (!hDIBtmp) // can't resize buffer for loading
+ {
+// debugprint( "LoadDIB_FromMemory error: realloc failed\n" );
+ goto ErrExitNoUnlock; //MPB
+ }
+ else
+ hDIB = hDIBtmp;
+
+ lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB);
+
+ // read the color table
+// _lread (hFile, (LPSTR)(lpbi) + lpbi->biSize, nNumColors * sizeof(RGBQUAD));
+// debugprint( "LoadDIB_FromMemory, memcpy color table %i colors, so %i bytes\n", nNumColors, nNumColors * sizeof(RGBQUAD) );
+ memcpy( (LPSTR)(lpbi) + lpbi->biSize, pData, nNumColors * sizeof(RGBQUAD) );
+ pData += nNumColors * sizeof(RGBQUAD);
+
+ // offset to the bits from start of DIB header
+ offBits = lpbi->biSize + nNumColors * sizeof(RGBQUAD);
+
+ // If the bfOffBits field is non-zero, then the bits might *not* be
+ // directly following the color table in the file. Use the value in
+ // bfOffBits to seek the bits.
+
+ if (bmfHeader.bfOffBits != 0L)
+// _llseek(hFile, bmfHeader.bfOffBits, SEEK_SET);
+ pData = pDataStart + bmfHeader.bfOffBits;
+
+// debugprint( "bmfHeader.bfOffBits is %i\n", bmfHeader.bfOffBits );
+
+// if (MyRead(hFile, (LPSTR)lpbi + offBits, lpbi->biSizeImage))
+// goto OKExit;
+// debugprint( "Checking that pData(%i) + biSizeImage(%i), which is %i, is equal to pDataEnd(%i)\n",
+// pData, lpbi->biSizeImage, pData + lpbi->biSizeImage, pDataEnd );
+// if( pData + lpbi->biSizeImage != pDataEnd ) condition relaxed
+// {
+// debugprint( "LoadDIB_FromMemory error: bad size 3\n" );
+// goto ErrExit;
+// }
+
+// debugprint( "LoadDIB_FromMemory, memcpy the bits, %i bytes. Image is w %i, h.%i\n",
+// lpbi->biSizeImage, lpbi->biWidth, lpbi->biHeight );
+// debugprint( "Writing to lpbi (%i) + offBits (%i)\n", lpbi, offBits );
+
+ memcpy( (LPSTR)lpbi + offBits, pData, lpbi->biSizeImage );
+// pData += lpbi->biSizeImage;
+// if( pData != pDataEnd ) // Should end up one byte past end of data. - condition relaxed
+// debugprint( "LoadDIB_FromMemory: ERROR! Ended up at %i instead of %i\n", pData, pDataEnd );
+ goto OKExit;
+
+ErrExit:
+ GlobalUnlock(hDIB);
+ErrExitNoUnlock:
+ GlobalFree(hDIB);
+// debugprint( "LoadDIB_FromMemory Error!\n" );
+ return NULL;
+
+OKExit:
+ GlobalUnlock(hDIB);
+ return hDIB;
+}
diff --git a/CODE/DIBUTIL.CPP b/CODE/DIBUTIL.CPP
new file mode 100644
index 0000000..07d0ccd
--- /dev/null
+++ b/CODE/DIBUTIL.CPP
@@ -0,0 +1,1316 @@
+/*
+** Command & Conquer Red Alert(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 .
+*/
+
+//**********************************************************************
+//
+// dibutil.c
+//
+// Source file for Device-Independent Bitmap (DIB) API. Provides
+// the following functions:
+//
+// CreateDIB() - Creates new DIB
+// FindDIBBits() - Sets pointer to the DIB bits
+// DIBWidth() - Gets the width of the DIB
+// DIBHeight() - Gets the height of the DIB
+// PaletteSize() - Calculates the buffer size required by a palette
+// DIBNumColors() - Calculates number of colors in the DIB's color table
+// CreateDIBPalette() - Creates a palette from a DIB
+// DIBToBitmap() - Creates a bitmap from a DIB
+// BitmapToDIB() - Creates a DIB from a bitmap
+// PalEntriesOnDevice()- Gets the number of palette entries of a device
+// GetSystemPalette() - Returns a handle to the current system palette
+// AllocRoomForDIB() - Allocates memory for a DIB
+// ChangeDIBFormat() - Changes a DIB's BPP and/or compression format
+// ChangeBitmapFormat()- Changes a bitmap to a DIB with specified BPP and
+// compression format
+//
+// Development Team: Mark Bader
+// Patrick Schreiber
+// Garrett McAuliffe
+// Eric Flo
+// Tony Claflin
+//
+// Written by Microsoft Product Support Services, Developer Support.
+// COPYRIGHT:
+//
+// (C) Copyright Microsoft Corp. 1993. All rights reserved.
+//
+// You have a royalty-free right to use, modify, reproduce and
+// distribute the Sample Files (and/or any modified version) in
+// any way you find useful, provided that you agree that
+// Microsoft has no warranty obligations or liability for any
+// Sample Application Files which are modified.
+//
+//**********************************************************************
+
+/* header files */
+#include
+#include
+#include "dibapi.h"
+#include "dibutil.h"
+#include
+
+
+/*************************************************************************
+ *
+ * CreateDIB()
+ *
+ * Parameters:
+ *
+ * DWORD dwWidth - Width for new bitmap, in pixels
+ * DWORD dwHeight - Height for new bitmap
+ * WORD wBitCount - Bit Count for new DIB (1, 4, 8, or 24)
+ *
+ * Return Value:
+ *
+ * HDIB - Handle to new DIB
+ *
+ * Description:
+ *
+ * This function allocates memory for and initializes a new DIB by
+ * filling in the BITMAPINFOHEADER, allocating memory for the color
+ * table, and allocating memory for the bitmap bits. As with all
+ * HDIBs, the header, colortable and bits are all in one contiguous
+ * memory block. This function is similar to the CreateBitmap()
+ * Windows API.
+ *
+ * The colortable and bitmap bits are left uninitialized (zeroed) in the
+ * returned HDIB.
+ *
+ *
+ * History: Date Author Reason
+ * 3/20/92 Mark Bader Created
+ *
+ ************************************************************************/
+
+HDIB FAR CreateDIB(DWORD dwWidth, DWORD dwHeight, WORD wBitCount)
+{
+ BITMAPINFOHEADER bi; // bitmap header
+ LPBITMAPINFOHEADER lpbi; // pointer to BITMAPINFOHEADER
+ DWORD dwLen; // size of memory block
+ HDIB hDIB;
+ DWORD dwBytesPerLine; // Number of bytes per scanline
+
+
+ // Make sure bits per pixel is valid
+ if (wBitCount <= 1)
+ wBitCount = 1;
+ else if (wBitCount <= 4)
+ wBitCount = 4;
+ else if (wBitCount <= 8)
+ wBitCount = 8;
+ else if (wBitCount <= 24)
+ wBitCount = 24;
+ else
+ wBitCount = 4; // set default value to 4 if parameter is bogus
+
+ // initialize BITMAPINFOHEADER
+ bi.biSize = sizeof(BITMAPINFOHEADER);
+ bi.biWidth = dwWidth; // fill in width from parameter
+ bi.biHeight = dwHeight; // fill in height from parameter
+ bi.biPlanes = 1; // must be 1
+ bi.biBitCount = wBitCount; // from parameter
+ bi.biCompression = BI_RGB;
+ bi.biSizeImage = 0; // 0's here mean "default"
+ bi.biXPelsPerMeter = 0;
+ bi.biYPelsPerMeter = 0;
+ bi.biClrUsed = 0;
+ bi.biClrImportant = 0;
+
+ // calculate size of memory block required to store the DIB. This
+ // block should be big enough to hold the BITMAPINFOHEADER, the color
+ // table, and the bits
+
+ dwBytesPerLine = WIDTHBYTES(wBitCount * dwWidth);
+ dwLen = bi.biSize + PaletteSize((LPSTR)&bi) + (dwBytesPerLine * dwHeight);
+
+ // alloc memory block to store our bitmap
+ hDIB = GlobalAlloc(GHND, dwLen);
+
+ // major bummer if we couldn't get memory block
+ if (!hDIB)
+ {
+ return NULL;
+ }
+
+ // lock memory and get pointer to it
+ lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB);
+
+ // use our bitmap info structure to fill in first part of
+ // our DIB with the BITMAPINFOHEADER
+ *lpbi = bi;
+
+ // Since we don't know what the colortable and bits should contain,
+ // just leave these blank. Unlock the DIB and return the HDIB.
+
+ GlobalUnlock(hDIB);
+
+ /* return handle to the DIB */
+ return hDIB;
+}
+
+
+
+/*************************************************************************
+ *
+ * FindDIBBits()
+ *
+ * Parameter:
+ *
+ * LPSTR lpDIB - pointer to packed-DIB memory block
+ *
+ * Return Value:
+ *
+ * LPSTR - pointer to the DIB bits
+ *
+ * Description:
+ *
+ * This function calculates the address of the DIB's bits and returns a
+ * pointer to the DIB bits.
+ *
+ * History: Date Author Reason
+ * 6/01/91 Garrett McAuliffe Created
+ * 9/15/91 Patrick Schreiber Added header and comments
+ *
+ ************************************************************************/
+
+
+LPSTR FAR FindDIBBits(LPCSTR lpDIB)
+{
+ return (LPSTR)(lpDIB + *(LPDWORD)lpDIB + PaletteSize(lpDIB));
+}
+
+
+/*************************************************************************
+ *
+ * DIBWidth()
+ *
+ * Parameter:
+ *
+ * LPSTR lpDIB - pointer to packed-DIB memory block
+ *
+ * Return Value:
+ *
+ * DWORD - width of the DIB
+ *
+ * Description:
+ *
+ * This function gets the width of the DIB from the BITMAPINFOHEADER
+ * width field if it is a Windows 3.0-style DIB or from the BITMAPCOREHEADER
+ * width field if it is an OS/2-style DIB.
+ *
+ * History: Date Author Reason
+ * 6/01/91 Garrett McAuliffe Created
+ * 9/15/91 Patrick Schreiber Added header and comments
+ *
+ ************************************************************************/
+
+
+DWORD FAR DIBWidth(LPCSTR lpDIB)
+{
+ LPBITMAPINFOHEADER lpbmi; // pointer to a Win 3.0-style DIB
+ LPBITMAPCOREHEADER lpbmc; // pointer to an OS/2-style DIB
+
+ /* point to the header (whether Win 3.0 and OS/2) */
+
+ lpbmi = (LPBITMAPINFOHEADER)lpDIB;
+ lpbmc = (LPBITMAPCOREHEADER)lpDIB;
+
+ /* return the DIB width if it is a Win 3.0 DIB */
+ if (lpbmi->biSize == sizeof(BITMAPINFOHEADER))
+ return lpbmi->biWidth;
+ else /* it is an OS/2 DIB, so return its width */
+ return (DWORD)lpbmc->bcWidth;
+}
+
+
+/*************************************************************************
+ *
+ * DIBHeight()
+ *
+ * Parameter:
+ *
+ * LPSTR lpDIB - pointer to packed-DIB memory block
+ *
+ * Return Value:
+ *
+ * DWORD - height of the DIB
+ *
+ * Description:
+ *
+ * This function gets the height of the DIB from the BITMAPINFOHEADER
+ * height field if it is a Windows 3.0-style DIB or from the BITMAPCOREHEADER
+ * height field if it is an OS/2-style DIB.
+ *
+ * History: Date Author Reason
+ * 6/01/91 Garrett McAuliffe Created
+ * 9/15/91 Patrick Schreiber Added header and comments
+ *
+ ************************************************************************/
+
+
+DWORD FAR DIBHeight(LPCSTR lpDIB)
+{
+ LPBITMAPINFOHEADER lpbmi; // pointer to a Win 3.0-style DIB
+ LPBITMAPCOREHEADER lpbmc; // pointer to an OS/2-style DIB
+
+ /* point to the header (whether OS/2 or Win 3.0 */
+
+ lpbmi = (LPBITMAPINFOHEADER)lpDIB;
+ lpbmc = (LPBITMAPCOREHEADER)lpDIB;
+
+ /* return the DIB height if it is a Win 3.0 DIB */
+ if (lpbmi->biSize == sizeof(BITMAPINFOHEADER))
+ return lpbmi->biHeight;
+ else /* it is an OS/2 DIB, so return its height */
+ return (DWORD)lpbmc->bcHeight;
+}
+
+
+/*************************************************************************
+ *
+ * PaletteSize()
+ *
+ * Parameter:
+ *
+ * LPSTR lpDIB - pointer to packed-DIB memory block
+ *
+ * Return Value:
+ *
+ * WORD - size of the color palette of the DIB
+ *
+ * Description:
+ *
+ * This function gets the size required to store the DIB's palette by
+ * multiplying the number of colors by the size of an RGBQUAD (for a
+ * Windows 3.0-style DIB) or by the size of an RGBTRIPLE (for an OS/2-
+ * style DIB).
+ *
+ * History: Date Author Reason
+ * 6/01/91 Garrett McAuliffe Created
+ * 9/15/91 Patrick Schreiber Added header and comments
+ *
+ ************************************************************************/
+
+
+WORD FAR PaletteSize(LPCSTR lpDIB)
+{
+ /* calculate the size required by the palette */
+ if (IS_WIN30_DIB (lpDIB))
+ return (WORD FAR)(DIBNumColors(lpDIB) * sizeof(RGBQUAD));
+ else
+ return (WORD FAR)(DIBNumColors(lpDIB) * sizeof(RGBTRIPLE));
+}
+
+
+/*************************************************************************
+ *
+ * DIBNumColors()
+ *
+ * Parameter:
+ *
+ * LPSTR lpDIB - pointer to packed-DIB memory block
+ *
+ * Return Value:
+ *
+ * WORD - number of colors in the color table
+ *
+ * Description:
+ *
+ * This function calculates the number of colors in the DIB's color table
+ * by finding the bits per pixel for the DIB (whether Win3.0 or OS/2-style
+ * DIB). If bits per pixel is 1: colors=2, if 4: colors=16, if 8: colors=256,
+ * if 24, no colors in color table.
+ *
+ * History: Date Author Reason
+ * 6/01/91 Garrett McAuliffe Created
+ * 9/15/91 Patrick Schreiber Added header and comments
+ *
+ ************************************************************************/
+
+
+WORD FAR DIBNumColors(LPCSTR lpDIB)
+{
+ WORD wBitCount; // DIB bit count
+
+ /* If this is a Windows-style DIB, the number of colors in the
+ * color table can be less than the number of bits per pixel
+ * allows for (i.e. lpbi->biClrUsed can be set to some value).
+ * If this is the case, return the appropriate value.
+ */
+
+ if (IS_WIN30_DIB(lpDIB))
+ {
+ DWORD dwClrUsed;
+
+ dwClrUsed = ((LPBITMAPINFOHEADER)lpDIB)->biClrUsed;
+ if (dwClrUsed)
+ return (WORD)dwClrUsed;
+ }
+
+ /* Calculate the number of colors in the color table based on
+ * the number of bits per pixel for the DIB.
+ */
+ if (IS_WIN30_DIB(lpDIB))
+ wBitCount = ((LPBITMAPINFOHEADER)lpDIB)->biBitCount;
+ else
+ wBitCount = ((LPBITMAPCOREHEADER)lpDIB)->bcBitCount;
+
+ /* return number of colors based on bits per pixel */
+ switch (wBitCount)
+ {
+ case 1:
+ return 2;
+
+ case 4:
+ return 16;
+
+ case 8:
+ return 256;
+
+ default:
+ return 0;
+ }
+}
+
+
+/*************************************************************************
+ *
+ * CreateDIBPalette()
+ *
+ * Parameter:
+ *
+ * HDIB hDIB - specifies the DIB
+ *
+ * Return Value:
+ *
+ * HPALETTE - specifies the palette
+ *
+ * Description:
+ *
+ * This function creates a palette from a DIB by allocating memory for the
+ * logical palette, reading and storing the colors from the DIB's color table
+ * into the logical palette, creating a palette from this logical palette,
+ * and then returning the palette's handle. This allows the DIB to be
+ * displayed using the best possible colors (important for DIBs with 256 or
+ * more colors).
+ *
+ * History: Date Author Reason
+ * 6/01/91 Garrett McAuliffe Created
+ * 9/15/91 Patrick Schreiber Added header and comments
+ *
+ ************************************************************************/
+
+
+HPALETTE FAR CreateDIBPalette(HDIB hDIB)
+{
+ LPLOGPALETTE lpPal; // pointer to a logical palette
+ HANDLE hLogPal; // handle to a logical palette
+ HPALETTE hPal = NULL; // handle to a palette
+ int i, wNumColors; // loop index, number of colors in color table
+ LPSTR lpbi; // pointer to packed-DIB
+ LPBITMAPINFO lpbmi; // pointer to BITMAPINFO structure (Win3.0)
+ LPBITMAPCOREINFO lpbmc; // pointer to BITMAPCOREINFO structure (OS/2)
+ BOOL bWinStyleDIB; // flag which signifies whether this is a Win3.0 DIB
+
+ /* if handle to DIB is invalid, return NULL */
+
+ if (!hDIB)
+ return NULL;
+
+ /* lock DIB memory block and get a pointer to it */
+ lpbi = (LPSTR)GlobalLock(hDIB);
+
+ /* get pointer to BITMAPINFO (Win 3.0) */
+ lpbmi = (LPBITMAPINFO)lpbi;
+
+ /* get pointer to BITMAPCOREINFO (OS/2 1.x) */
+ lpbmc = (LPBITMAPCOREINFO)lpbi;
+
+ /* get the number of colors in the DIB */
+ wNumColors = DIBNumColors(lpbi);
+
+ /* is this a Win 3.0 DIB? */
+ bWinStyleDIB = IS_WIN30_DIB(lpbi);
+ if (wNumColors)
+ {
+ /* allocate memory block for logical palette */
+ hLogPal = GlobalAlloc(GHND, sizeof(LOGPALETTE) + sizeof(PALETTEENTRY) *
+ wNumColors);
+
+ /* if not enough memory, clean up and return NULL */
+ if (!hLogPal)
+ {
+ GlobalUnlock(hDIB);
+ return NULL;
+ }
+
+ /* lock memory block and get pointer to it */
+ lpPal = (LPLOGPALETTE)GlobalLock(hLogPal);
+
+ /* set version and number of palette entries */
+ lpPal->palVersion = PALVERSION;
+ lpPal->palNumEntries = (WORD)wNumColors;
+
+ /* store RGB triples (if Win 3.0 DIB) or RGB quads (if OS/2 DIB)
+ * into palette
+ */
+ for (i = 0; i < wNumColors; i++)
+ {
+ if (bWinStyleDIB)
+ {
+ lpPal->palPalEntry[i].peRed = lpbmi->bmiColors[i].rgbRed;
+ lpPal->palPalEntry[i].peGreen = lpbmi->bmiColors[i].rgbGreen;
+ lpPal->palPalEntry[i].peBlue = lpbmi->bmiColors[i].rgbBlue;
+ lpPal->palPalEntry[i].peFlags = 0;
+ }
+ else
+ {
+ lpPal->palPalEntry[i].peRed = lpbmc->bmciColors[i].rgbtRed;
+ lpPal->palPalEntry[i].peGreen = lpbmc->bmciColors[i].rgbtGreen;
+ lpPal->palPalEntry[i].peBlue = lpbmc->bmciColors[i].rgbtBlue;
+ lpPal->palPalEntry[i].peFlags = 0;
+ }
+ }
+
+ /* create the palette and get handle to it */
+ hPal = CreatePalette(lpPal);
+
+ /* if error getting handle to palette, clean up and return NULL */
+ if (!hPal)
+ {
+ GlobalUnlock(hLogPal);
+ GlobalFree(hLogPal);
+ return NULL;
+ }
+ }
+
+ /* clean up */
+ GlobalUnlock(hLogPal);
+ GlobalFree(hLogPal);
+ GlobalUnlock(hDIB);
+
+ /* return handle to DIB's palette */
+ return hPal;
+}
+
+
+/*************************************************************************
+ *
+ * DIBToBitmap()
+ *
+ * Parameters:
+ *
+ * HDIB hDIB - specifies the DIB to convert
+ *
+ * HPALETTE hPal - specifies the palette to use with the bitmap
+ *
+ * Return Value:
+ *
+ * HBITMAP - identifies the device-dependent bitmap
+ *
+ * Description:
+ *
+ * This function creates a bitmap from a DIB using the specified palette.
+ * If no palette is specified, default is used.
+ *
+ * NOTE:
+ *
+ * The bitmap returned from this funciton is always a bitmap compatible
+ * with the screen (e.g. same bits/pixel and color planes) rather than
+ * a bitmap with the same attributes as the DIB. This behavior is by
+ * design, and occurs because this function calls CreateDIBitmap to
+ * do its work, and CreateDIBitmap always creates a bitmap compatible
+ * with the hDC parameter passed in (because it in turn calls
+ * CreateCompatibleBitmap).
+ *
+ * So for instance, if your DIB is a monochrome DIB and you call this
+ * function, you will not get back a monochrome HBITMAP -- you will
+ * get an HBITMAP compatible with the screen DC, but with only 2
+ * colors used in the bitmap.
+ *
+ * If your application requires a monochrome HBITMAP returned for a
+ * monochrome DIB, use the function SetDIBits().
+ *
+ * Also, the DIBpassed in to the function is not destroyed on exit. This
+ * must be done later, once it is no longer needed.
+ *
+ * History: Date Author Reason
+ * 6/01/91 Garrett McAuliffe Created
+ * 9/15/91 Patrick Schreiber Added header and comments
+ * 3/27/92 Mark Bader Added comments about resulting
+ * bitmap format
+ *
+ ************************************************************************/
+
+
+HBITMAP FAR DIBToBitmap(HDIB hDIB, HPALETTE hPal)
+{
+ LPSTR lpDIBHdr, lpDIBBits; // pointer to DIB header, pointer to DIB bits
+ HBITMAP hBitmap; // handle to device-dependent bitmap
+ HDC hDC; // handle to DC
+ HPALETTE hOldPal = NULL; // handle to a palette
+
+ /* if invalid handle, return NULL */
+
+ if (!hDIB)
+ return NULL;
+
+ /* lock memory block and get a pointer to it */
+ lpDIBHdr = (LPSTR)GlobalLock(hDIB);
+
+ /* get a pointer to the DIB bits */
+ lpDIBBits = FindDIBBits(lpDIBHdr);
+
+ /* get a DC */
+ hDC = GetDC(NULL);
+ if (!hDC)
+ {
+ /* clean up and return NULL */
+ GlobalUnlock(hDIB);
+ return NULL;
+ }
+
+ /* select and realize palette */
+ if (hPal)
+ hOldPal = SelectPalette(hDC, hPal, FALSE);
+ RealizePalette(hDC);
+
+ /* create bitmap from DIB info. and bits */
+ hBitmap = CreateDIBitmap(hDC, (LPBITMAPINFOHEADER)lpDIBHdr, CBM_INIT,
+ lpDIBBits, (LPBITMAPINFO)lpDIBHdr, DIB_RGB_COLORS);
+
+ /* restore previous palette */
+ if (hOldPal)
+ SelectPalette(hDC, hOldPal, FALSE);
+
+ /* clean up */
+ ReleaseDC(NULL, hDC);
+ GlobalUnlock(hDIB);
+
+ /* return handle to the bitmap */
+ return hBitmap;
+}
+
+
+/*************************************************************************
+ *
+ * BitmapToDIB()
+ *
+ * Parameters:
+ *
+ * HBITMAP hBitmap - specifies the bitmap to convert
+ *
+ * HPALETTE hPal - specifies the palette to use with the bitmap
+ *
+ * Return Value:
+ *
+ * HDIB - identifies the device-dependent bitmap
+ *
+ * Description:
+ *
+ * This function creates a DIB from a bitmap using the specified palette.
+ *
+ * History: Date Author Reason
+ * 6/01/91 Garrett McAuliffe Created
+ * 9/15/91 Patrick Schreiber Added header and comments
+ * 12/10/91 Patrick Schreiber Added bits per pixel validation
+ * and check GetObject return value
+ *
+ ************************************************************************/
+
+
+HDIB FAR BitmapToDIB(HBITMAP hBitmap, HPALETTE hPal)
+{
+ BITMAP bm; // bitmap structure
+ BITMAPINFOHEADER bi; // bitmap header
+ BITMAPINFOHEADER FAR *lpbi; // pointer to BITMAPINFOHEADER
+ DWORD dwLen; // size of memory block
+ HANDLE hDIB, h; // handle to DIB, temp handle
+ HDC hDC; // handle to DC
+ WORD biBits; // bits per pixel
+
+ /* check if bitmap handle is valid */
+
+ if (!hBitmap)
+ return NULL;
+
+ /* fill in BITMAP structure, return NULL if it didn't work */
+ if (!GetObject(hBitmap, sizeof(bm), (LPSTR)&bm))
+ return NULL;
+
+ /* if no palette is specified, use default palette */
+ if (hPal == NULL)
+ hPal = GetStockObject(DEFAULT_PALETTE);
+
+ /* calculate bits per pixel */
+ biBits = (WORD)( bm.bmPlanes * bm.bmBitsPixel );
+
+ /* make sure bits per pixel is valid */
+ if (biBits <= 1)
+ biBits = 1;
+ else if (biBits <= 4)
+ biBits = 4;
+ else if (biBits <= 8)
+ biBits = 8;
+ else /* if greater than 8-bit, force to 24-bit */
+ biBits = 24;
+
+ /* initialize BITMAPINFOHEADER */
+ bi.biSize = sizeof(BITMAPINFOHEADER);
+ bi.biWidth = bm.bmWidth;
+ bi.biHeight = bm.bmHeight;
+ bi.biPlanes = 1;
+ bi.biBitCount = biBits;
+ bi.biCompression = BI_RGB;
+ bi.biSizeImage = 0;
+ bi.biXPelsPerMeter = 0;
+ bi.biYPelsPerMeter = 0;
+ bi.biClrUsed = 0;
+ bi.biClrImportant = 0;
+
+ /* calculate size of memory block required to store BITMAPINFO */
+ dwLen = bi.biSize + PaletteSize((LPSTR)&bi);
+
+ /* get a DC */
+ hDC = GetDC(NULL);
+
+ /* select and realize our palette */
+ hPal = SelectPalette(hDC, hPal, FALSE);
+ RealizePalette(hDC);
+
+ /* alloc memory block to store our bitmap */
+ hDIB = GlobalAlloc(GHND, dwLen);
+
+ /* if we couldn't get memory block */
+ if (!hDIB)
+ {
+ /* clean up and return NULL */
+ SelectPalette(hDC, hPal, TRUE);
+ RealizePalette(hDC);
+ ReleaseDC(NULL, hDC);
+ return NULL;
+ }
+
+ /* lock memory and get pointer to it */
+ lpbi = (BITMAPINFOHEADER FAR *)GlobalLock(hDIB);
+
+ /* use our bitmap info. to fill BITMAPINFOHEADER */
+ *lpbi = bi;
+
+ /* call GetDIBits with a NULL lpBits param, so it will calculate the
+ * biSizeImage field for us
+ */
+ GetDIBits(hDC, hBitmap, 0, (WORD)bi.biHeight, NULL, (LPBITMAPINFO)lpbi,
+ DIB_RGB_COLORS);
+
+ /* get the info. returned by GetDIBits and unlock memory block */
+ bi = *lpbi;
+ GlobalUnlock(hDIB);
+
+ /* if the driver did not fill in the biSizeImage field, make one up */
+ if (bi.biSizeImage == 0)
+ bi.biSizeImage = WIDTHBYTES((DWORD)bm.bmWidth * biBits) * bm.bmHeight;
+
+ /* realloc the buffer big enough to hold all the bits */
+ dwLen = bi.biSize + PaletteSize((LPSTR)&bi) + bi.biSizeImage;
+ h = GlobalReAlloc(hDIB, dwLen, 0);
+ if (h)
+ hDIB = h;
+ else
+ {
+ /* clean up and return NULL */
+ GlobalFree(hDIB);
+ hDIB = NULL;
+ SelectPalette(hDC, hPal, TRUE);
+ RealizePalette(hDC);
+ ReleaseDC(NULL, hDC);
+ return NULL;
+ }
+
+ /* lock memory block and get pointer to it */
+ lpbi = (BITMAPINFOHEADER FAR *)GlobalLock(hDIB);
+
+ /* call GetDIBits with a NON-NULL lpBits param, and actualy get the
+ * bits this time
+ */
+ if (GetDIBits(hDC, hBitmap, 0, (WORD)bi.biHeight, (LPSTR)lpbi + (WORD)lpbi
+ ->biSize + PaletteSize((LPSTR)lpbi), (LPBITMAPINFO)lpbi,
+ DIB_RGB_COLORS) == 0)
+ {
+ /* clean up and return NULL */
+ GlobalUnlock(hDIB);
+ hDIB = NULL;
+ SelectPalette(hDC, hPal, TRUE);
+ RealizePalette(hDC);
+ ReleaseDC(NULL, hDC);
+ return NULL;
+ }
+ bi = *lpbi;
+
+ /* clean up */
+ GlobalUnlock(hDIB);
+ SelectPalette(hDC, hPal, TRUE);
+ RealizePalette(hDC);
+ ReleaseDC(NULL, hDC);
+
+ /* return handle to the DIB */
+ return hDIB;
+}
+
+
+/*************************************************************************
+ *
+ * PalEntriesOnDevice()
+ *
+ * Parameter:
+ *
+ * HDC hDC - device context
+ *
+ * Return Value:
+ *
+ * int - number of palette entries on device
+ *
+ * Description:
+ *
+ * This function gets the number of palette entries on the specified device
+ *
+ * History: Date Author Reason
+ * 6/01/91 Garrett McAuliffe Created
+ * 9/15/91 Patrick Schreiber Added header and comments
+ *
+ ************************************************************************/
+
+
+int FAR PalEntriesOnDevice(HDC hDC)
+{
+ int nColors; // number of colors
+
+ /* Find out the number of palette entries on this
+ * device.
+ */
+
+ nColors = GetDeviceCaps(hDC, SIZEPALETTE);
+
+ /* For non-palette devices, we'll use the # of system
+ * colors for our palette size.
+ */
+ if (!nColors)
+ nColors = GetDeviceCaps(hDC, NUMCOLORS);
+ assert(nColors);
+ return nColors;
+}
+
+
+/*************************************************************************
+ *
+ * GetSystemPalette()
+ *
+ * Parameters:
+ *
+ * None
+ *
+ * Return Value:
+ *
+ * HPALETTE - handle to a copy of the current system palette
+ *
+ * Description:
+ *
+ * This function returns a handle to a palette which represents the system
+ * palette. The system RGB values are copied into our logical palette using
+ * the GetSystemPaletteEntries function.
+ *
+ * History:
+ *
+ * Date Author Reason
+ * 6/01/91 Garrett McAuliffe Created
+ * 9/15/91 Patrick Schreiber Added header and comments
+ * 12/20/91 Mark Bader Added GetSystemPaletteEntries call
+ *
+ ************************************************************************/
+
+
+HPALETTE FAR GetSystemPalette(void)
+{
+ HDC hDC; // handle to a DC
+ static HPALETTE hPal = NULL; // handle to a palette
+ HANDLE hLogPal; // handle to a logical palette
+ LPLOGPALETTE lpLogPal; // pointer to a logical palette
+ int nColors; // number of colors
+
+ /* Find out how many palette entries we want. */
+
+ hDC = GetDC(NULL);
+ if (!hDC)
+ return NULL;
+ nColors = PalEntriesOnDevice(hDC); // Number of palette entries
+
+ /* Allocate room for the palette and lock it. */
+ hLogPal = GlobalAlloc(GHND, sizeof(LOGPALETTE) + nColors * sizeof(
+ PALETTEENTRY));
+
+ /* if we didn't get a logical palette, return NULL */
+ if (!hLogPal)
+ return NULL;
+
+ /* get a pointer to the logical palette */
+ lpLogPal = (LPLOGPALETTE)GlobalLock(hLogPal);
+
+ /* set some important fields */
+ lpLogPal->palVersion = (WORD)PALVERSION;
+ lpLogPal->palNumEntries = (WORD)nColors;
+
+ /* Copy the current system palette into our logical palette */
+
+ GetSystemPaletteEntries(hDC, 0, nColors,
+ (LPPALETTEENTRY)(lpLogPal->palPalEntry));
+
+ /* Go ahead and create the palette. Once it's created,
+ * we no longer need the LOGPALETTE, so free it.
+ */
+
+ hPal = CreatePalette(lpLogPal);
+
+ /* clean up */
+ GlobalUnlock(hLogPal);
+ GlobalFree(hLogPal);
+ ReleaseDC(NULL, hDC);
+
+ return hPal;
+}
+
+
+/*************************************************************************
+ *
+ * AllocRoomForDIB()
+ *
+ * Parameters:
+ *
+ * BITMAPINFOHEADER - bitmap info header stucture
+ *
+ * HBITMAP - handle to the bitmap
+ *
+ * Return Value:
+ *
+ * HDIB - handle to memory block
+ *
+ * Description:
+ *
+ * This routine takes a BITMAPINOHEADER, and returns a handle to global
+ * memory which can contain a DIB with that header. It also initializes
+ * the header portion of the global memory. GetDIBits() is used to determine
+ * the amount of room for the DIB's bits. The total amount of memory
+ * needed = sizeof(BITMAPINFOHEADER) + size of color table + size of bits.
+ *
+ * History: Date Author Reason
+ * 6/01/91 Garrett McAuliffe Created
+ * 12/11/91 Patrick Schreiber Added header and some comments
+ *
+ ************************************************************************/
+
+HANDLE AllocRoomForDIB(BITMAPINFOHEADER bi, HBITMAP hBitmap)
+{
+ DWORD dwLen;
+ HANDLE hDIB;
+ HDC hDC;
+ LPBITMAPINFOHEADER lpbi;
+ HANDLE hTemp;
+
+ /* Figure out the size needed to hold the BITMAPINFO structure
+ * (which includes the BITMAPINFOHEADER and the color table).
+ */
+
+ dwLen = bi.biSize + PaletteSize((LPSTR) &bi);
+ hDIB = GlobalAlloc(GHND,dwLen);
+
+ /* Check that DIB handle is valid */
+ if (!hDIB)
+ return NULL;
+
+ /* Set up the BITMAPINFOHEADER in the newly allocated global memory,
+ * then call GetDIBits() with lpBits = NULL to have it fill in the
+ * biSizeImage field for us.
+ */
+ lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB);
+ *lpbi = bi;
+
+ hDC = GetDC(NULL);
+ GetDIBits(hDC, hBitmap, 0, (WORD) bi.biHeight,
+ NULL, (LPBITMAPINFO) lpbi, DIB_RGB_COLORS);
+ ReleaseDC(NULL, hDC);
+
+ /* If the driver did not fill in the biSizeImage field,
+ * fill it in -- NOTE: this is a bug in the driver!
+ */
+ if (lpbi->biSizeImage == 0)
+ lpbi->biSizeImage = WIDTHBYTES((DWORD)lpbi->biWidth * lpbi->biBitCount) *
+ lpbi->biHeight;
+
+ /* Get the size of the memory block we need */
+ dwLen = lpbi->biSize + PaletteSize((LPSTR) &bi) + lpbi->biSizeImage;
+
+ /* Unlock the memory block */
+ GlobalUnlock(hDIB);
+
+ /* ReAlloc the buffer big enough to hold all the bits */
+ hTemp = GlobalReAlloc(hDIB,dwLen,0);
+ if (hTemp)
+ return hTemp;
+ else
+ {
+ /* Else free memory block and return failure */
+ GlobalFree(hDIB);
+ return NULL;
+ }
+}
+
+
+/*************************************************************************
+ *
+ * ChangeDIBFormat()
+ *
+ * Parameter:
+ *
+ * HDIB - handle to packed-DIB in memory
+ *
+ * WORD - desired bits per pixel
+ *
+ * DWORD - desired compression format
+ *
+ * Return Value:
+ *
+ * HDIB - handle to the new DIB if successful, else NULL
+ *
+ * Description:
+ *
+ * This function will convert the bits per pixel and/or the compression
+ * format of the specified DIB. Note: If the conversion was unsuccessful,
+ * we return NULL. The original DIB is left alone. Don't use code like the
+ * following:
+ *
+ * hMyDIB = ChangeDIBFormat(hMyDIB, 8, BI_RLE4);
+ *
+ * The conversion will fail, but hMyDIB will now be NULL and the original
+ * DIB will now hang around in memory. We could have returned the old
+ * DIB, but we wanted to allow the programmer to check whether this
+ * conversion succeeded or failed.
+ *
+ * History:
+ *
+ * Date Author Reason
+ * 6/01/91 Garrett McAuliffe Created
+ * 12/10/91 Patrick Schreiber Modified from converting RGB to RLE8
+ * to converting RGB/RLE to RGB/RLE.
+ * Added wBitCount and dwCompression
+ * parameters. Also added header and
+ * comments.
+ *
+ ************************************************************************/
+
+HDIB FAR ChangeDIBFormat(HDIB hDIB, WORD wBitCount, DWORD dwCompression)
+{
+ HDC hDC; // Handle to DC
+ HBITMAP hBitmap; // Handle to bitmap
+ BITMAP Bitmap; // BITMAP data structure
+ BITMAPINFOHEADER bi; // Bitmap info header
+ LPBITMAPINFOHEADER lpbi; // Pointer to bitmap info
+ HDIB hNewDIB = NULL; // Handle to new DIB
+ HPALETTE hPal, hOldPal; // Handle to palette, prev pal
+ WORD DIBBPP, NewBPP; // DIB bits per pixel, new bpp
+ DWORD DIBComp, NewComp;// DIB compression, new compression
+
+ /* Check for a valid DIB handle */
+ if (!hDIB)
+ return NULL;
+
+ /* Get the old DIB's bits per pixel and compression format */
+ lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB);
+ DIBBPP = ((LPBITMAPINFOHEADER)lpbi)->biBitCount;
+ DIBComp = ((LPBITMAPINFOHEADER)lpbi)->biCompression;
+ GlobalUnlock(hDIB);
+
+ /* Validate wBitCount and dwCompression
+ * They must match correctly (i.e., BI_RLE4 and 4 BPP or
+ * BI_RLE8 and 8BPP, etc.) or we return failure */
+ if (wBitCount == 0)
+ {
+ NewBPP = DIBBPP;
+ if ((dwCompression == BI_RLE4 && NewBPP == 4) ||
+ (dwCompression == BI_RLE8 && NewBPP == 8) ||
+ (dwCompression == BI_RGB))
+ NewComp = dwCompression;
+ else
+ return NULL;
+ }
+ else if (wBitCount == 1 && dwCompression == BI_RGB)
+ {
+ NewBPP = wBitCount;
+ NewComp = BI_RGB;
+ }
+ else if (wBitCount == 4)
+ {
+ NewBPP = wBitCount;
+ if (dwCompression == BI_RGB || dwCompression == BI_RLE4)
+ NewComp = dwCompression;
+ else
+ return NULL;
+ }
+ else if (wBitCount == 8)
+ {
+ NewBPP = wBitCount;
+ if (dwCompression == BI_RGB || dwCompression == BI_RLE8)
+ NewComp = dwCompression;
+ else
+ return NULL;
+ }
+ else if (wBitCount == 24 && dwCompression == BI_RGB)
+ {
+ NewBPP = wBitCount;
+ NewComp = BI_RGB;
+ }
+ else
+ return NULL;
+
+ /* Save the old DIB's palette */
+ hPal = CreateDIBPalette(hDIB);
+ if (!hPal)
+ return NULL;
+
+ /* Convert old DIB to a bitmap */
+ hBitmap = DIBToBitmap(hDIB, hPal);
+ if (!hBitmap)
+ {
+ DeleteObject(hPal);
+ return NULL;
+ }
+
+ /* Get info about the bitmap */
+ GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&Bitmap);
+
+ /* Fill in the BITMAPINFOHEADER appropriately */
+ bi.biSize = sizeof(BITMAPINFOHEADER);
+ bi.biWidth = Bitmap.bmWidth;
+ bi.biHeight = Bitmap.bmHeight;
+ bi.biPlanes = 1;
+ bi.biBitCount = NewBPP;
+ bi.biCompression = NewComp;
+ bi.biSizeImage = 0;
+ bi.biXPelsPerMeter = 0;
+ bi.biYPelsPerMeter = 0;
+ bi.biClrUsed = 0;
+ bi.biClrImportant = 0;
+
+ /* Go allocate room for the new DIB */
+ hNewDIB = AllocRoomForDIB(bi, hBitmap);
+ if (!hNewDIB)
+ return NULL;
+
+ /* Get a pointer to the new DIB */
+ lpbi = (LPBITMAPINFOHEADER)GlobalLock(hNewDIB);
+
+ /* Get a DC and select/realize our palette in it */
+ hDC = GetDC(NULL);
+ hOldPal = SelectPalette(hDC, hPal, FALSE);
+ RealizePalette(hDC);
+
+ /* Call GetDIBits and get the new DIB bits */
+ if (!GetDIBits(hDC, hBitmap, 0, (WORD) lpbi->biHeight,
+ (LPSTR)lpbi + (WORD)lpbi->biSize + PaletteSize((LPSTR)lpbi),
+ (LPBITMAPINFO)lpbi, DIB_RGB_COLORS))
+ {
+ GlobalUnlock(hNewDIB);
+ GlobalFree(hNewDIB);
+ hNewDIB = NULL;
+ }
+
+ /* Clean up and return */
+ SelectPalette(hDC, hOldPal, TRUE);
+ RealizePalette(hDC);
+ ReleaseDC(NULL, hDC);
+
+ if (hNewDIB)
+ /* Unlock the new DIB's memory block */
+ GlobalUnlock(hNewDIB);
+
+ DeleteObject(hBitmap);
+ DeleteObject(hPal);
+
+ return hNewDIB;
+}
+
+
+/*************************************************************************
+ *
+ * ChangeBitmapFormat()
+ *
+ * Parameter:
+ *
+ * HBITMAP - handle to a bitmap
+ *
+ * WORD - desired bits per pixel
+ *
+ * DWORD - desired compression format
+ *
+ * HPALETTE - handle to palette
+ *
+ * Return Value:
+ *
+ * HDIB - handle to the new DIB if successful, else NULL
+ *
+ * Description:
+ *
+ * This function will convert a bitmap to the specified bits per pixel
+ * and compression format. The bitmap and it's palette will remain
+ * after calling this function.
+ *
+ * History:
+ *
+ * Date Author Reason
+ * 6/01/91 Garrett McAuliffe Created
+ * 12/10/91 Patrick Schreiber Modified from converting RGB to RLE8
+ * to converting RGB/RLE to RGB/RLE.
+ * Added wBitCount and dwCompression
+ * parameters. Also added header and
+ * comments.
+ * 12/11/91 Patrick Schreiber Destroy old DIB if conversion was
+ * successful.
+ * 12/16/91 Patrick Schreiber Modified from converting DIB to new
+ * DIB to bitmap to new DIB. Added palette
+ * parameter.
+ *
+ ************************************************************************/
+
+HDIB FAR ChangeBitmapFormat(HBITMAP hBitmap,
+ WORD wBitCount,
+ DWORD dwCompression,
+ HPALETTE hPal)
+{
+ HDC hDC; // Screen DC
+ HDIB hNewDIB=NULL; // Handle to new DIB
+ BITMAP Bitmap; // BITMAP data structure
+ BITMAPINFOHEADER bi; // Bitmap info. header
+ LPBITMAPINFOHEADER lpbi; // Pointer to bitmap header
+ HPALETTE hOldPal=NULL; // Handle to palette
+ WORD NewBPP; // New bits per pixel
+ DWORD NewComp; // New compression format
+
+ /* Check for a valid bitmap handle */
+ if (!hBitmap)
+ return NULL;
+
+ /* Validate wBitCount and dwCompression
+ * They must match correctly (i.e., BI_RLE4 and 4 BPP or
+ * BI_RLE8 and 8BPP, etc.) or we return failure
+ */
+ if (wBitCount == 0)
+ {
+ NewComp = dwCompression;
+ if (NewComp == BI_RLE4)
+ NewBPP = 4;
+ else if (NewComp == BI_RLE8)
+ NewBPP = 8;
+ else /* Not enough info */
+ return NULL;
+ }
+ else if (wBitCount == 1 && dwCompression == BI_RGB)
+ {
+ NewBPP = wBitCount;
+ NewComp = BI_RGB;
+ }
+ else if (wBitCount == 4)
+ {
+ NewBPP = wBitCount;
+ if (dwCompression == BI_RGB || dwCompression == BI_RLE4)
+ NewComp = dwCompression;
+ else
+ return NULL;
+ }
+ else if (wBitCount == 8)
+ {
+ NewBPP = wBitCount;
+ if (dwCompression == BI_RGB || dwCompression == BI_RLE8)
+ NewComp = dwCompression;
+ else
+ return NULL;
+ }
+ else if (wBitCount == 24 && dwCompression == BI_RGB)
+ {
+ NewBPP = wBitCount;
+ NewComp = BI_RGB;
+ }
+ else
+ return NULL;
+
+ /* Get info about the bitmap */
+ GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&Bitmap);
+
+ /* Fill in the BITMAPINFOHEADER appropriately */
+ bi.biSize = sizeof(BITMAPINFOHEADER);
+ bi.biWidth = Bitmap.bmWidth;
+ bi.biHeight = Bitmap.bmHeight;
+ bi.biPlanes = 1;
+ bi.biBitCount = NewBPP;
+ bi.biCompression = NewComp;
+ bi.biSizeImage = 0;
+ bi.biXPelsPerMeter = 0;
+ bi.biYPelsPerMeter = 0;
+ bi.biClrUsed = 0;
+ bi.biClrImportant = 0;
+
+ /* Go allocate room for the new DIB */
+ hNewDIB = AllocRoomForDIB(bi, hBitmap);
+ if (!hNewDIB)
+ return NULL;
+
+ /* Get a pointer to the new DIB */
+ lpbi = (LPBITMAPINFOHEADER)GlobalLock(hNewDIB);
+
+ /* If we have a palette, get a DC and select/realize it */
+ if (hPal)
+ {
+ hDC = GetDC(NULL);
+ hOldPal = SelectPalette(hDC, hPal, FALSE);
+ RealizePalette(hDC);
+ }
+
+ /* Call GetDIBits and get the new DIB bits */
+ if (!GetDIBits(hDC, hBitmap, 0, (WORD) lpbi->biHeight,
+ (LPSTR)lpbi + (WORD)lpbi->biSize + PaletteSize((LPSTR)lpbi),
+ (LPBITMAPINFO)lpbi, DIB_RGB_COLORS))
+ {
+ GlobalUnlock(hNewDIB);
+ GlobalFree(hNewDIB);
+ hNewDIB = NULL;
+ }
+
+ /* Clean up and return */
+ if (hOldPal)
+ {
+ SelectPalette(hDC, hOldPal, TRUE);
+ RealizePalette(hDC);
+ ReleaseDC(NULL, hDC);
+ }
+
+ if (hNewDIB)
+ {
+ /* Unlock the new DIB's memory block */
+ GlobalUnlock(hNewDIB);
+ }
+
+ return hNewDIB;
+}
diff --git a/CODE/DIBUTIL.H b/CODE/DIBUTIL.H
new file mode 100644
index 0000000..3360080
--- /dev/null
+++ b/CODE/DIBUTIL.H
@@ -0,0 +1,41 @@
+/*
+** Command & Conquer Red Alert(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 .
+*/
+
+/*
+ * dibutil.h
+ *
+ * Copyright (c) 1991 Microsoft Corporation. All rights reserved.
+ *
+ * Header file for Device-Independent Bitmap (DIB) API. Provides
+ * function prototypes and constants for the following functions:
+ *
+ * AllocRoomForDIB() - Allocates memory for a DIB
+ *
+ */
+
+
+/* DIB constants */
+#define PALVERSION 0x300
+
+/* DIB macros */
+#define IS_WIN30_DIB(lpbi) ((*(LPDWORD)(lpbi)) == sizeof(BITMAPINFOHEADER))
+#define RECTWIDTH(lpRect) ((lpRect)->right - (lpRect)->left)
+#define RECTHEIGHT(lpRect) ((lpRect)->bottom - (lpRect)->top)
+
+/* function prototypes */
+HANDLE AllocRoomForDIB(BITMAPINFOHEADER bi, HBITMAP hBitmap);
diff --git a/CODE/DISPLAY.CPP b/CODE/DISPLAY.CPP
new file mode 100644
index 0000000..d0a6e6d
--- /dev/null
+++ b/CODE/DISPLAY.CPP
@@ -0,0 +1,4509 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/DISPLAY.CPP 3 3/09/97 8:04p Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : DISPLAY.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : September 10, 1993 *
+ * *
+ * Last Update : October 20, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * DisplayClass::Compute_Start_Pos -- Computes player's start pos from unit coords. *
+ * DisplayClass::AI -- Handles the maintenance tasks for the map display. *
+ * DisplayClass::All_To_Look -- Direct all objects to look around for the player. *
+ * DisplayClass::Calculated_Cell -- Fetch a map cell based on specified method. *
+ * DisplayClass::Cell_Object -- Determines what has been clicked on. *
+ * DisplayClass::Cell_Shadow -- Determine what shadow icon to use for the cell. *
+ * DisplayClass::Center_Map -- Centers the map about the currently selected objects *
+ * DisplayClass::Click_Cell_Calc -- Determines cell from screen X & Y. *
+ * DisplayClass::Closest_Free_Spot -- Finds the closest cell sub spot that is free. *
+ * DisplayClass::Coord_To_Pixel -- Determines X and Y pixel coordinates. *
+ * DisplayClass::Cursor_Mark -- Set or resets the cursor display flag bits. *
+ * DisplayClass::DisplayClass -- Default constructor for display class. *
+ * DisplayClass::Draw_It -- Draws the tactical map. *
+ * DisplayClass::Encroach_Shadow -- Causes the shadow to creep back by one cell. *
+ * DisplayClass::Flag_Cell -- Flag the specified cell to be redrawn. *
+ * DisplayClass::Flag_To_Redraw -- Flags the display so that it will be redrawn as soon as poss*
+ * DisplayClass::Get_Occupy_Dimensions -- computes width & height of the given occupy list *
+ * DisplayClass::Good_Reinforcement_Cell -- Checks cell for renforcement legality. *
+ * DisplayClass::In_View -- Determines if cell is visible on screen. *
+ * DisplayClass::Init_Clear -- Clears the display to a known state. *
+ * DisplayClass::Init_IO -- Creates the map's button list *
+ * DisplayClass::Init_Theater -- Theater-specific initialization *
+ * DisplayClass::Is_Spot_Free -- Determines if cell sub spot is free of occupation. *
+ * DisplayClass::Map_Cell -- Mark specified cell as having been mapped. *
+ * DisplayClass::Mouse_Left_Held -- Handles the left button held down. *
+ * DisplayClass::Mouse_Left_Press -- Handles the left mouse button press. *
+ * DisplayClass::Mouse_Left_Release -- Handles the left mouse button release. *
+ * DisplayClass::Mouse_Left_Up -- Handles the left mouse "cruising" over the map. *
+ * DisplayClass::Mouse_Right_Press -- Handles the right mouse button press. *
+ * DisplayClass::Next_Object -- Searches for next object on display. *
+ * DisplayClass::One_Time -- Performs any special one time initializations. *
+ * DisplayClass::Passes_Proximity_Check -- Determines if building placement is near friendly sq*
+ * DisplayClass::Pixel_To_Coord -- converts screen coord to COORDINATE *
+ * DisplayClass::Prev_Object -- Searches for the previous object on the map. *
+ * DisplayClass::Read_INI -- Reads map control data from INI file. *
+ * DisplayClass::Redraw_Icons -- Draws all terrain icons necessary. *
+ * DisplayClass::Redraw_Shadow -- Draw the shadow overlay. *
+ * DisplayClass::Refresh_Band -- Causes all cells under the rubber band to be redrawn. *
+ * DisplayClass::Refresh_Cells -- Redraws all cells in list. *
+ * DisplayClass::Remove -- Removes a game object from the rendering system. *
+ * DisplayClass::Repair_Mode_Control -- Controls the repair mode. *
+ * DisplayClass::Scroll_Map -- Scroll the tactical map in desired direction. *
+ * DisplayClass::Select_These -- All selectable objects in region are selected. *
+ * DisplayClass::Sell_Mode_Control -- Controls the sell mode. *
+ * DisplayClass::Set_Cursor_Pos -- Controls the display and animation of the tac cursor. *
+ * DisplayClass::Set_Cursor_Shape -- Changes the shape of the terrain square cursor. *
+ * DisplayClass::Set_Tactical_Position -- Sets the tactical view position. *
+ * DisplayClass::Set_View_Dimensions -- Sets the tactical display screen coordinates. *
+ * DisplayClass::Shroud_Cell -- Returns the specified cell into the shrouded condition. *
+ * DisplayClass::Submit -- Adds a game object to the map rendering system. *
+ * DisplayClass::TacticalClass::Action -- Processes input for the tactical map. *
+ * DisplayClass::Text_Overlap_List -- Creates cell overlap list for specified text string. *
+ * DisplayClass::Write_INI -- Write the map data to the INI file specified. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+#include "vortex.h"
+
+/*
+** These layer control elements are used to group the displayable objects
+** so that proper overlap can be obtained.
+*/
+LayerClass DisplayClass::Layer[LAYER_COUNT];
+
+/*
+** Fading tables
+*/
+unsigned char DisplayClass::FadingBrighten[256];
+unsigned char DisplayClass::FadingShade[256];
+unsigned char DisplayClass::FadingWayDark[256];
+unsigned char DisplayClass::FadingLight[256];
+unsigned char DisplayClass::FadingGreen[256];
+unsigned char DisplayClass::FadingYellow[256];
+unsigned char DisplayClass::FadingRed[256];
+unsigned char DisplayClass::TranslucentTable[(MAGIC_COL_COUNT+1)*256];
+unsigned char DisplayClass::WhiteTranslucentTable[(1+1)*256];
+unsigned char DisplayClass::MouseTranslucentTable[(4+1)*256];
+void const * DisplayClass::TransIconset;
+unsigned char DisplayClass::UnitShadow[(USHADOW_COL_COUNT+1)*256];
+unsigned char DisplayClass::UnitShadowAir[(USHADOW_COL_COUNT+1)*256];
+unsigned char DisplayClass::SpecialGhost[2*256];
+
+void const * DisplayClass::ShadowShapes;
+unsigned char DisplayClass::ShadowTrans[(SHADOW_COL_COUNT+1)*256];
+
+/*
+** Bit array of cell redraw flags
+*/
+BooleanVectorClass DisplayClass::CellRedraw;
+
+/*
+** The main button that intercepts user input to the map
+*/
+DisplayClass::TacticalClass DisplayClass::TacButton;
+
+
+static int const TEX_X = 0;
+static int const TEX_Y = 6;
+static int const TEX_W = 14;
+
+
+/***********************************************************************************************
+ * DisplayClass::DisplayClass -- Default constructor for display class. *
+ * *
+ * This constructor for the display class just initializes some of the display settings. *
+ * Most settings are initialized with the correct values at the time that the Init function *
+ * is called. There are some cases where default values are wise and this routine fills *
+ * those particular ones in. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 12/06/1994 JLB : Created. *
+ *=============================================================================================*/
+DisplayClass::DisplayClass(void) :
+ TacticalCoord(0),
+ TacLeptonWidth(0),
+ TacLeptonHeight(0),
+ ZoneCell(0),
+ ZoneOffset(0),
+ CursorSize(0),
+ ProximityCheck(false),
+ PendingObjectPtr(0),
+ PendingObject(0),
+ PendingHouse(HOUSE_NONE),
+ TacPixelX(0),
+ TacPixelY(0),
+ DesiredTacticalCoord(0),
+ IsToRedraw(true),
+ IsRepairMode(false),
+ IsSellMode(false),
+ IsTargettingMode(SPC_NONE),
+ IsRubberBand(false),
+ IsTentative(false),
+ IsShadowPresent(false),
+ BandX(0),
+ BandY(0),
+ NewX(0),
+ NewY(0)
+{
+ ShadowShapes = 0;
+ TransIconset = 0;
+
+ Set_View_Dimensions(0, 8, 320/CELL_PIXEL_W, 200/CELL_PIXEL_H);
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::One_Time -- Performs any special one time initializations. *
+ * *
+ * This routine is called from the game initialization process. It is to perform any one *
+ * time initializations necessary for the map display system. It allocates the staging *
+ * buffer needed for the radar map. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: This routine must be called ONCE and only once. *
+ * *
+ * HISTORY: *
+ * 05/31/1994 JLB : Created. *
+ * 05/31/1994 JLB : Handles layer system now. *
+ * 06/02/1994 JLB : Takes care of misc display tables and data allocation. *
+ *=============================================================================================*/
+void DisplayClass::One_Time(void)
+{
+ MapClass::One_Time();
+
+ /*
+ ** Init the CellRedraw bit array. Do not do this in the constructor, since the
+ ** BooleanVector may not have been constructed yet.
+ */
+ CellRedraw.Resize(MAP_CELL_TOTAL);
+
+ for (LayerType layer = LAYER_FIRST; layer < LAYER_COUNT; layer++) {
+ Layer[layer].One_Time();
+ }
+
+ /*
+ ** Load the generic transparent icon set.
+ */
+ TransIconset = MFCD::Retrieve("TRANS.ICN");
+
+ #ifndef NDEBUG
+ RawFileClass file("SHADOW.SHP");
+ if (file.Is_Available()) {
+ ShadowShapes = Load_Alloc_Data(file);
+ } else {
+ ShadowShapes = MFCD::Retrieve("SHADOW.SHP");
+ }
+ #else
+ ShadowShapes = MFCD::Retrieve("SHADOW.SHP");
+ #endif
+
+ Set_View_Dimensions(0, 8 * RESFACTOR);
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Init_Clear -- clears the display to a known state *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 03/17/1995 BRR : Created. *
+ *=============================================================================================*/
+void DisplayClass::Init_Clear(void)
+{
+ MapClass::Init_Clear();
+
+ /*
+ ** Clear any object being placed
+ */
+ PendingObjectPtr = 0;
+ PendingObject = 0;
+ PendingHouse = HOUSE_NONE;
+ CursorSize = 0;
+ IsTargettingMode = SPC_NONE;
+ IsRepairMode = false;
+ IsRubberBand = false;
+ IsTentative = false;
+ IsSellMode = false;
+
+ /*
+ ** Empty all the display's layers
+ */
+ for (LayerType layer = LAYER_FIRST; layer < LAYER_COUNT; layer++) {
+ Layer[layer].Init();
+ }
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Init_IO -- clears & re-builds the map's button list *
+ * *
+ * INPUT: *
+ * none. *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 03/17/1995 BRR : Created. *
+ *=============================================================================================*/
+void DisplayClass::Init_IO(void)
+{
+ MapClass::Init_IO();
+ /*
+ ** Re-attach our buttons to the main map button list, only in non-edit mode.
+ */
+ if (!Debug_Map) {
+ TacButton.Zap();
+ Add_A_Button(TacButton);
+ }
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Init_Theater -- Performs theater-specific initialization (mixfiles, etc) *
+ * *
+ * INPUT: *
+ * theater new theater *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 03/17/1995 BRR : Created. *
+ * 05/07/1996 JLB : Added translucent tables. *
+ *=============================================================================================*/
+void DisplayClass::Init_Theater(TheaterType theater)
+{
+ char fullname[16];
+ static TLucentType const MouseCols[4] = {
+ {BLACK, BLACK, 110, 0},
+ {WHITE, WHITE, 110, 0},
+ {LTGREY, LTGREY, 110, 0},
+ {DKGREY, DKGREY, 110, 0}
+ };
+ static TLucentType const MagicCols[MAGIC_COL_COUNT] = {
+ {32,32,110,0},
+ {33,33,110,0},
+ {34,34,110,0},
+ {35,35,110,0},
+ {36,36,110,0},
+ {37,37,110,0},
+ {38,38,110,0},
+ {39,39,110,0},
+ {BLACK, BLACK, 200, 0},
+ {WHITE, BLACK, 40, 0},
+ {LTGREY, BLACK, 80, 0},
+ {DKGREY, BLACK, 140, 0},
+ {LTGREEN, BLACK,130,0}
+ };
+ static TLucentType const WhiteCols[1] = {
+ {1, WHITE, 80, 0}
+ };
+ static TLucentType const ShadowCols[SHADOW_COL_COUNT] = {
+ {WHITE+1, BLACK,130,0},
+ {WHITE, BLACK,170,0},
+ {LTGRAY, BLACK,250,0},
+ {DKGRAY, BLACK,250,0}
+ };
+ static TLucentType const UShadowCols[USHADOW_COL_COUNT] = {
+ {LTGREEN, BLACK,130,0}
+ };
+ static TLucentType const UShadowColsAir[USHADOW_COL_COUNT] = {
+ {LTGREEN, WHITE,0,0}
+ };
+ static TLucentType const UShadowColsSnow[USHADOW_COL_COUNT] = {
+ {LTGREEN, BLACK,75,0}
+ };
+
+ /*
+ ** Invoke parent's init routine.
+ */
+ MapClass::Init_Theater(theater);
+
+ /*
+ ** Save the new theater value
+ */
+ Scen.Theater = theater;
+
+ /*
+ ** Unload old mixfiles, and cache the new ones
+ */
+ sprintf(fullname, "%s.MIX", Theaters[theater].Root);
+
+#ifndef WIN32
+LastTheater = THEATER_NONE;
+#endif
+
+ if (Scen.Theater != LastTheater) {
+ if (TheaterData != NULL) {
+ delete TheaterData;
+ }
+ TheaterData = new MFCD(fullname, &FastKey);
+ assert(TheaterData != NULL);
+
+ bool theaterload = TheaterData->Cache(TheaterBuffer);
+ assert(theaterload);
+// LastTheater = Scen.Theater;
+ }
+
+ /*
+ ** Load the custom palette associated with this theater.
+ ** The fading palettes will have to be generated as well.
+ */
+ sprintf(fullname, "%s.PAL", Theaters[theater].Root);
+ PaletteClass const * ptr = (PaletteClass *)MFCD::Retrieve(fullname);
+ GamePalette = * ptr;
+
+ OriginalPalette = GamePalette;
+
+ Build_Fading_Table(GamePalette, FadingGreen, GREEN, 110);
+
+ Build_Fading_Table(GamePalette, FadingYellow, YELLOW, 140);
+
+ Build_Fading_Table(GamePalette, FadingRed, RED, 140);
+
+ Build_Translucent_Table(GamePalette, &MouseCols[0], 4, MouseTranslucentTable);
+
+ Build_Translucent_Table(GamePalette, &MagicCols[0], MAGIC_COL_COUNT, TranslucentTable);
+
+ Build_Translucent_Table(GamePalette, &WhiteCols[0], 1, WhiteTranslucentTable);
+
+ Build_Translucent_Table(GamePalette, &ShadowCols[0], SHADOW_COL_COUNT, ShadowTrans);
+
+ Conquer_Build_Translucent_Table(GamePalette, &UShadowColsAir[0], USHADOW_COL_COUNT, UnitShadowAir);
+ memcpy(&UnitShadowAir[256], ColorRemaps[PCOLOR_GOLD].RemapTable, sizeof(ColorRemaps[PCOLOR_GOLD].RemapTable));
+ if (theater == THEATER_SNOW) {
+ Conquer_Build_Translucent_Table(GamePalette, &UShadowColsSnow[0], USHADOW_COL_COUNT, UnitShadow);
+ } else {
+ Conquer_Build_Translucent_Table(GamePalette, &UShadowCols[0], USHADOW_COL_COUNT, UnitShadow);
+ }
+
+ if (theater == THEATER_SNOW) {
+ Conquer_Build_Fading_Table(GamePalette, FadingShade, BLACK, 75);
+ } else {
+ Conquer_Build_Fading_Table(GamePalette, FadingShade, BLACK, 130);
+ }
+
+ Conquer_Build_Fading_Table(GamePalette, FadingLight, WHITE, 85);
+
+ /*
+ ** Create the shadow color used by aircraft.
+ */
+ Conquer_Build_Fading_Table(GamePalette, &SpecialGhost[256], BLACK, 100);
+ for (int index = 0; index < 256; index++) {
+ SpecialGhost[index] = 0;
+ }
+
+ Make_Fading_Table(GamePalette, FadingBrighten, WHITE, 25);
+
+ Make_Fading_Table(GamePalette, FadingWayDark, DKGRAY, 192);
+
+ /*
+ ** Adjust the palette according to the visual control option settings.
+ */
+ Options.Fixup_Palette();
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Text_Overlap_List -- Creates cell overlap list for specified text string. *
+ * *
+ * This routine is used to create an overlap list that specifies all the cells that are *
+ * covered by the specified text string. This overlap list is used to handle map refresh *
+ * logic. *
+ * *
+ * INPUT: text -- Pointer to the text that would appear on the map and must have an *
+ * overlap list generated. *
+ * *
+ * x,y -- The coordinates that the text would appear (upper left corner). *
+ * *
+ * OUTPUT: Returns with a pointer to an overlap list that covers all cells "under" the text *
+ * if were displayed at the coordinates specified. The list is actually a series of *
+ * offsets from the display's upper left corner cell number. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 12/06/1994 JLB : Created. *
+ * 12/07/1994 JLB : Sidebar fixup. *
+ * 08/13/1995 JLB : Optimized for variable sized help text. *
+ *=============================================================================================*/
+short const * DisplayClass::Text_Overlap_List(char const * text, int x, int y) const
+{
+ static short _list[60];
+ int count = ARRAY_SIZE(_list);
+
+ if (text != NULL) {
+ short * ptr = &_list[0];
+ int len = String_Pixel_Width(text)+CELL_PIXEL_W;
+ int right = TacPixelX + Lepton_To_Pixel(TacLeptonWidth);
+
+ /*
+ ** If the help text would spill into the sidebar, then flag this fact, but
+ ** shorten the apparent length so that the icon list calculation will
+ ** function correctly.
+ */
+ if (x+len >= TacPixelX+Lepton_To_Pixel(TacLeptonWidth)) {
+ len = right-x;
+ *ptr++ = REFRESH_SIDEBAR;
+ count--;
+ }
+
+ /*
+ ** Build the list of overlap cell offset values according to the text
+ ** coordinate and the length.
+ */
+ if (x <= right) {
+ CELL ul = Click_Cell_Calc(x, y-1);
+ CELL lr = Click_Cell_Calc(x+len-1, Bound(y+24, TacPixelY, TacPixelY+Lepton_To_Pixel(TacLeptonHeight) - 1));
+
+ if (ul == -1) ul = Click_Cell_Calc(x, y);
+
+ if (ul != -1 && lr != -1) {
+ for (int yy = Cell_Y(ul); yy <= Cell_Y(lr); yy++) {
+ for (int xx = Cell_X(ul); xx <= Cell_X(lr); xx++) {
+ *ptr++ = XY_Cell(xx, yy) - Coord_Cell(TacticalCoord);
+ count--;
+ if (count < 2) break;
+ }
+ if (count < 2) break;
+ }
+ }
+ }
+
+ *ptr = REFRESH_EOL;
+ }
+ return(_list);
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Set_View_Dimensions -- Sets the tactical display screen coordinates. *
+ * *
+ * Use this routine to set the tactical map screen coordinates and dimensions. This routine *
+ * is typically used when the screen size or position changes as a result of the sidebar *
+ * changing position or appearance. *
+ * *
+ * INPUT: x,y -- The X and Y pixel position on the screen for the tactical map upper left *
+ * corner. *
+ * *
+ * width -- The width of the tactical display (in icons). If this parameter is *
+ * omitted, then the width will be as wide as the screen will allow. *
+ * *
+ * height-- The height of the tactical display (in icons). If this parameter is *
+ * omitted, then the width will be as wide as the screen will allow. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 12/06/1994 JLB : Created. *
+ * 06/27/1995 JLB : Adjusts tactical map position if necessary. *
+ *=============================================================================================*/
+void DisplayClass::Set_View_Dimensions(int x, int y, int width, int height)
+{
+ if (width == -1) {
+ TacLeptonWidth = Pixel_To_Lepton(SeenBuff.Get_Width()-x);
+ } else {
+ TacLeptonWidth = width * CELL_LEPTON_W;
+ }
+
+ if (height == -1) {
+ height = (SeenBuff.Get_Height()-y) / CELL_PIXEL_H;
+ }
+ TacLeptonHeight = height * CELL_LEPTON_H;
+
+ /*
+ ** Adjust the tactical cell if it is now in an invalid position
+ ** because of the changed dimensions.
+ */
+ int xx = 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;
+ WindowList[WINDOW_TACTICAL][WINDOWY] = y;
+ WindowList[WINDOW_TACTICAL][WINDOWWIDTH] = Lepton_To_Pixel(TacLeptonWidth);
+ WindowList[WINDOW_TACTICAL][WINDOWHEIGHT] = Lepton_To_Pixel(TacLeptonHeight);
+ if (Window == WINDOW_TACTICAL) {
+ Change_Window(0);
+ Change_Window(Window);
+ }
+ IsToRedraw = true;
+ Flag_To_Redraw(false);
+
+ TacButton.X = TacPixelX;
+ TacButton.Y = TacPixelY;
+ TacButton.Width = Lepton_To_Pixel(TacLeptonWidth);
+ TacButton.Height = Lepton_To_Pixel(TacLeptonHeight);
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Set_Cursor_Shape -- Changes the shape of the terrain square cursor. *
+ * *
+ * This routine is used to set up the terrain cursor according to the size of the object *
+ * that is to be placed down. The terrain cursor looks like an arbitrary collection of *
+ * hatched square overlays. Typical use is when placing buildings. *
+ * *
+ * INPUT: list -- A pointer to the list that contains offsets to the cells that are to *
+ * be marked. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/03/1994 JLB : Created. *
+ * 06/26/1995 JLB : Puts placement cursor into static buffer. *
+ *=============================================================================================*/
+void DisplayClass::Set_Cursor_Shape(short const * list)
+{
+ if (CursorSize) {
+ Cursor_Mark(ZoneCell+ZoneOffset, false);
+ }
+ ZoneOffset = 0;
+
+ if (list) {
+ int w,h;
+ static short _list[50];
+
+ memcpy(_list, list, sizeof(_list));
+ CursorSize = _list;
+ Get_Occupy_Dimensions (w, h, CursorSize);
+ ZoneOffset = -(((h/2)*MAP_CELL_W)+(w/2));
+ Cursor_Mark(ZoneCell+ZoneOffset, true);
+ } else {
+ CursorSize = 0;
+ }
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Passes_Proximity_Check -- Determines if building placement is near friendly sq*
+ * *
+ * This routine is used by the building placement cursor logic to determine whether the *
+ * at the current cursor position if the building would be adjacent to another friendly *
+ * building. In cases where this is not true, then the building cannot be placed at all. *
+ * This determination is returned by the function. *
+ * *
+ * INPUT: object -- The building object that the current placement system is examining. *
+ * *
+ * house -- The house to base the proximity check upon. Typically this is the *
+ * player's house, but in multiplay, the computer needs to check for *
+ * proximity as well. *
+ * *
+ * list -- Pointer to the building's offset list. *
+ * *
+ * trycell -- The cell to base the offset list on. *
+ * *
+ * OUTPUT: bool; Can the pending building object be placed at the present cursor location *
+ * checking only for proximity to friendly buildings? If this isn't for a *
+ * building type object, then this routine always returns true. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/06/1994 JLB : Created. *
+ * 06/07/1994 JLB : Handles concrete check. *
+ * 10/11/1994 BWG : Added IsProximate check for ore refineries *
+ *=============================================================================================*/
+bool DisplayClass::Passes_Proximity_Check(ObjectTypeClass const * object, HousesType house, short const * list, CELL trycell) const
+{
+ short const * ptr;
+ int retval = -1;
+ bool noradar = false;
+ bool nomapped = false;
+ bool shipyard = false;
+
+ if (house == PlayerPtr->Class->House) {
+ PassedProximity = false;
+ }
+
+ /*
+ ** In editor mode, the proximity check always passes.
+ */
+ if (Debug_Map) {
+ return(true);
+ }
+
+ if (list == NULL || trycell == 0) {
+ return(true);
+ }
+
+ if (object == NULL || object->What_Am_I() != RTTI_BUILDINGTYPE) {
+ return(true);
+ }
+
+ BuildingTypeClass const * building = (BuildingTypeClass const *)object;
+
+ /*
+ ** Scan through all cells that the building foundation would cover. If any adjacent
+ ** cells to these are of friendly persuasion, then consider the proximity check to
+ ** have been a success.
+ */
+ ptr = list;
+// ptr = CursorSize;
+ CELL cell = trycell;
+// CELL cell = ZoneCell;
+ if (building->Adjacent == 1) {
+ while (*ptr != REFRESH_EOL && (retval == -1) ) {
+ cell = trycell + *ptr++;
+// cell = ZoneCell + ZoneOffset + *ptr++;
+
+ if (!In_Radar(cell)) {
+ retval = false;
+ noradar = true;
+ break;
+ }
+
+ for (FacingType facing = FACING_FIRST; facing < FACING_COUNT; facing++) {
+ CELL newcell = Adjacent_Cell(cell, facing);
+
+ if (!In_Radar(newcell)) continue;
+
+ if (!(*this)[newcell].IsMapped) {
+ nomapped = true;
+ }
+ 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.
+ */
+ //BG: Modified so only walls can be placed next to walls - buildings can't.
+ //JLB: Except for bibs, in which case buildings can be placed next to these.
+ if (building->IsWall ||
+ ((*this)[newcell].Smudge != SMUDGE_NONE && SmudgeTypeClass::As_Reference((*this)[newcell].Smudge).IsBib)) {
+
+ if ((*this)[newcell].Owner == house) {
+ retval = true;
+ break;
+ }
+ }
+
+ // we've found a building...
+ if (base != NULL && base->What_Am_I() == RTTI_BUILDING && base->House->Class->House == house && ((BuildingClass *)base)->Class->IsBase) {
+ retval = true;
+ break;
+ }
+
+ /* BG: modifications to allow buildings one cell away from other buildings.
+ ** This is done by scanning each cell that fails the check (hence getting
+ ** to this point) and looking at the n/s/e/w adjacent cells to see if they
+ ** have buildings in them. If they do, and they match us, then succeed.
+ */
+ if (retval != -1) break;
+
+ for (FacingType newface = FACING_N; newface < FACING_COUNT; newface++) {
+ CELL newercell = Adjacent_Cell(newcell, newface);
+
+ if (building->IsWall ||
+ ((*this)[newercell].Smudge != SMUDGE_NONE && SmudgeTypeClass::As_Reference((*this)[newercell].Smudge).IsBib)) {
+
+ if ((*this)[newercell].Owner == house) {
+ retval = true;
+ break;
+ }
+ }
+
+ TechnoClass * newbase = (*this)[newercell].Cell_Techno();
+
+ // we've found a building...
+ if (newbase != NULL && newbase->What_Am_I() == RTTI_BUILDING && newbase->House->Class->House == house && ((BuildingClass const *)newbase)->Class->IsBase) {
+ retval = true;
+ break;
+ }
+ }
+ if (retval != -1) break;
+ }
+ }
+ }
+
+ if (retval == -1) retval = false;
+
+ if (house == PlayerPtr->Class->House) {
+ PassedProximity = (retval != false);
+ }
+
+ /*
+ ** If this object has special dispensation to be placed further than one cell from
+ ** other regular buildings, then check for this case now. Only bother to check if
+ ** it hasn't already been given permission to be placed down.
+ */
+ if (!retval && !noradar && object->What_Am_I() == RTTI_BUILDINGTYPE) {
+
+ // For land mines, let's make it check proximity within 10 squares
+ if (building->Adjacent > 1) {
+ for (int index = 0; index < Buildings.Count(); index++) {
+ BuildingClass * obj = Buildings.Ptr(index);
+ if (obj != NULL && !obj->IsInLimbo && obj->House->Class->House == house && obj->Class->IsBase) {
+ int centdist = ::Distance(obj->Center_Coord(), Cell_Coord(cell));
+ centdist /= CELL_LEPTON_W;
+ centdist -= (obj->Class->Width() + obj->Class->Height()) / 2;
+ if (centdist <= building->Adjacent) {
+ retval = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return((bool)retval);
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Set_Cursor_Pos -- Controls the display and animation of the tac cursor. *
+ * *
+ * This routine controls the location, display, and animation of the *
+ * tactical map cursor. *
+ * *
+ * INPUT: pos -- Position to move the cursor do. If -1 is passed then *
+ * the cursor will just be hidden. If the position *
+ * passed is the same as the last position passed in, *
+ * then animation could occur (based on timers). *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/22/1991 JLB : Created. *
+ * 06/02/1994 JLB : Converted to member function. *
+ * 06/08/1994 JLB : If position is -1, then follow mouse. *
+ * 02/28/1995 JLB : Forces placement cursor to fit on map. *
+ *=============================================================================================*/
+CELL DisplayClass::Set_Cursor_Pos(CELL pos)
+{
+ CELL prevpos; // Last position of cursor (for jump-back reasons).
+
+ /*
+ ** Follow the mouse position if no cell number is provided.
+ */
+ if (pos == -1) {
+ pos = Click_Cell_Calc(Get_Mouse_X(), Get_Mouse_Y());
+ }
+
+ if (CursorSize == NULL) {
+ prevpos = ZoneCell;
+ ZoneCell = pos;
+ return(prevpos);
+ }
+
+ /*
+ ** Adjusts the position so that the placement cursor is never part way off the
+ ** tactical map.
+ */
+ int w,h;
+ Get_Occupy_Dimensions(w, h, CursorSize);
+
+ int x = Cell_X(pos + ZoneOffset);
+ int y = Cell_Y(pos + ZoneOffset);
+
+ if (x < Coord_XCell(TacticalCoord)) x = Coord_XCell(TacticalCoord);
+ if (y < Coord_YCell(TacticalCoord)) y = Coord_YCell(TacticalCoord);
+ if (x+w >= Coord_XCell(TacticalCoord) + Lepton_To_Cell(TacLeptonWidth)) x = Coord_XCell(TacticalCoord)+Lepton_To_Cell(TacLeptonWidth)-w;
+ if (y+h >= Coord_YCell(TacticalCoord) + Lepton_To_Cell(TacLeptonHeight)) y = Coord_YCell(TacticalCoord)+Lepton_To_Cell(TacLeptonHeight)-h;
+ pos = XY_Cell(x, y) - ZoneOffset;
+
+ /*
+ ** This checks to see if NO animation or drawing is to occur and, if so,
+ ** exits.
+ */
+ if (pos == ZoneCell) return(pos);
+
+ prevpos = ZoneCell;
+
+ /*
+ ** If the cursor is visible, then handle the graphic update.
+ ** Otherwise, just update the global position of the cursor.
+ */
+ if (CursorSize != NULL) {
+
+ /*
+ ** Erase the old cursor (if it exists) AND the cursor is moving.
+ */
+ if (pos != ZoneCell && ZoneCell != -1) {
+ Cursor_Mark(ZoneCell+ZoneOffset, false);
+ }
+
+ /*
+ ** Render the cursor (could just be animation).
+ */
+ if (pos != -1) {
+ Cursor_Mark(pos+ZoneOffset, true);
+ }
+ }
+ ZoneCell = pos;
+ ProximityCheck = Passes_Proximity_Check(PendingObject, PendingHouse, CursorSize, ZoneCell+ZoneOffset);
+
+ return(prevpos);
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Get_Occupy_Dimensions -- computes width & height of the given occupy list *
+ * *
+ * INPUT: *
+ * w ptr to fill in with height *
+ * h ptr to fill in with width *
+ * *
+ * OUTPUT: *
+ * none. *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 03/31/1995 BRR : Created. *
+ *=============================================================================================*/
+void DisplayClass::Get_Occupy_Dimensions(int & w, int & h, short const * list) const
+{
+ int min_x = MAP_CELL_W;
+ int max_x = -MAP_CELL_W;
+ int min_y = MAP_CELL_H;
+ int max_y = -MAP_CELL_H;
+ int x,y;
+
+ w = 0;
+ h = 0;
+
+ if (!list) {
+ /*
+ ** Loop through all cell offsets, accumulating max & min x- & y-coords
+ */
+ while (*list != REFRESH_EOL) {
+ /*
+ ** Compute x & y coords of the current cell offset. We can't use Cell_X()
+ ** & Cell_Y(), because they use shifts to compute the values, and if the
+ ** offset is negative we'll get a bogus coordinate!
+ */
+ x = (*list) % MAP_CELL_W;
+ y = (*list) / MAP_CELL_H;
+
+ max_x = max(max_x, x);
+ min_x = min(min_x, x);
+ max_y = max(max_y, y);
+ min_y = min(min_y, y);
+
+ list++;
+ }
+
+ w = max(1, max_x - min_x + 1);
+ h = min(1, max_y - min_y + 1);
+ }
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Cursor_Mark -- Set or resets the cursor display flag bits. *
+ * *
+ * This routine will clear or set the cursor display bits on the map. *
+ * If the bit is set, then the cursor will be rendered on that map *
+ * icon. *
+ * *
+ * INPUT: pos -- Position of the upper left corner of the cursor. *
+ * *
+ * on -- Should the bit be turned on? *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: Be sure that every call to set the bits is matched by a *
+ * corresponding call to clear the bits. *
+ * *
+ * HISTORY: *
+ * 09/04/1991 JLB : Created. *
+ * 06/02/1994 JLB : Converted to member function. *
+ *=============================================================================================*/
+void DisplayClass::Cursor_Mark(CELL pos, bool on)
+{
+ CELL const * ptr;
+ CellClass * cellptr;
+
+ if (pos == -1) return;
+
+ /*
+ ** For every cell in the CursorSize list, invoke its Redraw_Objects and
+ ** toggle its IsCursorHere flag
+ */
+ ptr = CursorSize;
+ while (*ptr != REFRESH_EOL) {
+ CELL cell = pos + *ptr++;
+ if (In_Radar(cell)) {
+ cellptr = &(*this)[cell];
+ cellptr->Redraw_Objects();
+ if (on) {
+ cellptr->IsCursorHere = true;
+ } else {
+ cellptr->IsCursorHere = false;
+ }
+ }
+ }
+
+ /*
+ ** For every cell in the PendingObjectPtr's Overlap_List, invoke its
+ ** Redraw_Objects routine.
+ */
+ if (PendingObjectPtr) {
+ ptr = PendingObjectPtr->Overlap_List();
+ while (*ptr != REFRESH_EOL) {
+ CELL cell = pos + *ptr++;
+ if (In_Radar(cell)) {
+ cellptr = &(*this)[cell];
+ cellptr->Redraw_Objects();
+ }
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::AI -- Handles the maintenance tasks for the map display. *
+ * *
+ * This routine is called once per game display frame (15 times per second). It handles *
+ * the mouse shape tracking and map scrolling as necessary. *
+ * *
+ * INPUT: input -- The next key just fetched from the input queue. *
+ * *
+ * x,y -- Mouse coordinates. *
+ * *
+ * OUTPUT: Modifies the input code if necessary. When the input code is consumed, it gets *
+ * set to 0. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/01/1994 JLB : Created. *
+ * 06/02/1994 JLB : Filters mouse click input. *
+ * 06/07/1994 JLB : Fixed so template click will behave right. *
+ * 10/14/1994 JLB : Changing cursor shape over target. *
+ * 12/31/1994 JLB : Takes mouse coordinates as parameters. *
+ * 06/27/1995 JLB : Breaks out of rubber band mode if mouse leaves map. *
+ *=============================================================================================*/
+void DisplayClass::AI(KeyNumType & input, int x, int y)
+{
+ if (
+ IsRubberBand &&
+ (Get_Mouse_X() < TacPixelX ||
+ Get_Mouse_Y() < TacPixelY ||
+ Get_Mouse_X() >= (TacPixelX + Lepton_To_Pixel(TacLeptonWidth)) ||
+ Get_Mouse_Y() >= (TacPixelY + Lepton_To_Pixel(TacLeptonHeight)))) {
+ Mouse_Left_Release(-1, Get_Mouse_X(), Get_Mouse_Y(), NULL, ACTION_NONE);
+ }
+
+ MapClass::AI(input, x, y);
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Submit -- Adds a game object to the map rendering system. *
+ * *
+ * This routine is used to add an arbitrary (but tangible) game object to the map. It will *
+ * be rendered (made visible) once it is submitted to this function. This function builds *
+ * the list of game objects that get rendered each frame as necessary. It is possible to *
+ * submit the game object to different rendering layers. All objects in a layer get drawn *
+ * at the same time. Using this layer method it becomes possible to have objects "below" *
+ * other objects. *
+ * *
+ * INPUT: object -- Pointer to the object to add. *
+ * *
+ * layer -- The layer to add the object to. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/31/1994 JLB : Created. *
+ * 05/31/1994 JLB : Improved layer system. *
+ * 05/31/1994 JLB : Sorts object position if this is for the ground layer. *
+ *=============================================================================================*/
+void DisplayClass::Submit(ObjectClass const * object, LayerType layer)
+{
+ if (object) {
+ Layer[layer].Submit(object, (layer == LAYER_GROUND));
+ }
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Remove -- Removes a game object from the rendering system. *
+ * *
+ * Every object that is to disappear from the map must be removed from the rendering *
+ * system. *
+ * *
+ * INPUT: object -- The object to remove. *
+ * *
+ * layer -- The layer to remove it from. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/31/1994 JLB : Created. *
+ * 05/31/1994 JLB : Improved layer system. *
+ *=============================================================================================*/
+void DisplayClass::Remove(ObjectClass const * object, LayerType layer)
+{
+ assert(object != 0);
+ assert(object->IsActive);
+
+ if (object) {
+ Layer[layer].Delete((ObjectClass *)object);
+ }
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Click_Cell_Calc -- Determines cell from screen X & Y. *
+ * *
+ * This routine is used to determine the cell that is located at the *
+ * screen pixel coordinates given. Typical use is when the player *
+ * clicks with the mouse on the tactical map. *
+ * *
+ * INPUT: x,y -- Screen pixel coordinates. *
+ * *
+ * OUTPUT: Returns with cell that is under the coordinates specified. *
+ * If the coordinate specified is outside of the tactical *
+ * map, then -1 is returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/27/1994 JLB : Created. *
+ *=============================================================================================*/
+CELL DisplayClass::Click_Cell_Calc(int x, int y) const
+{
+ x -= TacPixelX;
+ x = Pixel_To_Lepton(x);
+ y -= TacPixelY;
+ y = Pixel_To_Lepton(y);
+
+ 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::Scroll_Map -- Scroll the tactical map in desired direction. *
+ * *
+ * This routine is used to scroll the tactical map view in the desired *
+ * direction. It can also be used to determine if scrolling would be *
+ * legal without actually performing any scrolling action. *
+ * *
+ * INPUT: facing -- The direction to scroll the tactical map. *
+ * *
+ * distance -- The distance in leptons to scroll the map. *
+ * *
+ * really -- Should the map actually be scrolled? If false, *
+ * then only the legality of a scroll is checked. *
+ * *
+ * OUTPUT: bool; Would scrolling in the desired direction be possible? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 10/07/1992 JLB : Created. *
+ * 05/20/1994 JLB : Converted to member function. *
+ * 08/09/1995 JLB : Added distance parameter. *
+ * 08/10/1995 JLB : Any direction scrolling. *
+ *=============================================================================================*/
+bool DisplayClass::Scroll_Map(DirType facing, int & distance, bool really)
+{
+ /*
+ ** If the distance is invalid then no further checking is required. Bail
+ ** with a no-can-do flag.
+ */
+ if (distance == 0) return(false);
+ FacingType crude = Dir_Facing(facing);
+
+ if (Coord_X(TacticalCoord) == Cell_To_Lepton(MapCellX) && crude != FACING_W) {
+ if (crude == FACING_SW) facing = DIR_S;
+ if (crude == FACING_NW) facing = DIR_N;
+ }
+ if (Coord_Y(TacticalCoord) == Cell_To_Lepton(MapCellY) && crude != FACING_N) {
+ if (crude == FACING_NW) facing = DIR_W;
+ if (crude == FACING_NE) facing = DIR_E;
+ }
+ if (Coord_X(TacticalCoord) + TacLeptonWidth == Cell_To_Lepton(MapCellX+MapCellWidth) && crude != FACING_E) {
+ if (crude == FACING_NE) facing = DIR_N;
+ if (crude == FACING_SE) facing = DIR_S;
+ }
+ if (Coord_Y(TacticalCoord) + TacLeptonHeight == Cell_To_Lepton(MapCellY+MapCellHeight) && crude != FACING_S) {
+ if (crude == FACING_SE) facing = DIR_E;
+ if (crude == FACING_SW) facing = DIR_W;
+ }
+
+ /*
+ ** Determine the coordinate that it wants to scroll to.
+ */
+ COORDINATE coord = Coord_Move(TacticalCoord, facing, distance);
+
+ /*
+ ** Clip the new coordinate to the edges of the game world.
+ */
+ int xx = (int)(short)Coord_X(coord) - (short)Cell_To_Lepton(MapCellX);
+ int yy = (int)(short)Coord_Y(coord) - (short)Cell_To_Lepton(MapCellY);
+ bool shifted = Confine_Rect(&xx, &yy, TacLeptonWidth, TacLeptonHeight, Cell_To_Lepton(MapCellWidth), Cell_To_Lepton(MapCellHeight));
+ if (xx < 0) {
+ xx = 0;
+ shifted = true;
+ }
+ if (yy < 0) {
+ yy = 0;
+ shifted = true;
+ }
+ coord = XY_Coord(xx + Cell_To_Lepton(MapCellX), yy + Cell_To_Lepton(MapCellY));
+
+ /*
+ ** If the desired scroll was bound by the edge of the map, then adjust the distance to more accurately
+ ** reflect the actual distance moved.
+ */
+ if (shifted) {
+ distance = Distance(TacticalCoord, coord);
+ }
+
+ /*
+ ** If the new coordinate is the same as the old, then no scrolling would occur.
+ */
+ if (!distance || coord == TacticalCoord) return(false);
+
+ /*
+ ** Since the new coordinate is different than the old one, possibly adjust the real
+ ** tactical map accordingly.
+ */
+ if (really) {
+ Set_Tactical_Position(coord);
+ IsToRedraw = true;
+ Flag_To_Redraw(false);
+
+ /*
+ ** Scrolled map REQUIRES all top layer units to be redrawn.
+ */
+ for (int index = 0; index < Layer[LAYER_TOP].Count(); index++) {
+ Layer[LAYER_TOP][index]->Mark(MARK_CHANGE);
+ }
+ for (index = 0; index < Layer[LAYER_AIR].Count(); index++) {
+ Layer[LAYER_AIR][index]->Mark(MARK_CHANGE);
+ }
+
+
+ }
+ return(true);
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Refresh_Cells -- Redraws all cells in list. *
+ * *
+ * This routine is used to flag all cells in the specified list for *
+ * redrawing. *
+ * *
+ * INPUT: cell -- The origin cell that the list is offset from. *
+ * *
+ * list -- Pointer to a list of offsets from the origin cell. *
+ * Each cell so specified is flagged for redraw. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: This routine is rather slow (by definition). *
+ * *
+ * HISTORY: *
+ * 05/14/1994 JLB : Created. *
+ * 08/01/1994 JLB : Simplified. *
+ *=============================================================================================*/
+void DisplayClass::Refresh_Cells(CELL cell, short const * list)
+{
+ short tlist[36];
+
+ if (*list == REFRESH_SIDEBAR) {
+ list++;
+ }
+
+ List_Copy(list, ARRAY_SIZE(tlist), tlist);
+ short * tt = tlist;
+ while (*tt != REFRESH_EOL) {
+ CELL newcell = cell + *tt++;
+ 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) const
+{
+ static char const _shadow[256]={
+ -1,33, 2, 2,34,37, 2, 2,
+ 4,26, 6, 6, 4,26, 6, 6,
+ 35,45,17,17,38,41,17,17,
+ 4,26, 6, 6, 4,26, 6, 6,
+ 8,21,10,10,27,31,10,10,
+ 12,23,14,14,12,23,14,14,
+ 8,21,10,10,27,31,10,10,
+ 12,23,14,14,12,23,14,14,
+
+ 32,36,25,25,44,40,25,25,
+ 19,30,20,20,19,30,20,20,
+ 39,43,29,29,42,46,29,29,
+ 19,30,20,20,19,30,20,20,
+ 8,21,10,10,27,31,10,10,
+ 12,23,14,14,12,23,14,14,
+ 8,21,10,10,27,31,10,10,
+ 12,23,14,14,12,23,14,14,
+
+ 1, 1, 3, 3,16,16, 3, 3,
+ 5, 5, 7, 7, 5, 5, 7, 7,
+ 24,24,18,18,28,28,18,18,
+ 5, 5, 7, 7, 5, 5, 7, 7,
+ 9, 9,11,11,22,22,11,11,
+ 13,13,-2,-2,13,13,-2,-2,
+ 9, 9,11,11,22,22,11,11,
+ 13,13,-2,-2,13,13,-2,-2,
+
+ 1, 1, 3, 3,16,16, 3, 3,
+ 5, 5, 7, 7, 5, 5, 7, 7,
+ 24,24,18,18,28,28,18,18,
+ 5, 5, 7, 7, 5, 5, 7, 7,
+ 9, 9,11,11,22,22,11,11,
+ 13,13,-2,-2,13,13,-2,-2,
+ 9, 9,11,11,22,22,11,11,
+ 13,13,-2,-2,13,13,-2,-2
+ };
+
+ int index = 0, value = -1;
+
+ /*
+ ** Don't map cells that are at the top or bottom edge. This solves
+ ** problem of accessing cells off the top or bottom of the map and into
+ ** who-knows-what memory.
+ */
+#ifdef FIXIT_CSII // checked - ajw 9/28/98
+ if ((unsigned)(Cell_Y(cell)-1) >= MAP_CELL_H-2) return(-1);
+#else
+ if ((unsigned)(Cell_Y(cell)-1) > MAP_CELL_H-2) return(-1);
+#endif
+ //if ((unsigned)(Cell_Y(cell)-1) > MAP_CELL_H-2) return(-2);
+
+ CellClass const * cellptr = &(*this)[cell];
+
+ /*
+ ** Presume solid black if that is what is here already.
+ */
+ if (!cellptr->IsVisible && !cellptr->IsMapped) value = -2;
+
+ if (cellptr->IsMapped /*&& !cellptr->IsVisible*/) {
+ /*
+ ** Build an index into the lookup table using all 8 surrounding cells.
+ ** We're mapping a revealed cell and we only care about the existence
+ ** of black cells. Bit numbering starts at the upper-right corner and
+ ** goes around the cell clockwise, so 0x80 = directly north.
+ */
+ cellptr-= MAP_CELL_W + 1;
+ if (!cellptr->IsMapped) index |= 0x40;
+ cellptr++;
+ if (!cellptr->IsMapped) index |= 0x80;
+ cellptr++;
+ if (!cellptr->IsMapped) index |= 0x01;
+ cellptr += MAP_CELL_W - 2;
+ if (!cellptr->IsMapped) index |= 0x20;
+ cellptr += 2;
+ if (!cellptr->IsMapped) index |= 0x02;
+ cellptr += MAP_CELL_W - 2;
+ if (!cellptr->IsMapped) index |= 0x10;
+ cellptr++;
+ if (!cellptr->IsMapped) index |= 0x08;
+ cellptr++;
+ if (!cellptr->IsMapped) index |= 0x04;
+
+ value = _shadow[index];
+ }
+ return(value);
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Map_Cell -- Mark specified cell as having been mapped. *
+ * *
+ * This routine maps the specified cell. The cell must not already *
+ * have been mapped and the mapping player must be the human. *
+ * This routine will update any adjacent cell map icon as appropriate. *
+ * *
+ * INPUT: cell -- The cell to be mapped. *
+ * *
+ * house -- The player that is doing the mapping. *
+ * *
+ * OUTPUT: bool; Was action taken to map this cell? *
+ * *
+ * WARNINGS: none. *
+ * *
+ * HISTORY: *
+ * 08/05/1992 JLB : Created. *
+ * 04/30/1994 JLB : Converted to member function. *
+ * 05/24/1994 JLB : Takes pointer to HouseClass. *
+ * 02/20/1996 JLB : Allied units reveal the map for the player. *
+ *=============================================================================================*/
+bool DisplayClass::Map_Cell(CELL cell, HouseClass * house)
+{
+ /*
+ ** First check for the condition where we're spying on a house's radar
+ ** facility, to see if his mapping is applicable to us.
+ */
+ if (house && house != PlayerPtr) {
+ if (house->RadarSpied & (1<<(PlayerPtr->Class->House))) house = PlayerPtr;
+ if (Session.Type == GAME_NORMAL && house->Is_Ally(PlayerPtr)) house = PlayerPtr;
+ }
+
+ if (house != PlayerPtr || !In_Radar(cell)) return(false);
+
+ CellClass * cellptr = &(*this)[cell];
+
+ /*
+ ** Don't bother remapping this cell if it is already mapped.
+ */
+ if (cellptr->IsMapped) {
+ if (!cellptr->IsVisible) {
+ cellptr->Redraw_Objects();
+ }
+ return(false);
+ }
+
+ /*
+ ** Mark the cell as being mapped. This must be done first because
+ ** if the IsVisible flag must be set, then it might affect the
+ ** adjacent cell processing.
+ */
+ cellptr->IsMapped = true;
+ cellptr->Redraw_Objects();
+ if (Cell_Shadow(cell) == -1) {
+ cellptr->IsVisible = true;
+ }
+
+ /*
+ ** 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);
+ CellClass * cptr = &(*this)[c];
+ cptr->Redraw_Objects();
+
+ if (c != cell && !cptr->IsVisible) {
+ shadow = Cell_Shadow(c);
+
+ if (shadow == -1) {
+ if (!cptr->IsMapped) {
+ Map_Cell(c, house);
+ } else {
+ cptr->IsVisible = true;
+ }
+ } else {
+ if (shadow != -2 && !cptr->IsMapped) {
+ Map_Cell(c, house);
+ }
+ }
+ }
+ }
+
+ TechnoClass * tech = (*this)[cell].Cell_Techno();
+ if (tech) {
+ tech->Revealed(house);
+ }
+ return(true);
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Coord_To_Pixel -- Determines X and Y pixel coordinates. *
+ * *
+ * This is the routine that figures out the location on the screen for *
+ * a specified coordinate. It is one of the fundamental routines *
+ * necessary for rendering the game objects. It performs some quick *
+ * tests to see if the coordinate is in a visible region and returns *
+ * this check as a boolean value. *
+ * *
+ * INPUT: coord -- The coordinate to check. *
+ * *
+ * x,y -- Reference to the pixel coordinates that this *
+ * coordinate would be when rendered. *
+ * *
+ * OUTPUT: bool; Is this coordinate in a visible portion of the map? *
+ * *
+ * WARNINGS: If the coordinate is not in a visible portion of the *
+ * map, then this X and Y parameters are not set. *
+ * *
+ * HISTORY: *
+ * 05/14/1994 JLB : Created. *
+ * 12/15/1994 JLB : Converted to member function. *
+ * 01/07/1995 JLB : Uses inline functions to extract coord components. *
+ * 08/09/1995 JLB : Uses new coordinate system. *
+ *=============================================================================================*/
+#define EDGE_ZONE (CELL_LEPTON_W*2)
+bool DisplayClass::Coord_To_Pixel(COORDINATE coord, int &x, int &y) const
+{
+ 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) const
+{
+ return(*this)[cell].Cell_Object(x, y);
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Draw_It -- Draws the tactical map. *
+ * *
+ * This will draw the tactical map at the recorded position. This *
+ * routine is used whenever the tactical map moves or needs to be *
+ * completely redrawn. It will handle making the necessary adjustments *
+ * to accomodate a moving cursor. *
+ * *
+ * INPUT: forced -- bool; force redraw of the entire display? *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 04/15/1991 JLB : Created. (benchmark = 292) *
+ * 04/15/1991 JLB : Added _cell2meta[] reference array (206) *
+ * 04/15/1991 JLB : Added actual map reference for terrain (207) *
+ * 04/16/1991 JLB : _cell2meta converted to int (194) *
+ * 04/16/1991 JLB : References actual CellIcon[] array (204) *
+ * 04/16/1991 JLB : Cell size increased to 16 x 16 (167) *
+ * 04/17/1991 JLB : Cell based tactical map rendering (165) *
+ * 04/22/1991 JLB : Uses Draw_Stamp() for icon rendering (426) *
+ * 04/22/1991 JLB : Draw_Stamp uses LogicPage now (276) *
+ * 04/23/1991 JLB : Map active location cursor (334) *
+ * 05/02/1991 JLB : Added smoothing and 3 icons sets (431) *
+ * 05/22/1991 JLB : Broken into Draw_Map() and Refresh_Map(). *
+ * 09/14/1991 JLB : Uses Refresh_Cell when new cells scroll onto display. *
+ * 05/12/1992 JLB : Destination page support. *
+ * 02/14/1994 JLB : Revamped. *
+ * 05/01/1994 JLB : Converted to member function. *
+ * 12/15/1994 JLB : Updated to work with display hierarchy. *
+ * 12/24/1994 JLB : Examines redraw bit intelligently. *
+ * 12/24/1994 JLB : Combined with old Refresh_Map() function. *
+ * 01/10/1995 JLB : Rubber band drawing. *
+ *=============================================================================================*/
+ void DisplayClass::Draw_It(bool forced)
+{
+ int x,y; // Working cell index values.
+
+ MapClass::Draw_It(forced);
+
+ if (IsToRedraw || forced) {
+ BStart(BENCH_TACTICAL);
+ IsToRedraw = false;
+
+ /*
+ ** In rubber band mode, mark all cells under the "rubber band" to be
+ ** redrawn.
+ */
+ Refresh_Band();
+
+ /*
+ ** Mark all cells under the vortex to be redrawn
+ */
+ ChronalVortex.Set_Redraw();
+
+
+ /*
+ ** If the multiplayer message system is displaying one or more messages,
+ ** flag all cells covered by the messages to redraw. This will prevent
+ ** messages from smearing the map if it scrolls.
+ */
+ int num = Session.Messages.Num_Messages();
+ if (num > 0) {
+ 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 > 2) {
+ for (cell = Coord_Cell(TacticalCoord) + MAP_CELL_W*3;
+ cell < Coord_Cell(TacticalCoord) + MAP_CELL_W*3 + Lepton_To_Cell(TacLeptonWidth)+1; cell++) {
+ (*this)[cell].Redraw_Objects();
+ }
+ }
+ if (num > 3) {
+ for (cell = Coord_Cell(TacticalCoord) + MAP_CELL_W*4;
+ cell < Coord_Cell(TacticalCoord) + MAP_CELL_W*4 + Lepton_To_Cell(TacLeptonWidth)+1; cell++) {
+ (*this)[cell].Redraw_Objects();
+ }
+ }
+ }
+
+ /*
+ ** Check for a movement of the tactical map. If there has been some
+ ** movement, then part (or all) of the icons must be redrawn.
+ */
+ if (Lepton_To_Pixel(Coord_X(DesiredTacticalCoord)) != Lepton_To_Pixel(Coord_X(TacticalCoord)) ||
+ Lepton_To_Pixel(Coord_Y(DesiredTacticalCoord)) != Lepton_To_Pixel(Coord_Y(TacticalCoord))) {
+
+ int xmod = Lepton_To_Pixel(Coord_X(DesiredTacticalCoord));
+ int ymod = Lepton_To_Pixel(Coord_Y(DesiredTacticalCoord));
+
+ int oldx = Lepton_To_Pixel(Coord_X(TacticalCoord))-xmod; // Old relative offset.
+ int oldy = Lepton_To_Pixel(Coord_Y(TacticalCoord))-ymod;
+
+ int oldw = Lepton_To_Pixel(TacLeptonWidth)-ABS(oldx); // Replicable width.
+ int oldh = Lepton_To_Pixel(TacLeptonHeight)-ABS(oldy); // Replicable height.
+
+ if (oldw < 1) forced = true;
+ if (oldh < 1) forced = true;
+
+
+#ifdef WIN32 //For WIN32 only redraw the edges of the map that move into view
+
+ /*
+ ** Work out which map edges need to be redrawn
+ */
+ BOOL redraw_right = (oldx < 0) ? true : false; //Right hand edge
+ BOOL redraw_left = (oldx > 0) ? true : false; //Left hand edge
+ BOOL redraw_bottom= (oldy < 0) ? true : false; //Bottom edge
+ BOOL redraw_top = (oldy > 0) ? true : false; //Top edge
+
+ /*
+ ** Blit any replicable block to avoid having to drawstamp.
+ */
+ if (!forced && (oldw != Lepton_To_Pixel(TacLeptonWidth) || oldh != Lepton_To_Pixel(TacLeptonHeight))) {
+ Set_Cursor_Pos(-1);
+
+ /*
+ ** If hid page is in video memory then blit from the seen page to avoid blitting
+ ** an overlapped region.
+ */
+ if (HidPage.Get_IsDirectDraw()) {
+ Hide_Mouse();
+ SeenBuff.Blit(HidPage,
+ ((oldx < 0) ? -oldx : 0) +TacPixelX,
+ ((oldy < 0) ? -oldy : 0) +TacPixelY,
+ ((oldx < 0) ? 0 : oldx) +TacPixelX,
+ ((oldy < 0) ? 0 : oldy) +TacPixelY,
+ oldw,
+ oldh);
+ Show_Mouse();
+ } else {
+ HidPage.Blit(HidPage,
+ ((oldx < 0) ? -oldx : 0) +TacPixelX,
+ ((oldy < 0) ? -oldy : 0) +TacPixelY,
+ ((oldx < 0) ? 0 : oldx) +TacPixelX,
+ ((oldy < 0) ? 0 : oldy) +TacPixelY,
+ oldw,
+ oldh);
+ }
+
+ } else {
+ forced = true;
+ }
+
+ if (oldx < 0) oldx = 0;
+ if (oldy < 0) oldy = 0;
+
+ /*
+ ** Record new map position for future reference.
+ */
+ ScenarioInit++;
+ Set_Tactical_Position(DesiredTacticalCoord);
+ ScenarioInit--;
+
+ if (!forced) {
+
+ /*
+ **
+ ** Set the 'redraw stamp' bit for any cells that could not be copied.
+ **
+ */
+ int startx = -Lepton_To_Pixel(Coord_XLepton(TacticalCoord));
+ int starty = -Lepton_To_Pixel(Coord_YLepton(TacticalCoord));
+ oldw -= 24;
+ oldh -= 24;
+
+ if (abs(oldx) < 0x25 && abs(oldy) < 0x25) {
+
+ /*
+ ** The width of the area we redraw depends on the scroll speed
+ */
+ int extra_x = (abs(oldx)>=16) ? 2 : 1;
+ int extra_y = (abs(oldy)>=16) ? 2 : 1;
+
+ /*
+ ** Flag the cells across the top of the visible area if required
+ */
+ if (redraw_top) {
+ for (y = starty; y <= starty+CELL_PIXEL_H*extra_y; y += CELL_PIXEL_H) {
+ for (x = startx; x <= Lepton_To_Pixel(TacLeptonWidth)+((CELL_PIXEL_W*2)); x += CELL_PIXEL_W) {
+ CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX,
+ Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY);
+
+ if (c > 0) (*this)[c].Redraw_Objects(true);
+ }
+ }
+ }
+
+ /*
+ ** Flag the cells across the bottom of the visible area if required
+ */
+ if (redraw_bottom) {
+ for (y = Lepton_To_Pixel(TacLeptonHeight)-CELL_PIXEL_H*(1+extra_y); y <= Lepton_To_Pixel(TacLeptonHeight)+CELL_PIXEL_H*3; y += CELL_PIXEL_H) {
+ for (x = startx; x <= Lepton_To_Pixel(TacLeptonWidth)+((CELL_PIXEL_W*2)); x += CELL_PIXEL_W) {
+ CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX,
+ Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY);
+
+ if (c > 0) (*this)[c].Redraw_Objects(true);
+ }
+ }
+ }
+
+ /*
+ ** Flag the cells down the left of the visible area if required
+ */
+ if (redraw_left) {
+ for (x = startx; x <= startx + CELL_PIXEL_W*extra_x; x += CELL_PIXEL_W) {
+ for (y = starty; y <= Lepton_To_Pixel(TacLeptonHeight)+((CELL_PIXEL_H*2)); y += CELL_PIXEL_H) {
+ CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX,
+ Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY);
+
+ if (c > 0) (*this)[c].Redraw_Objects(true);
+ }
+ }
+ }
+
+ /*
+ ** Flag the cells down the right of the visible area if required
+ */
+ if (redraw_right) {
+ for (x = Lepton_To_Pixel(TacLeptonWidth)-CELL_PIXEL_W*(extra_x+1); x <= Lepton_To_Pixel(TacLeptonWidth)+CELL_PIXEL_W*3; x += CELL_PIXEL_W) {
+ for (y = starty; y <= Lepton_To_Pixel(TacLeptonHeight)+((CELL_PIXEL_H*2)); y += CELL_PIXEL_H) {
+ CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX,
+ Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY);
+
+ if (c > 0) (*this)[c].Redraw_Objects(true);
+ }
+ }
+ }
+
+ } else {
+
+ /*
+ ** Set the 'redraw stamp' bit for any cells that could not be copied.
+ */
+ int startx = -Lepton_To_Pixel(Coord_XLepton(TacticalCoord));
+ int starty = -Lepton_To_Pixel(Coord_YLepton(TacticalCoord));
+ oldw -= 24;
+ oldh -= 24;
+ for (y = starty; y <= Lepton_To_Pixel(TacLeptonHeight)+((CELL_PIXEL_H*2)); y += CELL_PIXEL_H) {
+ for (x = startx; x <= Lepton_To_Pixel(TacLeptonWidth)+((CELL_PIXEL_W*2)); x += CELL_PIXEL_W) {
+ if (x <= oldx || x >= oldx+oldw || y <= oldy || y >= oldy+oldh) {
+ CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX,
+ Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY);
+
+ if (c > 0) {
+ (*this)[c].Redraw_Objects(true);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ } else {
+
+ /*
+ ** Set the tactical coordinate just in case the desired tactical has changed but
+ ** not enough to result in any visible map change. This is likely to occur with very
+ ** slow scroll rates.
+ */
+ ScenarioInit++;
+ if (DesiredTacticalCoord != TacticalCoord) {
+ Set_Tactical_Position(DesiredTacticalCoord);
+ }
+ ScenarioInit--;
+ }
+
+
+#else //WIN32
+ /*
+ ** Blit any replicable block to avoid having to drawstamp.
+ */
+ if (!forced && (oldw != Lepton_To_Pixel(TacLeptonWidth) || oldh != Lepton_To_Pixel(TacLeptonHeight))) {
+ Set_Cursor_Pos(-1);
+
+ HidPage.Blit(HidPage,
+ ((oldx < 0) ? -oldx : 0) +TacPixelX,
+ ((oldy < 0) ? -oldy : 0) +TacPixelY,
+ ((oldx < 0) ? 0 : oldx) +TacPixelX,
+ ((oldy < 0) ? 0 : oldy) +TacPixelY,
+ oldw,
+ oldh);
+ } else {
+ forced = true;
+ }
+
+ if (oldx < 0) oldx = 0;
+ if (oldy < 0) oldy = 0;
+
+ /*
+ ** Record new map position for future reference.
+ */
+ ScenarioInit++;
+ Set_Tactical_Position(DesiredTacticalCoord);
+ ScenarioInit--;
+
+ if (!forced) {
+
+ /*
+ ** Set the 'redraw stamp' bit for any cells that could not be copied.
+ */
+ int startx = -Lepton_To_Pixel(Coord_XLepton(TacticalCoord));
+ int starty = -Lepton_To_Pixel(Coord_YLepton(TacticalCoord));
+ oldw -= 24;
+ oldh -= 24;
+ for (y = starty; y <= Lepton_To_Pixel(TacLeptonHeight)+((CELL_PIXEL_H*2)); y += CELL_PIXEL_H) {
+ for (x = startx; x <= Lepton_To_Pixel(TacLeptonWidth)+((CELL_PIXEL_W*2)); x += CELL_PIXEL_W) {
+ if (x <= oldx || x >= oldx+oldw || y <= oldy || y >= oldy+oldh) {
+ CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX,
+ Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY);
+
+ if (c > 0) {
+ (*this)[c].Redraw_Objects(true);
+ }
+ }
+ }
+ }
+ }
+
+ } else {
+
+ /*
+ ** Set the tactical coordinate just in case the desired tactical has changed but
+ ** not enough to result in any visible map change. This is likely to occur with very
+ ** slow scroll rates.
+ */
+ ScenarioInit++;
+ if (DesiredTacticalCoord != TacticalCoord) {
+ Set_Tactical_Position(DesiredTacticalCoord);
+ }
+ ScenarioInit--;
+ }
+#endif
+
+ /*
+ ** If the entire tactical map is forced to be redrawn, then set all the redraw flags
+ ** and let the normal processing take care of the rest.
+ */
+ if (forced) {
+ CellRedraw.Set();
+ }
+
+ /*
+ ** The first order of business is to redraw all the underlying icons that are
+ ** flagged to be redrawn.
+ */
+ if (HidPage.Lock()) {
+ Redraw_Icons();
+
+ /*
+ ** Draw the infantry bodies in this special layer.
+ */
+// for (int index = 0; index < Anims.Count(); index++) {
+// AnimClass * anim = Anims.Ptr(index);
+// if (*anim >= ANIM_CORPSE1 && *anim <= ANIM_CORPSE3) {
+// anim->Render(forced);
+// }
+// }
+
+#ifdef SORTDRAW
+ Redraw_OIcons();
+#endif
+
+ HidPage.Unlock();
+ }
+
+#ifndef WIN32
+ /*
+ ** Once the icons are drawn, duplicate the bottom line of the screen into the phantom
+ ** area one line below the screen. This causes the predator effect to work on any
+ ** shape drawn at the bottom of the screen.
+ */
+ HidPage.Blit(HidPage, 0, HidPage.Get_Height()-1, 0, HidPage.Get_Height(), HidPage.Get_Width(), 1, false);
+#endif
+
+ if (HidPage.Lock()) {
+
+ /*
+ ** Draw the vortex effect over the terrain
+ */
+ ChronalVortex.Render();
+
+ /*
+ ** Redraw the game objects layer by layer. The layer drawing occurs on the ground layer
+ ** first and then followed by all the layers in increasing altitude.
+ */
+ BStart(BENCH_OBJECTS);
+ for (LayerType layer = LAYER_FIRST; layer < LAYER_COUNT; layer++) {
+ for (int index = 0; index < Layer[layer].Count(); index++) {
+ ObjectClass * ptr = Layer[layer][index];
+
+#ifdef SORTDRAW
+ /*
+ ** Techno objects are drawn as part of the cell redraw process since techno
+ ** objects in the ground layer are handled by the Occupier and Overlapper
+ ** pointer lists.
+ */
+ if (!Debug_Map && ptr->Is_Techno() && layer == LAYER_GROUND && ((TechnoClass*)ptr)->Visual_Character() == VISUAL_NORMAL) continue;
+#endif
+
+// if (ptr->What_Am_I() == RTTI_ANIM && *((AnimClass*)ptr) >= ANIM_CORPSE1 && *((AnimClass*)ptr) <= ANIM_CORPSE3) {
+// continue;
+// }
+ assert(ptr->IsActive);
+ ptr->Render(forced);
+ }
+ }
+ BEnd(BENCH_OBJECTS);
+
+ //ChronalVortex.Render();
+ /*
+ ** Finally, redraw the shadow overlay as necessary.
+ */
+ BStart(BENCH_SHROUD);
+ Redraw_Shadow();
+ BEnd(BENCH_SHROUD);
+ }
+ HidPage.Unlock();
+
+#ifdef SORTDRAW
+ for (int index = 0; index < Layer[LAYER_GROUND].Count(); index++) {
+ Layer[LAYER_GROUND][index]->IsToDisplay = false;
+ }
+#endif
+
+ /*
+ ** Draw the rubber band over the top of it all.
+ */
+ if (IsRubberBand) {
+ LogicPage->Draw_Rect(BandX+TacPixelX, BandY+TacPixelY, NewX+TacPixelX, NewY+TacPixelY, WHITE);
+ }
+
+ /*
+ ** Clear the redraw flags so that normal redraw flag setting can resume.
+ */
+ CellRedraw.Reset();
+
+#ifdef SCENARIO_EDITOR
+ /*
+ ** If we're placing an object (PendingObject is non-NULL), and that object
+ ** is NOT an icon, smudge, or overlay, draw it here.
+ ** Terrain, Buildings & Aircraft aren't drawn at the cell's center coord;
+ ** they're drawn at the upper left coord, so I have to AND the coord value
+ ** with 0xFF00FF00 to strip off the lepton coordinates, but leave the
+ ** cell coordinates.
+ */
+ if (Debug_Map && PendingObjectPtr) {
+ PendingObjectPtr->Coord = PendingObjectPtr->Class_Of().Coord_Fixup(Cell_Coord(ZoneCell + ZoneOffset));
+ PendingObjectPtr->Render(true);
+ }
+#endif
+ BEnd(BENCH_TACTICAL);
+ }
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Redraw_Icons -- Draws all terrain icons necessary. *
+ * *
+ * This routine will redraw all of the terrain icons that are flagged *
+ * to be redrawn. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none. *
+ * *
+ * HISTORY: *
+ * 02/14/1994 JLB : Created. *
+ * 05/01/1994 JLB : Converted to member function. *
+ * 06/20/1994 JLB : Uses cell drawing support function. *
+ * 12/06/1994 JLB : Scans tactical view in separate row/column loops *
+ * 12/24/1994 JLB : Uses the cell bit flag array to determine what to redraw. *
+ *=============================================================================================*/
+void DisplayClass::Redraw_Icons(void)
+{
+ IsShadowPresent = false;
+ for (int y = -Coord_YLepton(TacticalCoord); y <= TacLeptonHeight; y += CELL_LEPTON_H) {
+ for (int x = -Coord_XLepton(TacticalCoord); x <= TacLeptonWidth; x += CELL_LEPTON_W) {
+ COORDINATE coord = Coord_Add(TacticalCoord, XY_Coord(x, y));
+ CELL cell = Coord_Cell(coord);
+ coord = Coord_Whole(Cell_Coord(cell));
+
+ /*
+ ** Only cells flagged to be redraw are examined.
+ */
+ if (In_View(cell) && Is_Cell_Flagged(cell)) {
+ int xpixel;
+ int ypixel;
+
+ if (Coord_To_Pixel(coord, xpixel, ypixel)) {
+ CellClass * cellptr = &(*this)[coord];
+
+ /*
+ ** If there is a portion of the underlying icon that could be visible,
+ ** then draw it. Also draw the cell if the shroud is off.
+ */
+ if (cellptr->IsMapped || Debug_Unshroud) {
+ cellptr->Draw_It(xpixel, ypixel);
+ }
+
+ /*
+ ** If any cell is not fully mapped, then flag it so that the shadow drawing
+ ** process will occur. Only draw the shadow if Debug_Unshroud is false.
+ */
+ if (!cellptr->IsVisible && !Debug_Unshroud) {
+ IsShadowPresent = true;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+#ifdef SORTDRAW
+void DisplayClass::Redraw_OIcons(void)
+{
+ for (int y = -Coord_YLepton(TacticalCoord); y <= TacLeptonHeight; y += CELL_LEPTON_H) {
+ for (int x = -Coord_XLepton(TacticalCoord); x <= TacLeptonWidth; x += CELL_LEPTON_W) {
+ COORDINATE coord = Coord_Add(TacticalCoord, XY_Coord(x, y));
+ CELL cell = Coord_Cell(coord);
+ coord = Coord_Whole(Cell_Coord(cell));
+
+ /*
+ ** Only cells flagged to be redraw are examined.
+ */
+ if (In_View(cell) && Is_Cell_Flagged(cell)) {
+ int xpixel;
+ int ypixel;
+
+ if (Coord_To_Pixel(coord, xpixel, ypixel)) {
+ CellClass * cellptr = &(*this)[coord];
+
+ /*
+ ** If there is a portion of the underlying icon that could be visible,
+ ** then draw it. Also draw the cell if the shroud is off.
+ */
+ if (cellptr->IsMapped || Debug_Unshroud) {
+ cellptr->Draw_It(xpixel, ypixel, true);
+ }
+ }
+ }
+ }
+ }
+}
+#endif
+
+
+/***********************************************************************************************
+ * DisplayClass::Redraw_Shadow -- Draw the shadow overlay. *
+ * *
+ * This routine is called after all other tactical map rendering takes place. It draws *
+ * the shadow map over the tactical map. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 01/01/1995 JLB : Created. *
+ * 08/06/1995 JLB : Clips the fill rect if necessary. *
+ *=============================================================================================*/
+void DisplayClass::Redraw_Shadow(void)
+{
+ if (IsShadowPresent) {
+ for (int y = -Coord_YLepton(TacticalCoord); y <= TacLeptonHeight; y += CELL_LEPTON_H) {
+ for (int x = -Coord_XLepton(TacticalCoord); x <= TacLeptonWidth; x += CELL_LEPTON_W) {
+ COORDINATE coord = Coord_Add(TacticalCoord, XY_Coord(x, y));
+ CELL cell = Coord_Cell(coord);
+ coord = Coord_Whole(Cell_Coord(cell));
+
+ /*
+ ** Only cells flagged to be redrawn are examined.
+ */
+ if (In_View(cell) && Is_Cell_Flagged(cell)) {
+ int xpixel;
+ int ypixel;
+
+ if (Coord_To_Pixel(coord, xpixel, ypixel)) {
+ CellClass * cellptr = &(*this)[coord];
+ if (cellptr->IsVisible) continue;
+ int shadow = -2;
+ if (cellptr->IsMapped) {
+ shadow = Cell_Shadow(cell);
+ }
+ if (shadow >= 0) {
+ CC_Draw_Shape(ShadowShapes, shadow, xpixel, ypixel, WINDOW_TACTICAL, SHAPE_GHOST, NULL, ShadowTrans);
+ } else {
+ if (shadow != -1) {
+ int ww = CELL_PIXEL_W;
+ int hh = CELL_PIXEL_H;
+
+ if (Clip_Rect(&xpixel, &ypixel, &ww, &hh, Lepton_To_Pixel(TacLeptonWidth), Lepton_To_Pixel(TacLeptonHeight)) >= 0) {
+ LogicPage->Fill_Rect(TacPixelX+xpixel, TacPixelY+ypixel, TacPixelX+xpixel+ww-1, TacPixelY+ypixel+hh-1, BLACK);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Next_Object -- Searches for next object on display. *
+ * *
+ * This utility routine is used to find the "next" object from the object specified. This *
+ * is typically used when is pressed and the current object shifts. *
+ * *
+ * INPUT: object -- The current object to base the "next" calculation off of. *
+ * *
+ * OUTPUT: Returns with a pointer to the next object. If there is no objects available, *
+ * then NULL is returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/20/1994 JLB : Created. *
+ *=============================================================================================*/
+ObjectClass * DisplayClass::Next_Object(ObjectClass * object) const
+{
+ ObjectClass * firstobj = NULL;
+ bool foundmatch = false;
+
+ if (object == NULL) {
+ foundmatch = true;
+ }
+ for (unsigned uindex = 0; uindex < Layer[LAYER_GROUND].Count(); uindex++) {
+ ObjectClass * obj = Layer[LAYER_GROUND][uindex];
+
+ /*
+ ** Verify that the object can be selected by and is owned by the player.
+ */
+ if (obj != NULL && obj->Is_Players_Army()) {
+ if (firstobj == NULL) firstobj = obj;
+ if (foundmatch) return(obj);
+ if (object == obj) foundmatch = true;
+ }
+ }
+ return(firstobj);
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Prev_Object -- Searches for the previous object on the map. *
+ * *
+ * This routine will search for the previous object. Previous is defined as the one listed *
+ * before the specified object in the ground layer. If there is no specified object, then *
+ * the last object in the ground layer is returned. *
+ * *
+ * INPUT: object -- Pointer to the object that "previous" is to be defined from. *
+ * *
+ * OUTPUT: Returns with a pointer to the object previous to the specified one. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 08/24/1995 JLB : Created. *
+ *=============================================================================================*/
+ObjectClass * DisplayClass::Prev_Object(ObjectClass * object) const
+{
+ ObjectClass * firstobj = NULL;
+ bool foundmatch = false;
+
+ if (object == NULL) {
+ foundmatch = true;
+ }
+ for (int uindex = Layer[LAYER_GROUND].Count()-1; uindex >= 0; uindex--) {
+ ObjectClass * obj = Layer[LAYER_GROUND][uindex];
+
+ /*
+ ** Verify that the object can be selected by and is owned by the player.
+ */
+ if (obj != NULL && obj->Is_Players_Army()) {
+ if (firstobj == NULL) firstobj = obj;
+ if (foundmatch) return(obj);
+ if (object == obj) foundmatch = true;
+ }
+ }
+
+ return(firstobj);
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Pixel_To_Coord -- converts screen coord to COORDINATE *
+ * *
+ * INPUT: *
+ * x,y pixel coordinates to convert *
+ * *
+ * OUTPUT: *
+ * COORDINATE of pixel *
+ * *
+ * WARNINGS: *
+ * none. *
+ * *
+ * HISTORY: *
+ * 11/09/1994 BR : Created. *
+ * 12/06/1994 JLB : Uses map dimension variables in display class. *
+ * 12/10/1994 JLB : Uses union to speed building coordinate value. *
+ *=============================================================================================*/
+COORDINATE DisplayClass::Pixel_To_Coord(int x, int y) const
+{
+ /*
+ ** Normalize the pixel coordinates to be relative to the upper left corner
+ ** of the tactical map. The coordinates are expressed in leptons.
+ */
+ x -= TacPixelX;
+ x = Pixel_To_Lepton(x);
+ y -= TacPixelY;
+ y = Pixel_To_Lepton(y);
+
+ /*
+ ** If pixel coordinate is over the tactical map, then translate it into a coordinate
+ ** value. If not, then just return with NULL.
+ */
+ 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. *
+ * *
+ * waypoint -- Closest waypoint to use for finding appropriate map edge. *
+ * *
+ * cell -- Cell to find closest edge to if waypoint not specified. *
+ * *
+ * loco -- The locomotion of the reinforcements that are trying to enter. *
+ * *
+ * zonecheck -- Is zone checking required? *
+ * *
+ * mzone -- The movement zone type to check against (only if zone checking). *
+ * *
+ * OUTPUT: Returns with the calculated cell. If 0, then this indicates *
+ * that no legal cell was found. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 10/07/1992 JLB : Created. *
+ * 04/11/1994 JLB : Revamped. *
+ * 05/18/1994 JLB : Converted to member function. *
+ * 12/18/1995 JLB : Handles edge preference scan. *
+ * 06/24/1996 JLB : Removed Dune II legacy code. *
+ * 06/25/1996 JLB : Rewrote and greatly simplified. *
+ * 10/05/1996 JLB : Checks for zone and crushable status. *
+ *=============================================================================================*/
+CELL DisplayClass::Calculated_Cell(SourceType dir, WAYPOINT waypoint, CELL cell, SpeedType loco, bool zonecheck, MZoneType mzone) const
+{
+ bool vert = false;
+ bool horz = false;
+ int x = 0;
+ int y = 0;
+ CELL punt = 0; // If all else fails, return this cell location.
+ int zone = -1; // Tentative zone for legality checking.
+
+ /*
+ ** Waypoint edge detection for ground based reinforcements that have a waypoint origin are
+ ** determined by finding the closest map edge to the waypoint. Reinforcement location
+ ** scanning starts from that position.
+ */
+ CELL trycell = -1;
+ if (waypoint != -1) {
+ trycell = Scen.Waypoint[waypoint];
+ }
+ if (trycell == -1) {
+ trycell = cell;
+ }
+
+ /*
+ ** If zone checking is requested, then find the correct zone to use.
+ */
+ if (zonecheck && trycell != -1) {
+ zone = (*this)[trycell].Zones[mzone];
+ }
+
+ /*
+ ** If the cell or waypoint specified as been detected as legal, then set up the map edge
+ ** scanning values accordingly.
+ */
+ if (trycell != -1) {
+ x = Cell_X(trycell) - MapCellX;
+ x = min(x, (-Cell_X(trycell) + (MapCellX+MapCellWidth)));
+
+ y = Cell_Y(trycell) - MapCellY;
+ y = min(y, (-Cell_Y(trycell) + (MapCellY+MapCellHeight)));
+
+ if (x < y) {
+ vert = true;
+ horz = false;
+ if ((Cell_X(trycell)-MapCellX) < MapCellWidth/2) {
+ x = -1;
+ } else {
+ x = MapCellWidth;
+ }
+ y = Cell_Y(trycell) - MapCellY;
+
+ } else {
+
+ vert = false;
+ horz = true;
+ if ((Cell_Y(trycell)-MapCellY) < MapCellHeight/2) {
+ y = -1;
+ } else {
+ y = MapCellHeight;
+ }
+ x = Cell_X(trycell) - MapCellX;
+ }
+ }
+
+ /*
+ ** If no map edge can be inferred from the waypoint, then go with the
+ ** map edge specified by the edge parameter.
+ */
+ if (!vert && !horz) {
+ switch (dir) {
+ default:
+ case SOURCE_NORTH:
+ horz = true;
+ y = -1;
+ x = Random_Pick(0, MapCellWidth-1);
+ break;
+
+ case SOURCE_SOUTH:
+ horz = true;
+ y = MapCellHeight;
+ x = Random_Pick(0, MapCellWidth-1);
+ break;
+
+ case SOURCE_EAST:
+ vert = true;
+ x = MapCellWidth;
+ y = Random_Pick(0, MapCellHeight-1);
+ break;
+
+ case SOURCE_WEST:
+ vert = true;
+ x = -1;
+ y = Random_Pick(0, MapCellHeight-1);
+ break;
+ }
+ }
+
+ /*
+ ** Determine the default reinforcement cell if all else fails.
+ */
+ punt = XY_Cell(x + MapCellX, y + MapCellY);
+
+ /*
+ ** Scan through the vertical and horizontal edges of the map looking for
+ ** a relatively clear cell for object placement. The cell scanned is
+ ** from the edge position specified by the X and Y variables.
+ */
+ if (vert) {
+ int modifier = (x > MapCellX) ? -1 : 1;
+
+ for (int index = 0; index < MapCellHeight; index++) {
+ CELL trycell = XY_Cell(x + MapCellX, ((y + index) % MapCellHeight) + MapCellY);
+
+ if (Good_Reinforcement_Cell(trycell, trycell+modifier, loco, zone, mzone)) {
+ return(trycell);
+ }
+ }
+ }
+
+ if (horz) {
+ int modifier = (y > MapCellY) ? -MAP_CELL_W : MAP_CELL_W;
+
+ for (int index = 0; index < MapCellWidth; index++) {
+ CELL trycell = XY_Cell(((x + index) % MapCellWidth) + MapCellX, y + MapCellY);
+
+ if (Good_Reinforcement_Cell(trycell, trycell+modifier, loco, zone, mzone)) {
+ return(trycell);
+ }
+ }
+ }
+
+ /*
+ ** If there was no success in finding a suitable reinforcement edge cell, then return
+ ** with the default 'punt' cell location.
+ */
+ return(punt);
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Good_Reinforcement_Cell -- Checks cell for renforcement legality. *
+ * *
+ * This routine will check the secified cell (given the specified conditions) and determine *
+ * if that is a good cell for reinforcement purposes. It checks for passability of the cell *
+ * as well as zone and whether blocking walls can be crushed. *
+ * *
+ * INPUT: outcell -- The cell that is just outside the edge of the map. *
+ * *
+ * incell -- The cell that is just inside the edge of the map. *
+ * *
+ * loco -- The locomotion type of the reinforcement. *
+ * *
+ * zone -- The zone that the eventual movement destination lies. A reinforcement *
+ * edge must fall within the same zone. *
+ * *
+ * mzone -- The zone check type to check against (if zone checking required) *
+ * *
+ * OUTPUT: bool; Is the specified cell good for reinforcement purposes? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 10/05/1996 JLB : Created. *
+ *=============================================================================================*/
+bool DisplayClass::Good_Reinforcement_Cell(CELL outcell, CELL incell, SpeedType loco, int zone, MZoneType mzone) const
+{
+ /*
+ ** If the map edge location is not clear for object placement, then this is not
+ ** a good cell for reinforcement purposes.
+ */
+ if (!(*this)[outcell].Is_Clear_To_Move(loco, false, false)) {
+ return(false);
+ }
+
+ /*
+ ** If it looks like the on-map cell cannot be driven on to, then return with
+ ** the failure code.
+ */
+ if (!(*this)[incell].Is_Clear_To_Move(loco, false, false, zone, mzone)) {
+ return(false);
+ }
+
+ /*
+ ** If the reinforcement cell is already occupied, then return a failure code.
+ */
+ if ((*this)[outcell].Cell_Techno() != NULL) {
+ return(false);
+ }
+ if ((*this)[incell].Cell_Techno() != NULL) return(false);
+
+ /*
+ ** All tests have passed, return with success code.
+ */
+//Mono_Printf("<%04X>\n", incell);Keyboard->Get();
+ return(true);
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Select_These -- All selectable objects in region are selected. *
+ * *
+ * Use this routine to simultaneously select all objects within the coordinate region *
+ * specified. This routine is used by the multi-select rubber band handler. *
+ * *
+ * INPUT: coord1 -- Coordinate of one corner of the selection region. *
+ * *
+ * coord2 -- The opposite corner of the selection region. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 01/19/1995 JLB : Created. *
+ * 04/25/1995 JLB : Limited to non-building type. *
+ * 03/06/1996 JLB : Allows selection of aircraft with bounding box. *
+ *=============================================================================================*/
+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 bounding box.
+ */
+ HouseClass * hptr = HouseClass::As_Pointer(obj->Owner());
+ if ( (hptr != NULL && hptr->IsPlayerControl) &&
+ obj->Class_Of().IsSelectable &&
+ obj->What_Am_I() != RTTI_BUILDING &&
+ x >= x1 && x <= x2 && y >= y1 && y <= y2) {
+ if (obj->Select()) {
+ AllowVoice = false;
+ }
+ }
+ }
+
+ /*
+ ** Select any aircraft with the bounding box.
+ */
+ for (int air_index = 0; air_index < Aircraft.Count(); air_index++) {
+ AircraftClass * aircraft = Aircraft.Ptr(air_index);
+ COORDINATE ocoord = aircraft->Center_Coord();
+ int x = Coord_X(ocoord);
+ int y = Coord_Y(ocoord);
+
+ /*
+ ** Only try to select objects that are owned by the player, are allowed to be
+ ** selected, and are within the bounding box.
+ */
+ if ( aircraft->House->IsPlayerControl &&
+ aircraft->Class->IsSelectable &&
+ !aircraft->IsSelected &&
+ x >= x1 && x <= x2 && y >= y1 && y <= y2) {
+
+ if (aircraft->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();
+ }
+
+ /*
+ ** Stretching the rubber band requires all objects to be redrawn.
+ */
+ for (int index = 0; index < Layer[LAYER_TOP].Count(); index++) {
+ Layer[LAYER_TOP][index]->Mark(MARK_CHANGE);
+ }
+ for (index = 0; index < Layer[LAYER_AIR].Count(); index++) {
+ Layer[LAYER_AIR][index]->Mark(MARK_CHANGE);
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::TacticalClass::Action -- Processes input for the tactical map. *
+ * *
+ * This routine handles the input directed at the tactical map. Since input, in this *
+ * regard, includes even the presence of the mouse over the tactical map, this routine *
+ * is called nearly every game frame. It handles adjusting the mouse shape as well as *
+ * giving orders to units. *
+ * *
+ * INPUT: flags -- The gadget event flags that triggered the call to this function. *
+ * *
+ * key -- A reference to the keyboard event (if any). *
+ * *
+ * OUTPUT: bool; Should processing be aborted on any succeeding buttons in the chain? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 02/17/1995 JLB : Created. *
+ *=============================================================================================*/
+int DisplayClass::TacticalClass::Action(unsigned flags, KeyNumType & key)
+{
+ int x,y; // Sub cell pixel coordinates.
+ bool shadow;
+ ObjectClass * object = 0;
+ ActionType action = ACTION_NONE; // Action possible with currently selected object.
+
+ /*
+ ** Set some working variables that depend on the mouse position. For the press
+ ** or release event, special mouse queuing storage variables are used. Other
+ ** events must use the current mouse position globals.
+ */
+ if (flags & (LEFTPRESS|LEFTRELEASE|RIGHTPRESS|RIGHTRELEASE)) {
+ x = Keyboard->MouseQX;
+ y = Keyboard->MouseQY;
+ } else {
+ x = Get_Mouse_X();
+ y = Get_Mouse_Y();
+ }
+ bool edge = (y == 0 || x == 0 || x == SeenBuff.Get_Width()-1 || y == SeenBuff.Get_Height()-1);
+ COORDINATE coord = Map.Pixel_To_Coord(x, y);
+ CELL cell = Coord_Cell(coord);
+ if (coord) {
+
+ shadow = (!Map[cell].IsMapped && !Debug_Unshroud);
+ x -= Map.TacPixelX;
+ y -= Map.TacPixelY;
+
+ /*
+ ** Cause any displayed cursor to move along with the mouse cursor.
+ */
+ if (cell != Map.ZoneCell) {
+ Map.Set_Cursor_Pos(cell);
+ }
+
+ /*
+ ** Determine the object that the mouse is currently over.
+ */
+ if (!shadow) {
+ object = Map.Close_Object(coord);
+
+ /*
+ ** Special case check to ignore cloaked object if not owned by the player.
+ */
+ if (object != NULL && object->Is_Techno() && !((TechnoClass *)object)->IsOwnedByPlayer && (((TechnoClass *)object)->Cloak == CLOAKED || ((TechnoClass *)object)->Techno_Type_Class()->IsInvisible)) {
+ object = NULL;
+ }
+ }
+
+ /*
+ ** If there is a currently selected object, then the action to perform if
+ ** the left mouse button were clicked must be determined.
+ */
+ if (CurrentObject.Count()) {
+ if (object != NULL) {
+ action = 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_NUCLEAR_BOMB) {
+ action = ACTION_NUKE_BOMB;
+ }
+
+ if (Map.IsTargettingMode == SPC_PARA_BOMB) {
+ action = ACTION_PARA_BOMB;
+ }
+
+ if (Map.IsTargettingMode == SPC_PARA_INFANTRY) {
+ action = ACTION_PARA_INFANTRY;
+ }
+
+ if (Map.IsTargettingMode == SPC_SPY_MISSION) {
+ action = ACTION_SPY_MISSION;
+ }
+
+ if (Map.IsTargettingMode == SPC_IRON_CURTAIN) {
+ action = ACTION_IRON_CURTAIN;
+ }
+
+ if (Map.IsTargettingMode == SPC_CHRONOSPHERE) {
+ action = ACTION_CHRONOSPHERE;
+ }
+
+ if (Map.IsTargettingMode == SPC_CHRONO2) {
+ action = ACTION_CHRONO2;
+ if (shadow) action = ACTION_NOMOVE;
+ ObjectClass const * tobject = As_Object(PlayerPtr->UnitToTeleport);
+
+ /*
+ ** Determine if the object can be teleported to the destination cell.
+ */
+ if (tobject != NULL && tobject->Is_Techno()) {
+ TechnoClass const * uobject = (TechnoClass const *)tobject;
+ if (!uobject->Can_Teleport_Here(cell)) {
+// if (((UnitClass *)As_Object(PlayerPtr->UnitToTeleport))->Can_Enter_Cell(cell, FACING_NONE) != MOVE_OK) {
+ action = ACTION_NOMOVE;
+ }
+
+ }
+#ifdef FIXIT_CSII // checked - ajw 9/28/98
+ else { // If the object is no longer valid, cancel targetting mode.
+ action = ACTION_NOMOVE;
+ Map.IsTargettingMode = SPC_NONE;
+ }
+#endif
+ }
+
+ if (Map.PendingObject) {
+ action = ACTION_NONE;
+ }
+ }
+
+ /*
+ ** Move any cursor displayed.
+ */
+ if (cell != Map.ZoneCell) {
+ Map.Set_Cursor_Pos(cell);
+ }
+
+ /*
+ ** A right mouse button press cancels the current action or selection.
+ */
+ if (flags & RIGHTPRESS) {
+ Map.Mouse_Right_Press();
+ }
+
+ /*
+ ** Make sure that if the mouse button has been released and the map doesn't know about it,
+ ** then it must be informed. Do this by faking a mouse release event.
+ */
+ if ((flags & LEFTUP) && Map.IsRubberBand) {
+ flags |= LEFTRELEASE;
+ }
+
+ /*
+ ** When the mouse buttons aren't pressed, only the mouse cursor shape is processed.
+ ** The shape changes depending on what object the mouse is currently over and what
+ ** object is currently selected.
+ */
+ if (!edge) {
+ if (flags & LEFTUP) {
+ Map.Mouse_Left_Up(cell, shadow, object, action);
+ }
+ }
+
+ /*
+ ** Normal actions occur when the mouse button is released. The press event is
+ ** intercepted and possible rubber-band mode is flagged.
+ */
+ if (flags & LEFTRELEASE) {
+ Map.Mouse_Left_Release(cell, x, y, object, action);
+ }
+
+ /*
+ ** When the mouse is first pressed on the map, then record the mouse
+ ** position so that a proper check before going into rubber band
+ ** mode can be made. Rubber band mode starts when the mouse is
+ ** held down and moved a certain minimum distance.
+ */
+ if (!edge && (flags & LEFTPRESS)) {
+ Map.Mouse_Left_Up(cell, shadow, object, action);
+ Map.Mouse_Left_Press(x, y);
+ }
+
+ /*
+ ** While the mouse is being held down, determine if rubber band mode should
+ ** start. If rubber band mode is already active, then update the size
+ ** and flag the map to redraw it.
+ */
+ if (flags & LEFTHELD) {
+ Map.Mouse_Left_Held(x, y);
+ }
+ }
+
+ return(GadgetClass::Action(0, key));
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Mouse_Right_Press -- Handles the right mouse button press. *
+ * *
+ * This routine is called when the right mouse button is pressed. This action is supposed *
+ * to cancel whatever mode or process is active. If there is nothing to cancel, then it *
+ * will default to unselecting any units that might be currently selected. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 02/24/1995 JLB : Created. *
+ *=============================================================================================*/
+void DisplayClass::Mouse_Right_Press(void)
+{
+ if (PendingObjectPtr && PendingObjectPtr->Is_Techno()) {
+ //PendingObjectPtr->Transmit_Message(RADIO_OVER_OUT);
+ PendingObjectPtr = 0;
+ PendingObject = 0;
+ PendingHouse = HOUSE_NONE;
+ Set_Cursor_Shape(0);
+ } else {
+ if (IsRepairMode) {
+ IsRepairMode = false;
+ } else {
+ if (IsSellMode) {
+ IsSellMode = false;
+ } else {
+ if (IsTargettingMode != SPC_NONE) {
+ IsTargettingMode = SPC_NONE;
+ } else {
+ Unselect_All();
+ }
+ }
+ }
+ }
+
+ // If it breaks... call 228.
+ Set_Default_Mouse(MOUSE_NORMAL, Map.IsSmall);
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Mouse_Left_Up -- Handles the left mouse "cruising" over the map. *
+ * *
+ * This routine is called continuously while the mouse is over the tactical map but there *
+ * are no mouse buttons pressed. Typically, this adjusts the mouse shape and the pop-up *
+ * help text. *
+ * *
+ * INPUT: shadow -- Is the mouse hovering over shadowed terrain? *
+ * *
+ * object -- Pointer to the object that the mouse is currently over (may be NULL). *
+ * *
+ * action -- This is the action that the currently selected object (if any) will *
+ * perform if the left mouse button were clicked at this location. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 02/24/1995 JLB : Created. *
+ * 07/05/1995 JLB : Removed pop up help text for shadow and terrain after #3. *
+ *=============================================================================================*/
+void DisplayClass::Mouse_Left_Up(CELL cell, bool shadow, ObjectClass * object, ActionType action, bool wsmall)
+{
+ IsTentative = false;
+
+ TARGET target = TARGET_NONE;
+ if (object != NULL) {
+ target = object->As_Target();
+ } else {
+ if (cell != -1) {
+ target = As_Target(cell);
+ }
+ }
+
+ /*
+ ** Don't allow selection of an object that is located in shadowed terrain.
+ ** In fact, just show the normal move cursor in order to keep the shadowed
+ ** terrain a mystery.
+ */
+ if (shadow) {
+ switch (action) {
+ case ACTION_NO_DEPLOY:
+ Set_Default_Mouse(MOUSE_NO_DEPLOY, wsmall);
+ break;
+
+ case ACTION_NO_ENTER:
+ Set_Default_Mouse(MOUSE_NO_ENTER, wsmall);
+ break;
+
+ case ACTION_NO_GREPAIR:
+ Set_Default_Mouse(MOUSE_NO_GREPAIR, wsmall);
+ break;
+
+ case ACTION_DAMAGE:
+ Set_Default_Mouse(MOUSE_NORMAL, wsmall);
+ break;
+
+ case ACTION_GREPAIR:
+ Set_Default_Mouse(MOUSE_NORMAL, wsmall);
+ break;
+
+ case ACTION_GUARD_AREA:
+ Set_Default_Mouse(MOUSE_AREA_GUARD, wsmall);
+ break;
+
+ case ACTION_NONE:
+ Set_Default_Mouse(MOUSE_NORMAL, wsmall);
+ break;
+
+ case ACTION_NO_SELL:
+ case ACTION_SELL:
+ case ACTION_SELL_UNIT:
+ Set_Default_Mouse(MOUSE_NO_SELL_BACK, wsmall);
+ break;
+
+ case ACTION_NO_REPAIR:
+ case ACTION_REPAIR:
+ Set_Default_Mouse(MOUSE_NO_REPAIR, wsmall);
+ break;
+
+ case ACTION_NUKE_BOMB:
+ Set_Default_Mouse(MOUSE_NUCLEAR_BOMB, wsmall);
+ break;
+
+ case ACTION_AIR_STRIKE:
+ case ACTION_PARA_BOMB:
+ case ACTION_PARA_INFANTRY:
+ case ACTION_SPY_MISSION:
+ case ACTION_IRON_CURTAIN:
+ Set_Default_Mouse(MOUSE_AIR_STRIKE, wsmall);
+ break;
+
+ case ACTION_CHRONOSPHERE:
+ Set_Default_Mouse(MOUSE_CHRONO_SELECT, wsmall);
+ break;
+
+ case ACTION_CHRONO2:
+ Set_Default_Mouse(MOUSE_CHRONO_DEST, wsmall);
+ break;
+
+ case ACTION_HEAL:
+ Set_Default_Mouse(MOUSE_HEAL, wsmall);
+ break;
+
+ case ACTION_NOMOVE:
+ if (CurrentObject.Count() && CurrentObject[0]->What_Am_I() == RTTI_AIRCRAFT) {
+ Set_Default_Mouse(MOUSE_NO_MOVE, wsmall);
+ break;
+ }
+ // Fall into next case for non aircraft object types.
+
+ default:
+ Set_Default_Mouse(MOUSE_CAN_MOVE, wsmall);
+ break;
+
+ }
+ } else {
+
+ /*
+ ** Change the mouse shape according to the default action that will occur
+ ** if the mouse button were clicked at this location.
+ */
+ switch (action) {
+ case ACTION_NO_DEPLOY:
+ Set_Default_Mouse(MOUSE_NO_DEPLOY, wsmall);
+ break;
+
+ case ACTION_NO_ENTER:
+ Set_Default_Mouse(MOUSE_NO_ENTER, wsmall);
+ break;
+
+ case ACTION_NO_GREPAIR:
+ Set_Default_Mouse(MOUSE_NO_GREPAIR, wsmall);
+ break;
+
+ case ACTION_DAMAGE:
+ Set_Default_Mouse(MOUSE_DAMAGE, wsmall);
+ break;
+
+ case ACTION_GREPAIR:
+ Set_Default_Mouse(MOUSE_GREPAIR, wsmall);
+ break;
+
+ case ACTION_TOGGLE_SELECT:
+ case ACTION_SELECT:
+ Set_Default_Mouse(MOUSE_CAN_SELECT, wsmall);
+ break;
+
+ case ACTION_MOVE:
+ Set_Default_Mouse(MOUSE_CAN_MOVE, wsmall);
+ break;
+
+ case ACTION_GUARD_AREA:
+ Set_Default_Mouse(MOUSE_AREA_GUARD, wsmall);
+ break;
+
+ case ACTION_ATTACK:
+ if (Target_Legal(target) && CurrentObject.Count() == 1 && CurrentObject[0]->Is_Techno() && ((TechnoClass *)CurrentObject[0])->In_Range(target, 0)) {
+ Set_Default_Mouse(MOUSE_STAY_ATTACK, wsmall);
+ break;
+ }
+ // fall into next case.
+
+ case ACTION_HARVEST:
+ Set_Default_Mouse(MOUSE_CAN_ATTACK, wsmall);
+ break;
+
+ case ACTION_SABOTAGE:
+ Set_Default_Mouse(MOUSE_DEMOLITIONS, wsmall);
+ break;
+
+ case ACTION_ENTER:
+ case ACTION_CAPTURE:
+ Set_Default_Mouse(MOUSE_ENTER, wsmall);
+ break;
+
+ case ACTION_NOMOVE:
+ Set_Default_Mouse(MOUSE_NO_MOVE, wsmall);
+ break;
+
+ case ACTION_NO_SELL:
+ Set_Default_Mouse(MOUSE_NO_SELL_BACK, wsmall);
+ break;
+
+ case ACTION_NO_REPAIR:
+ Set_Default_Mouse(MOUSE_NO_REPAIR, wsmall);
+ break;
+
+ case ACTION_SELF:
+ Set_Default_Mouse(MOUSE_DEPLOY, wsmall);
+ break;
+
+ case ACTION_REPAIR:
+ Set_Default_Mouse(MOUSE_REPAIR, wsmall);
+ break;
+
+ case ACTION_SELL_UNIT:
+ Set_Default_Mouse(MOUSE_SELL_UNIT, wsmall);
+ break;
+
+ case ACTION_SELL:
+ Set_Default_Mouse(MOUSE_SELL_BACK, wsmall);
+ break;
+
+ case ACTION_NUKE_BOMB:
+ Set_Default_Mouse(MOUSE_NUCLEAR_BOMB, wsmall);
+ break;
+
+ case ACTION_AIR_STRIKE:
+ case ACTION_PARA_BOMB:
+ case ACTION_PARA_INFANTRY:
+ case ACTION_SPY_MISSION:
+ case ACTION_IRON_CURTAIN:
+ Set_Default_Mouse(MOUSE_AIR_STRIKE, wsmall);
+ break;
+
+ case ACTION_CHRONOSPHERE:
+ Set_Default_Mouse(MOUSE_CHRONO_SELECT, wsmall);
+ break;
+
+ case ACTION_CHRONO2:
+ Set_Default_Mouse(MOUSE_CHRONO_DEST, wsmall);
+ break;
+
+ case ACTION_HEAL:
+ Set_Default_Mouse(MOUSE_HEAL, wsmall);
+ break;
+
+ default:
+ Set_Default_Mouse(MOUSE_NORMAL, wsmall);
+ break;
+ }
+ }
+
+ /*
+ ** Never display help text if the mouse is held over the radar map.
+ */
+ if (wsmall) {
+ return;
+ }
+
+ /*
+ ** Give a generic help message when over shadow terrain.
+ */
+ if (shadow) {
+// if (Scen.Scenario < 4) {
+ Help_Text(TXT_SHADOW);
+// } else {
+// Help_Text(TXT_NONE);
+// }
+ } else {
+
+ /*
+ ** If the mouse is held over objects on the map, then help text may
+ ** pop up that tells what the object is. This call informs the help
+ ** system of the text name for the object under the mouse.
+ */
+ if (object != NULL) {
+ int text;
+ int color = LTGREY;
+
+ /*
+ ** Fetch the appropriate background color for help text.
+ */
+ if (PlayerPtr->Is_Ally(object)) {
+ color = GREEN;
+ } else {
+ if (object->Owner() == HOUSE_NONE || object->Owner() == HOUSE_NEUTRAL) {
+ color = LTGREY;
+ } else {
+ color = PINK;
+ }
+ }
+
+ /*
+ ** Fetch the name of the object. If it is an enemy object, then
+ ** the exact identity is glossed over with a generic text.
+ */
+ text = object->Full_Name();
+ if (object->Is_Techno() && !((TechnoTypeClass const &)object->Class_Of()).IsNominal) {
+
+ if (!((TechnoClass *)object)->House->Is_Ally(PlayerPtr)) {
+// if (!PlayerPtr->Is_Ally(object)) {
+ switch (object->What_Am_I()) {
+ case RTTI_INFANTRY:
+ text = TXT_ENEMY_SOLDIER;
+ break;
+
+ case RTTI_UNIT:
+ text = TXT_ENEMY_VEHICLE;
+ break;
+
+ case RTTI_BUILDING:
+ text = TXT_ENEMY_STRUCTURE;
+ break;
+ }
+ }
+ }
+
+ if (/*Scen.Scenario > 3 ||*/ object->What_Am_I() != RTTI_TERRAIN) {
+ Help_Text(text, -1, -1, color);
+ } else {
+ Help_Text(TXT_NONE);
+ }
+ } else {
+ if ((*this)[cell].Land_Type() == LAND_TIBERIUM) {
+ Help_Text(TXT_MINERALS);
+ } else {
+ Help_Text(TXT_NONE);
+ }
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Mouse_Left_Release -- Handles the left mouse button release. *
+ * *
+ * This routine is called when the left mouse button is released over the tactical map. *
+ * The release event is the workhorse of the game. Most actions occur at the moment of *
+ * mouse release. *
+ * *
+ * INPUT: cell -- The cell that the mouse is over. *
+ * *
+ * x,y -- The mouse pixel coordinate. *
+ * *
+ * object -- Pointer to the object that the mouse is over. *
+ * *
+ * action -- The action that the currently selected object (if any) will *
+ * perform. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 02/24/1995 JLB : Created. *
+ * 03/27/1995 JLB : Handles sell and repair actions. *
+ *=============================================================================================*/
+void DisplayClass::Mouse_Left_Release(CELL cell, int x, int y, ObjectClass * object, ActionType action, bool wsmall)
+{
+ if (PendingObjectPtr) {
+ /*
+ ** Try to place the pending object onto the map.
+ */
+ if (ProximityCheck) {
+ OutList.Add(EventClass(EventClass::PLACE, PendingObjectPtr->What_Am_I(), cell + ZoneOffset));
+ } else {
+ Speak(VOX_DEPLOY);
+ }
+
+ } else {
+
+ if (IsRubberBand) {
+ Refresh_Band();
+ Select_These(XYP_Coord(BandX, BandY), XYP_Coord(x, y));
+
+ Set_Default_Mouse(MOUSE_NORMAL, wsmall);
+ IsRubberBand = false;
+ IsTentative = false;
+ Map.DisplayClass::IsToRedraw = true;
+ Map.Flag_To_Redraw(false);
+
+ } else {
+
+ /*
+ ** Toggle the select state of the object.
+ */
+ if (action == ACTION_TOGGLE_SELECT) {
+ if (!object || !CurrentObject.Count() || 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, wsmall);
+ }
+
+ /*
+ ** 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;
+ FormMove = false;
+ FormSpeed = SPEED_WHEEL;
+ FormMaxSpeed = MPH_LIGHT_SPEED;
+
+ if ( (action == ACTION_MOVE || action == ACTION_NOMOVE) && CurrentObject.Count()) {
+
+ /*
+ ** Scan all units. If any are selected that shouldn't be, or aren't
+ ** selected but should be, then this is not a formation move.
+ */
+ int group = 254; // init to invalid group #
+
+ if (CurrentObject[0]->Is_Foot()) {
+ group = ((FootClass *)CurrentObject[0])->Group;
+ }
+
+ /*
+ ** Presume this is a formation move unless something is detected
+ ** that will prevent it.
+ */
+ FormMove = true;
+
+ /*
+ ** First scan through all the selected units to make sure that they
+ ** are all of the same team and have been assigned a particular formation
+ */
+ for (int index = 0; index < CurrentObject.Count(); index++) {
+ ObjectClass const * tobject = CurrentObject[index];
+
+ /*
+ ** Only moveable (i.e., FootClass) objects can ever be in a formation
+ ** so if a selected object isn't of a FootClass type then it can't be
+ ** a formation move.
+ */
+ if (tobject->Is_Foot() == false) {
+ FormMove = false;
+ break;
+ }
+
+ /*
+ ** If the object is not part of the same team as the rest of the
+ ** selected group, or it just plain has never been assigned a
+ ** formation offset, then it can't be a formation move.
+ */
+ FootClass const * foot = (FootClass *)tobject;
+ if (foot->Group != group || foot->XFormOffset == 0x80000000) {
+ FormMove = false;
+ break;
+ }
+
+ /*
+ ** Determine the formation speed on the presumption that this
+ ** will turn out to be a formation move.
+ */
+ FormMaxSpeed = foot->Techno_Type_Class()->MaxSpeed;
+ FormSpeed = foot->Techno_Type_Class()->Speed;
+ }
+
+ /*
+ ** Loop through all objects (that can theoretically be part of a team) and
+ ** if there are any that are part of the currently selected team, but
+ ** are not currently selected themselves, then this will force this move
+ ** to NOT be a formation move.
+ */
+ if (FormMove) {
+ for (index = 0; index < ::Logic.Count(); index++) {
+ ObjectClass const * obj = ::Logic[index];
+
+ /*
+ ** If the object is selected, then it has already been scanned
+ ** by the previous loop.
+ */
+ if (obj->IsSelected) continue;
+
+ /*
+ ** Only consider footclass objects.
+ */
+ if (!obj->Is_Foot()) continue;
+
+ FootClass const * foot = (FootClass *)obj;
+
+ /*
+ ** Only consider objects that are owned by the player.
+ */
+ if (!foot->IsOwnedByPlayer) continue;
+
+ /*
+ ** If another member of this team has been discovered and
+ ** it isn't selected, then the formation move cannot take
+ ** place.
+ */
+ if (foot->Group == group) {
+ FormMove = false;
+ break;
+ }
+ }
+ }
+ }
+
+ for (int index = 0; index < CurrentObject.Count(); index++) {
+ ObjectClass * tobject = CurrentObject[index];
+
+ if (object != NULL) {
+ tobject->Active_Click_With(tobject->What_Action(object), object);
+ } else {
+
+ /*
+ ** Trap for formation moves: if this unit is part of a
+ ** formation (being part of a team qualifies) and they're
+ ** told to move, adjust the target destination so they stay
+ ** in formation when they arrive.
+ */
+ CELL newmove = cell;
+ int whatami = tobject->What_Am_I();
+ if (action == ACTION_MOVE && tobject->Is_Foot()) {
+ int oldisform;
+ FootClass * foot = (FootClass *)tobject;
+ oldisform = foot->IsFormationMove;
+ foot->IsFormationMove = FormMove;
+ if (FormMove && foot->Group != 255 ) {
+ newmove = foot->Adjust_Dest(cell);
+ }
+ foot->IsFormationMove = oldisform;
+ }
+ tobject->Active_Click_With(tobject->What_Action(cell), newmove);
+ }
+ AllowVoice = false;
+ }
+ AllowVoice = true;
+ FormMove = false;
+
+ if (action == ACTION_REPAIR && object->What_Am_I() == RTTI_BUILDING) {
+ OutList.Add(EventClass(EventClass::REPAIR, TargetClass(object)));
+ }
+ if (action == ACTION_SELL_UNIT && object) {
+ switch (object->What_Am_I()) {
+ case RTTI_AIRCRAFT:
+ case RTTI_UNIT:
+ OutList.Add(EventClass(EventClass::SELL, TargetClass(object)));
+ break;
+
+ default:
+ break;
+ }
+
+ }
+ if (action == ACTION_SELL) {
+ if (object) {
+ OutList.Add(EventClass(EventClass::SELL, TargetClass(object)));
+ } else {
+ OutList.Add(EventClass(EventClass::SELLCELL, cell));
+// OutList.Add(EventClass(EventClass::SELL, ::As_Target(cell)));
+ }
+ }
+
+ if (action == ACTION_NUKE_BOMB) {
+ OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_NUCLEAR_BOMB, cell));
+ }
+
+ if (action == ACTION_PARA_BOMB) {
+ OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_PARA_BOMB, cell));
+ }
+ if (action == ACTION_PARA_INFANTRY) {
+ OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_PARA_INFANTRY, cell));
+ }
+ if (action == ACTION_SPY_MISSION) {
+ OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_SPY_MISSION, cell));
+ }
+ if (action == ACTION_IRON_CURTAIN) {
+ OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_IRON_CURTAIN, cell));
+ }
+ if (action == ACTION_CHRONOSPHERE) {
+ OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_CHRONOSPHERE, cell));
+ }
+ if (action == ACTION_CHRONO2) {
+ OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_CHRONO2, cell));
+ }
+ }
+
+ IsTentative = false;
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Mouse_Left_Press -- Handles the left mouse button press. *
+ * *
+ * Handle the left mouse button press while over the tactical map. If it isn't is *
+ * repair or sell mode, then a tentative transition to rubber band mode is flagged. If the *
+ * mouse moves a sufficient distance from this recorded position, then rubber band mode *
+ * is officially started. *
+ * *
+ * INPUT: x,y -- The mouse coordinates at the time of the press. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 02/24/1995 JLB : Created. *
+ *=============================================================================================*/
+void DisplayClass::Mouse_Left_Press(int x, int y)
+{
+ if (!IsRepairMode && !IsSellMode && IsTargettingMode == SPC_NONE && !PendingObject) {
+ IsTentative = true;
+ BandX = x;
+ BandY = y;
+ NewX = x;
+ NewY = y;
+ }
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Mouse_Left_Held -- Handles the left button held down. *
+ * *
+ * This routine is called continuously while the left mouse button is held down over *
+ * the tactical map. This handles the rubber band mode detection and dragging. *
+ * *
+ * INPUT: x,y -- The mouse coordinate. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 02/24/1995 JLB : Created. *
+ *=============================================================================================*/
+void DisplayClass::Mouse_Left_Held(int x, int y)
+{
+ if (IsRubberBand) {
+ if (x != NewX || y != NewY) {
+ x = Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1);
+ y = Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1);
+ Refresh_Band();
+ NewX = x;
+ NewY = y;
+ IsToRedraw = true;
+ Flag_To_Redraw(false);
+ }
+ } else {
+
+ /*
+ ** If the mouse is still held down while a tentative extended select is possible, then
+ ** check to see if the mouse has moved a sufficient distance in order to activate
+ ** extended select mode.
+ */
+ if (IsTentative) {
+
+ /*
+ ** The mouse must have moved a minimum distance before rubber band mode can be
+ ** initiated.
+ */
+ if (ABS(x - BandX) > 4 || ABS(y - BandY) > 4) {
+ IsRubberBand = true;
+ x = Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1);
+ y = Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1);
+ NewX = x;
+ NewY = y;
+ IsToRedraw = true;
+ Flag_To_Redraw(false);
+
+ /*
+ ** Stretching the rubber band requires all objects to be redrawn.
+ */
+ for (int index = 0; index < Layer[LAYER_TOP].Count(); index++) {
+ Layer[LAYER_TOP][index]->Mark(MARK_CHANGE);
+ }
+ for (index = 0; index < Layer[LAYER_AIR].Count(); index++) {
+ Layer[LAYER_AIR][index]->Mark(MARK_CHANGE);
+ }
+
+ }
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::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 = (int)Coord_X(coord) - (int)Cell_To_Lepton(MapCellX);
+ int yy = (int)Coord_Y(coord) - (int)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. *
+ * 10/20/1996 JLB : Doesn't wrap. *
+ *=============================================================================================*/
+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->IsOwnedByPlayer) {
+ 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->IsOwnedByPlayer) {
+ 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->IsOwnedByPlayer) {
+ x += (((long)Coord_XCell(bldgp->Coord)) * 16);
+ y += (((long)Coord_YCell(bldgp->Coord)) * 16);
+ num += 16;
+ }
+ }
+
+ for (i = 0; i < Vessels.Count(); i++) {
+ VesselClass * bldgp = Vessels.Ptr(i);
+ if (!bldgp->IsInLimbo && bldgp->IsOwnedByPlayer) {
+ x += (((long)Coord_XCell(bldgp->Coord)));
+ y += (((long)Coord_YCell(bldgp->Coord)));
+ num++;
+ }
+ }
+
+ /*
+ ** Divide each coord by 'num' to compute the average value
+ */
+ if (num > 0) {
+ x /= num;
+ } else {
+ x = 0;
+ }
+
+ if (num > 0) {
+ y /= num;
+ } else {
+ y = 0;
+ }
+
+ /*
+ ** Tactical position is based on the cell of the upper left corner. Make adjustments
+ ** and bound the calculated location to the map dimensions.
+ */
+// x -= 5 * RESFACTOR;
+// y -= 4 * RESFACTOR;
+ if (x < MapCellX + 5*RESFACTOR) x = MapCellX + 5*RESFACTOR;
+ if (y < MapCellY + 4*RESFACTOR) y = MapCellY + 4*RESFACTOR;
+ if (x > MapCellX+MapCellWidth - 5*RESFACTOR) x = MapCellX+MapCellWidth - 5*RESFACTOR;
+ if (y > MapCellY+MapCellHeight - 4*RESFACTOR) y = MapCellY+MapCellHeight - 4*RESFACTOR;
+
+ Scen.Waypoint[WAYPT_HOME] = Scen.Views[0] = Scen.Views[1] = Scen.Views[2] = Scen.Views[3] = XY_Cell(x, y);
+
+ Map.Set_Tactical_Position(Coord_Whole(Cell_Coord((Scen.Views[0] - (MAP_CELL_W * 4 * RESFACTOR)) - (5*RESFACTOR))));
+// Set_Tactical_Position(Cell_Coord(XY_Cell(x, y)));
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Sell_Mode_Control -- Controls the sell mode. *
+ * *
+ * This routine will control the sell mode for the player. *
+ * *
+ * INPUT: control -- The mode to set the sell state to. *
+ * 0 = Turn sell mode off. *
+ * 1 = Turn sell mode on. *
+ * -1 = Toggle sell mode. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/08/1995 JLB : Created. *
+ *=============================================================================================*/
+void DisplayClass::Sell_Mode_Control(int control)
+{
+ bool mode = IsSellMode;
+ switch (control) {
+ case 0:
+ mode = false;
+ break;
+
+ case -1:
+ mode = (IsSellMode == false);
+ break;
+
+ case 1:
+ mode = true;
+ break;
+ }
+
+ if (mode != IsSellMode && !PendingObject) {
+ IsRepairMode = false;
+ if (mode && PlayerPtr->BScan) {
+ IsSellMode = true;
+ Unselect_All();
+ } else {
+ IsSellMode = false;
+ Revert_Mouse_Shape();
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Repair_Mode_Control -- Controls the repair mode. *
+ * *
+ * This routine is used to control the repair mode for the player. *
+ * *
+ * INPUT: control -- The mode to set the repair to. *
+ * 0 = Turn repair off. *
+ * 1 = Turn repair on. *
+ * -1= Toggle repair state. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/08/1995 JLB : Created. *
+ *=============================================================================================*/
+void DisplayClass::Repair_Mode_Control(int control)
+{
+ bool mode = IsRepairMode;
+ switch (control) {
+ case 0:
+ mode = false;
+ break;
+
+ case -1:
+ mode = (IsRepairMode == false);
+ break;
+
+ case 1:
+ mode = true;
+ break;
+ }
+
+ if (mode != IsRepairMode && !PendingObject) {
+ IsSellMode = false;
+ if (mode && PlayerPtr->BScan) {
+ IsRepairMode = true;
+ Unselect_All();
+ } else {
+ IsRepairMode = false;
+ Revert_Mouse_Shape();
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::In_View -- Determines if cell is visible on screen. *
+ * *
+ * Use this routine to determine if the specified cell is visible on *
+ * the display. This is a useful fact, since many display operations *
+ * can be skipped if the cell is not visible. *
+ * *
+ * INPUT: cell -- The cell number to check. *
+ * *
+ * OUTPUT: bool; Is this cell visible on the display? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 04/30/1994 JLB : Created. *
+ * 04/30/1994 JLB : Converted to member function. *
+ *=============================================================================================*/
+bool DisplayClass::In_View(register CELL cell) const
+{
+ if (cell & 0xC000) return(false);
+
+ COORDINATE coord = Coord_Whole(Cell_Coord(cell));
+ COORDINATE tcoord = Coord_Whole(TacticalCoord);
+
+ if ((unsigned)(Coord_X(coord) - Coord_X(tcoord)) > TacLeptonWidth+CELL_LEPTON_W-1) return(false);
+ if ((unsigned)(Coord_Y(coord) - Coord_Y(tcoord)) > TacLeptonHeight+CELL_LEPTON_H-1) return(false);
+ return(true);
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Closest_Free_Spot -- Finds the closest cell sub spot that is free. *
+ * *
+ * Use this routine to find the sub cell spot closest to the coordinate specified that is *
+ * free from occupation. Typical use of this is for infantry destination calculation. *
+ * *
+ * INPUT: coord -- The coordinate to use as the starting point when finding the closest *
+ * free spot. *
+ * *
+ * any -- Ignore occupation and just return the closest sub cell spot? *
+ * *
+ * OUTPUT: Returns with the coordinate of the closest free (possibly) sub cell location. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 09/22/1995 JLB : Created. *
+ *=============================================================================================*/
+COORDINATE DisplayClass::Closest_Free_Spot(COORDINATE coord, bool any) const
+{
+ if (coord & HIGH_COORD_MASK) {
+ return(0x00800080);
+ }
+ return Map[coord].Closest_Free_Spot(coord, any);
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Is_Spot_Free -- Determines if cell sub spot is free of occupation. *
+ * *
+ * Use this routine to determine if the coordinate (rounded to the nearest sub cell *
+ * position) is free for placement. Typical use of this would be for infantry placement. *
+ * *
+ * INPUT: coord -- The coordinate to examine for "freeness". The coordinate is rounded to *
+ * the nearest free sub cell spot. *
+ * *
+ * OUTPUT: Is the sub spot indicated by the coordinate free from previous occupation? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 09/22/1995 JLB : Created. *
+ *=============================================================================================*/
+bool DisplayClass::Is_Spot_Free(COORDINATE coord) const
+{
+ if (coord & HIGH_COORD_MASK) {
+ return(0x00800080);
+ }
+ return Map[coord].Is_Spot_Free(CellClass::Spot_Index(coord));
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Center_Map -- Centers the map about the currently selected objects *
+ * *
+ * This routine will average the position of all the selected objects and then center *
+ * the map about those objects. *
+ * *
+ * INPUT: center -- The is an optional center about override coordinate. If specified, *
+ * then the map will be centered about that coordinate. Otherwise it *
+ * will center about the average location of all selected objects. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: The map position changes by this routine. *
+ * *
+ * HISTORY: *
+ * 08/22/1995 JLB : Created. *
+ * 09/16/1996 JLB : Takes coordinate to center about (as override). *
+ *=============================================================================================*/
+void DisplayClass::Center_Map(COORDINATE center)
+{
+ int x = 0;
+// unsigned x = 0;
+ int y = 0;
+// unsigned y = 0;
+ bool centerit = false;
+
+ if (CurrentObject.Count()) {
+
+ for (int index = 0; index < CurrentObject.Count(); index++) {
+ COORDINATE coord = CurrentObject[index]->Center_Coord();
+
+ x += Coord_X(coord);
+ y += Coord_Y(coord);
+ }
+
+ x /= CurrentObject.Count();
+ y /= CurrentObject.Count();
+ centerit = true;
+ }
+
+ if (center != 0L) {
+ x = Coord_X(center);
+ y = Coord_Y(center);
+ centerit = true;
+ }
+
+ if (centerit) {
+ x = x - (int)TacLeptonWidth/2;
+ if (x < Cell_To_Lepton(MapCellX)) x = Cell_To_Lepton(MapCellX);
+
+ y = y - (int)TacLeptonHeight/2;
+ if (y < Cell_To_Lepton(MapCellY)) y = Cell_To_Lepton(MapCellY);
+
+ Set_Tactical_Position(XY_Coord(x, y));
+ }
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Encroach_Shadow -- Causes the shadow to creep back by one cell. *
+ * *
+ * This routine will cause the shadow to creep back by one cell. Multiple calls to this *
+ * routine will result in the shadow becoming more and more invasive until only the sight *
+ * range of player controlled units will keep the shadow pushed back. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 10/16/1995 JLB : Created. *
+ *=============================================================================================*/
+void DisplayClass::Encroach_Shadow(void)
+{
+ for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) {
+ if (!In_Radar(cell)) continue;
+
+ CellClass * cellptr = &(*this)[cell];
+ if (cellptr->IsVisible || !cellptr->IsMapped) continue;
+
+ cellptr->IsToShroud = true;
+ }
+
+ /*
+ ** Mark all shadow edge cells to be fully shrouded. All adjacent mapped
+ ** cell should become partially shrouded.
+ */
+ for (cell = 0; cell < MAP_CELL_TOTAL; cell++) {
+ if (!In_Radar(cell)) continue;
+
+ if ((*this)[cell].IsToShroud) {
+ (*this)[cell].IsToShroud = false;
+ Shroud_Cell(cell);
+ }
+ }
+
+ All_To_Look();
+
+ Flag_To_Redraw(true);
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Shroud_Cell -- Returns the specified cell into the shrouded condition. *
+ * *
+ * This routine is called to add the shroud back to the cell specified. Typical of this *
+ * would be when the shroud is to regenerate. *
+ * *
+ * INPUT: cell -- The cell that the shroud is to be regenerated upon. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: Adjacent cells might be affected by this routine. The affect is determined *
+ * according to the legality of the partial shadow artwork. In the illegal cases *
+ * the adjacent cell might become shrouded as well. *
+ * *
+ * HISTORY: *
+ * 10/17/1995 JLB : Created. *
+ * 06/17/1996 JLB : Modified to handle the new shadow pieces. *
+ *=============================================================================================*/
+void DisplayClass::Shroud_Cell(CELL cell/*KO, bool shadeit*/)
+{
+ if (PlayerPtr->IsGPSActive) {
+ if ( (*this)[cell].Jammed & (1 << PlayerPtr->Class->House) ) {
+ return;
+ }
+ }
+ if (!In_Radar(cell)) return;
+
+ CellClass * cellptr = &(*this)[cell];
+ if (cellptr->IsMapped) {
+
+ cellptr->IsMapped = false;
+ cellptr->IsVisible = false;
+ cellptr->Redraw_Objects();
+
+ /*
+ ** Check adjacent cells. There might be some weird combination of
+ ** shrouded cells such that more cells must be shrouded in order for
+ ** this to work.
+ */
+ for (FacingType dir = FACING_FIRST; dir < FACING_COUNT; dir++) {
+ CELL c = Adjacent_Cell(cell, dir);
+ CellClass * cptr = &(*this)[c];
+
+ /*
+ ** If this adjacent cell must be completely shrouded as a result
+ ** of the map change, yet it isn't already shrouded, then recursively
+ ** shroud that cell.
+ */
+ if (c != cell) {
+ cptr->IsVisible = false;
+ }
+
+ /*
+ ** Always redraw the cell because, more than likely, the shroud
+ ** edge will change shape because of the map change.
+ */
+ cptr->Redraw_Objects();
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Read_INI -- Reads map control data from INI file. *
+ * *
+ * This routine is used to read the map control data from the INI *
+ * file. *
+ * *
+ * INPUT: buffer -- Pointer to the loaded INI file data. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: The TriggerClass INI data must have been read before calling this function. *
+ * *
+ * HISTORY: *
+ * 05/27/1994 JLB : Created. *
+ *=============================================================================================*/
+void DisplayClass::Read_INI(CCINIClass & ini)
+{
+ /*
+ ** Read the map dimensions.
+ */
+ char const * const name = "Map";
+ int x = ini.Get_Int(name, "X", 1);
+ int y = ini.Get_Int(name, "Y", 1);
+ int w = ini.Get_Int(name, "Width", MAP_CELL_W-2);
+ int h = ini.Get_Int(name, "Height", MAP_CELL_H-2);
+
+#ifndef FIXIT_VERSION_3 // Map size no longer restricted.
+
+#ifdef FIXIT_CSII // checked - ajw
+ if(Session.Type >= GAME_MODEM && Session.Type <= GAME_INTERNET && PlayingAgainstVersion < VERSION_AFTERMATH_CS) {
+ /*
+ ** HACK ALERT:
+ ** Force the map to be limited to the size that 96x96 would be. If the
+ ** size is greater (due to hacking?) then shrink it down to legal size.
+ ** BG Note: only do this for multiplayer games against non-AfterMath.
+ */
+ if (w * h > 96 * 96) {
+ h -= (((w*h) - (96*96)) / w) + 1;
+ }
+ }
+#else
+ /*
+ ** HACK ALERT:
+ ** Force the map to be limited to the size that 96x96 would be. If the
+ ** size is greater (due to hacking?) then shrink it down to legal size.
+ */
+ if (w * h > 96 * 96) {
+ h -= (((w*h) - (96*96)) / w) + 1;
+ }
+#endif
+
+#endif // !FIXIT_VERSION_3
+
+ Set_Map_Dimensions( x, y, w, h );
+
+ /*
+ ** The theater is determined at this point. There is specific data that
+ ** is custom to this data. Load the custom data (as it related to terrain)
+ ** at this point.
+ */
+ Scen.Theater = ini.Get_TheaterType(name, "Theater", THEATER_TEMPERATE);
+
+ /*
+ ** Remove any old theater specific uncompressed shapes
+ */
+#ifdef WIN32
+ if (Scen.Theater != LastTheater) {
+ Reset_Theater_Shapes();
+ }
+#endif //WIN32
+
+ /*
+ ** Now that the theater is known, init the entire map hierarchy
+ */
+ Init(Scen.Theater);
+
+ /*
+ ** Special initializations occur when the theater is known.
+ */
+ TerrainTypeClass::Init(Scen.Theater);
+ TemplateTypeClass::Init(Scen.Theater);
+ OverlayTypeClass::Init(Scen.Theater);
+ UnitTypeClass::Init(Scen.Theater);
+ InfantryTypeClass::Init(Scen.Theater);
+ BuildingTypeClass::Init(Scen.Theater);
+ BulletTypeClass::Init(Scen.Theater);
+ AnimTypeClass::Init(Scen.Theater);
+ AircraftTypeClass::Init(Scen.Theater);
+ VesselTypeClass::Init(Scen.Theater);
+ SmudgeTypeClass::Init(Scen.Theater);
+
+ /*
+ ** Read the Waypoint entries.
+ */
+ for (int i = 0; i < WAYPT_COUNT; i++) {
+ char buf[20];
+ sprintf(buf, "%d", i);
+ Scen.Waypoint[i] = ini.Get_Int("Waypoints", buf, -1);
+
+ if (Scen.Waypoint[i] != -1) {
+ (*this)[Scen.Waypoint[i]].IsWaypoint = 1;
+ }
+ }
+
+ /*
+ ** Set the starting position (do this after Init(), which clears the cells'
+ ** IsWaypoint flags).
+ */
+ if (Scen.Waypoint[WAYPT_HOME] == -1) {
+ Scen.Waypoint[WAYPT_HOME] = XY_Cell(MapCellX + 5*RESFACTOR, MapCellY + 4*RESFACTOR);
+ }
+
+ Scen.Views[0] = Scen.Views[1] = Scen.Views[2] = Scen.Views[3] = Scen.Waypoint[WAYPT_HOME];
+ Set_Tactical_Position(Cell_Coord((Scen.Waypoint[WAYPT_HOME] - (MAP_CELL_W * 4 * RESFACTOR)) - (5*RESFACTOR)));
+
+ /*
+ ** Loop through all CellTrigger entries.
+ */
+ int len = ini.Entry_Count("CellTriggers");
+ for (int index = 0; index < len; index++) {
+
+ /*
+ ** Get a cell trigger and cell assignment.
+ */
+ char const * cellentry = ini.Get_Entry("CellTriggers", index);
+ TriggerTypeClass * tp = ini.Get_TriggerType("CellTriggers", cellentry);
+ CELL cell = atoi(cellentry);
+
+ if (tp != NULL && !(*this)[cell].Trigger.Is_Valid()) {
+ TriggerClass * tt = Find_Or_Make(tp);
+ if (tt) {
+ tt->AttachCount++;
+ tt->Cell = cell;
+ (*this)[cell].Trigger = tt;
+ }
+ }
+ }
+
+ /*
+ ** Read the map template data.
+ */
+ static char const * const MAPPACK = "MapPack";
+ len = ini.Get_UUBlock(MAPPACK, _staging_buffer, sizeof(_staging_buffer));
+ BufferStraw bstraw(_staging_buffer, len);
+ Map.Read_Binary(bstraw);
+
+ LastTheater = Scen.Theater;
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Write_INI -- Write the map data to the INI file specified. *
+ * *
+ * This routine will output all the data of this map to the INI database specified. *
+ * *
+ * INPUT: ini -- Reference to the INI handler to store the map data to. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: Any existing map data in the INI database will be replaced by this function. *
+ * *
+ * HISTORY: *
+ * 07/03/1996 JLB : Created. *
+ *=============================================================================================*/
+void DisplayClass::Write_INI(CCINIClass & ini)
+{
+ char entry[20];
+
+ /*
+ ** Save the map parameters.
+ */
+ static char const * const NAME = "Map";
+ ini.Clear(NAME);
+ ini.Put_TheaterType(NAME, "Theater", Scen.Theater);
+ ini.Put_Int(NAME, "X", MapCellX);
+ ini.Put_Int(NAME, "Y", MapCellY);
+ ini.Put_Int(NAME, "Width", MapCellWidth);
+ ini.Put_Int(NAME, "Height", MapCellHeight);
+
+ /*
+ ** Save the Waypoint entries.
+ */
+ static char const * const WAYNAME = "Waypoints";
+ ini.Clear(WAYNAME);
+ for (int i = 0; i < WAYPT_COUNT; i++) {
+ if (Scen.Waypoint[i] != -1) {
+ sprintf(entry, "%d", i);
+ ini.Put_Int(WAYNAME, entry, Scen.Waypoint[i]);
+ }
+ }
+
+ /*
+ ** Save the cell's triggers.
+ */
+ static char const * const CELLTRIG = "CellTriggers";
+ ini.Clear(CELLTRIG);
+ for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) {
+ if ((*this)[cell].Trigger.Is_Valid()) {
+ TriggerClass * tp = (*this)[cell].Trigger;
+ if (tp != NULL) {
+
+ /*
+ ** Generate entry name.
+ */
+ sprintf(entry, "%d", cell);
+
+ /*
+ ** Save entry.
+ */
+ ini.Put_TriggerType(CELLTRIG, entry, tp->Class);
+ }
+ }
+ }
+
+ /*
+ ** Write the map template data out to the ini file.
+ */
+ static char const * const MAPPACK = "MapPack";
+ BufferPipe bpipe(_staging_buffer, sizeof(_staging_buffer));
+ int len = Map.Write_Binary(bpipe);
+ ini.Clear(MAPPACK);
+ if (len) {
+ ini.Put_UUBlock(MAPPACK, _staging_buffer, len);
+ }
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::All_To_Look -- Direct all objects to look around for the player. *
+ * *
+ * This routine will scan through all objects and tell them to look if they are supposed *
+ * to be able to reveal the map for the player. This routine may be necessary in cases *
+ * of gap generator reshroud logic. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 09/23/1996 JLB : Created. *
+ *=============================================================================================*/
+void DisplayClass::All_To_Look(bool units_only)
+{
+ for (int index = 0; index < Layer[LAYER_GROUND].Count(); index++) {
+ ObjectClass * object = Layer[LAYER_GROUND][index];
+ if (object != NULL && object->Is_Techno()) {
+ TechnoClass * tech = ((TechnoClass *)object);
+
+ if (tech->What_Am_I() == RTTI_BUILDING && units_only) continue;
+
+ if (tech->House->IsPlayerControl) {
+ if (tech->IsDiscoveredByPlayer) {
+ object->Look();
+ }
+ } else {
+ if (tech->What_Am_I() == RTTI_BUILDING && Rule.IsAllyReveal && tech->House->Is_Ally(PlayerPtr)) {
+ tech->Look();
+ }
+ }
+ }
+ }
+}
+
+
+void DisplayClass::Constrained_Look(COORDINATE center, LEPTON distance)
+{
+ for (int index = 0; index < Layer[LAYER_GROUND].Count(); index++) {
+ ObjectClass * object = Layer[LAYER_GROUND][index];
+ if (object != NULL && object->Is_Techno()) {
+ TechnoClass * tech = ((TechnoClass *)object);
+
+// if (tech->What_Am_I() == RTTI_BUILDING && units_only) continue;
+
+ if (tech->House->IsPlayerControl) {
+ if (tech->IsDiscoveredByPlayer && Distance(tech->Center_Coord(), center) <= (tech->Techno_Type_Class()->SightRange * CELL_LEPTON_W) + distance) {
+ object->Look();
+ }
+ } else {
+ if (tech->What_Am_I() == RTTI_BUILDING && Rule.IsAllyReveal && tech->House->Is_Ally(PlayerPtr) &&
+ Distance(tech->Center_Coord(), center) <= (tech->Techno_Type_Class()->SightRange * CELL_LEPTON_W) + distance) {
+
+ tech->Look();
+ }
+ }
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * DisplayClass::Flag_Cell -- Flag the specified cell to be redrawn. *
+ * *
+ * This will flag the cell to be redrawn. *
+ * *
+ * INPUT: cell -- The cell to be flagged. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 10/20/1996 JLB : Created. *
+ *=============================================================================================*/
+void DisplayClass::Flag_Cell(CELL cell)
+{
+ Flag_To_Redraw(false);
+ IsToRedraw = true;
+ CellRedraw[cell] = true;
+}
diff --git a/CODE/DISPLAY.H b/CODE/DISPLAY.H
new file mode 100644
index 0000000..786922d
--- /dev/null
+++ b/CODE/DISPLAY.H
@@ -0,0 +1,321 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/DISPLAY.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : DISPLAY.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : May 1, 1994 *
+ * *
+ * Last Update : May 1, 1994 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifndef DISPLAY_H
+#define DISPLAY_H
+
+#include "map.h"
+#include "layer.h"
+
+
+#define ICON_PIXEL_W 24
+#define ICON_PIXEL_H 24
+#define ICON_LEPTON_W 256
+#define ICON_LEPTON_H 256
+#define CELL_PIXEL_W ICON_PIXEL_W
+#define CELL_PIXEL_H ICON_PIXEL_H
+#define CELL_LEPTON_W ICON_LEPTON_W
+#define CELL_LEPTON_H ICON_LEPTON_H
+
+// -----------------------------------------------------------
+#define PIXEL_LEPTON_W (ICON_LEPTON_W/ICON_PIXEL_W)
+#define PIXEL_LEPTON_H (ICON_LEPTON_H/ICON_PIXEL_H)
+
+#define SIDE_BAR_TAC_WIDTH 10
+#define SIDE_BAR_TAC_HEIGHT 8
+
+extern COORDINATE Coord_Add(COORDINATE coord1, COORDINATE coord2);
+
+class DisplayClass: public MapClass
+{
+ public:
+
+ /*
+ ** The tactical map display position is indicated by the cell of the
+ ** upper left hand corner. These should not be altered directly. Use
+ ** the Set_Tactical_Position function instead.
+ */
+ COORDINATE TacticalCoord;
+
+ /*
+ ** The dimensions (in cells) of the visible window onto the game map. This tactical
+ ** map is how the player interacts and views the game world.
+ */
+ LEPTON TacLeptonWidth;
+ LEPTON TacLeptonHeight;
+
+ /*
+ ** These layer control elements are used to group the displayable objects
+ ** so that proper overlap can be obtained.
+ */
+ static LayerClass Layer[LAYER_COUNT];
+
+ /*
+ ** This records the position and shape of a placement cursor to display
+ ** over the map. This cursor is used when placing buildings and also used
+ ** extensively by the scenario editor.
+ */
+ CELL ZoneCell;
+ short ZoneOffset;
+ short const *CursorSize;
+ bool ProximityCheck; // Is proximity check ok?
+
+ /*
+ ** This holds the building type that is about to be placed upon the map.
+ ** It is only valid during the building placement state. The PendingLegal
+ ** flag is updated as the cursor moves and it reflects the legality of
+ ** placing the building at the desired location.
+ */
+ ObjectClass * PendingObjectPtr;
+ ObjectTypeClass const * PendingObject;
+ HousesType PendingHouse;
+
+ static unsigned char FadingBrighten[256];
+ static unsigned char FadingShade[256];
+ static unsigned char FadingWayDark[256];
+ static unsigned char FadingLight[256];
+ static unsigned char FadingGreen[256];
+ static unsigned char FadingYellow[256];
+ static unsigned char FadingRed[256];
+ static unsigned char TranslucentTable[(MAGIC_COL_COUNT+1)*256];
+ static unsigned char WhiteTranslucentTable[(1+1)*256];
+ static unsigned char MouseTranslucentTable[(4+1)*256];
+ static void const *TransIconset;
+ static unsigned char UnitShadow[(USHADOW_COL_COUNT+1)*256];
+ static unsigned char UnitShadowAir[(USHADOW_COL_COUNT+1)*256];
+ static unsigned char SpecialGhost[2*256];
+
+ //-------------------------------------------------------------------------
+ DisplayClass(void);
+ DisplayClass(NoInitClass const & x) : MapClass(x) {};
+
+ virtual void Read_INI(CCINIClass & ini);
+ void Write_INI(CCINIClass & ini);
+
+ /*
+ ** Initialization
+ */
+ virtual void One_Time(void); // One-time inits
+ virtual void Init_Clear(void); // Clears all to known state
+ virtual void Init_IO(void); // Inits button list
+ virtual void Init_Theater(TheaterType theater); // Theater-specific inits
+
+ /*
+ ** General display/map/interface support functionality.
+ */
+ virtual void AI(KeyNumType &input, int x, int y);
+ virtual void Draw_It(bool complete=false);
+
+ /*
+ ** Added functionality.
+ */
+ void All_To_Look(bool units_only=false);
+ void Constrained_Look(COORDINATE coord, LEPTON distance);
+ void Shroud_Cell(CELL cell/*KO, bool shadeit = false*/);
+ void Encroach_Shadow(void);
+ void Center_Map(COORDINATE center=0L);
+ virtual bool Map_Cell(CELL cell, HouseClass *house);
+ virtual CELL Click_Cell_Calc(int x, int y) const;
+ virtual void Help_Text(int , int =-1, int =-1, int =YELLOW, bool =false) {};
+ virtual MouseType Get_Mouse_Shape(void) const = 0;
+ virtual bool Scroll_Map(DirType facing, int & distance, bool really);
+ virtual void Refresh_Cells(CELL cell, short const *list);
+ virtual void Set_View_Dimensions(int x, int y, int width=-1, int height=-1);
+
+ /*
+ ** Pending object placement control.
+ */
+ virtual void Put_Place_Back(TechnoClass * ) {}; // Affects 'pending' system.
+ void Cursor_Mark(CELL pos, bool on);
+ void Set_Cursor_Shape(short const * list);
+ CELL Set_Cursor_Pos(CELL pos = -1);
+ void Get_Occupy_Dimensions(int & w, int & h, short const *list) const;
+
+ /*
+ ** Tactical map only functionality.
+ */
+ virtual void Set_Tactical_Position(COORDINATE coord);
+ void Refresh_Band(void);
+ void Select_These(COORDINATE coord1, COORDINATE coord2);
+ COORDINATE Pixel_To_Coord(int x, int y) const;
+ bool Coord_To_Pixel(COORDINATE coord, int & x, int & y) const;
+ bool Push_Onto_TacMap(COORDINATE &source, COORDINATE &dest);
+ void Remove(ObjectClass const * object, LayerType layer);
+ void Submit(ObjectClass const * object, LayerType layer);
+ CELL Calculated_Cell(SourceType dir, WAYPOINT waypoint=-1, CELL cell=-1, SpeedType loco=SPEED_FOOT, bool zonecheck=true, MZoneType mzone=MZONE_NORMAL) const;
+ bool In_View(register CELL cell) const;
+ bool Passes_Proximity_Check(ObjectTypeClass const * object, HousesType house, short const * list, CELL trycell) const;
+ ObjectClass * Cell_Object(CELL cell, int x=0, int y=0) const;
+ ObjectClass * Next_Object(ObjectClass * object) const;
+ ObjectClass * Prev_Object(ObjectClass * object) const;
+ int Cell_Shadow(CELL cell) const;
+ short const * Text_Overlap_List(char const * text, int x, int y) const;
+ bool Is_Spot_Free(COORDINATE coord) const;
+ COORDINATE Closest_Free_Spot(COORDINATE coord, bool any=false) const;
+ void Sell_Mode_Control(int control);
+ void Repair_Mode_Control(int control);
+
+ virtual void Flag_Cell(CELL cell);
+ bool Is_Cell_Flagged(CELL cell) const {return CellRedraw.Is_True(cell);};
+
+ /*
+ ** Computes starting position based on player's units' Coords.
+ */
+ void Compute_Start_Pos(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(CELL cell, bool shadow, ObjectClass * object, ActionType action, bool wsmall = false);
+ virtual void Mouse_Left_Held(int x, int y);
+ virtual void Mouse_Left_Release(CELL cell, int x, int y, ObjectClass * object, ActionType action, bool wsmall = false);
+
+ public:
+ /*
+ ** This is the pixel offset for the upper left corner of the tactical map.
+ */
+ int TacPixelX;
+ int TacPixelY;
+
+ /*
+ ** This is the coordinate that the tactical map should be in at next available opportunity.
+ */
+ COORDINATE DesiredTacticalCoord;
+
+ /*
+ ** If something in the tactical map is to be redrawn, this flag is set to true.
+ */
+ unsigned IsToRedraw:1;
+
+ /*
+ ** If the player is currently wielding a wrench (to select buildings for repair),
+ ** then this flag is true. In such a state, normal movement and combat orders
+ ** are preempted.
+ */
+ unsigned IsRepairMode:1;
+
+ /*
+ ** If the player is currently in "sell back" mode, then this flag will be
+ ** true. While in this mode, anything clicked on will be sold back to the
+ ** "factory".
+ */
+ unsigned IsSellMode:1;
+
+ /*
+ ** If the player is currently in ion cannon targeting mode, then this
+ ** flag will be true. While in this mode, anything clicked on will be
+ ** be destroyed by the ION cannon.
+ */
+ SpecialWeaponType IsTargettingMode;
+
+ protected:
+
+ /*
+ ** If it is currently in rubber band mode (multi unit selection), then this
+ ** flag will be true. While in such a mode, normal input is preempted while
+ ** the extended selection is in progress.
+ */
+ unsigned IsRubberBand:1;
+
+ /*
+ ** The moment the mouse is held down, this flag gets set. If the mouse is dragged
+ ** a sufficient distance while held down, then true rubber band mode selection
+ ** can begin. Using a minimum distance prevents accidental rubber band selection
+ ** mode from being initiated.
+ */
+ unsigned IsTentative:1;
+
+ /*
+ ** This gadget class is used for capturing input to the tactical map. All mouse input
+ ** will be routed through this gadget.
+ */
+ class TacticalClass : public GadgetClass {
+ public:
+ TacticalClass(void) : GadgetClass(0,0,0,0,LEFTPRESS|LEFTRELEASE|LEFTHELD|LEFTUP|RIGHTPRESS,true) {};
+
+ 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(void);
+ void Redraw_OIcons(void);
+ void Redraw_Shadow(void);
+
+ /*
+ ** This bit array is used to flag cells to be redrawn. If the icon needs to
+ ** be redrawn for a cell, then the corresponding flag will be true.
+ */
+ static BooleanVectorClass CellRedraw;
+
+ bool Good_Reinforcement_Cell(CELL outcell, CELL incell, SpeedType loco, int zone, MZoneType mzone) const;
+};
+
+
+#endif
diff --git a/CODE/DOOR.CPP b/CODE/DOOR.CPP
new file mode 100644
index 0000000..a145bd6
--- /dev/null
+++ b/CODE/DOOR.CPP
@@ -0,0 +1,202 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/DOOR.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : DOOR.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 06/11/95 *
+ * *
+ * Last Update : June 14, 1995 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * DoorClass::AI -- Handles the door processing logic. *
+ * DoorClass::Close_Door -- Try to close the unit's door. *
+ * DoorClass::DoorClass -- Constructor for the DoorClass object. *
+ * DoorClass::Door_Stage -- Fetches the current door animation frame. *
+ * DoorClass::Open_Door -- Opens the door for this unit. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+#include "function.h"
+
+
+/***********************************************************************************************
+ * DoorClass::DoorClass -- Constructor for the DoorClass object. *
+ * *
+ * This constructor sets the door to an initial closed state. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/14/1995 JLB : Created. *
+ *=============================================================================================*/
+DoorClass::DoorClass(void)
+{
+ State = IS_CLOSED;
+ IsToRedraw = false;
+ Stages = 0;
+}
+
+
+/***********************************************************************************************
+ * DoorClass::AI -- Handles the door processing logic. *
+ * *
+ * This routine should be called every game frame. It handles the door closing and opening *
+ * logic. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/13/1995 JLB : Created. *
+ *=============================================================================================*/
+void DoorClass::AI(void)
+{
+ if (Control.Graphic_Logic()) {
+ if (Control.Fetch_Stage() >= Stages) {
+ Control.Set_Rate(0);
+ switch (State) {
+ case IS_OPENING:
+ State = IS_OPEN;
+ break;
+
+ case IS_CLOSING:
+ State = IS_CLOSED;
+ break;
+ }
+ }
+ IsToRedraw = true;
+ }
+}
+
+
+/***********************************************************************************************
+ * DoorClass::Open_Door -- Opens the door for this unit. *
+ * *
+ * This routine will perform the door open operation for this unit. It will control vehicle *
+ * rotation if necessary. *
+ * *
+ * INPUT: rate -- The animation rate (delay) to use for the door animation logic. *
+ * *
+ * stages -- The number of animations stages that this door must pass through. *
+ * *
+ * OUTPUT: Was action initiated to open the door? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/08/1995 JLB : Created. *
+ *=============================================================================================*/
+bool DoorClass::Open_Door(int rate, int stages)
+{
+ switch (State) {
+ case IS_CLOSED:
+ case IS_CLOSING:
+ State = IS_OPENING;
+ Stages = stages-1;
+ Control.Set_Stage(0);
+ Control.Set_Rate(rate);
+ return(true);
+ }
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * DoorClass::Close_Door -- Try to close the unit's door. *
+ * *
+ * This routine will attempt to close the unit's door. If the door is already closed or *
+ * in the process of closing, then no action is performed. *
+ * *
+ * INPUT: rate -- The animation rate (delay) to use for the door animation logic. *
+ * *
+ * stages -- The number of animations stages that this door must pass through. *
+ * *
+ * OUTPUT: Action was initiated to close the door? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/08/1995 JLB : Created. *
+ *=============================================================================================*/
+bool DoorClass::Close_Door(int rate, int stages)
+{
+ switch (State) {
+ case IS_OPEN:
+ case IS_OPENING:
+ State = IS_CLOSING;
+ Stages = stages-1;
+ Control.Set_Stage(0);
+ Control.Set_Rate(rate);
+ return(true);
+ }
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * DoorClass::Door_Stage -- Fetches the current door animation frame. *
+ * *
+ * Use this routine to fetch the current door animation frame number. Frame zero is the *
+ * closed frame and frame 'N' is the open frame. If the door is in the process of opening *
+ * or closing, the appropriate frame number is used. 'N' is defined as the number of *
+ * stages in the animation minus 1 (e.g., a four frame animation will return a door stage *
+ * number between 0 and 3, inclusive). *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: Returns with the door animation frame number. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 06/14/1995 JLB : Created. *
+ *=============================================================================================*/
+int DoorClass::Door_Stage(void) const
+{
+ switch (State) {
+ case IS_CLOSING:
+ return((Stages-1) - Control.Fetch_Stage());
+
+ case IS_CLOSED:
+ return(0);
+
+ case IS_OPENING:
+ return(Control.Fetch_Stage());
+
+ case IS_OPEN:
+ return(Stages-1);
+ }
+ return(0);
+}
diff --git a/CODE/DOOR.H b/CODE/DOOR.H
new file mode 100644
index 0000000..9ad73a6
--- /dev/null
+++ b/CODE/DOOR.H
@@ -0,0 +1,89 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/DOOR.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : DOOR.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 06/11/95 *
+ * *
+ * Last Update : June 11, 1995 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifndef DOOR_H
+#define DOOR_H
+
+class DoorClass
+{
+ private:
+
+ /*
+ ** This is the animation control handler.
+ */
+ StageClass Control;
+
+ /*
+ ** This is the recorded number of stages of the current
+ ** door animation process.
+ */
+ unsigned char Stages;
+
+ /*
+ ** This is the door state.
+ */
+ enum {
+ IS_CLOSED, // Door is closed.
+ IS_OPENING, // Door is in the process of opening.
+ IS_OPEN, // Door is fully open.
+ IS_CLOSING // Door is in the process of closing.
+ } State;
+
+ /*
+ ** If the animation for this door indicates that the object it is
+ ** attached to should be redrawn, then this flag will be true.
+ */
+ unsigned IsToRedraw:1;
+
+ public:
+ DoorClass(void);
+ DoorClass(NoInitClass const & x) : Control(x) {};
+
+ bool Time_To_Redraw(void) {return(IsToRedraw);};
+ void Clear_Redraw_Flag(void) {IsToRedraw = false;};
+ void AI(void);
+ int Door_Stage(void) const;
+ bool Is_Door_Opening(void) const {return(State == IS_OPENING);};
+ bool Is_Door_Closing(void) const {return(State == IS_CLOSING);};
+ bool Open_Door(int rate, int stages);
+ bool Close_Door(int rate, int stages);
+ bool Is_Door_Open(void) const {return(State == IS_OPEN);};
+ bool Is_Door_Closed(void) const {return(State == IS_CLOSED);};
+ bool Is_Ready_To_Open(void) const;
+};
+
+#endif
diff --git a/CODE/DPMI.CPP b/CODE/DPMI.CPP
new file mode 100644
index 0000000..03c972e
--- /dev/null
+++ b/CODE/DPMI.CPP
@@ -0,0 +1,169 @@
+/*
+** Command & Conquer Red Alert(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&c0\vcs\code\dpmi.cpv 4.41 04 Jul 1996 16:12:42 JOE_BOSTIC $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : DPMI.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : July 2, 1994 *
+ * *
+ * Last Update : July 2, 1994 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#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/CODE/DPMI.H b/CODE/DPMI.H
new file mode 100644
index 0000000..78a1bf5
--- /dev/null
+++ b/CODE/DPMI.H
@@ -0,0 +1,175 @@
+/*
+** Command & Conquer Red Alert(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&c0\vcs\code\dpmi.h_v 4.43 05 Jul 1996 17:58:40 JOE_BOSTIC $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : DPMI.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : July 2, 1994 *
+ * *
+ * Last Update : July 2, 1994 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifndef DPMI_Hx
+#define DPMI_Hx
+#include
+#include
+#include
+#include
+
+
+extern void output(short port, short data);
+
+
+class DOSSegmentClass {
+ /*
+ ** This is the selector/segment value. In real mode it is the segment, in protected
+ ** mode it is the selector (also 16 bits). This value is moved into DS or ES when
+ ** accessing memory.
+ ** Note: in Watcom flat addressing, Selector == Segment<<4 (ex: 0A0000h)
+ */
+ unsigned int Selector;
+
+ /*
+ ** These are C equivalents for pushing and popping the DS segment register. By using
+ ** these, it is possible to create very small code that uses a segment and
+ ** offset without damaging the DS register. These are especially useful in protected
+ ** mode, but they are legal in real mode as well.
+ */
+ void Push_DS(void) {/*__emit__(0x1E);*/};
+ void Pop_DS(void) {/*__emit__(0x1F);*/};
+
+ public:
+ DOSSegmentClass(void);
+ ~DOSSegmentClass(void);
+ DOSSegmentClass(unsigned short segment, long size=(1024L*64L));
+
+ unsigned int Get_Selector(void);
+
+ /*
+ ** This routine is used to assign where the descriptor actually points to in
+ ** low DOS memory. In real mode, this is a simple segment assignment and the size
+ ** is always 64K regardless of what is specified. In protected mode, the segment
+ ** is used to update the selector and the size can be any length.
+ ** In Watcom flat mode, it sets Selector == segment<<4
+ */
+ void Assign(unsigned short segment, long size=(1024L*64L));
+
+ /*
+ ** These routines will move the data to/from regular memory and the segment/descriptor
+ ** memory.
+ */
+ void Copy_To(void *source, int dest, int size);
+ void Copy_From(void *dest, int source, int size);
+ void Copy_Word_To(short data, int dest);
+ void Copy_Byte_To(char data, int dest);
+ void Copy_DWord_To(long data, int dest);
+ short Copy_Word_From(int source);
+ char Copy_Byte_From(int source);
+ long Copy_DWord_From(int source);
+
+ /*
+ ** These routines move data around between sections of segmented (descriptor) memory.
+ ** Typically, this is used when accessing DOS memory in protected mode or when dealing
+ ** with hard memory areas such as the screen.
+ */
+ static void Copy(DOSSegmentClass &src, int soffset, DOSSegmentClass &dest, int doffset, int size);
+ static void Swap(DOSSegmentClass &src, int soffset, DOSSegmentClass &dest, int doffset, int size);
+};
+
+
+inline DOSSegmentClass::DOSSegmentClass(void)
+{
+ Selector = 0xB0000;
+}
+
+inline DOSSegmentClass::~DOSSegmentClass(void)
+{
+}
+
+inline void DOSSegmentClass::Copy_Word_To(short data, int dest)
+{
+ *(short *)(Selector+dest) = data;
+}
+
+inline void DOSSegmentClass::Copy_Byte_To(char data, int dest)
+{
+ *(char *)(Selector+dest) = data;
+}
+
+inline void DOSSegmentClass::Copy_DWord_To(long data, int dest)
+{
+ *(long *)(Selector+dest) = data;
+}
+
+inline void DOSSegmentClass::Assign(unsigned short segment, long)
+{
+ Selector = (long)(segment)<<4L;
+}
+
+inline DOSSegmentClass::DOSSegmentClass(unsigned short segment, long)
+{
+ Assign(segment);
+}
+
+inline void DOSSegmentClass::Copy_To(void *source, int dest, int size)
+{
+ memmove((void*)(Selector+dest), source, (unsigned)size);
+}
+
+inline void DOSSegmentClass::Copy_From(void *dest, int source, int size)
+{
+ memmove(dest, (void*)(Selector+source), (unsigned)size);
+}
+
+inline void DOSSegmentClass::Copy(DOSSegmentClass &src, int soffset, DOSSegmentClass &dest, int doffset, int size) {
+ memmove((void*)(dest.Selector+doffset), (void*)(src.Selector+soffset), (unsigned)size);
+}
+
+inline short DOSSegmentClass::Copy_Word_From(int source)
+{
+ return *(short*)(Selector+source);
+}
+
+inline char DOSSegmentClass::Copy_Byte_From(int source)
+{
+ return *(char*)(Selector+source);
+}
+
+inline long DOSSegmentClass::Copy_DWord_From(int source)
+{
+ return *(long*)(Selector+source);
+}
+
+inline unsigned int DOSSegmentClass::Get_Selector(void)
+{
+ return Selector;
+}
+#endif
+
+
diff --git a/CODE/DRIVE.CPP b/CODE/DRIVE.CPP
new file mode 100644
index 0000000..b349259
--- /dev/null
+++ b/CODE/DRIVE.CPP
@@ -0,0 +1,2333 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/DRIVE.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : DRIVE.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : April 22, 1994 *
+ * *
+ * Last Update : October 31, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * DriveClass::AI -- Processes unit movement and rotation. *
+ * DriveClass::Approach_Target -- Handles approaching the target in order to attack it. *
+ * DriveClass::Assign_Destination -- Set the unit's NavCom. *
+ * DriveClass::Class_Of -- Fetches a reference to the class type for this object. *
+ * DriveClass::Debug_Dump -- Displays status information to monochrome screen. *
+ * DriveClass::Do_Turn -- Tries to turn the vehicle to the specified direction. *
+ * DriveClass::DriveClass -- Constructor for drive class object. *
+ * DriveClass::Fixup_Path -- Adds smooth start path to normal movement path. *
+ * DriveClass::Force_Track -- Forces the unit to use the indicated track. *
+ * DriveClass::Lay_Track -- Handles track laying logic for the unit. *
+ * DriveClass::Limbo -- Prepares vehicle and then limbos it. *
+ * DriveClass::Mark_Track -- Marks the midpoint of the track as occupied. *
+ * DriveClass::Ok_To_Move -- Checks to see if this object can begin moving. *
+ * DriveClass::Per_Cell_Process -- Handles when unit finishes movement into a cell. *
+ * DriveClass::Response_Attack -- Voice feedback when ordering the unit to attack a target. *
+ * DriveClass::Response_Move -- Voice feedback when ordering the unit to move. *
+ * DriveClass::Response_Select -- Voice feedback when selecting the unit. *
+ * DriveClass::Scatter -- Causes the unit to travel to a nearby safe cell. *
+ * DriveClass::Smooth_Turn -- Handles the low level coord calc for smooth turn logic. *
+ * DriveClass::Start_Of_Move -- Tries to get a unit to advance toward cell. *
+ * DriveClass::Stop_Driver -- Handles removing occupation bits when driving stops. *
+ * DriveClass::Teleport_To -- Teleport object to specified location. *
+ * DriveClass::While_Moving -- Processes unit movement. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+
+
+#ifdef NEVER
+void test(void)
+{
+ enum nums {one, two, three};
+
+ nums x;
+ nums *ptr;
+
+ ptr = &x;
+}
+#endif
+
+
+/***********************************************************************************************
+ * DriveClass::Response_Select -- Voice feedback when selecting the unit. *
+ * *
+ * This is the voice to play when the unit is selected. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 12/30/1994 JLB : Created. *
+ *=============================================================================================*/
+void DriveClass::Response_Select(void)
+{
+ assert(IsActive);
+
+ static VocType _response[] = {
+ VOC_VEHIC,
+ VOC_REPORT,
+ VOC_YESSIR,
+ VOC_YESSIR,
+ VOC_YESSIR,
+ VOC_AWAIT
+ };
+ VocType response = _response[Sim_Random_Pick(0, ARRAY_SIZE(_response)-1)];
+ if (AllowVoice) {
+ Sound_Effect(response, fixed(1), -(ID+1));
+ }
+}
+
+
+/***********************************************************************************************
+ * DriveClass::Response_Move -- Voice feedback when ordering the unit to move. *
+ * *
+ * This plays the audio feedback when ordering this unit to move to a new destination. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 12/30/1994 JLB : Created. *
+ *=============================================================================================*/
+void DriveClass::Response_Move(void)
+{
+ assert(IsActive);
+
+ static VocType _response[] = {
+ VOC_ACKNOWL,
+ VOC_AFFIRM,
+ };
+ VocType response = _response[Sim_Random_Pick(0, ARRAY_SIZE(_response)-1)];
+ if (AllowVoice) {
+ Sound_Effect(response, fixed(1), -(ID+1));
+ }
+}
+
+
+/***********************************************************************************************
+ * DriveClass::Response_Attack -- Voice feedback when ordering the unit to attack a target. *
+ * *
+ * This plays the audio feedback when ordering this unit to attack. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 12/30/1994 JLB : Created. *
+ *=============================================================================================*/
+void DriveClass::Response_Attack(void)
+{
+ assert(IsActive);
+
+ static VocType _response[] = {
+ VOC_AFFIRM,
+ VOC_ACKNOWL
+ };
+ VocType response = _response[Sim_Random_Pick(0, ARRAY_SIZE(_response)-1)];
+ if (AllowVoice) {
+ Sound_Effect(response, fixed(1), -(ID+1));
+ }
+}
+
+
+/***********************************************************************************************
+ * DriveClass::Scatter -- Causes the unit to travel to a nearby safe cell. *
+ * *
+ * This routine is called when the unit discovers that it should get out of the "hot seat" *
+ * and move to an adjacent cell. Since the safety of the adjacent cell is not determined *
+ * before the move begins, it will appear that the unit is just scattering (which it *
+ * should). *
+ * *
+ * INPUT: threat -- The coordinate of the source of the threat. The unit will try to move *
+ * roughly away from the threat. *
+ * *
+ * forced -- The threat is real and a serious effort to scatter should be made. *
+ * *
+ * nokidding-- The scatter should affect the player's infantry even if it otherwise *
+ * wouldn't have. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 09/25/1994 JLB : Created. *
+ * 09/27/1995 JLB : Revised to never scatter if already moving. *
+ * 07/09/1996 JLB : Moved to DriveClass so that ships will scatter too. *
+ * 08/02/1996 JLB : Added the "nokidding" parameter. *
+ *=============================================================================================*/
+void DriveClass::Scatter(COORDINATE threat, bool forced, bool nokidding)
+{
+ assert(IsActive);
+
+ /*
+ ** Certain missions prevent scattering regardless of whether it would be
+ ** a good idea or not.
+ */
+ if (MissionControl[Mission].IsParalyzed) return;
+
+ if ((What_Am_I() != RTTI_UNIT || !((UnitClass *)this)->IsDumping) && (!Target_Legal(NavCom) || (nokidding && !IsRotating))) {
+ if (!Target_Legal(TarCom) || forced || Random_Pick(1, 4) == 1) {
+ FacingType toface;
+ FacingType newface;
+ CELL newcell;
+
+ if (threat != 0) {
+ toface = Dir_Facing(Direction8(threat, Coord));
+ toface = toface + FacingType(Random_Pick(0, 2)-1);
+ } else {
+ toface = Dir_Facing(PrimaryFacing.Current());
+ toface = toface + FacingType(Random_Pick(0, 2)-1);
+ }
+
+ for (FacingType face = FACING_N; face < FACING_COUNT; face++) {
+ newface = toface + face;
+ newcell = Adjacent_Cell(Coord_Cell(Coord), newface);
+
+ if (Map.In_Radar(newcell) && Can_Enter_Cell(newcell) == MOVE_OK) {
+ Assign_Destination(::As_Target(newcell));
+ }
+ }
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * DriveClass::Limbo -- Prepares vehicle and then limbos it. *
+ * *
+ * This routine removes the occupation bits for the vehicle and also handles cleaning up *
+ * any vehicle reservation bits. After this, it then proceeds with limboing the unit. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: bool; Was the vehicle limboed? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 12/22/1994 JLB : Created. *
+ *=============================================================================================*/
+bool DriveClass::Limbo(void)
+{
+ if (!IsInLimbo) {
+ Stop_Driver();
+ TrackNumber = -1;
+ }
+ return(FootClass::Limbo());
+}
+
+
+/***********************************************************************************************
+ * DriveClass::Stop_Driver -- Handles removing occupation bits when driving stops. *
+ * *
+ * This routine will remove the "reservation" flag (if present) when the vehicle is *
+ * required to stop movement. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: bool; Was the vehicle stopped? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 12/22/1994 JLB : Created. *
+ *=============================================================================================*/
+bool DriveClass::Stop_Driver(void)
+{
+ assert(IsActive);
+
+ /*
+ ** We only need to do something if the vehicle is actually going
+ ** somewhere.
+ */
+ if (Head_To_Coord()) {
+
+ /*
+ ** Safe off whether the vehicle is down or not so we know whether
+ ** we have to put it back down.
+ */
+ int temp = IsDown;
+
+ /*
+ ** If the vehicle is down, pick it up so it doesn't interfere with
+ ** our flags.
+ */
+ if (temp) {
+ Mark(MARK_UP);
+ }
+
+ /*
+ ** Call the drive class function which will let us release the
+ ** reserved track.
+ */
+ Mark_Track(Head_To_Coord(), MARK_UP);
+
+ /*
+ ** If it was down it should be down when we are done.
+ */
+ if (temp) {
+ Mark(MARK_DOWN);
+ }
+ }
+ return(FootClass::Stop_Driver());
+}
+
+
+/***********************************************************************************************
+ * DriveClass::Do_Turn -- Tries to turn the vehicle to the specified direction. *
+ * *
+ * This routine will set the vehicle to rotate to the direction specified. For tracked *
+ * vehicles, it is just a simple rotation. For wheeled vehicles, it performs a series *
+ * of short drives (three point turn) to face the desired direction. *
+ * *
+ * INPUT: dir -- The direction that this vehicle should face. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/29/1995 JLB : Created. *
+ *=============================================================================================*/
+void DriveClass::Do_Turn(DirType dir)
+{
+ assert(IsActive);
+
+ if (dir != PrimaryFacing) {
+
+#ifdef TOFIX
+ /*
+ ** Special rotation track is needed for units that
+ ** cannot rotate in place.
+ */
+ if (Special.IsThreePoint && TrackNumber == -1 && Techno_Type_Class()->Speed == SPEED_WHEEL) {
+ int facediff; // Signed difference between current and desired facing.
+ FacingType face; // Current facing (ordinal value).
+
+ facediff = PrimaryFacing.Difference(dir) >> 5;
+ facediff = Bound(facediff, -2, 2);
+ if (facediff) {
+ face = Dir_Facing(PrimaryFacing);
+
+ IsOnShortTrack = true;
+ Force_Track(face*FACING_COUNT + (face + facediff), Coord);
+
+ Path[0] = FACING_NONE;
+ Set_Speed(0xFF); // Full speed.
+ }
+ } else {
+ PrimaryFacing.Set_Desired(dir);
+ }
+#else
+ PrimaryFacing.Set_Desired(dir);
+// IsRotating = true;
+#endif
+ }
+}
+
+
+/***********************************************************************************************
+ * DriveClass::Teleport_To -- Teleport object to specified location. *
+ * *
+ * This will teleport the object to the specified location or as close as possible to it *
+ * if the destination is blocked. *
+ * *
+ * INPUT: cell -- The desired destination cell to teleport to. *
+ * *
+ * OUTPUT: bool; Was the teleport successful? *
+ * *
+ * WARNINGS: All current activity of this object will be terminated by the teleport. It will *
+ * arrive at the destination in static guard mode. *
+ * *
+ * HISTORY: *
+ * 10/21/1996 JLB : Created. *
+ * 10/31/1996 JLB : Handles flag teleport case. *
+ *=============================================================================================*/
+bool DriveClass::Teleport_To(CELL cell)
+{
+ /*
+ ** All cargo gets destroyed.
+ */
+ if (Rule.IsChronoKill) {
+ Kill_Cargo(NULL);
+ }
+
+ Stop_Driver();
+ Force_Track(-1, 0);
+ PrimaryFacing.Set_Current(PrimaryFacing.Desired());
+ Transmit_Message(RADIO_OVER_OUT);
+ Assign_Destination(TARGET_NONE);
+ Assign_Target(TARGET_NONE);
+ Assign_Mission(MISSION_NONE);
+ Commence();
+ Mark(MARK_UP);
+
+ /*
+ ** A teleported unit will drop the flag right where it's at.
+ */
+ if (What_Am_I() == RTTI_UNIT && ((UnitClass *)this)->Flagged != HOUSE_NONE) {
+ HouseClass::As_Pointer(((UnitClass *)this)->Flagged)->Flag_Attach(Coord_Cell(Coord));
+ }
+
+ if (Can_Enter_Cell(cell) != MOVE_OK) {
+ cell = Map.Nearby_Location(cell, Techno_Type_Class()->Speed);
+ }
+ Coord = Cell_Coord(cell);
+ Mark(MARK_DOWN);
+ return(true);
+}
+
+
+/***********************************************************************************************
+ * DriveClass::Force_Track -- Forces the unit to use the indicated track. *
+ * *
+ * This override (nuclear bomb) style routine is to be used when a unit needs to start *
+ * on a movement track but is outside the normal movement system. This occurs when a *
+ * harvester starts driving off of a refinery. *
+ * *
+ * INPUT: track -- The track number to start on. *
+ * *
+ * coord -- The coordinate that the unit will end up at when the movement track *
+ * is completed. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 03/17/1995 JLB : Created. *
+ *=============================================================================================*/
+void DriveClass::Force_Track(int track, COORDINATE coord)
+{
+ assert(IsActive);
+
+ TrackNumber = track;
+ TrackIndex = 0;
+ if (coord != 0) {
+ Start_Driver(coord);
+ }
+}
+
+
+/***********************************************************************************************
+ * DriveClass::DriveClass -- Constructor for drive class object. *
+ * *
+ * This will initialize the drive class to its default state. It is called as a result *
+ * of creating a unit. *
+ * *
+ * INPUT: classid -- The unit's ID class. It is passed on to the foot class constructor. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/13/1994 JLB : Created. *
+ *=============================================================================================*/
+DriveClass::DriveClass(RTTIType rtti, int id, HousesType house) :
+ FootClass(rtti, id, house),
+ IsMoebius(false),
+ IsHarvesting(false),
+ IsTurretLockedDown(false),
+ IsOnShortTrack(false),
+ SpeedAccum(0),
+ MoebiusCountDown(0),
+ MoebiusCell(0),
+ TrackNumber(-1),
+ TrackIndex(0)
+{
+}
+
+
+#ifdef CHEAT_KEYS
+/***********************************************************************************************
+ * DriveClass::Debug_Dump -- Displays status information to monochrome screen. *
+ * *
+ * This debug utility function will display the status of the drive class to the mono *
+ * screen. It is through this information that bugs can be tracked down. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/31/1994 JLB : Created. *
+ *=============================================================================================*/
+void DriveClass::Debug_Dump(MonoClass * mono) const
+{
+ assert(IsActive);
+
+ mono->Fill_Attrib(66, 14, 12, 1, IsMoebius ? MonoClass::INVERSE : MonoClass::NORMAL);
+ FootClass::Debug_Dump(mono);
+}
+#endif
+
+
+/***********************************************************************************************
+ * DriveClass::Smooth_Turn -- Handles the low level coord calc for smooth turn logic. *
+ * *
+ * This routine calculates the new coordinate value needed for the *
+ * smooth turn logic. The adjustment and flag values must be *
+ * determined prior to entering this routine. *
+ * *
+ * INPUT: adj -- The adjustment coordinate as lifted from the *
+ * correct smooth turn table. *
+ * *
+ * dir -- Pointer to dir for possible modification *
+ * according to the flag bits. *
+ * *
+ * OUTPUT: Returns with the coordinate the unit should positioned to. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 03/14/1994 JLB : Created. *
+ * 07/13/1994 JLB : Converted to member function. *
+ *=============================================================================================*/
+COORDINATE DriveClass::Smooth_Turn(COORDINATE adj, DirType & dir)
+{
+ assert(IsActive);
+
+ DirType workdir = dir;
+ int x,y;
+ int temp;
+ TrackControlType flags = TrackControl[TrackNumber].Flag;
+
+ x = Coord_X(adj);
+ y = Coord_Y(adj);
+
+ if (flags & F_T) {
+ temp = x;
+ x = y;
+ y = temp;
+ workdir = (DirType)(DIR_W - workdir);
+ }
+
+ if (flags & F_X) {
+ x = -x;
+ workdir = (DirType)-workdir;
+ }
+
+ if (flags & F_Y) {
+ y = -y;
+ workdir = (DirType)(DIR_S - workdir);
+ }
+
+ dir = workdir;
+
+ return(XY_Coord( (LEPTON)(Coord_X(Head_To_Coord()) + x), (LEPTON)(Coord_Y(Head_To_Coord()) + y)));
+}
+
+
+/***********************************************************************************************
+ * DriveClass::Assign_Destination -- Set the unit's NavCom. *
+ * *
+ * This routine is used to set the unit's navigation computer to the *
+ * specified target. Once the navigation computer is set, the unit *
+ * will start planning and moving toward the destination. *
+ * *
+ * INPUT: target -- The destination target for the unit to head to. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 09/07/1992 JLB : Created. *
+ * 04/15/1994 JLB : Converted to member function. *
+ *=============================================================================================*/
+void DriveClass::Assign_Destination(TARGET target)
+{
+ assert(IsActive);
+
+ /*
+ ** Abort early if there is anything wrong with the parameters
+ ** or the unit already is assigned the specified destination.
+ */
+ if (target == NavCom) return;
+
+ /*
+ ** For harvesting type vehicles, it might go into a dock and unload procedure
+ ** when the harvester is full and an empty refinery is selected as a target.
+ */
+ BuildingClass * b = As_Building(target);
+
+ /*
+ ** If the player clicked on refinery but it is not busy, then assign
+ ** it to unload at the refinery.
+ */
+ if (b != NULL && *b == STRUCT_REFINERY && What_Am_I() == RTTI_UNIT && ((UnitTypeClass *)Techno_Type_Class())->IsToHarvest) {
+ if (Contact_With_Whom() != b && !b->In_Radio_Contact()) {
+ /*
+ ** Establish radio contact protocol. If the facility responds correctly,
+ ** then remain in radio contact and proceed toward the desired destination.
+ */
+ if (Transmit_Message(RADIO_HELLO, b) == RADIO_ROGER) {
+ if (Mission != MISSION_ENTER && Mission != MISSION_HARVEST) {
+ Assign_Mission(MISSION_ENTER);
+ target = TARGET_NONE;
+ } else {
+// target = TARGET_NONE;
+ }
+ } else {
+// target = TARGET_NONE;
+ }
+ } else {
+// target = TARGET_NONE;
+ }
+ }
+
+ /*
+ ** Set the unit's navigation computer.
+ */
+ FootClass::Assign_Destination(target);
+
+ Path[0] = FACING_NONE; // Force recalculation of path.
+ if (!IsDriving && Mission != MISSION_UNLOAD) {
+ Start_Of_Move();
+ }
+}
+
+
+/***********************************************************************************************
+ * DriveClass::While_Moving -- Processes unit movement. *
+ * *
+ * This routine is used to process movement for the units as they move. *
+ * It is called many times for each cell's worth of movement. This *
+ * routine only applies after the next cell HeadTo has been determined. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: true/false; Should this routine be called again? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 02/02/1992 JLB : Created. *
+ * 04/15/1994 JLB : Converted to member function. *
+ *=============================================================================================*/
+bool DriveClass::While_Moving(void)
+{
+ assert(IsActive);
+
+ /*
+ ** Perform quick legality checks.
+ */
+ if (!IsDriving || TrackNumber == -1 || (IsRotating && !Techno_Type_Class()->IsTurretEquipped)) {
+ SpeedAccum = 0; // Kludge? No speed should accumulate if movement is on hold.
+ return(false);
+ }
+
+ /*
+ ** If enough movement has accumulated so that the unit can
+ ** visibly move on the map, then process accordingly.
+ ** Slow the unit down if he's carrying a flag.
+ */
+ MPHType maxspeed = MPHType(min(Techno_Type_Class()->MaxSpeed * SpeedBias * House->GroundspeedBias, (int)MPH_LIGHT_SPEED));
+ if (IsFormationMove) maxspeed = FormationMaxSpeed;
+
+ int actual; // Working movement addition value.
+ if (((UnitClass *)this)->Flagged != HOUSE_NONE) {
+ actual = SpeedAccum + ((int)maxspeed/2) * fixed(Speed, 256);
+ } else {
+ actual = SpeedAccum + maxspeed * fixed(Speed, 256);
+ }
+
+ if (actual > PIXEL_LEPTON_W) {
+ TurnTrackType const * track; // Track control pointer.
+ TrackType const * ptr; // Pointer to coord offset values.
+ int tracknum; // The track number being processed.
+ FacingType nextface; // Next facing queued in path.
+ bool adj; // Is a turn coming up?
+
+ track = &TrackControl[TrackNumber];
+ if (IsOnShortTrack) {
+ tracknum = track->StartTrack;
+ } else {
+ tracknum = track->Track;
+ }
+ ptr = RawTracks[tracknum-1].Track;
+ nextface = Path[0];
+
+ /*
+ ** Determine if there is a turn coming up. If there is
+ ** a turn, then track jumping might occur.
+ */
+ adj = false;
+ if (nextface != FACING_NONE && Dir_Facing(track->Facing) != nextface) {
+ adj = true;
+ }
+
+ /*
+ ** Skip ahead the number of track steps required (limited only
+ ** by track length). Set the unit to the new position and
+ ** flag the unit accordingly.
+ */
+ Mark(MARK_UP);
+ while (actual > PIXEL_LEPTON_W) {
+ COORDINATE offset;
+ DirType dir;
+
+ actual -= PIXEL_LEPTON_W;
+
+ offset = ptr[TrackIndex].Offset;
+ if (offset || !TrackIndex) {
+ dir = ptr[TrackIndex].Facing;
+ Coord = Smooth_Turn(offset, dir);
+
+ PrimaryFacing.Set(dir);
+
+ /*
+ ** See if "per cell" processing is necessary.
+ */
+ if (TrackIndex && RawTracks[tracknum-1].Cell == TrackIndex) {
+ Mark(MARK_DOWN);
+ Per_Cell_Process(PCP_DURING);
+ if (!IsActive) {
+ return(false);
+ }
+ Mark(MARK_UP);
+ }
+
+ /*
+ ** The unit could "jump tracks". Check to see if the unit should
+ ** do so.
+ */
+ if (/**this != UNIT_GUNBOAT &&*/ nextface != FACING_NONE && adj && RawTracks[tracknum-1].Jump == TrackIndex && TrackIndex) {
+ TurnTrackType const * newtrack; // Proposed jump-to track.
+ int tnum;
+
+ tnum = (int)(Dir_Facing(track->Facing) * FACING_COUNT) + (int)nextface;
+ newtrack = &TrackControl[tnum];
+ if (newtrack->Track && RawTracks[newtrack->Track-1].Entry) {
+ COORDINATE c = Head_To_Coord();
+ int oldspeed = Speed;
+
+ c = Adjacent_Cell(c, nextface);
+
+ switch (Can_Enter_Cell(Coord_Cell(c), nextface)) {
+ case MOVE_OK:
+ IsOnShortTrack = false; // Shouldn't be necessary, but...
+ TrackNumber = tnum;
+ track = newtrack;
+
+ tracknum = track->Track;
+ TrackIndex = RawTracks[tracknum-1].Entry-1; // Anticipate increment.
+ ptr = RawTracks[tracknum-1].Track;
+ adj = false;
+
+ Stop_Driver();
+ IsDriving = true;
+ Per_Cell_Process(PCP_END);
+ IsDriving = false;
+ if (!IsActive) return(false);
+ if (Start_Driver(c)) {
+ Set_Speed(oldspeed);
+ memcpy((char*)&Path[0], (char*)&Path[1], CONQUER_PATH_MAX-1);
+ Path[CONQUER_PATH_MAX-1] = FACING_NONE;
+ } else {
+ Path[0] = FACING_NONE;
+ TrackNumber = -1;
+ actual = 0;
+ }
+ break;
+
+ case MOVE_CLOAK:
+ Map[c].Shimmer();
+ break;
+
+ case MOVE_TEMP:
+#ifdef TOFIX
+ if (*this == UNIT_HARVESTER || !House->IsHuman) {
+#else
+ if (!House->IsHuman) {
+#endif
+ Map[c].Incoming(0, true, true);
+ }
+ break;
+ }
+ }
+ }
+ TrackIndex++;
+
+ } else {
+ actual = 0;
+ Coord = Head_To_Coord();
+ Stop_Driver();
+ TrackNumber = -1;
+ TrackIndex = NULL;
+
+ /*
+ ** Perform "per cell" activities.
+ */
+ Mark(MARK_DOWN);
+ Per_Cell_Process(PCP_END);
+ if (!IsActive) return(false);
+ Mark(MARK_UP);
+
+ break;
+ }
+ }
+ if (IsActive) {
+ Mark(MARK_DOWN);
+ }
+ }
+
+ /*
+ ** Replace any remainder back into the unit's movement
+ ** accumulator to be processed next pass.
+ */
+ SpeedAccum = actual;
+ return(true);
+}
+
+
+/***********************************************************************************************
+ * DriveClass::Per_Cell_Process -- Handles when unit finishes movement into a cell. *
+ * *
+ * This routine is called when a unit has mostly or completely *
+ * entered a cell. The unit might be in the middle of a movement track *
+ * when this routine is called. It's primary purpose is to perform *
+ * sighting and other "per cell" activities. *
+ * *
+ * INPUT: why -- Specifies the circumstances under which this routine was called. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 11/03/1993 JLB : Created. *
+ * 03/30/1994 JLB : Revamped for track system. *
+ * 04/15/1994 JLB : Converted to member function. *
+ * 06/18/1994 JLB : Converted to virtual function. *
+ * 06/18/1994 JLB : Distinguishes between center and near-center conditions. *
+ *=============================================================================================*/
+void DriveClass::Per_Cell_Process(PCPType why)
+{
+ assert(IsActive);
+
+ if (why == PCP_END) {
+ CELL cell = Coord_Cell(Coord);
+
+ /*
+ ** Check to see if it has reached its destination. If so, then clear the NavCom
+ ** regardless of the remaining path list.
+ */
+ if (As_Cell(NavCom) == cell) {
+ IsTurretLockedDown = false;
+ NavCom = TARGET_NONE;
+ Path[0] = FACING_NONE;
+ }
+
+ Lay_Track();
+ }
+
+ FootClass::Per_Cell_Process(why);
+}
+
+
+/***********************************************************************************************
+ * DriveClass::Start_Of_Move -- Tries to get a unit to advance toward cell. *
+ * *
+ * This will try to start a unit advancing toward the cell it is *
+ * facing. It will check for and handle legality and reserving of the *
+ * necessary cell. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: true/false; Should this routine be called again because *
+ * initial start operation is temporarily delayed? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 02/02/1992 JLB : Created. *
+ * 10/18/1993 JLB : This should be called repeatedly until HeadTo is not NULL. *
+ * 03/16/1994 JLB : Revamped for track logic. *
+ * 04/15/1994 JLB : Converted to member function. *
+ * 06/19/1995 JLB : Fixed so that it won't fire on ground unnecessarily. *
+ * 07/13/1995 JLB : Handles bumping into cloaked objects. *
+ * 09/22/1995 JLB : Breaks out of hopeless hunt mode. *
+ * 07/10/1996 JLB : Sets scan limit if necessary. *
+ *=============================================================================================*/
+bool DriveClass::Start_Of_Move(void)
+{
+ assert(IsActive);
+
+ FacingType facing; // Direction movement will commence.
+ DirType dir; // Desired actual facing toward destination.
+ int facediff; // Difference between current and desired facing.
+ int speed; // Speed of unit.
+ CELL destcell; // Cell of destination.
+ LandType ground; // Ground unit is entering.
+ COORDINATE dest; // Destination coordinate.
+
+ facing = Path[0];
+
+ if (!Target_Legal(NavCom) && facing == FACING_NONE) {
+ IsTurretLockedDown = false;
+ Stop_Driver();
+ if (Mission == MISSION_MOVE) {
+ Enter_Idle_Mode();
+ }
+ return(false); // Why is it calling this routine!?!
+ }
+
+ /*
+ ** Reduce the path length if the target is a unit and the
+ ** range to the unit is less than the precalculated path steps.
+ */
+ if (facing != FACING_NONE) {
+ int dist;
+
+ if (Is_Target_Vessel(NavCom) || Is_Target_Unit(NavCom) || Is_Target_Infantry(NavCom)) {
+ dist = Lepton_To_Cell((LEPTON)Distance(NavCom));
+
+ if (dist < ARRAY_SIZE(Path)) {
+ Path[dist] = FACING_NONE;
+ facing = Path[0]; // Maybe needed.
+ }
+ }
+ }
+
+ /*
+ ** If the path is invalid at this point, then generate one. If
+ ** generating a new path fails, then abort NavCom.
+ */
+ if (facing == FACING_NONE) {
+
+ /*
+ ** If after a path search, there is still no valid path, then set the
+ ** NavCom to null and let the script take care of assigning a new
+ ** navigation target.
+ */
+ if (PathDelay != 0) {
+ return(false);
+ }
+
+ if (!Basic_Path()) {
+
+ /*
+ ** If the unit is close enough to the target then just stop
+ ** driving now. This prevents the fidgeting that would occur
+ ** if they mindlessly kept trying to get to the exact location
+ ** desired. This is quite necessary since it is typical to move
+ ** several units with the same mouse click.
+ */
+ if (!Is_On_Priority_Mission() && Distance(NavCom) < Rule.CloseEnoughDistance && (Mission == MISSION_MOVE || Mission == MISSION_GUARD_AREA)) {
+ Assign_Destination(TARGET_NONE);
+ if (!IsActive) return(false);
+ } else {
+ /*
+ ** If a basic path could not be found, but the immediate move destination is
+ ** blocked by a friendly temporary blockage, then cause that blockage
+ ** to scatter.
+ */
+ CELL cell = Adjacent_Cell(Coord_Cell(Center_Coord()), PrimaryFacing.Current());
+ if (Map.In_Radar(cell)) {
+ MoveType ok = Can_Enter_Cell(cell);
+ if (ok == MOVE_TEMP) {
+ CellClass * cellptr = &Map[cell];
+ TechnoClass * blockage = cellptr->Cell_Techno();
+ if (blockage && House->Is_Ally(blockage)) {
+
+ /*
+ ** If the target can be told to get out of the way, only bother
+ ** to do so if we aren't very close to the target and this
+ ** object can just say "good enough" and stop here.
+ */
+ if (Distance(NavCom) < Rule.CloseEnoughDistance && !In_Radio_Contact()) {
+ Assign_Destination(TARGET_NONE);
+ return(false);
+ } else {
+ cellptr->Incoming(0, true, false);
+// cellptr->Incoming(0, true, true);
+ }
+ }
+ }
+ }
+
+ if (TryTryAgain > 0) {
+ TryTryAgain--;
+ } else {
+ Assign_Destination(TARGET_NONE);
+ if (!IsActive) return(false);
+ if (IsNewNavCom) Sound_Effect(VOC_SCOLD);
+ IsNewNavCom = false;
+ }
+ }
+
+ /*
+ ** Since the path was blocked, check to make sure that it was completely
+ ** blocked. If so and it has a valid TarCom and it is out of range of the
+ ** TarCom, then give this unit a range limit so that it might not pick
+ ** a "can't reach" target again.
+ */
+ if (!Target_Legal(NavCom) && Target_Legal(TarCom) && !In_Range(TarCom)) {
+ IsScanLimited = true;
+ if (Team.Is_Valid()) Team->Scan_Limit();
+ Assign_Target(TARGET_NONE);
+ }
+
+ /*
+ ** Stop the movement, for now, and let the subsequent logic in later game
+ ** frames resume movement as appropriate.
+ */
+ Stop_Driver();
+ TrackNumber = -1;
+ IsTurretLockedDown = false;
+ return(false);
+ }
+
+ /*
+ ** If a basic path could be found, but the immediate move destination is
+ ** blocked by a friendly temporary blockage, then cause that blockage
+ ** to scatter.
+ */
+ CELL cell = Adjacent_Cell(Coord_Cell(Center_Coord()), Path[0]);
+ if (Map.In_Radar(cell)) {
+ MoveType ok = Can_Enter_Cell(cell);
+ if (ok == MOVE_TEMP) {
+ CellClass * cellptr = &Map[cell];
+ TechnoClass * blockage = cellptr->Cell_Techno();
+ if (blockage && House->Is_Ally(blockage)) {
+
+ /*
+ ** If the target can be told to get out of the way, only bother
+ ** to do so if we aren't very close to the target and this
+ ** object can just say "good enough" and stop here.
+ */
+ if (Distance(NavCom) < Rule.CloseEnoughDistance && !In_Radio_Contact()) {
+ Assign_Destination(TARGET_NONE);
+ return(false);
+ } else {
+ cellptr->Incoming(0, true, false);
+// cellptr->Incoming(0, true, true);
+ }
+ }
+ }
+ }
+
+ TryTryAgain = PATH_RETRY;
+ facing = Path[0];
+ }
+
+ /*
+ ** Determine the coordinate of the next cell to move into.
+ */
+ dest = Adjacent_Cell(Coord, facing);
+ dir = Facing_Dir(facing);
+
+ /*
+ ** Set the facing correctly if it isn't already correct. This
+ ** means starting a rotation track if necessary.
+ */
+ facediff = PrimaryFacing.Difference(dir);
+ if (facediff) {
+
+ /*
+ ** Request a change of facing.
+ */
+ Do_Turn(dir);
+ return(true);
+
+ } else {
+
+ /* NOTE: Beyond this point, actual track assignment can begin.
+ **
+ ** If the cell to move into is impassable (probably for some unexpected
+ ** reason), then abort the path list and set the speed to zero. The
+ ** next time this routine is called, a new path will be generated.
+ */
+ destcell = Coord_Cell(dest);
+ Mark(MARK_UP);
+ MoveType cando = Can_Enter_Cell(destcell, facing);
+ Mark(MARK_DOWN);
+
+ if (cando != MOVE_OK) {
+
+ if (Mission == MISSION_MOVE /*KO&& House->IsHuman */&& Distance(NavCom) < Rule.CloseEnoughDistance) {
+ Assign_Destination(TARGET_NONE);
+ if (!IsActive) return(false);//BG
+ }
+
+ /*
+ ** If a temporary friendly object is blocking the path, then cause it to
+ ** get out of the way.
+ */
+ if (cando == MOVE_TEMP) {
+ Map[destcell].Incoming(0, true, true);
+ }
+
+ /*
+ ** If a cloaked object is blocking, then shimmer the cell.
+ */
+ if (cando == MOVE_CLOAK) {
+ Map[destcell].Shimmer();
+ }
+
+ Stop_Driver();
+ if (cando != MOVE_MOVING_BLOCK) {
+ Path[0] = FACING_NONE; // Path is blocked!
+ }
+
+ /*
+ ** If blocked by a moving block then just exit start of move and
+ ** try again next tick.
+ */
+ if (cando == MOVE_DESTROYABLE) {
+ if (Map[destcell].Cell_Object()) {
+ if (!House->Is_Ally(Map[destcell].Cell_Object())) {
+ Override_Mission(MISSION_ATTACK, Map[destcell].Cell_Object()->As_Target(), TARGET_NONE);
+ }
+ } else {
+ if (Map[destcell].Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Map[destcell].Overlay).IsWall) {
+ Override_Mission(MISSION_ATTACK, ::As_Target(destcell), TARGET_NONE);
+ }
+ }
+ } else {
+ if (IsNewNavCom) Sound_Effect(VOC_SCOLD);
+ }
+ IsNewNavCom = false;
+ TrackNumber = -1;
+ return(true);
+ }
+
+ /*
+ ** Determine the speed that the unit can travel to the desired square.
+ */
+ ground = Map[destcell].Land_Type();
+ speed = Ground[ground].Cost[Techno_Type_Class()->Speed] * 256;
+
+ /* change speed if it's related to a team move */
+ if (IsFormationMove) speed = Ground[ground].Cost[FormationSpeed] * 256;
+ if (!speed) speed = 128;
+
+#ifdef NEVER
+ /*
+ ** Set the jiggle flag if the terrain would cause the unit
+ ** to jiggle when travelled over.
+ */
+ BaseF &= ~BASEF_JIGGLE;
+ if (Ground[ground].Jiggle) {
+ BaseF |= BASEF_JIGGLE;
+ }
+#endif
+
+ /*
+ ** A damaged unit has a reduced speed.
+ */
+ if (Health_Ratio() <= Rule.ConditionYellow /*(Techno_Type_Class()->MaxStrength>>1) > Strength*/) {
+ speed -= (speed/4); // Three quarters speed.
+ }
+ if ((speed != Speed)/* || !SpeedAdd*/) {
+ Set_Speed(speed); // Full speed.
+ }
+
+ /*
+ ** Reserve the destination cell so that it won't become
+ ** occupied AS this unit is moving into it.
+ */
+ if (cando != MOVE_OK) {
+ Path[0] = FACING_NONE; // Path is blocked!
+ TrackNumber = -1;
+ dest = NULL;
+ } else {
+
+ Overrun_Square(Coord_Cell(dest), true);
+
+ /*
+ ** Determine which track to use (based on recorded path).
+ */
+ FacingType nextface = Path[1];
+ if (nextface == FACING_NONE) nextface = facing;
+
+ IsOnShortTrack = false;
+ TrackNumber = facing * FACING_COUNT + (int)nextface;
+ if (TrackControl[TrackNumber].Track == 0) {
+ Path[0] = FACING_NONE;
+ TrackNumber = -1;
+ return(true);
+ } else {
+ if (TrackControl[TrackNumber].Flag & F_D) {
+ /*
+ ** If the middle cell of a two cell track contains a crate,
+ ** the check for goodies before movement starts.
+ */
+ if (!Map[destcell].Goodie_Check(this)) {
+ cando = MOVE_NO;
+ if (!IsActive) return(false);
+ } else {
+ if (!IsActive) return(false);
+ dest = Adjacent_Cell(dest, nextface);
+ destcell = Coord_Cell(dest);
+ cando = Can_Enter_Cell(destcell);
+ }
+ if (!IsActive) return(false);
+
+ if (cando != MOVE_OK) {
+
+ /*
+ ** If a temporary friendly object is blocking the path, then cause it to
+ ** get out of the way.
+ */
+ if (cando == MOVE_TEMP) {
+ Map[destcell].Incoming(0, true, true);
+ }
+
+ /*
+ ** If a cloaked object is blocking, then shimmer the cell.
+ */
+ if (cando == MOVE_CLOAK) {
+ Map[destcell].Shimmer();
+ }
+
+ Path[0] = FACING_NONE; // Path is blocked!
+ TrackNumber = -1;
+ dest = NULL;
+ if (cando == MOVE_DESTROYABLE) {
+
+ if (Map[destcell].Cell_Object()) {
+ if (!House->Is_Ally(Map[destcell].Cell_Object())) {
+ Override_Mission(MISSION_ATTACK, Map[destcell].Cell_Object()->As_Target(), TARGET_NONE);
+ }
+ } else {
+ if (Map[destcell].Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Map[destcell].Overlay).IsWall) {
+ Override_Mission(MISSION_ATTACK, ::As_Target(destcell), TARGET_NONE);
+ }
+ }
+ IsNewNavCom = false;
+ TrackIndex = 0;
+ return(true);
+ }
+ } else {
+ memcpy((char*)&Path[0], (char*)&Path[2], CONQUER_PATH_MAX-2);
+ Path[CONQUER_PATH_MAX-2] = FACING_NONE;
+ IsPlanningToLook = true;
+ }
+ } else {
+ memcpy((char*)&Path[0], (char*)&Path[1], CONQUER_PATH_MAX-1);
+ }
+ Path[CONQUER_PATH_MAX-1] = FACING_NONE;
+ }
+ }
+
+ IsNewNavCom = false;
+ TrackIndex = 0;
+ if (!Start_Driver(dest)) {
+ TrackNumber = -1;
+ Path[0] = FACING_NONE;
+ Set_Speed(0);
+ }
+ }
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * DriveClass::AI -- Processes unit movement and rotation. *
+ * *
+ * This routine is used to process unit movement and rotation. It *
+ * functions autonomously from the script system. Thus, once a unit *
+ * is give rotation command or movement path, it will follow this *
+ * until specifically instructed to stop. The advantage of this *
+ * method is that it allows smooth movement of units, faster game *
+ * execution, and reduced script complexity (since actual movement *
+ * dynamics need not be controlled directly by the scripts). *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: This routine relies on the process control bits for the *
+ * specified unit (for speed reasons). Thus, only setting *
+ * movement, rotation, or path list will the unit perform *
+ * any physics. *
+ * *
+ * HISTORY: *
+ * 09/26/1993 JLB : Created. *
+ * 04/15/1994 JLB : Converted to member function. *
+ *=============================================================================================*/
+void DriveClass::AI(void)
+{
+ assert(IsActive);
+
+ FootClass::AI();
+ if (!IsActive || Height > 0) return;
+
+ /*
+ ** Is this a unit that's been teleported using the chronosphere, and if so,
+ ** has his timer expired such that he needs to teleport back?
+ */
+ if (IsMoebius) {
+#ifdef FIXIT_CSII // checked - ajw 9/28/98
+ if (What_Am_I() != RTTI_UNIT || ((UnitClass *)this)->Class->Type != UNIT_CHRONOTANK) {
+#endif
+ if (MoebiusCountDown == 0) {
+ IsMoebius = false;
+ Teleport_To(MoebiusCell);
+ MoebiusCell = 0;
+ }
+#ifdef FIXIT_CSII // checked - ajw 9/28/98
+ }
+#endif
+ }
+
+ /*
+ ** If the unit is following a track, then continue
+ ** to do so -- mindlessly.
+ */
+ if (TrackNumber != -1) {
+
+ /*
+ ** Perform the movement accumulation.
+ */
+ While_Moving();
+ if (!IsActive) return;
+ if (TrackNumber == -1 && (Target_Legal(NavCom) || Path[0] != FACING_NONE) && (What_Am_I() != RTTI_UNIT || !((UnitClass*)this)->IsDumping)) {
+ Start_Of_Move();
+ if (!IsActive) return;
+ While_Moving();
+ if (!IsActive) return;
+ }
+
+ } else {
+
+ /*
+ ** For tracked units that are rotating in place, perform the rotation now.
+ */
+#ifdef TOFIX
+ if ((Class->Speed == SPEED_FLOAT || Class->Speed == SPEED_HOVER || Class->Speed == SPEED_TRACK || (Class->Speed == SPEED_WHEEL && !Special.IsThreePoint)) && PrimaryFacing.Is_Rotating()) {
+ if (PrimaryFacing.Rotation_Adjust(Class->ROT)) {
+ Mark(MARK_CHANGE);
+ }
+#else
+ if (PrimaryFacing.Is_Rotating()) {
+ Mark(MARK_CHANGE_REDRAW);
+ if (PrimaryFacing.Rotation_Adjust(Techno_Type_Class()->ROT * House->GroundspeedBias)) {
+ Mark(MARK_CHANGE_REDRAW);
+ }
+#endif
+ if (!IsRotating) {
+ Per_Cell_Process(PCP_ROTATION);
+ if (!IsActive) return;
+ }
+
+ } else {
+
+ /*
+ ** The unit has no track to follow, but if there
+ ** is a navigation target or a remaining path,
+ ** then start on a new track.
+ */
+ if ((Mission != MISSION_GUARD || Target_Legal(NavCom)) && Mission != MISSION_UNLOAD) {
+ if (Target_Legal(NavCom) || Path[0] != FACING_NONE) {
+
+ /*
+ ** Double check to make sure that the movement destination is
+ ** in a zone that this unit can travel to. If not, then abort
+ ** the navigation target.
+ */
+ if (IsLocked && Mission != MISSION_ENTER && Target_Legal(NavCom) && !Is_In_Same_Zone(As_Cell(NavCom))) {
+ Stop_Driver();
+ Assign_Destination(TARGET_NONE);
+ } else {
+ Start_Of_Move();
+ if (!IsActive) return;
+ While_Moving();
+ if (!IsActive) return;
+ }
+ } else {
+ Stop_Driver();
+ }
+ }
+ }
+ }
+}
+
+
+/***********************************************************************************************
+ * DriveClass::Fixup_Path -- Adds smooth start path to normal movement path. *
+ * *
+ * This routine modifies the path of the specified unit so that it *
+ * will not start out with a rotation. This is necessary for those *
+ * vehicles that have difficulty with rotating in place. Typically, *
+ * this includes wheeled vehicles. *
+ * *
+ * INPUT: unit -- Pointer to the unit to adjust. *
+ * *
+ * path -- Pointer to path structure. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: Only units that require a fixup get modified. The *
+ * modification only occurs, if there is a legal path to *
+ * do so. *
+ * *
+ * HISTORY: *
+ * 04/03/1994 JLB : Created. *
+ * 04/06/1994 JLB : Uses path structure. *
+ * 04/10/1994 JLB : Diagonal smooth turn added. *
+ * 04/15/1994 JLB : Converted to member function. *
+ *=============================================================================================*/
+void DriveClass::Fixup_Path(PathType * path)
+{
+ assert(IsActive);
+
+ FacingType stage[6]={FACING_N,FACING_N,FACING_N,FACING_N,FACING_N,FACING_N}; // Prefix path elements.
+ int facediff; // The facing difference value (0..4 | 0..-4).
+ static FacingType _path[4][6] = {
+ {(FacingType)2,(FacingType)0,(FacingType)2,(FacingType)0,(FacingType)0,(FacingType)0},
+ {(FacingType)3,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)0,(FacingType)0},
+ {(FacingType)4,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)0,(FacingType)0},
+ {(FacingType)4,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)1,(FacingType)0}
+ };
+ static FacingType _dpath[4][6] = {
+ {(FacingType)0,(FacingType)0,(FacingType)0,(FacingType)0,(FacingType)0,(FacingType)0},
+ {(FacingType)3,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)0,(FacingType)0},
+ {(FacingType)4,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)1,(FacingType)0},
+ {(FacingType)5,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)1,(FacingType)0}
+ };
+
+ int index;
+ int counter; // Path addition
+ FacingType * ptr; // Path list pointer.
+ FacingType * ptr2; // Copy of new path list pointer.
+ FacingType nextpath; // Next path value.
+ CELL cell; // Working cell value.
+ bool ok;
+
+ /*
+ ** Verify that the unit is valid and there is a path problem to resolve.
+ */
+ if (!path || path->Command[0] == FACING_NONE) {
+ return;
+ }
+
+ /*
+ ** Only wheeled vehicles need a path fixup -- to avoid 3 point turns.
+ */
+#ifdef TOFIX
+ if (!Special.IsThreePoint || Class->Speed != SPEED_WHEEL) {
+#else
+ if (What_Am_I() == RTTI_UNIT || What_Am_I() == RTTI_VESSEL) {
+// if (What_Am_I() == RTTI_UNIT) {
+#endif
+ return;
+ }
+
+ /*
+ ** If the original path starts in the same direction as the unit, then
+ ** there is no problem to resolve -- abort.
+ */
+ facediff = PrimaryFacing.Difference((DirType)(path->Command[0]<<5)) >> 5;
+
+ if (!facediff) return;
+
+ if (Dir_Facing(PrimaryFacing) & FACING_NE) {
+ ptr = &_dpath[(FacingType)ABS((int)facediff)-FACING_NE][1]; // Pointer to path adjust list.
+ counter = (int)_dpath[(FacingType)ABS((int)facediff)-FACING_NE][0]; // Number of path adjusts.
+ } else {
+ ptr = &_path[(FacingType)ABS((int)facediff)-FACING_NE][1]; // Pointer to path adjust list.
+ counter = (int)_path[(FacingType)ABS((int)facediff)-FACING_NE][0]; // Number of path adjusts.
+ }
+ ptr2 = ptr;
+
+ ok = true; // Presume adjustment is all ok.
+ cell = Coord_Cell(Coord); // Starting cell.
+ nextpath = Dir_Facing(PrimaryFacing); // Starting path.
+ for (index = 0; index < counter; index++) {
+
+ /*
+ ** Determine next path element and add it to the
+ ** working path list.
+ */
+ if (facediff > 0) {
+ nextpath = nextpath + *ptr++;
+ } else {
+ nextpath = nextpath - *ptr++;
+ }
+ stage[index] = nextpath;
+ cell = Adjacent_Cell(cell, nextpath);
+ //cell = Coord_Cell(Adjacent_Cell(Cell_Coord(cell), nextpath));
+
+ /*
+ ** If it can't enter this cell, then abort the path
+ ** building operation without adjusting the unit's
+ ** path.
+ */
+ if (Can_Enter_Cell(cell, nextpath) != MOVE_OK) {
+ ok = false;
+ break;
+ }
+ }
+
+ /*
+ ** If veering to the left was not successful, then try veering
+ ** to the right. This only makes sense if the vehicle is trying
+ ** to turn 180 degrees.
+ */
+ if (!ok && ABS(facediff) == 4) {
+ ptr = ptr2; // Pointer to path adjust list.
+ facediff = -facediff;
+ ok = true; // Presume adjustment is all ok.
+ cell = Coord_Cell(Coord); // Starting cell.
+ nextpath = Dir_Facing(PrimaryFacing); // Starting path.
+ for (index = 0; index < counter; index++) {
+
+ /*
+ ** Determine next path element and add it to the
+ ** working path list.
+ */
+ if (facediff > 0) {
+ nextpath = nextpath + *ptr++;
+ } else {
+ nextpath = nextpath - *ptr++;
+ }
+ stage[index] = nextpath;
+ cell = Coord_Cell(Adjacent_Cell(Cell_Coord(cell), nextpath));
+
+ /*
+ ** If it can't enter this cell, then abort the path
+ ** building operation without adjusting the unit's
+ ** path.
+ */
+ if (Can_Enter_Cell(cell, nextpath) != MOVE_OK) {
+ ok = false;
+ break;
+ }
+ }
+ }
+
+ /*
+ ** If a legal path addition was created, then install it in place
+ ** of the first path value. The initial path entry is to be replaced
+ ** with a sequence of path entries that create smooth turning.
+ */
+ if (ok) {
+ if (path->Length <= 1) {
+ memmove((char *)&stage[0], (char*)path->Command, max(counter, 1));
+ path->Length = counter;
+ } else {
+
+ /*
+ ** Optimize the transition path step from the smooth turn
+ ** first part as it joins with the rest of the normal
+ ** path. The normal prefix path steps are NOT to be optimized.
+ */
+ if (counter) {
+ counter--;
+ path->Command[0] = stage[counter];
+ Optimize_Moves(path, MOVE_OK);
+ }
+
+ /*
+ ** If there is more than one prefix path element, then
+ ** insert the rest now.
+ */
+ if (counter) {
+ memmove((char*)&path->Command[0], (char*)&path->Command[counter], 40-counter);
+ memmove((char*)&stage[0], (char*)&path->Command[0], counter);
+ path->Length += counter;
+ }
+ }
+ path->Command[path->Length] = FACING_NONE;
+ }
+}
+
+
+/***********************************************************************************************
+ * DriveClass::Lay_Track -- Handles track laying logic for the unit. *
+ * *
+ * This routine handles the track laying for the unit. This entails examining the unit's *
+ * current location as well as the direction and whether this unit is allowed to lay *
+ * tracks in the first place. *
+ * *
+ * INPUT: none *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 05/28/1994 JLB : Created. *
+ *=============================================================================================*/
+void DriveClass::Lay_Track(void)
+{
+ assert(IsActive);
+
+#ifdef NEVER
+ static IconCommandType * _trackdirs[8] = {
+ TrackN_S,
+ TrackNE_SW,
+ TrackE_W,
+ TrackNW_SE,
+ TrackN_S,
+ TrackNE_SW,
+ TrackE_W,
+ TrackNW_SE
+ };
+
+ if (!(ClassF & CLASSF_TRACKS)) return;
+
+ Icon_Install(Coord_Cell(Coord), _trackdirs[Facing_To_8(BodyFacing)]);
+#endif
+}
+
+
+/***********************************************************************************************
+ * DriveClass::Mark_Track -- Marks the midpoint of the track as occupied. *
+ * *
+ * This routine will ensure that the midpoint (if any) of the track that the unit is *
+ * following, will be marked according to the mark type specified. *
+ * *
+ * INPUT: headto -- The head to coordinate. *
+ * *
+ * type -- The type of marking to perform. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/30/1995 JLB : Created. *
+ *=============================================================================================*/
+void DriveClass::Mark_Track(COORDINATE headto, MarkType type)
+{
+ assert(IsActive);
+
+ int value;
+
+ if (type == MARK_UP) {
+ value = false;
+ } else {
+ value = true;
+ }
+
+ if (headto) {
+ if (!IsOnShortTrack && TrackNumber != -1) {
+
+ /*
+ ** If we have not passed the per cell process point we need
+ ** to deal with it.
+ */
+ int tracknum = TrackControl[TrackNumber].Track;
+ if (tracknum) {
+ TrackType const * ptr = RawTracks[tracknum - 1].Track;
+ int cellidx = RawTracks[tracknum - 1].Cell;
+ if (cellidx > -1) {
+ DirType dir = ptr[cellidx].Facing;
+
+ if (TrackIndex < cellidx && cellidx != -1) {
+ COORDINATE offset = Smooth_Turn(ptr[cellidx].Offset, dir);
+ Map[offset].Flag.Occupy.Vehicle = value;
+ }
+ }
+ }
+ }
+ Map[headto].Flag.Occupy.Vehicle = value;
+ }
+}
+
+
+/***********************************************************************************************
+ * DriveClass::Ok_To_Move -- Checks to see if this object can begin moving. *
+ * *
+ * This routine is used to verify that this object is allowed to move. Some objects can *
+ * be temporarily occupied and thus cannot move until the situation permits. *
+ * *
+ * INPUT: direction -- The direction that movement would be desired. *
+ * *
+ * OUTPUT: Can the unit move in the direction specified? *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 07/29/1995 JLB : Created. *
+ *=============================================================================================*/
+bool DriveClass::Ok_To_Move(DirType ) const
+{
+ assert(IsActive);
+
+ return true;
+}
+
+
+/***************************************************************************
+** Smooth turn track tables. These are coordinate offsets from the center
+** of the destination cell. These are the raw tracks that are modified
+** by negating the X and Y portions as necessary. Also for reverse travelling
+** direction, the track list can be processed backward.
+**
+** Track 1 = N
+** Track 2 = NE
+** Track 3 = N->NE 45 deg (double path consumption)
+** Track 4 = N->E 90 deg (double path consumption)
+** Track 5 = NE->SE 90 deg (double path consumption)
+** Track 6 = NE->N 45 deg (double path consumption)
+** Track 7 = N->NE (facing change only)
+** Track 8 = NE->E (facing change only)
+** Track 9 = N->E (facing change only)
+** Track 10= NE->SE (facing change only)
+** Track 11= back up into refinery
+** Track 12= drive out of refinery
+*/
+#pragma warn -ias
+DriveClass::TrackType const DriveClass::Track1[24] = {
+ {0x00F50000L,(DirType)0},
+ {0x00EA0000L,(DirType)0},
+ {0x00DF0000L,(DirType)0},
+ {0x00D40000L,(DirType)0},
+ {0x00C90000L,(DirType)0},
+ {0x00BE0000L,(DirType)0},
+ {0x00B30000L,(DirType)0},
+ {0x00A80000L,(DirType)0},
+ {0x009D0000L,(DirType)0},
+ {0x00920000L,(DirType)0},
+ {0x00870000L,(DirType)0},
+ {0x007C0000L,(DirType)0}, // Track jump check here.
+ {0x00710000L,(DirType)0},
+ {0x00660000L,(DirType)0},
+ {0x005B0000L,(DirType)0},
+ {0x00500000L,(DirType)0},
+ {0x00450000L,(DirType)0},
+ {0x003A0000L,(DirType)0},
+ {0x002F0000L,(DirType)0},
+ {0x00240000L,(DirType)0},
+ {0x00190000L,(DirType)0},
+ {0x000E0000L,(DirType)0},
+ {0x00030000L,(DirType)0},
+ {0x00000000L,(DirType)0}
+};
+
+DriveClass::TrackType const DriveClass::Track2[] = {
+ {0x00F8FF08L,(DirType)32},
+ {0x00F0FF10L,(DirType)32},
+ {0x00E8FF18L,(DirType)32},
+ {0x00E0FF20L,(DirType)32},
+ {0x00D8FF28L,(DirType)32},
+ {0x00D0FF30L,(DirType)32},
+ {0x00C8FF38L,(DirType)32},
+ {0x00C0FF40L,(DirType)32},
+ {0x00B8FF48L,(DirType)32},
+ {0x00B0FF50L,(DirType)32},
+ {0x00A8FF58L,(DirType)32},
+ {0x00A0FF60L,(DirType)32},
+ {0x0098FF68L,(DirType)32},
+ {0x0090FF70L,(DirType)32},
+ {0x0088FF78L,(DirType)32},
+ {0x0080FF80L,(DirType)32}, // Track jump check here.
+ {0x0078FF88L,(DirType)32},
+ {0x0070FF90L,(DirType)32},
+ {0x0068FF98L,(DirType)32},
+ {0x0060FFA0L,(DirType)32},
+ {0x0058FFA8L,(DirType)32},
+ {0x0050FFB0L,(DirType)32},
+ {0x0048FFB8L,(DirType)32},
+ {0x0040FFC0L,(DirType)32},
+ {0x0038FFC8L,(DirType)32},
+ {0x0030FFD0L,(DirType)32},
+ {0x0028FFD8L,(DirType)32},
+ {0x0020FFE0L,(DirType)32},
+ {0x0018FFE8L,(DirType)32},
+ {0x0010FFF0L,(DirType)32},
+ {0x0008FFF8L,(DirType)32},
+ {0x00000000L,(DirType)32}
+};
+
+DriveClass::TrackType const DriveClass::Track3[] = {
+ {0x01F5FF00L,(DirType)0},
+ {0x01EAFF00L,(DirType)0},
+ {0x01DFFF00L,(DirType)0},
+ {0x01D4FF00L,(DirType)0},
+ {0x01C9FF00L,(DirType)0},
+ {0x01BEFF00L,(DirType)0},
+ {0x01B3FF00L,(DirType)0},
+ {0x01A8FF00L,(DirType)0},
+ {0x019DFF00L,(DirType)0},
+ {0x0192FF00L,(DirType)0},
+ {0x0187FF00L,(DirType)0},
+ {0x0180FF00L,(DirType)0},
+ {0x0175FF00L,(DirType)0}, // Jump entry point here.
+ {0x016BFF00L,(DirType)0},
+ {0x0160FF02L,(DirType)1},
+ {0x0155FF04L,(DirType)3},
+ {0x014CFF06L,(DirType)4},
+ {0x0141FF08L,(DirType)5},
+ {0x0137FF0BL,(DirType)7},
+ {0x012EFF0FL,(DirType)8},
+ {0x0124FF13L,(DirType)9},
+ {0x011AFF17L,(DirType)11},
+ {0x0110FF1BL,(DirType)12},
+ {0x0107FF1FL,(DirType)13}, // Center cell processing here.
+ {0x00FCFF24L,(DirType)15},
+ {0x00F3FF28L,(DirType)16},
+ {0x00ECFF2CL,(DirType)17},
+ {0x00E0FF32L,(DirType)19},
+ {0x00D7FF36L,(DirType)20},
+ {0x00CFFF3DL,(DirType)21},
+ {0x00C6FF42L,(DirType)23},
+ {0x00BAFF49L,(DirType)24},
+ {0x00B0FF4DL,(DirType)25},
+ {0x00A8FF58L,(DirType)27},
+ {0x00A0FF60L,(DirType)28},
+ {0x0098FF68L,(DirType)29},
+ {0x0090FF70L,(DirType)31},
+ {0x0088FF78L,(DirType)32},
+ {0x0080FF80L,(DirType)32}, // Track jump check here.
+ {0x0078FF88L,(DirType)32},
+ {0x0070FF90L,(DirType)32},
+ {0x0068FF98L,(DirType)32},
+ {0x0060FFA0L,(DirType)32},
+ {0x0058FFA8L,(DirType)32},
+ {0x0050FFB0L,(DirType)32},
+ {0x0048FFB8L,(DirType)32},
+ {0x0040FFC0L,(DirType)32},
+ {0x0038FFC8L,(DirType)32},
+ {0x0030FFD0L,(DirType)32},
+ {0x0028FFD8L,(DirType)32},
+ {0x0020FFE0L,(DirType)32},
+ {0x0018FFE8L,(DirType)32},
+ {0x0010FFF0L,(DirType)32},
+ {0x0008FFF8L,(DirType)32},
+ {0x00000000L,(DirType)32}
+};
+
+DriveClass::TrackType const DriveClass::Track4[] = {
+ {0x00F5FF00L,(DirType)0},
+ {0x00EBFF00L,(DirType)0},
+ {0x00E0FF00L,(DirType)0},
+ {0x00D5FF00L,(DirType)0},
+ {0x00CBFF01L,(DirType)0},
+ {0x00C0FF03L,(DirType)0},
+ {0x00B5FF05L,(DirType)1},
+ {0x00ABFF07L,(DirType)1},
+ {0x00A0FF0AL,(DirType)2},
+ {0x0095FF0DL,(DirType)3},
+ {0x008BFF10L,(DirType)4},
+ {0x0080FF14L,(DirType)5}, // Track entry here.
+ {0x0075FF18L,(DirType)8},
+ {0x006DFF1CL,(DirType)12},
+ {0x0063FF22L,(DirType)16},
+ {0x005AFF25L,(DirType)20},
+ {0x0052FF2BL,(DirType)23},
+ {0x0048FF32L,(DirType)27},
+ {0x0040FF37L,(DirType)32},
+ {0x0038FF3DL,(DirType)36},
+ {0x0030FF46L,(DirType)39},
+ {0x002BFF4FL,(DirType)43},
+ {0x0024FF58L,(DirType)47},
+ {0x0020FF60L,(DirType)51},
+ {0x001BFF6DL,(DirType)54},
+ {0x0017FF79L,(DirType)57},
+ {0x0014FF82L,(DirType)60}, // Track jump here.
+ {0x0011FF8FL,(DirType)62},
+ {0x000DFF98L,(DirType)63},
+ {0x0009FFA2L,(DirType)64},
+ {0x0006FFACL,(DirType)64},
+ {0x0004FFB5L,(DirType)66},
+ {0x0003FFC0L,(DirType)64},
+ {0x0002FFCBL,(DirType)64},
+ {0x0001FFD5L,(DirType)64},
+ {0x0000FFE0L,(DirType)64},
+ {0x0000FFEBL,(DirType)64},
+ {0x0000FFF5L,(DirType)64},
+ {0x00000000L,(DirType)64}
+};
+
+DriveClass::TrackType const DriveClass::Track5[] = {
+ {0xFFF8FE08L,(DirType)32},
+ {0xFFF0FE10L,(DirType)32},
+ {0xFFE8FE18L,(DirType)32},
+ {0xFFE0FE20L,(DirType)32},
+ {0xFFD8FE28L,(DirType)32},
+ {0xFFD0FE30L,(DirType)32},
+ {0xFFC8FE38L,(DirType)32},
+ {0xFFC0FE40L,(DirType)32},
+ {0xFFB8FE48L,(DirType)32},
+ {0xFFB0FE50L,(DirType)32},
+ {0xFFA8FE58L,(DirType)32},
+ {0xFFA0FE60L,(DirType)32},
+ {0xFF98FE68L,(DirType)32},
+ {0xFF90FE70L,(DirType)32},
+ {0xFF88FE78L,(DirType)32},
+ {0xFF80FE80L,(DirType)32}, // Track entry here.
+ {0xFF78FE88L,(DirType)32},
+ {0xFF71FE90L,(DirType)32},
+ {0xFF6AFE97L,(DirType)32},
+ {0xFF62FE9FL,(DirType)32},
+ {0xFF5AFEA8L,(DirType)32},
+ {0xFF53FEB0L,(DirType)35},
+ {0xFF4BFEB7L,(DirType)38},
+ {0xFF44FEBEL,(DirType)41},
+ {0xFF3EFEC4L,(DirType)44},
+ {0xFF39FECEL,(DirType)47},
+ {0xFF34FED8L,(DirType)50},
+ {0xFF30FEE0L,(DirType)53},
+ {0xFF2DFEEBL,(DirType)56},
+ {0xFF2CFEF5L,(DirType)59},
+ {0xFF2BFF00L,(DirType)62},
+ {0xFF2CFF0BL,(DirType)66},
+ {0xFF2DFF15L,(DirType)69},
+ {0xFF30FF1FL,(DirType)72},
+ {0xFF34FF28L,(DirType)75},
+ {0xFF39FF30L,(DirType)78},
+ {0xFF3EFF3AL,(DirType)81},
+ {0xFF44FF44L,(DirType)84},
+ {0xFF4BFF4BL,(DirType)87},
+ {0xFF53FF50L,(DirType)90},
+ {0xFF5AFF58L,(DirType)93},
+ {0xFF62FF60L,(DirType)96},
+ {0xFF6AFF68L,(DirType)96},
+ {0xFF71FF70L,(DirType)96},
+ {0xFF78FF78L,(DirType)96},
+ {0xFF80FF80L,(DirType)96}, // Track jump check here.
+ {0xFF88FF88L,(DirType)96},
+ {0xFF90FF90L,(DirType)96},
+ {0xFF98FF98L,(DirType)96},
+ {0xFFA0FFA0L,(DirType)96},
+ {0xFFA8FFA8L,(DirType)96},
+ {0xFFB0FFB0L,(DirType)96},
+ {0xFFB8FFB8L,(DirType)96},
+ {0xFFC0FFC0L,(DirType)96},
+ {0xFFC8FFC8L,(DirType)96},
+ {0xFFD0FFD0L,(DirType)96},
+ {0xFFD8FFD8L,(DirType)96},
+ {0xFFE0FFE0L,(DirType)96},
+ {0xFFE8FFE8L,(DirType)96},
+ {0xFFF0FFF0L,(DirType)96},
+ {0xFFF8FFF8L,(DirType)96},
+ {0x00000000L,(DirType)96}
+};
+
+DriveClass::TrackType const DriveClass::Track6[] = {
+ {0x0100FE00L,(DirType)32},
+ {0x00F8FE08L,(DirType)32},
+ {0x00F0FE10L,(DirType)32},
+ {0x00E8FE18L,(DirType)32},
+ {0x00E0FE20L,(DirType)32},
+ {0x00D8FE28L,(DirType)32},
+ {0x00D0FE30L,(DirType)32},
+ {0x00C8FE38L,(DirType)32},
+ {0x00C0FE40L,(DirType)32},
+ {0x00B8FE48L,(DirType)32},
+ {0x00B0FE50L,(DirType)32},
+ {0x00A8FE58L,(DirType)32},
+ {0x00A0FE60L,(DirType)32},
+ {0x0098FE68L,(DirType)32},
+ {0x0090FE70L,(DirType)32},
+ {0x0088FE78L,(DirType)32},
+ {0x0080FE80L,(DirType)32}, // Jump entry point here.
+ {0x0078FE88L,(DirType)32},
+ {0x0070FE90L,(DirType)32},
+ {0x0068FE98L,(DirType)32},
+ {0x0060FEA0L,(DirType)32},
+ {0x0058FEA8L,(DirType)32},
+ {0x0055FEAEL,(DirType)32},
+ {0x004EFEB8L,(DirType)35},
+ {0x0048FEC0L,(DirType)37},
+ {0x0042FEC9L,(DirType)40},
+ {0x003BFED2L,(DirType)43},
+ {0x0037FEDAL,(DirType)45},
+ {0x0032FEE3L,(DirType)48},
+ {0x002BFEEBL,(DirType)51},
+ {0x0026FEF5L,(DirType)53},
+ {0x0022FEFEL,(DirType)56},
+ {0x001CFF08L,(DirType)59},
+ {0x0019FF12L,(DirType)61},
+ {0x0015FF1BL,(DirType)64},
+ {0x0011FF26L,(DirType)64},
+ {0x000EFF30L,(DirType)64},
+ {0x000BFF39L,(DirType)64},
+ {0x0009FF43L,(DirType)64},
+ {0x0007FF4EL,(DirType)64},
+ {0x0005FF57L,(DirType)64},
+ {0x0003FF62L,(DirType)64},
+ {0x0001FF6DL,(DirType)64},
+ {0x0000FF77L,(DirType)64},
+ {0x0000FF80L,(DirType)64}, // Track jump check here.
+ {0x0000FF8BL,(DirType)64},
+ {0x0000FF95L,(DirType)64},
+ {0x0000FFA0L,(DirType)64},
+ {0x0000FFABL,(DirType)64},
+ {0x0000FFB5L,(DirType)64},
+ {0x0000FFC0L,(DirType)64},
+ {0x0000FFCBL,(DirType)64},
+ {0x0000FFD5L,(DirType)64},
+ {0x0000FFE0L,(DirType)64},
+ {0x0000FFEBL,(DirType)64},
+ {0x0000FFF5L,(DirType)64},
+ {0x00000000L,(DirType)64}
+};
+
+DriveClass::TrackType const DriveClass::Track7[] = {
+ {0x0006FFFFL,(DirType)0},
+ {0x000CFFFEL,(DirType)4},
+ {0x0011FFFCL,(DirType)8},
+ {0x0018FFFAL,(DirType)12},
+ {0x001FFFF6L,(DirType)16},
+ {0x0024FFF3L,(DirType)19},
+ {0x002BFFF0L,(DirType)22},
+ {0x0030FFFDL,(DirType)23},
+ {0x0035FFEBL,(DirType)24},
+ {0x0038FFE8L,(DirType)25},
+ {0x003CFFE6L,(DirType)26},
+ {0x0040FFE3L,(DirType)27},
+ {0x0043FFE0L,(DirType)28},
+ {0x0046FFDDL,(DirType)29},
+ {0x0043FFDFL,(DirType)30},
+ {0x0040FFE1L,(DirType)30},
+ {0x003CFFE3L,(DirType)30},
+ {0x0038FFE5L,(DirType)30},
+ {0x0035FFE7L,(DirType)31},
+ {0x0030FFE9L,(DirType)31},
+ {0x002BFFEBL,(DirType)31},
+ {0x0024FFEDL,(DirType)31},
+ {0x001FFFF1L,(DirType)31},
+ {0x0018FFF4L,(DirType)32},
+ {0x0011FFF7L,(DirType)32},
+ {0x000CFFFAL,(DirType)32},
+ {0x0006FFFDL,(DirType)32},
+ {0x00000000L,(DirType)32}
+};
+
+DriveClass::TrackType const DriveClass::Track8[] = {
+ {0x0003FFFCL,(DirType)32},
+ {0x0006FFF7L,(DirType)36},
+ {0x000AFFF1L,(DirType)40},
+ {0x000CFFEBL,(DirType)44},
+ {0x000DFFE4L,(DirType)46},
+ {0x000EFFDCL,(DirType)48},
+ {0x000FFFD5L,(DirType)50},
+ {0x0010FFD0L,(DirType)52},
+ {0x0011FFC9L,(DirType)54},
+ {0x0012FFC2L,(DirType)56},
+ {0x0011FFC0L,(DirType)58},
+ {0x0010FFC2L,(DirType)60},
+ {0x000EFFC9L,(DirType)62},
+ {0x000CFFCFL,(DirType)64},
+ {0x000AFFD5L,(DirType)64},
+ {0x0008FFDAL,(DirType)64},
+ {0x0006FFE2L,(DirType)64},
+ {0x0004FFE9L,(DirType)64},
+ {0x0002FFEFL,(DirType)64},
+ {0x0001FFF5L,(DirType)64},
+ {0x0000FFF9L,(DirType)64},
+ {0x00000000L,(DirType)64}
+};
+
+DriveClass::TrackType const DriveClass::Track9[] = {
+ {0xFFF50002L,(DirType)0},
+ {0xFFEB0004L,(DirType)2},
+ {0xFFE00006L,(DirType)4},
+ {0xFFD50009L,(DirType)6},
+ {0xFFCE000CL,(DirType)9},
+ {0xFFC8000FL,(DirType)11},
+ {0xFFC00012L,(DirType)13},
+ {0xFFB80015L,(DirType)16},
+ {0xFFC00012L,(DirType)18},
+ {0xFFC8000EL,(DirType)20},
+ {0xFFCE000AL,(DirType)22},
+ {0xFFD50004L,(DirType)24},
+ {0xFFDE0000L,(DirType)26},
+ {0xFFE9FFF8L,(DirType)28},
+ {0xFFEEFFF2L,(DirType)30},
+ {0xFFF5FFEBL,(DirType)32},
+ {0xFFFDFFE1L,(DirType)34},
+ {0x0002FFD8L,(DirType)36},
+ {0x0007FFD2L,(DirType)39},
+ {0x000BFFCBL,(DirType)41},
+ {0x0010FFC5L,(DirType)43},
+ {0x0013FFBEL,(DirType)45},
+ {0x0015FFB7L,(DirType)48},
+ {0x0013FFBEL,(DirType)50},
+ {0x0011FFC5L,(DirType)52},
+ {0x000BFFCCL,(DirType)54},
+ {0x0008FFD4L,(DirType)56},
+ {0x0005FFDFL,(DirType)58},
+ {0x0003FFEBL,(DirType)62},
+ {0x0001FFF5L,(DirType)64},
+ {0x00000000L,(DirType)64}
+};
+
+DriveClass::TrackType const DriveClass::Track10[] = {
+ {0xFFF6000BL,(DirType)32},
+ {0xFFF00015L,(DirType)37},
+ {0xFFEB0020L,(DirType)42},
+ {0xFFE9002BL,(DirType)47},
+ {0xFFE50032L,(DirType)52},
+ {0xFFE30038L,(DirType)57},
+ {0xFFE00040L,(DirType)60},
+ {0xFFE20038L,(DirType)62},
+ {0xFFE40032L,(DirType)64},
+ {0xFFE5002AL,(DirType)68},
+ {0xFFE6001EL,(DirType)70},
+ {0xFFE70015L,(DirType)72},
+ {0xFFE8000BL,(DirType)74},
+ {0xFFE90000L,(DirType)76},
+ {0xFFE8FFF5L,(DirType)78},
+ {0xFFE7FFEBL,(DirType)80},
+ {0xFFE6FFE0L,(DirType)82},
+ {0xFFE5FFD5L,(DirType)84},
+ {0xFFE4FFCEL,(DirType)86},
+ {0xFFE2FFC5L,(DirType)88},
+ {0xFFE0FFC0L,(DirType)90},
+ {0xFFE3FFC5L,(DirType)92},
+ {0xFFE5FFCEL,(DirType)94},
+ {0xFFE9FFD5L,(DirType)95},
+ {0xFFEBFFE0L,(DirType)96},
+ {0xFFF0FFEBL,(DirType)96},
+ {0xFFF6FFF5L,(DirType)96},
+ {0x00000000L,(DirType)96}
+};
+
+DriveClass::TrackType const DriveClass::Track11[] = {
+ {0x01000000L,DIR_SW},
+ {0x00F30008L,DIR_SW},
+ {0x00E50010L,DIR_SW_X1},
+ {0x00D60018L,DIR_SW_X1},
+ {0x00C80020L,DIR_SW_X1},
+ {0x00B90028L,DIR_SW_X1},
+ {0x00AB0030L,DIR_SW_X2},
+ {0x009C0038L,DIR_SW_X2},
+ {0x008D0040L,DIR_SW_X2},
+ {0x007F0048L,DIR_SW_X2},
+ {0x00710050L,DIR_SW_X2},
+ {0x00640058L,DIR_SW_X2},
+ {0x00550060L,DIR_SW_X2},
+
+ {0x00000000L,DIR_SW_X2}
+};
+
+DriveClass::TrackType const DriveClass::Track12[] = {
+ {0xFF550060L,DIR_SW_X2},
+ {0xFF640058L,DIR_SW_X2},
+ {0xFF710050L,DIR_SW_X2},
+ {0xFF7F0048L,DIR_SW_X2},
+ {0xFF8D0040L,DIR_SW_X2},
+ {0xFF9C0038L,DIR_SW_X2},
+ {0xFFAB0030L,DIR_SW_X2},
+ {0xFFB90028L,DIR_SW_X1},
+ {0xFFC80020L,DIR_SW_X1},
+ {0xFFD60018L,DIR_SW_X1},
+ {0xFFE50010L,DIR_SW_X1},
+ {0xFFF30008L,DIR_SW},
+
+ {0x00000000L,DIR_SW}
+};
+
+#if(1)
+/*
+** Drive out of weapon's factory.
+*/
+DriveClass::TrackType const DriveClass::Track13[] = {
+ {XYP_COORD(0,-35),DIR_S},
+ {XYP_COORD(0,-34),DIR_S},
+ {XYP_COORD(0,-33),DIR_S},
+ {XYP_COORD(0,-32),DIR_S},
+ {XYP_COORD(0,-31),DIR_S},
+ {XYP_COORD(0,-30),DIR_S},
+ {XYP_COORD(0,-29),DIR_S},
+ {XYP_COORD(0,-28),DIR_S},
+ {XYP_COORD(0,-27),DIR_S},
+ {XYP_COORD(0,-26),DIR_S},
+ {XYP_COORD(0,-25),DIR_S},
+ {XYP_COORD(0,-24),DIR_S},
+ {XYP_COORD(0,-23),DIR_S},
+ {XYP_COORD(0,-22),DIR_S},
+ {XYP_COORD(0,-21),DIR_S},
+ {XYP_COORD(0,-20),DIR_S},
+ {XYP_COORD(0,-19),DIR_S},
+ {XYP_COORD(0,-18),DIR_S},
+ {XYP_COORD(0,-17),DIR_S},
+ {XYP_COORD(0,-16),DIR_S},
+ {XYP_COORD(0,-15),DIR_S},
+ {XYP_COORD(0,-14),DIR_S},
+ {XYP_COORD(0,-13),DIR_S},
+ {XYP_COORD(0,-12),DIR_S},
+ {XYP_COORD(0,-11),DIR_S},
+ {XYP_COORD(0,-10),DIR_S},
+ {XYP_COORD(0,-9),DIR_S},
+ {XYP_COORD(0,-8),DIR_S},
+ {XYP_COORD(0,-7),DIR_S},
+ {XYP_COORD(0,-6),DIR_S},
+ {XYP_COORD(0,-5),DIR_S},
+ {XYP_COORD(0,-4),DIR_S},
+ {XYP_COORD(0,-3),DIR_S},
+ {XYP_COORD(0,-2),DIR_S},
+ {XYP_COORD(0,-1),DIR_S},
+
+ {0x00000000L,DIR_S}
+};
+#else
+/*
+** Drive out of weapon's factory.
+*/
+DriveClass::TrackType const DriveClass::Track13[] = {
+ {XYP_COORD(10,-21),(DirType)(DIR_SW-10)},
+ {XYP_COORD(10,-21),(DirType)(DIR_SW-10)},
+ {XYP_COORD(10,-20),(DirType)(DIR_SW-10)},
+ {XYP_COORD(10,-20),(DirType)(DIR_SW-10)},
+ {XYP_COORD(9,-18),(DirType)(DIR_SW-10)},
+ {XYP_COORD(9,-18),(DirType)(DIR_SW-10)},
+ {XYP_COORD(9,-17),(DirType)(DIR_SW-10)},
+ {XYP_COORD(8,-16),(DirType)(DIR_SW-10)},
+ {XYP_COORD(8,-15),(DirType)(DIR_SW-10)},
+ {XYP_COORD(7,-14),(DirType)(DIR_SW-10)},
+ {XYP_COORD(7,-13),(DirType)(DIR_SW-10)},
+ {XYP_COORD(6,-12),(DirType)(DIR_SW-10)},
+ {XYP_COORD(6,-11),(DirType)(DIR_SW-10)},
+ {XYP_COORD(5,-10),(DirType)(DIR_SW-10)},
+ {XYP_COORD(5,-9),(DirType)(DIR_SW-10)},
+ {XYP_COORD(4,-8),(DirType)(DIR_SW-10)},
+ {XYP_COORD(4,-7),(DirType)(DIR_SW-10)},
+ {XYP_COORD(3,-6),(DirType)(DIR_SW-10)},
+ {XYP_COORD(3,-5),(DirType)(DIR_SW-9)},
+ {XYP_COORD(2,-4),(DirType)(DIR_SW-7)},
+ {XYP_COORD(2,-3),(DirType)(DIR_SW-5)},
+ {XYP_COORD(1,-2),(DirType)(DIR_SW-3)},
+ {XYP_COORD(1,-1),(DirType)(DIR_SW-1)},
+
+ {0x00000000L,DIR_SW}
+};
+#endif
+
+/*
+** There are a limited basic number of tracks that a vehicle can follow. These
+** are they. Each track can be interpreted differently but this is controlled
+** by the TrackControl structure elaborated elsewhere.
+*/
+DriveClass::RawTrackType const DriveClass::RawTracks[13] = {
+ {Track1, -1, 0, -1},
+ {Track2, -1, 0, -1},
+ {Track3, 37, 12, 22},
+ {Track4, 26, 11, 19},
+ {Track5, 45, 15, 31},
+ {Track6, 44, 16, 27},
+ {Track7, -1, 0, -1},
+ {Track8, -1, 0, -1},
+ {Track9, -1, 0, -1},
+ {Track10, -1, 0, -1},
+ {Track11, -1, 0, -1},
+ {Track12, -1, 0, -1},
+ {Track13, -1, 0, -1}
+};
+
+
+/***************************************************************************
+** Smooth turning control table. Given two directions in a path list, this
+** table determines which track to use and what modifying operations need
+** be performed on the track data.
+*/
+DriveClass::TurnTrackType const DriveClass::TrackControl[67] = {
+ {1, 0, DIR_N, F_}, // 0-0
+ {3, 7, DIR_NE, F_D}, // 0-1 (raw chart)
+ {4, 9, DIR_E, F_D}, // 0-2 (raw chart)
+ {0, 0, DIR_SE, F_}, // 0-3 !
+ {0, 0, DIR_S, F_}, // 0-4 !
+ {0, 0, DIR_SW, F_}, // 0-5 !
+ {4, 9, DIR_W, (DriveClass::TrackControlType)(F_X|F_D)}, // 0-6
+ {3, 7, DIR_NW, (DriveClass::TrackControlType)(F_X|F_D)}, // 0-7
+ {6, 8, DIR_N, (DriveClass::TrackControlType)(F_T|F_X|F_Y|F_D)}, // 1-0
+ {2, 0, DIR_NE, F_}, // 1-1 (raw chart)
+ {6, 8, DIR_E, F_D}, // 1-2 (raw chart)
+ {5, 10, DIR_SE, F_D}, // 1-3 (raw chart)
+ {0, 0, DIR_S, F_}, // 1-4 !
+ {0, 0, DIR_SW, F_}, // 1-5 !
+ {0, 0, DIR_W, F_}, // 1-6 !
+ {5, 10, DIR_NW, (DriveClass::TrackControlType)(F_T|F_X|F_Y|F_D)}, // 1-7
+ {4, 9, DIR_N, (DriveClass::TrackControlType)(F_T|F_X|F_Y|F_D)}, // 2-0
+ {3, 7, DIR_NE, (DriveClass::TrackControlType)(F_T|F_X|F_Y|F_D)}, // 2-1
+ {1, 0, DIR_E, (DriveClass::TrackControlType)(F_T|F_X)}, // 2-2
+ {3, 7, DIR_SE, (DriveClass::TrackControlType)(F_T|F_X|F_D)}, // 2-3
+ {4, 9, DIR_S, (DriveClass::TrackControlType)(F_T|F_X|F_D)}, // 2-4
+ {0, 0, DIR_SW, F_}, // 2-5 !
+ {0, 0, DIR_W, F_}, // 2-6 !
+ {0, 0, DIR_NW, F_}, // 2-7 !
+ {0, 0, DIR_N, F_}, // 3-0 !
+ {5, 10, DIR_NE, (DriveClass::TrackControlType)(F_Y|F_D)}, // 3-1
+ {6, 8, DIR_E, (DriveClass::TrackControlType)(F_Y|F_D)}, // 3-2
+ {2, 0, DIR_SE, F_Y}, // 3-3
+ {6, 8, DIR_S, (DriveClass::TrackControlType)(F_T|F_X|F_D)}, // 3-4
+ {5, 10, DIR_SW, (DriveClass::TrackControlType)(F_T|F_X|F_D)}, // 3-5
+ {0, 0, DIR_W, F_}, // 3-6 !
+ {0, 0, DIR_NW, F_}, // 3-7 !
+ {0, 0, DIR_N, F_}, // 4-0 !
+ {0, 0, DIR_NE, F_}, // 4-1 !
+ {4, 9, DIR_E, (DriveClass::TrackControlType)(F_Y|F_D)}, // 4-2
+ {3, 7, DIR_SE, (DriveClass::TrackControlType)(F_Y|F_D)}, // 4-3
+ {1, 0, DIR_S, F_Y}, // 4-4
+ {3, 7, DIR_SW, (DriveClass::TrackControlType)(F_X|F_Y|F_D)}, // 4-5
+ {4, 9, DIR_W, (DriveClass::TrackControlType)(F_X|F_Y|F_D)}, // 4-6
+ {0, 0, DIR_NW, F_}, // 4-7 !
+ {0, 0, DIR_N, F_}, // 5-0 !
+ {0, 0, DIR_NE, F_}, // 5-1 !
+ {0, 0, DIR_E, F_}, // 5-2 !
+ {5, 10, DIR_SE, (DriveClass::TrackControlType)(F_T|F_D)}, // 5-3
+ {6, 8, DIR_S, (DriveClass::TrackControlType)(F_T|F_D)}, // 5-4
+ {2, 0, DIR_SW, F_T}, // 5-5
+ {6, 8, DIR_W, (DriveClass::TrackControlType)(F_X|F_Y|F_D)}, // 5-6
+ {5, 10, DIR_NW, (DriveClass::TrackControlType)(F_X|F_Y|F_D)}, // 5-7
+ {4, 9, DIR_N, (DriveClass::TrackControlType)(F_T|F_Y|F_D)}, // 6-0
+ {0, 0, DIR_NE, F_}, // 6-1 !
+ {0, 0, DIR_E, F_}, // 6-2 !
+ {0, 0, DIR_SE, F_}, // 6-3 !
+ {4, 9, DIR_S, (DriveClass::TrackControlType)(F_T|F_D)}, // 6-4
+ {3, 7, DIR_SW, (DriveClass::TrackControlType)(F_T|F_D)}, // 6-5
+ {1, 0, DIR_W, F_T}, // 6-6
+ {3, 7, DIR_NW, (DriveClass::TrackControlType)(F_T|F_Y|F_D)}, // 6-7
+ {6, 8, DIR_N, (DriveClass::TrackControlType)(F_T|F_Y|F_D)}, // 7-0
+ {5, 10, DIR_NE, (DriveClass::TrackControlType)(F_T|F_Y|F_D)}, // 7-1
+ {0, 0, DIR_E, F_}, // 7-2 !
+ {0, 0, DIR_SE, F_}, // 7-3 !
+ {0, 0, DIR_S, F_}, // 7-4 !
+ {5, 10, DIR_SW, (DriveClass::TrackControlType)(F_X|F_D)}, // 7-5
+ {6, 8, DIR_W, (DriveClass::TrackControlType)(F_X|F_D)}, // 7-6
+ {2, 0, DIR_NW, F_X}, // 7-7
+
+ {11, 11, DIR_SW, F_}, // Backup harvester into refinery.
+ {12, 12, DIR_SW_X2, F_}, // Drive back into refinery.
+ {13, 13, DIR_SW, F_} // Drive out of weapons factory.
+};
+
+
+
diff --git a/CODE/DRIVE.H b/CODE/DRIVE.H
new file mode 100644
index 0000000..1971c4a
--- /dev/null
+++ b/CODE/DRIVE.H
@@ -0,0 +1,211 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/DRIVE.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : DRIVE.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : April 14, 1994 *
+ * *
+ * Last Update : April 14, 1994 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifndef DRIVE_H
+#define DRIVE_H
+
+#include "foot.h"
+
+/****************************************************************************
+** Movable objects are handled by this class definition. Moveable objects
+** cover everything except buildings.
+*/
+class DriveClass : public FootClass
+{
+ public:
+ /*
+ ** If this unit performing harvesting action, then this flag is true. The flag
+ ** is located here because the other bit flags here give it a free place to
+ ** reside.
+ */
+ unsigned IsHarvesting:1;
+
+ /*
+ ** This flag controls whether the unit has been moebius'd into a
+ ** different location, and whether the MoebiusCountDown timer should be
+ ** used to take him back where he belongs.
+ */
+ unsigned IsMoebius:1;
+
+ /*
+ ** This controls how long a unit can exist in its alternate location
+ ** before being pulled back by the chronosphere into its normal location.
+ */
+ CDTimerClass MoebiusCountDown;
+
+ /*
+ ** This is the coord the unit will be taken back to once its moebius
+ ** effect wears off.
+ */
+ CELL MoebiusCell;
+
+ /*
+ ** Some units must have their turret locked down to face their body direction.
+ ** When this flag is set, this condition is in effect. This flag is a more
+ ** accurate check than examining the TrackNumber since the turret may be
+ ** rotating into position so that a pending track may start. During this process
+ ** the track number does not indicate anything.
+ */
+ unsigned IsTurretLockedDown:1;
+
+ /*
+ ** This vehicle could be processing a "short track". A short track is one that
+ ** doesn't actually go anywhere. Kind of like turning in place.
+ */
+ unsigned IsOnShortTrack:1;
+
+ /*---------------------------------------------------------------------
+ ** Constructors, Destructors, and overloaded operators.
+ */
+ DriveClass(RTTIType rtti, int id, HousesType house);
+ DriveClass(NoInitClass const & x) : FootClass(x), MoebiusCountDown(x) {};
+ virtual ~DriveClass(void) {};
+
+ /*---------------------------------------------------------------------
+ ** Member function prototypes.
+ */
+ bool Teleport_To(CELL cell);
+ virtual void Response_Select(void);
+ virtual void Response_Move(void);
+ virtual void Response_Attack(void);
+ virtual void Scatter(COORDINATE threat, bool forced=false, bool nokidding=false);
+ virtual bool Limbo(void);
+ void Do_Turn(DirType dir);
+ virtual void Overrun_Square(CELL , bool =true) {};
+ virtual void Assign_Destination(TARGET target);
+ virtual void Per_Cell_Process(PCPType why);
+ virtual bool Ok_To_Move(DirType ) const;
+ virtual void AI(void);
+ #ifdef CHEAT_KEYS
+ virtual void Debug_Dump(MonoClass *mono) const;
+ #endif
+ void Force_Track(int track, COORDINATE coord);
+ virtual bool Stop_Driver(void);
+
+ void Mark_Track(COORDINATE headto, MarkType type);
+
+ /**********************************************************************
+ ** These enumerations are used as working constants that exist only
+ ** in the DriveClass namespace.
+ */
+ enum DriveClassEnum {
+ BACKUP_INTO_REFINERY=64, // Track to backup into refinery.
+ OUT_OF_REFINERY, // Track to leave refinery.
+ OUT_OF_WEAPON_FACTORY // Track to leave weapons factory.
+ };
+
+ /****************************************************************************
+ ** Smooth turning tracks are controlled by this structure and these
+ ** processing bits.
+ */
+ typedef enum TrackControlType {
+ F_=0x00, // No translation necessary?
+ F_T=0x01, // Transpose X and Y components?
+ F_X=0x02, // Reverse X component sign?
+ F_Y=0x04, // Reverse Y component sign?
+ F_D=0x08 // Two cell consumption?
+ } TrackControlType;
+
+ private:
+
+ typedef struct {
+ char Track; // Which track to use.
+ char StartTrack; // Track when starting from stand-still.
+ DirType Facing; // Facing when track has been completed.
+ DriveClass::TrackControlType Flag; // List processing flag bits.
+ } TurnTrackType;
+
+ typedef struct {
+ COORDINATE Offset; // Offset to origin coordinate.
+ DirType Facing; // Facing (primary track).
+ } TrackType;
+
+ typedef struct {
+ TrackType const * Track; // Pointer to track list.
+ int Jump; // Index where track jumping is allowed.
+ int Entry; // Entry point if jumping to this track.
+ int Cell; // Per cell process should occur at this index.
+ } RawTrackType;
+
+ /*
+ ** These speed values are used to accumulate movement and then
+ ** convert them into pixel "steps" that are then translated through
+ ** the currently running track so that the unit will move.
+ */
+ int SpeedAccum;
+
+ /*
+ ** This the track control logic (used for ground vehicles only). The 'Track'
+ ** variable holds the track being followed (0 == not following track). The
+ ** 'TrackIndex' variable holds the current index into the specified track
+ ** (starts at 0).
+ */
+ int TrackNumber;
+ int TrackIndex;
+
+ /*---------------------------------------------------------------------
+ ** Member function prototypes.
+ */
+ virtual void Fixup_Path(PathType *path);
+ bool While_Moving(void);
+ bool Start_Of_Move(void);
+ void Lay_Track(void);
+ COORDINATE Smooth_Turn(COORDINATE adj, DirType & dir);
+
+ static TurnTrackType const TrackControl[67];
+ static RawTrackType const RawTracks[13];
+ static TrackType const Track13[];
+ static TrackType const Track12[];
+ static TrackType const Track11[];
+ static TrackType const Track10[];
+ static TrackType const Track9[];
+ static TrackType const Track8[];
+ static TrackType const Track7[];
+ static TrackType const Track6[];
+ static TrackType const Track5[];
+ static TrackType const Track4[];
+ static TrackType const Track3[];
+ static TrackType const Track2[];
+ static TrackType const Track1[24];
+};
+
+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/CODE/DROP.CPP b/CODE/DROP.CPP
new file mode 100644
index 0000000..8c5c55e
--- /dev/null
+++ b/CODE/DROP.CPP
@@ -0,0 +1,219 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/DROP.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : DROP.CPP *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 01/24/96 *
+ * *
+ * Last Update : January 24, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+#include "drop.h"
+
+
+DropListClass::DropListClass(int id, char * text, int max_len, TextPrintType flags, int x, int y, int w, int h, void const * up, void const * down) :
+ EditClass(id, text, max_len, flags, x, y, w, 9*RESFACTOR, ALPHANUMERIC),
+ IsDropped(false),
+ ListHeight(h),
+ DropButton(0, down, x+w, y),
+ List(0, x, y+Get_Build_Frame_Height(down), w+Get_Build_Frame_Width(down), h, flags, up, down)
+{
+ Fancy_Text_Print("", 0, 0, 0, 0, flags);
+ EditClass::Height = FontHeight+1;
+ List.Make_Peer(*this);
+ DropButton.Make_Peer(*this);
+}
+
+
+void DropListClass::Zap(void)
+{
+ Collapse();
+ List.Zap();
+ DropButton.Zap();
+ EditClass::Zap();
+}
+
+DropListClass & DropListClass::Add(LinkClass & object)
+{
+ DropButton.Add(object);
+ return((DropListClass &)EditClass::Add(object));
+}
+
+DropListClass & DropListClass::Add_Tail(LinkClass & object)
+{
+ DropButton.Add_Tail(object);
+ return((DropListClass &)EditClass::Add_Tail(object));
+}
+
+DropListClass & DropListClass::Add_Head(LinkClass & object)
+{
+ DropButton.Add_Head(object);
+ return((DropListClass &)EditClass::Add_Head(object));
+}
+
+DropListClass * DropListClass::Remove(void)
+{
+ if (IsDropped) {
+ Collapse();
+ }
+ DropButton.Remove();
+ return((DropListClass *)EditClass::Remove());
+}
+
+int DropListClass::Add_Item(char const * text)
+{
+ strncpy(String, text, MaxLength);
+ Flag_To_Redraw();
+ return(List.Add_Item(text));
+}
+
+char const * DropListClass::Current_Item(void)
+{
+ return(List.Current_Item());
+}
+
+int DropListClass::Current_Index(void)
+{
+ return(List.Current_Index());
+}
+
+void DropListClass::Set_Selected_Index(int index)
+{
+ if ((unsigned)index < List.Count()) {
+ List.Set_Selected_Index(index);
+ strcpy(String, List.Get_Item(Current_Index()));
+ } else {
+ String[0] = '\0';
+ }
+}
+
+
+void DropListClass::Clear_Focus(void)
+{
+ Collapse();
+}
+
+void DropListClass::Peer_To_Peer(unsigned flags, KeyNumType & key, ControlClass & whom)
+{
+ if (&whom == &DropButton) {
+ if (flags & LEFTRELEASE) {
+ if (IsDropped) {
+ Collapse();
+ key = (KeyNumType)(ID | KN_BUTTON);
+ } else {
+ Expand();
+ }
+ }
+ }
+
+ if (&whom == &List) {
+ strncpy(String, List.Current_Item(), MaxLength);
+ Flag_To_Redraw();
+ key = (KeyNumType)(ID | KN_BUTTON);
+ }
+}
+
+void DropListClass::Expand(void)
+{
+ if (!IsDropped) {
+ List.X = X;
+ List.Y = Y+9*RESFACTOR;
+ List.Width = Width;
+ List.Height = ListHeight;
+ List.Add(Head_Of_List());
+ List.Flag_To_Redraw();
+ IsDropped = true;
+ }
+}
+
+void DropListClass::Collapse(void)
+{
+ if (IsDropped) {
+ List.Remove();
+ IsDropped = false;
+ }
+}
+
+
+DropListClass & DropListClass::operator = (DropListClass const & list)
+{
+ if (this == &list) return(*this);
+ EditClass::operator =(list);
+ List = list.List;
+ IsDropped = list.IsDropped;
+ ListHeight = list.ListHeight;
+ DropButton = list.DropButton;
+ List.Make_Peer(*this);
+ DropButton.Make_Peer(*this);
+ return(*this);
+}
+
+
+DropListClass::DropListClass(DropListClass const & list) :
+ EditClass(list),
+ IsDropped(list.IsDropped),
+ ListHeight(list.ListHeight),
+ DropButton(list.DropButton),
+ List(list.List)
+{
+ List.Make_Peer(*this);
+ DropButton.Make_Peer(*this);
+}
+
+
+void DropListClass::Set_Position(int x, int y)
+{
+ EditClass::Set_Position(x, y);
+ List.Set_Position(x, y + Get_Build_Frame_Height(DropButton.Get_Shape_Data()));
+ DropButton.Set_Position(x + Width, y);
+}
+
+
+void DropListClass::Set_Selected_Index(char const * text)
+{
+ if (text) {
+ for (int index = 0; index < Count(); index++) {
+ if (stricmp(text, List.Get_Item(index)) == 0) {
+ Set_Selected_Index(index);
+ break;
+ }
+ }
+ }
+}
+
+#ifdef WOLAPI_INTEGRATION
+void DropListClass::Flag_To_Redraw(void)
+{
+ if( IsDropped )
+ List.Flag_To_Redraw();
+ EditClass::Flag_To_Redraw();
+}
+#endif
diff --git a/CODE/DROP.H b/CODE/DROP.H
new file mode 100644
index 0000000..0f8e791
--- /dev/null
+++ b/CODE/DROP.H
@@ -0,0 +1,353 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/DROP.H 1 3/03/97 10:24a Joe_bostic $ */
+/***********************************************************************************************
+ *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
+ ***********************************************************************************************
+ * *
+ * Project Name : Command & Conquer *
+ * *
+ * File Name : DROP.H *
+ * *
+ * Programmer : Joe L. Bostic *
+ * *
+ * Start Date : 07/05/96 *
+ * *
+ * Last Update : July 5, 1996 [JLB] *
+ * *
+ *---------------------------------------------------------------------------------------------*
+ * Functions: *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifndef DROP_H
+#define DROP_H
+
+#include "list.h"
+#include "edit.h"
+
+class DropListClass : public EditClass {
+ public:
+ DropListClass(int id, char * text, int max_len, TextPrintType flags, int x, int y, int w, int h, void const * up, void const * down);
+ virtual ~DropListClass(void) {};
+
+ virtual DropListClass & Add(LinkClass & object);
+ virtual DropListClass & Add_Tail(LinkClass & object);
+ virtual DropListClass & Add_Head(LinkClass & object);
+ virtual DropListClass * Remove(void);
+ virtual void Zap(void);
+
+ virtual int Add_Item(char const * text);
+ virtual char const * Current_Item(void);
+ virtual int Current_Index(void);
+ virtual void Set_Selected_Index(int index);
+ virtual void Set_Selected_Index(char const * text);
+ virtual void Peer_To_Peer(unsigned flags, KeyNumType &, ControlClass & whom);
+ virtual void Clear_Focus(void);
+ virtual int Count(void) const {return(List.Count());};
+ virtual char const * Get_Item(int index) const {return(List.Get_Item(index));};
+
+#ifdef WOLAPI_INTEGRATION
+ virtual void Flag_To_Redraw(void);
+#endif
+
+ void Expand(void);
+ void Collapse(void);
+
+ virtual void Set_Position(int x, int y);
+
+ DropListClass & operator = (DropListClass const & list);
+ DropListClass(DropListClass const & list);
+
+ /*
+ ** Indicates whether the list box has dropped down or not.
+ */
+ unsigned IsDropped:1;
+
+ /*
+ ** Height of list box when it is expanded.
+ */
+ int ListHeight;
+
+ /*
+ ** Drop down button.
+ */
+ ShapeButtonClass DropButton;
+
+ /*
+ ** List object when it is expanded.
+ */
+ ListClass List;
+};
+
+
+
+
+template
+class TDropListClass : public EditClass {
+ public:
+ TDropListClass(int id, char * text, int max_len, TextPrintType flags, int x, int y, int w, int h, void const * up, void const * down);
+ TDropListClass(TDropListClass const & list);
+ virtual ~TDropListClass(void) {};
+
+ T operator [] (int index) const {return(List[index]);};
+ T & operator [] (int index) {return(List[index]);};
+
+ virtual TDropListClass & Add(LinkClass & object);
+ virtual TDropListClass & Add_Tail(LinkClass & object);
+ virtual TDropListClass & Add_Head(LinkClass & object);
+ virtual TDropListClass * Remove(void);
+ void Zap(void);
+
+ virtual int Add_Item(T text);
+ virtual T Current_Item(void);
+ virtual int Current_Index(void);
+ virtual void Set_Selected_Index(int index);
+ virtual void Set_Selected_Index(T item);
+ virtual void Peer_To_Peer(unsigned flags, KeyNumType &, ControlClass & whom);
+ virtual void Clear_Focus(void);
+ virtual int Count(void) const {return(List.Count());};
+ virtual T Get_Item(int index) const {return(List.Get_Item(index));};
+
+
+ void Expand(void);
+ void Collapse(void);
+
+ virtual void Set_Position(int x, int y);
+
+ TDropListClass & operator = (TDropListClass const & list);
+
+ /*
+ ** Indicates whether the list box has dropped down or not.
+ */
+ unsigned IsDropped:1;
+
+ /*
+ ** Height of list box when it is expanded.
+ */
+ int ListHeight;
+
+ /*
+ ** Drop down button.
+ */
+ ShapeButtonClass DropButton;
+
+ /*
+ ** List object when it is expanded.
+ */
+ TListClass List;
+};
+
+
+template
+TDropListClass::TDropListClass(int id, char * text, int max_len, TextPrintType flags, int x, int y, int w, int h, void const * up, void const * down) :
+ EditClass(id, text, max_len, flags, x, y, w, 9, ALPHANUMERIC),
+ IsDropped(false),
+ ListHeight(h),
+ DropButton(0, down, x+w, y),
+ List(0, x, y+Get_Build_Frame_Height(down), w+Get_Build_Frame_Width(down), h, flags, up, down)
+{
+ List.Make_Peer(*this);
+ DropButton.Make_Peer(*this);
+}
+
+
+template
+void TDropListClass::Zap(void)
+{
+ Collapse();
+ List.Zap();
+ DropButton.Zap();
+ EditClass::Zap();
+}
+
+
+template
+TDropListClass & TDropListClass::Add(LinkClass & object)
+{
+ DropButton.Add(object);
+ return((TDropListClass &)EditClass::Add(object));
+}
+
+
+template
+TDropListClass & TDropListClass::Add_Tail(LinkClass & object)
+{
+ DropButton.Add_Tail(object);
+ return((TDropListClass &)EditClass::Add_Tail(object));
+}
+
+
+template
+TDropListClass & TDropListClass::Add_Head(LinkClass & object)
+{
+ DropButton.Add_Head(object);
+ return((TDropListClass &)EditClass::Add_Head(object));
+}
+
+
+template
+TDropListClass * TDropListClass::Remove(void)
+{
+ if (IsDropped) {
+ Collapse();
+ }
+ DropButton.Remove();
+ return((TDropListClass *)EditClass::Remove());
+}
+
+
+template
+int TDropListClass::Add_Item(T item)
+{
+ strncpy(String, item->Description(), MaxLength);
+ Flag_To_Redraw();
+ return(List.Add_Item(item));
+}
+
+
+template
+T TDropListClass::Current_Item(void)
+{
+ return(List.Current_Item());
+}
+
+
+template
+int TDropListClass::Current_Index(void)
+{
+ return(List.Current_Index());
+}
+
+
+template
+void TDropListClass::Set_Selected_Index(int index)
+{
+ if ((unsigned)index < List.Count()) {
+ List.Set_Selected_Index(index);
+ strncpy(String, List.Get_Item(Current_Index())->Description(), MaxLength);
+ } else {
+ String[0] = '\0';
+ }
+}
+
+
+template
+void TDropListClass::Clear_Focus(void)
+{
+ Collapse();
+}
+
+
+template
+void TDropListClass::Peer_To_Peer(unsigned flags, KeyNumType & key, ControlClass & whom)
+{
+ if (&whom == &DropButton) {
+ if (flags & LEFTRELEASE) {
+ if (IsDropped) {
+ Collapse();
+ key = (KeyNumType)(ID | KN_BUTTON);
+ } else {
+ Expand();
+ }
+ }
+ }
+
+ if (&whom == &List) {
+ strncpy(String, List.Current_Item()->Description(), MaxLength);
+ Flag_To_Redraw();
+ key = (KeyNumType)(ID | KN_BUTTON);
+ }
+}
+
+
+template
+void TDropListClass::Expand(void)
+{
+ if (!IsDropped) {
+ List.X = X;
+ List.Y = Y+9;
+ List.Width = Width;
+ List.Height = ListHeight;
+ List.Add(Head_Of_List());
+ List.Flag_To_Redraw();
+ IsDropped = true;
+ }
+}
+
+
+template
+void TDropListClass::Collapse(void)
+{
+ if (IsDropped) {
+ List.Remove();
+ IsDropped = false;
+ }
+}
+
+
+template
+TDropListClass & TDropListClass::operator = (TDropListClass const & list)
+{
+ if (this == &list) return(*this);
+ EditClass::operator =(list);
+ List = list.List;
+ IsDropped = list.IsDropped;
+ ListHeight = list.ListHeight;
+ DropButton = list.DropButton;
+ List.Make_Peer(*this);
+ DropButton.Make_Peer(*this);
+ return(*this);
+}
+
+
+template
+TDropListClass::TDropListClass(TDropListClass const & list) :
+ EditClass(list),
+ IsDropped(list.IsDropped),
+ ListHeight(list.ListHeight),
+ DropButton(list.DropButton),
+ List(list.List)
+{
+ List.Make_Peer(*this);
+ DropButton.Make_Peer(*this);
+}
+
+
+template
+void TDropListClass::Set_Position(int x, int y)
+{
+ EditClass::Set_Position(x, y);
+ List.Set_Position(x, y + Get_Build_Frame_Height(DropButton.Get_Shape_Data()));
+ DropButton.Set_Position(x + Width, y);
+}
+
+
+template
+void TDropListClass::Set_Selected_Index(T text)
+{
+ for (int index = 0; index < Count(); index++) {
+ if (text == List.Get_Item(index)) {
+ Set_Selected_Index(index);
+ break;
+ }
+ }
+}
+
+
+#endif
diff --git a/CODE/DTABLE.CPP b/CODE/DTABLE.CPP
new file mode 100644
index 0000000..c4dd9a6
--- /dev/null
+++ b/CODE/DTABLE.CPP
@@ -0,0 +1,1446 @@
+/*
+** Command & Conquer Red Alert(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 .
+*/
+
+
+long DiffTable[] = {
+ 0, // Index = 0, Token = 0
+ 1, // Index = 0, Token = 1
+ 3, // Index = 0, Token = 2
+ 4, // Index = 0, Token = 3
+ 7, // Index = 0, Token = 4
+ 8, // Index = 0, Token = 5
+ 10, // Index = 0, Token = 6
+ 11, // Index = 0, Token = 7
+ 0, // Index = 0, Token = 8
+ -1, // Index = 0, Token = 9
+ -3, // Index = 0, Token = 10
+ -4, // Index = 0, Token = 11
+ -7, // Index = 0, Token = 12
+ -8, // Index = 0, Token = 13
+ -10, // Index = 0, Token = 14
+ -11, // Index = 0, Token = 15
+ 1, // Index = 1, Token = 0
+ 3, // Index = 1, Token = 1
+ 5, // Index = 1, Token = 2
+ 7, // Index = 1, Token = 3
+ 9, // Index = 1, Token = 4
+ 11, // Index = 1, Token = 5
+ 13, // Index = 1, Token = 6
+ 15, // Index = 1, Token = 7
+ -1, // Index = 1, Token = 8
+ -3, // Index = 1, Token = 9
+ -5, // Index = 1, Token = 10
+ -7, // Index = 1, Token = 11
+ -9, // Index = 1, Token = 12
+ -11, // Index = 1, Token = 13
+ -13, // Index = 1, Token = 14
+ -15, // Index = 1, Token = 15
+ 1, // Index = 2, Token = 0
+ 3, // Index = 2, Token = 1
+ 5, // Index = 2, Token = 2
+ 7, // Index = 2, Token = 3
+ 10, // Index = 2, Token = 4
+ 12, // Index = 2, Token = 5
+ 14, // Index = 2, Token = 6
+ 16, // Index = 2, Token = 7
+ -1, // Index = 2, Token = 8
+ -3, // Index = 2, Token = 9
+ -5, // Index = 2, Token = 10
+ -7, // Index = 2, Token = 11
+ -10, // Index = 2, Token = 12
+ -12, // Index = 2, Token = 13
+ -14, // Index = 2, Token = 14
+ -16, // Index = 2, Token = 15
+ 1, // Index = 3, Token = 0
+ 3, // Index = 3, Token = 1
+ 6, // Index = 3, Token = 2
+ 8, // Index = 3, Token = 3
+ 11, // Index = 3, Token = 4
+ 13, // Index = 3, Token = 5
+ 16, // Index = 3, Token = 6
+ 18, // Index = 3, Token = 7
+ -1, // Index = 3, Token = 8
+ -3, // Index = 3, Token = 9
+ -6, // Index = 3, Token = 10
+ -8, // Index = 3, Token = 11
+ -11, // Index = 3, Token = 12
+ -13, // Index = 3, Token = 13
+ -16, // Index = 3, Token = 14
+ -18, // Index = 3, Token = 15
+ 1, // Index = 4, Token = 0
+ 3, // Index = 4, Token = 1
+ 6, // Index = 4, Token = 2
+ 8, // Index = 4, Token = 3
+ 12, // Index = 4, Token = 4
+ 14, // Index = 4, Token = 5
+ 17, // Index = 4, Token = 6
+ 19, // Index = 4, Token = 7
+ -1, // Index = 4, Token = 8
+ -3, // Index = 4, Token = 9
+ -6, // Index = 4, Token = 10
+ -8, // Index = 4, Token = 11
+ -12, // Index = 4, Token = 12
+ -14, // Index = 4, Token = 13
+ -17, // Index = 4, Token = 14
+ -19, // Index = 4, Token = 15
+ 1, // Index = 5, Token = 0
+ 4, // Index = 5, Token = 1
+ 7, // Index = 5, Token = 2
+ 10, // Index = 5, Token = 3
+ 13, // Index = 5, Token = 4
+ 16, // Index = 5, Token = 5
+ 19, // Index = 5, Token = 6
+ 22, // Index = 5, Token = 7
+ -1, // Index = 5, Token = 8
+ -4, // Index = 5, Token = 9
+ -7, // Index = 5, Token = 10
+ -10, // Index = 5, Token = 11
+ -13, // Index = 5, Token = 12
+ -16, // Index = 5, Token = 13
+ -19, // Index = 5, Token = 14
+ -22, // Index = 5, Token = 15
+ 1, // Index = 6, Token = 0
+ 4, // Index = 6, Token = 1
+ 7, // Index = 6, Token = 2
+ 10, // Index = 6, Token = 3
+ 14, // Index = 6, Token = 4
+ 17, // Index = 6, Token = 5
+ 20, // Index = 6, Token = 6
+ 23, // Index = 6, Token = 7
+ -1, // Index = 6, Token = 8
+ -4, // Index = 6, Token = 9
+ -7, // Index = 6, Token = 10
+ -10, // Index = 6, Token = 11
+ -14, // Index = 6, Token = 12
+ -17, // Index = 6, Token = 13
+ -20, // Index = 6, Token = 14
+ -23, // Index = 6, Token = 15
+ 1, // Index = 7, Token = 0
+ 4, // Index = 7, Token = 1
+ 8, // Index = 7, Token = 2
+ 11, // Index = 7, Token = 3
+ 15, // Index = 7, Token = 4
+ 18, // Index = 7, Token = 5
+ 22, // Index = 7, Token = 6
+ 25, // Index = 7, Token = 7
+ -1, // Index = 7, Token = 8
+ -4, // Index = 7, Token = 9
+ -8, // Index = 7, Token = 10
+ -11, // Index = 7, Token = 11
+ -15, // Index = 7, Token = 12
+ -18, // Index = 7, Token = 13
+ -22, // Index = 7, Token = 14
+ -25, // Index = 7, Token = 15
+ 2, // Index = 8, Token = 0
+ 6, // Index = 8, Token = 1
+ 10, // Index = 8, Token = 2
+ 14, // Index = 8, Token = 3
+ 18, // Index = 8, Token = 4
+ 22, // Index = 8, Token = 5
+ 26, // Index = 8, Token = 6
+ 30, // Index = 8, Token = 7
+ -2, // Index = 8, Token = 8
+ -6, // Index = 8, Token = 9
+ -10, // Index = 8, Token = 10
+ -14, // Index = 8, Token = 11
+ -18, // Index = 8, Token = 12
+ -22, // Index = 8, Token = 13
+ -26, // Index = 8, Token = 14
+ -30, // Index = 8, Token = 15
+ 2, // Index = 9, Token = 0
+ 6, // Index = 9, Token = 1
+ 10, // Index = 9, Token = 2
+ 14, // Index = 9, Token = 3
+ 19, // Index = 9, Token = 4
+ 23, // Index = 9, Token = 5
+ 27, // Index = 9, Token = 6
+ 31, // Index = 9, Token = 7
+ -2, // Index = 9, Token = 8
+ -6, // Index = 9, Token = 9
+ -10, // Index = 9, Token = 10
+ -14, // Index = 9, Token = 11
+ -19, // Index = 9, Token = 12
+ -23, // Index = 9, Token = 13
+ -27, // Index = 9, Token = 14
+ -31, // Index = 9, Token = 15
+ 2, // Index = 10, Token = 0
+ 6, // Index = 10, Token = 1
+ 11, // Index = 10, Token = 2
+ 15, // Index = 10, Token = 3
+ 21, // Index = 10, Token = 4
+ 25, // Index = 10, Token = 5
+ 30, // Index = 10, Token = 6
+ 34, // Index = 10, Token = 7
+ -2, // Index = 10, Token = 8
+ -6, // Index = 10, Token = 9
+ -11, // Index = 10, Token = 10
+ -15, // Index = 10, Token = 11
+ -21, // Index = 10, Token = 12
+ -25, // Index = 10, Token = 13
+ -30, // Index = 10, Token = 14
+ -34, // Index = 10, Token = 15
+ 2, // Index = 11, Token = 0
+ 7, // Index = 11, Token = 1
+ 12, // Index = 11, Token = 2
+ 17, // Index = 11, Token = 3
+ 23, // Index = 11, Token = 4
+ 28, // Index = 11, Token = 5
+ 33, // Index = 11, Token = 6
+ 38, // Index = 11, Token = 7
+ -2, // Index = 11, Token = 8
+ -7, // Index = 11, Token = 9
+ -12, // Index = 11, Token = 10
+ -17, // Index = 11, Token = 11
+ -23, // Index = 11, Token = 12
+ -28, // Index = 11, Token = 13
+ -33, // Index = 11, Token = 14
+ -38, // Index = 11, Token = 15
+ 2, // Index = 12, Token = 0
+ 7, // Index = 12, Token = 1
+ 13, // Index = 12, Token = 2
+ 18, // Index = 12, Token = 3
+ 25, // Index = 12, Token = 4
+ 30, // Index = 12, Token = 5
+ 36, // Index = 12, Token = 6
+ 41, // Index = 12, Token = 7
+ -2, // Index = 12, Token = 8
+ -7, // Index = 12, Token = 9
+ -13, // Index = 12, Token = 10
+ -18, // Index = 12, Token = 11
+ -25, // Index = 12, Token = 12
+ -30, // Index = 12, Token = 13
+ -36, // Index = 12, Token = 14
+ -41, // Index = 12, Token = 15
+ 3, // Index = 13, Token = 0
+ 9, // Index = 13, Token = 1
+ 15, // Index = 13, Token = 2
+ 21, // Index = 13, Token = 3
+ 28, // Index = 13, Token = 4
+ 34, // Index = 13, Token = 5
+ 40, // Index = 13, Token = 6
+ 46, // Index = 13, Token = 7
+ -3, // Index = 13, Token = 8
+ -9, // Index = 13, Token = 9
+ -15, // Index = 13, Token = 10
+ -21, // Index = 13, Token = 11
+ -28, // Index = 13, Token = 12
+ -34, // Index = 13, Token = 13
+ -40, // Index = 13, Token = 14
+ -46, // Index = 13, Token = 15
+ 3, // Index = 14, Token = 0
+ 10, // Index = 14, Token = 1
+ 17, // Index = 14, Token = 2
+ 24, // Index = 14, Token = 3
+ 31, // Index = 14, Token = 4
+ 38, // Index = 14, Token = 5
+ 45, // Index = 14, Token = 6
+ 52, // Index = 14, Token = 7
+ -3, // Index = 14, Token = 8
+ -10, // Index = 14, Token = 9
+ -17, // Index = 14, Token = 10
+ -24, // Index = 14, Token = 11
+ -31, // Index = 14, Token = 12
+ -38, // Index = 14, Token = 13
+ -45, // Index = 14, Token = 14
+ -52, // Index = 14, Token = 15
+ 3, // Index = 15, Token = 0
+ 10, // Index = 15, Token = 1
+ 18, // Index = 15, Token = 2
+ 25, // Index = 15, Token = 3
+ 34, // Index = 15, Token = 4
+ 41, // Index = 15, Token = 5
+ 49, // Index = 15, Token = 6
+ 56, // Index = 15, Token = 7
+ -3, // Index = 15, Token = 8
+ -10, // Index = 15, Token = 9
+ -18, // Index = 15, Token = 10
+ -25, // Index = 15, Token = 11
+ -34, // Index = 15, Token = 12
+ -41, // Index = 15, Token = 13
+ -49, // Index = 15, Token = 14
+ -56, // Index = 15, Token = 15
+ 4, // Index = 16, Token = 0
+ 12, // Index = 16, Token = 1
+ 21, // Index = 16, Token = 2
+ 29, // Index = 16, Token = 3
+ 38, // Index = 16, Token = 4
+ 46, // Index = 16, Token = 5
+ 55, // Index = 16, Token = 6
+ 63, // Index = 16, Token = 7
+ -4, // Index = 16, Token = 8
+ -12, // Index = 16, Token = 9
+ -21, // Index = 16, Token = 10
+ -29, // Index = 16, Token = 11
+ -38, // Index = 16, Token = 12
+ -46, // Index = 16, Token = 13
+ -55, // Index = 16, Token = 14
+ -63, // Index = 16, Token = 15
+ 4, // Index = 17, Token = 0
+ 13, // Index = 17, Token = 1
+ 22, // Index = 17, Token = 2
+ 31, // Index = 17, Token = 3
+ 41, // Index = 17, Token = 4
+ 50, // Index = 17, Token = 5
+ 59, // Index = 17, Token = 6
+ 68, // Index = 17, Token = 7
+ -4, // Index = 17, Token = 8
+ -13, // Index = 17, Token = 9
+ -22, // Index = 17, Token = 10
+ -31, // Index = 17, Token = 11
+ -41, // Index = 17, Token = 12
+ -50, // Index = 17, Token = 13
+ -59, // Index = 17, Token = 14
+ -68, // Index = 17, Token = 15
+ 5, // Index = 18, Token = 0
+ 15, // Index = 18, Token = 1
+ 25, // Index = 18, Token = 2
+ 35, // Index = 18, Token = 3
+ 46, // Index = 18, Token = 4
+ 56, // Index = 18, Token = 5
+ 66, // Index = 18, Token = 6
+ 76, // Index = 18, Token = 7
+ -5, // Index = 18, Token = 8
+ -15, // Index = 18, Token = 9
+ -25, // Index = 18, Token = 10
+ -35, // Index = 18, Token = 11
+ -46, // Index = 18, Token = 12
+ -56, // Index = 18, Token = 13
+ -66, // Index = 18, Token = 14
+ -76, // Index = 18, Token = 15
+ 5, // Index = 19, Token = 0
+ 16, // Index = 19, Token = 1
+ 27, // Index = 19, Token = 2
+ 38, // Index = 19, Token = 3
+ 50, // Index = 19, Token = 4
+ 61, // Index = 19, Token = 5
+ 72, // Index = 19, Token = 6
+ 83, // Index = 19, Token = 7
+ -5, // Index = 19, Token = 8
+ -16, // Index = 19, Token = 9
+ -27, // Index = 19, Token = 10
+ -38, // Index = 19, Token = 11
+ -50, // Index = 19, Token = 12
+ -61, // Index = 19, Token = 13
+ -72, // Index = 19, Token = 14
+ -83, // Index = 19, Token = 15
+ 6, // Index = 20, Token = 0
+ 18, // Index = 20, Token = 1
+ 31, // Index = 20, Token = 2
+ 43, // Index = 20, Token = 3
+ 56, // Index = 20, Token = 4
+ 68, // Index = 20, Token = 5
+ 81, // Index = 20, Token = 6
+ 93, // Index = 20, Token = 7
+ -6, // Index = 20, Token = 8
+ -18, // Index = 20, Token = 9
+ -31, // Index = 20, Token = 10
+ -43, // Index = 20, Token = 11
+ -56, // Index = 20, Token = 12
+ -68, // Index = 20, Token = 13
+ -81, // Index = 20, Token = 14
+ -93, // Index = 20, Token = 15
+ 6, // Index = 21, Token = 0
+ 19, // Index = 21, Token = 1
+ 33, // Index = 21, Token = 2
+ 46, // Index = 21, Token = 3
+ 61, // Index = 21, Token = 4
+ 74, // Index = 21, Token = 5
+ 88, // Index = 21, Token = 6
+ 101, // Index = 21, Token = 7
+ -6, // Index = 21, Token = 8
+ -19, // Index = 21, Token = 9
+ -33, // Index = 21, Token = 10
+ -46, // Index = 21, Token = 11
+ -61, // Index = 21, Token = 12
+ -74, // Index = 21, Token = 13
+ -88, // Index = 21, Token = 14
+ -101, // Index = 21, Token = 15
+ 7, // Index = 22, Token = 0
+ 22, // Index = 22, Token = 1
+ 37, // Index = 22, Token = 2
+ 52, // Index = 22, Token = 3
+ 67, // Index = 22, Token = 4
+ 82, // Index = 22, Token = 5
+ 97, // Index = 22, Token = 6
+ 112, // Index = 22, Token = 7
+ -7, // Index = 22, Token = 8
+ -22, // Index = 22, Token = 9
+ -37, // Index = 22, Token = 10
+ -52, // Index = 22, Token = 11
+ -67, // Index = 22, Token = 12
+ -82, // Index = 22, Token = 13
+ -97, // Index = 22, Token = 14
+ -112, // Index = 22, Token = 15
+ 8, // Index = 23, Token = 0
+ 24, // Index = 23, Token = 1
+ 41, // Index = 23, Token = 2
+ 57, // Index = 23, Token = 3
+ 74, // Index = 23, Token = 4
+ 90, // Index = 23, Token = 5
+ 107, // Index = 23, Token = 6
+ 123, // Index = 23, Token = 7
+ -8, // Index = 23, Token = 8
+ -24, // Index = 23, Token = 9
+ -41, // Index = 23, Token = 10
+ -57, // Index = 23, Token = 11
+ -74, // Index = 23, Token = 12
+ -90, // Index = 23, Token = 13
+ -107, // Index = 23, Token = 14
+ -123, // Index = 23, Token = 15
+ 9, // Index = 24, Token = 0
+ 27, // Index = 24, Token = 1
+ 45, // Index = 24, Token = 2
+ 63, // Index = 24, Token = 3
+ 82, // Index = 24, Token = 4
+ 100, // Index = 24, Token = 5
+ 118, // Index = 24, Token = 6
+ 136, // Index = 24, Token = 7
+ -9, // Index = 24, Token = 8
+ -27, // Index = 24, Token = 9
+ -45, // Index = 24, Token = 10
+ -63, // Index = 24, Token = 11
+ -82, // Index = 24, Token = 12
+ -100, // Index = 24, Token = 13
+ -118, // Index = 24, Token = 14
+ -136, // Index = 24, Token = 15
+ 10, // Index = 25, Token = 0
+ 30, // Index = 25, Token = 1
+ 50, // Index = 25, Token = 2
+ 70, // Index = 25, Token = 3
+ 90, // Index = 25, Token = 4
+ 110, // Index = 25, Token = 5
+ 130, // Index = 25, Token = 6
+ 150, // Index = 25, Token = 7
+ -10, // Index = 25, Token = 8
+ -30, // Index = 25, Token = 9
+ -50, // Index = 25, Token = 10
+ -70, // Index = 25, Token = 11
+ -90, // Index = 25, Token = 12
+ -110, // Index = 25, Token = 13
+ -130, // Index = 25, Token = 14
+ -150, // Index = 25, Token = 15
+ 11, // Index = 26, Token = 0
+ 33, // Index = 26, Token = 1
+ 55, // Index = 26, Token = 2
+ 77, // Index = 26, Token = 3
+ 99, // Index = 26, Token = 4
+ 121, // Index = 26, Token = 5
+ 143, // Index = 26, Token = 6
+ 165, // Index = 26, Token = 7
+ -11, // Index = 26, Token = 8
+ -33, // Index = 26, Token = 9
+ -55, // Index = 26, Token = 10
+ -77, // Index = 26, Token = 11
+ -99, // Index = 26, Token = 12
+ -121, // Index = 26, Token = 13
+ -143, // Index = 26, Token = 14
+ -165, // Index = 26, Token = 15
+ 12, // Index = 27, Token = 0
+ 36, // Index = 27, Token = 1
+ 60, // Index = 27, Token = 2
+ 84, // Index = 27, Token = 3
+ 109, // Index = 27, Token = 4
+ 133, // Index = 27, Token = 5
+ 157, // Index = 27, Token = 6
+ 181, // Index = 27, Token = 7
+ -12, // Index = 27, Token = 8
+ -36, // Index = 27, Token = 9
+ -60, // Index = 27, Token = 10
+ -84, // Index = 27, Token = 11
+ -109, // Index = 27, Token = 12
+ -133, // Index = 27, Token = 13
+ -157, // Index = 27, Token = 14
+ -181, // Index = 27, Token = 15
+ 13, // Index = 28, Token = 0
+ 39, // Index = 28, Token = 1
+ 66, // Index = 28, Token = 2
+ 92, // Index = 28, Token = 3
+ 120, // Index = 28, Token = 4
+ 146, // Index = 28, Token = 5
+ 173, // Index = 28, Token = 6
+ 199, // Index = 28, Token = 7
+ -13, // Index = 28, Token = 8
+ -39, // Index = 28, Token = 9
+ -66, // Index = 28, Token = 10
+ -92, // Index = 28, Token = 11
+ -120, // Index = 28, Token = 12
+ -146, // Index = 28, Token = 13
+ -173, // Index = 28, Token = 14
+ -199, // Index = 28, Token = 15
+ 14, // Index = 29, Token = 0
+ 43, // Index = 29, Token = 1
+ 73, // Index = 29, Token = 2
+ 102, // Index = 29, Token = 3
+ 132, // Index = 29, Token = 4
+ 161, // Index = 29, Token = 5
+ 191, // Index = 29, Token = 6
+ 220, // Index = 29, Token = 7
+ -14, // Index = 29, Token = 8
+ -43, // Index = 29, Token = 9
+ -73, // Index = 29, Token = 10
+ -102, // Index = 29, Token = 11
+ -132, // Index = 29, Token = 12
+ -161, // Index = 29, Token = 13
+ -191, // Index = 29, Token = 14
+ -220, // Index = 29, Token = 15
+ 16, // Index = 30, Token = 0
+ 48, // Index = 30, Token = 1
+ 81, // Index = 30, Token = 2
+ 113, // Index = 30, Token = 3
+ 146, // Index = 30, Token = 4
+ 178, // Index = 30, Token = 5
+ 211, // Index = 30, Token = 6
+ 243, // Index = 30, Token = 7
+ -16, // Index = 30, Token = 8
+ -48, // Index = 30, Token = 9
+ -81, // Index = 30, Token = 10
+ -113, // Index = 30, Token = 11
+ -146, // Index = 30, Token = 12
+ -178, // Index = 30, Token = 13
+ -211, // Index = 30, Token = 14
+ -243, // Index = 30, Token = 15
+ 17, // Index = 31, Token = 0
+ 52, // Index = 31, Token = 1
+ 88, // Index = 31, Token = 2
+ 123, // Index = 31, Token = 3
+ 160, // Index = 31, Token = 4
+ 195, // Index = 31, Token = 5
+ 231, // Index = 31, Token = 6
+ 266, // Index = 31, Token = 7
+ -17, // Index = 31, Token = 8
+ -52, // Index = 31, Token = 9
+ -88, // Index = 31, Token = 10
+ -123, // Index = 31, Token = 11
+ -160, // Index = 31, Token = 12
+ -195, // Index = 31, Token = 13
+ -231, // Index = 31, Token = 14
+ -266, // Index = 31, Token = 15
+ 19, // Index = 32, Token = 0
+ 58, // Index = 32, Token = 1
+ 97, // Index = 32, Token = 2
+ 136, // Index = 32, Token = 3
+ 176, // Index = 32, Token = 4
+ 215, // Index = 32, Token = 5
+ 254, // Index = 32, Token = 6
+ 293, // Index = 32, Token = 7
+ -19, // Index = 32, Token = 8
+ -58, // Index = 32, Token = 9
+ -97, // Index = 32, Token = 10
+ -136, // Index = 32, Token = 11
+ -176, // Index = 32, Token = 12
+ -215, // Index = 32, Token = 13
+ -254, // Index = 32, Token = 14
+ -293, // Index = 32, Token = 15
+ 21, // Index = 33, Token = 0
+ 64, // Index = 33, Token = 1
+ 107, // Index = 33, Token = 2
+ 150, // Index = 33, Token = 3
+ 194, // Index = 33, Token = 4
+ 237, // Index = 33, Token = 5
+ 280, // Index = 33, Token = 6
+ 323, // Index = 33, Token = 7
+ -21, // Index = 33, Token = 8
+ -64, // Index = 33, Token = 9
+ -107, // Index = 33, Token = 10
+ -150, // Index = 33, Token = 11
+ -194, // Index = 33, Token = 12
+ -237, // Index = 33, Token = 13
+ -280, // Index = 33, Token = 14
+ -323, // Index = 33, Token = 15
+ 23, // Index = 34, Token = 0
+ 70, // Index = 34, Token = 1
+ 118, // Index = 34, Token = 2
+ 165, // Index = 34, Token = 3
+ 213, // Index = 34, Token = 4
+ 260, // Index = 34, Token = 5
+ 308, // Index = 34, Token = 6
+ 355, // Index = 34, Token = 7
+ -23, // Index = 34, Token = 8
+ -70, // Index = 34, Token = 9
+ -118, // Index = 34, Token = 10
+ -165, // Index = 34, Token = 11
+ -213, // Index = 34, Token = 12
+ -260, // Index = 34, Token = 13
+ -308, // Index = 34, Token = 14
+ -355, // Index = 34, Token = 15
+ 26, // Index = 35, Token = 0
+ 78, // Index = 35, Token = 1
+ 130, // Index = 35, Token = 2
+ 182, // Index = 35, Token = 3
+ 235, // Index = 35, Token = 4
+ 287, // Index = 35, Token = 5
+ 339, // Index = 35, Token = 6
+ 391, // Index = 35, Token = 7
+ -26, // Index = 35, Token = 8
+ -78, // Index = 35, Token = 9
+ -130, // Index = 35, Token = 10
+ -182, // Index = 35, Token = 11
+ -235, // Index = 35, Token = 12
+ -287, // Index = 35, Token = 13
+ -339, // Index = 35, Token = 14
+ -391, // Index = 35, Token = 15
+ 28, // Index = 36, Token = 0
+ 85, // Index = 36, Token = 1
+ 143, // Index = 36, Token = 2
+ 200, // Index = 36, Token = 3
+ 258, // Index = 36, Token = 4
+ 315, // Index = 36, Token = 5
+ 373, // Index = 36, Token = 6
+ 430, // Index = 36, Token = 7
+ -28, // Index = 36, Token = 8
+ -85, // Index = 36, Token = 9
+ -143, // Index = 36, Token = 10
+ -200, // Index = 36, Token = 11
+ -258, // Index = 36, Token = 12
+ -315, // Index = 36, Token = 13
+ -373, // Index = 36, Token = 14
+ -430, // Index = 36, Token = 15
+ 31, // Index = 37, Token = 0
+ 94, // Index = 37, Token = 1
+ 157, // Index = 37, Token = 2
+ 220, // Index = 37, Token = 3
+ 284, // Index = 37, Token = 4
+ 347, // Index = 37, Token = 5
+ 410, // Index = 37, Token = 6
+ 473, // Index = 37, Token = 7
+ -31, // Index = 37, Token = 8
+ -94, // Index = 37, Token = 9
+ -157, // Index = 37, Token = 10
+ -220, // Index = 37, Token = 11
+ -284, // Index = 37, Token = 12
+ -347, // Index = 37, Token = 13
+ -410, // Index = 37, Token = 14
+ -473, // Index = 37, Token = 15
+ 34, // Index = 38, Token = 0
+ 103, // Index = 38, Token = 1
+ 173, // Index = 38, Token = 2
+ 242, // Index = 38, Token = 3
+ 313, // Index = 38, Token = 4
+ 382, // Index = 38, Token = 5
+ 452, // Index = 38, Token = 6
+ 521, // Index = 38, Token = 7
+ -34, // Index = 38, Token = 8
+ -103, // Index = 38, Token = 9
+ -173, // Index = 38, Token = 10
+ -242, // Index = 38, Token = 11
+ -313, // Index = 38, Token = 12
+ -382, // Index = 38, Token = 13
+ -452, // Index = 38, Token = 14
+ -521, // Index = 38, Token = 15
+ 38, // Index = 39, Token = 0
+ 114, // Index = 39, Token = 1
+ 191, // Index = 39, Token = 2
+ 267, // Index = 39, Token = 3
+ 345, // Index = 39, Token = 4
+ 421, // Index = 39, Token = 5
+ 498, // Index = 39, Token = 6
+ 574, // Index = 39, Token = 7
+ -38, // Index = 39, Token = 8
+ -114, // Index = 39, Token = 9
+ -191, // Index = 39, Token = 10
+ -267, // Index = 39, Token = 11
+ -345, // Index = 39, Token = 12
+ -421, // Index = 39, Token = 13
+ -498, // Index = 39, Token = 14
+ -574, // Index = 39, Token = 15
+ 42, // Index = 40, Token = 0
+ 126, // Index = 40, Token = 1
+ 210, // Index = 40, Token = 2
+ 294, // Index = 40, Token = 3
+ 379, // Index = 40, Token = 4
+ 463, // Index = 40, Token = 5
+ 547, // Index = 40, Token = 6
+ 631, // Index = 40, Token = 7
+ -42, // Index = 40, Token = 8
+ -126, // Index = 40, Token = 9
+ -210, // Index = 40, Token = 10
+ -294, // Index = 40, Token = 11
+ -379, // Index = 40, Token = 12
+ -463, // Index = 40, Token = 13
+ -547, // Index = 40, Token = 14
+ -631, // Index = 40, Token = 15
+ 46, // Index = 41, Token = 0
+ 138, // Index = 41, Token = 1
+ 231, // Index = 41, Token = 2
+ 323, // Index = 41, Token = 3
+ 417, // Index = 41, Token = 4
+ 509, // Index = 41, Token = 5
+ 602, // Index = 41, Token = 6
+ 694, // Index = 41, Token = 7
+ -46, // Index = 41, Token = 8
+ -138, // Index = 41, Token = 9
+ -231, // Index = 41, Token = 10
+ -323, // Index = 41, Token = 11
+ -417, // Index = 41, Token = 12
+ -509, // Index = 41, Token = 13
+ -602, // Index = 41, Token = 14
+ -694, // Index = 41, Token = 15
+ 51, // Index = 42, Token = 0
+ 153, // Index = 42, Token = 1
+ 255, // Index = 42, Token = 2
+ 357, // Index = 42, Token = 3
+ 459, // Index = 42, Token = 4
+ 561, // Index = 42, Token = 5
+ 663, // Index = 42, Token = 6
+ 765, // Index = 42, Token = 7
+ -51, // Index = 42, Token = 8
+ -153, // Index = 42, Token = 9
+ -255, // Index = 42, Token = 10
+ -357, // Index = 42, Token = 11
+ -459, // Index = 42, Token = 12
+ -561, // Index = 42, Token = 13
+ -663, // Index = 42, Token = 14
+ -765, // Index = 42, Token = 15
+ 56, // Index = 43, Token = 0
+ 168, // Index = 43, Token = 1
+ 280, // Index = 43, Token = 2
+ 392, // Index = 43, Token = 3
+ 505, // Index = 43, Token = 4
+ 617, // Index = 43, Token = 5
+ 729, // Index = 43, Token = 6
+ 841, // Index = 43, Token = 7
+ -56, // Index = 43, Token = 8
+ -168, // Index = 43, Token = 9
+ -280, // Index = 43, Token = 10
+ -392, // Index = 43, Token = 11
+ -505, // Index = 43, Token = 12
+ -617, // Index = 43, Token = 13
+ -729, // Index = 43, Token = 14
+ -841, // Index = 43, Token = 15
+ 61, // Index = 44, Token = 0
+ 184, // Index = 44, Token = 1
+ 308, // Index = 44, Token = 2
+ 431, // Index = 44, Token = 3
+ 555, // Index = 44, Token = 4
+ 678, // Index = 44, Token = 5
+ 802, // Index = 44, Token = 6
+ 925, // Index = 44, Token = 7
+ -61, // Index = 44, Token = 8
+ -184, // Index = 44, Token = 9
+ -308, // Index = 44, Token = 10
+ -431, // Index = 44, Token = 11
+ -555, // Index = 44, Token = 12
+ -678, // Index = 44, Token = 13
+ -802, // Index = 44, Token = 14
+ -925, // Index = 44, Token = 15
+ 68, // Index = 45, Token = 0
+ 204, // Index = 45, Token = 1
+ 340, // Index = 45, Token = 2
+ 476, // Index = 45, Token = 3
+ 612, // Index = 45, Token = 4
+ 748, // Index = 45, Token = 5
+ 884, // Index = 45, Token = 6
+ 1020, // Index = 45, Token = 7
+ -68, // Index = 45, Token = 8
+ -204, // Index = 45, Token = 9
+ -340, // Index = 45, Token = 10
+ -476, // Index = 45, Token = 11
+ -612, // Index = 45, Token = 12
+ -748, // Index = 45, Token = 13
+ -884, // Index = 45, Token = 14
+ -1020, // Index = 45, Token = 15
+ 74, // Index = 46, Token = 0
+ 223, // Index = 46, Token = 1
+ 373, // Index = 46, Token = 2
+ 522, // Index = 46, Token = 3
+ 672, // Index = 46, Token = 4
+ 821, // Index = 46, Token = 5
+ 971, // Index = 46, Token = 6
+ 1120, // Index = 46, Token = 7
+ -74, // Index = 46, Token = 8
+ -223, // Index = 46, Token = 9
+ -373, // Index = 46, Token = 10
+ -522, // Index = 46, Token = 11
+ -672, // Index = 46, Token = 12
+ -821, // Index = 46, Token = 13
+ -971, // Index = 46, Token = 14
+ -1120, // Index = 46, Token = 15
+ 82, // Index = 47, Token = 0
+ 246, // Index = 47, Token = 1
+ 411, // Index = 47, Token = 2
+ 575, // Index = 47, Token = 3
+ 740, // Index = 47, Token = 4
+ 904, // Index = 47, Token = 5
+ 1069, // Index = 47, Token = 6
+ 1233, // Index = 47, Token = 7
+ -82, // Index = 47, Token = 8
+ -246, // Index = 47, Token = 9
+ -411, // Index = 47, Token = 10
+ -575, // Index = 47, Token = 11
+ -740, // Index = 47, Token = 12
+ -904, // Index = 47, Token = 13
+ -1069, // Index = 47, Token = 14
+ -1233, // Index = 47, Token = 15
+ 90, // Index = 48, Token = 0
+ 271, // Index = 48, Token = 1
+ 452, // Index = 48, Token = 2
+ 633, // Index = 48, Token = 3
+ 814, // Index = 48, Token = 4
+ 995, // Index = 48, Token = 5
+ 1176, // Index = 48, Token = 6
+ 1357, // Index = 48, Token = 7
+ -90, // Index = 48, Token = 8
+ -271, // Index = 48, Token = 9
+ -452, // Index = 48, Token = 10
+ -633, // Index = 48, Token = 11
+ -814, // Index = 48, Token = 12
+ -995, // Index = 48, Token = 13
+ -1176, // Index = 48, Token = 14
+ -1357, // Index = 48, Token = 15
+ 99, // Index = 49, Token = 0
+ 298, // Index = 49, Token = 1
+ 497, // Index = 49, Token = 2
+ 696, // Index = 49, Token = 3
+ 895, // Index = 49, Token = 4
+ 1094, // Index = 49, Token = 5
+ 1293, // Index = 49, Token = 6
+ 1492, // Index = 49, Token = 7
+ -99, // Index = 49, Token = 8
+ -298, // Index = 49, Token = 9
+ -497, // Index = 49, Token = 10
+ -696, // Index = 49, Token = 11
+ -895, // Index = 49, Token = 12
+ -1094, // Index = 49, Token = 13
+ -1293, // Index = 49, Token = 14
+ -1492, // Index = 49, Token = 15
+ 109, // Index = 50, Token = 0
+ 328, // Index = 50, Token = 1
+ 547, // Index = 50, Token = 2
+ 766, // Index = 50, Token = 3
+ 985, // Index = 50, Token = 4
+ 1204, // Index = 50, Token = 5
+ 1423, // Index = 50, Token = 6
+ 1642, // Index = 50, Token = 7
+ -109, // Index = 50, Token = 8
+ -328, // Index = 50, Token = 9
+ -547, // Index = 50, Token = 10
+ -766, // Index = 50, Token = 11
+ -985, // Index = 50, Token = 12
+ -1204, // Index = 50, Token = 13
+ -1423, // Index = 50, Token = 14
+ -1642, // Index = 50, Token = 15
+ 120, // Index = 51, Token = 0
+ 360, // Index = 51, Token = 1
+ 601, // Index = 51, Token = 2
+ 841, // Index = 51, Token = 3
+ 1083, // Index = 51, Token = 4
+ 1323, // Index = 51, Token = 5
+ 1564, // Index = 51, Token = 6
+ 1804, // Index = 51, Token = 7
+ -120, // Index = 51, Token = 8
+ -360, // Index = 51, Token = 9
+ -601, // Index = 51, Token = 10
+ -841, // Index = 51, Token = 11
+ -1083, // Index = 51, Token = 12
+ -1323, // Index = 51, Token = 13
+ -1564, // Index = 51, Token = 14
+ -1804, // Index = 51, Token = 15
+ 132, // Index = 52, Token = 0
+ 397, // Index = 52, Token = 1
+ 662, // Index = 52, Token = 2
+ 927, // Index = 52, Token = 3
+ 1192, // Index = 52, Token = 4
+ 1457, // Index = 52, Token = 5
+ 1722, // Index = 52, Token = 6
+ 1987, // Index = 52, Token = 7
+ -132, // Index = 52, Token = 8
+ -397, // Index = 52, Token = 9
+ -662, // Index = 52, Token = 10
+ -927, // Index = 52, Token = 11
+ -1192, // Index = 52, Token = 12
+ -1457, // Index = 52, Token = 13
+ -1722, // Index = 52, Token = 14
+ -1987, // Index = 52, Token = 15
+ 145, // Index = 53, Token = 0
+ 436, // Index = 53, Token = 1
+ 728, // Index = 53, Token = 2
+ 1019, // Index = 53, Token = 3
+ 1311, // Index = 53, Token = 4
+ 1602, // Index = 53, Token = 5
+ 1894, // Index = 53, Token = 6
+ 2185, // Index = 53, Token = 7
+ -145, // Index = 53, Token = 8
+ -436, // Index = 53, Token = 9
+ -728, // Index = 53, Token = 10
+ -1019, // Index = 53, Token = 11
+ -1311, // Index = 53, Token = 12
+ -1602, // Index = 53, Token = 13
+ -1894, // Index = 53, Token = 14
+ -2185, // Index = 53, Token = 15
+ 160, // Index = 54, Token = 0
+ 480, // Index = 54, Token = 1
+ 801, // Index = 54, Token = 2
+ 1121, // Index = 54, Token = 3
+ 1442, // Index = 54, Token = 4
+ 1762, // Index = 54, Token = 5
+ 2083, // Index = 54, Token = 6
+ 2403, // Index = 54, Token = 7
+ -160, // Index = 54, Token = 8
+ -480, // Index = 54, Token = 9
+ -801, // Index = 54, Token = 10
+ -1121, // Index = 54, Token = 11
+ -1442, // Index = 54, Token = 12
+ -1762, // Index = 54, Token = 13
+ -2083, // Index = 54, Token = 14
+ -2403, // Index = 54, Token = 15
+ 176, // Index = 55, Token = 0
+ 528, // Index = 55, Token = 1
+ 881, // Index = 55, Token = 2
+ 1233, // Index = 55, Token = 3
+ 1587, // Index = 55, Token = 4
+ 1939, // Index = 55, Token = 5
+ 2292, // Index = 55, Token = 6
+ 2644, // Index = 55, Token = 7
+ -176, // Index = 55, Token = 8
+ -528, // Index = 55, Token = 9
+ -881, // Index = 55, Token = 10
+ -1233, // Index = 55, Token = 11
+ -1587, // Index = 55, Token = 12
+ -1939, // Index = 55, Token = 13
+ -2292, // Index = 55, Token = 14
+ -2644, // Index = 55, Token = 15
+ 194, // Index = 56, Token = 0
+ 582, // Index = 56, Token = 1
+ 970, // Index = 56, Token = 2
+ 1358, // Index = 56, Token = 3
+ 1746, // Index = 56, Token = 4
+ 2134, // Index = 56, Token = 5
+ 2522, // Index = 56, Token = 6
+ 2910, // Index = 56, Token = 7
+ -194, // Index = 56, Token = 8
+ -582, // Index = 56, Token = 9
+ -970, // Index = 56, Token = 10
+ -1358, // Index = 56, Token = 11
+ -1746, // Index = 56, Token = 12
+ -2134, // Index = 56, Token = 13
+ -2522, // Index = 56, Token = 14
+ -2910, // Index = 56, Token = 15
+ 213, // Index = 57, Token = 0
+ 639, // Index = 57, Token = 1
+ 1066, // Index = 57, Token = 2
+ 1492, // Index = 57, Token = 3
+ 1920, // Index = 57, Token = 4
+ 2346, // Index = 57, Token = 5
+ 2773, // Index = 57, Token = 6
+ 3199, // Index = 57, Token = 7
+ -213, // Index = 57, Token = 8
+ -639, // Index = 57, Token = 9
+ -1066, // Index = 57, Token = 10
+ -1492, // Index = 57, Token = 11
+ -1920, // Index = 57, Token = 12
+ -2346, // Index = 57, Token = 13
+ -2773, // Index = 57, Token = 14
+ -3199, // Index = 57, Token = 15
+ 234, // Index = 58, Token = 0
+ 703, // Index = 58, Token = 1
+ 1173, // Index = 58, Token = 2
+ 1642, // Index = 58, Token = 3
+ 2112, // Index = 58, Token = 4
+ 2581, // Index = 58, Token = 5
+ 3051, // Index = 58, Token = 6
+ 3520, // Index = 58, Token = 7
+ -234, // Index = 58, Token = 8
+ -703, // Index = 58, Token = 9
+ -1173, // Index = 58, Token = 10
+ -1642, // Index = 58, Token = 11
+ -2112, // Index = 58, Token = 12
+ -2581, // Index = 58, Token = 13
+ -3051, // Index = 58, Token = 14
+ -3520, // Index = 58, Token = 15
+ 258, // Index = 59, Token = 0
+ 774, // Index = 59, Token = 1
+ 1291, // Index = 59, Token = 2
+ 1807, // Index = 59, Token = 3
+ 2324, // Index = 59, Token = 4
+ 2840, // Index = 59, Token = 5
+ 3357, // Index = 59, Token = 6
+ 3873, // Index = 59, Token = 7
+ -258, // Index = 59, Token = 8
+ -774, // Index = 59, Token = 9
+ -1291, // Index = 59, Token = 10
+ -1807, // Index = 59, Token = 11
+ -2324, // Index = 59, Token = 12
+ -2840, // Index = 59, Token = 13
+ -3357, // Index = 59, Token = 14
+ -3873, // Index = 59, Token = 15
+ 284, // Index = 60, Token = 0
+ 852, // Index = 60, Token = 1
+ 1420, // Index = 60, Token = 2
+ 1988, // Index = 60, Token = 3
+ 2556, // Index = 60, Token = 4
+ 3124, // Index = 60, Token = 5
+ 3692, // Index = 60, Token = 6
+ 4260, // Index = 60, Token = 7
+ -284, // Index = 60, Token = 8
+ -852, // Index = 60, Token = 9
+ -1420, // Index = 60, Token = 10
+ -1988, // Index = 60, Token = 11
+ -2556, // Index = 60, Token = 12
+ -3124, // Index = 60, Token = 13
+ -3692, // Index = 60, Token = 14
+ -4260, // Index = 60, Token = 15
+ 312, // Index = 61, Token = 0
+ 936, // Index = 61, Token = 1
+ 1561, // Index = 61, Token = 2
+ 2185, // Index = 61, Token = 3
+ 2811, // Index = 61, Token = 4
+ 3435, // Index = 61, Token = 5
+ 4060, // Index = 61, Token = 6
+ 4684, // Index = 61, Token = 7
+ -312, // Index = 61, Token = 8
+ -936, // Index = 61, Token = 9
+ -1561, // Index = 61, Token = 10
+ -2185, // Index = 61, Token = 11
+ -2811, // Index = 61, Token = 12
+ -3435, // Index = 61, Token = 13
+ -4060, // Index = 61, Token = 14
+ -4684, // Index = 61, Token = 15
+ 343, // Index = 62, Token = 0
+ 1030, // Index = 62, Token = 1
+ 1717, // Index = 62, Token = 2
+ 2404, // Index = 62, Token = 3
+ 3092, // Index = 62, Token = 4
+ 3779, // Index = 62, Token = 5
+ 4466, // Index = 62, Token = 6
+ 5153, // Index = 62, Token = 7
+ -343, // Index = 62, Token = 8
+ -1030, // Index = 62, Token = 9
+ -1717, // Index = 62, Token = 10
+ -2404, // Index = 62, Token = 11
+ -3092, // Index = 62, Token = 12
+ -3779, // Index = 62, Token = 13
+ -4466, // Index = 62, Token = 14
+ -5153, // Index = 62, Token = 15
+ 378, // Index = 63, Token = 0
+ 1134, // Index = 63, Token = 1
+ 1890, // Index = 63, Token = 2
+ 2646, // Index = 63, Token = 3
+ 3402, // Index = 63, Token = 4
+ 4158, // Index = 63, Token = 5
+ 4914, // Index = 63, Token = 6
+ 5670, // Index = 63, Token = 7
+ -378, // Index = 63, Token = 8
+ -1134, // Index = 63, Token = 9
+ -1890, // Index = 63, Token = 10
+ -2646, // Index = 63, Token = 11
+ -3402, // Index = 63, Token = 12
+ -4158, // Index = 63, Token = 13
+ -4914, // Index = 63, Token = 14
+ -5670, // Index = 63, Token = 15
+ 415, // Index = 64, Token = 0
+ 1246, // Index = 64, Token = 1
+ 2078, // Index = 64, Token = 2
+ 2909, // Index = 64, Token = 3
+ 3742, // Index = 64, Token = 4
+ 4573, // Index = 64, Token = 5
+ 5405, // Index = 64, Token = 6
+ 6236, // Index = 64, Token = 7
+ -415, // Index = 64, Token = 8
+ -1246, // Index = 64, Token = 9
+ -2078, // Index = 64, Token = 10
+ -2909, // Index = 64, Token = 11
+ -3742, // Index = 64, Token = 12
+ -4573, // Index = 64, Token = 13
+ -5405, // Index = 64, Token = 14
+ -6236, // Index = 64, Token = 15
+ 457, // Index = 65, Token = 0
+ 1372, // Index = 65, Token = 1
+ 2287, // Index = 65, Token = 2
+ 3202, // Index = 65, Token = 3
+ 4117, // Index = 65, Token = 4
+ 5032, // Index = 65, Token = 5
+ 5947, // Index = 65, Token = 6
+ 6862, // Index = 65, Token = 7
+ -457, // Index = 65, Token = 8
+ -1372, // Index = 65, Token = 9
+ -2287, // Index = 65, Token = 10
+ -3202, // Index = 65, Token = 11
+ -4117, // Index = 65, Token = 12
+ -5032, // Index = 65, Token = 13
+ -5947, // Index = 65, Token = 14
+ -6862, // Index = 65, Token = 15
+ 503, // Index = 66, Token = 0
+ 1509, // Index = 66, Token = 1
+ 2516, // Index = 66, Token = 2
+ 3522, // Index = 66, Token = 3
+ 4529, // Index = 66, Token = 4
+ 5535, // Index = 66, Token = 5
+ 6542, // Index = 66, Token = 6
+ 7548, // Index = 66, Token = 7
+ -503, // Index = 66, Token = 8
+ -1509, // Index = 66, Token = 9
+ -2516, // Index = 66, Token = 10
+ -3522, // Index = 66, Token = 11
+ -4529, // Index = 66, Token = 12
+ -5535, // Index = 66, Token = 13
+ -6542, // Index = 66, Token = 14
+ -7548, // Index = 66, Token = 15
+ 553, // Index = 67, Token = 0
+ 1660, // Index = 67, Token = 1
+ 2767, // Index = 67, Token = 2
+ 3874, // Index = 67, Token = 3
+ 4981, // Index = 67, Token = 4
+ 6088, // Index = 67, Token = 5
+ 7195, // Index = 67, Token = 6
+ 8302, // Index = 67, Token = 7
+ -553, // Index = 67, Token = 8
+ -1660, // Index = 67, Token = 9
+ -2767, // Index = 67, Token = 10
+ -3874, // Index = 67, Token = 11
+ -4981, // Index = 67, Token = 12
+ -6088, // Index = 67, Token = 13
+ -7195, // Index = 67, Token = 14
+ -8302, // Index = 67, Token = 15
+ 608, // Index = 68, Token = 0
+ 1825, // Index = 68, Token = 1
+ 3043, // Index = 68, Token = 2
+ 4260, // Index = 68, Token = 3
+ 5479, // Index = 68, Token = 4
+ 6696, // Index = 68, Token = 5
+ 7914, // Index = 68, Token = 6
+ 9131, // Index = 68, Token = 7
+ -608, // Index = 68, Token = 8
+ -1825, // Index = 68, Token = 9
+ -3043, // Index = 68, Token = 10
+ -4260, // Index = 68, Token = 11
+ -5479, // Index = 68, Token = 12
+ -6696, // Index = 68, Token = 13
+ -7914, // Index = 68, Token = 14
+ -9131, // Index = 68, Token = 15
+ 669, // Index = 69, Token = 0
+ 2008, // Index = 69, Token = 1
+ 3348, // Index = 69, Token = 2
+ 4687, // Index = 69, Token = 3
+ 6027, // Index = 69, Token = 4
+ 7366, // Index = 69, Token = 5
+ 8706, // Index = 69, Token = 6
+ 10045, // Index = 69, Token = 7
+ -669, // Index = 69, Token = 8
+ -2008, // Index = 69, Token = 9
+ -3348, // Index = 69, Token = 10
+ -4687, // Index = 69, Token = 11
+ -6027, // Index = 69, Token = 12
+ -7366, // Index = 69, Token = 13
+ -8706, // Index = 69, Token = 14
+ -10045, // Index = 69, Token = 15
+ 736, // Index = 70, Token = 0
+ 2209, // Index = 70, Token = 1
+ 3683, // Index = 70, Token = 2
+ 5156, // Index = 70, Token = 3
+ 6630, // Index = 70, Token = 4
+ 8103, // Index = 70, Token = 5
+ 9577, // Index = 70, Token = 6
+ 11050, // Index = 70, Token = 7
+ -736, // Index = 70, Token = 8
+ -2209, // Index = 70, Token = 9
+ -3683, // Index = 70, Token = 10
+ -5156, // Index = 70, Token = 11
+ -6630, // Index = 70, Token = 12
+ -8103, // Index = 70, Token = 13
+ -9577, // Index = 70, Token = 14
+ -11050, // Index = 70, Token = 15
+ 810, // Index = 71, Token = 0
+ 2431, // Index = 71, Token = 1
+ 4052, // Index = 71, Token = 2
+ 5673, // Index = 71, Token = 3
+ 7294, // Index = 71, Token = 4
+ 8915, // Index = 71, Token = 5
+ 10536, // Index = 71, Token = 6
+ 12157, // Index = 71, Token = 7
+ -810, // Index = 71, Token = 8
+ -2431, // Index = 71, Token = 9
+ -4052, // Index = 71, Token = 10
+ -5673, // Index = 71, Token = 11
+ -7294, // Index = 71, Token = 12
+ -8915, // Index = 71, Token = 13
+ -10536, // Index = 71, Token = 14
+ -12157, // Index = 71, Token = 15
+ 891, // Index = 72, Token = 0
+ 2674, // Index = 72, Token = 1
+ 4457, // Index = 72, Token = 2
+ 6240, // Index = 72, Token = 3
+ 8023, // Index = 72, Token = 4
+ 9806, // Index = 72, Token = 5
+ 11589, // Index = 72, Token = 6
+ 13372, // Index = 72, Token = 7
+ -891, // Index = 72, Token = 8
+ -2674, // Index = 72, Token = 9
+ -4457, // Index = 72, Token = 10
+ -6240, // Index = 72, Token = 11
+ -8023, // Index = 72, Token = 12
+ -9806, // Index = 72, Token = 13
+ -11589, // Index = 72, Token = 14
+ -13372, // Index = 72, Token = 15
+ 980, // Index = 73, Token = 0
+ 2941, // Index = 73, Token = 1
+ 4902, // Index = 73, Token = 2
+ 6863, // Index = 73, Token = 3
+ 8825, // Index = 73, Token = 4
+ 10786, // Index = 73, Token = 5
+ 12747, // Index = 73, Token = 6
+ 14708, // Index = 73, Token = 7
+ -980, // Index = 73, Token = 8
+ -2941, // Index = 73, Token = 9
+ -4902, // Index = 73, Token = 10
+ -6863, // Index = 73, Token = 11
+ -8825, // Index = 73, Token = 12
+ -10786, // Index = 73, Token = 13
+ -12747, // Index = 73, Token = 14
+ -14708, // Index = 73, Token = 15
+ 1078, // Index = 74, Token = 0
+ 3235, // Index = 74, Token = 1
+ 5393, // Index = 74, Token = 2
+ 7550, // Index = 74, Token = 3
+ 9708, // Index = 74, Token = 4
+ 11865, // Index = 74, Token = 5
+ 14023, // Index = 74, Token = 6
+ 16180, // Index = 74, Token = 7
+ -1078, // Index = 74, Token = 8
+ -3235, // Index = 74, Token = 9
+ -5393, // Index = 74, Token = 10
+ -7550, // Index = 74, Token = 11
+ -9708, // Index = 74, Token = 12
+ -11865, // Index = 74, Token = 13
+ -14023, // Index = 74, Token = 14
+ -16180, // Index = 74, Token = 15
+ 1186, // Index = 75, Token = 0
+ 3559, // Index = 75, Token = 1
+ 5932, // Index = 75, Token = 2
+ 8305, // Index = 75, Token = 3
+ 10679, // Index = 75, Token = 4
+ 13052, // Index = 75, Token = 5
+ 15425, // Index = 75, Token = 6
+ 17798, // Index = 75, Token = 7
+ -1186, // Index = 75, Token = 8
+ -3559, // Index = 75, Token = 9
+ -5932, // Index = 75, Token = 10
+ -8305, // Index = 75, Token = 11
+ -10679, // Index = 75, Token = 12
+ -13052, // Index = 75, Token = 13
+ -15425, // Index = 75, Token = 14
+ -17798, // Index = 75, Token = 15
+ 1305, // Index = 76, Token = 0
+ 3915, // Index = 76, Token = 1
+ 6526, // Index = 76, Token = 2
+ 9136, // Index = 76, Token = 3
+ 11747, // Index = 76, Token = 4
+ 14357, // Index = 76, Token = 5
+ 16968, // Index = 76, Token = 6
+ 19578, // Index = 76, Token = 7
+ -1305, // Index = 76, Token = 8
+ -3915, // Index = 76, Token = 9
+ -6526, // Index = 76, Token = 10
+ -9136, // Index = 76, Token = 11
+ -11747, // Index = 76, Token = 12
+ -14357, // Index = 76, Token = 13
+ -16968, // Index = 76, Token = 14
+ -19578, // Index = 76, Token = 15
+ 1435, // Index = 77, Token = 0
+ 4306, // Index = 77, Token = 1
+ 7178, // Index = 77, Token = 2
+ 10049, // Index = 77, Token = 3
+ 12922, // Index = 77, Token = 4
+ 15793, // Index = 77, Token = 5
+ 18665, // Index = 77, Token = 6
+ 21536, // Index = 77, Token = 7
+ -1435, // Index = 77, Token = 8
+ -4306, // Index = 77, Token = 9
+ -7178, // Index = 77, Token = 10
+ -10049, // Index = 77, Token = 11
+ -12922, // Index = 77, Token = 12
+ -15793, // Index = 77, Token = 13
+ -18665, // Index = 77, Token = 14
+ -21536, // Index = 77, Token = 15
+ 1579, // Index = 78, Token = 0
+ 4737, // Index = 78, Token = 1
+ 7896, // Index = 78, Token = 2
+ 11054, // Index = 78, Token = 3
+ 14214, // Index = 78, Token = 4
+ 17372, // Index = 78, Token = 5
+ 20531, // Index = 78, Token = 6
+ 23689, // Index = 78, Token = 7
+ -1579, // Index = 78, Token = 8
+ -4737, // Index = 78, Token = 9
+ -7896, // Index = 78, Token = 10
+ -11054, // Index = 78, Token = 11
+ -14214, // Index = 78, Token = 12
+ -17372, // Index = 78, Token = 13
+ -20531, // Index = 78, Token = 14
+ -23689, // Index = 78, Token = 15
+ 1737, // Index = 79, Token = 0
+ 5211, // Index = 79, Token = 1
+ 8686, // Index = 79, Token = 2
+ 12160, // Index = 79, Token = 3
+ 15636, // Index = 79, Token = 4
+ 19110, // Index = 79, Token = 5
+ 22585, // Index = 79, Token = 6
+ 26059, // Index = 79, Token = 7
+ -1737, // Index = 79, Token = 8
+ -5211, // Index = 79, Token = 9
+ -8686, // Index = 79, Token = 10
+ -12160, // Index = 79, Token = 11
+ -15636, // Index = 79, Token = 12
+ -19110, // Index = 79, Token = 13
+ -22585, // Index = 79, Token = 14
+ -26059, // Index = 79, Token = 15
+ 1911, // Index = 80, Token = 0
+ 5733, // Index = 80, Token = 1
+ 9555, // Index = 80, Token = 2
+ 13377, // Index = 80, Token = 3
+ 17200, // Index = 80, Token = 4
+ 21022, // Index = 80, Token = 5
+ 24844, // Index = 80, Token = 6
+ 28666, // Index = 80, Token = 7
+ -1911, // Index = 80, Token = 8
+ -5733, // Index = 80, Token = 9
+ -9555, // Index = 80, Token = 10
+ -13377, // Index = 80, Token = 11
+ -17200, // Index = 80, Token = 12
+ -21022, // Index = 80, Token = 13
+ -24844, // Index = 80, Token = 14
+ -28666, // Index = 80, Token = 15
+ 2102, // Index = 81, Token = 0
+ 6306, // Index = 81, Token = 1
+ 10511, // Index = 81, Token = 2
+ 14715, // Index = 81, Token = 3
+ 18920, // Index = 81, Token = 4
+ 23124, // Index = 81, Token = 5
+ 27329, // Index = 81, Token = 6
+ 31533, // Index = 81, Token = 7
+ -2102, // Index = 81, Token = 8
+ -6306, // Index = 81, Token = 9
+ -10511, // Index = 81, Token = 10
+ -14715, // Index = 81, Token = 11
+ -18920, // Index = 81, Token = 12
+ -23124, // Index = 81, Token = 13
+ -27329, // Index = 81, Token = 14
+ -31533, // Index = 81, Token = 15
+ 2312, // Index = 82, Token = 0
+ 6937, // Index = 82, Token = 1
+ 11562, // Index = 82, Token = 2
+ 16187, // Index = 82, Token = 3
+ 20812, // Index = 82, Token = 4
+ 25437, // Index = 82, Token = 5
+ 30062, // Index = 82, Token = 6
+ 34687, // Index = 82, Token = 7
+ -2312, // Index = 82, Token = 8
+ -6937, // Index = 82, Token = 9
+ -11562, // Index = 82, Token = 10
+ -16187, // Index = 82, Token = 11
+ -20812, // Index = 82, Token = 12
+ -25437, // Index = 82, Token = 13
+ -30062, // Index = 82, Token = 14
+ -34687, // Index = 82, Token = 15
+ 2543, // Index = 83, Token = 0
+ 7630, // Index = 83, Token = 1
+ 12718, // Index = 83, Token = 2
+ 17805, // Index = 83, Token = 3
+ 22893, // Index = 83, Token = 4
+ 27980, // Index = 83, Token = 5
+ 33068, // Index = 83, Token = 6
+ 38155, // Index = 83, Token = 7
+ -2543, // Index = 83, Token = 8
+ -7630, // Index = 83, Token = 9
+ -12718, // Index = 83, Token = 10
+ -17805, // Index = 83, Token = 11
+ -22893, // Index = 83, Token = 12
+ -27980, // Index = 83, Token = 13
+ -33068, // Index = 83, Token = 14
+ -38155, // Index = 83, Token = 15
+ 2798, // Index = 84, Token = 0
+ 8394, // Index = 84, Token = 1
+ 13990, // Index = 84, Token = 2
+ 19586, // Index = 84, Token = 3
+ 25183, // Index = 84, Token = 4
+ 30779, // Index = 84, Token = 5
+ 36375, // Index = 84, Token = 6
+ 41971, // Index = 84, Token = 7
+ -2798, // Index = 84, Token = 8
+ -8394, // Index = 84, Token = 9
+ -13990, // Index = 84, Token = 10
+ -19586, // Index = 84, Token = 11
+ -25183, // Index = 84, Token = 12
+ -30779, // Index = 84, Token = 13
+ -36375, // Index = 84, Token = 14
+ -41971, // Index = 84, Token = 15
+ 3077, // Index = 85, Token = 0
+ 9232, // Index = 85, Token = 1
+ 15388, // Index = 85, Token = 2
+ 21543, // Index = 85, Token = 3
+ 27700, // Index = 85, Token = 4
+ 33855, // Index = 85, Token = 5
+ 40011, // Index = 85, Token = 6
+ 46166, // Index = 85, Token = 7
+ -3077, // Index = 85, Token = 8
+ -9232, // Index = 85, Token = 9
+ -15388, // Index = 85, Token = 10
+ -21543, // Index = 85, Token = 11
+ -27700, // Index = 85, Token = 12
+ -33855, // Index = 85, Token = 13
+ -40011, // Index = 85, Token = 14
+ -46166, // Index = 85, Token = 15
+ 3385, // Index = 86, Token = 0
+ 10156, // Index = 86, Token = 1
+ 16928, // Index = 86, Token = 2
+ 23699, // Index = 86, Token = 3
+ 30471, // Index = 86, Token = 4
+ 37242, // Index = 86, Token = 5
+ 44014, // Index = 86, Token = 6
+ 50785, // Index = 86, Token = 7
+ -3385, // Index = 86, Token = 8
+ -10156, // Index = 86, Token = 9
+ -16928, // Index = 86, Token = 10
+ -23699, // Index = 86, Token = 11
+ -30471, // Index = 86, Token = 12
+ -37242, // Index = 86, Token = 13
+ -44014, // Index = 86, Token = 14
+ -50785, // Index = 86, Token = 15
+ 3724, // Index = 87, Token = 0
+ 11172, // Index = 87, Token = 1
+ 18621, // Index = 87, Token = 2
+ 26069, // Index = 87, Token = 3
+ 33518, // Index = 87, Token = 4
+ 40966, // Index = 87, Token = 5
+ 48415, // Index = 87, Token = 6
+ 55863, // Index = 87, Token = 7
+ -3724, // Index = 87, Token = 8
+ -11172, // Index = 87, Token = 9
+ -18621, // Index = 87, Token = 10
+ -26069, // Index = 87, Token = 11
+ -33518, // Index = 87, Token = 12
+ -40966, // Index = 87, Token = 13
+ -48415, // Index = 87, Token = 14
+ -55863, // Index = 87, Token = 15
+ 4095, // Index = 88, Token = 0
+ 12286, // Index = 88, Token = 1
+ 20478, // Index = 88, Token = 2
+ 28669, // Index = 88, Token = 3
+ 36862, // Index = 88, Token = 4
+ 45053, // Index = 88, Token = 5
+ 53245, // Index = 88, Token = 6
+ 61436, // Index = 88, Token = 7
+ -4095, // Index = 88, Token = 8
+ -12286, // Index = 88, Token = 9
+ -20478, // Index = 88, Token = 10
+ -28669, // Index = 88, Token = 11
+ -36862, // Index = 88, Token = 12
+ -45053, // Index = 88, Token = 13
+ -53245, // Index = 88, Token = 14
+ -61436 // Index = 88, Token = 15
+};
+
diff --git a/CODE/DYNAVEC.CPP b/CODE/DYNAVEC.CPP
new file mode 100644
index 0000000..0dacb60
--- /dev/null
+++ b/CODE/DYNAVEC.CPP
@@ -0,0 +1,299 @@
+/*
+** Command & Conquer Red Alert(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: /CounterStrike/DYNAVEC.CPP 1 3/03/97 10:24a Joe_bostic $ */
+/***************************************************************************
+ * *
+ * Project Name : Red Alert *
+ * *
+ * File Name : DYNAVEC.CPP *
+ * *
+ * Programmer : Bill R Randolph *
+ * *
+ * Start Date : 09/18/96 *
+ * *
+ * Last Update : September 18, 1996 [BRR] *
+ * *
+ *-------------------------------------------------------------------------*
+ * Functions: *
+ * DynamicVectorClass::Add -- Add an element to the vector. *
+ * DynamicVectorClass::Add_Head -- Adds element to head of the list. *
+ * DynamicVectorClass::Add_Head -- Adds element to head of the list. *
+ * DynamicVectorClass::Delete -- Deletes specified index from vector. *
+ * DynamicVectorClass::Delete -- Remove specified object from vector. *
+ * DynamicVectorClass::DynamicVectorClass -- Constructor *
+ * DynamicVectorClass::ID -- Find matching value in dynamic vector. *
+ * DynamicVectorClass::Resize -- Changes size of a dynamic vector. *
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#include "function.h"
+#include "vector.h"
+#ifdef WINSOCK_IPX
+#include "WSProto.h"
+#include "WSPUDP.h"
+#endif //WINSOCK_IPX
+#include
+#include
+
+/***********************************************************************************************
+ * DynamicVectorClass::DynamicVectorClass -- Constructor for dynamic vector. *
+ * *
+ * This is the normal constructor for the dynamic vector class. It is similar to the normal *
+ * vector class constructor. The vector is initialized to contain the number of elements *
+ * specified in the "size" parameter. The memory is allocated from free store unless the *
+ * optional array parameter is provided. In this case it will place the vector at the *
+ * memory location specified. *
+ * *
+ * INPUT: size -- The maximum number of objects allowed in this vector. *
+ * *
+ * array -- Optional pointer to the memory area to place the vector at. *
+ * *
+ * OUTPUT: none *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 03/10/1995 JLB : Created. *
+ *=============================================================================================*/
+template
+DynamicVectorClass::DynamicVectorClass(unsigned size, T const * array)
+ : VectorClass(size, array)
+{
+ GrowthStep = 10;
+ ActiveCount = 0;
+}
+
+
+/***********************************************************************************************
+ * DynamicVectorClass::Resize -- Changes the size of a dynamic vector. *
+ * *
+ * Use this routine to change the size of the vector. The size changed is the maximum *
+ * number of allocated objects within this vector. If a memory buffer is provided, then *
+ * the vector will be located there. Otherwise, the memory will be allocated out of free *
+ * store. *
+ * *
+ * INPUT: newsize -- The desired maximum size of this vector. *
+ * *
+ * array -- Optional pointer to a previously allocated memory array. *
+ * *
+ * OUTPUT: bool; Was vector successfully resized according to specifications? *
+ * *
+ * WARNINGS: Failure to resize the vector could be the result of lack of free store. *
+ * *
+ * HISTORY: *
+ * 03/10/1995 JLB : Created. *
+ *=============================================================================================*/
+template
+int DynamicVectorClass::Resize(unsigned newsize, T const * array)
+{
+ if (VectorClass::Resize(newsize, array)) {
+ if (Length() < ActiveCount) ActiveCount = Length();
+ return(true);
+ }
+ return(false);
+}
+
+
+/***********************************************************************************************
+ * DynamicVectorClass::ID -- Find matching value in the dynamic vector. *
+ * *
+ * Use this routine to find a matching object (by value) in the vector. Unlike the base *
+ * class ID function of similar name, this one restricts the scan to the current number *
+ * of valid objects. *
+ * *
+ * INPUT: object -- A reference to the object that a match is to be found in the *
+ * vector. *
+ * *
+ * OUTPUT: Returns with the index number of the object that is equivalent to the one *
+ * specified. If no equivalent object could be found then -1 is returned. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 03/13/1995 JLB : Created. *
+ *=============================================================================================*/
+template
+int DynamicVectorClass::ID(T const & object)
+{
+ for (int index = 0; index < Count(); index++) {
+ if ((*this)[index] == object) return(index);
+ }
+ return(-1);
+}
+
+
+/***********************************************************************************************
+ * DynamicVectorClass::Add -- Add an element to the vector. *
+ * *
+ * Use this routine to add an element to the vector. The vector will automatically be *
+ * resized to accomodate the new element IF the vector was allocated previously and the *
+ * growth rate is not zero. *
+ * *
+ * INPUT: object -- Reference to the object that will be added to the vector. *
+ * *
+ * OUTPUT: bool; Was the object added successfully? If so, the object is added to the end *
+ * of the vector. *
+ * *
+ * WARNINGS: none *
+ * *
+ * HISTORY: *
+ * 03/10/1995 JLB : Created. *
+ *=============================================================================================*/
+template
+int DynamicVectorClass