472 |
if (Stone *st = GetStone(move(get_pos(), dir))) { |
if (Stone *st = GetStone(move(get_pos(), dir))) { |
473 |
if (state == ACTIVE) { // allowed to trigger? |
if (state == ACTIVE) { // allowed to trigger? |
474 |
state = IDLE; |
state = IDLE; |
475 |
if (st->is_kind("st-pull")) { |
|
476 |
send_impulse(st->get_pos(), dir); |
// if (st->is_kind("st-pull")) { |
477 |
} |
// send_impulse(st->get_pos(), dir); |
478 |
else { |
// } |
479 |
SendMessage(st, "trigger", Value(dir)); |
// else { |
480 |
} |
SendMessage(st, "trigger", Value(dir)); |
481 |
|
// } |
482 |
} |
} |
483 |
return true; |
return true; |
484 |
} |
} |
657 |
|
|
658 |
This example actually presents the special case of a ``complete'' |
This example actually presents the special case of a ``complete'' |
659 |
cluster. A cluster is complete if none of its stones has an |
cluster. A cluster is complete if none of its stones has an |
660 |
unconnected socket. If such a group is touched with a magic wand, all |
unconnected socket. |
661 |
its constituents explode. [They should, anyway. This is not |
|
662 |
implemented yet.] |
When touched with a magic wand the puzzle stones rotate |
663 |
|
row- or columnwise. If one of these rows/columns contains exactly |
664 |
|
one stone, then that stone is moved (regardless whether it is a |
665 |
|
member of a cluster or not). |
666 |
|
|
667 |
\subsection puzzlea Attributes |
\subsection puzzlea Attributes |
668 |
|
|
671 |
a socket on one of the four faces. You will normally simply use |
a socket on one of the four faces. You will normally simply use |
672 |
one of the Lua constants \c PUZ_0000 to \c PUZ_1111. |
one of the Lua constants \c PUZ_0000 to \c PUZ_1111. |
673 |
|
|
674 |
|
- \b oxyd |
675 |
|
If 1 then the puzzle stones act oxyd-compatible: Complete clusters |
676 |
|
explode, when they get touched. All other puzzle stones rotate row- |
677 |
|
or columnwise. Groups of oxyd-compatible puzzle stones are shuffled |
678 |
|
randomly at level startup. |
679 |
|
|
680 |
|
|
681 |
\subsection puzzlee Example |
\subsection puzzlee Example |
682 |
<table> |
<table> |
683 |
<tr> |
<tr> |
708 |
class PuzzleStone : public Stone { |
class PuzzleStone : public Stone { |
709 |
INSTANCELISTOBJ(PuzzleStone); |
INSTANCELISTOBJ(PuzzleStone); |
710 |
public: |
public: |
711 |
PuzzleStone(int connections=0) : Stone("st-puzzle") { |
PuzzleStone(int connections, bool oxyd1_compatible_) |
712 |
|
: Stone("st-puzzle") |
713 |
|
{ |
714 |
set_attrib("connections", connections); |
set_attrib("connections", connections); |
715 |
|
set_attrib("oxyd", int(oxyd1_compatible_)); |
716 |
} |
} |
717 |
private: |
private: |
718 |
typedef vector<GridPos> Cluster; |
typedef vector<GridPos> Cluster; |
719 |
|
|
720 |
DirectionBits get_connections(); |
DirectionBits get_connections(); |
721 |
|
bool oxyd1_compatible() const { return int_attrib("oxyd") != 0; } |
722 |
|
|
723 |
static bool visit_dir(vector<GridPos> &stack, |
static bool visit_dir(vector<GridPos> &stack, |
724 |
GridPos curpos, Direction dir); |
GridPos curpos, Direction dir); |
729 |
void maybe_move_cluster(Cluster &c, bool is_complete, Direction dir); |
void maybe_move_cluster(Cluster &c, bool is_complete, Direction dir); |
730 |
void rotate_cluster(const Cluster &c); |
void rotate_cluster(const Cluster &c); |
731 |
|
|
732 |
|
void shuffle(int count); |
733 |
|
|
734 |
|
void message(const string& msg, const Value &val); |
735 |
|
|
736 |
bool on_laserhit(Direction /*dir*/) { |
bool on_laserhit(Direction /*dir*/) { |
737 |
return get_connections() == 0; |
return get_connections() == 0; |
738 |
} |
} |
830 |
void |
void |
831 |
PuzzleStone::find_row_or_column_cluster(Cluster &c, GridPos startpos, Direction dir) |
PuzzleStone::find_row_or_column_cluster(Cluster &c, GridPos startpos, Direction dir) |
832 |
{ |
{ |
833 |
|
assert(dir != NODIR); |
834 |
|
|
835 |
GridPos p = startpos; |
GridPos p = startpos; |
836 |
while (dynamic_cast<PuzzleStone*>(GetStone(p))) { |
while (dynamic_cast<PuzzleStone*>(GetStone(p))) { |
837 |
c.push_back(p); |
c.push_back(p); |
913 |
|
|
914 |
// Every combination of ``sockets'' has its own model. |
// Every combination of ``sockets'' has its own model. |
915 |
int modelno = int_attrib("connections"); |
int modelno = int_attrib("connections"); |
916 |
|
if (oxyd1_compatible()) modelno += 16; |
917 |
sprintf(x, "%d", modelno); |
sprintf(x, "%d", modelno); |
918 |
modelname += x; |
modelname += x; |
919 |
set_model(modelname); |
set_model(modelname); |
943 |
} |
} |
944 |
|
|
945 |
void |
void |
946 |
|
PuzzleStone::shuffle(int count) |
947 |
|
{ |
948 |
|
if (oxyd1_compatible()) { |
949 |
|
Direction move_dir = Direction(4*(rand()/(RAND_MAX+1.0))); |
950 |
|
Cluster c; |
951 |
|
find_row_or_column_cluster(c, get_pos(), reverse(move_dir)); |
952 |
|
if (!c.empty()) { |
953 |
|
GridPos last = c.back(); |
954 |
|
c.clear(); |
955 |
|
find_row_or_column_cluster(c, last, move_dir); |
956 |
|
|
957 |
|
// if cluster too small to rotate -> turn direction by 90 deg |
958 |
|
if (c.size() <= 1) { |
959 |
|
move_dir = rotate(move_dir, true); |
960 |
|
c.clear(); |
961 |
|
find_row_or_column_cluster(c, last, move_dir); |
962 |
|
} |
963 |
|
|
964 |
|
if (c.size() > 1) { |
965 |
|
|
966 |
|
// @@@ FIXME: check whether rotation is impossible |
967 |
|
// (e.g. blocked by unmoveable stone at both sides) |
968 |
|
|
969 |
|
while (count--) |
970 |
|
rotate_cluster(c); |
971 |
|
} |
972 |
|
} |
973 |
|
} |
974 |
|
} |
975 |
|
|
976 |
|
void PuzzleStone::message(const string& msg, const Value &) { |
977 |
|
if (msg == "init") { // shuffle at level startup |
978 |
|
int count = options::Difficulty == DIFFICULTY_EASY |
979 |
|
? 1 |
980 |
|
: int(5*(rand()/(RAND_MAX+1.0))); |
981 |
|
|
982 |
|
shuffle(count); |
983 |
|
} |
984 |
|
} |
985 |
|
|
986 |
|
void |
987 |
PuzzleStone::on_impulse(const Impulse& impulse) |
PuzzleStone::on_impulse(const Impulse& impulse) |
988 |
{ |
{ |
989 |
Cluster c; |
if (!oxyd1_compatible()) { |
990 |
bool is_complete = find_cluster(c); |
Cluster c; |
991 |
maybe_move_cluster(c, is_complete, impulse.dir); |
bool is_complete = find_cluster(c); |
992 |
|
maybe_move_cluster(c, is_complete, impulse.dir); |
993 |
|
} |
994 |
} |
} |
995 |
|
|
996 |
|
|
1001 |
if (get_connections() == NODIRBIT) |
if (get_connections() == NODIRBIT) |
1002 |
return; |
return; |
1003 |
|
|
1004 |
Direction move_dir=get_push_direction(sc); |
bool compatible = oxyd1_compatible(); |
1005 |
|
|
1006 |
if (wielded_item_is(sc.actor, "it-magicwand")) { |
if (compatible) { |
1007 |
if (cluster_complete()) { |
Cluster c; |
1008 |
// make all cluster stones explode |
if (find_cluster(c)) { |
1009 |
|
// make complete cluster explode: |
1010 |
|
Cluster::iterator e = c.end(); |
1011 |
|
for (Cluster::iterator i = c.begin(); i != e; ++i) { |
1012 |
|
KillStone(*i); |
1013 |
|
SetItem(*i, MakeItem("it-explosion1")); |
1014 |
|
} |
1015 |
|
play_sound("explosion1"); |
1016 |
return; |
return; |
1017 |
} |
} |
1018 |
else if (contact_face(sc) != NODIR) { // not touching a corner |
} |
1019 |
Cluster c; |
|
1020 |
|
if (compatible || wielded_item_is(sc.actor, "it-magicwand")) { |
1021 |
|
Direction face = contact_face(sc); |
1022 |
|
if (face != NODIR) { // not touching a corner |
1023 |
|
Direction move_dir = reverse(face); |
1024 |
|
Cluster c; |
1025 |
find_row_or_column_cluster(c, get_pos(), move_dir); |
find_row_or_column_cluster(c, get_pos(), move_dir); |
1026 |
|
|
1027 |
if (move_dir == NODIR) // not enough impulse to move the cluster |
if (c.size() == 1) { |
1028 |
|
if (!compatible) { |
1029 |
|
// if cluster contains single stone -> move it if |
1030 |
|
// dest pos is free |
1031 |
|
GridPos dest = move(c[0], move_dir); |
1032 |
|
if (GetStone(dest) == 0) { |
1033 |
|
// check speed. here a lower speed than in |
1034 |
|
// get_push_direction() is enough. |
1035 |
|
ActorInfo *ai = sc.actor->get_actorinfo(); |
1036 |
|
if (ai->vel * sc.normal < -2) { |
1037 |
|
|
1038 |
|
Stone *puzz = YieldStone(c[0]); |
1039 |
|
SetStone(dest, puzz); |
1040 |
|
} |
1041 |
|
} |
1042 |
|
} |
1043 |
|
} |
1044 |
|
else { // otherwise rotate cluster |
1045 |
rotate_cluster(c); |
rotate_cluster(c); |
1046 |
else |
} |
|
; // TODO: move the incomplete cluster |
|
1047 |
} |
} |
1048 |
} |
} |
1049 |
else if (move_dir != NODIR) { |
else { |
1050 |
sc.actor->send_impulse(get_pos(), move_dir); |
Direction move_dir = get_push_direction(sc); |
1051 |
|
if (move_dir != NODIR) { // normal push |
1052 |
|
sc.actor->send_impulse(get_pos(), move_dir); |
1053 |
|
} |
1054 |
} |
} |
1055 |
} |
} |
1056 |
|
|
2467 |
|
|
2468 |
Register (new PullStone); |
Register (new PullStone); |
2469 |
|
|
2470 |
Register(new PuzzleStone); |
// PerOxyd/Enigma compatible puzzle stones: |
2471 |
Register("st-puzzle-hollow", new PuzzleStone(1)); |
Register(new PuzzleStone(0, false)); |
2472 |
Register("st-puzzle-n", new PuzzleStone(9)); |
Register("st-puzzle-hollow", new PuzzleStone(1, false)); |
2473 |
Register("st-puzzle-e", new PuzzleStone(5)); |
Register("st-puzzle-n", new PuzzleStone(9, false)); |
2474 |
Register("st-puzzle-s", new PuzzleStone(3)); |
Register("st-puzzle-e", new PuzzleStone(5, false)); |
2475 |
Register("st-puzzle-w", new PuzzleStone(2)); |
Register("st-puzzle-s", new PuzzleStone(3, false)); |
2476 |
Register("st-puzzle-ne", new PuzzleStone(13)); |
Register("st-puzzle-w", new PuzzleStone(2, false)); |
2477 |
Register("st-puzzle-nw", new PuzzleStone(10)); |
Register("st-puzzle-ne", new PuzzleStone(13, false)); |
2478 |
Register("st-puzzle-es", new PuzzleStone(7)); |
Register("st-puzzle-nw", new PuzzleStone(10, false)); |
2479 |
Register("st-puzzle-sw", new PuzzleStone(4)); |
Register("st-puzzle-es", new PuzzleStone(7, false)); |
2480 |
Register("st-puzzle-ns", new PuzzleStone(11)); |
Register("st-puzzle-sw", new PuzzleStone(4, false)); |
2481 |
Register("st-puzzle-ew", new PuzzleStone(6)); |
Register("st-puzzle-ns", new PuzzleStone(11, false)); |
2482 |
Register("st-puzzle-nes", new PuzzleStone(15)); |
Register("st-puzzle-ew", new PuzzleStone(6, false)); |
2483 |
Register("st-puzzle-new", new PuzzleStone(14)); |
Register("st-puzzle-nes", new PuzzleStone(15, false)); |
2484 |
Register("st-puzzle-nsw", new PuzzleStone(12)); |
Register("st-puzzle-new", new PuzzleStone(14, false)); |
2485 |
Register("st-puzzle-esw", new PuzzleStone(8)); |
Register("st-puzzle-nsw", new PuzzleStone(12, false)); |
2486 |
Register("st-puzzle-nesw", new PuzzleStone(16)); |
Register("st-puzzle-esw", new PuzzleStone(8, false)); |
2487 |
|
Register("st-puzzle-nesw", new PuzzleStone(16, false)); |
2488 |
|
|
2489 |
|
// Oxyd1 compatible puzzle stones: |
2490 |
|
Register("st-puzzle2-hollow", new PuzzleStone(1, true)); |
2491 |
|
Register("st-puzzle2-n", new PuzzleStone(9, true)); |
2492 |
|
Register("st-puzzle2-e", new PuzzleStone(5, true)); |
2493 |
|
Register("st-puzzle2-s", new PuzzleStone(3, true)); |
2494 |
|
Register("st-puzzle2-w", new PuzzleStone(2, true)); |
2495 |
|
Register("st-puzzle2-ne", new PuzzleStone(13, true)); |
2496 |
|
Register("st-puzzle2-nw", new PuzzleStone(10, true)); |
2497 |
|
Register("st-puzzle2-es", new PuzzleStone(7, true)); |
2498 |
|
Register("st-puzzle2-sw", new PuzzleStone(4, true)); |
2499 |
|
Register("st-puzzle2-ns", new PuzzleStone(11, true)); |
2500 |
|
Register("st-puzzle2-ew", new PuzzleStone(6, true)); |
2501 |
|
Register("st-puzzle2-nes", new PuzzleStone(15, true)); |
2502 |
|
Register("st-puzzle2-new", new PuzzleStone(14, true)); |
2503 |
|
Register("st-puzzle2-nsw", new PuzzleStone(12, true)); |
2504 |
|
Register("st-puzzle2-esw", new PuzzleStone(8, true)); |
2505 |
|
Register("st-puzzle2-nesw", new PuzzleStone(16, true)); |
2506 |
|
|
2507 |
Register (new RotatorStone (true)); |
Register (new RotatorStone (true)); |
2508 |
Register (new RotatorStone (false)); |
Register (new RotatorStone (false)); |