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 "ultima/ultima4/game/portal.h"
24 #include "ultima/ultima4/map/annotation.h"
25 #include "ultima/ultima4/map/city.h"
26 #include "ultima/ultima4/game/context.h"
27 #include "ultima/ultima4/map/dungeon.h"
28 #include "ultima/ultima4/game/game.h"
29 #include "ultima/ultima4/map/location.h"
30 #include "ultima/ultima4/map/mapmgr.h"
31 #include "ultima/ultima4/game/names.h"
32 #include "ultima/ultima4/gfx/screen.h"
33 #include "ultima/ultima4/map/shrine.h"
34 #include "ultima/ultima4/map/tile.h"
35 
36 namespace Ultima {
37 namespace Ultima4 {
38 
createDngLadder(Location * location,PortalTriggerAction action,Portal * p)39 void createDngLadder(Location *location, PortalTriggerAction action, Portal *p) {
40 	if (!p) return;
41 	else {
42 		p->_destid = location->_map->_id;
43 		if (action == ACTION_KLIMB && location->_coords.z == 0) {
44 			p->_exitPortal = true;
45 			p->_destid = 1;
46 		} else p->_exitPortal = false;
47 		p->_message = "";
48 		p->_portalConditionsMet = nullptr;
49 		p->_portalTransportRequisites = TRANSPORT_FOOT_OR_HORSE;
50 		p->_retroActiveDest = nullptr;
51 		p->_saveLocation = false;
52 		p->_start = location->_coords;
53 		p->_start.z += (action == ACTION_KLIMB) ? -1 : 1;
54 	}
55 }
56 
usePortalAt(Location * location,MapCoords coords,PortalTriggerAction action)57 int usePortalAt(Location *location, MapCoords coords, PortalTriggerAction action) {
58 	Map *destination;
59 	char msg[32] = {0};
60 
61 	const Portal *portal = location->_map->portalAt(coords, action);
62 	Portal dngLadder;
63 
64 	/* didn't find a portal there */
65 	if (!portal) {
66 
67 		/* if it's a dungeon, then ladders are predictable.  Create one! */
68 		if (location->_context == CTX_DUNGEON) {
69 			Dungeon *dungeon = dynamic_cast<Dungeon *>(location->_map);
70 			assert(dungeon);
71 
72 			if ((action & ACTION_KLIMB) && dungeon->ladderUpAt(coords))
73 				createDngLadder(location, action, &dngLadder);
74 			else if ((action & ACTION_DESCEND) && dungeon->ladderDownAt(coords))
75 				createDngLadder(location, action, &dngLadder);
76 			else
77 				return 0;
78 			portal = &dngLadder;
79 		} else {
80 			return 0;
81 		}
82 	}
83 
84 	/* conditions not met for portal to work */
85 	if (portal && portal->_portalConditionsMet && !(*portal->_portalConditionsMet)(portal))
86 		return 0;
87 	/* must klimb or descend on foot! */
88 	else if (g_context->_transportContext & ~TRANSPORT_FOOT && (action == ACTION_KLIMB || action == ACTION_DESCEND)) {
89 		g_screen->screenMessage("%sOnly on foot!\n", action == ACTION_KLIMB ? "Klimb\n" : "");
90 		return 1;
91 	}
92 
93 	destination = mapMgr->get(portal->_destid);
94 
95 	if (portal->_message.empty()) {
96 
97 		switch (action) {
98 		case ACTION_DESCEND:
99 			sprintf(msg, "Descend down to level %d\n", portal->_start.z + 1);
100 			break;
101 		case ACTION_KLIMB:
102 			if (portal->_exitPortal)
103 				sprintf(msg, "Klimb up!\nLeaving...\n");
104 			else
105 				sprintf(msg, "Klimb up!\nTo level %d\n", portal->_start.z + 1);
106 			break;
107 		case ACTION_ENTER:
108 			switch (destination->_type) {
109 			case Map::CITY: {
110 				City *city = dynamic_cast<City *>(destination);
111 				assert(city);
112 				g_screen->screenMessage("Enter %s!\n\n%s\n\n", city->_type.c_str(), city->getName().c_str());
113 			}
114 			break;
115 			case Map::SHRINE:
116 				g_screen->screenMessage("Enter the %s!\n\n", destination->getName().c_str());
117 				break;
118 			case Map::DUNGEON:
119 #ifdef IOS_ULTIMA4
120 				U4IOS::testFlightPassCheckPoint("Enter " + destination->getName());
121 #endif
122 				g_screen->screenMessage("Enter dungeon!\n\n%s\n\n", destination->getName().c_str());
123 				break;
124 			default:
125 				break;
126 			}
127 			break;
128 		case ACTION_NONE:
129 		default:
130 			break;
131 		}
132 	}
133 
134 	/* check the transportation requisites of the portal */
135 	if (g_context->_transportContext & ~portal->_portalTransportRequisites) {
136 		g_screen->screenMessage("Only on foot!\n");
137 		return 1;
138 	}
139 	/* ok, we know the portal is going to work -- now display the custom message, if any */
140 	else if (!portal->_message.empty() || strlen(msg))
141 		g_screen->screenMessage("%s", portal->_message.empty() ? msg : portal->_message.c_str());
142 
143 	/* portal just exits to parent map */
144 	if (portal->_exitPortal) {
145 		g_game->exitToParentMap();
146 		g_music->playMapMusic();
147 		return 1;
148 	} else if (portal->_destid == location->_map->_id)
149 		location->_coords = portal->_start;
150 
151 	else {
152 		g_game->setMap(destination, portal->_saveLocation, portal);
153 		g_music->playMapMusic();
154 	}
155 
156 	/* if the portal changes the map retroactively, do it here */
157 	/*
158 	 * note that we use c->location instead of location, since
159 	 * location has probably been invalidated above
160 	 */
161 	if (portal->_retroActiveDest && g_context->_location->_prev) {
162 		g_context->_location->_prev->_coords = portal->_retroActiveDest->_coords;
163 		g_context->_location->_prev->_map = mapMgr->get(portal->_retroActiveDest->_mapid);
164 	}
165 
166 	if (destination->_type == Map::SHRINE) {
167 		Shrine *shrine = dynamic_cast<Shrine *>(destination);
168 		assert(shrine);
169 		shrine->enter();
170 	}
171 
172 	return 1;
173 }
174 
175 } // End of namespace Ultima4
176 } // End of namespace Ultima
177