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 "xeen/dialogs/dialogs_char_info.h"
24 #include "xeen/dialogs/dialogs_create_char.h"
25 #include "xeen/dialogs/dialogs_party.h"
26 #include "xeen/dialogs/dialogs_input.h"
27 #include "xeen/dialogs/dialogs_query.h"
28 #include "xeen/character.h"
29 #include "xeen/events.h"
30 #include "xeen/party.h"
31 #include "xeen/xeen.h"
32 
33 namespace Xeen {
34 
PartyDialog(XeenEngine * vm)35 PartyDialog::PartyDialog(XeenEngine *vm) : ButtonContainer(vm),
36 		PartyDrawer(vm), _vm(vm) {
37 	initDrawStructs();
38 }
39 
show(XeenEngine * vm)40 void PartyDialog::show(XeenEngine *vm) {
41 	PartyDialog *dlg = new PartyDialog(vm);
42 	dlg->execute();
43 	delete dlg;
44 }
45 
execute()46 void PartyDialog::execute() {
47 	EventsManager &events = *_vm->_events;
48 	FileManager &files = *_vm->_files;
49 	Interface &intf = *_vm->_interface;
50 	Party &party = *_vm->_party;
51 	Screen &screen = *_vm->_screen;
52 	Sound &sound = *_vm->_sound;
53 	Windows &windows = *_vm->_windows;
54 	bool modeFlag = false;
55 	int startingChar = 0;
56 
57 	sound.playSong(files._ccNum ? "newbrigh.m" : "inn.m");
58 	loadButtons();
59 	setupBackground();
60 
61 	while (!_vm->shouldExit()) {
62 		_vm->_mode = MODE_INTERACTIVE;
63 
64 		// Build up a list of available characters in the Roster that are on the
65 		// same side of Xeen as the player is currently on
66 		loadCharacters();
67 
68 		Window &w = windows[11];
69 		w.open();
70 		setupFaces(startingChar, false);
71 		w.writeString(Common::String::format(Res.PARTY_DIALOG_TEXT, _partyDetails.c_str()));
72 		w.drawList(&_faceDrawStructs[0], 4);
73 
74 		_uiSprites.draw(w, 0, Common::Point(16, 100));
75 		_uiSprites.draw(w, 2, Common::Point(52, 100));
76 		_uiSprites.draw(w, 4, Common::Point(87, 100));
77 		_uiSprites.draw(w, 6, Common::Point(122, 100));
78 		_uiSprites.draw(w, 8, Common::Point(157, 100));
79 		_uiSprites.draw(w, 10, Common::Point(192, 100));
80 		if (g_vm->getGameID() == GType_Swords)
81 			Res._logoSprites.draw(1, 0, Common::Point(232, 9));
82 
83 		screen.loadPalette("mm4.pal");
84 
85 		if (modeFlag) {
86 			windows[0].update();
87 			events.setCursor(0);
88 			screen.fadeIn();
89 		} else {
90 			if (_vm->getGameID() == GType_DarkSide) {
91 				screen.fadeOut();
92 				windows[0].update();
93 			}
94 
95 			doScroll(false, false);
96 			events.setCursor(0);
97 
98 			if (_vm->getGameID() == GType_DarkSide) {
99 				screen.fadeIn();
100 			}
101 		}
102 
103 		bool breakFlag = false;
104 		while (!_vm->shouldExit() && !breakFlag) {
105 			do {
106 				events.pollEventsAndWait();
107 				checkEvents(_vm);
108 			} while (!_vm->shouldExit() && !_buttonValue);
109 			if (Common::KEYCODE_F1 == _buttonValue ||
110 				Common::KEYCODE_F2 == _buttonValue ||
111 				Common::KEYCODE_F3 == _buttonValue ||
112 				Common::KEYCODE_F4 == _buttonValue ||
113 				Common::KEYCODE_F5 == _buttonValue ||
114 				Common::KEYCODE_F6 == _buttonValue) {
115 				// Show character info
116 				_buttonValue -= Common::KEYCODE_F1;
117 				if (_buttonValue < (int)party._activeParty.size())
118 					CharacterInfo::show(_vm, _buttonValue);
119 			} else if (Common::KEYCODE_1 == _buttonValue ||
120 					   Common::KEYCODE_2 == _buttonValue ||
121 					   Common::KEYCODE_3 == _buttonValue ||
122 					   Common::KEYCODE_4 == _buttonValue) {
123 				_buttonValue -= Common::KEYCODE_1 - 7;
124 				if ((_buttonValue - 7 + startingChar) < (int)_charList.size()) {
125 					// Check if the selected character is already in the party
126 					uint idx = 0;
127 					for (; idx < party._activeParty.size(); ++idx) {
128 						if (_charList[_buttonValue - 7 + startingChar] ==
129 							party._activeParty[idx]._rosterId)
130 							break;
131 					}
132 
133 					// Only add the character if they're not already in the party
134 					if (idx == party._activeParty.size()) {
135 						if (party._activeParty.size() == MAX_ACTIVE_PARTY) {
136 							sound.playFX(21);
137 							ErrorScroll::show(_vm, Res.YOUR_PARTY_IS_FULL);
138 						} else {
139 							// Add the character to the active party
140 							party._activeParty.push_back(party._roster[_charList[_buttonValue - 7 + startingChar]]);
141 							startingCharChanged(startingChar);
142 						}
143 					}
144 				}
145 
146 			} else if (Common::KEYCODE_UP == _buttonValue ||
147 					   Common::KEYCODE_KP8 == _buttonValue) {
148 				// Up arrow
149 				if (startingChar > 0) {
150 					startingChar -= 4;
151 					startingCharChanged(startingChar);
152 				}
153 
154 			} else if (Common::KEYCODE_DOWN == _buttonValue ||
155 					   Common::KEYCODE_KP2 == _buttonValue) {
156 				// Down arrow
157 				if (startingChar < ((int)_charList.size() - 4)) {
158 					startingChar += 4;
159 					startingCharChanged(startingChar);
160 				}
161 
162 			} else if (Res.KeyConstants.DialogsParty.KEY_EXIT == _buttonValue ||
163 				Common::KEYCODE_ESCAPE == _buttonValue ||
164 				Common::KEYCODE_SPACE == _buttonValue) {
165 				if (party._activeParty.size() == 0) {
166 					ErrorScroll::show(_vm, Res.NO_ONE_TO_ADVENTURE_WITH);
167 				} else {
168 					if (_vm->_mode != MODE_STARTUP) {
169 						for (int idx = OBSCURITY_NONE; idx >= OBSCURITY_BLACK; --idx) {
170 							events.updateGameCounter();
171 							intf.obscureScene((Obscurity)idx);
172 							w.update();
173 
174 							while (events.timeElapsed() < 1)
175 								events.pollEventsAndWait();
176 						}
177 					}
178 
179 					w.close();
180 					party._mazeId = party._priorMazeId;
181 
182 					party.copyPartyToRoster();
183 					//_vm->_saves->writeCharFile();
184 					return;
185 				}
186 
187 			} else if (Res.KeyConstants.DialogsParty.KEY_CREATE == _buttonValue) {
188 				// Create
189 				if (_charList.size() == XEEN_TOTAL_CHARACTERS) {
190 					ErrorScroll::show(_vm, Res.YOUR_ROSTER_IS_FULL);
191 				} else {
192 					// Save Background
193 					Graphics::ManagedSurface savedBg;
194 					savedBg.copyFrom(screen);
195 
196 					screen.fadeOut();
197 					w.close();
198 
199 					// Show the create character dialog
200 					CreateCharacterDialog::show(_vm);
201 
202 					party.copyPartyToRoster();
203 					//_vm->_saves->writeCharFile();
204 
205 					// Restore Background
206 					screen.fadeOut();
207 					screen.blitFrom(savedBg);
208 					windows[0].update();
209 
210 					modeFlag = true;
211 					breakFlag = true;
212 				}
213 
214 			} else if (Res.KeyConstants.DialogsParty.KEY_DELETE == _buttonValue) {
215 				// Delete character
216 				if (_charList.size() > 0) {
217 					int charButtonValue = selectCharacter(true, startingChar);
218 					if (charButtonValue != 0) {
219 						int charIndex = charButtonValue - Common::KEYCODE_1 + startingChar;
220 						Character &c = party._roster[_charList[charIndex]];
221 						if (c.hasSlayerSword()) {
222 							ErrorScroll::show(_vm, Res.HAS_SLAYER_SWORD);
223 						} else {
224 							Common::String msg = Common::String::format(Res.SURE_TO_DELETE_CHAR,
225 																		c._name.c_str(), Res.CLASS_NAMES[c._class]);
226 							if (Confirm::show(_vm, msg)) {
227 								// If the character is in the party, remove it
228 								for (uint idx = 0; idx < party._activeParty.size(); ++idx) {
229 									if (party._activeParty[idx]._rosterId == c._rosterId) {
230 										party._activeParty.remove_at(idx);
231 										break;
232 									}
233 								}
234 
235 								// Empty the character in the roster
236 								c.clear();
237 
238 								loadCharacters();
239 								startingChar = 0;
240 								startingCharChanged(startingChar);
241 							}
242 						}
243 					}
244 				}
245 
246 			} else if (Res.KeyConstants.DialogsParty.KEY_REMOVE == _buttonValue) {
247 				// Remove character
248 				if (party._activeParty.size() > 0) {
249 					int charButtonValue = selectCharacter(false, startingChar);
250 					if (charButtonValue != 0) {
251 						party.copyPartyToRoster();
252 						party._activeParty.remove_at(charButtonValue - Common::KEYCODE_F1);
253 					}
254 					startingCharChanged(startingChar);
255 				}
256 			}
257 		}
258 	}
259 }
260 
loadCharacters()261 void PartyDialog::loadCharacters() {
262 	Map &map = *_vm->_map;
263 	Party &party = *_vm->_party;
264 
265 	_charList.clear();
266 	for (int i = 0; i < XEEN_TOTAL_CHARACTERS; ++i) {
267 		Character &player = party._roster[i];
268 		if (player._name.empty() || player._xeenSide != map._loadCcNum)
269 			continue;
270 
271 		_charList.push_back(i);
272 	}
273 }
274 
loadButtons()275 void PartyDialog::loadButtons() {
276 	_uiSprites.load("inn.icn");
277 	addButton(Common::Rect(16, 100, 40, 120), Common::KEYCODE_UP, &_uiSprites);
278 	addButton(Common::Rect(52, 100, 76, 120), Common::KEYCODE_DOWN, &_uiSprites);
279 
280 	addButton(Common::Rect(87, 100, 111, 120), Res.KeyConstants.DialogsParty.KEY_DELETE, &_uiSprites);
281 	addButton(Common::Rect(122, 100, 146, 120), Res.KeyConstants.DialogsParty.KEY_REMOVE, &_uiSprites);
282 	addButton(Common::Rect(157, 100, 181, 120), Res.KeyConstants.DialogsParty.KEY_CREATE, &_uiSprites);
283 	addButton(Common::Rect(192, 100, 216, 120), Res.KeyConstants.DialogsParty.KEY_EXIT, &_uiSprites);
284 
285 	addButton(Common::Rect(0, 0, 0, 0), Common::KEYCODE_ESCAPE);
286 }
287 
initDrawStructs()288 void PartyDialog::initDrawStructs() {
289 	_faceDrawStructs[0] = DrawStruct(0, 0, 0);
290 	_faceDrawStructs[1] = DrawStruct(0, 101, 0);
291 	_faceDrawStructs[2] = DrawStruct(0, 0, 43);
292 	_faceDrawStructs[3] = DrawStruct(0, 101, 43);
293 }
294 
setupBackground()295 void PartyDialog::setupBackground() {
296 	_vm->_screen->loadBackground("back.raw");
297 	_vm->_interface->assembleBorder();
298 }
299 
setupFaces(int firstDisplayChar,bool updateFlag)300 void PartyDialog::setupFaces(int firstDisplayChar, bool updateFlag) {
301 	Party &party = *_vm->_party;
302 	Common::String charNames[4];
303 	Common::String charRaces[4];
304 	Common::String charSex[4];
305 	Common::String charClasses[4];
306 	int posIndex;
307 	int charId;
308 
309 	// Reset the button areas for the display character images
310 	while (_buttons.size() > 7)
311 		_buttons.remove_at(7);
312 	addButton(Common::Rect(16, 16, 48, 48), Common::KEYCODE_1);
313 	addButton(Common::Rect(117, 16, 149, 48), Common::KEYCODE_2);
314 	addButton(Common::Rect(59, 59, 91, 91), Common::KEYCODE_3);
315 	addButton(Common::Rect(117, 59, 151, 91), Common::KEYCODE_4);
316 
317 
318 	for (posIndex = 0; posIndex < 4; ++posIndex) {
319 		charId = (firstDisplayChar + posIndex) >= (int)_charList.size() ? -1 :
320 			_charList[firstDisplayChar + posIndex];
321 		bool isInParty = party.isInParty(charId);
322 
323 		if (charId == -1) {
324 			while ((int)_buttons.size() >(7 + posIndex))
325 				_buttons.remove_at(_buttons.size() - 1);
326 			break;
327 		}
328 
329 		Common::Rect &b = _buttons[7 + posIndex]._bounds;
330 		b.moveTo((posIndex & 1) ? 117 : 16, b.top);
331 		Character &ps = party._roster[_charList[firstDisplayChar + posIndex]];
332 		charNames[posIndex] = isInParty ? Res.IN_PARTY : ps._name;
333 		charRaces[posIndex] = Res.RACE_NAMES[ps._race];
334 		charSex[posIndex] = Res.SEX_NAMES[ps._sex];
335 		charClasses[posIndex] = Res.CLASS_NAMES[ps._class];
336 	}
337 
338 	drawParty(updateFlag);
339 
340 	// Set up the sprite set to use for each face
341 	for (posIndex = 0; posIndex < 4; ++posIndex) {
342 		if ((firstDisplayChar + posIndex) >= (int)_charList.size())
343 			_faceDrawStructs[posIndex]._sprites = nullptr;
344 		else
345 			_faceDrawStructs[posIndex]._sprites = party._roster[
346 				_charList[firstDisplayChar + posIndex]]._faceSprites;
347 	}
348 
349 	_partyDetails = Common::String::format(Res.PARTY_DETAILS,
350 		charNames[0].c_str(), charRaces[0].c_str(), charSex[0].c_str(), charClasses[0].c_str(),
351 		charNames[1].c_str(), charRaces[1].c_str(), charSex[1].c_str(), charClasses[1].c_str(),
352 		charNames[2].c_str(), charRaces[2].c_str(), charSex[2].c_str(), charClasses[2].c_str(),
353 		charNames[3].c_str(), charRaces[3].c_str(), charSex[3].c_str(), charClasses[3].c_str()
354 		);
355 }
356 
startingCharChanged(int firstDisplayChar)357 void PartyDialog::startingCharChanged(int firstDisplayChar) {
358 	Windows &windows = *_vm->_windows;
359 	Window &w = windows[11];
360 
361 	setupFaces(firstDisplayChar, true);
362 	w.writeString(Common::String::format(Res.PARTY_DIALOG_TEXT, _partyDetails.c_str()));
363 	w.drawList(_faceDrawStructs, 4);
364 
365 	_uiSprites.draw(w, 0, Common::Point(16, 100));
366 	_uiSprites.draw(w, 2, Common::Point(52, 100));
367 	_uiSprites.draw(w, 4, Common::Point(87, 100));
368 	_uiSprites.draw(w, 6, Common::Point(122, 100));
369 	_uiSprites.draw(w, 8, Common::Point(157, 100));
370 	_uiSprites.draw(w, 10, Common::Point(192, 100));
371 
372 	w.update();
373 }
374 
selectCharacter(bool isDelete,int firstDisplayChar)375 int PartyDialog::selectCharacter(bool isDelete, int firstDisplayChar) {
376 	EventsManager &events = *_vm->_events;
377 	Party &party = *_vm->_party;
378 	Windows &windows = *_vm->_windows;
379 	Window &w = windows[28];
380 
381 	SpriteResource iconSprites;
382 	iconSprites.load("esc.icn");
383 
384 	w.setBounds(Common::Rect(50, isDelete ? 112 : 76, 266, isDelete ? 148 : 112));
385 	w.open();
386 	w.writeString(Common::String::format(Res.REMOVE_OR_DELETE_WHICH,
387 		Res.REMOVE_DELETE[isDelete ? 1 : 0]));
388 	iconSprites.draw(w, 0, Common::Point(225, isDelete ? 120 : 84));
389 	w.update();
390 
391 	saveButtons();
392 	addButton(Common::Rect(225, isDelete ? 120 : 84, 249, isDelete ? 140 : 104),
393 		Common::KEYCODE_ESCAPE, &iconSprites);
394 	addButton(Common::Rect(16, 16, 48, 48), Common::KEYCODE_1);
395 	addButton(Common::Rect(117, 16, 149, 48), Common::KEYCODE_2);
396 	addButton(Common::Rect(16, 59, 48, 91), Common::KEYCODE_3);
397 	addButton(Common::Rect(117, 59, 149, 91), Common::KEYCODE_4);
398 	addPartyButtons(_vm);
399 
400 	int result = -1, v;
401 	while (!_vm->shouldExit() && result == -1) {
402 		_buttonValue = 0;
403 		while (!_vm->shouldExit() && !_buttonValue) {
404 			events.pollEventsAndWait();
405 			checkEvents(_vm);
406 		}
407 
408 		switch (_buttonValue) {
409 		case Common::KEYCODE_ESCAPE:
410 			result = 0;
411 			break;
412 
413 		case Common::KEYCODE_F1:
414 		case Common::KEYCODE_F2:
415 		case Common::KEYCODE_F3:
416 		case Common::KEYCODE_F4:
417 		case Common::KEYCODE_F5:
418 		case Common::KEYCODE_F6:
419 			if (!isDelete) {
420 				v = _buttonValue - Common::KEYCODE_F1;
421 				if (v < (int)party._activeParty.size())
422 					result = _buttonValue;
423 			}
424 			break;
425 
426 		case Common::KEYCODE_1:
427 		case Common::KEYCODE_2:
428 		case Common::KEYCODE_3:
429 		case Common::KEYCODE_4:
430 			if (isDelete) {
431 				v = _buttonValue - Common::KEYCODE_1;
432 				if ((firstDisplayChar + v) < (int)_charList.size())
433 					result = _buttonValue;
434 			}
435 			break;
436 
437 		default:
438 			break;
439 		}
440 	}
441 
442 	w.close();
443 	restoreButtons();
444 	return result == -1 ? 0 : result;
445 }
446 
447 } // End of namespace Xeen
448