1 /**
2  ** Barge.cc - Ships, carts, flying-carpets.
3  **
4  ** Written: 7/13/2000 - JSF
5  **/
6 
7 /*
8 Copyright (C) 2000  Jeffrey S. Freedman
9 
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License
12 as published by the Free Software Foundation; either version 2
13 of the License, or (at your option) any later version.
14 
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23 */
24 
25 #ifdef HAVE_CONFIG_H
26 #  include <config.h>
27 #endif
28 
29 #include "gamemap.h"
30 #include "chunks.h"
31 #include "barge.h"
32 #include "gamewin.h"
33 #include "actors.h"
34 #include "Zombie.h"
35 #include "citerate.h"
36 #include "dir.h"
37 #include "objiter.h"
38 #include "game.h"
39 #include "databuf.h"
40 #include "ucsched.h"
41 #include "cheat.h"
42 #include "exult.h"
43 #include "shapeinf.h"
44 #include "ignore_unused_variable_warning.h"
45 
46 #ifdef USE_EXULTSTUDIO
47 #include "server.h"
48 #include "objserial.h"
49 #include "mouse.h"
50 #include "servemsg.h"
51 #endif
52 
53 using std::ostream;
54 using std::cout;
55 using std::endl;
56 
57 Game_object_shared Barge_object::editing;
58 
59 /*
60  *  Rotate a point 90 degrees to the right around a point.
61  *
62  *  In cartesian coords with 'c' as center, the rule is:
63  *      (newx, newy) = (oldy, -oldx)
64  */
65 
Rotate90r(Tile_coord const & t,Tile_coord const & c)66 inline Tile_coord Rotate90r(
67     Tile_coord const &t,            // Tile to move.
68     Tile_coord const &c         // Center to rotate around.
69 ) {
70 	// Get cart. coords. rel. to center.
71 	int rx = t.tx - c.tx;
72 	int ry = c.ty - t.ty;
73 	return Tile_coord(c.tx + ry, c.ty + rx, t.tz);
74 }
75 
76 /*
77  *  Rotate a point 90 degrees to the left around a point.
78  *
79  *  In cartesian coords with 'c' as center, the rule is:
80  *      (newx, newy) = (-oldy, oldx)
81  */
82 
Rotate90l(Tile_coord const & t,Tile_coord const & c)83 inline Tile_coord Rotate90l(
84     Tile_coord const &t,            // Tile to move.
85     Tile_coord const &c         // Center to rotate around.
86 ) {
87 	// Get cart. coords. rel. to center.
88 	int rx = t.tx - c.tx;
89 	int ry = c.ty - t.ty;
90 	return Tile_coord(c.tx - ry, c.ty - rx, t.tz);
91 }
92 
93 /*
94  *  Rotate a point 180 degrees around a point.
95  *
96  *  In cartesian coords with 'c' as center, the rule is:
97  *      (newx, newy) = (-oldx, -oldy)
98  */
99 
Rotate180(Tile_coord const & t,Tile_coord const & c)100 inline Tile_coord Rotate180(
101     Tile_coord const &t,            // Tile to move.
102     Tile_coord const &c         // Center to rotate around.
103 ) {
104 	// Get cart. coords. rel. to center.
105 	int rx = t.tx - c.tx;
106 	int ry = c.ty - t.ty;
107 	return Tile_coord(c.tx - rx, c.ty + ry, t.tz);
108 }
109 
110 /*
111  *  Figure tile where an object will be if it's rotated 90 degrees around
112  *  a point counterclockwise, assuming its 'hot spot'
113  *  is at its lower-right corner.
114  */
115 
Rotate90r(Game_window * gwin,Game_object * obj,int xtiles,int ytiles,Tile_coord const & c)116 inline Tile_coord Rotate90r(
117     Game_window *gwin,
118     Game_object *obj,
119     int xtiles, int ytiles,     // Object dimensions.
120     Tile_coord const &c         // Rotate around this.
121 ) {
122 	ignore_unused_variable_warning(gwin, xtiles);
123 	// Rotate hot spot.
124 	Tile_coord r = Rotate90r(obj->get_tile(), c);
125 	// New hotspot is what used to be the
126 	//   upper-right corner.
127 	r.tx = (r.tx + ytiles + c_num_tiles) % c_num_tiles;
128 	r.ty = (r.ty + c_num_tiles) % c_num_tiles;
129 	return r;
130 }
131 
132 /*
133  *  Figure tile where an object will be if it's rotated 90 degrees around
134  *  a point, assuming its 'hot spot' is at its lower-right corner.
135  */
136 
Rotate90l(Game_window * gwin,Game_object * obj,int xtiles,int ytiles,Tile_coord const & c)137 inline Tile_coord Rotate90l(
138     Game_window *gwin,
139     Game_object *obj,
140     int xtiles, int ytiles,     // Object dimensions.
141     Tile_coord const &c         // Rotate around this.
142 ) {
143 	ignore_unused_variable_warning(gwin, ytiles);
144 	// Rotate hot spot.
145 	Tile_coord r = Rotate90l(obj->get_tile(), c);
146 	// New hot-spot is old lower-left.
147 	r.ty = (r.ty + xtiles + c_num_tiles) % c_num_tiles;
148 	r.tx = (r.tx + c_num_tiles) % c_num_tiles;
149 	return r;
150 }
151 
152 /*
153  *  Figure tile where an object will be if it's rotated 180 degrees around
154  *  a point, assuming its 'hot spot' is at its lower-right corner.
155  */
156 
Rotate180(Game_window * gwin,Game_object * obj,int xtiles,int ytiles,Tile_coord const & c)157 inline Tile_coord Rotate180(
158     Game_window *gwin,
159     Game_object *obj,
160     int xtiles, int ytiles,     // Object dimensions.
161     Tile_coord const &c         // Rotate around this.
162 ) {
163 	ignore_unused_variable_warning(gwin);
164 	// Rotate hot spot.
165 	Tile_coord r = Rotate180(obj->get_tile(), c);
166 	// New hotspot is what used to be the
167 	//   upper-left corner.
168 	r.tx = (r.tx + xtiles + c_num_tiles) % c_num_tiles;
169 	r.ty = (r.ty + ytiles + c_num_tiles) % c_num_tiles;
170 	return r;
171 }
172 
173 /*
174  *  Swap dimensions.
175  */
176 
swap_dims()177 inline void Barge_object::swap_dims(
178 ) {
179 	int tmp = xtiles;       // Swap dims.
180 	xtiles = ytiles;
181 	ytiles = tmp;
182 }
183 
184 /*
185  *  Get footprint in tiles.
186  */
187 
get_tile_footprint()188 TileRect Barge_object::get_tile_footprint(
189 ) {
190 	Tile_coord pos = get_tile();
191 	int xts = get_xtiles();
192 	int yts = get_ytiles();
193 	TileRect foot((pos.tx - xts + 1 + c_num_tiles) % c_num_tiles,
194 	               (pos.ty - yts + 1 + c_num_tiles) % c_num_tiles, xts, yts);
195 	return foot;
196 }
197 
198 /*
199  *  Set center.
200  */
201 
set_center()202 inline void Barge_object::set_center(
203 ) {
204 	center = get_tile();
205 	center.tx = (center.tx - xtiles / 2 + c_num_tiles) % c_num_tiles;
206 	center.ty = (center.ty - ytiles / 2 + c_num_tiles) % c_num_tiles;
207 }
208 
209 /*
210  *  See if okay to rotate.
211  +++++++++++Handle wrapping here+++++++++++
212  */
213 
okay_to_rotate(Tile_coord const & pos)214 int Barge_object::okay_to_rotate(
215     Tile_coord const &pos           // New position (bottom-right).
216 ) {
217 	int lift = get_lift();
218 	// Special case for carpet.
219 	int move_type = (lift > 0) ? (MOVE_LEVITATE) : MOVE_ALL_TERRAIN;
220 	// Get footprint in tiles.
221 	TileRect foot = get_tile_footprint();
222 	int xts = get_xtiles();
223 	int yts = get_ytiles();
224 	// Get where new footprint will be.
225 	TileRect newfoot(pos.tx - yts + 1, pos.ty - xts + 1, yts, xts);
226 	int new_lift;
227 	if (newfoot.y < foot.y)     // Got a piece above the old one?
228 		// Check area.  (No dropping allowed.)
229 		if (Map_chunk::is_blocked(4, lift,
230 		                          newfoot.x, newfoot.y, newfoot.w, foot.y - newfoot.y,
231 		                          new_lift, move_type, 0) || new_lift != lift)
232 			return 0;
233 	if (foot.y + foot.h < newfoot.y + newfoot.h)
234 		// A piece below old one.
235 		if (Map_chunk::is_blocked(4, lift,
236 		                          newfoot.x, foot.y + foot.h, newfoot.w,
237 		                          newfoot.y + newfoot.h - (foot.y + foot.h),
238 		                          new_lift, move_type, 0) || new_lift != lift)
239 			return 0;
240 	if (newfoot.x < foot.x)     // Piece to the left?
241 		if (Map_chunk::is_blocked(4, lift,
242 		                          newfoot.x, newfoot.y, foot.x - newfoot.x, newfoot.h,
243 		                          new_lift, move_type, 0) || new_lift != lift)
244 			return 0;
245 	if (foot.x + foot.w < newfoot.x + newfoot.w)
246 		// Piece to the right.
247 		if (Map_chunk::is_blocked(4, lift,
248 		                          foot.x + foot.w, newfoot.y,
249 		                          newfoot.x + newfoot.w - (foot.x + foot.w), newfoot.h,
250 		                          new_lift, move_type, 0) || new_lift != lift)
251 			return 0;
252 	return 1;
253 }
254 
255 /*
256  *  Delete.
257  */
258 
~Barge_object()259 Barge_object::~Barge_object(
260 ) {
261 	delete path;
262 }
263 
264 /*
265  *  Gather up all objects that appear to be on this barge.
266  *  Also inits. 'center'.
267  */
268 
gather()269 void Barge_object::gather(
270 ) {
271 	if (!gmap->get_chunk_safely(get_cx(), get_cy()))
272 		return;         // Not set in world yet.
273 	ice_raft = false;       // We'll just detect it each time.
274 	objects.resize(perm_count); // Start fresh.
275 	// Get footprint in tiles.
276 	TileRect foot = get_tile_footprint();
277 	int lift = get_lift();      // How high we are.
278 	// Go through intersected chunks.
279 	Chunk_intersect_iterator next_chunk(foot);
280 	TileRect tiles;
281 	int cx;
282 	int cy;
283 	while (next_chunk.get_next(tiles, cx, cy)) {
284 		Map_chunk *chunk = gmap->get_chunk(cx, cy);
285 		tiles.x += cx * c_tiles_per_chunk;
286 		tiles.y += cy * c_tiles_per_chunk;
287 		Game_object *obj;
288 		Object_iterator next(chunk->get_objects());
289 		while ((obj = next.get_next()) != nullptr) {
290 			// Look at each object.
291 			if (obj == this)
292 				continue;
293 			if (obj->is_egg()) // don't pick up eggs
294 				continue;
295 			Tile_coord t = obj->get_tile();
296 			if (!tiles.has_world_point(t.tx, t.ty) ||
297 			        obj->get_owner() == this)
298 				continue;
299 			const Shape_info &info = obj->get_info();
300 			// Above barge, within 5-tiles up?
301 			bool isbarge = info.is_barge_part() /*+++ || !info.get_weight() */;
302 			if (t.tz + info.get_3d_height() > lift &&
303 			        ((isbarge && t.tz >= lift - 1) ||
304 			         (t.tz < lift + 5 && t.tz >= lift /*+++ + 1 */))) {
305 				objects.push_back(obj->shared_from_this());
306 				int btype = obj->get_info().get_barge_type();
307 				if (btype == Shape_info::barge_raft)
308 					ice_raft = true;
309 				else if (btype == Shape_info::barge_turtle)
310 					xtiles = 20;
311 			}
312 		}
313 	}
314 	set_center();
315 	// Test for boat.
316 	Map_chunk *chunk = gmap->get_chunk_safely(
317 	                       center.tx / c_tiles_per_chunk, center.ty / c_tiles_per_chunk);
318 	if (boat == -1 && chunk != nullptr) {
319 		ShapeID flat = chunk->get_flat(center.tx % c_tiles_per_chunk,
320 		                               center.ty % c_tiles_per_chunk);
321 		if (flat.is_invalid())
322 			boat = 0;
323 		else {
324 			const Shape_info &info = flat.get_info();
325 			boat = info.is_water();
326 		}
327 	}
328 	gathered = true;
329 }
330 
331 /*
332  *  Add a dirty rectangle for our current position.
333  */
334 
add_dirty()335 void Barge_object::add_dirty(
336 ) {
337 	int x;
338 	int y;           // Get lower-right corner.
339 	gwin->get_shape_location(this, x, y);
340 	int w = xtiles * c_tilesize;
341 	int h = ytiles * c_tilesize;
342 	TileRect box(x - w, y - h, w, h);
343 	const int barge_enlarge = (c_tilesize + c_tilesize / 4);
344 	const int barge_stretch = (4 * c_tilesize + c_tilesize / 2);
345 	box.enlarge(barge_enlarge);     // Make it a bit bigger.
346 	if (dir % 2) {      // Horizontal?  Stretch.
347 		box.x -= barge_enlarge / 2;
348 		box.w += barge_stretch;
349 	} else {
350 		box.y -= barge_enlarge / 2;
351 		box.h += barge_stretch;
352 	}
353 	box = gwin->clip_to_win(box);   // Intersect with screen.
354 	gwin->add_dirty(box);
355 }
356 
357 /*
358  *  Finish up moving all the objects by adding them back and deleting the
359  *  saved list of positions.
360  */
361 
finish_move(Tile_coord * positions,int newmap)362 void Barge_object::finish_move(
363     Tile_coord *positions,      // New positions.  Deleted when done.
364     int newmap          // Map #, or -1 for current.
365 ) {
366 	set_center();           // Update center.
367 	int cnt = objects.size();   // We'll move each object.
368 	for (int i = 0; i < cnt; i++) { // Now add them back in new location.
369 		Game_object *obj = get_object(i);
370 		if (i < perm_count) // Restore us as owner.
371 			obj->set_owner(this);
372 		obj->move(positions[i], newmap);
373 	}
374 	delete [] positions;
375 	// Check for scrolling.
376 	gwin->scroll_if_needed(center);
377 }
378 
379 /*
380  *  Turn to face a given direction.
381  */
382 
face_direction(int ndir)383 void Barge_object::face_direction(
384     int ndir            // 0-7 0==North.
385 ) {
386 	ndir /= 2;          // Convert to 0-3.
387 	switch ((4 + ndir - dir) % 4) {
388 	case 1:             // Rotate 90 degrees right.
389 		turn_right();
390 		break;
391 	case 2:
392 		turn_around();      // 180 degrees.
393 		break;
394 	case 3:
395 		turn_left();
396 		break;
397 	default:
398 		break;
399 	}
400 }
401 
402 /*
403  *  Travel towards a given tile.
404  */
405 
travel_to_tile(Tile_coord const & dest,int speed)406 void Barge_object::travel_to_tile(
407     Tile_coord const &dest,     // Destination.
408     int speed           // Time between frames (msecs).
409 ) {
410 	if (!path)
411 		path = new Zombie();
412 	// Set up new path.
413 	if (path->NewPath(get_tile(), dest, nullptr)) {
414 		frame_time = speed;
415 		// Figure new direction.
416 		Tile_coord cur = get_tile();
417 		int dy = Tile_coord::delta(cur.ty, dest.ty);
418 		int dx = Tile_coord::delta(cur.tx, dest.tx);
419 		int ndir = Get_direction4(-dy, dx);
420 		if (!ice_raft)      // Ice-raft doesn't rotate.
421 			face_direction(ndir);
422 		if (!in_queue())    // Not already in queue?
423 			gwin->get_tqueue()->add(Game::get_ticks(), this);
424 	} else
425 		frame_time = 0;     // Not moving.
426 }
427 
428 /*
429  *  Turn 90 degrees to the right.
430  */
431 
turn_right()432 void Barge_object::turn_right(
433 ) {
434 	add_dirty();            // Want to repaint old position.
435 	// Move the barge itself.
436 	Tile_coord rot = Rotate90r(gwin, this, xtiles, ytiles, center);
437 	if (!okay_to_rotate(rot))   // Check for blockage.
438 		return;
439 	Container_game_object::move(rot.tx, rot.ty, rot.tz);
440 	swap_dims();            // Exchange xtiles, ytiles.
441 	dir = (dir + 1) % 4;    // Increment direction.
442 	int cnt = objects.size();   // We'll move each object.
443 	// But 1st, remove & save new pos.
444 	auto *positions = new Tile_coord[cnt];
445 	for (int i = 0; i < cnt; i++) {
446 		Game_object *obj = get_object(i);
447 		int frame = obj->get_framenum();
448 		const Shape_info &info = obj->get_info();
449 		positions[i] = Rotate90r(gwin, obj, info.get_3d_xtiles(frame),
450 		                         info.get_3d_ytiles(frame), center);
451         Game_object_shared keep;
452 		obj->remove_this(&keep);    // Remove object from world.
453 		// Set to rotated frame.
454 		obj->change_frame(obj->get_rotated_frame(1));
455 		obj->set_invalid(); // So it gets added back right.
456 	}
457 	finish_move(positions);     // Add back & del. positions.
458 }
459 
460 /*
461  *  Turn 90 degrees to the left.
462  */
463 
turn_left()464 void Barge_object::turn_left(
465 ) {
466 	add_dirty();            // Want to repaint old position.
467 	// Move the barge itself.
468 	Tile_coord rot = Rotate90l(gwin, this, xtiles, ytiles, center);
469 	if (!okay_to_rotate(rot))   // Check for blockage.
470 		return;
471 	Container_game_object::move(rot.tx, rot.ty, rot.tz);
472 	swap_dims();            // Exchange xtiles, ytiles.
473 	dir = (dir + 3) % 4;    // Increment direction.
474 	int cnt = objects.size();   // We'll move each object.
475 	// But 1st, remove & save new pos.
476 	auto *positions = new Tile_coord[cnt];
477 	for (int i = 0; i < cnt; i++) {
478 		Game_object *obj = get_object(i);
479 		int frame = obj->get_framenum();
480 		const Shape_info &info = obj->get_info();
481 		positions[i] = Rotate90l(gwin, obj, info.get_3d_xtiles(frame),
482 		                         info.get_3d_ytiles(frame), center);
483 		Game_object_shared keep;
484 		obj->remove_this(&keep);    // Remove object from world.
485 		// Set to rotated frame.
486 		obj->change_frame(obj->get_rotated_frame(3));
487 		obj->set_invalid(); // So it gets added back right.
488 	}
489 	finish_move(positions);     // Add back & del. positions.
490 }
491 
492 /*
493  *  Turn 180 degrees.
494  */
495 
turn_around()496 void Barge_object::turn_around(
497 ) {
498 	add_dirty();            // Want to repaint old position.
499 	// Move the barge itself.
500 	Tile_coord rot = Rotate180(gwin, this, xtiles, ytiles, center);
501 	Container_game_object::move(rot.tx, rot.ty, rot.tz);
502 	dir = (dir + 2) % 4;    // Increment direction.
503 	int cnt = objects.size();   // We'll move each object.
504 	// But 1st, remove & save new pos.
505 	auto *positions = new Tile_coord[cnt];
506 	for (int i = 0; i < cnt; i++) {
507 		Game_object *obj = get_object(i);
508 		int frame = obj->get_framenum();
509 		const Shape_info &info = obj->get_info();
510 		positions[i] = Rotate180(gwin, obj, info.get_3d_xtiles(frame),
511 		                         info.get_3d_ytiles(frame), center);
512 		Game_object_shared keep;
513 		obj->remove_this(&keep);    // Remove object from world.
514 		// Set to rotated frame.
515 		obj->change_frame(obj->get_rotated_frame(2));
516 		obj->set_invalid(); // So it gets added back right.
517 	}
518 	finish_move(positions);     // Add back & del. positions.
519 }
520 
521 /*
522  *  Ending 'barge mode'.
523  */
524 
done()525 void Barge_object::done(
526 ) {
527 	gathered = false;       // Clear for next time. (needed for SI turtle)
528 	static int norecurse = 0;   // Don't recurse on the code below.
529 	if (norecurse > 0)
530 		return;
531 	norecurse++;
532 	if (boat == 1) {        // Look for sails on boat.
533 		// Pretend they were clicked on.
534 		int cnt = objects.size();   // Look for open sail.
535 		for (int i = 0; i < cnt; i++) {
536 			Game_object *obj = objects[i].get();
537 			if (obj->get_info().get_barge_type() == Shape_info::barge_sails &&
538 			        (obj->get_framenum() & 7) < 4) {
539 				obj->activate();
540 				break;
541 			}
542 		}
543 	}
544 	norecurse--;
545 }
546 
547 /*
548  *  Is it okay to land?
549  */
550 
okay_to_land()551 int Barge_object::okay_to_land(
552 ) {
553 	TileRect foot = get_tile_footprint();
554 	int lift = get_lift();      // How high we are.
555 	// Go through intersected chunks.
556 	Chunk_intersect_iterator next_chunk(foot);
557 	TileRect tiles;
558 	int cx;
559 	int cy;
560 	while (next_chunk.get_next(tiles, cx, cy)) {
561 		// Check each tile.
562 		Map_chunk *chunk = gmap->get_chunk(cx, cy);
563 		for (int ty = tiles.y; ty < tiles.y + tiles.h; ty++)
564 			for (int tx = tiles.x; tx < tiles.x + tiles.w; tx++)
565 				if (chunk->get_highest_blocked(lift, tx, ty) != -1 ||
566 				        chunk->get_flat(tx, ty).get_info().is_water())
567 					return 0;
568 	}
569 	return 1;
570 }
571 
572 /*
573  *  Handle a time event (for animation).
574  */
575 
handle_event(unsigned long curtime,uintptr udata)576 void Barge_object::handle_event(
577     unsigned long curtime,      // Current time of day.
578     uintptr udata          // Ignored.
579 ) {
580 	if (!path || !frame_time || gwin->get_moving_barge() != this)
581 		return;         // We shouldn't be doing anything.
582 	Tile_coord tile;        // Get spot & walk there.
583 	// Take two steps for speed.
584 	if (!path->GetNextStep(tile) || !Barge_object::step(tile))
585 		frame_time = 0;
586 	else if (!first_step) {     // But not when just starting.
587 		taking_2nd_step = true;
588 		if (!path->GetNextStep(tile) || !Barge_object::step(tile))
589 			frame_time = 0;
590 		taking_2nd_step = false;
591 	}
592 	if (frame_time)         // Still good?
593 		gwin->get_tqueue()->add(curtime + frame_time, this, udata);
594 	first_step = false;     // After 1st, move 2 at a time.
595 }
596 
597 /*
598  *  Move to a new absolute location.
599  */
600 
move(int newtx,int newty,int newlift,int newmap)601 void Barge_object::move(
602     int newtx,
603     int newty,
604     int newlift,
605     int newmap
606 ) {
607 	if (!chunk) {       // Not currently on map?
608 		// UNTIL drag-n-drop does the gather properly.
609 		Container_game_object::move(newtx, newty, newlift, newmap);
610 		set_center();
611 		set_to_gather();
612 		return;
613 	}
614 	if (!gathered)          // Happens in SI with turtle.
615 		gather();
616 	// Want to repaint old position.
617 	add_dirty();
618 	// Get current location.
619 	Tile_coord old = get_tile();
620 	if (newmap == -1) newmap = get_map_num();
621 	// Move the barge itself.
622 	Container_game_object::move(newtx, newty, newlift, newmap);
623 	set_center();
624 	// Get deltas.
625 	int dx = newtx - old.tx;
626 	int dy = newty - old.ty;
627 	int dz = newlift - old.tz;
628 	int cnt = objects.size();   // We'll move each object.
629 	// But 1st, remove & save new pos.
630 	auto *positions = new Tile_coord[cnt];
631 	int i;
632 	for (i = 0; i < cnt; i++) {
633 		Game_object *obj = get_object(i);
634 		Tile_coord ot = obj->get_tile();
635 		// Watch for world-wrapping.
636 		positions[i] = Tile_coord(
637 		                   (ot.tx + dx + c_num_tiles) % c_num_tiles,
638 		                   (ot.ty + dy + c_num_tiles) % c_num_tiles,
639 		                   ot.tz + dz);
640 		Game_object_shared keep;
641 		obj->remove_this(&keep);    // Remove object from world.
642 		obj->set_invalid(); // So it gets added back right.
643 		if (!taking_2nd_step) {
644 			// Animate a few shapes.
645 			int frame = obj->get_framenum();
646 			switch (obj->get_info().get_barge_type()) {
647 			case Shape_info::barge_wheel:       // Cart wheel.
648 				obj->change_frame(((frame + 1) & 3) | (frame & 32));
649 				break;
650 			case Shape_info::barge_draftanimal:     // Draft horse.
651 				obj->change_frame(((frame + 4) & 15) | (frame & 32));
652 				break;
653 			}
654 		}
655 	}
656 	finish_move(positions, newmap); // Add back & del. positions.
657 }
658 
659 /*
660  *  Remove an object.
661  */
662 
remove(Game_object * obj)663 void Barge_object::remove(
664     Game_object *obj
665 ) {
666 	obj->set_owner(nullptr);
667 	Game_object_shared keep;
668 	obj->remove_this(&keep);        // Now remove from outside world.
669 }
670 
671 /*
672  *  Add an object.
673  *
674  *  Output: 0, meaning object should also be added to chunk.
675  */
676 
add(Game_object * obj,bool dont_check,bool combine,bool noset)677 bool Barge_object::add(
678     Game_object *obj,
679     bool dont_check,
680     bool combine,           // True to try to combine obj.  MAY
681     //   cause obj to be deleted.
682     bool noset      // True to prevent actors from setting sched. weapon.
683 ) {
684 	ignore_unused_variable_warning(dont_check, combine, noset);
685 	objects.push_back(obj->shared_from_this());     // Add to list.
686 	return false;         // We want it added to the chunk.
687 }
688 
689 /*
690  *  Is a given object part of the barge (after a 'gather')?
691  */
692 
contains(Game_object * obj)693 bool Barge_object::contains(
694     Game_object *obj
695 ) {
696 	for (auto& object : objects)
697 		if (obj == object.get())
698 			return true;
699 	return false;
700 }
701 
702 /*
703  *  Drop another onto this.
704  *
705  *  Output: 0 to reject, 1 to accept.
706  */
707 
drop(Game_object * obj)708 bool Barge_object::drop(
709     Game_object *obj
710 ) {
711 	ignore_unused_variable_warning(obj);
712 	return false;           //++++++Later.
713 }
714 
715 /*
716  *  Paint at given spot in world.
717  */
718 
paint()719 void Barge_object::paint(
720 ) {
721 	// DON'T paint barge shape itself.
722 	// The objects are in the chunk too.
723 	if (gwin->paint_eggs) {
724 		Container_game_object::paint();
725 		int pix = sman->get_special_pixel(CURSED_PIXEL);
726 		int rx;
727 		int by;
728 		int lx;
729 		int ty; // Right, bottom, left, top.
730 		gwin->get_shape_location(this, rx, by);
731 		lx = rx - xtiles * c_tilesize + 1;
732 		ty = by - ytiles * c_tilesize + 1;
733 		// Little square at lower-right.
734 		gwin->get_win()->fill8(pix, 4, 4, rx - 2, by - 2);
735 		// Little square at top.
736 		gwin->get_win()->fill8(pix, 4, 4, lx - 1, ty - 1);
737 		// Horiz. line along top, bottom.
738 		gwin->get_win()->fill8(pix, xtiles * c_tilesize, 1, lx, ty);
739 		gwin->get_win()->fill8(pix, xtiles * c_tilesize, 1, lx, by);
740 		// Vert. line to left, right.
741 		gwin->get_win()->fill8(pix, 1, ytiles * c_tilesize, lx, ty);
742 		gwin->get_win()->fill8(pix, 1, ytiles * c_tilesize, rx, ty);
743 	}
744 }
745 
746 /*
747  *  Edit in ExultStudio.
748  */
749 
activate(int)750 void Barge_object::activate(
751     int /* event */
752 ) {
753 	edit();
754 }
755 
756 /*
757  *  Edit in ExultStudio.
758  *
759  *  Output: True if map-editing & ES is present.
760  */
761 
edit()762 bool Barge_object::edit(
763 ) {
764 #ifdef USE_EXULTSTUDIO
765 	if (client_socket >= 0 &&   // Talking to ExultStudio?
766 	        cheat.in_map_editor()) {
767 		editing.reset();
768 		Tile_coord t = get_tile();
769 		if (Barge_object_out(client_socket, this, t.tx, t.ty, t.tz,
770 		                     get_shapenum(), get_framenum(),
771 		                     xtiles, ytiles, dir) != -1) {
772 			cout << "Sent barge data to ExultStudio" << endl;
773 			editing = shared_from_this();
774 		} else
775 			cout << "Error sending barge data to ExultStudio" <<
776 			     endl;
777 		return true;
778 	}
779 #endif
780 	return false;
781 }
782 
783 
784 /*
785  *  Message to update from ExultStudio.
786  */
787 
update_from_studio(unsigned char * data,int datalen)788 void Barge_object::update_from_studio(
789     unsigned char *data,
790     int datalen
791 ) {
792 #ifdef USE_EXULTSTUDIO
793 	Barge_object *barge;
794 	int tx;
795 	int ty;
796 	int tz;
797 	int shape;
798 	int frame;
799 	int xtiles;
800 	int ytiles;
801 	int dir;
802 	if (!Barge_object_in(data, datalen, barge, tx, ty, tz, shape, frame,
803 	                     xtiles, ytiles, dir)) {
804 		cout << "Error decoding barge" << endl;
805 		return;
806 	}
807 	if (barge && barge != editing.get()) {
808 		cout << "Barge from ExultStudio is not being edited" << endl;
809 		return;
810 	}
811 	// Keeps NPC alive until end of function
812 	Game_object_shared keep = std::move(editing);
813 	if (!barge) {       // Create a new one?
814 		int x;
815 		int y;
816 		if (!Get_click(x, y, Mouse::hand, nullptr)) {
817 			if (client_socket >= 0)
818 				Exult_server::Send_data(client_socket,
819 				                        Exult_server::cancel);
820 			return;
821 		}
822 		if (shape == -1)
823 			shape = 961;    // FOR NOW.
824 		// Create.  Gets initialized below.
825 		auto obj = std::make_shared<Barge_object>(shape, 0, 0, 0, 0, 0, 0, 0);
826 		barge = obj.get();
827 		keep = std::move(obj);
828 		int lift;       // Try to drop at increasing hts.
829 		for (lift = 0; lift < 12; lift++)
830 			if (gwin->drop_at_lift(barge, x, y, lift) == 1)
831 				break;
832 		if (lift == 12) {
833 			if (client_socket >= 0)
834 				Exult_server::Send_data(client_socket,
835 				                        Exult_server::cancel);
836 			delete barge;
837 			return;
838 		}
839 		if (client_socket >= 0)
840 			Exult_server::Send_data(client_socket,
841 			                        Exult_server::user_responded);
842 	}
843 	barge->xtiles = xtiles;
844 	barge->ytiles = ytiles;
845 	barge->dir = dir;
846 	if (shape != -1)
847 		barge->set_shape(shape);
848 	gwin->add_dirty(barge);
849 	cout << "Barge updated" << endl;
850 #else
851 	ignore_unused_variable_warning(data, datalen);
852 #endif
853 }
854 
855 /*
856  *  Step onto an adjacent tile.
857  *
858  *  Output: false if blocked.
859  *      Dormant is set if off screen.
860  */
861 
step(Tile_coord t,int frame,bool force)862 bool Barge_object::step(
863     Tile_coord t,           // Tile to step onto.
864     int frame,              // Frame (ignored).
865     bool force              // Forces the step to happen.
866 ) {
867 	ignore_unused_variable_warning(frame);
868 	if (!gathered)          // Happens in SI with turtle.
869 		gather();
870 	Tile_coord cur = get_tile();
871 	// Blocked? (Assume ht.=4, for now.)
872 	int move_type;
873 	if (cur.tz > 0)
874 		move_type = MOVE_LEVITATE;
875 	else if (force)
876 		move_type = MOVE_ALL;
877 	else if (boat)
878 		move_type = MOVE_SWIM;
879 	else
880 		move_type = MOVE_WALK;
881 	// No rising/dropping.
882 	if (Map_chunk::is_blocked(get_xtiles(), get_ytiles(),
883 	                          4, cur, t, move_type, 0, 0))
884 		return false;     // Done.
885 	move(t.tx, t.ty, t.tz);     // Move it & its objects.
886 	// Near an egg?
887 	Map_chunk *nlist = gmap->get_chunk(get_cx(), get_cy());
888 	nlist->activate_eggs(gwin->get_main_actor(), t.tx, t.ty, t.tz,
889 	                     cur.tx, cur.ty);
890 	return true;         // Add back to queue for next time.
891 }
892 
893 /*
894  *  Write out.
895  */
896 
write_ireg(ODataSource * out)897 void Barge_object::write_ireg(
898     ODataSource *out
899 ) {
900 	unsigned char buf[20];      // 13-byte entry + length-byte.
901 	unsigned char *ptr = write_common_ireg(12, buf);
902 	// Write size.
903 	*ptr++ = xtiles;
904 	*ptr++ = ytiles;
905 	*ptr++ = 0;         // Unknown.
906 	// Flags (quality).  Taking B3 to in-
907 	//   dicate barge mode.
908 	*ptr++ = (dir << 1) | ((gwin->get_moving_barge() == this) ? (1 << 3) : 0);
909 	*ptr++ = 0;         // (Quantity).
910 	*ptr++ = nibble_swap(get_lift());
911 	*ptr++ = 0;         // Data2.
912 	*ptr++ = 0;         //
913 	out->write(reinterpret_cast<char *>(buf), ptr - buf);
914 	// Write permanent objects.
915 	for (int i = 0; i < perm_count; i++) {
916 		Game_object *obj = get_object(i);
917 		obj->write_ireg(out);
918 	}
919 	out->write1(0x01);          // A 01 terminates the list.
920 	// Write scheduled usecode.
921 	Game_map::write_scheduled(out, this);
922 }
923 
924 // Get size of IREG. Returns -1 if can't write to buffer
get_ireg_size()925 int Barge_object::get_ireg_size() {
926 	// These shouldn't ever happen, but you never know
927 	if (gwin->get_moving_barge() == this || Usecode_script::find(this))
928 		return -1;
929 
930 	int total_size = 8 + get_common_ireg_size();
931 
932 	for (int i = 0; i < perm_count; i++) {
933 		Game_object *obj = get_object(i);
934 		int size = obj->get_ireg_size();
935 
936 		if (size < 0) return -1;
937 
938 		total_size += size;
939 	}
940 
941 	total_size += 1;
942 
943 	return total_size;
944 }
945 
946 /*
947  *  This is called after all elements have been read in and added.
948  */
949 
elements_read()950 void Barge_object::elements_read(
951 ) {
952 	perm_count = 0;         // So we don't get haystack!
953 	complete = true;
954 }
955 
956