1 /*
2    Copyright (C) 2003 - 2018 by David White <dave@whitevine.net>
3    Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY.
11 
12    See the COPYING file for more details.
13 */
14 
15 /**
16  * Terrain-palette in the editor.
17  */
18 
19 #define GETTEXT_DOMAIN "wesnoth-editor"
20 
21 #include "editor/palette/terrain_palettes.hpp"
22 
23 #include "gettext.hpp"
24 #include "serialization/string_utils.hpp"
25 
26 namespace {
27 	static t_translation::terrain_code fg_terrain;
28 	static t_translation::terrain_code bg_terrain;
29 }
30 
31 namespace editor {
32 
get_selected_bg_terrain()33 const t_translation::terrain_code& get_selected_bg_terrain() {
34 	return bg_terrain;
35 }
36 
get_selected_fg_terrain()37 const t_translation::terrain_code& get_selected_fg_terrain() {
38 	return fg_terrain;
39 }
40 
selected_fg_item() const41 const t_translation::terrain_code& terrain_palette::selected_fg_item() const { return fg_terrain; }
selected_bg_item() const42 const t_translation::terrain_code& terrain_palette::selected_bg_item() const { return bg_terrain; }
43 
44 
is_valid_terrain(const t_translation::terrain_code & c)45 static bool is_valid_terrain(const t_translation::terrain_code & c) {
46 	return !(c == t_translation::VOID_TERRAIN || c == t_translation::FOGGED);
47 }
48 
select_bg_item(const std::string & item_id)49 void terrain_palette::select_bg_item(const std::string& item_id) {
50 	bg_terrain = item_map_[item_id];
51 	editor_palette<t_translation::terrain_code>::select_bg_item(item_id);
52 }
53 
select_fg_item(const std::string & item_id)54 void terrain_palette::select_fg_item(const std::string& item_id) {
55 	fg_terrain = item_map_[item_id];
56 	editor_palette<t_translation::terrain_code>::select_fg_item(item_id);
57 }
58 
select_bg_item(const t_translation::terrain_code & terrain)59 void terrain_palette::select_bg_item(const t_translation::terrain_code& terrain) {
60 	bg_terrain = terrain;
61 	editor_palette<t_translation::terrain_code>::select_bg_item(get_id(terrain));
62 }
63 
select_fg_item(const t_translation::terrain_code & terrain)64 void terrain_palette::select_fg_item(const t_translation::terrain_code& terrain) {
65 	fg_terrain = terrain;
66 	editor_palette<t_translation::terrain_code>::select_fg_item(get_id(terrain));
67 }
68 
69 
setup(const config & cfg)70 void terrain_palette::setup(const config& cfg)
71 {
72 	// Get the available terrains temporary in items
73 	t_translation::ter_list items = map().get_terrain_list();
74 
75 	//move "invalid" items to the end
76 	std::stable_partition(items.begin(), items.end(), is_valid_terrain);
77 
78 	// Get the available groups and add them to the structure
79 	std::set<std::string> group_names;
80 	for(const config &group : cfg.child_range("terrain_group")) {
81 		if(group_names.count(group["id"]) == 0) {
82 			config group_cfg;
83 			group_cfg["id"] = group["id"];
84 			group_cfg["name"] = group["name"];
85 
86 			group_cfg["icon"] = group["icon"].str();
87 			group_cfg["core"] = group["core"];
88 			groups_.emplace_back(group_cfg);
89 
90 			group_names.insert(groups_.back().id);
91 		}
92 	}
93 	for(const config &group : cfg.child_range("editor_group")) {
94 		if(group_names.count(group["id"]) == 0) {
95 			config group_cfg;
96 			group_cfg["id"] = group["id"];
97 			group_cfg["name"] = group["name"];
98 
99 			group_cfg["icon"] = "icons/terrain/terrain_" + group["icon"].str();
100 			group_cfg["core"] = group["core"];
101 			groups_.emplace_back(group_cfg);
102 
103 			group_names.insert(groups_.back().id);
104 		}
105 	}
106 
107 	std::map<std::string, item_group*> id_to_group;
108 	for (item_group& group : groups_) {
109 		id_to_group.emplace(group.id, &group);
110 	}
111 
112 	// add the groups for all terrains to the map
113 	for (const t_translation::terrain_code& t : items) {
114 
115 		const terrain_type& t_info = map().get_terrain_info(t);
116 		DBG_ED << "Palette: processing terrain " << t_info.name()
117 			<< "(editor name: '" << t_info.editor_name() << "') "
118 			<< "(" << t_info.number() << ")"
119 			<< ": " << t_info.editor_group() << "\n";
120 
121 		// don't display terrains that were automatically created from base+overlay
122 		if (t_info.is_combined()) continue;
123 		// nor display terrains that have hide_in_editor=true
124 		if (t_info.hide_in_editor()) continue;
125 
126 		// add the terrain to the requested groups
127 		const std::vector<std::string>& keys = utils::split(t_info.editor_group());
128 		bool core = false;
129 
130 		item_map_[get_id(t)] = t;
131 
132 		for (const std::string& k : keys) {
133 			group_map_[k].push_back(get_id(t));
134 			nmax_items_ = std::max<int>(nmax_items_, group_map_[k].size());
135 			std::map<std::string, item_group*>::iterator i = id_to_group.find(k);
136 			if (i != id_to_group.end()) {
137 				if (i->second->core) {
138 					core = true;
139 				}
140 			}
141 		}
142 
143 		// A terrain is considered core iff it appears in at least
144 		// one core terrain group
145 		if (core) {
146 			// Add the terrain to the default group
147 			group_map_["all"].push_back(get_id(t));
148 			nmax_items_ = std::max<int>(nmax_items_, group_map_["all"].size());
149 		} else {
150 			non_core_items_.insert(get_id(t));
151 		}
152 
153 	}
154 
155 	// Set the default terrain
156 	select_fg_item("regular_mountains");
157 	select_bg_item("grassland");
158 
159 	// Set the default group
160 	set_group("all");
161 
162 	if(active_group().empty()) {
163 		ERR_ED << "No items found." << std::endl;
164 	}
165 }
166 
draw_item(const t_translation::terrain_code & terrain,surface & image,std::stringstream & tooltip_text)167 void terrain_palette::draw_item(const t_translation::terrain_code& terrain,
168 		surface& image, std::stringstream& tooltip_text) {
169 
170 	const t_translation::terrain_code base_terrain = map().get_terrain_info(terrain).default_base();
171 
172 	//Draw default base for overlay terrains
173 	if(base_terrain != t_translation::NONE_TERRAIN) {
174 		const std::string base_filename = map().get_terrain_info(base_terrain).editor_image();
175 		surface base_image(image::get_image(base_filename));
176 
177 		if(base_image == nullptr) {
178 			tooltip_text << "BASE IMAGE NOT FOUND\n";
179 			ERR_ED << "image for terrain : '" << base_filename << "' not found" << std::endl;
180 			base_image = image::get_image(game_config::images::missing);
181 			if(base_image == nullptr) {
182 				ERR_ED << "Placeholder image not found" << std::endl;
183 				return;
184 			}
185 		}
186 
187 		if(base_image->w != item_size_ || base_image->h != item_size_) {
188 			base_image = scale_surface(base_image,
189 				item_size_, item_size_);
190 		}
191 	}
192 
193 	const std::string filename = map().get_terrain_info(terrain).editor_image();
194 	image = image::get_image(filename);
195 	if(image == nullptr) {
196 		tooltip_text << "IMAGE NOT FOUND\n";
197 		ERR_ED << "image for terrain: '" << filename << "' not found" << std::endl;
198 		image = image::get_image(game_config::images::missing);
199 		if(image == nullptr) {
200 			ERR_ED << "Placeholder image not found" << std::endl;
201 			return;
202 		}
203 	}
204 
205 	if(image->w != item_size_ || image->h != item_size_) {
206 		image = scale_surface(image, item_size_, item_size_);
207 	}
208 
209 	tooltip_text << map().get_terrain_editor_string(terrain);
210 	if(gui_.get_draw_terrain_codes()) {
211 		tooltip_text << " " + font::unicode_em_dash + " " << terrain;
212 	}
213 }
214 
terrain_palette(editor_display & gui,const config & cfg,editor_toolkit & toolkit)215 terrain_palette::terrain_palette(editor_display &gui, const config& cfg, editor_toolkit &toolkit)
216 //TODO avoid magic numbers
217 	:	editor_palette<t_translation::terrain_code>(gui, cfg, 36, 4, toolkit)
218 {
219 }
220 
get_id(const t_translation::terrain_code & terrain)221 const std::string& terrain_palette::get_id(const t_translation::terrain_code& terrain)
222 {
223 	const terrain_type& t_info = map().get_terrain_info(terrain);
224 	return t_info.id();
225 }
226 
get_help_string()227 std::string terrain_palette::get_help_string()
228 {
229 	std::ostringstream msg;
230 	msg << _("FG: ") << map().get_terrain_editor_string(selected_fg_item())	<< " | "
231 		<< _("BG: ") << map().get_terrain_editor_string(selected_bg_item());
232 	return msg.str();
233 }
234 
235 
236 }
237