2650 |
} |
} |
2651 |
|
|
2652 |
void |
void |
2653 |
ShogunStone::on_impulse(Direction dir) |
ShogunStone::on_impulse(Direction dir) |
2654 |
{ |
{ |
2655 |
if (transfer_smallest_hole(move(get_pos(), dir))) |
if (transfer_smallest_hole(move(get_pos(), dir))) |
2656 |
play_sound("st-move"); |
play_sound("st-move"); |
3809 |
void remove_arms (DirectionBits arms); |
void remove_arms (DirectionBits arms); |
3810 |
|
|
3811 |
void animcb() { set_model(model()); } |
void animcb() { set_model(model()); } |
3812 |
void warpActorsInRange(bool clockwise); |
void handleActorsAndItems(bool clockwise); |
3813 |
|
|
3814 |
virtual const char *model() const = 0; |
virtual const char *model() const = 0; |
3815 |
virtual const char *anim() const = 0; |
virtual const char *anim() const = 0; |
3900 |
}; |
}; |
3901 |
} |
} |
3902 |
|
|
3903 |
void |
// ------------------------------------- |
3904 |
Turnstile_Pivot_Base::warpActorsInRange(bool clockwise) { |
// Turnstile_Arm implementation |
3905 |
// the turnstile itself already has been rotated |
// ------------------------------------- |
|
// now handle all actors in Range |
|
|
|
|
|
GridPos pv_pos = get_pos(); |
|
|
V2 pv_center(pv_pos.x+.5, pv_pos.y+.5); |
|
|
|
|
|
const int to_index[3][3] = { |
|
|
{ 0, 7, 6 }, // x == 0 |
|
|
{ 1,-1, 5 }, // x == 1 |
|
|
{ 2, 3, 4 } // x == 2 |
|
|
}; |
|
|
|
|
|
// for each field calculate whether an arm has passed by: |
|
|
bool arm_seen[8]; |
|
|
{ |
|
|
const DirectionBits neededArm[2][8] = { |
|
|
// anticlockwise : |
|
|
{ WESTBIT, NORTHBIT, NORTHBIT, EASTBIT, EASTBIT, SOUTHBIT, SOUTHBIT, WESTBIT }, |
|
|
// clockwise : |
|
|
{ NORTHBIT, NORTHBIT, EASTBIT, EASTBIT, SOUTHBIT, SOUTHBIT, WESTBIT, WESTBIT } |
|
|
}; |
|
|
DirectionBits arms = arms_present(); // already the rotated state |
|
|
assert(arms); // pivots w/o arms should not rotate |
|
|
|
|
|
for (int i = 0; i<8; ++i) { |
|
|
arm_seen[i] = arms & neededArm[clockwise][i]; |
|
|
} |
|
|
} |
|
|
|
|
|
vector<Actor*> actorsInRange; |
|
|
|
|
|
// tested range is sqrt(sqr(1.5)+sqr(0.5)) ( = radius hit by turnstile) + 19/64 ( = max. actor radius) |
|
|
if (GetActorsInRange(pv_center, 1.879, actorsInRange)) { |
|
|
for (vector<Actor*>::iterator ac = actorsInRange.begin(); ac != actorsInRange.end(); ++ac) { |
|
|
const V2& ac_center = (*ac)->get_pos(); |
|
|
GridPos ac_pos(ac_center); |
|
|
int dx = ac_pos.x-pv_pos.x; |
|
|
int dy = ac_pos.y-pv_pos.y; |
|
|
|
|
|
if (dx<-1 || dx>1 || dy<-1 || dy>1) { // actor is outside of turnstile |
|
|
// @@@ FIXME: test if actor was hit by turnstile -> set actor speed |
|
|
} |
|
|
else { // actor is inside the turnstile |
|
|
const int to_x[8] = { -1, 0, 1, 1, 1, 0, -1, -1 }; |
|
|
const int to_y[8] = { -1, -1, -1, 0, 1, 1, 1, 0 }; |
|
|
|
|
|
int idx_source = to_index[dx+1][dy+1]; |
|
|
assert(idx_source != -1); // actor inside pivot -- impossible |
|
|
|
|
|
const int rot_index[4][8] = { |
|
|
{ 6, 0, 0, 2, 2, 4, 4, 6 }, // anticlockwise |
|
|
{ 2, 2, 4, 4, 6, 6, 0, 0 }, // clockwise |
|
|
{ 6, -1, 0, -1, 2, -1, 4, -1 }, // anticlockwise (oxyd-compatible) |
|
|
{ 2, -1, 4, -1, 6, -1, 0, -1 }, // clockwise (oxyd-compatible) |
|
|
}; |
|
|
|
|
|
bool compatible = oxyd_compatible(); |
|
|
int idx_target = rot_index[clockwise+2*compatible][idx_source]; // destination index |
|
|
|
|
|
enum { STAY, WARP, SHATTER } action = STAY; |
|
|
|
|
|
if (compatible) { |
|
|
bool is_impulse_sender = (*ac == dynamic_cast<Actor*>(ImpulseSender())); |
|
|
|
|
|
if (is_impulse_sender) { |
|
|
action = WARP; |
|
|
} |
|
|
else { // other actors |
|
|
if (arm_seen[idx_source]) { // this actor has been hit by an arm |
|
|
action = SHATTER; |
|
|
} |
|
|
} |
|
|
} |
|
|
else { // not oxyd-compatible |
|
|
if (arm_seen[idx_source]) { |
|
|
action = WARP; |
|
|
} |
|
|
// @@@ FIXME: shattered actors are warped |
|
|
} |
|
|
|
|
|
switch (action) { |
|
|
case WARP: { |
|
|
GridPos ac_target_pos(pv_pos.x+to_x[idx_target], pv_pos.y+to_y[idx_target]); |
|
|
world::WarpActor(*ac, ac_target_pos.x+.5, ac_target_pos.y+.5); |
|
|
|
|
|
Stone *st = GetStone(ac_target_pos); |
|
|
if (!st) break; |
|
|
|
|
|
// destination is blocked |
|
|
|
|
|
Turnstile_Arm *arm = dynamic_cast<Turnstile_Arm*>(st); |
|
|
if (arm && !compatible) { // if blocking stone is turnstile arm -> hit it! |
|
|
const int impulse_dir[2][8] = { |
|
|
// anticlockwise |
|
|
{ SOUTHBIT|WESTBIT, WESTBIT, NORTHBIT|WESTBIT, NORTHBIT, |
|
|
NORTHBIT|EASTBIT, EASTBIT, SOUTHBIT|EASTBIT, SOUTHBIT }, |
|
|
// clockwise |
|
|
{ NORTHBIT|EASTBIT, EASTBIT, SOUTHBIT|EASTBIT, SOUTHBIT, |
|
|
SOUTHBIT|WESTBIT, WESTBIT, NORTHBIT|WESTBIT, NORTHBIT } |
|
|
}; |
|
|
|
|
|
DirectionBits possible_impulses = |
|
|
static_cast<DirectionBits>(impulse_dir[clockwise][idx_target]); |
|
|
|
|
|
for (int d = 0; d<4; ++d) { |
|
|
if (has_dir(possible_impulses, Direction(d))) { |
|
|
SendImpulse(ac_target_pos, Direction(d), *ac); |
|
|
} |
|
|
} |
|
|
|
|
|
if (GetStone(ac_target_pos) == 0) { // arm disappeared |
|
|
break; |
|
|
} |
|
|
} |
|
|
// fall-through (if arm didn't rotate or other stone was blocking) |
|
|
} |
|
|
case SHATTER: |
|
|
SendMessage(*ac, "shatter"); |
|
|
break; |
|
|
} |
|
|
|
|
|
// @@@ FIXME: it's possible that two actors are moved to the same destination field. |
|
|
// In that case the second actor is put on top of the first actor |
|
|
// (happens only in non-oxyd-compat-mode with three balls or pullers/impulsestones) |
|
|
// |
|
|
// Note: With black and whiteball it's normally no problem, |
|
|
// because when one of the actors was moving, it's looking natural. |
|
|
// Problems occur when small balls come into play. |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
// @@@ FIXME: handle items in range (using on_stonehit()) |
|
|
} |
|
3906 |
|
|
3907 |
void |
void |
3908 |
Turnstile_Arm::on_impulse(Direction dir) { |
Turnstile_Arm::on_impulse(Direction dir) { |
3939 |
} |
} |
3940 |
} |
} |
3941 |
|
|
3942 |
#if 0 // old code : |
// -------------------------------------------- |
3943 |
|
// Turnstile_Pivot_Base implementation |
3944 |
void |
// -------------------------------------------- |
|
Turnstile_Arm::actor_hit(const StoneContact &sc) |
|
|
{ |
|
|
Turnstile_Pivot_Base *pivot = get_pivot(); |
|
|
|
|
|
enum Action { ROTL, ROTR, stay }; |
|
|
static Action actions[4][4] = { |
|
|
{ stay, ROTL, stay, ROTR }, // west arm |
|
|
{ ROTR, stay, ROTL, stay }, // south arm |
|
|
{ stay, ROTR, stay, ROTL }, // east arm |
|
|
{ ROTL, stay, ROTR, stay } // north arm |
|
|
}; |
|
|
|
|
|
ActorInfo *ai = sc.actor->get_actorinfo(); |
|
|
Direction dir = reverse(contact_face(sc)); |
|
|
if (dir!=enigma::NODIR && ai->vel * sc.normal < -4) |
|
|
{ |
|
|
if (pivot) { |
|
|
Action a = actions[get_dir()][dir]; |
|
|
GridPos pos = move(get_pos(), dir); |
|
|
V2 destpos (pos.x + 0.5, pos.y + 0.5); |
|
|
if (a == ROTL) { |
|
|
if (pivot->rotate_left ()) |
|
|
world::WarpActor (sc.actor, destpos[0], destpos[1]); |
|
|
} |
|
|
else if (a == ROTR) { |
|
|
if (pivot->rotate_right ()) |
|
|
world::WarpActor (sc.actor, destpos[0], destpos[1]); |
|
|
} |
|
|
} |
|
|
else { |
|
|
// Move arms not attached to a pivot individually |
|
|
MaybeMoveStone(get_pos(), dir); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
#endif // old code |
|
3945 |
|
|
3946 |
DirectionBits |
DirectionBits |
3947 |
Turnstile_Pivot_Base::arms_present() |
Turnstile_Pivot_Base::arms_present() |
4029 |
if (can_rotate) { |
if (can_rotate) { |
4030 |
// play_sound("rotate-left"); |
// play_sound("rotate-left"); |
4031 |
play_sound("st-magic"); |
play_sound("st-magic"); |
|
// set_anim("st-turnstile-anim"); |
|
4032 |
set_anim(anim()); |
set_anim(anim()); |
4033 |
|
|
4034 |
remove_arms(arms); |
remove_arms(arms); |
4035 |
set_arms(rotate(arms, false)); |
set_arms(rotate(arms, false)); |
4036 |
warpActorsInRange(false); |
handleActorsAndItems(false); |
4037 |
|
|
4038 |
player::IncMoveCounter(); |
player::IncMoveCounter(); |
4039 |
} |
} |
4066 |
if (can_rotate) { |
if (can_rotate) { |
4067 |
// play_sound("rotate-right"); |
// play_sound("rotate-right"); |
4068 |
play_sound("st-magic"); |
play_sound("st-magic"); |
|
// set_anim("st-turnstile-anim"); |
|
4069 |
set_anim(anim()); |
set_anim(anim()); |
4070 |
|
|
4071 |
remove_arms(arms); |
remove_arms(arms); |
4072 |
set_arms(rotate(arms, true)); |
set_arms(rotate(arms, true)); |
4073 |
warpActorsInRange(true); |
handleActorsAndItems(true); |
4074 |
|
|
4075 |
player::IncMoveCounter(); |
player::IncMoveCounter(); |
4076 |
} |
} |
4077 |
return can_rotate; |
return can_rotate; |
4078 |
} |
} |
4079 |
|
|
4080 |
|
void |
4081 |
|
Turnstile_Pivot_Base::handleActorsAndItems(bool clockwise) { |
4082 |
|
GridPos pv_pos = get_pos(); |
4083 |
|
V2 pv_center(pv_pos.x+.5, pv_pos.y+.5); |
4084 |
|
|
4085 |
|
const int to_index[3][3] = { |
4086 |
|
{ 0, 7, 6 }, // x == 0 |
4087 |
|
{ 1,-1, 5 }, // x == 1 |
4088 |
|
{ 2, 3, 4 } // x == 2 |
4089 |
|
}; |
4090 |
|
const int to_x[8] = { -1, 0, 1, 1, 1, 0, -1, -1 }; |
4091 |
|
const int to_y[8] = { -1, -1, -1, 0, 1, 1, 1, 0 }; |
4092 |
|
|
4093 |
|
// for each field calculate whether an arm has passed by: |
4094 |
|
bool arm_seen[8]; |
4095 |
|
{ |
4096 |
|
const DirectionBits neededArm[2][8] = { |
4097 |
|
// anticlockwise : |
4098 |
|
{ WESTBIT, NORTHBIT, NORTHBIT, EASTBIT, EASTBIT, SOUTHBIT, SOUTHBIT, WESTBIT }, |
4099 |
|
// clockwise : |
4100 |
|
{ NORTHBIT, NORTHBIT, EASTBIT, EASTBIT, SOUTHBIT, SOUTHBIT, WESTBIT, WESTBIT } |
4101 |
|
}; |
4102 |
|
DirectionBits arms = arms_present(); // already the rotated state |
4103 |
|
assert(arms); // pivots w/o arms should not rotate |
4104 |
|
|
4105 |
|
for (int i = 0; i<8; ++i) { |
4106 |
|
arm_seen[i] = arms & neededArm[clockwise][i]; |
4107 |
|
} |
4108 |
|
} |
4109 |
|
|
4110 |
|
// The turnstile itself already has been rotated. |
4111 |
|
|
4112 |
|
// Handle items in range : |
4113 |
|
for (int i = 0; i<8; ++i) { |
4114 |
|
if (arm_seen[i]) { |
4115 |
|
GridPos pos(pv_pos.x+to_x[i], pv_pos.y+to_y[i]); |
4116 |
|
if (Item *it = GetItem(pos)) it->on_stonehit(this); // hit with pivot (shouldn't matter) |
4117 |
|
} |
4118 |
|
} |
4119 |
|
|
4120 |
|
// Now handle all actors in range : |
4121 |
|
vector<Actor*> actorsInRange; |
4122 |
|
|
4123 |
|
// tested range is sqrt(sqr(1.5)+sqr(0.5)) ( = radius hit by turnstile) + 19/64 ( = max. actor radius) |
4124 |
|
if (GetActorsInRange(pv_center, 1.879, actorsInRange)) { |
4125 |
|
for (vector<Actor*>::iterator ac = actorsInRange.begin(); ac != actorsInRange.end(); ++ac) { |
4126 |
|
const V2& ac_center = (*ac)->get_pos(); |
4127 |
|
GridPos ac_pos(ac_center); |
4128 |
|
int dx = ac_pos.x-pv_pos.x; |
4129 |
|
int dy = ac_pos.y-pv_pos.y; |
4130 |
|
|
4131 |
|
if (dx<-1 || dx>1 || dy<-1 || dy>1) { // actor is outside of turnstile |
4132 |
|
// @@@ FIXME: test if actor was hit by turnstile -> set actor speed |
4133 |
|
} |
4134 |
|
else { // actor is inside the turnstile |
4135 |
|
int idx_source = to_index[dx+1][dy+1]; |
4136 |
|
assert(idx_source != -1); // actor inside pivot -- impossible |
4137 |
|
|
4138 |
|
const int rot_index[4][8] = { |
4139 |
|
{ 6, 0, 0, 2, 2, 4, 4, 6 }, // anticlockwise |
4140 |
|
{ 2, 2, 4, 4, 6, 6, 0, 0 }, // clockwise |
4141 |
|
{ 6, -1, 0, -1, 2, -1, 4, -1 }, // anticlockwise (oxyd-compatible) |
4142 |
|
{ 2, -1, 4, -1, 6, -1, 0, -1 }, // clockwise (oxyd-compatible) |
4143 |
|
}; |
4144 |
|
|
4145 |
|
bool compatible = oxyd_compatible(); |
4146 |
|
int idx_target = rot_index[clockwise+2*compatible][idx_source]; // destination index |
4147 |
|
|
4148 |
|
enum { STAY, WARP, SHATTER } action = STAY; |
4149 |
|
|
4150 |
|
if (compatible) { |
4151 |
|
bool is_impulse_sender = (*ac == dynamic_cast<Actor*>(ImpulseSender())); |
4152 |
|
|
4153 |
|
if (is_impulse_sender) { |
4154 |
|
action = WARP; |
4155 |
|
} |
4156 |
|
else { // other actors |
4157 |
|
if (arm_seen[idx_source]) { // this actor has been hit by an arm |
4158 |
|
action = SHATTER; |
4159 |
|
} |
4160 |
|
} |
4161 |
|
} |
4162 |
|
else { // not oxyd-compatible |
4163 |
|
if (arm_seen[idx_source]) { |
4164 |
|
action = WARP; |
4165 |
|
} |
4166 |
|
// @@@ FIXME: shattered actors are warped |
4167 |
|
} |
4168 |
|
|
4169 |
|
switch (action) { |
4170 |
|
case WARP: { |
4171 |
|
GridPos ac_target_pos(pv_pos.x+to_x[idx_target], pv_pos.y+to_y[idx_target]); |
4172 |
|
world::WarpActor(*ac, ac_target_pos.x+.5, ac_target_pos.y+.5); |
4173 |
|
|
4174 |
|
Stone *st = GetStone(ac_target_pos); |
4175 |
|
if (!st) break; |
4176 |
|
|
4177 |
|
// destination is blocked |
4178 |
|
|
4179 |
|
Turnstile_Arm *arm = dynamic_cast<Turnstile_Arm*>(st); |
4180 |
|
if (arm && !compatible) { // if blocking stone is turnstile arm -> hit it! |
4181 |
|
const int impulse_dir[2][8] = { |
4182 |
|
// anticlockwise |
4183 |
|
{ SOUTHBIT|WESTBIT, WESTBIT, NORTHBIT|WESTBIT, NORTHBIT, |
4184 |
|
NORTHBIT|EASTBIT, EASTBIT, SOUTHBIT|EASTBIT, SOUTHBIT }, |
4185 |
|
// clockwise |
4186 |
|
{ NORTHBIT|EASTBIT, EASTBIT, SOUTHBIT|EASTBIT, SOUTHBIT, |
4187 |
|
SOUTHBIT|WESTBIT, WESTBIT, NORTHBIT|WESTBIT, NORTHBIT } |
4188 |
|
}; |
4189 |
|
|
4190 |
|
DirectionBits possible_impulses = |
4191 |
|
static_cast<DirectionBits>(impulse_dir[clockwise][idx_target]); |
4192 |
|
|
4193 |
|
for (int d = 0; d<4; ++d) { |
4194 |
|
if (has_dir(possible_impulses, Direction(d))) { |
4195 |
|
SendImpulse(ac_target_pos, Direction(d), *ac); |
4196 |
|
} |
4197 |
|
} |
4198 |
|
|
4199 |
|
if (GetStone(ac_target_pos) == 0) { // arm disappeared |
4200 |
|
break; |
4201 |
|
} |
4202 |
|
} |
4203 |
|
// fall-through (if arm didn't rotate or other stone was blocking) |
4204 |
|
} |
4205 |
|
case SHATTER: { |
4206 |
|
SendMessage(*ac, "shatter"); |
4207 |
|
break; |
4208 |
|
} |
4209 |
|
} |
4210 |
|
|
4211 |
|
// @@@ FIXME: it's possible that two actors are moved to the same destination field. |
4212 |
|
// In that case the second actor is put on top of the first actor |
4213 |
|
// (happens only in non-oxyd-compat-mode with three balls or pullers/impulsestones) |
4214 |
|
// |
4215 |
|
// Note: With black and whiteball it's normally no problem, |
4216 |
|
// because when one of the actors was moving, it's looking natural. |
4217 |
|
// Problems occur when small balls come into play. |
4218 |
|
} |
4219 |
|
} |
4220 |
|
} |
4221 |
|
|
4222 |
|
} |
4223 |
|
|
4224 |
//---------------------------------------- |
//---------------------------------------- |
4225 |
// EasyModeStone |
// EasyModeStone |
4226 |
// |
// |