329 |
}; |
}; |
330 |
} |
} |
331 |
|
|
332 |
|
|
333 |
//---------------------------------------- |
//---------------------------------------- |
334 |
// SwapStone |
// SwapStone |
335 |
//---------------------------------------- |
//---------------------------------------- |
|
|
|
|
/** \page st-swap Swap Stone |
|
|
|
|
|
This stone can exchange its position with other neighboring stones |
|
|
if it is hit hard enough. In a way, this makes swap stones a kind |
|
|
of "movable stone", except that they can be only exchanged with |
|
|
other stones and may not be moved on empty fields. |
|
|
|
|
|
\subsection swape Example |
|
|
\verbatim |
|
|
set_stone("st-swap", 10,10) |
|
|
\endverbatim |
|
|
|
|
|
\image html st-swap.png |
|
|
*/ |
|
336 |
namespace |
namespace |
337 |
{ |
{ |
338 |
class SwapStone : public Stone, public TimeHandler { |
class SwapStone : public Stone, public TimeHandler { |
339 |
public: |
public: |
340 |
SwapStone() |
SwapStone(); |
|
: Stone("st-swap") |
|
|
, state(IDLE) |
|
|
, in_exchange_with(0) |
|
|
, move_dir(NODIR) |
|
|
{} |
|
|
|
|
341 |
private: |
private: |
|
|
|
342 |
// Object interface |
// Object interface |
343 |
SwapStone *clone(); |
SwapStone *clone(); |
344 |
void dispose(); |
void dispose(); |
348 |
void on_removal(); |
void on_removal(); |
349 |
|
|
350 |
// Stone interface |
// Stone interface |
351 |
void on_impulse(const Impulse& impulse); |
void on_impulse (const Impulse &impulse); |
352 |
bool is_removable() { return state == IDLE; } |
bool is_removable() { return state == IDLE; } |
353 |
void actor_hit(const StoneContact &sc); |
void actor_hit (const StoneContact &sc); |
354 |
void actor_inside(Actor *a) { SendMessage(a, "shatter"); } |
void actor_inside(Actor *a) { SendMessage(a, "shatter"); } |
355 |
|
|
356 |
// TimeHandler interface |
// TimeHandler interface |
360 |
enum State { IDLE, COME, GO } state; |
enum State { IDLE, COME, GO } state; |
361 |
YieldedGridStone *in_exchange_with; |
YieldedGridStone *in_exchange_with; |
362 |
Direction move_dir; |
Direction move_dir; |
|
|
|
363 |
}; |
}; |
364 |
|
} |
365 |
|
|
366 |
SwapStone *SwapStone::clone() { |
SwapStone::SwapStone() |
367 |
SwapStone *other = new SwapStone(*this); |
: Stone("st-swap"), |
368 |
other->in_exchange_with = 0; |
state(IDLE), |
369 |
return other; |
in_exchange_with(0), |
370 |
} |
move_dir(NODIR) |
371 |
|
{} |
372 |
|
|
373 |
void SwapStone::dispose() { |
SwapStone * |
374 |
if (state == COME && in_exchange_with != 0) { |
SwapStone::clone() |
375 |
in_exchange_with->dispose(); |
{ |
376 |
delete in_exchange_with; |
SwapStone *other = new SwapStone(*this); |
377 |
} |
other->in_exchange_with = 0; |
378 |
delete this; |
return other; |
379 |
|
} |
380 |
|
|
381 |
|
void SwapStone::dispose() |
382 |
|
{ |
383 |
|
if (state == COME && in_exchange_with != 0) { |
384 |
|
in_exchange_with->dispose(); |
385 |
|
delete in_exchange_with; |
386 |
} |
} |
387 |
|
delete this; |
388 |
|
} |
389 |
|
|
390 |
void SwapStone::on_removal() { |
void SwapStone::on_removal() |
391 |
if (state == COME) { |
{ |
392 |
g_timer.remove_alarm(this); |
if (state == COME) { |
393 |
} |
g_timer.remove_alarm(this); |
394 |
} |
} |
395 |
|
} |
396 |
|
|
397 |
void SwapStone::alarm() { |
/* Animation finished; put the "swapped" stone to its new position. */ |
398 |
assert(state == COME); |
void SwapStone::alarm() |
399 |
|
{ |
400 |
|
GridPos oldPos = move(get_pos(), reverse(move_dir)); |
401 |
|
|
402 |
state = IDLE; |
// Set the swapped stone (this also kills the old (inactive) swap stone) |
403 |
init_model(); |
in_exchange_with->set_stone(oldPos); |
404 |
play_sound("st-move"); |
delete in_exchange_with; |
405 |
|
in_exchange_with = 0; |
406 |
|
|
407 |
GridPos oldPos = move(get_pos(), reverse(move_dir)); |
state = IDLE; |
408 |
KillStone(oldPos); |
init_model(); |
409 |
|
play_sound("st-move"); |
410 |
|
} |
411 |
|
|
412 |
assert(in_exchange_with); |
void |
413 |
in_exchange_with->set_stone(oldPos); |
SwapStone::on_impulse(const Impulse& impulse) |
414 |
delete in_exchange_with; |
{ |
415 |
in_exchange_with = 0; |
if (state == IDLE) { |
416 |
} |
GridPos oldp = get_pos(); |
417 |
|
GridPos newp = move(oldp, impulse.dir); |
418 |
|
|
419 |
void SwapStone::on_impulse(const Impulse& impulse) { |
if (!IsLevelBorder(newp)) { |
420 |
if (state == IDLE) { |
Stone *other = GetStone(newp); |
421 |
GridPos oldp = get_pos(); |
if (other && other->is_removable()) { |
422 |
GridPos newp = move(oldp, impulse.dir); |
SwapStone *newStone = new SwapStone; |
423 |
|
newStone->state = COME; |
424 |
if (!IsLevelBorder(newp)) { |
newStone->in_exchange_with = new YieldedGridStone(other); // yields 'other' |
425 |
Stone *other = GetStone(newp); |
newStone->move_dir = impulse.dir; |
|
if (other && other->is_removable()) { |
|
|
SwapStone *newStone = clone(); |
|
|
|
|
|
newStone->state = COME; |
|
|
newStone->in_exchange_with = new YieldedGridStone(other); // yields 'other' |
|
|
newStone->move_dir = impulse.dir; |
|
426 |
|
|
427 |
g_timer.set_alarm(newStone, 0.1, false); |
g_timer.set_alarm(newStone, 0.1, false); |
428 |
|
|
429 |
SetStone(newp, newStone); |
SetStone(newp, newStone); |
430 |
|
|
431 |
state = GO; |
state = GO; |
432 |
move_dir = impulse.dir; |
move_dir = impulse.dir; |
433 |
init_model(); |
init_model(); |
434 |
|
|
435 |
play_sound ("st-move"); |
play_sound ("st-move"); |
436 |
player::IncMoveCounter(1); |
player::IncMoveCounter(1); |
|
} |
|
437 |
} |
} |
438 |
} |
} |
439 |
} |
} |
440 |
|
} |
441 |
|
|
442 |
void SwapStone::actor_hit(const StoneContact &sc) { |
void |
443 |
Direction dir = get_push_direction (sc); |
SwapStone::actor_hit(const StoneContact &sc) |
444 |
if (dir != NODIR) { |
{ |
445 |
send_impulse(get_pos(), dir); |
Direction dir = get_push_direction (sc); |
446 |
} |
if (dir != NODIR) { |
447 |
|
send_impulse(get_pos(), dir); |
448 |
} |
} |
449 |
|
} |
450 |
|
|
451 |
void SwapStone::init_model() { |
void SwapStone::init_model() |
452 |
const char *model = 0; |
{ |
453 |
switch (state) { |
static char *models_come[] = { "st-swap-w", "st-swap-s", "st-swap-e", "st-swap-n" }; |
454 |
case IDLE: model = "st-swap"; break; |
static char *models_go[] = { "st-swap-e", "st-swap-n", "st-swap-w", "st-swap-s" }; |
|
case COME: { |
|
|
const char *models[] = { "st-swap-w", "st-swap-s", "st-swap-e", "st-swap-n" }; |
|
|
model = models[move_dir]; |
|
|
break; |
|
|
} |
|
|
case GO: { |
|
|
const char *models[] = { "st-swap-e", "st-swap-n", "st-swap-w", "st-swap-s" }; |
|
|
model = models[move_dir]; |
|
|
break; |
|
|
} |
|
|
} |
|
455 |
|
|
456 |
set_model(model); |
const char *model = 0; |
457 |
|
switch (state) { |
458 |
|
case IDLE: model = "st-swap"; break; |
459 |
|
case COME: model = models_come[move_dir]; break; |
460 |
|
case GO: model = models_go[move_dir]; break; |
461 |
} |
} |
462 |
|
|
463 |
|
set_model(model); |
464 |
} |
} |
465 |
|
|
466 |
|
|
467 |
//---------------------------------------- |
//---------------------------------------- |
468 |
// BlockStone |
// BlockStone |
469 |
//---------------------------------------- |
//---------------------------------------- |
1709 |
{ |
{ |
1710 |
class ActorImpulseBase : public Stone { |
class ActorImpulseBase : public Stone { |
1711 |
public: |
public: |
1712 |
ActorImpulseBase(const char *kind) : Stone(kind), state(IDLE) {} |
ActorImpulseBase(const char *kind) : Stone(kind), state(IDLE) { |
1713 |
|
// set_attrib("force", Value()); |
1714 |
|
} |
1715 |
|
|
1716 |
protected: |
protected: |
1717 |
virtual void actor_hit (const StoneContact &sc) { |
virtual void actor_hit (const StoneContact &sc) { |
1718 |
if (state == IDLE) { |
if (state == IDLE) { |
|
// normal points from stone-center _to_ actor. |
|
1719 |
// actor_hit is called before reflect, but the force added below |
// actor_hit is called before reflect, but the force added below |
1720 |
// is applied to actor after the reflection. |
// is applied to actor after the reflection. |
1721 |
|
|
1722 |
V2 normal = normalize(sc.actor->get_pos() - get_pos().center()); |
double forcefac = enigma::BumperForce; |
1723 |
double forcefac = 800; |
double_attrib("force", &forcefac); |
|
V2 forcevec = normal*forcefac; |
|
|
|
|
|
// { |
|
|
// ActorInfo *ai = sc.actor->get_actorinfo(); |
|
|
// double vel_abs = length(ai->vel); |
|
|
// double force_abs = length(forcevec); |
|
|
// warning("actor_hit (vel=%f=[%f/%f], force=%f=[%f/%f] mass=%f)", |
|
|
// vel_abs, ai->vel[0], ai->vel[1], |
|
|
// force_abs, forcevec[0], forcevec[1], |
|
|
// ai->mass); |
|
|
// } |
|
1724 |
|
|
1725 |
sc.actor->add_force (forcevec); |
V2 vec = normalize(sc.actor->get_pos() - get_pos().center()); |
1726 |
|
sc.actor->add_force (vec * forcefac); |
1727 |
|
|
1728 |
play_sound("impulse"); |
play_sound("impulse"); |
1729 |
set_anim("st-actorimpulse-anim"); |
set_anim("st-actorimpulse-anim"); |
1743 |
enum State { IDLE, PULSING, BROKEN }; |
enum State { IDLE, PULSING, BROKEN }; |
1744 |
State state; |
State state; |
1745 |
}; |
}; |
|
} |
|
1746 |
|
|
1747 |
|
|
|
//---------------------------------------- |
|
|
// ActorImpulseStone |
|
|
//---------------------------------------- |
|
|
namespace |
|
|
{ |
|
1748 |
class ActorImpulseStone : public ActorImpulseBase { |
class ActorImpulseStone : public ActorImpulseBase { |
1749 |
CLONEOBJ(ActorImpulseStone); |
CLONEOBJ(ActorImpulseStone); |
1750 |
public: |
public: |
1751 |
ActorImpulseStone() : ActorImpulseBase("st-actorimpulse") {} |
ActorImpulseStone() : ActorImpulseBase("st-actorimpulse") {} |
1752 |
}; |
}; |
|
} |
|
1753 |
|
|
1754 |
|
|
|
//---------------------------------------- |
|
|
// ActorImpulseStone Invisible |
|
|
//---------------------------------------- |
|
|
namespace |
|
|
{ |
|
1755 |
class ActorImpulseStoneInvisible : public ActorImpulseBase { |
class ActorImpulseStoneInvisible : public ActorImpulseBase { |
1756 |
CLONEOBJ(ActorImpulseStoneInvisible); |
CLONEOBJ(ActorImpulseStoneInvisible); |
1757 |
public: |
public: |
1759 |
|
|
1760 |
void actor_hit(const StoneContact& sc) { |
void actor_hit(const StoneContact& sc) { |
1761 |
if (player::wielded_item_is(sc.actor, "it-brush")) { |
if (player::wielded_item_is(sc.actor, "it-brush")) { |
1762 |
GridPos p = get_pos(); |
Stone *st = MakeStone("st-actorimpulse"); |
1763 |
KillStone(p); |
SetStone(get_pos(), st); |
|
SetStone(p, MakeStone("st-actorimpulse")); |
|
|
Stone *st = GetStone(p); |
|
1764 |
st->actor_hit(sc); |
st->actor_hit(sc); |
1765 |
} |
} |
1766 |
else { |
else |
1767 |
ActorImpulseBase::actor_hit(sc); |
ActorImpulseBase::actor_hit(sc); |
|
} |
|
1768 |
} |
} |
1769 |
}; |
}; |
1770 |
} |
} |