1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * Additional copyright for this file:
8 * Copyright (C) 1994-1998 Revolution Software Ltd.
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 */
24
25
26 #include "common/rect.h"
27
28 #include "sword2/sword2.h"
29 #include "sword2/defs.h"
30 #include "sword2/header.h"
31 #include "sword2/mouse.h"
32 #include "sword2/screen.h"
33
34 namespace Sword2 {
35
36 #define MENUDEEP 40
37 #define MAXMENUANIMS 8
38
clearIconArea(int menu,int pocket,Common::Rect * r)39 void Mouse::clearIconArea(int menu, int pocket, Common::Rect *r) {
40 byte *buf = _vm->_screen->getScreen();
41 int16 screenWide = _vm->_screen->getScreenWide();
42 byte menuIconWidth;
43
44 // Initialize menu icon width at correct size
45 // depending if we are using pc or psx version.
46 if (Sword2Engine::isPsx())
47 menuIconWidth = RDMENU_PSXICONWIDE;
48 else
49 menuIconWidth = RDMENU_ICONWIDE;
50
51
52 r->top = menu * (RENDERDEEP + MENUDEEP) + (MENUDEEP - RDMENU_ICONDEEP) / 2;
53 r->bottom = r->top + RDMENU_ICONDEEP;
54 r->left = RDMENU_ICONSTART + pocket * (menuIconWidth + RDMENU_ICONSPACING);
55 r->right = r->left + menuIconWidth;
56
57 byte *dst = buf + r->top * screenWide + r->left;
58
59 for (int i = 0; i < RDMENU_ICONDEEP; i++) {
60 memset(dst, 0, menuIconWidth);
61 dst += screenWide;
62 }
63 }
64
65 /**
66 * This function should be called regularly to process the menubar system. The
67 * rate at which this function is called will dictate how smooth the menu
68 * system is.
69 */
70
processMenu()71 void Mouse::processMenu() {
72 uint8 menu;
73 uint8 i, j;
74 uint8 frameCount;
75 Common::Rect r1, r2;
76 static int32 lastTime = 0;
77
78 byte *buf = _vm->_screen->getScreen();
79 int16 screenWide = _vm->_screen->getScreenWide();
80 byte menuIconWidth;
81
82 if (Sword2Engine::isPsx())
83 menuIconWidth = RDMENU_PSXICONWIDE;
84 else
85 menuIconWidth = RDMENU_ICONWIDE;
86
87
88 if (lastTime == 0) {
89 lastTime = _vm->getMillis();
90 frameCount = 1;
91 } else {
92 int32 delta = _vm->getMillis() - lastTime;
93
94 if (delta > 250) {
95 lastTime += delta;
96 delta = 250;
97 frameCount = 1;
98 } else {
99 frameCount = (uint8) ((_iconCount + 8) * delta / 750);
100 lastTime += frameCount * 750 / (_iconCount + 8);
101 }
102 }
103
104 // Note: The "almost hidden" menu state exists only so that the menu
105 // will be redrawn one last time before it's completely hidden. We do
106 // not need a corresponding "almost shown" state because the menu will
107 // always be redrawn while it's shown anyway. (We may want to change
108 // this later.)
109
110 while (frameCount-- > 0) {
111 for (menu = RDMENU_TOP; menu <= RDMENU_BOTTOM; menu++) {
112 if (_menuStatus[menu] == RDMENU_HIDDEN || _menuStatus[menu] == RDMENU_ALMOST_HIDDEN || _menuStatus[menu] == RDMENU_SHOWN)
113 continue;
114
115 int target, direction, nextState;
116
117 if (_menuStatus[menu] == RDMENU_OPENING) {
118 target = MAXMENUANIMS;
119 direction = 1;
120 nextState = RDMENU_SHOWN;
121 } else {
122 target = 0;
123 direction = -1;
124 nextState = RDMENU_ALMOST_HIDDEN;
125 }
126
127 bool complete = true;
128
129 // Propagate animation from the first icon...
130 for (i = RDMENU_MAXPOCKETS - 1; i > 0; i--) {
131 _pocketStatus[menu][i] = _pocketStatus[menu][i - 1];
132
133 if (_pocketStatus[menu][i] != target)
134 complete = false;
135 }
136
137 if (_pocketStatus[menu][i] != target)
138 complete = false;
139
140 // ...and animate the first icon
141 if (_pocketStatus[menu][0] != target)
142 _pocketStatus[menu][0] += direction;
143
144 if (complete)
145 _menuStatus[menu] = nextState;
146 }
147 }
148
149 for (menu = RDMENU_TOP; menu <= RDMENU_BOTTOM; menu++) {
150 if (_menuStatus[menu] == RDMENU_HIDDEN)
151 continue;
152
153 if (_menuStatus[menu] == RDMENU_ALMOST_HIDDEN)
154 _menuStatus[menu] = RDMENU_HIDDEN;
155
156 // Draw the menu here.
157 int32 curx = RDMENU_ICONSTART + menuIconWidth / 2;
158 int32 cury = (MENUDEEP / 2) + (RENDERDEEP + MENUDEEP) * menu;
159
160 for (i = 0; i < RDMENU_MAXPOCKETS; i++) {
161 if (_icons[menu][i]) {
162 int32 xoff, yoff;
163
164 // Since we no longer clear the screen after
165 // each frame we need to clear the icon area.
166
167 clearIconArea(menu, i, &r1);
168
169 if (_pocketStatus[menu][i] == MAXMENUANIMS) {
170 xoff = (menuIconWidth / 2);
171 r2.left = curx - xoff;
172 r2.right = r2.left + menuIconWidth;
173 yoff = (RDMENU_ICONDEEP / 2);
174 r2.top = cury - yoff;
175 r2.bottom = r2.top + RDMENU_ICONDEEP;
176 } else {
177 xoff = (menuIconWidth / 2) * _pocketStatus[menu][i] / MAXMENUANIMS;
178 r2.left = curx - xoff;
179 r2.right = curx + xoff;
180 yoff = (RDMENU_ICONDEEP / 2) * _pocketStatus[menu][i] / MAXMENUANIMS;
181 r2.top = cury - yoff;
182 r2.bottom = cury + yoff;
183 }
184
185 if (xoff != 0 && yoff != 0) {
186 byte *dst = buf + r2.top * screenWide + r2.left;
187 byte *src = _icons[menu][i];
188
189 if (_pocketStatus[menu][i] != MAXMENUANIMS) {
190 _vm->_screen->scaleImageFast(
191 dst, screenWide, r2.right - r2.left, r2.bottom - r2.top,
192 src, menuIconWidth, menuIconWidth, RDMENU_ICONDEEP);
193 } else {
194 for (j = 0; j < RDMENU_ICONDEEP; j++) {
195 memcpy(dst, src, menuIconWidth);
196 src += menuIconWidth;
197 dst += screenWide;
198 }
199 }
200 }
201 _vm->_screen->updateRect(&r1);
202 }
203 curx += (RDMENU_ICONSPACING + menuIconWidth);
204 }
205 }
206 }
207
208 /**
209 * This function brings a specified menu into view.
210 * @param menu RDMENU_TOP or RDMENU_BOTTOM, depending on which menu to show
211 * @return RD_OK, or an error code
212 */
213
showMenu(uint8 menu)214 int32 Mouse::showMenu(uint8 menu) {
215
216 // Do not show menu in PSX version, as there was really
217 // nothing similar in the original game (menu was started
218 // using SELECT button in psx pad)
219 if (Sword2Engine::isPsx() && menu == RDMENU_TOP)
220 return RD_OK;
221
222 // Check for invalid menu parameter
223 if (menu > RDMENU_BOTTOM)
224 return RDERR_INVALIDMENU;
225
226 // Check that the menu is not currently shown, or in the process of
227 // being shown.
228 if (_menuStatus[menu] == RDMENU_SHOWN || _menuStatus[menu] == RDMENU_OPENING)
229 return RDERR_INVALIDCOMMAND;
230
231 _menuStatus[menu] = RDMENU_OPENING;
232 return RD_OK;
233 }
234
235 /**
236 * This function hides a specified menu.
237 * @param menu RDMENU_TOP or RDMENU_BOTTOM depending on which menu to hide
238 * @return RD_OK, or an error code
239 */
240
hideMenu(uint8 menu)241 int32 Mouse::hideMenu(uint8 menu) {
242
243 // In PSX version, do nothing. There is no such menu.
244 if (Sword2Engine::isPsx() && menu == RDMENU_TOP)
245 return RD_OK;
246
247 // Check for invalid menu parameter
248 if (menu > RDMENU_BOTTOM)
249 return RDERR_INVALIDMENU;
250
251 // Check that the menu is not currently hidden, or in the process of
252 // being hidden.
253 if (_menuStatus[menu] == RDMENU_HIDDEN || _menuStatus[menu] == RDMENU_CLOSING)
254 return RDERR_INVALIDCOMMAND;
255
256 _menuStatus[menu] = RDMENU_CLOSING;
257 return RD_OK;
258 }
259
260 /**
261 * This function hides both menus immediately.
262 */
263
closeMenuImmediately()264 void Mouse::closeMenuImmediately() {
265 Common::Rect r;
266 int i;
267
268 _menuStatus[RDMENU_TOP] = RDMENU_HIDDEN;
269 _menuStatus[RDMENU_BOTTOM] = RDMENU_HIDDEN;
270
271 for (i = 0; i < RDMENU_MAXPOCKETS; i++) {
272 if (_icons[RDMENU_TOP][i]) {
273 clearIconArea(RDMENU_TOP, i, &r);
274 _vm->_screen->updateRect(&r);
275 }
276 if (_icons[RDMENU_BOTTOM][i]) {
277 clearIconArea(RDMENU_BOTTOM, i, &r);
278 _vm->_screen->updateRect(&r);
279 }
280 }
281
282 memset(_pocketStatus, 0, sizeof(uint8) * 2 * RDMENU_MAXPOCKETS);
283 }
284
285 /**
286 * This function sets a menubar icon.
287 * @param menu RDMENU_TOP or RDMENU_BOTTOM, depending on which menu to change
288 * @param pocket the menu pocket to change
289 * @param icon icon data, or NULL to clear the icon
290 * @return RD_OK, or an error code
291 */
292
setMenuIcon(uint8 menu,uint8 pocket,byte * icon)293 int32 Mouse::setMenuIcon(uint8 menu, uint8 pocket, byte *icon) {
294 Common::Rect r;
295 byte menuIconWidth;
296
297 if (Sword2Engine::isPsx())
298 menuIconWidth = RDMENU_PSXICONWIDE;
299 else
300 menuIconWidth = RDMENU_ICONWIDE;
301
302 // Check for invalid menu parameter.
303 if (menu > RDMENU_BOTTOM)
304 return RDERR_INVALIDMENU;
305
306 // Check for invalid pocket parameter
307 if (pocket >= RDMENU_MAXPOCKETS)
308 return RDERR_INVALIDPOCKET;
309
310 // If there is an icon in the requested menu/pocket, clear it out.
311 if (_icons[menu][pocket]) {
312 _iconCount--;
313 free(_icons[menu][pocket]);
314 _icons[menu][pocket] = NULL;
315 clearIconArea(menu, pocket, &r);
316 _vm->_screen->updateRect(&r);
317 }
318
319 // Only put the icon in the pocket if it is not NULL
320 if (icon != NULL) {
321 _iconCount++;
322 _icons[menu][pocket] = (byte *)malloc(menuIconWidth * RDMENU_ICONDEEP);
323 if (_icons[menu][pocket] == NULL)
324 return RDERR_OUTOFMEMORY;
325 memcpy(_icons[menu][pocket], icon, menuIconWidth * RDMENU_ICONDEEP);
326 }
327
328 return RD_OK;
329 }
330
331 } // End of namespace Sword2
332