1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "ultima/nuvie/core/nuvie_defs.h"
24 #include "ultima/nuvie/core/game.h"
25 #include "ultima/nuvie/actors/actor.h"
26 #include "ultima/nuvie/core/map.h"
27 #include "ultima/nuvie/core/party.h"
28 #include "ultima/nuvie/script/script.h"
29 #include "ultima/nuvie/core/anim_manager.h"
30 #include "ultima/nuvie/gui/widgets/map_window.h"
31 #include "ultima/nuvie/core/tile_manager.h"
32 #include "ultima/nuvie/core/game_clock.h"
33 #include "ultima/nuvie/core/effect_manager.h"
34 #include "ultima/nuvie/usecode/usecode.h"
35 #include "ultima/nuvie/gui/widgets/msg_scroll.h"
36 #include "ultima/nuvie/actors/actor_manager.h"
37 #include "ultima/nuvie/sound/sound_manager.h"
38 #include "ultima/nuvie/core/u6_objects.h"
39 #include "ultima/nuvie/core/effect.h"
40 #include "ultima/nuvie/core/player.h"
41
42 namespace Ultima {
43 namespace Nuvie {
44
45 #define MESG_ANIM_HIT_WORLD ANIM_CB_HIT_WORLD
46 #define MESG_ANIM_HIT ANIM_CB_HIT
47 #define MESG_ANIM_DONE ANIM_CB_DONE
48 #define MESG_EFFECT_COMPLETE EFFECT_CB_COMPLETE
49 //#define MESG_INPUT_READY EVENT_CB_INPUT_READY
50 #define MESG_INPUT_READY MSGSCROLL_CB_TEXT_READY
51
52 #define TRANSPARENT_COLOR 0xFF /* transparent pixel color */
53
54 #define EXP_EFFECT_TILE_NUM 382
55
56 QuakeEffect *QuakeEffect::current_quake = NULL;
57 FadeEffect *FadeEffect::current_fade = NULL;
58
59
60 /* Add self to effect list (for future deletion).
61 */
Effect()62 Effect::Effect() : defunct(false) {
63 retain_count = 0;
64 game = Game::get_game();
65 effect_manager = game->get_effect_manager();
66 effect_manager->add_effect(this);
67 }
68
69
~Effect()70 Effect::~Effect() {
71 // FIXME: should we remove self from Callbacks' default targets?
72 }
73
74
75 /* Start managing new animation. (AnimMgr will do that actually, but we point to
76 * it and can stop it when necessary)
77 */
add_anim(NuvieAnim * anim)78 void Effect::add_anim(NuvieAnim *anim) {
79 anim->set_target(this); // add self as callback target for anim
80 game->get_map_window()->get_anim_manager()->new_anim(anim);
81 }
82
83
84 /* Fire from a cannon in direction: 0=north, 1=east, 2=south, 3=west,
85 * -1=use cannon frame
86 */
CannonballEffect(Obj * src_obj,sint8 direction)87 CannonballEffect::CannonballEffect(Obj *src_obj, sint8 direction)
88 : target_loc() {
89 usecode = game->get_usecode();
90 obj = src_obj;
91 MapCoord obj_loc(obj->x, obj->y, obj->z);
92 target_loc = obj_loc;
93
94 if (direction == -1)
95 direction = obj->frame_n;
96
97 uint8 target_dist = 5; // distance that cannonball will fly
98 if (direction == NUVIE_DIR_N)
99 target_loc.y -= target_dist;
100 else if (direction == NUVIE_DIR_E)
101 target_loc.x += target_dist;
102 else if (direction == NUVIE_DIR_S)
103 target_loc.y += target_dist;
104 else if (direction == NUVIE_DIR_W)
105 target_loc.x -= target_dist;
106
107 start_anim();
108 }
109
110
111 /* Pause world & start animation. */
start_anim()112 void CannonballEffect::start_anim() {
113 MapCoord obj_loc(obj->x, obj->y, obj->z);
114
115 game->pause_world();
116 game->pause_anims();
117 game->pause_user();
118
119 anim = new TossAnim(game->get_tile_manager()->get_tile(399),
120 obj_loc, target_loc, CANNON_SPEED, TOSS_TO_BLOCKING | TOSS_TO_ACTOR | TOSS_TO_OBJECT);
121 add_anim(anim);
122 }
123
124
125 /* Handle messages from animation. Hit actors & walls. */
callback(uint16 msg,CallBack * caller,void * msg_data)126 uint16 CannonballEffect::callback(uint16 msg, CallBack *caller, void *msg_data) {
127 bool stop_effect = false;
128 Actor *hit_actor = NULL;
129
130 switch (msg) {
131 case MESG_ANIM_HIT_WORLD: {
132 MapCoord *hit_loc = static_cast<MapCoord *>(msg_data);
133 Tile *obj_tile = game->get_obj_manager()->get_obj_tile(hit_loc->x, hit_loc->y, hit_loc->z);
134 Tile *tile = game->get_game_map()->get_tile(hit_loc->x, hit_loc->y,
135 hit_loc->z);
136
137 if ((tile->flags2 & TILEFLAG_MISSILE_BOUNDARY)
138 || (obj_tile && (obj_tile->flags2 & TILEFLAG_MISSILE_BOUNDARY))) {
139 //new ExplosiveEffect(hit_loc->x, hit_loc->y, 2);
140 new ExpEffect(EXP_EFFECT_TILE_NUM, MapCoord(hit_loc->x, hit_loc->y, hit_loc->z));
141 stop_effect = true;
142 }
143 break;
144 }
145 case MESG_ANIM_HIT: {
146 MapEntity *hit_ent = static_cast<MapEntity *>(msg_data);
147 if (hit_ent->entity_type == ENT_ACTOR) {
148 //hit_ent->actor->hit(32);
149 hit_actor = hit_ent->actor;
150 stop_effect = true;
151 }
152 if (hit_ent->entity_type == ENT_OBJ) {
153 DEBUG(0, LEVEL_DEBUGGING, "hit object %d at %x,%x,%x\n", hit_ent->obj->obj_n, hit_ent->obj->x, hit_ent->obj->y, hit_ent->obj->z);
154 // FIX: U6 specific
155 // FIX: hit any part of ship, and reduce qty of center
156 if (hit_ent->obj->obj_n == 412) {
157 uint8 f = hit_ent->obj->frame_n;
158 if (f == 9 || f == 15 || f == 11 || f == 13) { // directions
159 if (hit_ent->obj->qty < 20) hit_ent->obj->qty = 0;
160 else hit_ent->obj->qty -= 20;
161 if (hit_ent->obj->qty == 0)
162 game->get_scroll()->display_string("Ship broke!\n");
163 stop_effect = true;
164 }
165 }
166 }
167 break;
168 }
169 case MESG_ANIM_DONE:
170 //new ExplosiveEffect(target_loc.x, target_loc.y, 3);
171 new ExpEffect(EXP_EFFECT_TILE_NUM, MapCoord(target_loc.x, target_loc.y, target_loc.z));
172 stop_effect = true;
173 break;
174 }
175
176 if (stop_effect) {
177 if (hit_actor) {
178 anim->pause(); //pause to avoid recursive problems when animations are called from actor_hit() lua script.
179 Game::get_game()->get_script()->call_actor_hit(hit_actor, 32, true);
180 }
181 if (msg != MESG_ANIM_DONE) // this msg means anim stopped itself
182 anim->stop();
183 game->unpause_all();
184 usecode->message_obj(obj, MESG_EFFECT_COMPLETE, this);
185 delete_self();
186 }
187 return (0);
188 }
189
190 #define EXP_EFFECT_SPEED 3
191
192
ExpEffect(uint16 tileNum,MapCoord location)193 ExpEffect::ExpEffect(uint16 tileNum, MapCoord location) {
194 start_loc = location;
195 finished_tiles = 0;
196 exp_tile_num = tileNum;
197 usecode = NULL;
198 obj = NULL;
199
200 start_anim();
201 }
202
203
204 /* Pause world & start animation. */
start_anim()205 void ExpEffect::start_anim() {
206 game->pause_world();
207 game->pause_anims();
208 game->pause_user();
209
210 targets.resize(16);
211
212 targets[0] = MapCoord(start_loc.x + 2, start_loc.y - 1, start_loc.z);
213 targets[1] = MapCoord(start_loc.x + 1, start_loc.y + 2, start_loc.z);
214 targets[2] = MapCoord(start_loc.x, start_loc.y - 2, start_loc.z);
215
216 targets[3] = MapCoord(start_loc.x + 1, start_loc.y - 1, start_loc.z);
217 targets[4] = MapCoord(start_loc.x - 1, start_loc.y + 2, start_loc.z);
218 targets[5] = MapCoord(start_loc.x - 1, start_loc.y - 1, start_loc.z);
219
220 targets[6] = MapCoord(start_loc.x - 2, start_loc.y, start_loc.z);
221 targets[7] = MapCoord(start_loc.x - 1, start_loc.y + 1, start_loc.z);
222 targets[8] = MapCoord(start_loc.x, start_loc.y + 2, start_loc.z);
223
224 targets[9] = MapCoord(start_loc.x - 1, start_loc.y - 2, start_loc.z);
225 targets[10] = MapCoord(start_loc.x - 2, start_loc.y - 1, start_loc.z);
226 targets[11] = MapCoord(start_loc.x - 2, start_loc.y + 1, start_loc.z);
227
228 targets[12] = MapCoord(start_loc.x + 2, start_loc.y + 1, start_loc.z);
229 targets[13] = MapCoord(start_loc.x + 2, start_loc.y, start_loc.z);
230 targets[14] = MapCoord(start_loc.x + 1, start_loc.y + 1, start_loc.z);
231 targets[15] = MapCoord(start_loc.x + 1, start_loc.y - 2, start_loc.z);
232
233
234 anim = new ProjectileAnim(exp_tile_num, &start_loc, targets, EXP_EFFECT_SPEED, true);
235 add_anim(anim);
236
237 }
238
ProjectileEffect(uint16 tileNum,MapCoord start,MapCoord target,uint8 speed,bool trailFlag,uint16 initialTileRotation,uint16 rotationAmount,uint8 src_y_offset)239 ProjectileEffect::ProjectileEffect(uint16 tileNum, MapCoord start, MapCoord target, uint8 speed, bool trailFlag, uint16 initialTileRotation, uint16 rotationAmount, uint8 src_y_offset) {
240 vector<MapCoord> t;
241 t.push_back(target);
242
243 init(tileNum, start, t, speed, trailFlag, initialTileRotation, rotationAmount, src_y_offset);
244 }
245
ProjectileEffect(uint16 tileNum,MapCoord start,vector<MapCoord> t,uint8 speed,bool trailFlag,uint16 initialTileRotation)246 ProjectileEffect::ProjectileEffect(uint16 tileNum, MapCoord start, vector<MapCoord> t, uint8 speed, bool trailFlag, uint16 initialTileRotation) {
247 init(tileNum, start, t, speed, trailFlag, initialTileRotation, 0, 0);
248 }
249
init(uint16 tileNum,MapCoord start,vector<MapCoord> t,uint8 speed,bool trailFlag,uint16 initialTileRotation,uint16 rotationAmount,uint8 src_y_offset)250 void ProjectileEffect::init(uint16 tileNum, MapCoord start, vector<MapCoord> t, uint8 speed, bool trailFlag, uint16 initialTileRotation, uint16 rotationAmount, uint8 src_y_offset) {
251 finished_tiles = 0;
252
253 tile_num = tileNum;
254 start_loc = start;
255 anim_speed = speed;
256 trail = trailFlag;
257 initial_tile_rotation = initialTileRotation;
258 rotation_amount = rotationAmount;
259
260 src_tile_y_offset = src_y_offset;
261 targets = t;
262
263 start_anim();
264 }
265
266 /* Pause world & start animation. */
start_anim()267 void ProjectileEffect::start_anim() {
268 game->pause_world();
269 //game->pause_anims();
270 game->pause_user();
271
272 add_anim(new ProjectileAnim(tile_num, &start_loc, targets, anim_speed, trail, initial_tile_rotation, rotation_amount, src_tile_y_offset));
273
274 }
275 /* Handle messages from animation. Hit actors & walls. */
callback(uint16 msg,CallBack * caller,void * msg_data)276 uint16 ProjectileEffect::callback(uint16 msg, CallBack *caller, void *msg_data) {
277 bool stop_effect = false;
278 switch (msg) {
279 case MESG_ANIM_HIT_WORLD: {
280 MapCoord *hit_loc = static_cast<MapCoord *>(msg_data);
281 Tile *tile = game->get_game_map()->get_tile(hit_loc->x, hit_loc->y,
282 hit_loc->z);
283 if (tile->flags1 & TILEFLAG_WALL) {
284 //new ExplosiveEffect(hit_loc->x, hit_loc->y, 2);
285 stop_effect = true;
286 }
287 break;
288 }
289 case MESG_ANIM_HIT: {
290 MapEntity *hit_ent = static_cast<MapEntity *>(msg_data);
291 hit_entities.push_back(*hit_ent);
292 break;
293 }
294 case MESG_ANIM_DONE:
295 //new ExplosiveEffect(target_loc.x, target_loc.y, 3);
296 stop_effect = true;
297 break;
298 }
299
300 if (stop_effect) {
301 //finished_tiles++;
302
303 if (msg != MESG_ANIM_DONE) // this msg means anim stopped itself
304 ((NuvieAnim *)caller)->stop();
305 //if(finished_tiles == 16)
306 // {
307 game->unpause_world();
308 game->unpause_user();
309 game->unpause_anims();
310 //usecode->message_obj(obj, MESG_EFFECT_COMPLETE, this);
311 delete_self();
312 // }
313 }
314 return (0);
315 }
316
317
318 /*** TimedEffect ***/
start_timer(uint32 delay)319 void TimedEffect::start_timer(uint32 delay) {
320 if (!timer)
321 timer = new TimedCallback(this, NULL, delay, true);
322 }
323
324
stop_timer()325 void TimedEffect::stop_timer() {
326 if (timer) {
327 timer->clear_target();
328 timer = NULL;
329 }
330 }
331
332
333 /*** QuakeEffect ***/
334 /* Shake the visible play area around for `duration' milliseconds. Magnitude
335 * determines the speed of movement. An actor may be selected to keep the
336 * MapWindow centered on after the Quake.
337 */
QuakeEffect(uint8 magnitude,uint32 duration,Actor * keep_on)338 QuakeEffect::QuakeEffect(uint8 magnitude, uint32 duration, Actor *keep_on) {
339 // single use only, so MapWindow doesn't keep moving away from center
340 // ...and do nothing if magnitude isn't usable
341 if (current_quake || magnitude == 0) {
342 delete_self();
343 return;
344 }
345 current_quake = this; // cleared in timer function
346
347 map_window = game->get_map_window();
348 stop_time = game->get_clock()->get_ticks() + duration;
349 strength = magnitude;
350
351 // get random direction (always move left-right more than up-down)
352 init_directions();
353
354 map_window->get_pos(&orig.x, &orig.y);
355 map_window->get_level(&orig.z);
356 orig_actor = keep_on;
357 map_window->set_freeze_blacking_location(true);
358
359 start_timer(strength * 5);
360 }
361
362
~QuakeEffect()363 QuakeEffect::~QuakeEffect() {
364 }
365
366
367 /* On TIMED: Move map.
368 */
callback(uint16 msg,CallBack * caller,void * msg_data)369 uint16 QuakeEffect::callback(uint16 msg, CallBack *caller, void *msg_data) {
370 // uint8 twice_strength = strength * 2;
371 if (msg != MESG_TIMED)
372 return (0);
373 if (game->get_clock()->get_ticks() >= stop_time) {
374 stop_quake();
375 return (0);
376 }
377 recenter_map();
378 map_window->shiftMapRelative(sx, sy);
379
380 // move in opposite direction on next call
381 if (sx == -(4 * strength) || sx == (4 * strength))
382 sx = (sx == -(4 * strength)) ? (2 * strength) : -(2 * strength);
383 else if (sx == -(2 * strength) || sx == (2 * strength))
384 sx = 0;
385
386 if (sy == -(2 * strength) || sy == (2 * strength))
387 sy = 0;
388
389 if (sx == 0 && sy == 0)
390 init_directions();
391 return (0);
392 }
393
394
395 /* Finish effect. Move map back to initial position.
396 */
stop_quake()397 void QuakeEffect::stop_quake() {
398 current_quake = NULL;
399 map_window->set_freeze_blacking_location(false);
400 recenter_map();
401 delete_self();
402 }
403
404
405 /* Set sx,sy to a random direction. (always move left-right more than up-down)
406 */
init_directions()407 void QuakeEffect::init_directions() {
408 uint8 dir = NUVIE_RAND() % 8;
409 sx = 0;
410 sy = 0;
411
412 switch (dir) {
413 case NUVIE_DIR_N :
414 sy = -(strength * 2);
415 break;
416 case NUVIE_DIR_NE :
417 sx = (strength * 4);
418 sy = -(strength * 2);
419 break;
420 case NUVIE_DIR_E :
421 sx = (strength * 4);
422 break;
423 case NUVIE_DIR_SE :
424 sx = (strength * 4);
425 sy = (strength * 2);
426 break;
427 case NUVIE_DIR_S :
428 sy = (strength * 2);
429 break;
430 case NUVIE_DIR_SW :
431 sx = -(strength * 4);
432 sy = (strength * 2);
433 break;
434 case NUVIE_DIR_W :
435 sx = -(strength * 4);
436 break;
437 case NUVIE_DIR_NW :
438 sx = -(strength * 4);
439 sy = -(strength * 2);
440 break;
441 }
442 }
443
444
445 /* Center map on original actor or move to original location.
446 */
recenter_map()447 void QuakeEffect::recenter_map() {
448 if (orig_actor)
449 map_window->centerMapOnActor(orig_actor);
450 else
451 map_window->moveMap(orig.x, orig.y, orig.z);
452 }
453
454
455 /*** HitEffect ***/
456 /* Hit target actor. FIXME: implement duration and hitting a location
457 */
HitEffect(Actor * target,uint32 duration)458 HitEffect::HitEffect(Actor *target, uint32 duration) {
459 game->pause_user();
460 add_anim(new HitAnim(target));
461 Game::get_game()->get_sound_manager()->playSfx(NUVIE_SFX_HIT); //FIXME use NUVIE_SFX_SAMPLE defines here.
462 }
463
HitEffect(MapCoord location)464 HitEffect::HitEffect(MapCoord location) {
465 game->pause_user();
466 add_anim(new HitAnim(&location));
467 Game::get_game()->get_sound_manager()->playSfx(NUVIE_SFX_HIT); //FIXME use NUVIE_SFX_SAMPLE defines here.
468 }
469
470 /* On ANIM_DONE: end
471 */
callback(uint16 msg,CallBack * caller,void * msg_data)472 uint16 HitEffect::callback(uint16 msg, CallBack *caller, void *msg_data) {
473 if (msg == MESG_ANIM_DONE) {
474 game->unpause_user();
475 delete_self();
476 }
477 return (0);
478 }
479
TextEffect(Std::string text)480 TextEffect::TextEffect(Std::string text) { // default somewhat centered on player for cheat messages
481 MapWindow *map_window = game->get_map_window();
482 if (!map_window || map_window->Status() != WIDGET_VISIBLE) // scripted sequence like intro and intro menu
483 return;
484 MapCoord loc = game->get_player()->get_actor()->get_location();
485 loc.x = (loc.x - map_window->get_cur_x() - 2) * 16;
486 loc.y = (loc.y - map_window->get_cur_y() - 1) * 16;
487
488 add_anim(new TextAnim(text, loc, 1500));
489 }
490
491 /*** TextEffect ***/
492 /* Print Text to MapWindow for duration
493 */
TextEffect(Std::string text,MapCoord location)494 TextEffect::TextEffect(Std::string text, MapCoord location) {
495 add_anim(new TextAnim(text, location, 1500));
496 }
497
498 /* On ANIM_DONE: end
499 */
callback(uint16 msg,CallBack * caller,void * msg_data)500 uint16 TextEffect::callback(uint16 msg, CallBack *caller, void *msg_data) {
501 if (msg == MESG_ANIM_DONE) {
502 delete_self();
503 }
504 return (0);
505 }
506
507 /*** ExplosiveEffect ***/
ExplosiveEffect(uint16 x,uint16 y,uint32 size,uint16 dmg)508 ExplosiveEffect::ExplosiveEffect(uint16 x, uint16 y, uint32 size, uint16 dmg)
509 : start_at() {
510 start_at.x = x;
511 start_at.y = y;
512 radius = size;
513 hit_damage = dmg;
514
515 start_anim();
516 }
517
518
519 /* Pause world & start animation.
520 */
start_anim()521 void ExplosiveEffect::start_anim() {
522 game->pause_world();
523 game->pause_user();
524 add_anim(new ExplosiveAnim(&start_at, radius));
525 }
526
527
528 /* Handle messages from animation. Hit actors & objects.
529 */
callback(uint16 msg,CallBack * caller,void * msg_data)530 uint16 ExplosiveEffect::callback(uint16 msg, CallBack *caller, void *msg_data) {
531 bool stop_effect = false;
532 switch (msg) {
533 case MESG_ANIM_HIT: {
534 MapEntity *hit_ent = static_cast<MapEntity *>(msg_data);
535 if (hit_ent->entity_type == ENT_ACTOR) {
536 if (hit_damage != 0) // hit actor if effect causes damage
537 hit_ent->actor->hit(hit_damage);
538 } else if (hit_ent->entity_type == ENT_OBJ) {
539 DEBUG(0, LEVEL_DEBUGGING, "Explosion hit object %d (%x,%x)\n", hit_ent->obj->obj_n, hit_ent->obj->x, hit_ent->obj->y);
540 stop_effect = hit_object(hit_ent->obj);
541 }
542 break;
543 }
544 case MESG_ANIM_DONE:
545 stop_effect = true;
546 break;
547 }
548
549 if (stop_effect) {
550 if (msg != MESG_ANIM_DONE)
551 anim->stop();
552 game->unpause_world();
553 game->unpause_user();
554 delete_self();
555 }
556 return (0);
557 }
558
559
560 /* UseCodeExplosiveEffect: before deleting send message to source object
561 */
delete_self()562 void UseCodeExplosiveEffect::delete_self() {
563 if (obj)
564 game->get_usecode()->message_obj(obj, MESG_EFFECT_COMPLETE, this);
565 Effect::delete_self();
566 }
567
568
569 /* The explosion hit an object.
570 * Returns true if the effect should end, false to continue.
571 */
hit_object(Obj * hit_obj)572 bool UseCodeExplosiveEffect::hit_object(Obj *hit_obj) {
573 // ignite & destroy powder kegs (U6)
574 if (hit_obj->obj_n == 223 && hit_obj != original_obj) {
575 // FIXME: this doesn't belong here (U6/obj specific)
576 uint16 x = hit_obj->x, y = hit_obj->y;
577 game->get_obj_manager()->remove_obj_from_map(hit_obj);
578 delete_obj(hit_obj);
579 if (obj) // pass our source obj on to next effect as original_obj
580 new UseCodeExplosiveEffect(NULL, x, y, 2, hit_damage, obj);
581 else // pass original_obj on to next effect
582 new UseCodeExplosiveEffect(NULL, x, y, 2, hit_damage, original_obj);
583 }
584 return (false);
585 }
586
587
588 /*** ThrowObjectEffect ***/
ThrowObjectEffect()589 ThrowObjectEffect::ThrowObjectEffect() {
590 obj_manager = game->get_obj_manager();
591
592 anim = NULL;
593 throw_obj = NULL;
594 throw_tile = 0;
595 throw_speed = 0;
596 degrees = 0;
597 stop_flags = 0;
598 }
599
600
start_anim()601 void ThrowObjectEffect::start_anim() {
602 game->pause_anims();
603 game->pause_world();
604 game->pause_user();
605
606 assert(throw_tile || throw_obj); // make sure it was properly initialized
607 assert(throw_speed != 0);
608
609 if (throw_obj)
610 anim = new TossAnim(throw_obj, degrees, start_at, stop_at, throw_speed, stop_flags);
611 else
612 anim = new TossAnim(throw_tile, start_at, stop_at, throw_speed, stop_flags);
613 add_anim(anim);
614 }
615
616
617 /* Object has stopped. */
hit_target()618 void ThrowObjectEffect::hit_target() {
619 if (anim)
620 anim->stop();
621 game->unpause_all();
622 delete_self();
623 }
624
625
626 /* The animation will travel from original object location to drop location if
627 * NULL actor is specified.
628 */
DropEffect(Obj * obj,uint16 qty,Actor * actor,MapCoord * drop_loc)629 DropEffect::DropEffect(Obj *obj, uint16 qty, Actor *actor, MapCoord *drop_loc) {
630 drop_from_actor = actor;
631 start_at = drop_from_actor ? drop_from_actor->get_location() : MapCoord(obj->x, obj->y, obj->z);
632 stop_at = *drop_loc;
633 degrees = 90;
634
635 get_obj(obj, qty); // remove from actor, set throw_obj
636
637 if (start_at != stop_at) {
638 throw_speed = 192; // animation speed
639 start_anim();
640 } else
641 hit_target(); // done already? why bother calling DropEffect? :p
642 }
643
644
645 /* Take `qty' objects of a stack if necessary, and remove from the actor's
646 * inventory. Set `throw_obj'.
647 */
get_obj(Obj * obj,uint16 qty)648 void DropEffect::get_obj(Obj *obj, uint16 qty) {
649 throw_obj = obj_manager->get_obj_from_stack(obj, qty);
650 if (drop_from_actor)
651 drop_from_actor->inventory_remove_obj(throw_obj);
652 }
653
654
655 /* On ANIM_HIT_WORLD: end at hit location
656 * On ANIM_DONE: end
657 */
callback(uint16 msg,CallBack * caller,void * msg_data)658 uint16 DropEffect::callback(uint16 msg, CallBack *caller, void *msg_data) {
659 // if throw_obj is NULL, object already hit target
660 if (!throw_obj || (msg != MESG_ANIM_DONE && msg != MESG_ANIM_HIT_WORLD))
661 return (0);
662
663 if (msg == MESG_ANIM_HIT_WORLD && stop_at == *(MapCoord *)msg_data
664 && anim)
665 anim->stop();
666
667 hit_target();
668 return (0);
669 }
670
671
672 /* Add object to map. (call before completing effect) */
hit_target()673 void DropEffect::hit_target() {
674 throw_obj->x = stop_at.x;
675 throw_obj->y = stop_at.y;
676 throw_obj->z = stop_at.z;
677
678 //FIXME drop logic should probably be in lua script.
679 if (drop_from_actor && obj_manager->is_breakable(throw_obj) && start_at.distance(stop_at) > 1) {
680 nuvie_game_t game_type = game->get_game_type();
681 if (game_type == NUVIE_GAME_U6 && throw_obj->obj_n == OBJ_U6_DRAGON_EGG) {
682 throw_obj->frame_n = 1; //brake egg.
683 obj_manager->add_obj(throw_obj, OBJ_ADD_TOP);
684 } else if (game_type == NUVIE_GAME_U6 && throw_obj->obj_n == OBJ_U6_MIRROR) {
685 throw_obj->frame_n = 2; //break mirror.
686 obj_manager->add_obj(throw_obj, OBJ_ADD_TOP);
687 } else {
688 // remove items from container if there is one
689 if (game->get_usecode()->is_container(throw_obj)) {
690 U6Link *link = throw_obj->container->start();
691 for (; link != NULL; link = throw_obj->container->start()) {
692 Obj *obj = (Obj *)link->data;
693 obj_manager->moveto_map(obj, stop_at);
694 }
695 }
696 obj_manager->unlink_from_engine(throw_obj);
697 delete_obj(throw_obj);
698 }
699
700 Game::get_game()->get_scroll()->display_string("\nIt broke!\n");
701 Game::get_game()->get_sound_manager()->playSfx(NUVIE_SFX_BROKEN_GLASS);
702 } else {
703 Obj *dest_obj = obj_manager->get_obj(stop_at.x, stop_at.y, stop_at.z);
704 if (obj_manager->can_store_obj(dest_obj, throw_obj))
705 obj_manager->moveto_container(throw_obj, dest_obj);
706 else
707 obj_manager->add_obj(throw_obj, OBJ_ADD_TOP);
708 }
709 throw_obj = NULL; // set as dropped
710
711 // not appropriate to do "Events::endAction(true)" from here to display
712 // prompt, as we MUST unpause_user() in ThrowObjectEffect::hit_target, and
713 // that would be redundant and may not unpause everything if wait mode was
714 // already cancelled... so just prompt
715 game->get_scroll()->display_string("\n");
716 game->get_scroll()->display_prompt();
717
718 game->get_map_window()->updateBlacking();
719 ThrowObjectEffect::hit_target(); // calls delete_self()
720 }
721
722
723 /*** MissileEffect ***/
MissileEffect(uint16 tile_num,uint16 obj_n,const MapCoord & source,const MapCoord & target,uint8 dmg,uint8 intercept,uint16 speed)724 MissileEffect::MissileEffect(uint16 tile_num, uint16 obj_n, const MapCoord &source,
725 const MapCoord &target, uint8 dmg,
726 uint8 intercept, uint16 speed) {
727 actor_manager = game->get_actor_manager();
728 hit_actor = 0;
729 hit_obj = 0;
730
731 init(tile_num, obj_n, source, target, dmg, intercept, speed);
732 }
733
734 /* Start effect. If target is unset then the actor is the target. */
init(uint16 tile_num,uint16 obj_n,const MapCoord & source,const MapCoord & target,uint32 dmg,uint8 intercept,uint32 speed)735 void MissileEffect::init(uint16 tile_num, uint16 obj_n,
736 const MapCoord &source, const MapCoord &target,
737 uint32 dmg, uint8 intercept, uint32 speed) {
738 assert(tile_num || obj_n); // at least obj_n must be set
739 // (although it might work if throw_obj is already set)
740 assert(speed != 0);
741 assert(intercept != 0); // must hit target
742
743 if (obj_n != 0)
744 throw_obj = new_obj(obj_n, 0, 0, 0, 0);
745 if (tile_num != 0)
746 throw_tile = game->get_tile_manager()->get_tile(tile_num);
747 else if (throw_obj != 0)
748 throw_tile = obj_manager->get_obj_tile(throw_obj->obj_n, 0);
749 throw_speed = speed;
750 hit_damage = dmg;
751
752 start_at = source;
753 stop_at = target;
754 stop_flags = intercept;
755 assert(stop_at != start_at); // Hmm, can't attack self with boomerang then
756 // if (stop_at != start_at) {
757 // start_at.x=WRAPPED_COORD(start_at.x+1,start_at.z);
758 // start_at.y=WRAPPED_COORD(start_at.y-1,start_at.z);
759 // }
760
761 // set tile rotation here based on obj_num
762 if (throw_obj != 0) {
763 if (throw_obj->obj_n == OBJ_U6_SPEAR)
764 degrees = 315;
765 if (throw_obj->obj_n == OBJ_U6_THROWING_AXE)
766 degrees = 0;
767 if (throw_obj->obj_n == OBJ_U6_DAGGER)
768 degrees = 315;
769 if (throw_obj->obj_n == OBJ_U6_ARROW)
770 degrees = 270;
771 if (throw_obj->obj_n == OBJ_U6_BOLT)
772 degrees = 270;
773 }
774 start_anim();
775 }
776
777 /* On HIT: hit Actor or Obj and end
778 * On HIT_WORLD: end at hit location, hit Actor or Obj, else place obj
779 * On DONE: end
780 */
callback(uint16 msg,CallBack * caller,void * msg_data)781 uint16 MissileEffect::callback(uint16 msg, CallBack *caller, void *msg_data) {
782 if (msg != MESG_ANIM_DONE && msg != MESG_ANIM_HIT_WORLD && msg != MESG_ANIM_HIT)
783 return 0;
784
785 if (msg == MESG_ANIM_DONE) {
786 // will always hit anything at the target
787 // FIXME: only hit breakable objects like doors
788 // hit_obj = obj_manager->get_obj(stop_at.x,stop_at.y,stop_at.z);
789 hit_actor = actor_manager->get_actor(stop_at.x, stop_at.y, stop_at.z);
790 hit_target();
791 } else if (msg == MESG_ANIM_HIT && ((MapEntity *)msg_data)->entity_type == ENT_ACTOR) {
792 if (hit_damage != 0)
793 hit_actor = ((MapEntity *)msg_data)->actor;
794 hit_target();
795 } else if (msg == MESG_ANIM_HIT && ((MapEntity *)msg_data)->entity_type == ENT_OBJ) {
796 // FIXME: only hit breakable objects like doors
797 /* if(hit_damage != 0)
798 hit_obj = ((MapEntity*)msg_data)->obj;
799 hit_target();*/
800 }
801 // MESG_ANIM_HIT_WORLD
802 hit_blocking();
803 return 0;
804 }
805
806 /* Hit target or add object to map. (call before completing effect) */
hit_target()807 void MissileEffect::hit_target() {
808 if (hit_actor) {
809 hit_actor->hit(hit_damage, ACTOR_FORCE_HIT);
810 delete_obj(throw_obj);
811 throw_obj = 0; // don't drop
812 } else if (hit_obj) {
813 if (hit_obj->qty < hit_damage)
814 hit_obj->qty = 0;
815 else hit_obj->qty -= hit_damage;
816 delete_obj(throw_obj);
817 throw_obj = 0; // don't drop
818 }
819 if (throw_obj != 0) {
820 throw_obj->x = stop_at.x;
821 throw_obj->y = stop_at.y;
822 throw_obj->z = stop_at.z;
823 throw_obj->status |= OBJ_STATUS_OK_TO_TAKE | OBJ_STATUS_TEMPORARY;
824 if (obj_manager->is_stackable(throw_obj))
825 throw_obj->qty = 1; // stackable objects must have a quantity
826 obj_manager->add_obj(throw_obj, OBJ_ADD_TOP);
827 throw_obj = 0;
828 }
829
830 ThrowObjectEffect::hit_target(); // calls delete_self()
831 }
832
hit_blocking()833 void MissileEffect::hit_blocking() {
834 delete_obj(throw_obj);
835 ThrowObjectEffect::hit_target();
836 }
837
838
839 /*** SleepEffect ***/
840 /* The TimedAdvance is started after the fade-out completes. */
SleepEffect(Std::string until)841 SleepEffect::SleepEffect(Std::string until)
842 : timer(NULL),
843 stop_hour(0),
844 stop_minute(0),
845 stop_time("") {
846 stop_time = until;
847 game->pause_user();
848 effect_manager->watch_effect(this, new FadeEffect(FADE_PIXELATED, FADE_OUT));
849 }
850
851
SleepEffect(uint8 to_hour)852 SleepEffect::SleepEffect(uint8 to_hour)
853 : timer(NULL),
854 stop_hour(to_hour),
855 stop_minute(0),
856 stop_time("") {
857 game->pause_user();
858 effect_manager->watch_effect(this, new FadeEffect(FADE_PIXELATED, FADE_OUT));
859 }
860
861
~SleepEffect()862 SleepEffect::~SleepEffect() {
863 //if(timer) // make sure it doesn't try to call us again
864 // timer->clear_target();
865 }
866
867
868 /* As with TimedEffect, make sure the timer doesn't try to use callback again.
869 */
delete_self()870 void SleepEffect::delete_self() {
871 //timer->clear_target(); // this will also stop/delete the TimedAdvance
872 //timer = NULL;
873 Effect::delete_self();
874 }
875
876
877 /* Resume normal play when requested time has been reached.
878 */
879 //FIXME: need to handle TimedAdvance() errors and fade-in
callback(uint16 msg,CallBack * caller,void * data)880 uint16 SleepEffect::callback(uint16 msg, CallBack *caller, void *data) {
881 uint8 hour = Game::get_game()->get_clock()->get_hour();
882 uint8 minute = Game::get_game()->get_clock()->get_minute();
883
884 // waited for FadeEffect
885 if (msg == MESG_EFFECT_COMPLETE) {
886 if (timer == NULL) { // starting
887 if (stop_time != "") { // advance to start time
888 timer = new TimedAdvance(stop_time, 360); // 6 hours per second FIXME: it isnt going anywhere near that fast
889 timer->set_target(this);
890 timer->get_time_from_string(stop_hour, stop_minute, stop_time);
891 // stop_hour & stop_minute are checked each hour
892 } else { // advance a number of hours
893 uint16 advance_h = (hour == stop_hour) ? 24
894 : (hour < stop_hour) ? (stop_hour - hour)
895 : (24 - (hour - stop_hour));
896 timer = new TimedAdvance(advance_h, 360);
897 timer->set_target(this);
898 stop_minute = minute;
899 }
900 } else { // stopping
901 Party *party = game->get_party();
902 for (int s = 0; s < party->get_party_size(); s++) {
903 Actor *actor = party->get_actor(s);
904
905 //heal actors.
906 uint8 hp_diff = actor->get_maxhp() - actor->get_hp();
907 if (hp_diff > 0) {
908 if (hp_diff == 1)
909 hp_diff = 2;
910 actor->set_hp(actor->get_hp() + NUVIE_RAND() % (hp_diff / 2) + hp_diff / 2);
911 }
912 }
913 game->unpause_user();
914 delete_self();
915 }
916 return (0);
917 }
918 // assume msg == MESG_TIMED; will stop after effect completes
919 if (hour == stop_hour && minute >= stop_minute)
920 effect_manager->watch_effect(this, new FadeEffect(FADE_PIXELATED, FADE_IN));
921
922 return (0);
923 }
924
925
926 /*** FadeEffect ***/
FadeEffect(FadeType fade,FadeDirection dir,uint32 color,uint32 speed)927 FadeEffect::FadeEffect(FadeType fade, FadeDirection dir, uint32 color, uint32 speed) {
928 speed = speed ? speed : game->get_map_window()->get_win_area() * 2116; // was 256000
929 init(fade, dir, color, NULL, 0, 0, speed);
930 }
931
932 /* Takes an image to fade from/to. */
FadeEffect(FadeType fade,FadeDirection dir,Graphics::ManagedSurface * capture,uint32 speed)933 FadeEffect::FadeEffect(FadeType fade, FadeDirection dir, Graphics::ManagedSurface *capture, uint32 speed) {
934 speed = speed ? speed : game->get_map_window()->get_win_area() * 1620; // was 196000
935 init(fade, dir, 0, capture, 0, 0, speed); // color=black
936 }
937
938 /* Localizes effect to specific coordinates. The size of the effect is determined
939 * by the size of the image. */
FadeEffect(FadeType fade,FadeDirection dir,Graphics::ManagedSurface * capture,uint16 x,uint16 y,uint32 speed)940 FadeEffect::FadeEffect(FadeType fade, FadeDirection dir, Graphics::ManagedSurface *capture, uint16 x, uint16 y, uint32 speed) {
941 speed = speed ? speed : 1024;
942 init(fade, dir, 0, capture, x, y, speed); // color=black
943 }
944
945
init(FadeType fade,FadeDirection dir,uint32 color,Graphics::ManagedSurface * capture,uint16 x,uint16 y,uint32 speed)946 void FadeEffect::init(FadeType fade, FadeDirection dir, uint32 color, Graphics::ManagedSurface *capture, uint16 x, uint16 y, uint32 speed) {
947 if (current_fade) {
948 delete_self();
949 return;
950 }
951 current_fade = this; // cleared in dtor
952
953 screen = game->get_screen();
954 map_window = game->get_map_window();
955 viewport = new Common::Rect(map_window->GetRect());
956
957 fade_type = fade;
958 fade_dir = dir;
959 fade_speed = speed; // pixels-per-second (to check, not draw)
960
961 evtime = prev_evtime = 0;
962 fade_x = x;
963 fade_y = y;
964 fade_from = NULL;
965 fade_iterations = 0;
966 if (capture) {
967 fade_from = new Graphics::ManagedSurface(capture->w, capture->h, capture->format);
968 fade_from->blitFrom(*capture);
969 }
970
971 if (fade_type == FADE_PIXELATED || fade_type == FADE_PIXELATED_ONTOP) {
972 pixelated_color = color;
973 init_pixelated_fade();
974 } else
975 init_circle_fade();
976 }
977
978
~FadeEffect()979 FadeEffect::~FadeEffect() {
980 //moved to delete_self
981 }
982
delete_self()983 void FadeEffect::delete_self() {
984 if (current_fade == this) { // these weren't init. if FadeEffect didn't start
985 delete viewport;
986 if (fade_dir == FADE_IN) // overlay should be empty now, so just delete it
987 map_window->set_overlay(NULL);
988 if (fade_from)
989 SDL_FreeSurface(fade_from);
990
991 current_fade = NULL;
992 }
993
994 TimedEffect::delete_self();
995 }
996
997 /* Start effect. */
init_pixelated_fade()998 void FadeEffect::init_pixelated_fade() {
999 int fillret = -1; // check error
1000 overlay = map_window->get_overlay();
1001 if (overlay != NULL) {
1002 pixel_count = fade_from ? (fade_from->w) * (fade_from->h)
1003 : (overlay->w - fade_x) * (overlay->h - fade_y);
1004 // clear overlay to fill color or transparent
1005 if (fade_dir == FADE_OUT) {
1006 if (fade_from) { // fade from captured surface to transparent
1007 // put surface on transparent background (not checked)
1008 fillret = SDL_FillRect(overlay, NULL, uint32(TRANSPARENT_COLOR));
1009 Common::Rect overlay_rect(fade_x, fade_y, fade_x, fade_y);
1010 fillret = SDL_BlitSurface(fade_from, NULL, overlay, &overlay_rect);
1011 } else // fade from transparent to color
1012 fillret = SDL_FillRect(overlay, NULL, uint32(TRANSPARENT_COLOR));
1013 } else {
1014 if (fade_from) // fade from transparent to captured surface
1015 fillret = SDL_FillRect(overlay, NULL, uint32(TRANSPARENT_COLOR));
1016 else // fade from color to transparent
1017 fillret = SDL_FillRect(overlay, NULL, uint32(pixelated_color));
1018 }
1019 }
1020 if (fillret == -1) {
1021 DEBUG(0, LEVEL_DEBUGGING, "FadeEffect: error creating overlay surface\n");
1022 delete_self();
1023 return;
1024 }
1025
1026 // if FADE_PIXELATED_ONTOP is set, place the effect layer above the map border
1027 map_window->set_overlay_level((fade_type == FADE_PIXELATED)
1028 ? MAP_OVERLAY_DEFAULT : MAP_OVERLAY_ONTOP);
1029 colored_total = 0;
1030 start_timer(1); // fire timer continuously
1031 }
1032
1033
1034 /* Start effect.
1035 */
init_circle_fade()1036 void FadeEffect::init_circle_fade() {
1037 delete_self(); // FIXME
1038 }
1039
1040
1041 /* Called by the timer as frequently as possible. Do the appropriate
1042 * fade method and stop when the effect is complete.
1043 */
callback(uint16 msg,CallBack * caller,void * data)1044 uint16 FadeEffect::callback(uint16 msg, CallBack *caller, void *data) {
1045 bool fade_complete = false;
1046
1047 // warning: msg is assumed to be CB_TIMED and data is set
1048 evtime = *(uint32 *)(data);
1049
1050 // do effect
1051 if (fade_type == FADE_PIXELATED || fade_type == FADE_PIXELATED_ONTOP)
1052 fade_complete = (fade_dir == FADE_OUT) ? pixelated_fade_out() : pixelated_fade_in();
1053 else /* CIRCLE */
1054 fade_complete = (fade_dir == FADE_OUT) ? circle_fade_out() : circle_fade_in();
1055
1056 // done
1057 if (fade_complete == true) {
1058 delete_self();
1059 return (1);
1060 }
1061 return (0);
1062 }
1063
1064
1065 /* Scan the overlay, starting at pixel rnum, for a transparent pixel if fading
1066 * out, and a colored pixel if fading in.
1067 * Returns true if a free pixel was found and set as rnum.
1068 */
1069 // FIXME: this probably doesn't work because it only handles 8bpp
find_free_pixel(uint32 & rnum,uint32 pixelCount)1070 inline bool FadeEffect::find_free_pixel(uint32 &rnum, uint32 pixelCount) {
1071 uint8 scan_color = (fade_dir == FADE_OUT) ? TRANSPARENT_COLOR
1072 : pixelated_color;
1073 const uint8 *pixels = (const uint8 *)(overlay->getPixels());
1074
1075 for (uint32 p = rnum; p < pixelCount; p++) // check all pixels after rnum
1076 if (pixels[p] == scan_color) {
1077 rnum = p;
1078 return (true);
1079 }
1080 for (uint32 q = 0; q < rnum; q++) // check all pixels before rnum
1081 if (pixels[q] == scan_color) {
1082 rnum = q;
1083 return (true);
1084 }
1085 return (false);
1086 }
1087
1088
1089 /* Returns the next pixel to check/colorize. */
1090 #if 0
1091 #warning this crashes if x,y is near boundary
1092 #warning make sure center_thresh doesnt go over boundary
1093 inline uint32 FadeEffect::get_random_pixel(uint16 center_thresh) {
1094 if (center_x == -1 || center_y == -1)
1095 return (NUVIE_RAND() % pixel_count);
1096
1097 uint16 x = center_x, y = center_y;
1098 if (center_thresh == 0)
1099 center_thresh = overlay->w / 2;
1100 x += (NUVIE_RAND() % (center_thresh * 2)) - center_thresh,
1101 y += (NUVIE_RAND() % (center_thresh * 2)) - center_thresh;
1102 return ((y * overlay->w) + x);
1103 }
1104 #endif
1105
1106 /* Randomly add pixels of the appropriate color to the overlay. If the color
1107 * is -1, it will be taken from the "fade_from" surface.
1108 * Returns true when the overlay is completely colored.
1109 */
pixelated_fade_core(uint32 pixels_to_check,sint16 fade_to)1110 bool FadeEffect::pixelated_fade_core(uint32 pixels_to_check, sint16 fade_to) {
1111 Graphics::Surface s = overlay->getSubArea(Common::Rect(0, 0, overlay->w, overlay->h));
1112 uint8 *pixels = (uint8 *)s.getPixels();
1113 const uint8 *from_pixels = fade_from ? (const uint8 *)(fade_from->getPixels()) : NULL;
1114 uint32 p = 0; // scan counter
1115 uint32 rnum = 0; // pixel index
1116 uint32 colored = 0; // number of pixels that get colored
1117 uint16 fade_width = fade_from ? fade_from->w : overlay->w - fade_x;
1118 uint16 fade_height = fade_from ? fade_from->h : overlay->h - fade_y;
1119 uint8 color = fade_to;
1120
1121 if (fade_to == -1 && fade_from == NULL) {
1122 return false;
1123 }
1124
1125 while (p < pixels_to_check) {
1126 uint16 x = uint16(float(NUVIE_RAND()) * (fade_width - 1) / NUVIE_RAND_MAX) + fade_x,
1127 y = uint16(float(NUVIE_RAND()) * (fade_height - 1) / NUVIE_RAND_MAX) + fade_y;
1128 if (x >= overlay->w) x = overlay->w - 1; // prevent overflow if fade_from is too big
1129 if (y >= overlay->h) y = overlay->h - 1;
1130 rnum = y * overlay->w + x;
1131 //ERIC rnum = y*overlay->pitch + x;
1132
1133 if (fade_to == -1) { // get color from "fade_from"
1134 x -= fade_x;
1135 y -= fade_y;
1136 color = from_pixels[y * fade_from->w + x];
1137 }
1138 if (pixels[rnum] != color) {
1139 pixels[rnum] = color;
1140 ++colored;
1141 ++colored_total; // another pixel was set
1142 }
1143 ++p;
1144 }
1145 // all but two lines colored
1146 if (colored_total >= (pixel_count - fade_width * 2) || fade_iterations > FADE_EFFECT_MAX_ITERATIONS) { // fill the rest
1147 if (fade_to >= 0)
1148 SDL_FillRect(overlay, NULL, (uint32)fade_to);
1149 else { // Note: assert(fade_from) if(fade_to < 0)
1150 Common::Rect fade_from_rect(fade_from->w, (int16)fade_from->h);
1151 Common::Rect overlay_rect(fade_x, fade_y, fade_x + fade_from->w, fade_y + fade_from->h);
1152 SDL_BlitSurface(fade_from, &fade_from_rect, overlay, &overlay_rect);
1153 }
1154 return (true);
1155 } else return (false);
1156 }
1157
1158
1159 /* Color some of the mapwindow.
1160 * Returns true when all pixels have been filled, and nothing is visible.
1161 */
pixelated_fade_out()1162 bool FadeEffect::pixelated_fade_out() {
1163 if (fade_from)
1164 return (pixelated_fade_core(pixels_to_check(), TRANSPARENT_COLOR));
1165 return (pixelated_fade_core(pixels_to_check(), pixelated_color));
1166 }
1167
1168
1169 /* Clear some of the mapwindow.
1170 * Returns true when all colored pixels have been removed, and the MapWindow
1171 * is visible.
1172 */
pixelated_fade_in()1173 bool FadeEffect::pixelated_fade_in() {
1174 if (fade_from)
1175 return (pixelated_fade_core(pixels_to_check(), -1));
1176 return (pixelated_fade_core(pixels_to_check(), TRANSPARENT_COLOR));
1177 }
1178
1179
1180 /* Returns the number of pixels that should be checked/colored (based on speed)
1181 * since the previous call.
1182 */
pixels_to_check()1183 uint32 FadeEffect::pixels_to_check() {
1184 uint32 time_passed = (prev_evtime == 0) ? 0 : evtime - prev_evtime;
1185 uint32 fraction = 1000 / (time_passed > 0 ? time_passed : 1); // % of second passed, in milliseconds
1186 uint32 pixels_per_fraction = fade_speed / (fraction > 0 ? fraction : 1);
1187 prev_evtime = evtime;
1188 fade_iterations++;
1189 return (pixels_per_fraction);
1190 }
1191
1192
1193 /* Reduce the MapWindow's ambient light level, according to the set speed.
1194 * Returns true when nothing is visible.
1195 */
circle_fade_out()1196 bool FadeEffect::circle_fade_out() {
1197 // FIXME
1198 return (false);
1199 }
1200
1201
1202 /* Add to the MapWindow's ambient light level, according to the set speed.
1203 * Returns true when the light level has returned to normal.
1204 */
circle_fade_in()1205 bool FadeEffect::circle_fade_in() {
1206 // FIXME
1207 return (false);
1208 }
1209
1210
1211 /* Pause game and do FadeEffect.
1212 */
GameFadeInEffect(uint32 color)1213 GameFadeInEffect::GameFadeInEffect(uint32 color)
1214 : FadeEffect(FADE_PIXELATED_ONTOP, FADE_IN, color) {
1215 game->pause_user();
1216 }
1217
1218
~GameFadeInEffect()1219 GameFadeInEffect::~GameFadeInEffect() {
1220 }
1221
1222
1223 /* Identical to FadeEffect, but unpause game when finished.
1224 */
callback(uint16 msg,CallBack * caller,void * data)1225 uint16 GameFadeInEffect::callback(uint16 msg, CallBack *caller, void *data) {
1226 // done
1227 if (FadeEffect::callback(msg, caller, data) != 0)
1228 game->unpause_user();
1229 return (0);
1230 }
1231
1232
FadeObjectEffect(Obj * obj,FadeDirection dir)1233 FadeObjectEffect::FadeObjectEffect(Obj *obj, FadeDirection dir) {
1234 obj_manager = game->get_obj_manager();
1235 fade_obj = obj;
1236 fade_dir = dir;
1237
1238 Graphics::ManagedSurface *capture = game->get_map_window()->get_sdl_surface();
1239 if (fade_dir == FADE_IN) { // fading IN to object, so fade OUT from capture
1240 effect_manager->watch_effect(this, /* call me */
1241 new FadeEffect(FADE_PIXELATED, FADE_OUT, capture));
1242 obj_manager->add_obj(fade_obj, OBJ_ADD_TOP);
1243 game->get_map_window()->updateBlacking(); // object is likely a moongate
1244 } else if (fade_dir == FADE_OUT) {
1245 effect_manager->watch_effect(this, /* call me */
1246 new FadeEffect(FADE_PIXELATED, FADE_OUT, capture, 0, 0, game->get_map_window()->get_win_area() * 1058)); //was 128000
1247 // obj_manager->remove_obj(fade_obj);
1248 game->get_map_window()->updateBlacking();
1249 }
1250 SDL_FreeSurface(capture);
1251
1252 game->pause_user();
1253 }
1254
~FadeObjectEffect()1255 FadeObjectEffect::~FadeObjectEffect() {
1256 game->unpause_user();
1257 }
1258
1259 /* Assume FadeEffect is complete. */
callback(uint16 msg,CallBack * caller,void * data)1260 uint16 FadeObjectEffect::callback(uint16 msg, CallBack *caller, void *data) {
1261 delete_self();
1262 return (0);
1263 }
1264
1265
1266 /* These types of local/vanish effects are slightly longer than a normal Fade.
1267 * FIXME: FadeEffect should take local effect area, or change speed to time.
1268 */
VanishEffect(bool pause_user)1269 VanishEffect::VanishEffect(bool pause_user)
1270 : input_blocked(pause_user) {
1271 Graphics::ManagedSurface *capture = game->get_map_window()->get_sdl_surface();
1272 // effect_manager->watch_effect(this, /* call me */
1273 // new FadeEffect(FADE_PIXELATED, FADE_OUT, capture, 0, 0, 128000));
1274 effect_manager->watch_effect(this, /* call me */
1275 new FadeEffect(FADE_PIXELATED, FADE_OUT, capture));
1276 SDL_FreeSurface(capture);
1277
1278 if (input_blocked == VANISH_WAIT)
1279 game->pause_user();
1280 game->pause_anims();
1281 }
1282
~VanishEffect()1283 VanishEffect::~VanishEffect() {
1284 game->unpause_anims();
1285 if (input_blocked == VANISH_WAIT)
1286 game->unpause_user();
1287 }
1288
1289 /* Assume FadeEffect is complete. */
callback(uint16 msg,CallBack * caller,void * data)1290 uint16 VanishEffect::callback(uint16 msg, CallBack *caller, void *data) {
1291 delete_self();
1292 return (0);
1293 }
1294
1295
1296 /* TileFadeEffect */
TileFadeEffect(MapCoord loc,Tile * from,Tile * to,FadeType type,uint16 speed)1297 TileFadeEffect::TileFadeEffect(MapCoord loc, Tile *from, Tile *to, FadeType type, uint16 speed) {
1298 anim = NULL;
1299 to_tile = NULL;
1300 anim_tile = NULL;
1301 actor = NULL;
1302 color_from = color_to = 0;
1303 inc_reverse = false;
1304 spd = 0;
1305 add_anim(new TileFadeAnim(&loc, from, to, speed));
1306 num_anim_running = 1;
1307 }
1308
1309 //Fade out actor.
TileFadeEffect(Actor * a,uint16 speed)1310 TileFadeEffect::TileFadeEffect(Actor *a, uint16 speed) {
1311 inc_reverse = false;
1312 anim = NULL;
1313 to_tile = NULL;
1314 anim_tile = NULL;
1315 actor = a;
1316 color_from = color_to = 0;
1317 spd = speed;
1318 num_anim_running = 0;
1319 add_actor_anim();
1320 actor->hide();
1321 }
1322
~TileFadeEffect()1323 TileFadeEffect::~TileFadeEffect() {
1324
1325 }
1326
add_actor_anim()1327 void TileFadeEffect::add_actor_anim() {
1328 MapCoord loc = actor->get_location();
1329 Tile *from = actor->get_tile();
1330 add_tile_anim(loc, from);
1331
1332 Std::list<Obj *> *surrounding_objs = actor->get_surrounding_obj_list();
1333
1334 if (surrounding_objs) {
1335 Std::list<Obj *>::iterator obj_iter;
1336 for (obj_iter = surrounding_objs->begin(); obj_iter != surrounding_objs->end(); obj_iter++) {
1337 add_obj_anim(*obj_iter);
1338 }
1339 }
1340 }
1341
add_obj_anim(Obj * obj)1342 void TileFadeEffect::add_obj_anim(Obj *obj) {
1343 ObjManager *obj_manager = Game::get_game()->get_obj_manager();
1344 MapCoord loc(obj->x, obj->y, obj->z);
1345 add_tile_anim(loc, obj_manager->get_obj_tile(obj->obj_n, obj->frame_n));
1346 }
1347
add_fade_anim(MapCoord loc,Tile * tile)1348 void TileFadeEffect::add_fade_anim(MapCoord loc, Tile *tile) {
1349 add_anim(new TileFadeAnim(&loc, tile, NULL, spd));
1350 num_anim_running++;
1351 }
1352
add_tile_anim(MapCoord loc,Tile * tile)1353 void TileFadeEffect::add_tile_anim(MapCoord loc, Tile *tile) {
1354 TileManager *tile_manager = Game::get_game()->get_tile_manager();
1355 uint16 tile_num = tile->tile_num;
1356
1357 add_fade_anim(loc, tile);
1358
1359 if (tile->dbl_width) {
1360 tile_num--;
1361 loc.x -= 1;
1362 add_fade_anim(loc, tile_manager->get_tile(tile_num));
1363 loc.x += 1;
1364 }
1365
1366 if (tile->dbl_height) {
1367 tile_num--;
1368 loc.y -= 1;
1369 add_fade_anim(loc, tile_manager->get_tile(tile_num));
1370 loc.y += 1;
1371 }
1372
1373 if (tile->dbl_width && tile->dbl_height) {
1374 tile_num--;
1375 loc.x -= 1;
1376 loc.y -= 1;
1377 add_fade_anim(loc, tile_manager->get_tile(tile_num));
1378 loc.x += 1;
1379 loc.y += 1;
1380 }
1381 }
1382
callback(uint16 msg,CallBack * caller,void * data)1383 uint16 TileFadeEffect::callback(uint16 msg, CallBack *caller, void *data) {
1384 if (msg == MESG_ANIM_DONE) {
1385 num_anim_running--;
1386 }
1387
1388 if (num_anim_running == 0) {
1389 if (inc_reverse) {
1390 inc_reverse = false;
1391 add_actor_anim();
1392 return 0;
1393 }
1394
1395 if (actor)
1396 actor->show();
1397
1398 delete_self();
1399 }
1400
1401 return 0;
1402 }
1403
1404
TileBlackFadeEffect(Actor * a,uint8 fade_color,uint16 speed)1405 TileBlackFadeEffect::TileBlackFadeEffect(Actor *a, uint8 fade_color, uint16 speed) {
1406 init(fade_color, speed);
1407 actor = a;
1408 actor->hide();
1409 add_actor_anim();
1410 }
1411
TileBlackFadeEffect(Obj * o,uint8 fade_color,uint16 speed)1412 TileBlackFadeEffect::TileBlackFadeEffect(Obj *o, uint8 fade_color, uint16 speed) {
1413 init(fade_color, speed);
1414 obj = o;
1415 obj->set_invisible(true);
1416 add_obj_anim(obj);
1417 }
1418
init(uint8 fade_color,uint16 speed)1419 void TileBlackFadeEffect::init(uint8 fade_color, uint16 speed) {
1420 fade_speed = speed;
1421 color = fade_color;
1422 actor = NULL;
1423 obj = NULL;
1424 reverse = false;
1425
1426 num_anim_running = 0;
1427 }
~TileBlackFadeEffect()1428 TileBlackFadeEffect::~TileBlackFadeEffect() {
1429
1430 }
1431
add_actor_anim()1432 void TileBlackFadeEffect::add_actor_anim() {
1433 MapCoord loc = actor->get_location();
1434 Tile *from = actor->get_tile();
1435 add_tile_anim(loc, from);
1436
1437 Std::list<Obj *> *surrounding_objs = actor->get_surrounding_obj_list();
1438
1439 if (surrounding_objs) {
1440 Std::list<Obj *>::iterator obj_iter;
1441 for (obj_iter = surrounding_objs->begin(); obj_iter != surrounding_objs->end(); obj_iter++) {
1442 add_obj_anim(*obj_iter);
1443 }
1444 }
1445 }
1446
add_obj_anim(Obj * o)1447 void TileBlackFadeEffect::add_obj_anim(Obj *o) {
1448 MapCoord loc(o);
1449 Tile *from = Game::get_game()->get_obj_manager()->get_obj_tile(o->obj_n, o->frame_n);
1450 add_tile_anim(loc, from);
1451 }
1452
add_tile_anim(MapCoord loc,Tile * tile)1453 void TileBlackFadeEffect::add_tile_anim(MapCoord loc, Tile *tile) {
1454 TileManager *tile_manager = Game::get_game()->get_tile_manager();
1455 uint16 tile_num = tile->tile_num;
1456
1457 add_anim(new TileFadeAnim(&loc, tile, 0, color, reverse, fade_speed));
1458 num_anim_running++;
1459
1460 if (tile->dbl_width) {
1461 tile_num--;
1462 loc.x -= 1;
1463 add_anim(new TileFadeAnim(&loc, tile_manager->get_tile(tile_num), 0, color, reverse, fade_speed));
1464 num_anim_running++;
1465 loc.x += 1;
1466 }
1467
1468 if (tile->dbl_height) {
1469 tile_num--;
1470 loc.y -= 1;
1471 add_anim(new TileFadeAnim(&loc, tile_manager->get_tile(tile_num), 0, color, reverse, fade_speed));
1472 num_anim_running++;
1473 loc.y += 1;
1474 }
1475
1476 if (tile->dbl_width && tile->dbl_height) {
1477 tile_num--;
1478 loc.x -= 1;
1479 loc.y -= 1;
1480 add_anim(new TileFadeAnim(&loc, tile_manager->get_tile(tile_num), 0, color, reverse, fade_speed));
1481 num_anim_running++;
1482 loc.x += 1;
1483 loc.y += 1;
1484 }
1485 }
1486
callback(uint16 msg,CallBack * caller,void * data)1487 uint16 TileBlackFadeEffect::callback(uint16 msg, CallBack *caller, void *data) {
1488 if (msg == MESG_ANIM_DONE) {
1489 num_anim_running--;
1490 }
1491
1492 if (num_anim_running == 0) {
1493 if (reverse == false) {
1494 reverse = true;
1495 if (actor)
1496 add_actor_anim();
1497 else
1498 add_obj_anim(obj);
1499
1500 return 0;
1501 }
1502
1503 if (actor)
1504 actor->show();
1505 else
1506 obj->set_invisible(false);
1507
1508 delete_self();
1509 }
1510
1511 return 0;
1512 }
1513
XorEffect(uint32 eff_ms)1514 XorEffect::XorEffect(uint32 eff_ms)
1515 : map_window(game->get_map_window()),
1516 length(eff_ms) {
1517 game->pause_user();
1518 game->pause_anims();
1519
1520 init_effect();
1521 }
1522
init_effect()1523 void XorEffect::init_effect() {
1524 capture = map_window->get_sdl_surface();
1525 map_window->set_overlay_level(MAP_OVERLAY_DEFAULT);
1526 map_window->set_overlay(capture);
1527
1528 xor_capture(0xd); // changes black to pink
1529 start_timer(length);
1530 }
1531
1532 /* Timer finished. Cleanup. */
callback(uint16 msg,CallBack * caller,void * data)1533 uint16 XorEffect::callback(uint16 msg, CallBack *caller, void *data) {
1534 if (msg == MESG_TIMED) {
1535 stop_timer();
1536 game->unpause_anims();
1537 game->unpause_user();
1538 map_window->set_overlay(NULL);
1539 delete_self();
1540 }
1541 return 0;
1542 }
1543
1544 /* Do binary-xor on each pixel of the mapwindow image.*/
xor_capture(uint8 mod)1545 void XorEffect::xor_capture(uint8 mod) {
1546 Graphics::Surface s = capture->getSubArea(Common::Rect(0, 0, capture->w, capture->h));
1547
1548 uint8 *pixels = (uint8 *)s.getPixels();
1549 for (int p = 0; p < (capture->w * capture->h); p++)
1550 pixels[p] ^= mod;
1551 }
1552
1553
U6WhitePotionEffect(uint32 eff_ms,uint32 delay_ms,Obj * callback_obj)1554 U6WhitePotionEffect::U6WhitePotionEffect(uint32 eff_ms, uint32 delay_ms, Obj *callback_obj)
1555 : map_window(game->get_map_window()),
1556 state(0), start_length(1000),
1557 eff1_length(eff_ms), eff2_length(800),
1558 xray_length(delay_ms), capture(0),
1559 potion(callback_obj) {
1560 game->pause_user();
1561 game->pause_anims();
1562
1563 init_effect();
1564 }
1565
init_effect()1566 void U6WhitePotionEffect::init_effect() {
1567 // FIXME: play sound, and change state to 1 when sound is complete
1568 capture = map_window->get_sdl_surface();
1569 map_window->set_overlay_level(MAP_OVERLAY_DEFAULT);
1570 map_window->set_overlay(capture);
1571 start_timer(start_length);
1572 }
1573
1574 /* The state is changed from current to next when this is called. */
callback(uint16 msg,CallBack * caller,void * data)1575 uint16 U6WhitePotionEffect::callback(uint16 msg, CallBack *caller, void *data) {
1576 if (msg == MESG_TIMED) {
1577 stop_timer();
1578
1579 if (state == 0) { // start/sound effect
1580 // FIXME: make start_length a timeout, force sound to stop
1581 xor_capture(0xd); // changes black to pink
1582 start_timer(eff1_length);
1583 state = 1;
1584 } else if (state == 1) { // xor-effect
1585 map_window->set_overlay(NULL);
1586 start_timer(eff2_length);
1587 state = 2;
1588 } else if (state == 2) { // character outline
1589 game->unpause_anims();
1590 map_window->set_x_ray_view(X_RAY_ON);
1591 map_window->updateBlacking();
1592 start_timer(xray_length);
1593 state = 3;
1594 } else if (state == 3) { // x-ray
1595 map_window->set_x_ray_view(X_RAY_OFF);
1596 map_window->updateBlacking();
1597 game->unpause_user();
1598 if (potion)
1599 game->get_usecode()->message_obj(potion, MESG_EFFECT_COMPLETE, this);
1600 state = 4; // finished
1601 delete_self();
1602 }
1603 }
1604 return 0;
1605 }
1606
1607 /* Do binary-xor on each pixel of the mapwindow image.*/
xor_capture(uint8 mod)1608 void U6WhitePotionEffect::xor_capture(uint8 mod) {
1609 Graphics::Surface s = capture->getSubArea(Common::Rect(0, 0, capture->w, capture->h));
1610 uint8 *pixels = (uint8 *)s.getPixels();
1611 for (int p = 0; p < (capture->w * capture->h); p++)
1612 pixels[p] ^= mod;
1613 }
1614
1615
XRayEffect(uint32 eff_ms)1616 XRayEffect::XRayEffect(uint32 eff_ms) {
1617 xray_length = eff_ms;
1618 init_effect();
1619 }
1620
init_effect()1621 void XRayEffect::init_effect() {
1622 Game::get_game()->get_map_window()->set_x_ray_view(X_RAY_ON);
1623 start_timer(xray_length);
1624 }
1625
callback(uint16 msg,CallBack * caller,void * data)1626 uint16 XRayEffect::callback(uint16 msg, CallBack *caller, void *data) {
1627 if (msg == MESG_TIMED) {
1628 stop_timer();
1629 Game::get_game()->get_map_window()->set_x_ray_view(X_RAY_OFF);
1630 delete_self();
1631 }
1632
1633 return 0;
1634 }
1635
PauseEffect()1636 PauseEffect::PauseEffect() {
1637 game->pause_world();
1638 // FIXME: need a way to detect any keyboard/mouse input
1639 game->get_scroll()->set_input_mode(true, "\n", true);
1640 game->get_scroll()->request_input(this, 0);
1641 }
1642
1643 /* The effect ends when this is called. (if input is correct) */
callback(uint16 msg,CallBack * caller,void * data)1644 uint16 PauseEffect::callback(uint16 msg, CallBack *caller, void *data) {
1645 if (msg == MESG_INPUT_READY) {
1646 game->unpause_world();
1647 delete_self();
1648 }
1649 return 0;
1650 }
1651
WizardEyeEffect(MapCoord location,uint16 duration)1652 WizardEyeEffect::WizardEyeEffect(MapCoord location, uint16 duration) {
1653 game->get_map_window()->wizard_eye_start(location, duration, this);
1654 }
1655
callback(uint16 msg,CallBack * caller,void * data)1656 uint16 WizardEyeEffect::callback(uint16 msg, CallBack *caller, void *data) {
1657 if (msg == MESG_EFFECT_COMPLETE) {
1658 delete_self();
1659 }
1660
1661 return 0;
1662 }
1663
TextInputEffect(const char * allowed_chars,bool can_escape)1664 TextInputEffect::TextInputEffect(const char *allowed_chars, bool can_escape) {
1665 game->pause_world();
1666 // FIXME: need a way to detect any keyboard/mouse input
1667 game->get_gui()->unblock();
1668 game->get_scroll()->set_input_mode(true, allowed_chars, can_escape);
1669 game->get_scroll()->request_input(this, 0);
1670 }
1671
1672 /* The effect ends when this is called. (if input is correct) */
callback(uint16 msg,CallBack * caller,void * data)1673 uint16 TextInputEffect::callback(uint16 msg, CallBack *caller, void *data) {
1674 if (msg == MESG_INPUT_READY) {
1675 input = *(Std::string *)data;
1676 game->unpause_world();
1677 delete_self();
1678 }
1679 return 0;
1680 }
1681
1682
PeerEffect(uint16 x,uint16 y,uint8 z,Obj * callback_obj)1683 PeerEffect::PeerEffect(uint16 x, uint16 y, uint8 z, Obj *callback_obj)
1684 : map_window(game->get_map_window()), overlay(0),
1685 gem(callback_obj), area(x, y, z), tile_trans(0),
1686 map_pitch(0) {
1687 uint8 lvl = 0;
1688 map_window->get_level(&lvl);
1689 map_pitch = (lvl == 0) ? 1024 : 256;
1690
1691 init_effect();
1692 }
1693
init_effect()1694 void PeerEffect::init_effect() {
1695 overlay = map_window->get_sdl_surface();
1696 map_window->set_overlay_level(MAP_OVERLAY_DEFAULT);
1697 map_window->set_overlay(overlay);
1698 assert(overlay->w % PEER_TILEW == 0); // overlay must be a multiple of tile size
1699 SDL_FillRect(overlay, NULL, 0);
1700
1701 peer();
1702 }
1703
delete_self()1704 void PeerEffect::delete_self() {
1705 map_window->set_overlay(NULL);
1706 if (gem)
1707 game->get_usecode()->message_obj(gem, MESG_EFFECT_COMPLETE, this);
1708 else // FIXME: I don't want prompt display here, so it's also in UseCode,
1709 // but it has to be here if no object was set. (until we have another
1710 // way to tell caller effect is complete, and return to player)
1711 {
1712 game->get_scroll()->display_string("\n");
1713 game->get_scroll()->display_prompt();
1714 }
1715 Effect::delete_self();
1716 }
1717
peer()1718 void PeerEffect::peer() {
1719 uint16 w = overlay->w, h = overlay->h;
1720 // effect is limited to 48x48 area
1721 if (overlay->w > 48 * PEER_TILEW) w = 48 * PEER_TILEW;
1722 if (overlay->h > 48 * PEER_TILEW) h = 48 * PEER_TILEW;
1723
1724 MapCoord player_loc = game->get_player()->get_actor()->get_location();
1725 uint16 cx = player_loc.x - area.x; // rough center of area
1726 uint16 cy = player_loc.y - area.y;
1727 area.x %= map_pitch; // we have to wrap here because we use a map buffer
1728 area.y %= map_pitch;
1729 uint8 *mapbuffer = new uint8[48 * 48]; // array of tile types/colors
1730 memset(mapbuffer, 0x00, sizeof(uint8) * 48 * 48); // fill with black
1731 fill_buffer(mapbuffer, cx, cy);
1732
1733 for (int x = 0; x < w; x += PEER_TILEW)
1734 for (int y = 0; y < h; y += PEER_TILEW) {
1735 uint16 wx = area.x + x / PEER_TILEW, wy = area.y + y / PEER_TILEW;
1736 uint8 tile_type = mapbuffer[(wy - area.y) * 48 + (wx - area.x)];
1737 blit_tile(x, y, tile_type);
1738 if (tile_type != 0x00) {
1739 Actor *actor = game->get_actor_manager()->get_actor(wx, wy, area.z);
1740 if (actor)
1741 blit_actor(actor);
1742 }
1743 }
1744
1745 delete [] mapbuffer;
1746 }
1747
fill_buffer(uint8 * mapbuffer,uint16 x,uint16 y)1748 void PeerEffect::fill_buffer(uint8 *mapbuffer, uint16 x, uint16 y) {
1749 uint16 wx = area.x + x, wy = area.y + y;
1750 uint8 *tile = &mapbuffer[y * 48 + x];
1751
1752 if (*tile != 0x00)
1753 return; // already filled
1754
1755 wx %= map_pitch; // we have to wrap here because we use a map buffer
1756 wy %= map_pitch;
1757 *tile = get_tilemap_type(wx, wy, area.z);
1758
1759 // stop at unpassable tiles
1760 // FIXME: stop at Nothing/black tiles
1761 if (*tile != peer_tilemap[2]
1762 || game->get_game_map()->get_tile(wx, wy, area.z, true)->passable) {
1763 if (y > 0) {
1764 if (x > 0) fill_buffer(mapbuffer, x - 1, y - 1); // +-+-+
1765 if (y > 0) fill_buffer(mapbuffer, x, y - 1); // |\|/|
1766 if (x + 1 < 48) fill_buffer(mapbuffer, x + 1, y - 1);
1767 }
1768
1769 if (x > 0) fill_buffer(mapbuffer, x - 1, y); // +-+-+
1770 if (x + 1 < 48) fill_buffer(mapbuffer, x + 1, y);
1771
1772 if (y + 1 < 48) {
1773 if (x > 0) fill_buffer(mapbuffer, x - 1, y + 1); // |/|\|
1774 fill_buffer(mapbuffer, x, y + 1); // +-+-+
1775 if (x + 1 < 48) fill_buffer(mapbuffer, x + 1, y + 1);
1776 }
1777 }
1778 }
1779
blit_tile(uint16 x,uint16 y,uint8 c)1780 inline void PeerEffect::blit_tile(uint16 x, uint16 y, uint8 c) {
1781 Graphics::Surface s = overlay->getSubArea(Common::Rect(0, 0, overlay->w, overlay->h));
1782 uint8 *pixels = (uint8 *)s.getPixels();
1783 for (int j = 0; j < PEER_TILEW && j < overlay->h; j++)
1784 for (int i = 0; i < PEER_TILEW && i < overlay->w; i++) {
1785 if (peer_tile[i * PEER_TILEW + j] != tile_trans)
1786 pixels[overlay->w * (y + j) + (x + i)] = c;
1787 }
1788 }
1789
blit_actor(Actor * actor)1790 inline void PeerEffect::blit_actor(Actor *actor) {
1791 tile_trans = 1;
1792 blit_tile((actor->get_location().x - area.x)*PEER_TILEW,
1793 (actor->get_location().y - area.y)*PEER_TILEW, 0x0F);
1794 tile_trans = 0;
1795 if (game->get_player()->get_actor() == actor)
1796 blit_tile((actor->get_location().x - area.x)*PEER_TILEW,
1797 (actor->get_location().y - area.y)*PEER_TILEW, 0x0F);
1798 }
1799
get_tilemap_type(uint16 wx,uint16 wy,uint8 wz)1800 inline uint8 PeerEffect::get_tilemap_type(uint16 wx, uint16 wy, uint8 wz) {
1801 Map *map = game->get_game_map();
1802 // ignore objects (bridges and docks), and show coasts as land
1803 if (map->is_water(wx, wy, wz, true) && !map->get_tile(wx, wy, wz, true)->passable)
1804 return peer_tilemap[1];
1805 if (!map->is_passable(wx, wy, wz))
1806 return peer_tilemap[2];
1807 if (map->is_damaging(wx, wy, wz))
1808 return peer_tilemap[3];
1809 return peer_tilemap[0]; // ground/passable
1810 }
1811
WingStrikeEffect(Actor * target_actor)1812 WingStrikeEffect::WingStrikeEffect(Actor *target_actor) {
1813 actor = target_actor;
1814
1815 add_anim(new WingAnim(actor->get_location()));
1816 }
1817
callback(uint16 msg,CallBack * caller,void * data)1818 uint16 WingStrikeEffect::callback(uint16 msg, CallBack *caller, void *data) {
1819 switch (msg) {
1820 case MESG_ANIM_HIT :
1821 DEBUG(0, LEVEL_DEBUGGING, "hit target!\n");
1822 Game::get_game()->get_script()->call_actor_hit(actor, (NUVIE_RAND() % 20) + 1);
1823 break;
1824 case MESG_ANIM_DONE :
1825 delete_self();
1826 break;
1827 }
1828 return 0;
1829 }
1830
HailStormEffect(MapCoord target)1831 HailStormEffect::HailStormEffect(MapCoord target) {
1832 add_anim(new HailstormAnim(target));
1833 }
1834
callback(uint16 msg,CallBack * caller,void * data)1835 uint16 HailStormEffect::callback(uint16 msg, CallBack *caller, void *data) {
1836 switch (msg) {
1837 case MESG_ANIM_HIT :
1838 DEBUG(0, LEVEL_DEBUGGING, "hit target!\n");
1839 Game::get_game()->get_script()->call_actor_hit((Actor *)data, 1);
1840 break;
1841 case MESG_ANIM_DONE :
1842 delete_self();
1843 break;
1844 }
1845 return 0;
1846 }
1847
1848 /*** AsyncEffect ***/
AsyncEffect(Effect * e)1849 AsyncEffect::AsyncEffect(Effect *e) {
1850 effect_complete = false;
1851 effect = e;
1852 effect->retain();
1853 effect_manager->watch_effect(this, effect);
1854 }
1855
~AsyncEffect()1856 AsyncEffect::~AsyncEffect() {
1857 effect->release();
1858 }
1859
1860 /* The effect is marked as defunct after run finishes and will be removed from the system.*/
run(bool process_gui_input)1861 void AsyncEffect::run(bool process_gui_input) {
1862 if (!process_gui_input)
1863 Game::get_game()->pause_user();
1864 for (; effect_complete == false;) {
1865 //spin world
1866 Game::get_game()->update_once(process_gui_input);
1867 if (!effect_complete)
1868 Game::get_game()->update_once_display();
1869 }
1870 if (!process_gui_input)
1871 Game::get_game()->unpause_user();
1872 delete_self();
1873 }
1874
callback(uint16 msg,CallBack * caller,void * data)1875 uint16 AsyncEffect::callback(uint16 msg, CallBack *caller, void *data) {
1876
1877 // effect complete
1878 if (msg == MESG_EFFECT_COMPLETE) {
1879 effect_complete = true;
1880 }
1881
1882 return 0;
1883 }
1884
1885 } // End of namespace Nuvie
1886 } // End of namespace Ultima
1887