Files
commandergenius/project/jni/application/enigma/src/st_switches.cpp
2010-10-13 17:30:44 +03:00

703 lines
18 KiB
C++
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 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 "laser.hh"
#include "player.hh"
#include "server.hh"
#include "Inventory.hh"
#include "stones_internal.hh"
using namespace std;
using namespace world;
using namespace stones;
/* -------------------- Switch -------------------- */
namespace
{
class SwitchStone : public OnOffStone {
CLONEOBJ(SwitchStone);
public:
SwitchStone() : OnOffStone("st-switch"), state(IDLE) {}
private:
enum State { IDLE, TOGGLING } state;
void init_model() {
set_model(is_on() ? "st-switch-on" : "st-switch-off");
}
void actor_hit(const StoneContact &/*sc*/) {
set_on (!is_on());
}
virtual void set_on(bool newon) {
if (state == IDLE && newon != is_on()) {
// set_attrib("on", enigma::Value(newon));
sound_event ("switchon");
state = TOGGLING;
if (newon)
set_anim("st-switch-turnon");
else
set_anim("st-switch-turnoff");
PerformAction(this, newon);
}
}
void animcb() {
state = IDLE;
set_attrib("on", enigma::Value(!is_on()));
init_model();
// set_on(!is_on());
}
const char *collision_sound() { return "metal"; }
};
}
/* -------------------- Switch_black -------------------- */
namespace
{
class Switch_black : public OnOffStone {
CLONEOBJ(Switch_black);
public:
Switch_black() : OnOffStone("st-switch_black"),
state(IDLE)
{}
private:
enum State { IDLE, TOGGLING } state;
void init_model() {
set_model(is_on() ? "st-switch_black-on" : "st-switch_black-off");
}
void actor_hit(const StoneContact &sc) {
if (get_id (sc.actor) == world::ac_blackball)
set_on (!is_on());
}
virtual void set_on(bool newon) {
if (state == IDLE && newon != is_on()) {
// set_attrib("on", enigma::Value(newon));
sound_event ("switchon");
state = TOGGLING;
if (newon)
set_anim("st-switch_black-turnon");
else
set_anim("st-switch_black-turnoff");
PerformAction(this, newon);
}
}
void animcb() {
state = IDLE;
set_attrib("on", enigma::Value(!is_on()));
init_model();
// set_on(!is_on());
}
const char *collision_sound() { return "metal"; }
};
}
/* -------------------- Switch_white -------------------- */
namespace
{
class Switch_white : public OnOffStone {
CLONEOBJ(Switch_white);
public:
Switch_white() : OnOffStone("st-switch_white"),
state(IDLE)
{}
private:
enum State { IDLE, TOGGLING } state;
void init_model() {
set_model(is_on() ? "st-switch_white-on" : "st-switch_white-off");
}
void actor_hit(const StoneContact &sc) {
if (get_id (sc.actor) == world::ac_whiteball)
set_on (!is_on());
}
virtual void set_on(bool newon) {
if (state == IDLE && newon != is_on()) {
// set_attrib("on", enigma::Value(newon));
sound_event ("switchon");
state = TOGGLING;
if (newon)
set_anim("st-switch_white-turnon");
else
set_anim("st-switch_white-turnoff");
PerformAction(this, newon);
}
}
void animcb() {
state = IDLE;
set_attrib("on", enigma::Value(!is_on()));
init_model();
}
const char *collision_sound() { return "metal"; }
};
}
/* -------------------- Coin slot -------------------- */
namespace
{
class CoinSlot : public OnOffStone, public TimeHandler {
CLONEOBJ(CoinSlot);
public:
CoinSlot();
~CoinSlot();
private:
// Variables.
enum State { ACTIVE, INACTIVE } state;
double remaining_time;
// GridObject interface
void init_model();
void animcb();
// TimeHandler interface
void tick(double dtime);
// Stone interface
void actor_hit(const StoneContact &sc);
const char *collision_sound() { return "metal"; }
// Private methods.
void change_state(State newstate);
};
}
CoinSlot::CoinSlot()
: OnOffStone("st-coinslot"), state(INACTIVE), remaining_time(0)
{
}
CoinSlot::~CoinSlot() {
GameTimer.remove_alarm (this);
}
void CoinSlot::init_model() {
set_model(state==ACTIVE ? "st-coinslot-active" : "st-coinslot");
}
void CoinSlot::change_state(State newstate) {
if (state == newstate) return;
switch (newstate) {
case ACTIVE:
PerformAction(this, true);
GameTimer.activate(this);
break;
case INACTIVE:
PerformAction(this, false);
GameTimer.deactivate(this);
sound_event ("coinslotoff");
break;
}
state = newstate;
init_model();
}
void CoinSlot::animcb() {
change_state(ACTIVE);
init_model();
}
void CoinSlot::tick(double dtime)
{
ASSERT(remaining_time > 0, XLevelRuntime, "CoinSlot: tick called, but no remaining time");
remaining_time -= dtime;
if (remaining_time <= 0)
change_state(INACTIVE);
}
void CoinSlot::actor_hit(const StoneContact &sc)
{
if (enigma::Inventory *inv = player::GetInventory(sc.actor)) {
if (Item *it = inv->get_item (0)) {
ItemID id = get_id(it);
if (id == it_coin1 || id == it_coin2 || id == it_coin4) {
sound_event ("coinsloton");
set_anim("st-coin2slot");
double coin_value = 0;
it->double_attrib("value", &coin_value);
remaining_time += coin_value;
inv->yield_first();
player::RedrawInventory (inv);
delete it;
}
}
}
}
/* -------------------- Key switches -------------------- */
// Attributes:
//
// :keycode a numerical code; only keys with the same code
// can activate this switch
// :on 1 or 0
// :target,action as usual
/** \page st-key Key Switch Stone
This stone acts as a lock and can only be activated by
using a key item. You can use keycodes to let keys only
open specific key stones.
\subsection keye Example
\verbatim
set_stone( "st-key_b", 14,77, {action="openclose", target="door2"})
set_item("it-key_b", 12,7)
\endverbatim
\image html st-key0.png
*/
namespace
{
class KeyStone : public OnOffStone {
CLONEOBJ(KeyStone);
void init_model() {set_model(is_on() ? "st-key1" : "st-key0");}
void actor_hit(const StoneContact &sc);
const char *collision_sound() { return "metal"; }
bool check_matching_key (enigma::Inventory *inv);
public:
KeyStone(const char *kind="st-key", int keycode=0)
: OnOffStone(kind)
{
set_attrib("keycode", keycode);
}
};
class KeyStone_a : public KeyStone {
CLONEOBJ(KeyStone_a);
public:
KeyStone_a() : KeyStone("st-key_a", 1) {}
};
class KeyStone_b : public KeyStone {
CLONEOBJ(KeyStone_b);
public:
KeyStone_b() : KeyStone("st-key_b", 2) {}
};
class KeyStone_c : public KeyStone {
CLONEOBJ(KeyStone_c);
public:
KeyStone_c() : KeyStone("st-key_c", 3) {}
};
}
bool KeyStone::check_matching_key (enigma::Inventory *inv)
{
Item *it = inv->get_item(0);
int keycode, my_keycode = int_attrib ("keycode");
return (it
&& it->is_kind("it-key*")
&& it->int_attrib("keycode", &keycode)
&& my_keycode == keycode);
}
void KeyStone::actor_hit(const StoneContact &sc)
{
enigma::Inventory *inv = player::GetInventory(sc.actor);
if (!inv)
return;
bool toggle = false;
if (server::GameCompatibility == enigma::GAMET_ENIGMA) {
if (is_on()) {
if (!inv->is_full()) {
Item *key = MakeItem("it-key");
key->set_attrib ("keycode", int_attrib ("keycode"));
inv->add_item(key);
toggle = true;
}
}
else if (check_matching_key (inv)) {
DisposeObject (inv->yield_first());
toggle = true;
}
player::RedrawInventory (inv);
}
else {
if (check_matching_key (inv))
toggle = true;
}
if (toggle) {
set_on (!is_on());
PerformAction (this, is_on());
}
}
/* -------------------- FourSwitch -------------------- */
// Attributes:
//
// :on 1 or 0
// :target,action as usual
namespace
{
class FourSwitch : public OnOffStone {
CLONEOBJ(FourSwitch);
public:
FourSwitch() : OnOffStone("st-fourswitch"),
m_direction(NORTH),
m_inactive_so_far (true)
{}
private:
// Variables
Direction m_direction;
bool m_inactive_so_far;
// Private methods
void turn()
{
static int direction2idx[] = {
3, // WEST
2, // SOUTH
1, // EAST
0 // NORTH
};
if (!m_inactive_so_far) {
world::EmitSignalByIndex(this, direction2idx[m_direction], 0);
} else
m_inactive_so_far = false;
m_direction = rotate_cw (m_direction);
init_model();
set_on(!is_on());
sound_event ("fourswitch");
if (world::HaveSignals (this)) {
world::EmitSignalByIndex(this, direction2idx[m_direction], 1);
} else {
// no signal handler defined
PerformAction(this, is_on());
}
}
void init_model() {
switch (m_direction) {
case NORTH: set_model("st-fourswitch-n"); break;
case EAST: set_model("st-fourswitch-e"); break;
case SOUTH: set_model("st-fourswitch-s"); break;
case WEST: set_model("st-fourswitch-w"); break;
case NODIR: ASSERT(0, XLevelRuntime,
"FourSwitch: no direction defined (found in init_model)");
}
}
void actor_hit(const StoneContact &/*sc*/) {
turn();
}
virtual Value on_message (const Message &m)
{
if (m.message == "signal" || m.message == "trigger")
turn();
return Value();
}
const char *collision_sound() { return "metal"; }
};
}
/* -------------------- Laser / Time switches -------------------- */
namespace
{
class LaserTimeSwitchBase : public PhotoStone, public TimeHandler {
public:
LaserTimeSwitchBase(const char *kind);
virtual ~LaserTimeSwitchBase();
private:
// LaserTimeSwitchBase interface
virtual const char *get_active_model() const = 0;
virtual const char *get_inactive_model() const = 0;
virtual double timer_delay() const;
bool inverse() { return int_attrib("inverse") == 1; }
// Stone interface
void on_creation (GridPos p);
void on_removal (GridPos p);
const char *collision_sound() { return "metal"; }
// PhotoStone interface.
void notify_laseron();
void notify_laseroff();
// TimeHandler interface
void alarm();
protected:
// variables :
enum State { IDLE, LIGHTED, TOUCHED };
State state;
void change_state(State newstate);
};
class LaserSwitch : public LaserTimeSwitchBase {
CLONEOBJ(LaserSwitch);
public:
LaserSwitch();
private:
// LaserTimeSwitchBase interface
const char *get_active_model() const;
const char *get_inactive_model() const;
};
class LaserTimeSwitch : public LaserTimeSwitchBase {
CLONEOBJ(LaserTimeSwitch);
public:
LaserTimeSwitch(const char *kind = "st-lasertimeswitch");
private:
// LaserTimeSwitchBase interface
const char *get_active_model() const;
const char *get_inactive_model() const;
double timer_delay() const;
// Stone interface
void actor_hit(const StoneContact &sc);
};
class TimeSwitch : public LaserTimeSwitch {
CLONEOBJ(TimeSwitch);
public:
TimeSwitch();
private:
// ignore laser:
void notify_laseron();
void notify_laseroff();
};
}
LaserTimeSwitchBase::LaserTimeSwitchBase(const char *kind)
: PhotoStone(kind) , state(IDLE)
{}
LaserTimeSwitchBase::~LaserTimeSwitchBase() {
GameTimer.remove_alarm (this);
}
void LaserTimeSwitchBase::change_state(State newstate) {
if (state == newstate)
return;
if (state == IDLE) {
// sound_event ("st-switch");
set_model(get_active_model());
PerformAction(this, !inverse());
if (newstate == TOUCHED) {
double delay = timer_delay();
ASSERT(delay>0.0, XLevelRuntime, "LaserTimeSwitchBase: delay non-positive");
GameTimer.set_alarm(this, delay, false);
}
}
else if (newstate == IDLE) {
// sound_event ("st-switch");
set_model(get_inactive_model());
PerformAction(this, inverse());
}
else {
// it's not allowed to switch from LIGHTED to TOUCHED
ASSERT(!(state == LIGHTED && newstate == TOUCHED), XLevelRuntime,
"LaserTimeSwitchBase: trying to switch from lighted to touched");
}
state = newstate;
}
void LaserTimeSwitchBase::on_creation(GridPos)
{
set_model(get_inactive_model());
photo_activate();
}
void LaserTimeSwitchBase::on_removal(GridPos)
{
photo_deactivate();
}
void LaserTimeSwitchBase::notify_laseron() {
if (state != LIGHTED)
change_state(LIGHTED);
}
void LaserTimeSwitchBase::notify_laseroff() {
if (state == LIGHTED)
change_state(IDLE);
}
void LaserTimeSwitchBase::alarm() {
if (state == TOUCHED)
change_state(IDLE);
}
double LaserTimeSwitchBase::timer_delay() const {
return -1; // we have no timer delay
}
/* ---------- LaserSwitch ---------- */
LaserSwitch::LaserSwitch()
: LaserTimeSwitchBase("st-laserswitch")
{}
const char *LaserSwitch::get_active_model() const {
return "st-laserswitch1";
}
const char *LaserSwitch::get_inactive_model() const {
return "st-laserswitch0";
}
/* ---------- LaserTimeSwitch ---------- */
LaserTimeSwitch::LaserTimeSwitch(const char *kind)
: LaserTimeSwitchBase(kind)
{
set_attrib("delay", 1.8);
}
const char *LaserTimeSwitch::get_active_model() const {
return "st-time1switch";
}
const char *LaserTimeSwitch::get_inactive_model() const {
return "st-timeswitch";
}
double LaserTimeSwitch::timer_delay() const {
double delay;
if (!double_attrib("delay", &delay))
ASSERT(0, XLevelRuntime, "LaserTimeSwitch: delay not properly defined");
return delay;
}
void LaserTimeSwitch::actor_hit(const StoneContact &sc) {
if (sc.actor && state == IDLE)
change_state(TOUCHED);
}
/* ---------- TimeSwitch ---------- */
// Attributes:
//
// :on 1 or 0
// :target,action as usual
TimeSwitch::TimeSwitch()
: LaserTimeSwitch("st-timeswitch")
{
}
void TimeSwitch::notify_laseron() {} // ignore laser
void TimeSwitch::notify_laseroff() {}
/* -------------------- Floppy switch -------------------- */
// Attributes:
//
// :on 1 or 0
// :target,action as usual
namespace
{
class FloppyStone : public OnOffStone {
CLONEOBJ(FloppyStone);
public:
FloppyStone() : OnOffStone("st-floppy") {}
private:
// Stone interface
void init_model();
void actor_hit(const StoneContact &sc);
const char *collision_sound() { return "metal"; }
};
}
void FloppyStone::init_model()
{
set_model(is_on() ? "st-floppy1" : "st-floppy0");
}
void FloppyStone::actor_hit (const StoneContact &sc)
{
if (enigma::Inventory *inv = player::GetInventory(sc.actor)) {
if (is_on()) {
if (!inv->is_full()) {
inv->add_item (MakeItem("it-floppy"));
set_on(false);
PerformAction(this, is_on());
}
}
else if (player::WieldedItemIs (sc.actor, "it-floppy")) {
DisposeObject (inv->yield_first());
set_on(true);
PerformAction(this, is_on());
}
player::RedrawInventory (inv);
}
}
/* -------------------- Functions -------------------- */
void InitSwitches()
{
Register (new CoinSlot);
Register (new FloppyStone);
Register (new FourSwitch);
Register (new KeyStone);
Register (new KeyStone_a);
Register (new KeyStone_b);
Register (new KeyStone_c);
Register (new LaserSwitch);
Register (new LaserTimeSwitch);
Register (new SwitchStone);
Register (new Switch_black);
Register (new Switch_white);
Register (new TimeSwitch);
}