/* 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 "agds/screen.h" #include "agds/agds.h" #include "agds/animation.h" #include "agds/character.h" #include "agds/object.h" #include "agds/patch.h" #include "agds/region.h" #include "common/system.h" #include "graphics/managed_surface.h" namespace AGDS { int Screen::ObjectZCompare(const ObjectPtr &a, const ObjectPtr &b) { return b->z() - a->z(); } int Screen::AnimationZCompare(const ScreenAnimationDesc &a, const ScreenAnimationDesc &b) { return b.animation->z() - a.animation->z(); } Screen::Screen(AGDSEngine *engine, ObjectPtr object, ScreenLoadingType loadingType, const Common::String &prevScreen) : _engine(engine), _object(object), _background(nullptr), _name(object->getName()), _loadingType(loadingType), _previousScreen(prevScreen), _children(&ObjectZCompare), _animations(&AnimationZCompare), _applyingPatch(false), _characterNear(g_system->getHeight()), _characterFar(g_system->getHeight()), _fade(0) { add(object); } Screen::~Screen() { _children.clear(); } void Screen::scrollTo(Common::Point scroll) { int windowW = g_system->getWidth(); int windowH = g_system->getHeight(); if (!_background) { _scroll.x = _scroll.y = 0; return; } auto rect = _background->getRect(); int w = rect.width(), h = rect.height(); debug("picture size %dx%d", w, h); if (scroll.x + windowW > w) scroll.x = w - windowW; if (scroll.y + windowH > h) scroll.y = h - windowH; if (scroll.x < 0) scroll.x = 0; if (scroll.y < 0) scroll.y = 0; debug("setting scroll to %d,%d", scroll.x, scroll.y); _scroll = scroll; } float Screen::getZScale(int y) const { int h = g_system->getHeight(); int dy = h - y; if (dy > _characterNear) { if (dy < _characterFar) return 1.0f * (_characterFar - dy) / (_characterFar - _characterNear); else return 0.0f; } else return 1.0f; } bool Screen::add(ObjectPtr object) { if (object == NULL) { warning("refusing to add null to scene"); return false; } for (auto i = _children.begin(); i != _children.end();) { if (*i == object) { if (object->alive()) { debug("double adding object %s", object->getName().c_str()); return false; } else { debug("recovering object %s", object->getName().c_str()); object->alive(true); object->allowInitialise(false); return true; } } else ++i; } _children.insert(object); return true; } void Screen::add(AnimationPtr animation) { if (animation) _animations.insert({animation}); else warning("Screen: skipping null animation"); } bool Screen::remove(const AnimationPtr &animation) { if (!animation) return false; bool removed = false; for (auto i = _animations.begin(); i != _animations.end(); ++i) { if (i->animation == animation) { debug("removing animation %s:%s", animation->process().c_str(), animation->phaseVar().c_str()); i->removed = true; removed = true; } } return removed; } ObjectPtr Screen::find(const Common::String &name) { for (auto i = _children.begin(); i != _children.end(); ++i) { ObjectPtr &object = *i; if (object->getName() == name) return *i; } return ObjectPtr(); } bool Screen::remove(const ObjectPtr &object) { for (auto i = _children.begin(); i != _children.end(); ++i) { if (*i == object) { if (object->locked()) object->alive(false); else _children.erase(i); return true; } } return false; } bool Screen::remove(const Common::String &name) { for (auto i = _children.begin(); i != _children.end(); ++i) { const ObjectPtr &object = *i; if (object->getName() == name) { if (object->locked()) object->alive(false); else _children.erase(i); return true; } } return false; } AnimationPtr Screen::findAnimationByPhaseVar(const Common::String &phaseVar) { for (auto i = _animations.begin(); i != _animations.end(); ++i) { const auto &desc = *i; const auto &animation = desc.animation; if (!desc.removed && animation->phaseVar() == phaseVar) return animation; } return NULL; } void Screen::tick() { for (uint i = 0; i < _animations.size();) { const auto &desc = _animations.data()[i]; const auto &animation = desc.animation; if (!desc.removed && animation->tick()) ++i; else { debug("removing animation %s:%s", animation->process().c_str(), animation->phaseVar().c_str()); _animations.erase(_animations.begin() + i); } } } void Screen::paint(Graphics::Surface &backbuffer) const { auto ¤tInventoryObject = _engine->currentInventoryObject(); Character *character = _engine->currentCharacter(); auto child = _children.begin(); auto desc = _animations.begin(); while (child != _children.end() || desc != _animations.end() || character) { bool child_valid = child != _children.end(); bool animation_valid = desc != _animations.end(); if (animation_valid && desc->removed) { ++desc; continue; } bool z_valid = false; int z = 0; int render_type = -1; if (animation_valid) { const auto &animation = desc->animation; if (!z_valid || animation->z() > z) { z = animation->z(); z_valid = true; render_type = 1; } } if (child_valid) { if (!z_valid || (*child)->z() > z) { z = (*child)->z(); z_valid = true; render_type = 0; } } if (character) { if (!z_valid || character->z() > z) { z = character->z(); z_valid = true; render_type = 2; } } auto basePos = Common::Point() - _scroll; switch (render_type) { case 0: // debug("object z: %d", (*child)->z()); if ((*child) != currentInventoryObject && (*child)->alive()) { if ((*child)->scale() < 0) basePos = Common::Point(); (*child)->paint(*_engine, backbuffer, basePos); } ++child; break; case 1: // debug("animation z: %d", (*animation)->z()); if ((*desc).animation->scale() < 0) basePos = Common::Point(); (*desc).animation->paint(backbuffer, basePos); ++desc; break; case 2: // debug("character z: %d", character->z()); character->paint(backbuffer, basePos); character = nullptr; break; default: error("invalid logic in z-sort"); } } if (_fade != 0) { auto alpha = (100 - _fade) * 256 / 100; auto &format = backbuffer.format; for (int y = 0; y < backbuffer.h; ++y) { uint32 *line = (uint32 *)backbuffer.getBasePtr(0, y); int w = backbuffer.w; while (w--) { uint8 r, g, b; format.colorToRGB(*line, r, g, b); r = (r * alpha) >> 8; g = (g * alpha) >> 8; b = (b * alpha) >> 8; *line++ = format.RGBToColor(r, g, b); } } } } Common::Array Screen::find(Common::Point pos) const { Common::Array objects; objects.reserve(_children.size()); for (auto i = _children.begin(); i != _children.end(); ++i) { const auto &object = *i; auto visiblePos = (object->scale() >= 0) ? pos + _scroll : pos; if (object->pointIn(visiblePos) && object->alive()) { objects.insert_at(0, object); } } Character *character = _engine->currentCharacter(); if (character) { if (character->pointIn(pos - _scroll)) { objects.insert_at(0, character->object()); } } return objects; } Screen::KeyHandler Screen::findKeyHandler(const Common::String &keyName) { KeyHandler keyHandler; for (auto i = _children.begin(); i != _children.end(); ++i) { const auto &object = *i; if (!object->alive()) continue; uint ip = object->getKeyHandler(keyName); if (ip) { keyHandler.ip = ip; keyHandler.object = object; break; } } return keyHandler; } void Screen::load(const PatchPtr &patch) { debug("applying patch with %u objects", patch->objects.size()); _applyingPatch = true; for (uint i = 0; i < patch->objects.size(); ++i) { const Patch::Object &object = patch->objects[i]; debug("patch object %s %d", object.name.c_str(), object.flag); if (object.name == _name) continue; else if (object.flag <= 0) remove(object.name); else _engine->runObject(object.name, Common::String(), false); } _loadingType = patch->loadingType; if (!patch->prevScreenName.empty()) _previousScreen = patch->prevScreenName; _applyingPatch = false; } void Screen::save(const PatchPtr &patch) { patch->screenSaved = 1; patch->prevScreenName = _previousScreen; patch->loadingType = _loadingType; patch->objects.clear(); for (auto i = _children.begin(); i != _children.end(); ++i) { const auto &object = *i; if (!object->persistent() || !object->alive() || object->getName() == _name) continue; debug("saving patch object %s %d", object->getName().c_str(), object->alive()); patch->objects.push_back(Patch::Object(object->getName(), 1)); } } } // namespace AGDS