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 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "engines/nancy/nancy.h"
24 #include "engines/nancy/graphics.h"
25 #include "engines/nancy/resource.h"
26 #include "engines/nancy/cursor.h"
27 #include "engines/nancy/sound.h"
28 #include "engines/nancy/input.h"
29 #include "engines/nancy/util.h"
30 #include "engines/nancy/constants.h"
31
32 #include "engines/nancy/ui/inventorybox.h"
33
34 #include "engines/nancy/state/scene.h"
35
36 #include "engines/nancy/ui/scrollbar.h"
37
38 namespace Nancy {
39 namespace UI {
40
InventoryBox(RenderObject & redrawFrom)41 InventoryBox::InventoryBox(RenderObject &redrawFrom) :
42 RenderObject(redrawFrom, 6),
43 _scrollbar(nullptr),
44 _curtains(*this, this),
45 _scrollbarPos(0),
46 _curtainsFrameTime(0) {}
47
~InventoryBox()48 InventoryBox::~InventoryBox() {
49 _fullInventorySurface.free();
50 _iconsSurface.free(); delete _scrollbar;
51 }
52
init()53 void InventoryBox::init() {
54 Common::SeekableReadStream &stream = *g_nancy->getBootChunkStream("INV");
55 stream.seek(0, SEEK_SET);
56
57 _order.clear();
58
59 Common::Rect scrollbarSrcBounds;
60 readRect(stream, scrollbarSrcBounds);
61 Common::Point scrollbarDefaultPos;
62 scrollbarDefaultPos.x = stream.readUint16LE();
63 scrollbarDefaultPos.y = stream.readUint16LE();
64 uint16 scrollbarMaxScroll = stream.readUint16LE();
65
66 stream.seek(0xD6, SEEK_SET);
67
68 uint numFrames = g_nancy->getConstants().numCurtainAnimationFrames;
69 _curtainsSrc.resize(numFrames * 2);
70 for (uint i = 0; i < numFrames * 2; ++i) {
71 readRect(stream, _curtainsSrc[i]);
72 }
73
74 readRect(stream, _screenPosition);
75 _curtainsFrameTime = stream.readUint16LE();
76
77 Common::String inventoryBoxIconsImageName;
78 readFilename(stream, inventoryBoxIconsImageName);
79 readFilename(stream, _inventoryCursorsImageName);
80
81 stream.skip(8);
82 readRect(stream, _emptySpace);
83
84 char itemName[20];
85 uint itemNameLength = g_nancy->getGameType() == kGameTypeVampire ? 15 : 20;
86
87 _itemDescriptions.reserve(g_nancy->getConstants().numItems);
88 for (uint i = 0; i < g_nancy->getConstants().numItems; ++i) {
89 stream.read(itemName, itemNameLength);
90 itemName[itemNameLength - 1] = '\0';
91 _itemDescriptions.push_back(ItemDescription());
92 ItemDescription &desc = _itemDescriptions.back();
93 desc.name = Common::String(itemName);
94 desc.oneTimeUse = stream.readUint16LE();
95 readRect(stream, desc.sourceRect);
96 }
97
98 g_nancy->_resource->loadImage(inventoryBoxIconsImageName, _iconsSurface);
99
100 _fullInventorySurface.create(_screenPosition.width(), _screenPosition.height() * ((g_nancy->getConstants().numItems / 4) + 1), g_nancy->_graphicsManager->getScreenPixelFormat());
101 Common::Rect sourceRect = _screenPosition;
102 sourceRect.moveTo(0, 0);
103 _drawSurface.create(_fullInventorySurface, sourceRect);
104
105 for (uint i = 0; i < 4; ++i) {
106 Common::Rect &r = _itemHotspots[i].hotspot;
107 r = _screenPosition;
108 r.setWidth(r.width() / 2);
109 r.setHeight(r.height() / 2);
110 r.translate((i % 2) * r.width(), (i / 2) * r.height());
111 }
112
113 RenderObject::init();
114
115 _scrollbar = new Scrollbar(NancySceneState.getFrame(), 9, scrollbarSrcBounds, scrollbarDefaultPos, scrollbarMaxScroll - scrollbarDefaultPos.y);
116 _scrollbar->init();
117 _curtains.init();
118 }
119
updateGraphics()120 void InventoryBox::updateGraphics() {
121 if (_scrollbarPos != _scrollbar->getPos()) {
122 _scrollbarPos = _scrollbar->getPos();
123
124 onScrollbarMove();
125 }
126 }
127
registerGraphics()128 void InventoryBox::registerGraphics() {
129 RenderObject::registerGraphics();
130 _scrollbar->registerGraphics();
131 _curtains.registerGraphics();
132 }
133
handleInput(NancyInput & input)134 void InventoryBox::handleInput(NancyInput &input) {
135 if (_order.size()) {
136 _scrollbar->handleInput(input);
137 }
138
139 for (uint i = 0; i < 4; ++i) {
140 if (_itemHotspots[i].hotspot.contains(input.mousePos)) {
141 if (NancySceneState.getHeldItem() != -1) {
142 g_nancy->_cursorManager->setCursorType(CursorManager::kHotspotArrow);
143 if (input.input & NancyInput::kLeftMouseButtonUp) {
144 NancySceneState.addItemToInventory(NancySceneState.getHeldItem());
145 g_nancy->_sound->playSound("BULS");
146 }
147 } else if (_itemHotspots[i].itemID != -1) {
148 g_nancy->_cursorManager->setCursorType(CursorManager::kHotspotArrow);
149 if (input.input & NancyInput::kLeftMouseButtonUp) {
150 NancySceneState.removeItemFromInventory(_itemHotspots[i].itemID);
151 g_nancy->_sound->playSound("GLOB");
152 }
153 }
154 break;
155 }
156 }
157 }
158
addItem(int16 itemID)159 void InventoryBox::addItem(int16 itemID) {
160 if (_order.size() == 0) {
161 // Adds first item, start curtains animation
162 _curtains.setOpen(true);
163 }
164 Common::Array<int16> back = _order;
165 _order.clear();
166 _order.push_back(itemID);
167 _order.push_back(back);
168
169 onReorder();
170 }
171
removeItem(int16 itemID)172 void InventoryBox::removeItem(int16 itemID) {
173 for (auto &i : _order) {
174 if (i == itemID) {
175 _order.erase(&i);
176 onReorder();
177 break;
178 }
179 }
180 }
181
onReorder()182 void InventoryBox::onReorder() {
183 onScrollbarMove();
184
185 _fullInventorySurface.clear();
186 for (uint i = 0; i < _order.size(); ++i) {
187 Common::Rect dest;
188 dest.setWidth(_screenPosition.width() / 2);
189 dest.setHeight(_screenPosition.height() / 2);
190 dest.moveTo((i % 2) * dest.width(), (i / 2) * dest.height());
191 Common::Point destPoint = Common::Point (dest.left, dest.top);
192
193 _fullInventorySurface.blitFrom(_iconsSurface, _itemDescriptions[_order[i]].sourceRect, destPoint);
194 }
195
196 if (_order.size() > 0) {
197 _curtains.setOpen(true);
198 } else {
199 _curtains.setOpen(false);
200 }
201
202 _needsRedraw = true;
203 }
204
setHotspots(uint pageNr)205 void InventoryBox::setHotspots(uint pageNr) {
206 for (uint i = 0; i < 4; ++i) {
207 if (i + pageNr * 4 < _order.size()) {
208 _itemHotspots[i].itemID = _order[i + pageNr * 4];
209 } else {
210 _itemHotspots[i].itemID = -1;
211 }
212 }
213 }
214
onScrollbarMove()215 void InventoryBox::onScrollbarMove() {
216 float scrollPos = _scrollbar->getPos();
217
218 float numPages = (_order.size() - 1) / 4 + 1;
219 float pageFrac = 1 / numPages;
220 uint curPage = MIN<uint>(scrollPos / pageFrac, numPages - 1);
221
222 Common::Rect sourceRect = _screenPosition;
223 sourceRect.moveTo(0, curPage * sourceRect.height());
224 _drawSurface.create(_fullInventorySurface, sourceRect);
225
226 setHotspots(curPage);
227
228 _needsRedraw = true;
229 }
230
init()231 void InventoryBox::Curtains::init() {
232 Common::Rect bounds = _parent->getBounds();
233 _drawSurface.create(bounds.width(), bounds.height(), g_nancy->_graphicsManager->getInputPixelFormat());
234
235 if (g_nancy->getGameType() == kGameTypeVampire) {
236 _drawSurface.setPalette(g_nancy->_graphicsManager->_object0.getPalette(), 0, 256);
237 }
238
239 _screenPosition = _parent->getScreenPosition();
240 _nextFrameTime = 0;
241 setAnimationFrame(_curFrame);
242
243 setTransparent(true);
244
245 RenderObject::init();
246 }
247
updateGraphics()248 void InventoryBox::Curtains::updateGraphics() {
249 Time time = g_nancy->getTotalPlayTime();
250 if (_areOpen) {
251 if (_curFrame < g_nancy->getConstants().numCurtainAnimationFrames && time > _nextFrameTime) {
252 setAnimationFrame(++_curFrame);
253 _nextFrameTime = time + _parent->_curtainsFrameTime;
254
255 if (!_soundTriggered) {
256 _soundTriggered = true;
257 g_nancy->_sound->playSound("CURT");
258 }
259 }
260 } else {
261 if (_curFrame > 0 && time > _nextFrameTime) {
262 setAnimationFrame(--_curFrame);
263 _nextFrameTime = time + _parent->_curtainsFrameTime;
264
265 if (!_soundTriggered) {
266 _soundTriggered = true;
267 g_nancy->_sound->playSound("CURT");
268 }
269 }
270 }
271
272 if (_curFrame == 0 || _curFrame == g_nancy->getConstants().numCurtainAnimationFrames) {
273 _soundTriggered = false;
274 }
275 }
276
setAnimationFrame(uint frame)277 void InventoryBox::Curtains::setAnimationFrame(uint frame) {
278 Graphics::ManagedSurface &_object0 = g_nancy->_graphicsManager->_object0;
279 Common::Rect srcRect;
280 Common::Point destPoint;
281
282 if (frame > g_nancy->getConstants().numCurtainAnimationFrames - 1) {
283 setVisible(false);
284 return;
285 } else {
286 setVisible(true);
287 }
288
289 _drawSurface.clear(g_nancy->_graphicsManager->getTransColor());
290
291 // Draw left shade
292 srcRect = _parent->_curtainsSrc[frame * 2];
293 _drawSurface.blitFrom(_object0, srcRect, destPoint);
294
295 // Draw right shade
296 srcRect = _parent->_curtainsSrc[frame * 2 + 1];
297 destPoint.x = getBounds().width() - srcRect.width();
298 _drawSurface.blitFrom(_object0, srcRect, destPoint);
299
300 _needsRedraw = true;
301 }
302
303 } // End of namespace UI
304 } // End of namespace Nancy
305