1 /*
2  * Copyright (c) 2008 by Markus Pristovsek
3  *
4  * This file is part of the Simutrans project under the artistic license.
5  */
6 
7 #include <stdio.h>
8 #include <math.h>
9 
10 #include "../simdebug.h"
11 #include "../simworld.h"
12 #include "../simobj.h"
13 #include "../display/simimg.h"
14 #include "../player/simplay.h"
15 #include "../simtypes.h"
16 #include "../simunits.h"
17 
18 #include "../boden/grund.h"
19 
20 #include "../descriptor/groundobj_desc.h"
21 
22 #include "../utils/cbuffer_t.h"
23 #include "../utils/simrandom.h"
24 #include "../utils/simstring.h"
25 
26 #include "../dataobj/loadsave.h"
27 #include "../dataobj/translator.h"
28 #include "../dataobj/environment.h"
29 
30 #include "../obj/baum.h"
31 
32 #include "movingobj.h"
33 
34 /******************************** static stuff: desc management ****************************************************************/
35 
36 vector_tpl<const groundobj_desc_t *> movingobj_t::movingobj_typen(0);
37 
38 stringhashtable_tpl<groundobj_desc_t *> movingobj_t::desc_table;
39 
40 
41 bool compare_groundobj_desc(const groundobj_desc_t* a, const groundobj_desc_t* b);
42 
43 
successfully_loaded()44 bool movingobj_t::successfully_loaded()
45 {
46 	movingobj_typen.resize(desc_table.get_count());
47 	FOR(stringhashtable_tpl<groundobj_desc_t*>, const& i, desc_table) {
48 		movingobj_typen.insert_ordered(i.value, compare_groundobj_desc);
49 	}
50 	// iterate again to assign the index
51 	FOR(stringhashtable_tpl<groundobj_desc_t*>, const& i, desc_table) {
52 		i.value->index = movingobj_typen.index_of(i.value);
53 	}
54 
55 	if(desc_table.empty()) {
56 		movingobj_typen.append( NULL );
57 		DBG_MESSAGE("movingobj_t", "No movingobj found - feature disabled");
58 	}
59 	return true;
60 }
61 
62 
63 
register_desc(groundobj_desc_t * desc)64 bool movingobj_t::register_desc(groundobj_desc_t *desc)
65 {
66 	// remove duplicates
67 	if(  desc_table.remove( desc->get_name() )  ) {
68 		dbg->doubled( "movingobj", desc->get_name() );
69 	}
70 	desc_table.put(desc->get_name(), desc );
71 	return true;
72 }
73 
74 
75 
76 
77 /* also checks for distribution values
78  * @author prissi
79  */
random_movingobj_for_climate(climate cl)80 const groundobj_desc_t *movingobj_t::random_movingobj_for_climate(climate cl)
81 {
82 	// none there
83 	if(  desc_table.empty()  ) {
84 		return NULL;
85 	}
86 
87 	int weight = 0;
88 
89 	FOR(vector_tpl<groundobj_desc_t const*>, const i, movingobj_typen) {
90 		if (i->is_allowed_climate(cl) ) {
91 			weight += i->get_distribution_weight();
92 		}
93 	}
94 
95 	// now weight their distribution
96 	if (weight > 0) {
97 		const int w=simrand(weight);
98 		weight = 0;
99 		FOR(vector_tpl<groundobj_desc_t const*>, const i, movingobj_typen) {
100 			if (i->is_allowed_climate(cl)) {
101 				weight += i->get_distribution_weight();
102 				if(weight>=w) {
103 					return i;
104 				}
105 			}
106 		}
107 	}
108 	return NULL;
109 }
110 
111 
112 
113 /******************************* end of static ******************************************/
114 
115 
116 
117 // recalculates only the seasonal image
calc_image()118 void movingobj_t::calc_image()
119 {
120 	const groundobj_desc_t *desc=get_desc();
121 	const uint8 seasons = desc->get_seasons()-1;
122 	uint8 season = 0;
123 
124 	switch(  seasons  ) {
125 		case 0: { // summer only
126 			season = 0;
127 			break;
128 		}
129 		case 1: { // summer, snow
130 			season = welt->get_snowline() <= get_pos().z  ||  welt->get_climate( get_pos().get_2d() ) == arctic_climate;
131 			break;
132 		}
133 		case 2: { // summer, winter, snow
134 			season = welt->get_snowline() <= get_pos().z  ||  welt->get_climate( get_pos().get_2d() ) == arctic_climate ? 2 : welt->get_season() == 1;
135 			break;
136 		}
137 		default: {
138 			if(  welt->get_snowline() <= get_pos().z  ||  welt->get_climate( get_pos().get_2d() ) == arctic_climate  ) {
139 				season = seasons;
140 			}
141 			else {
142 				// resolution 1/8th month (0..95)
143 				const uint32 yearsteps = (welt->get_current_month()%12)*8 + ((welt->get_ticks()>>(welt->ticks_per_world_month_shift-3))&7) + 1;
144 				season = (seasons * yearsteps - 1) / 96;
145 			}
146 			break;
147 		}
148 	}
149 	set_image( get_desc()->get_image( season, ribi_t::get_dir(get_direction()) )->get_id() );
150 }
151 
152 
movingobj_t(loadsave_t * file)153 movingobj_t::movingobj_t(loadsave_t *file) : vehicle_base_t()
154 {
155 	rdwr(file);
156 	if(get_desc()) {
157 		welt->sync.add( this );
158 	}
159 }
160 
161 
movingobj_t(koord3d pos,const groundobj_desc_t * b)162 movingobj_t::movingobj_t(koord3d pos, const groundobj_desc_t *b ) : vehicle_base_t(pos)
163 {
164 	movingobjtype = movingobj_typen.index_of(b);
165 	weg_next = 0;
166 	timetochange = 0;	// will do random direct change anyway during next step
167 	direction = calc_set_direction( koord3d(0,0,0), koord3d(koord::west,0) );
168 	calc_image();
169 	welt->sync.add( this );
170 }
171 
172 
~movingobj_t()173 movingobj_t::~movingobj_t()
174 {
175 	welt->sync.remove( this );
176 }
177 
178 
check_season(const bool)179 bool movingobj_t::check_season(const bool)
180 {
181 	const image_id old_image = get_image();
182 	calc_image();
183 
184 	if(  get_image() != old_image  ) {
185 		mark_image_dirty( get_image(), 0 );
186 	}
187 	return true;
188 }
189 
190 
rdwr(loadsave_t * file)191 void movingobj_t::rdwr(loadsave_t *file)
192 {
193 	xml_tag_t d( file, "movingobj_t" );
194 
195 	vehicle_base_t::rdwr(file);
196 
197 	file->rdwr_enum(direction);
198 	if (file->is_loading()) {
199 		// restore dxdy information
200 		dx = dxdy[ ribi_t::get_dir(direction)*2];
201 		dy = dxdy[ ribi_t::get_dir(direction)*2+1];
202 	}
203 
204 	file->rdwr_byte(steps);
205 	file->rdwr_byte(steps_next);
206 
207 	pos_next.rdwr(file);
208 
209 	koord p = pos_next_next.get_2d();
210 	p.rdwr(file);
211 	if(file->is_loading()) {
212 		pos_next_next = koord3d(p, 0);
213 		// z-coordinate will be restored in hop_check
214 	}
215 
216 	file->rdwr_short(timetochange);
217 
218 	if(file->is_saving()) {
219 		const char *s = get_desc()->get_name();
220 		file->rdwr_str(s);
221 	}
222 	else {
223 		char bname[128];
224 		file->rdwr_str(bname, lengthof(bname));
225 		groundobj_desc_t *desc = desc_table.get(bname);
226 		if(  desc_table.empty()  ||  desc==NULL  ) {
227 			movingobjtype = simrand(movingobj_typen.get_count());
228 		}
229 		else {
230 			movingobjtype = (uint8)desc->get_index();
231 		}
232 		// if not there, desc will be zero
233 
234 		use_calc_height = true;
235 	}
236 	weg_next = 0;
237 }
238 
239 
240 
241 /**
242  * Open a new observation window for the object.
243  * @author Hj. Malthaner
244  */
show_info()245 void movingobj_t::show_info()
246 {
247 	if(env_t::tree_info) {
248 		obj_t::show_info();
249 	}
250 }
251 
252 
253 
254 /**
255  * @return Einen Beschreibungsstring f�r das Objekt, der z.B. in einem
256  * Beobachtungsfenster angezeigt wird.
257  * @author Hj. Malthaner
258  */
info(cbuffer_t & buf) const259 void movingobj_t::info(cbuffer_t & buf) const
260 {
261 	obj_t::info(buf);
262 
263 	buf.append(translator::translate(get_desc()->get_name()));
264 	if (char const* const maker = get_desc()->get_copyright()) {
265 		buf.append("\n");
266 		buf.printf(translator::translate("Constructed by %s"), maker);
267 	}
268 	buf.append("\n");
269 	buf.append(translator::translate("cost for removal"));
270 	char buffer[128];
271 	money_to_string( buffer, get_desc()->get_price()/100.0 );
272 	buf.append( buffer );
273 }
274 
275 
276 
cleanup(player_t * player)277 void movingobj_t::cleanup(player_t *player)
278 {
279 	player_t::book_construction_costs(player, -get_desc()->get_price(), get_pos().get_2d(), ignore_wt);
280 	mark_image_dirty( get_image(), 0 );
281 }
282 
283 
284 
285 
sync_step(uint32 delta_t)286 sync_result movingobj_t::sync_step(uint32 delta_t)
287 {
288 	weg_next += get_desc()->get_speed() * delta_t;
289 	weg_next -= do_drive( weg_next );
290 	return SYNC_OK;
291 }
292 
293 
294 
295 /* essential to find out about next step
296  * returns true, if we can go here
297  * (identical to fahrer)
298  */
check_next_tile(const grund_t * gr) const299 bool movingobj_t::check_next_tile( const grund_t *gr ) const
300 {
301 	if(gr==NULL) {
302 		// no ground => we cannot check further
303 		return false;
304 	}
305 
306 	const groundobj_desc_t *desc = get_desc();
307 	if( !desc->is_allowed_climate( welt->get_climate(gr->get_pos().get_2d()) ) ) {
308 		// not an allowed climate zone!
309 		return false;
310 	}
311 
312 	if(desc->get_waytype()==road_wt) {
313 		// can cross roads
314 		if(gr->get_typ()!=grund_t::boden  ||  !slope_t::is_way(gr->get_grund_hang())) {
315 			return false;
316 		}
317 		// only on roads, do not walk in cities
318 		if(gr->hat_wege()  &&  (!gr->hat_weg(road_wt)  ||  gr->get_weg(road_wt)->hat_gehweg())) {
319 			return false;
320 		}
321 		if(!desc->can_build_trees_here()) {
322 			return gr->find<baum_t>()==NULL;
323 		}
324 	}
325 	else if(desc->get_waytype()==air_wt) {
326 		// avoid towns to avoid flying through houses
327 		return gr->get_typ()==grund_t::boden  ||  gr->get_typ()==grund_t::wasser;
328 	}
329 	else if(desc->get_waytype()==water_wt) {
330 		// floating object
331 		return gr->get_typ()==grund_t::wasser  ||  gr->hat_weg(water_wt);
332 	}
333 	else if(desc->get_waytype()==ignore_wt) {
334 		// crosses nothing
335 		if(!gr->ist_natur()  ||  !slope_t::is_way(gr->get_grund_hang())) {
336 			return false;
337 		}
338 		if(!desc->can_build_trees_here()) {
339 			return gr->find<baum_t>()==NULL;
340 		}
341 	}
342 	return true;
343 }
344 
345 
346 
hop_check()347 grund_t* movingobj_t::hop_check()
348 {
349 	/* since we may be going diagonal without any road
350 	 * determining the next koord is a little tricky:
351 	 * If it is a diagonal, pos_next_next is calculated from current pos,
352 	 * Else pos_next_next is a single step from pos_next.
353 	 * otherwise objects would jump left/right on some diagonals
354 	 */
355 	if (timetochange != 0) {
356 		koord k(direction);
357 		if(k.x&k.y) {
358 			pos_next_next = get_pos() + k;
359 		}
360 		else {
361 			pos_next_next = pos_next + k;
362 		}
363 
364 		grund_t *gr = welt->lookup_kartenboden(pos_next_next.get_2d());
365 		if (check_next_tile(gr)) {
366 			pos_next_next = gr->get_pos();
367 		}
368 		else {
369 			timetochange = 0;
370 		}
371 	}
372 
373 	if (timetochange==0) {
374 		// direction change needed
375 		timetochange = simrand(speed_to_kmh(get_desc()->get_speed())/3);
376 		const koord pos=pos_next.get_2d();
377 		const grund_t *to[4];
378 		uint8 until=0;
379 		// find all tiles we can go
380 		for(  int i=0;  i<4;  i++  ) {
381 			const grund_t *check = welt->lookup_kartenboden(pos+koord::nsew[i]);
382 			if(check_next_tile(check)  &&  check->get_pos()!=get_pos()) {
383 				to[until++] = check;
384 			}
385 		}
386 		// if nothing found, return
387 		if(until==0) {
388 			pos_next_next = get_pos();
389 			// (better would be destruction?)
390 		}
391 		else {
392 			// else prepare for direction change
393 			const grund_t *next = to[simrand(until)];
394 			pos_next_next = next->get_pos();
395 		}
396 	}
397 	else {
398 		timetochange--;
399 	}
400 
401 	return welt->lookup(pos_next);
402 }
403 
404 
405 
hop(grund_t * gr)406 void movingobj_t::hop(grund_t* gr)
407 {
408 	leave_tile();
409 
410 	if(pos_next.get_2d()==get_pos().get_2d()) {
411 		direction = ribi_t::backward(direction);
412 		dx = -dx;
413 		dy = -dy;
414 		calc_image();
415 	}
416 	else {
417 		ribi_t::ribi old_dir = direction;
418 		direction = calc_set_direction( get_pos(), pos_next_next );
419 		if(old_dir!=direction) {
420 			calc_image();
421 		}
422 	}
423 
424 	set_pos(pos_next);
425 	enter_tile(gr);
426 
427 	// next position
428 	pos_next = pos_next_next;
429 }
430 
431 
432 
operator new(size_t)433 void *movingobj_t::operator new(size_t /*s*/)
434 {
435 	return freelist_t::gimme_node(sizeof(movingobj_t));
436 }
437 
438 
439 
operator delete(void * p)440 void movingobj_t::operator delete(void *p)
441 {
442 	freelist_t::putback_node(sizeof(movingobj_t),p);
443 }
444