1 //
2 //	message.c
3 //
4 //	Dissolve in/out messages into the "matrix"
5 //
6 //
7 //
8 #include <windows.h>
9 #include "globals.h"
10 #include "message.h"
11 #include "matrix.h"
12 
13 //
14 // this isn't really a random-number generator. It's based on
15 // a 16bit CRC algorithm. With the right mask (0xb400) it is possible
16 // to call this function 65536 times and get a unique result every time
17 // with *NO* repeats. The results look random but they're not - if we
18 // call this function another 65536 times we get exactly the same results
19 // in the same order. This is necessary for fading in messages because
20 // we need to be guaranteed that all cells...it's completely uniform in
21 // operation but looks random enough to be very effective
22 //
23 WORD crc_msgrand(WORD reg)
24 {
25 	const WORD mask = 0xb400;
26 
27 	if(reg & 1)
28 		reg = (reg >> 1) ^ mask;
29 	else
30 		reg = (reg >> 1);
31 
32 	return reg;
33 }
34 
35 //
36 //	Set a new message based on font and text
37 //
38 void SetMatrixMessage(MATRIX_MESSAGE *msg, HFONT hFont, TCHAR *text)
39 {
40 	HDC		hdc;
41 	RECT	rect;
42 	int		x, y;
43 
44 	HDC		hdcMessage;
45 	HBITMAP hbmMessage;
46 
47 	HANDLE	hOldFont, hOldBmp;
48 
49 	//
50 	// Create a monochrome off-screen buffer
51 	//
52 	hdc = GetDC(NULL);
53 
54 	hdcMessage = CreateCompatibleDC(hdc);
55 	hbmMessage = CreateBitmap(MAXMSG_WIDTH, MAXMSG_HEIGHT, 1, 1, 0);
56 	hOldBmp    = SelectObject(hdcMessage, hbmMessage);
57 
58 	ReleaseDC(NULL, hdc);
59 
60 	//
61 	// Draw text into bitmap
62 	//
63 	SetRect(&rect, 0, 0, msg->width, MAXMSG_HEIGHT);
64 	FillRect(hdcMessage, &rect, GetStockObject(WHITE_BRUSH));
65 
66 	hOldFont = SelectObject(hdcMessage, g_hFont);
67 	DrawText(hdcMessage, text, -1, &rect, DT_CENTER|DT_VCENTER|DT_WORDBREAK|DT_CALCRECT);
68 
69 	OffsetRect(&rect, (msg->width-(rect.right-rect.left))/2, (msg->height-(rect.bottom-rect.top))/2);
70 	DrawText(hdcMessage, text, -1, &rect, DT_CENTER|DT_VCENTER|DT_WORDBREAK);
71 
72 	//
73 	// Convert bitmap into an array of cells for easy drawing
74 	//
75 	for(y = 0; y < msg->height; y++)
76 	{
77 		for(x = 0; x < msg->width; x++)
78 		{
79 			msg->message[x][y] = GetPixel(hdcMessage, x, y) ? 0 : 1;
80 		}
81 	}
82 
83 	//
84 	//	Cleanup
85 	//
86 	SelectObject(hdcMessage, hOldFont);
87 	SelectObject(hdcMessage, hOldBmp);
88 
89 	DeleteDC(hdcMessage);
90 	DeleteObject(hbmMessage);
91 }
92 
93 //
94 //	Draw any part of the message that is visible. Make the
95 //  message "shimmer" by using a random glyph each time
96 //
97 void DrawMatrixMessage(MATRIX *matrix, MATRIX_MESSAGE *msg, HDC hdc)
98 {
99 	int x, y;
100 
101 	for(x = 0; x < msg->width; x++)
102 		for(y = 0; y < msg->height; y++)
103 			if((msg->message[x][y] & 0x8000) &&
104 			   (msg->message[x][y] & 0x00FF))
105 			{
106 				DrawGlyph(matrix, hdc, x * GLYPH_WIDTH, y * GLYPH_HEIGHT, RandomGlyph(MAX_INTENSITY));
107 			}
108 }
109 
110 //
111 //	Reveal specified amount of message
112 //
113 void RevealMatrixMessage(MATRIX_MESSAGE *msg, int amount)
114 {
115 	while(amount--)
116 	{
117 		int pos;
118 
119 		msg->random_reg1 = crc_msgrand(msg->random_reg1);
120 		pos = msg->random_reg1 & 0xffff;
121 
122 		msg->message[pos / 256][pos % 256] |= GLYPH_REDRAW;
123 	}
124 }
125 
126 //
127 //	Reset (hide) the message
128 //
129 void ClearMatrixMessage(MATRIX_MESSAGE *msg)
130 {
131 	int x, y;
132 
133 	for(x = 0; x < msg->width; x++)
134 		for(y = 0; y < msg->height; y++)
135 			msg->message[x][y] = 0;
136 }
137 
138 //
139 // convert from 50-500 (fast-slow) to slow(50) - fast(500)
140 //
141 int MessageSpeed()
142 {
143 	return (MSGSPEED_MAX-MSGSPEED_MIN) - (g_nMessageSpeed-MSGSPEED_MIN) + MSGSPEED_MIN;
144 }
145 
146 //
147 //	Called once for each iteration of the matrix
148 //
149 void DoMatrixMessage(HDC hdc, MATRIX *matrix)
150 {
151 	MATRIX_MESSAGE *msg = matrix->message;
152 
153 	int RealSpeed = MessageSpeed();
154 
155 	if(g_nNumMessages > 0)
156 	{
157 		// nothing to do yet..
158 		if(msg->counter++ < 0)
159 			return;
160 
161 		// has counter reached limit..clear the message
162 		if(msg->counter++ == RealSpeed / 2 + (RealSpeed/4))
163 			ClearMatrixMessage(msg);
164 
165 		// reset counter + display a new message
166 		if(msg->counter >= RealSpeed)
167 		{
168 			// mark all message-cells as being "invisible" so the
169 			// message gets cleared by the matrix decoding naturally
170 
171 			if(g_fRandomizeMessages)
172 				msg->msgindex = crc_rand() % g_nNumMessages;
173 			else
174 				msg->msgindex = (msg->msgindex + 1) % g_nNumMessages;
175 
176 			// make a new message..initially invisible
177 			SetMatrixMessage(msg, 0, g_szMessages[msg->msgindex]);
178 
179 			msg->counter = -(int)(crc_rand() % MSGSPEED_MAX);
180 		}
181 		// reveal the next part of the message
182 		else if(msg->counter < RealSpeed / 2)
183 		{
184 			int w = (g_nMessageSpeed - MSGSPEED_MIN);
185 			w = (1 << 16) + ((w<<16) / MSGSPEED_MAX);
186 			w = (w * 3 * g_nMessageSpeed) >> 16;
187 
188 			RevealMatrixMessage(msg, w + 100);
189 		}
190 
191 		//
192 		// draw whatever part of the message is visible at this time
193 		//
194 		DrawMatrixMessage(matrix, msg, hdc);
195 	}
196 }
197 
198 //
199 //	Set current font used for messages
200 //
201 void SetMessageFont(HWND hwnd, TCHAR *szFontName, int nPointSize, BOOL fBold)
202 {
203 	int		lfHeight;
204 	HDC		hdc;
205 	HFONT	hFont;
206 
207 	hdc = GetDC(hwnd);
208 
209 	lfHeight = -MulDiv(nPointSize, GetDeviceCaps(hdc, LOGPIXELSY), 72);
210 
211 	ReleaseDC(hwnd, hdc);
212 
213 	hFont = CreateFont(lfHeight, 0, 0, 0, fBold ? FW_BOLD: FW_NORMAL, 0, 0, 0,
214 		ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
215 		ANTIALIASED_QUALITY, DEFAULT_PITCH, szFontName);
216 
217 	if(hFont != 0)
218 	{
219 		if(g_hFont != 0)
220 			DeleteObject(g_hFont);
221 
222 		g_hFont = hFont;
223 	}
224 }
225 
226 //
227 //	Create a message!
228 //
229 MATRIX_MESSAGE *InitMatrixMessage(HWND hwnd, int width, int height)
230 {
231 	MATRIX_MESSAGE *msg;
232 
233 	if((msg = malloc(sizeof(MATRIX_MESSAGE))) == 0)
234 		return 0;
235 
236 	ClearMatrixMessage(msg);
237 
238 	msg->msgindex = 0;
239 	msg->width    = min(width, MAXMSG_WIDTH);
240 	msg->height   = min(height, MAXMSG_HEIGHT);
241 	msg->counter  = -(int)(crc_rand() % MSGSPEED_MIN + MSGSPEED_MIN);
242 
243 	msg->random_reg1 = (WORD)GetTickCount();
244 
245 	SetMessageFont(hwnd, g_szFontName, g_nFontSize, g_fFontBold);
246 
247 	SetMatrixMessage(msg, 0, g_szMessages[0]);
248 
249 	return msg;
250 }
251