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