22 |
#include "objects.hh" |
#include "objects.hh" |
23 |
#include "object_mixins.hh" |
#include "object_mixins.hh" |
24 |
#include "game.hh" |
#include "game.hh" |
|
// #include "world.hh" |
|
25 |
#include "laser.hh" |
#include "laser.hh" |
26 |
#include "sound.hh" |
#include "sound.hh" |
27 |
|
|
2261 |
INSTANCELISTOBJ(OxydStone); |
INSTANCELISTOBJ(OxydStone); |
2262 |
public: |
public: |
2263 |
OxydStone(); |
OxydStone(); |
|
~OxydStone(); |
|
2264 |
|
|
2265 |
static void shuffle_colors(); |
static void shuffle_colors(); |
2266 |
private: |
private: |
2267 |
enum State { CLOSED, OPEN, OPENING, CLOSING, BLINKING }; |
enum State { CLOSED, OPEN, OPENING, CLOSING, BLINKING }; |
2268 |
State state; |
State state; |
2269 |
|
|
|
display::Model *model_bak; |
|
|
|
|
2270 |
static bool blinking(OxydStone *a) { |
static bool blinking(OxydStone *a) { |
2271 |
return (a->state==BLINKING); |
return (a->state==BLINKING); |
2272 |
} |
} |
2298 |
OxydStone::InstanceList OxydStone::instances; |
OxydStone::InstanceList OxydStone::instances; |
2299 |
|
|
2300 |
OxydStone::OxydStone() |
OxydStone::OxydStone() |
2301 |
: PhotoStone("st-oxyd") |
: PhotoStone("st-oxyd"), |
2302 |
, state(CLOSED) |
state(CLOSED) |
|
, model_bak(0) |
|
2303 |
{ |
{ |
2304 |
set_attrib("flavor", "b"); |
set_attrib("flavor", "b"); |
2305 |
set_attrib("color", "0"); |
set_attrib("color", "0"); |
2306 |
} |
} |
2307 |
|
|
|
OxydStone::~OxydStone() |
|
|
{ |
|
|
delete model_bak; |
|
|
} |
|
|
|
|
2308 |
void |
void |
2309 |
OxydStone::message(const string &m, const Value &) |
OxydStone::message(const string &m, const Value &) |
2310 |
{ |
{ |
2311 |
if (m=="closeall") |
if (m=="closeall") { |
2312 |
for (unsigned i=0; i<instances.size(); ++i) |
for (unsigned i=0; i<instances.size(); ++i) |
2313 |
instances[i]->change_state(CLOSING); |
instances[i]->change_state(CLOSING); |
2314 |
|
} |
2315 |
else if (m=="shuffle") |
else if (m=="shuffle") |
2316 |
shuffle_colors(); |
shuffle_colors(); |
2317 |
else if (m=="trigger") |
else if (m=="trigger") |
2419 |
i=find_if(instances.begin(), instances.end(), blinking_or_opening); |
i=find_if(instances.begin(), instances.end(), blinking_or_opening); |
2420 |
|
|
2421 |
if (i != instances.end()) { |
if (i != instances.end()) { |
2422 |
// If colors match and stone (*i) is alredy blinking, open |
#if 0 |
2423 |
// both stones. Close one of them otherwise |
// If colors match and stone (*i) is already blinking, open |
2424 |
|
// both stones. Close one of them otherwise. |
2425 |
|
|
2426 |
|
// (This is the Oxyd behaviour, and it makes sense: Some |
2427 |
|
// levels (Oxyd 1 #70 for example) are too easy if both |
2428 |
|
// Oxyd stones of the same color can open at the same time |
2429 |
|
// and can be solved by sheer luck. Alas, this behaviour |
2430 |
|
// doesn't work with some new Enigma levels like Enigris |
2431 |
|
// and Simon Says, so it's currently disabled.) |
2432 |
if (mycolor == (*i)->int_attrib("color") && (*i)->state==BLINKING) { |
if (mycolor == (*i)->int_attrib("color") && (*i)->state==BLINKING) { |
2433 |
|
#else |
2434 |
|
if (mycolor == (*i)->int_attrib("color")) { |
2435 |
|
#endif |
2436 |
change_state(OPEN); |
change_state(OPEN); |
2437 |
(*i)->change_state(OPEN); |
(*i)->change_state(OPEN); |
2438 |
} else { |
} else { |
2456 |
void |
void |
2457 |
OxydStone::on_creation() |
OxydStone::on_creation() |
2458 |
{ |
{ |
2459 |
if (model_bak) { |
string flavor = "a"; |
2460 |
display::SetModel(GridLoc(GRID_STONES, get_pos()), model_bak); |
string_attrib("flavor", &flavor); |
2461 |
model_bak=0; |
set_model(string("st-oxyd") + flavor); |
|
} |
|
|
else |
|
|
{ |
|
|
string flavor = "a"; |
|
|
string_attrib("flavor", &flavor); |
|
|
set_model(string("st-oxyd") + flavor); |
|
|
} |
|
|
|
|
2462 |
photo_activate(); |
photo_activate(); |
2463 |
} |
} |
2464 |
|
|
2465 |
void |
void OxydStone::on_removal() |
|
OxydStone::on_removal() |
|
2466 |
{ |
{ |
2467 |
photo_deactivate(); |
photo_deactivate(); |
|
model_bak = display::YieldModel(GridLoc(GRID_STONES, get_pos())); |
|
2468 |
} |
} |
2469 |
|
|
2470 |
|
|
2875 |
} |
} |
2876 |
|
|
2877 |
void |
void |
2878 |
Turnstile_Pivot_Base::handleActorsAndItems(bool clockwise, Object *impulse_sender) { |
Turnstile_Pivot_Base::handleActorsAndItems(bool clockwise, Object *impulse_sender) |
2879 |
|
{ |
2880 |
GridPos pv_pos = get_pos(); |
GridPos pv_pos = get_pos(); |
2881 |
V2 pv_center(pv_pos.center()); |
V2 pv_center(pv_pos.center()); |
2882 |
|
|
2950 |
if (compatible) { |
if (compatible) { |
2951 |
bool is_impulse_sender = (*ac == dynamic_cast<Actor*>(impulse_sender)); |
bool is_impulse_sender = (*ac == dynamic_cast<Actor*>(impulse_sender)); |
2952 |
|
|
2953 |
if (is_impulse_sender) { |
if (is_impulse_sender) |
2954 |
action = WARP; |
action = WARP; |
|
} |
|
2955 |
else { // other actors |
else { // other actors |
2956 |
if (arm_seen[idx_source]) { // this actor has been hit by an arm |
if (arm_seen[idx_source]) // this actor has been hit by an arm |
2957 |
action = SHATTER; |
action = SHATTER; |
|
} |
|
2958 |
} |
} |
2959 |
} |
} |
2960 |
else { // not oxyd-compatible |
else { // not oxyd-compatible |
2961 |
if (arm_seen[idx_source]) { |
if (arm_seen[idx_source]) |
2962 |
action = WARP; |
action = WARP; |
|
} |
|
2963 |
// @@@ FIXME: shattered actors are warped |
// @@@ FIXME: shattered actors are warped |
2964 |
} |
} |
2965 |
|
|
2966 |
switch (action) { |
switch (action) { |
2967 |
case WARP: { |
case WARP: { |
2968 |
GridPos ac_target_pos(pv_pos.x+to_x[idx_target], pv_pos.y+to_y[idx_target]); |
GridPos ac_target_pos(pv_pos.x+to_x[idx_target], pv_pos.y+to_y[idx_target]); |
2969 |
world::WarpActor(*ac, ac_target_pos.x+.5, ac_target_pos.y+.5, true); |
world::WarpActor(*ac, ac_target_pos.x+.5, ac_target_pos.y+.5, true); |
2970 |
|
|
2971 |
Stone *st = GetStone(ac_target_pos); |
Stone *st = GetStone(ac_target_pos); |
2972 |
if (!st) break; |
if (!st) break; |
2973 |
|
|
2974 |
// destination is blocked |
// destination is blocked |
2975 |
|
|
2976 |
Turnstile_Arm *arm = dynamic_cast<Turnstile_Arm*>(st); |
Turnstile_Arm *arm = dynamic_cast<Turnstile_Arm*>(st); |
2977 |
if (arm && !compatible) { // if blocking stone is turnstile arm -> hit it! |
if (arm && !compatible) { // if blocking stone is turnstile arm -> hit it! |
2978 |
const int impulse_dir[2][8] = { |
const int impulse_dir[2][8] = { |
2979 |
// anticlockwise |
// anticlockwise |
2980 |
{ SOUTHBIT|WESTBIT, WESTBIT, NORTHBIT|WESTBIT, NORTHBIT, |
{ SOUTHBIT|WESTBIT, WESTBIT, NORTHBIT|WESTBIT, NORTHBIT, |
2981 |
NORTHBIT|EASTBIT, EASTBIT, SOUTHBIT|EASTBIT, SOUTHBIT }, |
NORTHBIT|EASTBIT, EASTBIT, SOUTHBIT|EASTBIT, SOUTHBIT }, |
2982 |
// clockwise |
// clockwise |
2983 |
{ NORTHBIT|EASTBIT, EASTBIT, SOUTHBIT|EASTBIT, SOUTHBIT, |
{ NORTHBIT|EASTBIT, EASTBIT, SOUTHBIT|EASTBIT, SOUTHBIT, |
2984 |
SOUTHBIT|WESTBIT, WESTBIT, NORTHBIT|WESTBIT, NORTHBIT } |
SOUTHBIT|WESTBIT, WESTBIT, NORTHBIT|WESTBIT, NORTHBIT } |
2985 |
}; |
}; |
2986 |
|
|
2987 |
DirectionBits possible_impulses = |
DirectionBits possible_impulses = |
2988 |
static_cast<DirectionBits>(impulse_dir[clockwise][idx_target]); |
static_cast<DirectionBits>(impulse_dir[clockwise][idx_target]); |
2989 |
|
|
2990 |
for (int d = 0; d<4; ++d) { |
for (int d = 0; d<4; ++d) { |
2991 |
if (has_dir(possible_impulses, Direction(d))) { |
if (has_dir(possible_impulses, Direction(d))) { |
2992 |
(*ac)->send_impulse(ac_target_pos, Direction(d)); |
(*ac)->send_impulse(ac_target_pos, Direction(d)); |
|
} |
|
2993 |
} |
} |
2994 |
|
} |
2995 |
|
|
2996 |
if (GetStone(ac_target_pos) == 0) { // arm disappeared |
if (GetStone(ac_target_pos) == 0) { // arm disappeared |
2997 |
break; |
break; |
|
} |
|
2998 |
} |
} |
|
// fall-through (if arm didn't rotate or other stone was blocking) |
|
|
} |
|
|
case SHATTER: { |
|
|
SendMessage(*ac, "shatter"); |
|
|
break; |
|
|
} |
|
|
case STAY: { // do nothing |
|
|
break; |
|
2999 |
} |
} |
3000 |
|
// fall-through (if arm didn't rotate or other stone was blocking) |
3001 |
|
} |
3002 |
|
case SHATTER: { |
3003 |
|
SendMessage(*ac, "shatter"); |
3004 |
|
break; |
3005 |
|
} |
3006 |
|
case STAY: break; // do nothing |
3007 |
} |
} |
3008 |
|
|
3009 |
// @@@ FIXME: it's possible that two actors are moved to the same destination field. |
// @@@ FIXME: it's possible that two actors are moved |
3010 |
// In that case the second actor is put on top of the first actor |
// to the same destination field. In that case the |
3011 |
// (happens only in non-oxyd-compat-mode with three balls or pullers/impulsestones) |
// second actor is put on top of the first actor |
3012 |
|
// (happens only in non-oxyd-compat-mode with three |
3013 |
|
// balls or pullers/impulsestones) |
3014 |
// |
// |
3015 |
// Note: With black and whiteball it's normally no problem, |
// Note: With black and whiteball it's normally no |
3016 |
// because when one of the actors was moving, it's looking natural. |
// problem, because when one of the actors was moving, |
3017 |
// Problems occur when small balls come into play. |
// it's looking natural. Problems occur when small |
3018 |
|
// balls come into play. |
3019 |
} |
} |
3020 |
} |
} |
3021 |
} |
} |
|
|
|
3022 |
} |
} |
3023 |
|
|
3024 |
|
|