1 /*
2  * Copyright (c) 2008 Markus Pristovsek
3  *
4  * This file is part of the Simutrans project under the artistic licence.
5  * (see licence.txt)
6  */
7 
8 /* Helper routines for AIs */
9 
10 #include "finance.h"
11 #include "ai.h"
12 
13 #include "../simcity.h"
14 #include "../simconvoi.h"
15 #include "../simhalt.h"
16 #include "../simintr.h"
17 #include "../simmenu.h"
18 #include "../simskin.h"
19 #include "../simware.h"
20 
21 #include "../bauer/brueckenbauer.h"
22 #include "../bauer/hausbauer.h"
23 #include "../bauer/tunnelbauer.h"
24 #include "../bauer/vehikelbauer.h"
25 #include "../bauer/wegbauer.h"
26 
27 #include "../descriptor/building_desc.h"
28 
29 #include "../dataobj/environment.h"
30 #include "../dataobj/loadsave.h"
31 
32 #include "../obj/zeiger.h"
33 
34 #include "../utils/cbuffer_t.h"
35 
36 #include "../vehicle/simvehicle.h"
37 
38 
39 /* The flesh for the place with road for headquarters searcher ... */
is_road_at(sint16 x,sint16 y) const40 bool ai_building_place_with_road_finder::is_road_at(sint16 x, sint16 y) const {
41 	grund_t *bd = welt->lookup_kartenboden( koord(x,y) );
42 	return bd && bd->hat_weg(road_wt);
43 }
44 
45 
is_area_ok(koord pos,sint16 w,sint16 h,climate_bits cl) const46 bool ai_building_place_with_road_finder::is_area_ok(koord pos, sint16 w, sint16 h, climate_bits cl) const {
47 	if(building_placefinder_t::is_area_ok(pos, w, h, cl)) {
48 		// check to not built on a road
49 		int i, j;
50 		for(j=pos.x; j<pos.x+w; j++) {
51 			for(i=pos.y; i<pos.y+h; i++) {
52 				if(is_road_at(j,i)) {
53 					return false;
54 				}
55 			}
56 		}
57 		// now check for road connection
58 		for(i = pos.y; i < pos.y + h; i++) {
59 			if(is_road_at(pos.x - 1, i) ||  is_road_at(pos.x + w, i)) {
60 				return true;
61 			}
62 		}
63 		for(i = pos.x; i < pos.x + w; i++) {
64 			if(is_road_at(i, pos.y - 1) ||  is_road_at(i, pos.y + h)) {
65 				return true;
66 			}
67 		}
68 	}
69 	return false;
70 }
71 
72 
73 /************************** and now the "real" helper functions ***************/
74 
75 /* return the halt on the map ground */
get_halt(const koord pos) const76 halthandle_t ai_t::get_halt(const koord pos ) const
77 {
78 	if(  grund_t *gr = welt->lookup_kartenboden(pos)  ) {
79 		return haltestelle_t::get_halt( gr->get_pos(), this );
80 	}
81 	return halthandle_t();
82 }
83 
84 
85 /* returns true,
86  * if there is already a connection
87  * @author prissi
88  */
is_connected(const koord start_pos,const koord dest_pos,const goods_desc_t * wtyp) const89 bool ai_t::is_connected( const koord start_pos, const koord dest_pos, const goods_desc_t *wtyp ) const
90 {
91 	// Dario: Check if there's a stop near destination
92 	const planquadrat_t* start_plan = welt->access(start_pos);
93 	const halthandle_t* start_list = start_plan->get_haltlist();
94 	const uint16 start_halt_count  = start_plan->get_haltlist_count();
95 
96 	// now try to find a route
97 	// ok, they are not in walking distance
98 	ware_t ware(wtyp);
99 	ware.set_zielpos(dest_pos);
100 	ware.menge = 1;
101 
102 	return (start_halt_count != 0)  &&  (haltestelle_t::search_route( start_list, start_halt_count, false, ware ) != haltestelle_t::NO_ROUTE);
103 }
104 
105 
106 // calls a general tool just like a player work do
call_general_tool(int tool,koord k,const char * param)107 bool ai_t::call_general_tool( int tool, koord k, const char *param )
108 {
109 	grund_t *gr = welt->lookup_kartenboden(k);
110 	koord3d pos = gr ? gr->get_pos() : koord3d::invalid;
111 	const char *old_param = tool_t::general_tool[tool]->get_default_param();
112 	tool_t::general_tool[tool]->set_default_param(param);
113 	const char * err = tool_t::general_tool[tool]->work( this, pos );
114 	if(err) {
115 		if(*err) {
116 			dbg->message("ai_t::call_general_tool()","failed for tool %i at (%s) because of \"%s\"", tool, pos.get_str(), err );
117 		}
118 		else {
119 			dbg->message("ai_t::call_general_tool()","not successful for tool %i at (%s)", tool, pos.get_str() );
120 		}
121 	}
122 	tool_t::general_tool[tool]->set_default_param(old_param);
123 	return err==0;
124 }
125 
126 
127 
128 /* returns ok, of there is a suitable space found
129  * only check into two directions, the ones given by dir
130  */
suche_platz(koord pos,koord & size,koord * dirs)131 bool ai_t::suche_platz(koord pos, koord &size, koord *dirs)
132 {
133 	sint16 length = abs( size.x + size.y );
134 
135 	const grund_t *gr = welt->lookup_kartenboden(pos);
136 	if(  gr==NULL  ) {
137 		return false;
138 	}
139 
140 	sint8 start_z = gr->get_hoehe();
141 	int max_dir = length==0 ? 1 : 2;
142 	bool place_ok;
143 
144 	// two rotations
145 	for(  int dir=0;  dir<max_dir;  dir++  ) {
146 		place_ok = true;
147 		for(  sint16 i=0;  i<=length;  i++  ) {
148 			grund_t *gr = welt->lookup_kartenboden(  pos + (dirs[dir]*i)  );
149 			if(  gr == NULL
150 				||  gr->get_halt().is_bound()
151 				||  !welt->can_flatten_tile(this, pos, start_z )
152 				||  !gr->ist_natur()
153 				||  gr->kann_alle_obj_entfernen(this) != NULL
154 				||  gr->get_hoehe() < welt->get_water_hgt( pos + (dirs[dir] * i) )  ) {
155 					// something is blocking construction, try next rotation
156 					place_ok = false;
157 					break;
158 			}
159 		}
160 		if(  place_ok  ) {
161 			// apparently we can build this rotation here
162 			size = dirs[dir]*length;
163 			return true;
164 		}
165 	}
166 	return false;
167 }
168 
169 
170 
171 /* needed renovation due to different sized factories
172  * also try "nicest" place first
173  * @author HJ & prissi
174  */
suche_platz(koord & start,koord & size,koord target,koord off)175 bool ai_t::suche_platz(koord &start, koord &size, koord target, koord off)
176 {
177 	// distance of last found point
178 	int dist=0x7FFFFFFF;
179 	koord	platz;
180 	int const cov = welt->get_settings().get_station_coverage();
181 	int xpos = start.x;
182 	int ypos = start.y;
183 
184 	koord dir[2];
185 	if(  abs(start.x-target.x)<abs(start.y-target.y)  ) {
186 		dir[0] = koord( 0, sgn(target.y-start.y) );
187 		dir[1] = koord( sgn(target.x-start.x), 0 );
188 	}
189 	else {
190 		dir[0] = koord( sgn(target.x-start.x), 0 );
191 		dir[1] = koord( 0, sgn(target.y-start.y) );
192 	}
193 
194 	DBG_MESSAGE("ai_t::suche_platz()","at (%i,%i) for size (%i,%i)",xpos,ypos,off.x,off.y);
195 	int maxy = min( welt->get_size().y, ypos + off.y + cov );
196 	int maxx = min( welt->get_size().x, xpos + off.x + cov );
197 	for (int y = max(0,ypos-cov);  y < maxy;  y++) {
198 		for (int x = max(0,xpos-cov);  x < maxx;  x++) {
199 			platz = koord(x,y);
200 			// no water tiles
201 			if(  welt->lookup_kartenboden(platz)->get_hoehe() <= welt->get_water_hgt(platz)  ) {
202 				continue;
203 			}
204 			// thus now check them
205 			int current_dist = koord_distance(platz,target);
206 			if(  current_dist<dist  &&  suche_platz(platz,size,dir)  ){
207 				// we will take the shortest route found
208 				start = platz;
209 				dist = koord_distance(platz,target);
210 			}
211 			else {
212 				koord test(x,y);
213 				if(  get_halt(test).is_bound()  ) {
214 DBG_MESSAGE("ai_t::suche_platz()","Search around stop at (%i,%i)",x,y);
215 
216 					// we are on a station that belongs to us
217 					int xneu=x-1, yneu=y-1;
218 					platz = koord(xneu,y);
219 					current_dist = koord_distance(platz,target);
220 					if(  current_dist<dist  &&  suche_platz(platz,size,dir)  ){
221 						// we will take the shortest route found
222 						start = platz;
223 						dist = current_dist;
224 					}
225 
226 					platz = koord(x,yneu);
227 					current_dist = koord_distance(platz,target);
228 					if(  current_dist<dist  &&  suche_platz(platz,size,dir)  ){
229 						// we will take the shortest route found
230 						start = platz;
231 						dist = current_dist;
232 					}
233 
234 					// search on the other side of the station
235 					xneu = x+1;
236 					yneu = y+1;
237 					platz = koord(xneu,y);
238 					current_dist = koord_distance(platz,target);
239 					if(  current_dist<dist  &&  suche_platz(platz,size,dir)  ){
240 						// we will take the shortest route found
241 						start = platz;
242 						dist = current_dist;
243 					}
244 
245 					platz = koord(x,yneu);
246 					current_dist = koord_distance(platz,target);
247 					if(  current_dist<dist  &&  suche_platz(platz,size,dir)  ){
248 						// we will take the shortest route found
249 						start = platz;
250 						dist = current_dist;
251 					}
252 				}
253 			}
254 		}
255 	}
256 	// of, success of short than maximum
257 	if(  dist<0x7FFFFFFF  ) {
258 		return true;
259 	}
260 	return false;
261 }
262 
263 
264 
clean_marker(koord place,koord size)265 void ai_t::clean_marker( koord place, koord size )
266 {
267 	koord pos;
268 	if(size.y<0) {
269 		place.y += size.y;
270 		size.y = -size.y;
271 	}
272 	if(size.x<0) {
273 		place.x += size.x;
274 		size.x = -size.x;
275 	}
276 	for(  pos.y=place.y;  pos.y<=place.y+size.y;  pos.y++  ) {
277 		for(  pos.x=place.x;  pos.x<=place.x+size.x;  pos.x++  ) {
278 			grund_t *gr = welt->lookup_kartenboden(pos);
279 			zeiger_t *z = gr->find<zeiger_t>();
280 			delete z;
281 		}
282 	}
283 }
284 
285 
286 
set_marker(koord place,koord size)287 void ai_t::set_marker( koord place, koord size )
288 {
289 	koord pos;
290 	if(size.y<0) {
291 		place.y += size.y;
292 		size.y = -size.y;
293 	}
294 	if(size.x<0) {
295 		place.x += size.x;
296 		size.x = -size.x;
297 	}
298 	for(  pos.y=place.y;  pos.y<=place.y+size.y;  pos.y++  ) {
299 		for(  pos.x=place.x;  pos.x<=place.x+size.x;  pos.x++  ) {
300 			grund_t *gr = welt->lookup_kartenboden(pos);
301 			zeiger_t *z = new zeiger_t(gr->get_pos(), this);
302 			z->set_image( skinverwaltung_t::belegtzeiger->get_image_id(0) );
303 			gr->obj_add( z );
304 		}
305 	}
306 }
307 
308 
309 
310 /* builds headquarters or upgrades one */
built_update_headquarter()311 bool ai_t::built_update_headquarter()
312 {
313 	// find next level
314 	const building_desc_t* desc = hausbauer_t::get_headquarters(get_headquarter_level(), welt->get_timeline_year_month());
315 	// is the a suitable one?
316 	if(desc!=NULL) {
317 		// cost is negative!
318 		sint64 const cost = welt->get_settings().cst_multiply_headquarter * desc->get_level() * desc->get_x() * desc->get_y();
319 		if(  finance->get_account_balance()+cost > finance->get_starting_money() ) {
320 			// and enough money left ...
321 			koord place = get_headquarter_pos();
322 			if(place!=koord::invalid) {
323 				// remove old hq
324 				grund_t *gr = welt->lookup_kartenboden(place);
325 				gebaeude_t *prev_hq = gr->find<gebaeude_t>();
326 				// other size?
327 				if(  desc->get_size()!=prev_hq->get_tile()->get_desc()->get_size()  ) {
328 					// needs new place
329 					place = koord::invalid;
330 				}
331 				else {
332 					// old positions false after rotation => correct it
333 					place = prev_hq->get_pos().get_2d() - prev_hq->get_tile()->get_offset();
334 				}
335 			}
336 			// needs new place?
337 			if(place==koord::invalid) {
338 				stadt_t *st = NULL;
339 				FOR(vector_tpl<halthandle_t>, const halt, haltestelle_t::get_alle_haltestellen()) {
340 					if(  halt->get_owner()==this  ) {
341 						st = welt->find_nearest_city(halt->get_basis_pos());
342 						break;
343 					}
344 				}
345 				if(st) {
346 					bool is_rotate=desc->get_all_layouts()>1;
347 					place = ai_building_place_with_road_finder(welt).find_place(st->get_pos(), desc->get_x(), desc->get_y(), desc->get_allowed_climate_bits(), &is_rotate);
348 				}
349 			}
350 			const char *err="No suitable ground!";
351 			if(  place!=koord::invalid  ) {
352 				err = tool_t::general_tool[TOOL_HEADQUARTER]->work( this, welt->lookup_kartenboden(place)->get_pos() );
353 				// success
354 				if(  err==NULL  ) {
355 					return true;
356 				}
357 			}
358 			// failed
359 			if(  place==koord::invalid  ||  err!=NULL  ) {
360 				dbg->warning( "ai_t::built_update_headquarter()", "HQ failed with : %s", translator::translate(err) );
361 			}
362 			return false;
363 		}
364 
365 	}
366 	return false;
367 }
368 
369 
370 
371 /**
372  * Find the last water tile using line algorithm
373  * start MUST be on land!
374  * @author Hajo
375  **/
find_shore(koord start,koord end) const376 koord ai_t::find_shore(koord start, koord end) const
377 {
378 	int x = start.x;
379 	int y = start.y;
380 	int xx = end.x;
381 	int yy = end.y;
382 
383 	int i, steps;
384 	int xp, yp;
385 	int xs, ys;
386 
387 	const int dx = xx - x;
388 	const int dy = yy - y;
389 
390 	steps = (abs(dx) > abs(dy) ? abs(dx) : abs(dy));
391 	if (steps == 0) steps = 1;
392 
393 	xs = (dx << 16) / steps;
394 	ys = (dy << 16) / steps;
395 
396 	xp = x << 16;
397 	yp = y << 16;
398 
399 	koord last = start;
400 	for (i = 0; i <= steps; i++) {
401 		koord next(xp>>16,yp>>16);
402 		if(next!=last) {
403 			if(!welt->lookup_kartenboden(next)->is_water()) {
404 				last = next;
405 			}
406 		}
407 		xp += xs;
408 		yp += ys;
409 	}
410 	// should always find something, since it ends in water ...
411 	return last;
412 }
413 
414 
415 
find_harbour(koord & start,koord & size,koord target)416 bool ai_t::find_harbour(koord &start, koord &size, koord target)
417 {
418 	koord shore = find_shore(target,start);
419 	// distance of last found point
420 	int dist=0x7FFFFFFF;
421 	koord k;
422 	// now find a nice shore next to here
423 	for(  k.y=max(1,shore.y-5);  k.y<shore.y+6  &&  k.y<welt->get_size().y-2; k.y++  ) {
424 		for(  k.x=max(1,shore.x-5);  k.x<shore.x+6  &&  k.y<welt->get_size().x-2; k.x++  ) {
425 			grund_t *gr = welt->lookup_kartenboden(k);
426 			if(  gr  &&  gr->get_grund_hang() != 0  &&  slope_t::is_way( gr->get_grund_hang() )  &&  gr->ist_natur()  &&  gr->get_hoehe() == welt->get_water_hgt(k)  &&  !gr->is_halt()  ) {
427 				koord zv = koord(gr->get_grund_hang());
428 				if(welt->lookup_kartenboden(k-zv)->get_weg_ribi(water_wt)) {
429 					// next place is also water
430 					koord dir[2] = { zv, koord(zv.y,zv.x) };
431 					koord platz = k+zv;
432 					int current_dist = koord_distance(k,target);
433 					if(  current_dist<dist  &&  suche_platz(platz,size,dir)  ){
434 						// we will take the shortest route found
435 						start = k;
436 						dist = current_dist;
437 					}
438 				}
439 			}
440 		}
441 	}
442 	return (dist<0x7FFFFFFF);
443 }
444 
445 
446 
create_simple_road_transport(koord platz1,koord size1,koord platz2,koord size2,const way_desc_t * road_weg)447 bool ai_t::create_simple_road_transport(koord platz1, koord size1, koord platz2, koord size2, const way_desc_t *road_weg )
448 {
449 	// sanity check here
450 	if(road_weg==NULL) {
451 		DBG_MESSAGE("ai_t::create_simple_road_transport()","called without valid way.");
452 		return false;
453 	}
454 
455 	// remove pointer
456 	clean_marker(platz1,size1);
457 	clean_marker(platz2,size2);
458 
459 	climate c1 = welt->get_climate(platz1);
460 	climate c2 = welt->get_climate(platz2);
461 
462 	if(  !(welt->flatten_tile( this, platz1, welt->lookup_kartenboden(platz1)->get_hoehe() )  &&  welt->flatten_tile( this, platz2, welt->lookup_kartenboden(platz2)->get_hoehe() ))  ) {
463 		// no flat land here?!?
464 		return false;
465 	}
466 	// ensure is land
467 	grund_t* bd = welt->lookup_kartenboden(platz1);
468 	if (bd->get_typ() == grund_t::wasser) {
469 		welt->set_water_hgt(platz1, bd->get_hoehe()-1);
470 		welt->access(platz1)->correct_water();
471 		welt->set_climate(platz1, c1, true);
472 	}
473 	bd = welt->lookup_kartenboden(platz2);
474 	if (bd->get_typ() == grund_t::wasser) {
475 		welt->set_water_hgt(platz2, bd->get_hoehe()-1);
476 		welt->access(platz2)->correct_water();
477 		welt->set_climate(platz2, c2, true);
478 	}
479 
480 	// is there already a connection?
481 	// get a default vehikel
482 	vehicle_desc_t test_desc(road_wt, 25, vehicle_desc_t::diesel );
483 	vehicle_t* test_driver = vehicle_builder_t::build(welt->lookup_kartenboden(platz1)->get_pos(), this, NULL, &test_desc);
484 	test_driver->set_flag( obj_t::not_on_map );
485 	route_t verbindung;
486 	if(  verbindung.calc_route(welt, welt->lookup_kartenboden(platz1)->get_pos(), welt->lookup_kartenboden(platz2)->get_pos(), test_driver, 0, 0)  &&
487 		 verbindung.get_count() < 2u*koord_distance(platz1,platz2))  {
488 DBG_MESSAGE("ai_passenger_t::create_simple_road_transport()","Already connection between %d,%d to %d,%d is only %i",platz1.x, platz1.y, platz2.x, platz2.y, verbindung.get_count() );
489 		// found something with the nearly same length
490 		delete test_driver;
491 		return true;
492 	}
493 	delete test_driver;
494 
495 	// no connection => built one!
496 	way_builder_t bauigel(this);
497 	bauigel.init_builder( way_builder_t::strasse, road_weg, tunnel_builder_t::get_tunnel_desc(road_wt,road_weg->get_topspeed(),welt->get_timeline_year_month()), bridge_builder_t::find_bridge(road_wt,road_weg->get_topspeed(),welt->get_timeline_year_month()) );
498 
499 	// we won't destroy cities (and save the money)
500 	bauigel.set_keep_existing_faster_ways(true);
501 	bauigel.set_keep_city_roads(true);
502 	bauigel.set_maximum(10000);
503 
504 	bauigel.calc_route(welt->lookup_kartenboden(platz1)->get_pos(),welt->lookup_kartenboden(platz2)->get_pos());
505 	INT_CHECK("ai 501");
506 
507 	if(  bauigel.calc_costs() > finance->get_netwealth()  ) {
508 		// too expensive
509 		return false;
510 	}
511 
512 	// now try route with terraforming
513 	way_builder_t baumaulwurf(this);
514 	baumaulwurf.init_builder( way_builder_t::strasse|way_builder_t::terraform_flag, road_weg, tunnel_builder_t::get_tunnel_desc(road_wt,road_weg->get_topspeed(),welt->get_timeline_year_month()), bridge_builder_t::find_bridge(road_wt,road_weg->get_topspeed(),welt->get_timeline_year_month()) );
515 	baumaulwurf.set_keep_existing_faster_ways(true);
516 	baumaulwurf.set_keep_city_roads(true);
517 	baumaulwurf.set_maximum(10000);
518 	baumaulwurf.calc_route(welt->lookup_kartenboden(platz1)->get_pos(),welt->lookup_kartenboden(platz2)->get_pos());
519 
520 	// build with terraforming if shorter and enough money is available
521 	bool with_tf = (baumaulwurf.get_count() > 2)  &&  (10*baumaulwurf.get_count() < 9*bauigel.get_count()  ||  bauigel.get_count() <= 2);
522 	if(  with_tf  &&  baumaulwurf.calc_costs() > finance->get_netwealth()  ) {
523 		// too expensive
524 		with_tf = false;
525 	}
526 
527 	// now build with or without terraforming
528 	if (with_tf) {
529 		DBG_MESSAGE("ai_t::create_simple_road_transport()","building not so simple road from %d,%d to %d,%d",platz1.x, platz1.y, platz2.x, platz2.y);
530 		baumaulwurf.build();
531 		return true;
532 	}
533 	else if(bauigel.get_count() > 2) {
534 		DBG_MESSAGE("ai_t::create_simple_road_transport()","building simple road from %d,%d to %d,%d",platz1.x, platz1.y, platz2.x, platz2.y);
535 		bauigel.build();
536 		return true;
537 	}
538 	// beware: The stop position might have changes!
539 	DBG_MESSAGE("ai_t::create_simple_road_transport()","building simple road from %d,%d to %d,%d failed",platz1.x, platz1.y, platz2.x, platz2.y);
540 	return false;
541 }
542 
543 
544 /* create new AI */
ai_t(uint8 nr)545 ai_t::ai_t(uint8 nr) : player_t( nr )
546 {
547 	road_transport = rail_transport = air_transport = ship_transport = false;
548 	construction_speed = env_t::default_ai_construction_speed;
549 }
550 
551 
rdwr(loadsave_t * file)552 void ai_t::rdwr(loadsave_t *file)
553 {
554 	player_t::rdwr(file);
555 
556 	if(  file->is_version_less(111, 1)  ) {
557 		// do not know about ai_t
558 		return;
559 	}
560 
561 	file->rdwr_long( construction_speed );
562 	file->rdwr_bool( road_transport );
563 	file->rdwr_bool( rail_transport );
564 	file->rdwr_bool( air_transport );
565 	file->rdwr_bool( ship_transport );
566 }
567 
568 
vehikel_search(waytype_t typ,const uint32 target_power,const sint32 target_speed,const goods_desc_t * target_freight,bool include_electric)569 const vehicle_desc_t *ai_t::vehikel_search(waytype_t typ, const uint32 target_power, const sint32 target_speed, const goods_desc_t * target_freight, bool include_electric)
570 {
571 	bool obsolete_allowed = welt->get_settings().get_allow_buying_obsolete_vehicles();
572 	return vehicle_builder_t::vehikel_search(typ, welt->get_timeline_year_month(), target_power, target_speed, target_freight, include_electric, !obsolete_allowed);
573 }
574