/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * 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 "dgds/clock.h" #include "dgds/globals.h" #include "dgds/dgds.h" #include "dgds/font.h" #include "dgds/includes.h" #include "dgds/request.h" namespace Dgds { class DragonTimeGlobal : public ReadWriteGlobal { public: DragonTimeGlobal(uint16 num, int16 *val, Clock &clock) : ReadWriteGlobal(num, val), _clock(clock) {} int16 set(int16 val) override { if (val != ReadWriteGlobal::get()) { ReadWriteGlobal::set(val); // notify that the time changed _clock.addGameTime(0); } return val; } private: Clock &_clock; }; Clock::Clock() : _visibleUser(true), _visibleScript(false), _days(0), _days2(0), _hours(0), _mins(0), _gameMinsAdded(0), _gameTicksUp(0), _gameTicksDown(0), _lastPlayTime(0), _millis(0) { } Global *Clock::getDaysGlobal(uint16 num) { return new DragonTimeGlobal(num, &_days, *this); } Global *Clock::getDays2Global(uint16 num) { return new ReadWriteGlobal(num, &_days2); } Global *Clock::getHoursGlobal(uint16 num) { return new DragonTimeGlobal(num, &_hours, *this); } Global *Clock::getMinsGlobal(uint16 num) { return new DragonTimeGlobal(num, &_mins, *this); } Global *Clock::getGameMinsAddedGlobal(uint16 num) { return new ReadOnlyGlobal(num, &_gameMinsAdded); } Global *Clock::getGameTicksUpGlobal(uint16 num) { return new ReadOnlyGlobal(num, &_gameTicksUp); } Global *Clock::getGameTicksDownGlobal(uint16 num) { return new ReadOnlyGlobal(num, &_gameTicksDown); } static int16 DAYS_PER_MONTH[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; void Clock::addGameTime(int mins) { _gameMinsAdded += mins; int nhours = (_mins + mins) / 60; _mins = (_mins + mins) % 60; if (_mins < 0) { _mins += 0x3c; nhours--; } int ndays = (_hours + nhours) / 24; _hours = (_hours + nhours) % 24; if (_hours < 0) { _hours += 24; _days -= 1; } _days += ndays; // TODO: if any change was made to days/hours/mins.. //if (plusmins + nhours + ndays != 0) // UINT_39e5_0ffa = 0; } Common::String Clock::getTimeStr() const { int month = 0; int day = _days + _days2 + 1; while (day > DAYS_PER_MONTH[month]) { day -= DAYS_PER_MONTH[month]; month++; if (month == ARRAYSIZE(DAYS_PER_MONTH)) month = 0; } DgdsEngine *engine = DgdsEngine::getInstance(); if (engine->getGameId() == GID_WILLY) { return Common::String::format("DAY %d, %2d:%02d", day - 1, _hours, _mins); } else if (engine->getGameLang() == Common::EN_ANY) { return Common::String::format("%2d/%02d %2d:%02d", month + 1, day, _hours, _mins); } else if (engine->getGameLang() == Common::DE_DEU) { return Common::String::format("%2d.%d %2d.%02d", day, month + 1, _hours, _mins); } else { error("Unsupported language %d", DgdsEngine::getInstance()->getGameLang()); } } void Clock::draw(Graphics::ManagedSurface &surf) { DgdsEngine *engine = DgdsEngine::getInstance(); if (!_visibleUser || !_visibleScript || engine->getGameId() != GID_DRAGON) return; const Common::String clockStr = getTimeStr(); const FontManager *fontman = engine->getFontMan(); const DgdsFont *font = fontman->getFont(FontManager::k4x5Font); int width = font->getMaxCharWidth() * 12 + 3; _drawPos.top = 0; _drawPos.bottom = font->getFontHeight() + 6; _drawPos.right = SCREEN_WIDTH; _drawPos.left = SCREEN_WIDTH - width; RequestData::fillBackground(&surf, _drawPos.left + 2, _drawPos.top + 2, _drawPos.width() - 4, _drawPos.height() - 4, 65); font->drawString(&surf, clockStr, _drawPos.left + 3, _drawPos.top + 3, _drawPos.width(), 0); } // Confirmed by timing the clock tick up in the inventory of Dragon and Willy Beamish // - 5 game minutes are exactly 25 real time seconds. static const int MILLIS_PER_GAME_MIN = 5000; static const int MILLIS_PER_TIMER_TICK = 60; // ~16.667 ticks per second. void Clock::update(bool gameRunning) { uint32 playTimeNow = DgdsEngine::getInstance()->getThisFrameMs(); // These timers are updated whether or not the game is running _gameTicksUp = playTimeNow / MILLIS_PER_TIMER_TICK; // Is there any reason to make this variable anything other than negative the other one?? // There seems to be no other way to set them as they are RO globals. _gameTicksDown = -_gameTicksUp; uint32 lastLastPlayTime = _lastPlayTime; _lastPlayTime = playTimeNow; if (lastLastPlayTime == 0 || !gameRunning) return; _millis += playTimeNow - lastLastPlayTime; int16 mins_to_add = _millis / MILLIS_PER_GAME_MIN; _millis = _millis % MILLIS_PER_GAME_MIN; if (mins_to_add) addGameTime(mins_to_add); } Common::String Clock::dump() const { return Common::String::format("days %d hours %d mins %d", _days, _mins, _hours); } Common::Error Clock::syncState(Common::Serializer &s) { s.syncAsUint32LE(_lastPlayTime); s.syncAsUint32LE(_millis); s.syncAsSint16LE(_gameMinsAdded); s.syncAsSint16LE(_gameTicksUp); s.syncAsSint16LE(_gameTicksDown); s.syncAsSint16LE(_days); s.syncAsSint16LE(_days2); s.syncAsSint16LE(_hours); s.syncAsSint16LE(_mins); s.syncAsSint16LE(_drawPos.left); s.syncAsSint16LE(_drawPos.top); s.syncAsSint16LE(_drawPos.right); s.syncAsSint16LE(_drawPos.bottom); s.syncAsByte(_visibleScript); s.syncAsByte(_visibleUser); return Common::kNoError; } } // end namespace Dgds