1 /* ResidualVM - A 3D game interpreter
2  *
3  * ResidualVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the AUTHORS
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/stark/ui/gamewindow.h"
24 
25 #include "engines/stark/scene.h"
26 
27 #include "engines/stark/gfx/driver.h"
28 
29 #include "engines/stark/resources/anim.h"
30 #include "engines/stark/resources/knowledgeset.h"
31 #include "engines/stark/resources/item.h"
32 #include "engines/stark/resources/location.h"
33 
34 #include "engines/stark/services/global.h"
35 #include "engines/stark/services/services.h"
36 #include "engines/stark/services/staticprovider.h"
37 #include "engines/stark/services/gameinterface.h"
38 #include "engines/stark/services/userinterface.h"
39 
40 #include "engines/stark/ui/actionmenu.h"
41 #include "engines/stark/ui/cursor.h"
42 #include "engines/stark/ui/inventorywindow.h"
43 
44 namespace Stark {
45 
GameWindow(Gfx::Driver * gfx,Cursor * cursor,ActionMenu * actionMenu,InventoryWindow * inventory)46 GameWindow::GameWindow(Gfx::Driver *gfx, Cursor *cursor, ActionMenu *actionMenu, InventoryWindow *inventory) :
47 		Window(gfx, cursor),
48 	_actionMenu(actionMenu),
49 	_inventory(inventory),
50 	_objectUnderCursor(nullptr) {
51 	_position = Common::Rect(Gfx::Driver::kGameViewportWidth, Gfx::Driver::kGameViewportHeight);
52 	_position.translate(0, Gfx::Driver::kTopBorderHeight);
53 	_visible = true;
54 
55 	_fadeRenderer = _gfx->createFadeRenderer();
56 }
57 
~GameWindow()58 GameWindow::~GameWindow() {
59 	delete _fadeRenderer;
60 }
61 
onRender()62 void GameWindow::onRender() {
63 	// List the items to render
64 	Resources::Location *location = StarkGlobal->getCurrent()->getLocation();
65 	_renderEntries = location->listRenderEntries();
66 	Gfx::LightEntryArray lightEntries = location->listLightEntries();
67 
68 	// Render all the scene items
69 	Gfx::RenderEntryArray::iterator element = _renderEntries.begin();
70 	while (element != _renderEntries.end()) {
71 		// Draw the current element
72 		(*element)->render(lightEntries);
73 
74 		// Go for the next one
75 		element++;
76 	}
77 
78 	float fadeLevel = StarkScene->getFadeLevel();
79 	if ((1.0f - fadeLevel) > 0.00001f) {
80 		_fadeRenderer->render(fadeLevel);
81 	}
82 }
83 
onMouseMove(const Common::Point & pos)84 void GameWindow::onMouseMove(const Common::Point &pos) {
85 	_renderEntries = StarkGlobal->getCurrent()->getLocation()->listRenderEntries();
86 	_cursor->setFading(false);
87 
88 	if (!StarkUserInterface->isInteractive()) {
89 		_objectUnderCursor = nullptr;
90 		_cursor->setCursorType(Cursor::kPassive);
91 		_cursor->setMouseHint("");
92 		return;
93 	}
94 
95 	int16 selectedInventoryItem = _inventory->getSelectedInventoryItem();
96 	int16 singlePossibleAction = -1;
97 	bool defaultAction = false;
98 
99 	checkObjectAtPos(pos, selectedInventoryItem, singlePossibleAction, defaultAction);
100 
101 	Common::String mouseHint;
102 
103 	if (selectedInventoryItem != -1 && !defaultAction) {
104 		VisualImageXMG *cursorImage = StarkGameInterface->getCursorImage(selectedInventoryItem);
105 		_cursor->setCursorImage(cursorImage);
106 		_cursor->setFading(singlePossibleAction == selectedInventoryItem);
107 	} else if (_objectUnderCursor) {
108 		switch (singlePossibleAction) {
109 			case -1:
110 				_cursor->setCursorType(Cursor::kActive);
111 				break;
112 			case Resources::PATTable::kActionLook:
113 				_cursor->setCursorType(Cursor::kEye);
114 				break;
115 			case Resources::PATTable::kActionTalk:
116 				_cursor->setCursorType(Cursor::kMouth);
117 				break;
118 			case Resources::PATTable::kActionUse:
119 				_cursor->setCursorType(Cursor::kHand);
120 				break;
121 			default:
122 				VisualImageXMG *cursorImage = StarkGameInterface->getCursorImage(singlePossibleAction);
123 				_cursor->setCursorImage(cursorImage);
124 				break;
125 		}
126 
127 		mouseHint = StarkGameInterface->getItemTitleAt(_objectUnderCursor, _objectRelativePosition);
128 	} else {
129 		// Not an object
130 		_cursor->setCursorType(Cursor::kDefault);
131 	}
132 	_cursor->setMouseHint(mouseHint);
133 }
134 
onClick(const Common::Point & pos)135 void GameWindow::onClick(const Common::Point &pos) {
136 	if (!StarkUserInterface->isInteractive()) {
137 		StarkUserInterface->markInteractionDenied();
138 		return;
139 	}
140 
141 	_actionMenu->close();
142 
143 	int16 selectedInventoryItem = _inventory->getSelectedInventoryItem();
144 	int16 singlePossibleAction = -1;
145 	bool defaultAction;
146 
147 	checkObjectAtPos(pos, selectedInventoryItem, singlePossibleAction, defaultAction);
148 
149 	if (_objectUnderCursor) {
150 		if (singlePossibleAction != -1) {
151 			StarkGameInterface->itemDoActionAt(_objectUnderCursor, singlePossibleAction, _objectRelativePosition);
152 		} else if (selectedInventoryItem == -1) {
153 			_actionMenu->open(_objectUnderCursor, _objectRelativePosition);
154 		}
155 	} else {
156 		// The walk code expects unscaled absolute mouse coordinates
157 		StarkGameInterface->walkTo(_cursor->getMousePosition(true));
158 	}
159 }
160 
onRightClick(const Common::Point & pos)161 void GameWindow::onRightClick(const Common::Point &pos) {
162 	if (!StarkUserInterface->isInteractive()) {
163 		return;
164 	}
165 
166 	int16 selectedInventoryItem = _inventory->getSelectedInventoryItem();
167 
168 	if (selectedInventoryItem == -1) {
169 		_inventory->open();
170 	} else {
171 		_inventory->setSelectedInventoryItem(-1);
172 	}
173 }
174 
onDoubleClick(const Common::Point & pos)175 void GameWindow::onDoubleClick(const Common::Point &pos) {
176 	if (!StarkUserInterface->isInteractive()) {
177 		StarkUserInterface->markInteractionDenied();
178 		return;
179 	}
180 
181 	if (StarkGameInterface->isAprilWalking()) {
182 		StarkGameInterface->setAprilRunning();
183 	}
184 }
185 
checkObjectAtPos(Common::Point pos,int16 selectedInventoryItem,int16 & singlePossibleAction,bool & isDefaultAction)186 void GameWindow::checkObjectAtPos(Common::Point pos, int16 selectedInventoryItem, int16 &singlePossibleAction, bool &isDefaultAction) {
187 	_objectUnderCursor = nullptr;
188 	singlePossibleAction = -1;
189 	isDefaultAction = false;
190 
191 	Math::Ray ray = StarkScene->makeRayFromMouse(_cursor->getMousePosition(true));
192 
193 	// Render entries are sorted from the farthest to the camera to the nearest
194 	// Loop in reverse order
195 	for (int i = _renderEntries.size() - 1; i >= 0; i--) {
196 		if (_renderEntries[i]->containsPoint(pos, _objectRelativePosition)
197 		    || _renderEntries[i]->intersectRay(ray)) {
198 			_objectUnderCursor = _renderEntries[i]->getOwner();
199 			break;
200 		}
201 	}
202 
203 	if (!_objectUnderCursor || !StarkGameInterface->itemHasActionAt(_objectUnderCursor, _objectRelativePosition, -1)) {
204 		// Only consider items with runnable scripts
205 		_objectUnderCursor = nullptr;
206 		return;
207 	}
208 
209 	int32 defaultAction = StarkGameInterface->itemGetDefaultActionAt(_objectUnderCursor, _objectRelativePosition);
210 	if (defaultAction != -1) {
211 		// Use the default action if there is one
212 		singlePossibleAction = defaultAction;
213 		isDefaultAction = true;
214 	} else if (selectedInventoryItem != -1) {
215 		// Use the selected inventory item if there is one
216 		if (StarkGameInterface->itemHasActionAt(_objectUnderCursor, _objectRelativePosition, selectedInventoryItem)) {
217 			singlePossibleAction = selectedInventoryItem;
218 		}
219 	} else {
220 		// Otherwise, use stock actions
221 		Resources::ActionArray actionsPossible = StarkGameInterface->listStockActionsPossibleForObjectAt(
222 				_objectUnderCursor, _objectRelativePosition);
223 
224 		if (actionsPossible.size() == 1) {
225 			singlePossibleAction = actionsPossible[0];
226 		}
227 	}
228 }
229 
reset()230 void GameWindow::reset() {
231 	_renderEntries.clear();
232 	_objectUnderCursor = nullptr;
233 	_objectRelativePosition.x = 0;
234 	_objectRelativePosition.y = 0;
235 }
236 
237 } // End of namespace Stark
238