844 lines
22 KiB
C++
844 lines
22 KiB
C++
/*
|
||
* Copyright (C) 2002,2003,2004 Daniel Heck
|
||
*
|
||
* This program is free software; you can redistribute it and/or
|
||
* modify it under the terms of the GNU General Public License
|
||
* as published by the Free Software Foundation; either version 2
|
||
* of the License, or (at your option) any later version.
|
||
*
|
||
* This program is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
* GNU General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU General Public License along
|
||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||
*
|
||
*/
|
||
|
||
#include "errors.hh"
|
||
#include "stones_internal.hh"
|
||
#include "server.hh"
|
||
#include "client.hh"
|
||
#include "player.hh"
|
||
#include "Inventory.hh"
|
||
|
||
using namespace std;
|
||
using namespace world;
|
||
using namespace stones;
|
||
|
||
|
||
/* -------------------- Helper routines -------------------- */
|
||
|
||
/*! Determine whether the actor hitting the stone can move stone
|
||
and return either the direction the stone should move or NODIR. */
|
||
Direction stones::get_push_direction (const StoneContact &sc)
|
||
{
|
||
ActorInfo *ai = sc.actor->get_actorinfo();
|
||
Direction dir = contact_face(sc);
|
||
|
||
// Make sure the speed component towards the face of the stone is
|
||
// large enough and pointing towards the stone.
|
||
if (dir!=enigma::NODIR && ai->vel * sc.normal < -4)
|
||
return reverse(dir);
|
||
return NODIR;
|
||
}
|
||
|
||
/* Move a stone (by sending an impulse) Called when an actor hits a
|
||
stone. */
|
||
bool stones::maybe_push_stone (const StoneContact &sc)
|
||
{
|
||
Direction dir = get_push_direction(sc);
|
||
if (dir != enigma::NODIR) {
|
||
sc.actor->send_impulse(sc.stonepos, dir);
|
||
return GetStone(sc.stonepos) == 0; // return true only if stone vanished
|
||
}
|
||
return false;
|
||
}
|
||
|
||
|
||
//======================================================================
|
||
// STONES
|
||
//======================================================================
|
||
|
||
/** \page lstones Available Stones
|
||
|
||
Oxyd Stones:
|
||
- \ref st-oxyd
|
||
- \ref st-fakeoxyd
|
||
- \ref st-fart
|
||
|
||
Movable stones:
|
||
- \ref st-brownie
|
||
- \ref st-wood
|
||
- \ref st-block
|
||
|
||
Stones that can trigger actions:
|
||
- \ref st-switch
|
||
- \ref st-fourswitch
|
||
- \ref st-laserswitch
|
||
- \ref st-key
|
||
- \ref st-coinslot
|
||
- \ref st-timer
|
||
|
||
Stones that can change their behaviour:
|
||
- \ref st-brick_magic
|
||
- \ref st-stonebrush
|
||
- \ref st-invisible_magic
|
||
- \ref st-break_invisible
|
||
|
||
Lasers and Mirrors:
|
||
- \ref st-laser
|
||
- \ref st-pmirror
|
||
- \ref st-3mirror
|
||
|
||
Other stones:
|
||
- \ref st-death
|
||
- \ref st-swap
|
||
- \ref st-bolder
|
||
- \ref st-puzzle
|
||
- \ref st-stone_break
|
||
- \ref st-window
|
||
- \ref st-break_acwhite
|
||
- \ref st-break_acblack
|
||
- \ref st-oneway
|
||
- \ref st-oneway_black
|
||
- \ref st-oneway_white
|
||
- \ref st-chameleon
|
||
*/
|
||
|
||
Stone::Stone()
|
||
{}
|
||
|
||
Stone::Stone(const char * kind)
|
||
: GridObject (kind)
|
||
{}
|
||
|
||
Stone::~Stone() { revokeDelayedImpulses(this); }
|
||
|
||
const StoneTraits &Stone::get_traits() const
|
||
{
|
||
static StoneTraits default_traits = {
|
||
"INVALID", st_INVALID, stf_none, material_stone, 1.0
|
||
};
|
||
return default_traits;
|
||
}
|
||
|
||
const char *Stone::get_kind() const
|
||
{
|
||
const StoneTraits &tr = get_traits();
|
||
if (tr.id != st_INVALID)
|
||
return tr.name;
|
||
else
|
||
return Object::get_kind();
|
||
}
|
||
|
||
StoneResponse Stone::collision_response(const StoneContact &) {
|
||
return STONE_REBOUND;
|
||
}
|
||
|
||
|
||
void Stone::actor_hit(const StoneContact &sc)
|
||
{
|
||
if (is_movable())
|
||
stones::maybe_push_stone (sc);
|
||
}
|
||
|
||
void Stone::actor_touch(const StoneContact &sc) {
|
||
}
|
||
|
||
void Stone::on_impulse(const Impulse& impulse) {
|
||
if (is_movable())
|
||
move_stone(impulse.dir);
|
||
}
|
||
|
||
const char * Stone::collision_sound() {
|
||
return "stone";
|
||
}
|
||
|
||
/* Move a stone (regardless whether it is_movable() or not) if
|
||
the destination field is free.
|
||
Returns: true if stone has been moved.
|
||
|
||
Note: This should be used by on_impulse() to perform a move.
|
||
*/
|
||
bool Stone::move_stone(GridPos newPos, const char *soundevent) {
|
||
GridPos p = get_pos();
|
||
|
||
if (!GetStone(newPos)) {
|
||
sound_event (soundevent);
|
||
|
||
MoveStone(p, newPos);
|
||
server::IncMoveCounter();
|
||
|
||
on_move();
|
||
if (Item *it = GetItem(newPos)) it->on_stonehit(this);
|
||
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
bool Stone::move_stone(Direction dir) {
|
||
return move_stone(move(get_pos(), dir), "movesmall");
|
||
}
|
||
|
||
void Stone::on_move() {
|
||
if (!is_floating())
|
||
ShatterActorsInsideField (get_pos());
|
||
}
|
||
|
||
/* Multiplies velocity with the attribute-matrix
|
||
hit_distortion_[xx,xy,yx,yy] and factor hit_factor
|
||
If components are not set, use ((1,0),(0,1)) as
|
||
default matrix, resp. defaultfactor as hit_factor. */
|
||
ecl::V2 Stone::distortedVelocity (ecl::V2 vel, double defaultfactor = 1.0) {
|
||
ecl::V2 newvel(0,0);
|
||
if(const Value *xx = this->get_attrib("hit_distortion_xx"))
|
||
newvel[0] += to_double(*xx) * vel[0];
|
||
else
|
||
newvel[0] += vel[0];
|
||
if(const Value *xy = this->get_attrib("hit_distortion_xy"))
|
||
newvel[0] += to_double(*xy) * vel[1];
|
||
if(const Value *yx = this->get_attrib("hit_distortion_yx"))
|
||
newvel[1] += to_double(*yx) * vel[0];
|
||
if(const Value *yy = this->get_attrib("hit_distortion_yy"))
|
||
newvel[1] += to_double(*yy) * vel[1];
|
||
else
|
||
newvel[1] += vel[1];
|
||
if (const Value *factor = this->get_attrib("hit_factor"))
|
||
newvel *= to_double(*factor);
|
||
else
|
||
newvel *= defaultfactor;
|
||
return newvel;
|
||
}
|
||
|
||
|
||
|
||
// *******************************************************************************
|
||
// Stones under development :
|
||
|
||
|
||
|
||
/* -------------------- Explosion stone -------------------- */
|
||
namespace
|
||
{
|
||
class ExplosionStone : public Stone {
|
||
CLONEOBJ(ExplosionStone);
|
||
|
||
StoneResponse collision_response(const StoneContact &) {
|
||
return STONE_PASS;
|
||
}
|
||
|
||
void actor_contact (Actor *a) {
|
||
SendMessage(a, "shatter");
|
||
}
|
||
void init_model() {
|
||
set_anim("st-explosion");
|
||
}
|
||
|
||
void animcb() {
|
||
KillStone(get_pos());
|
||
}
|
||
public:
|
||
ExplosionStone(): Stone("st-explosion")
|
||
{}
|
||
};
|
||
}
|
||
|
||
|
||
/* -------------------- Charge stone -------------------- */
|
||
|
||
// Attributes:
|
||
//
|
||
// :charge + - 0
|
||
namespace
|
||
{
|
||
class ChargeStone : public Stone {
|
||
CLONEOBJ(ChargeStone);
|
||
public:
|
||
ChargeStone(const char *kind, double charge)
|
||
: Stone(kind)
|
||
{
|
||
set_attrib("charge", charge);
|
||
}
|
||
private:
|
||
double get_charge() {
|
||
double q = 0;
|
||
double_attrib("charge", &q);
|
||
return max(-1.0, min(1.0, q));
|
||
}
|
||
void animcb() { init_model(); }
|
||
void actor_hit (const StoneContact &sc) {
|
||
ActorInfo *ai = sc.actor->get_actorinfo();
|
||
ai->charge = get_charge();
|
||
set_anim(string(get_kind())+"-anim");
|
||
}
|
||
};
|
||
}
|
||
|
||
|
||
/* -------------------- SpitterStone -------------------- */
|
||
|
||
namespace
|
||
{
|
||
class SpitterStone : public Stone {
|
||
CLONEOBJ(SpitterStone);
|
||
|
||
enum State { IDLE, LOADING, SPITTING };
|
||
|
||
// Variables
|
||
State state;
|
||
V2 ball_velocity;
|
||
|
||
// Functions.
|
||
void animcb();
|
||
void actor_hit (const StoneContact &sc);
|
||
|
||
public:
|
||
SpitterStone () : Stone("st-spitter"), state (IDLE) {
|
||
}
|
||
};
|
||
}
|
||
|
||
void SpitterStone::animcb() {
|
||
switch (state) {
|
||
case IDLE:
|
||
ASSERT(0, XLevelRuntime, "SpitterStone: animcb called with inconsistent state");
|
||
case LOADING: {
|
||
Actor *ball = MakeActor (ac_cannonball);
|
||
ActorInfo *ai = ball->get_actorinfo();
|
||
V2 center = get_pos().center();
|
||
|
||
state = SPITTING;
|
||
ai->vel = ball_velocity;
|
||
world::AddActor (center[0], center[1], ball);
|
||
set_anim ("st-spitter-spitting");
|
||
break;
|
||
}
|
||
case SPITTING:
|
||
init_model();
|
||
state = IDLE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
void SpitterStone::actor_hit (const StoneContact &sc)
|
||
{
|
||
if (state != IDLE)
|
||
return;
|
||
|
||
if (enigma::Inventory *inv = player::GetInventory(sc.actor)) {
|
||
int lifepos = inv->find("it-extralife");
|
||
if (lifepos != -1) {
|
||
delete inv->yield_item(lifepos);
|
||
player::RedrawInventory (inv);
|
||
ball_velocity = distortedVelocity(sc.actor->get_vel(), 1.0);
|
||
state = LOADING;
|
||
set_anim ("st-spitter-loading");
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/* -------------------- YieldedGridStone -------------------- */
|
||
|
||
YieldedGridStone::YieldedGridStone(Stone *st)
|
||
: stone(st)
|
||
{
|
||
GridPos pos = stone->get_pos();
|
||
model = display::YieldModel(GridLoc(GRID_STONES, pos));
|
||
YieldStone(pos);
|
||
}
|
||
|
||
YieldedGridStone::~YieldedGridStone()
|
||
{
|
||
ASSERT(!stone, XLevelRuntime,
|
||
"YieldedGridStone: destructor called though stone still exists");
|
||
ASSERT(!model, XLevelRuntime,
|
||
"YieldedGridStone: destructor called though model still exists");
|
||
}
|
||
|
||
void YieldedGridStone::set_stone(GridPos pos)
|
||
{
|
||
SetStone(pos, stone);
|
||
display::SetModel(GridLoc(GRID_STONES, stone->get_pos()), model);
|
||
stone->on_move();
|
||
stone = 0;
|
||
model = 0;
|
||
}
|
||
|
||
void YieldedGridStone::dispose()
|
||
{
|
||
stone->dispose();
|
||
delete model;
|
||
stone = 0;
|
||
model = 0;
|
||
}
|
||
|
||
|
||
/* -------------------- Oxyd compatibility stones -------------------- */
|
||
|
||
namespace
|
||
{
|
||
/* I have no idea what these stones are _really_ supposed to do;
|
||
they seemingly do not appear in the landscape and they create
|
||
normal floor tiles on creation. Other than that... who
|
||
knows... */
|
||
class Peroxyd_0xb8 : public Stone {
|
||
CLONEOBJ(Peroxyd_0xb8);
|
||
public:
|
||
Peroxyd_0xb8() : Stone("st-peroxyd-0xb8")
|
||
{}
|
||
|
||
void on_creation (GridPos p) {
|
||
SetFloor (p, MakeFloor ("fl-normal"));
|
||
KillStone(p);
|
||
}
|
||
};
|
||
|
||
class Peroxyd_0xb9 : public Stone {
|
||
CLONEOBJ(Peroxyd_0xb9);
|
||
public:
|
||
Peroxyd_0xb9() : Stone("st-peroxyd-0xb9")
|
||
{}
|
||
|
||
void on_creation (GridPos p) {
|
||
SetFloor (p, MakeFloor ("fl-normal"));
|
||
KillStone(p);
|
||
}
|
||
};
|
||
|
||
class Oxyd_0x18 : public Stone {
|
||
CLONEOBJ(Oxyd_0x18);
|
||
public:
|
||
Oxyd_0x18() : Stone("st-oxyd-0x18") {
|
||
}
|
||
void on_creation (GridPos p) {
|
||
KillStone (p);
|
||
}
|
||
};
|
||
}
|
||
|
||
|
||
/* -------------------- Flash stone -------------------- */
|
||
namespace
|
||
{
|
||
class FlashStone : public Stone {
|
||
CLONEOBJ(FlashStone);
|
||
|
||
void actor_hit (const StoneContact &sc) {
|
||
if (Actor *other = FindOtherMarble(sc.actor)) {
|
||
other->add_force (distortedVelocity(sc.actor->get_vel(), 20));
|
||
}
|
||
}
|
||
|
||
public:
|
||
FlashStone() : Stone ("st-flash")
|
||
{}
|
||
};
|
||
}
|
||
|
||
|
||
/* -------------------- Surprise stone -------------------- */
|
||
namespace
|
||
{
|
||
class SurpriseStone : public Stone {
|
||
CLONEOBJ (SurpriseStone);
|
||
public:
|
||
SurpriseStone() : Stone("st-surprise")
|
||
{}
|
||
|
||
void actor_hit (const StoneContact &) {
|
||
static const char *stonename[] = {
|
||
"st-grate1",
|
||
"st-death",
|
||
"st-surprise",
|
||
"st-glass1_hole",
|
||
"st-magic",
|
||
"st-knight",
|
||
"st-thief",
|
||
"st-plain_break",
|
||
"st-plain_breaking"
|
||
};
|
||
int idx = enigma::IntegerRand (1, 9) - 1;
|
||
sound_event ("stonetransform");
|
||
ReplaceStone (get_pos(), MakeStone (stonename[idx]));
|
||
}
|
||
};
|
||
}
|
||
|
||
|
||
/* -------------------- Coffee stone -------------------- */
|
||
namespace
|
||
{
|
||
class CoffeeStone : public Stone {
|
||
CLONEOBJ(CoffeeStone);
|
||
public:
|
||
CoffeeStone() : Stone ("st-coffee") {
|
||
}
|
||
|
||
void actor_hit (const StoneContact &) {
|
||
sound_event ("stonetransform");
|
||
ReplaceStone(get_pos(), MakeStone("st-glass_move"));
|
||
}
|
||
};
|
||
}
|
||
|
||
|
||
/* -------------------- Breaking stone -------------------- */
|
||
namespace
|
||
{
|
||
class BreakingStone : public Stone {
|
||
CLONEOBJ(BreakingStone);
|
||
|
||
void init_model() {
|
||
sound_event("stonedestroy");
|
||
set_anim("st-breaking");
|
||
}
|
||
|
||
void animcb() {
|
||
KillStone(get_pos());
|
||
}
|
||
public:
|
||
BreakingStone() : Stone("st-breaking") {
|
||
}
|
||
};
|
||
}
|
||
|
||
|
||
/* -------------------- Bug stone -------------------- */
|
||
namespace
|
||
{
|
||
class BugStone : public Stone {
|
||
CLONEOBJ(BugStone);
|
||
public:
|
||
BugStone() : Stone("st-bug") {
|
||
}
|
||
|
||
void actor_hit (const StoneContact &sc) {
|
||
if (get_id(sc.actor) == ac_bug) {
|
||
ReplaceStone(get_pos(), MakeStone("st-breaking"));
|
||
}
|
||
}
|
||
};
|
||
}
|
||
|
||
|
||
/* -------------------- Plain stones -------------------- */
|
||
|
||
/* These stones mimic the behaviour of the plain-looking stones in
|
||
Oxyd. */
|
||
namespace
|
||
{
|
||
class PlainStone : public Stone {
|
||
CLONEOBJ(PlainStone);
|
||
|
||
|
||
void on_laserhit (Direction) {
|
||
ReplaceStone (get_pos(), MakeStone("st-plain_cracked"));
|
||
}
|
||
|
||
const char *collision_sound() {return "stone";}
|
||
|
||
virtual Value message (const string &msg, const Value &) {
|
||
if (msg == "trigger" || msg == "signal") {
|
||
ReplaceStone(get_pos(), MakeStone("st-plain_hole"));
|
||
}
|
||
return Value();
|
||
}
|
||
void actor_hit (const StoneContact &sc) {
|
||
if (player::WieldedItemIs (sc.actor, "it-pencil")) {
|
||
enigma::Inventory *inv = player::GetInventory(sc.actor);
|
||
if (inv && inv->size() > 0) {
|
||
delete inv->yield_item(0);
|
||
player::RedrawInventory(inv);
|
||
sound_event("stonepaint");
|
||
ReplaceStone(get_pos(), MakeStone("st-firebreak"));
|
||
}
|
||
} else
|
||
Stone::actor_hit(sc);
|
||
}
|
||
public:
|
||
PlainStone() : Stone("st-plain") {}
|
||
};
|
||
|
||
class PlainStone_Hollow : public Stone {
|
||
CLONEOBJ(PlainStone_Hollow);
|
||
|
||
virtual Value message (const string &msg, const Value &) {
|
||
if (msg == "trigger" || msg == "signal") {
|
||
ReplaceStone(get_pos(), MakeStone("st-plain"));
|
||
}
|
||
return Value();
|
||
}
|
||
|
||
StoneResponse collision_response(const StoneContact &)
|
||
{ return STONE_PASS; }
|
||
|
||
bool is_floating() const { return true; }
|
||
public:
|
||
PlainStone_Hollow() : Stone("st-plain_hole") {
|
||
}
|
||
};
|
||
|
||
class PlainStone_Breaking : public Stone {
|
||
CLONEOBJ(PlainStone_Breaking);
|
||
|
||
void init_model() {
|
||
set_anim("st-plain_breaking");
|
||
}
|
||
void animcb() {
|
||
KillStone(get_pos());
|
||
}
|
||
const char *collision_sound() {return "metal";}
|
||
public:
|
||
PlainStone_Breaking() : Stone ("st-plain_breaking") {
|
||
}
|
||
};
|
||
|
||
class PlainStone_Breakable : public Stone {
|
||
CLONEOBJ(PlainStone_Breakable);
|
||
|
||
const char *collision_sound() {return "metal";}
|
||
|
||
void break_me() {
|
||
sound_event ("stonedestroy");
|
||
ReplaceStone(get_pos(), MakeStone ("st-plain_breaking"));
|
||
}
|
||
void on_laserhit (Direction) {
|
||
break_me();
|
||
}
|
||
virtual Value message (const string &msg, const Value &) {
|
||
if (msg =="ignite" || msg == "expl" || msg == "bombstone")
|
||
break_me();
|
||
return Value();
|
||
}
|
||
void actor_hit (const StoneContact &sc) {
|
||
if (player::WieldedItemIs (sc.actor, "it-hammer")) {
|
||
break_me();
|
||
}
|
||
}
|
||
|
||
void on_floor_change() {
|
||
if (Floor *fl = GetFloor (get_pos()))
|
||
if (fl->is_kind("fl-abyss"))
|
||
ReplaceStone (get_pos(), MakeStone("st-plain_falling"));
|
||
}
|
||
|
||
public:
|
||
PlainStone_Breakable() : Stone("st-plain_break") {
|
||
}
|
||
};
|
||
|
||
class PlainStone_Cracked : public Stone {
|
||
CLONEOBJ(PlainStone_Cracked);
|
||
|
||
void break_me() {
|
||
sound_event ("stonedestroy");
|
||
ReplaceStone(get_pos(), MakeStone("st-plain_breaking"));
|
||
}
|
||
|
||
void actor_hit (const StoneContact &sc) {
|
||
if (player::WieldedItemIs (sc.actor, "it-hammer")) {
|
||
break_me();
|
||
}
|
||
}
|
||
|
||
virtual Value message (const string &msg, const Value &) {
|
||
if (msg =="ignite" || msg == "expl" || msg == "bombstone")
|
||
break_me();
|
||
return Value();
|
||
}
|
||
const char *collision_sound() {return "metal";}
|
||
public:
|
||
PlainStone_Cracked() : Stone("st-plain_cracked") {
|
||
}
|
||
};
|
||
|
||
class PlainStone_Falling : public Stone {
|
||
CLONEOBJ(PlainStone_Falling);
|
||
|
||
void init_model() {
|
||
set_anim("st-plain_falling");
|
||
}
|
||
|
||
void animcb() {
|
||
sound_event ("stonedestroy");
|
||
KillStone(get_pos());
|
||
}
|
||
public:
|
||
PlainStone_Falling() : Stone("st-plain_falling") {
|
||
}
|
||
};
|
||
|
||
class PlainStone_Movable : public Stone {
|
||
CLONEOBJ(PlainStone_Movable);
|
||
|
||
void break_me() {
|
||
sound_event ("stonedestroy");
|
||
ReplaceStone(get_pos(), MakeStone ("st-plain_breaking"));
|
||
}
|
||
virtual Value message (const string &msg, const Value &) {
|
||
if (msg =="ignite" || msg == "expl" || msg == "bombstone")
|
||
break_me();
|
||
return Value();
|
||
}
|
||
void on_move() {
|
||
Stone::on_move();
|
||
GridPos p = get_pos();
|
||
if (Floor *fl = GetFloor (p)) {
|
||
if (fl->is_kind("fl-abyss")) {
|
||
ReplaceStone (p, MakeStone("st-plain_falling"));
|
||
}
|
||
else if (fl->is_kind("fl-swamp") || fl->is_kind("fl-water")) {
|
||
sound_event ("drown");
|
||
client::Msg_Sparkle (p.center());
|
||
KillStone (p);
|
||
}
|
||
}
|
||
}
|
||
|
||
bool is_movable () const { return true; }
|
||
|
||
void actor_hit (const StoneContact &sc) {
|
||
if (player::WieldedItemIs (sc.actor, "it-pencil")) {
|
||
enigma::Inventory *inv = player::GetInventory(sc.actor);
|
||
if (inv && inv->size() > 0) {
|
||
delete inv->yield_item(0);
|
||
player::RedrawInventory(inv);
|
||
sound_event("stonepaint");
|
||
ReplaceStone(get_pos(), MakeStone("st-firebreak_move"));
|
||
}
|
||
} else
|
||
Stone::actor_hit(sc);
|
||
}
|
||
|
||
public:
|
||
PlainStone_Movable() : Stone("st-plain_move") {
|
||
}
|
||
};
|
||
}
|
||
|
||
|
||
/* -------------------- Black- and Whiteballs Stones -------------------- */
|
||
|
||
namespace
|
||
{
|
||
class BlackBallsStone : public Stone {
|
||
CLONEOBJ(BlackBallsStone);
|
||
|
||
virtual Value on_message (const Message &m)
|
||
{
|
||
GridPos p = get_pos();
|
||
Actor *a = world::CurrentCollisionActor;
|
||
if (a && get_id(a) == ac_blackball) {
|
||
if (p.y == m.gridpos.y) {
|
||
SendMessage (GetStone (move (p, EAST)), "signal", 1.0);
|
||
SendMessage (GetStone (move (p, WEST)), "signal", 1.0);
|
||
SendMessage (GetStone (move (p, NORTH)), "signal", 0.0);
|
||
SendMessage (GetStone (move (p, SOUTH)), "signal", 0.0);
|
||
}
|
||
else {
|
||
SendMessage (GetStone (move (p, EAST)), "signal", 0.0);
|
||
SendMessage (GetStone (move (p, WEST)), "signal", 0.0);
|
||
SendMessage (GetStone (move (p, NORTH)), "signal", 1.0);
|
||
SendMessage (GetStone (move (p, SOUTH)), "signal", 1.0);
|
||
}
|
||
}
|
||
return Value();
|
||
}
|
||
public:
|
||
BlackBallsStone() : Stone ("st-blackballs") {
|
||
}
|
||
};
|
||
|
||
class WhiteBallsStone : public Stone {
|
||
CLONEOBJ(WhiteBallsStone);
|
||
|
||
virtual Value on_message (const Message &m)
|
||
{
|
||
GridPos p = get_pos();
|
||
Actor *a = world::CurrentCollisionActor;
|
||
if (a && get_id(a) == ac_whiteball) {
|
||
if (p.y == m.gridpos.y) {
|
||
SendMessage (GetStone (move (p, EAST)), "signal", 1.0);
|
||
SendMessage (GetStone (move (p, WEST)), "signal", 1.0);
|
||
SendMessage (GetStone (move (p, NORTH)), "signal", 0.0);
|
||
SendMessage (GetStone (move (p, SOUTH)), "signal", 0.0);
|
||
}
|
||
else {
|
||
SendMessage (GetStone (move (p, EAST)), "signal", 0.0);
|
||
SendMessage (GetStone (move (p, WEST)), "signal", 0.0);
|
||
SendMessage (GetStone (move (p, NORTH)), "signal", 1.0);
|
||
SendMessage (GetStone (move (p, SOUTH)), "signal", 1.0);
|
||
}
|
||
}
|
||
return Value();
|
||
}
|
||
|
||
public:
|
||
WhiteBallsStone() : Stone ("st-whiteballs") {
|
||
}
|
||
|
||
};
|
||
}
|
||
|
||
/* -------------------- Unimplemented stones -------------------- */
|
||
|
||
namespace
|
||
{
|
||
class FakeOxydA : public Stone {
|
||
CLONEOBJ(FakeOxydA);
|
||
public:
|
||
|
||
FakeOxydA() : Stone("st-fakeoxyda") {
|
||
}
|
||
|
||
void actor_hit (const StoneContact &) {
|
||
sound_event ("stonetransform");
|
||
ReplaceStone(get_pos(), MakeStone("st-glass1_move"));
|
||
}
|
||
};
|
||
}
|
||
|
||
|
||
// --------------------------------------------------------------------------------
|
||
|
||
extern void InitSwitches();
|
||
|
||
void stones::Init()
|
||
{
|
||
|
||
// Register(new ...);
|
||
|
||
Register (new ExplosionStone);
|
||
Register (new ChargeStone ("st-chargeplus", +1.0));
|
||
Register (new ChargeStone ("st-chargeminus", -1.0));
|
||
Register (new ChargeStone ("st-chargezero", 0.0));
|
||
Register (new SpitterStone);
|
||
Register (new Peroxyd_0xb8);
|
||
Register (new Peroxyd_0xb9);
|
||
Register (new Oxyd_0x18);
|
||
Register (new FlashStone);
|
||
Register (new SurpriseStone);
|
||
Register (new CoffeeStone);
|
||
Register (new BlackBallsStone);
|
||
Register (new WhiteBallsStone);
|
||
Register (new FakeOxydA);
|
||
Register (new BreakingStone);
|
||
Register (new BugStone);
|
||
Register (new PlainStone);
|
||
Register (new PlainStone_Hollow);
|
||
Register (new PlainStone_Breakable);
|
||
Register (new PlainStone_Breaking);
|
||
Register (new PlainStone_Cracked);
|
||
Register (new PlainStone_Movable);
|
||
Register (new PlainStone_Falling);
|
||
|
||
// Init stones from stones_simple.cc and stones_complex.cc:
|
||
Init_simple();
|
||
Init_complex();
|
||
InitSwitches();
|
||
}
|