1 /*
2  *
3  *   Copyright (c) 2002, 2003 Johannes Prix
4  *   Copyright (c) 2004-2010 Arthur Huillet
5  *
6  *
7  *  This file is part of Freedroid
8  *
9  *  Freedroid is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2 of the License, or
12  *  (at your option) any later version.
13  *
14  *  Freedroid is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with Freedroid; see the file COPYING. If not, write to the
21  *  Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
22  *  MA  02111-1307  USA
23  *
24  */
25 
26 #define _leveleditor_display_c
27 
28 #include "system.h"
29 
30 #include "defs.h"
31 #include "struct.h"
32 #include "global.h"
33 #include "proto.h"
34 
35 #include "lvledit/lvledit.h"
36 #include "lvledit/lvledit_actions.h"
37 #include "lvledit/lvledit_widgets.h"
38 
39 struct image level_editor_waypoint_cursor[2] = { EMPTY_IMAGE, EMPTY_IMAGE };
40 
41 #define DEFAULT_ZOOM_FACTOR 3.0
42 static float lvledit_zoom_factor = DEFAULT_ZOOM_FACTOR;
43 static float lvledit_zoom_factor_inv = 1.0 / DEFAULT_ZOOM_FACTOR;
44 
lvledit_zoomfact()45 float lvledit_zoomfact()
46 {
47 	return lvledit_zoom_factor;
48 }
49 
lvledit_zoomfact_inv()50 float lvledit_zoomfact_inv()
51 {
52 	return lvledit_zoom_factor_inv;
53 }
54 
lvledit_set_zoomfact(float zf)55 int lvledit_set_zoomfact(float zf)
56 {
57 	lvledit_zoom_factor = zf;
58 	lvledit_zoom_factor_inv = 1.0 / zf;
59 	return 0;
60 }
61 
62 /**
63  * Displays the GPS in the editor
64  */
gps_show()65 static void gps_show() {
66 	static char gps_text[200];
67 	// TRANSLATORS: Used to display a GPS position (X=10.5 Y=34.3 L=12 layer=1)
68 	snprintf(gps_text, sizeof(gps_text) - 1, _(" X=%3.1f Y=%3.1f L=%d layer=%d\n"), Me.pos.x, Me.pos.y, Me.pos.z, current_floor_layer);
69 	display_text(gps_text, User_Rect.x + 1, GameConfig.screen_height - 1 * get_font_height(get_current_font()), NULL /*&User_Rect */ , 1.0);
70 }
71 
72 /**
73  * Displays counted FPS right above GPS
74  */
lvledit_display_fps(void)75 void lvledit_display_fps(void) {
76 	static char fps_text[200];
77 	sprintf(fps_text, _(" FPS=%d\n"), get_current_fps());
78 	display_text(fps_text, User_Rect.x + 1,
79 		GameConfig.screen_height - 2 * get_font_height(get_current_font()), NULL, 1.0);
80 }
81 
82 /**
83  * Now we print out the map label information about this map location.
84  */
print_label_information(level * EditLevel)85 static void print_label_information(level *EditLevel)
86 {
87 	char PanelText[5000] = "";
88 	map_label *m;
89 
90 	m = get_map_label_from_coords(EditLevel, Me.pos.x, Me.pos.y);
91 
92 	if (m) {
93 		// Create the map label information
94 		sprintf(PanelText, _("\n Map label information: \n label_name=\"%s\"."), m->label_name);
95 
96 		// Display the map label information on the screen
97 		display_text(PanelText, User_Rect.x, GameConfig.screen_height - 5 * get_font_height(get_current_font()), NULL, 1.0);
98 
99 		return;
100 	}
101 }
102 
show_cursor(int must_zoom)103 static void show_cursor(int must_zoom)
104 {
105 	static struct image level_editor_cursor = EMPTY_IMAGE;
106 
107 #define HIGHLIGHTCOLOR 255
108 
109 	// Maybe, if the level editor floor cursor has not yet been loaded,
110 	// we need to load it.
111 	//
112 	if (!image_loaded(&level_editor_cursor)) {
113 		load_image(&level_editor_cursor, "level_editor_floor_cursor.png", USE_OFFSET);
114 	}
115 
116 	float scale = must_zoom ? lvledit_zoomfact_inv() : 1.0;
117 
118 	display_image_on_map(&level_editor_cursor, Me.pos.x, Me.pos.y, IMAGE_SCALE_TRANSFO(scale));
119 	print_label_information(EditLevel());
120 }
121 
122 /**
123  * This function is used to draw a line between given map tiles.  It is
124  * mainly used for the map editor to highlight connections and the
125  * current map tile target.
126  */
draw_connection_between_tiles(float x1,float y1,float x2,float y2,int mask,int rspawn)127 void draw_connection_between_tiles(float x1, float y1, float x2, float y2, int mask, int rspawn)
128 {
129 	float steps;
130 	float dist;
131 	int i;
132 	static struct image level_editor_dot_cursor = EMPTY_IMAGE;
133 
134 	float scale = (mask & ZOOM_OUT) ? lvledit_zoomfact_inv() : 1.0;
135 
136 	// Maybe, if the level editor dot cursor has not yet been loaded,
137 	// we need to load it.
138 	//
139 	if (!image_loaded(&level_editor_dot_cursor)) {
140 		load_image(&level_editor_dot_cursor, "level_editor_waypoint_dot.png", USE_OFFSET);
141 	}
142 
143 	// So now that the dot cursor has been loaded, we can start to
144 	// actually draw the dots.
145 	//
146 
147 	// We measure the distance that we have to go and then we draw some
148 	// dots at some convex combinations of our two vectors.  Very fine.
149 	//
150 	dist = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
151 
152 	steps = dist * 4;	// let's say 4 dots per square to mark the line, ok?
153 
154 	if (!steps)		// Oh, noh, the points are at the same place
155 		return;
156 	for (i = 0; i < steps + 1; i++) {
157 		float x, y;
158 		float r, g, b;
159 		x = (((float)i) / steps) * x1 + x2 * (steps - i) / steps;
160 		y = (((float)i) / steps) * y1 + y2 * (steps - i) / steps;
161 
162 		r = 1.0;
163 		if (rspawn)
164 			g = b = 0;
165 		else
166 			g = b = 1.0;
167 
168 		display_image_on_map(&level_editor_dot_cursor, x, y, IMAGE_SCALE_RGB_TRANSFO(scale, r, g, b));
169 
170 	}
171 
172 };				// void draw_connection_between_tiles ( .... )
173 
174 /**
175  * This function is used by thelevel *Editor integrated into
176  * FreedroidRPG.  It marks all waypoints with a cross.
177  */
show_waypoints(int mask)178 static void show_waypoints(int mask)
179 {
180 	float zf = (mask & ZOOM_OUT) ? lvledit_zoomfact_inv() : 1.0;
181 	waypoint *wpts = EditLevel()->waypoints.arr;
182 	float r, g, b;
183 	float x, y;
184 	int i, j;
185 	int x_min, x_max, y_min, y_max;
186 
187 	get_floor_boundaries(mask, &y_min, &y_max, &x_min, &x_max);
188 
189 	// Maybe, if the level editor floor cursor has not yet been loaded,
190 	// we need to load it.
191 	if (!image_loaded(&level_editor_waypoint_cursor[0])) {
192 		load_image(&level_editor_waypoint_cursor[0], "level_editor_waypoint_cursor.png", USE_OFFSET);
193 	}
194 
195 	if (!image_loaded(&level_editor_waypoint_cursor[1])) {
196 		load_image(&level_editor_waypoint_cursor[1], "level_editor_norand_waypoint_cursor.png", USE_OFFSET);
197 	}
198 
199 	for (i = 0; i < EditLevel()->waypoints.size; i++) {
200 		// Skip waypoints that are not visible
201 		if (wpts[i].x <= x_min || wpts[i].x >= x_max)
202 			continue;
203 		if (wpts[i].y <= y_min || wpts[i].y >= y_max)
204 			continue;
205 
206 		// Calculate the position of the waypoint
207 		x = wpts[i].x + 0.5;
208 		y = wpts[i].y + 0.5;
209 
210 		// Apply disco mode when current waypoint is selected
211 		object_vtx_color(&wpts[i], &r, &g, &b);
212 
213 		struct image *img = &level_editor_waypoint_cursor[wpts[i].suppress_random_spawn];
214 		display_image_on_map(img, x, y, IMAGE_SCALE_RGB_TRANSFO(zf, r, g, b));
215 
216 		// Get the connections of the waypoint
217 		int *connections = wpts[i].connections.arr;
218 
219 		for (j = 0; j < wpts[i].connections.size; j++) {
220 			// Check validity of the connected waypoint
221 			int connected_waypoint = connections[j];
222 			if (connected_waypoint < 0 || connected_waypoint >= EditLevel()->waypoints.size)
223 				continue;
224 
225 			waypoint *to_wp = &wpts[connected_waypoint];
226 
227 			if (((EditX() == wpts[i].x) && (EditY() == wpts[i].y)) || GameConfig.show_wp_connections) {
228 				draw_connection_between_tiles(x, y, to_wp->x + 0.5, to_wp->y + 0.5, mask, wpts[i].suppress_random_spawn);
229 			}
230 		}
231 	}
232 
233 	// Now we do something extra:  If there is a connection attempt currently
234 	// going on, then we also draw a connection from the origin point to the
235 	// current cursor (i.e. 'me') position.
236 	if (OriginWaypoint != (-1)) {
237 		// Draw the connection between the origin waypoint and the cursor (ie. 'me')
238 		draw_connection_between_tiles(wpts[OriginWaypoint].x + 0.5, wpts[OriginWaypoint].y + 0.5, Me.pos.x, Me.pos.y,
239 											mask, wpts[OriginWaypoint].suppress_random_spawn);
240 	}
241 }
242 
243 /**
244  * This function is used by thelevel *Editor integrated into
245  * FreedroidRPG.  It marks all places that have a label attached to them.
246  */
show_map_labels(int must_zoom)247 static void show_map_labels(int must_zoom)
248 {
249 	level *EditLevel = curShip.AllLevels[Me.pos.z];
250 	struct map_label *map_label;
251 	int i;
252 	float r, g, b;
253 	float scale = must_zoom ? lvledit_zoomfact_inv() : 1.0;
254 	int x_min, x_max, y_min, y_max;
255 
256 	get_floor_boundaries(must_zoom, &y_min, &y_max, &x_min, &x_max);
257 
258 	// Now we can draw a fine indicator at all the necessary positions ...
259 	for (i = 0; i < EditLevel->map_labels.size; i++) {
260 		// Get the map label
261 		map_label = &ACCESS_MAP_LABEL(EditLevel->map_labels, i);
262 
263 		// Apply disco mode when the current map label is selected
264 		object_vtx_color(map_label, &r, &g, &b);
265 
266 		// Skip map labels that are not visible
267 		if (map_label->pos.x < x_min || map_label->pos.x > x_max)
268 			continue;
269 		if (map_label->pos.y < y_min || map_label->pos.y > y_max)
270 			continue;
271 
272 		struct image *img = get_map_label_image();
273 		display_image_on_map(img, map_label->pos.x + 0.5, map_label->pos.y + 0.5, IMAGE_SCALE_RGB_TRANSFO(scale, r, g, b));
274 
275                 if (!GameConfig.omit_map_labels_in_level_editor) {
276                     show_backgrounded_label_at_map_position(map_label->label_name,
277                                                             0, map_label->pos.x,
278                                                             map_label->pos.y, must_zoom);
279                 }
280 	}
281 }
282 
283 /**
284  * Display the cursor in the leveleditor according to the currently
285  * selected tool etc.
286  */
display_cursor()287 static void display_cursor()
288 {
289 	struct widget *w;
290 	w = get_active_widget(GetMousePos_x(), GetMousePos_y());
291 
292 	if (w) {
293 		if (w->type != WIDGET_MAP)
294 			blit_mouse_cursor();
295 		else
296 			widget_lvledit_map_display_cursor();
297 	}
298 }
299 
leveleditor_display()300 void leveleditor_display()
301 {
302 	char linebuf[1000];
303 
304 	AssembleCombatPicture(ONLY_SHOW_MAP_AND_TEXT | SHOW_ITEMS | OMIT_TUX | GameConfig.omit_obstacles_in_level_editor *
305 			      OMIT_OBSTACLES | GameConfig.omit_enemies_in_level_editor * OMIT_ENEMIES | ZOOM_OUT *
306 			      GameConfig.zoom_is_on | OMIT_BLASTS | SKIP_LIGHT_RADIUS | NO_CURSOR | OMIT_ITEMS_LABEL);
307 
308 	start_image_batch();
309 	show_waypoints(ZOOM_OUT * GameConfig.zoom_is_on);
310 	show_map_labels(ZOOM_OUT * GameConfig.zoom_is_on);
311 	end_image_batch();
312 	show_cursor(ZOOM_OUT * GameConfig.zoom_is_on);
313 	gps_show();
314 
315 	if (GameConfig.Draw_Framerate)
316 		lvledit_display_fps();
317 
318 	set_current_font(FPS_Display_Font);
319 
320 	// Now we print out the current status directly onto the window:
321 	//
322 	if (OriginWaypoint != -1) {
323 		waypoint *wpts = EditLevel()->waypoints.arr;
324 
325 		sprintf(linebuf, _(" Source waypoint selected : X=%d Y=%d. "), wpts[OriginWaypoint].x, wpts[OriginWaypoint].y);
326 		put_string_left(FPS_Display_Font, GameConfig.screen_height - 2 * get_font_height(get_current_font()), linebuf);
327 	}
328 	// Now we print out the latest connection operation success or failure...
329 	//
330 	if (VanishingMessageEndDate > SDL_GetTicks()) {
331 		display_text(VanishingMessage, 1, GameConfig.screen_height - 8 * get_font_height(get_current_font()), NULL, 1.0);
332 	}
333 
334 	// Construct the linked list of visible levels.
335 	get_visible_levels();
336 
337 	display_widgets();
338 
339 	if (EditLevel()->random_dungeon) {
340 		sprintf(VanishingMessage, _(" This level is automatically generated. \n Editing will have no effect."));
341 		VanishingMessageEndDate = SDL_GetTicks() + 100;
342 	}
343 
344 	display_cursor();
345 	// Now that everything is blitted and printed, we may update the screen again...
346 	//
347 	our_SDL_flip_wrapper();
348 
349 }
350