/** * Draw command box on screen */ void FWRenderer::drawCommand() { unsigned int i; int x = 10, y = _cmdY; drawPlainBox(x, y, 301, 11, 0); drawBorder(x - 1, y - 1, 302, 12, 2); x += 2; y += 2; for (i = 0; i < _cmd.size(); i++) { x = drawChar(_cmd[i], x, y); } }
/** * Draw Line * @param x Line end coordinate * @param y Line end coordinate * @param width Horizontal line length * @param height Vertical line length * @param color Line color * @note Either width or height must be equal to 1 */ void FWRenderer::drawLine(int x, int y, int width, int height, byte color) { // this line is a special case of rectangle ;-) drawPlainBox(x, y, width, height, color); }
/** * Draw message in a box * @param str Message to draw * @param x Top left message box corner coordinate * @param y Top left message box corner coordinate * @param width Message box width * @param color Message box background color (Or if negative draws only the text) * @note Negative colors are used in Operation Stealth's timed cutscenes * (e.g. when first meeting The Movement for the Liberation of Santa Paragua). */ void FWRenderer::drawMessage(const char *str, int x, int y, int width, int color) { int i, tx, ty, tw; int line = 0, words = 0, cw = 0; int space = 0, extraSpace = 0; const bool isAmiga = (g_cine->getPlatform() == Common::kPlatformAmiga); if (color >= 0) { if (isAmiga) drawTransparentBox(x, y, width, 4); else drawPlainBox(x, y, width, 4, color); } tx = x + 4; ty = str[0] ? y - 5 : y + 4; tw = width - 8; for (i = 0; str[i]; i++, line--) { // Fit line of text into textbox if (!line) { while (str[i] == ' ') i++; line = fitLine(str + i, tw, words, cw); if ( str[i + line] != '\0' && str[i + line] != 0x7C && words) { space = (tw - cw) / words; extraSpace = (tw - cw) % words; } else { space = 5; extraSpace = 0; } ty += 9; if (color >= 0) { if (isAmiga) drawTransparentBox(x, ty, width, 9); else drawPlainBox(x, ty, width, 9, color); } tx = x + 4; } // draw characters if (str[i] == ' ') { tx += space + extraSpace; if (extraSpace) { extraSpace = 0; } } else { tx = drawChar(str[i], tx, ty); } } ty += 9; if (color >= 0) { if (isAmiga) drawTransparentBox(x, ty, width, 4); else drawPlainBox(x, ty, width, 4, color); drawDoubleBorder(x, y, width, ty - y + 4, isAmiga ? 18 : 2); } }
/** * Draw one overlay * @param it Overlay info * @todo Add handling of type 22 overlays */ void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) { int len, idx, width, height; ObjectStruct *obj; AnimData *sprite; byte *mask; byte color; switch (it->type) { // color sprite case 0: if (g_cine->_objectTable[it->objIdx].frame < 0) { break; } sprite = &g_cine->_animDataTable[g_cine->_objectTable[it->objIdx].frame]; len = sprite->_realWidth * sprite->_height; mask = new byte[len]; generateMask(sprite->data(), mask, len, g_cine->_objectTable[it->objIdx].part); remaskSprite(mask, it); drawMaskedSprite(g_cine->_objectTable[it->objIdx], mask); delete[] mask; break; // game message case 2: if (it->objIdx >= g_cine->_messageTable.size()) { return; } _messageLen += g_cine->_messageTable[it->objIdx].size(); drawMessage(g_cine->_messageTable[it->objIdx].c_str(), it->x, it->y, it->width, it->color); if (it->color >= 0) { // This test isn't in Future Wars's implementation waitForPlayerClick = 1; } break; // action failure message case 3: idx = it->objIdx * 4 + g_cine->_rnd.getRandomNumber(3); len = strlen(failureMessages[idx]); _messageLen += len; width = 6 * len + 20; width = width > 300 ? 300 : width; // The used color here differs from Future Wars drawMessage(failureMessages[idx], (320 - width) / 2, 80, width, _messageBg); waitForPlayerClick = 1; break; // bitmap case 4: if (g_cine->_objectTable[it->objIdx].frame >= 0) { FWRenderer::renderOverlay(it); } break; // masked background case 20: assert(it->objIdx < NUM_MAX_OBJECT); var5 = it->x; // A global variable updated here! obj = &g_cine->_objectTable[it->objIdx]; sprite = &g_cine->_animDataTable[obj->frame]; if (obj->frame < 0 || it->x < 0 || it->x > 8 || !_bgTable[it->x].bg || sprite->_bpp != 1) { break; } maskBgOverlay(_bgTable[it->x].bg, sprite->data(), sprite->_realWidth, sprite->_height, _backBuffer, obj->x, obj->y); break; // FIXME: Implement correct drawing of type 21 overlays. // Type 21 overlays aren't just filled rectangles, I found their drawing routine // from Operation Stealth's drawSprite routine. So they're likely some kind of sprites // and it's just a coincidence that the oxygen meter during the first arcade sequence // works even somehow currently. I tried the original under DOSBox and the oxygen gauge // is a long red bar that gets shorter as the air runs out. case 21: // A filled rectangle: case 22: // TODO: Check it this implementation really works correctly (Some things might be wrong, needs testing). assert(it->objIdx < NUM_MAX_OBJECT); obj = &g_cine->_objectTable[it->objIdx]; color = obj->part & 0x0F; width = obj->frame; height = obj->costume; drawPlainBox(obj->x, obj->y, width, height, color); debug(5, "renderOverlay: type=%d, x=%d, y=%d, width=%d, height=%d, color=%d", it->type, obj->x, obj->y, width, height, color); break; // something else default: FWRenderer::renderOverlay(it); break; } }
/** * Draw one overlay * @param it Overlay info * @todo Add handling of type 22 overlays */ void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) { int len, idx, width, height; ObjectStruct *obj; AnimData *sprite; byte color; switch (it->type) { // color sprite case 0: if (g_cine->_objectTable[it->objIdx].frame < 0) { break; } sprite = &g_cine->_animDataTable[g_cine->_objectTable[it->objIdx].frame]; drawSprite(&(*it), sprite->data(), sprite->_realWidth, sprite->_height, _backBuffer, g_cine->_objectTable[it->objIdx].x, g_cine->_objectTable[it->objIdx].y, g_cine->_objectTable[it->objIdx].part, sprite->_bpp); break; // game message case 2: if (it->objIdx >= g_cine->_messageTable.size()) { return; } _messageLen += g_cine->_messageTable[it->objIdx].size(); drawMessage(g_cine->_messageTable[it->objIdx].c_str(), it->x, it->y, it->width, it->color); if (it->color >= 0) { // This test isn't in Future Wars's implementation waitForPlayerClick = 1; } break; // action failure message case 3: idx = it->objIdx * 4 + g_cine->_rnd.getRandomNumber(3); len = strlen(failureMessages[idx]); _messageLen += len; width = 6 * len + 20; width = width > 300 ? 300 : width; // The used color here differs from Future Wars drawMessage(failureMessages[idx], (320 - width) / 2, 80, width, _messageBg); waitForPlayerClick = 1; break; // bitmap case 4: if (g_cine->_objectTable[it->objIdx].frame >= 0) { FWRenderer::renderOverlay(it); } break; // masked background case 20: assert(it->objIdx < NUM_MAX_OBJECT); var5 = it->x; // A global variable updated here! obj = &g_cine->_objectTable[it->objIdx]; sprite = &g_cine->_animDataTable[obj->frame]; if (obj->frame < 0 || it->x < 0 || it->x > 8 || !_bgTable[it->x].bg || sprite->_bpp != 1) { break; } maskBgOverlay(_bgTable[it->x].bg, sprite->data(), sprite->_realWidth, sprite->_height, _backBuffer, obj->x, obj->y); break; case 22: // TODO: Check it this implementation really works correctly (Some things might be wrong, needs testing). assert(it->objIdx < NUM_MAX_OBJECT); obj = &g_cine->_objectTable[it->objIdx]; color = obj->part & 0x0F; width = obj->frame; height = obj->costume; drawPlainBox(obj->x, obj->y, width, height, color); debug(5, "renderOverlay: type=%d, x=%d, y=%d, width=%d, height=%d, color=%d", it->type, obj->x, obj->y, width, height, color); break; // something else default: FWRenderer::renderOverlay(it); break; } }