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