261 |
return stone->get_pos().center(); |
return stone->get_pos().center(); |
262 |
} |
} |
263 |
|
|
|
|
|
|
|
|
264 |
//---------------------------------------- |
//---------------------------------------- |
265 |
// Field |
// Field |
266 |
//---------------------------------------- |
//---------------------------------------- |
322 |
StoneContact::StoneContact(Actor *a, GridPos p, |
StoneContact::StoneContact(Actor *a, GridPos p, |
323 |
const V2 &cp, const V2 &n) |
const V2 &cp, const V2 &n) |
324 |
: actor(a), stonepos(p), |
: actor(a), stonepos(p), |
325 |
response(STONE_REBOUND), |
response(STONE_PASS), |
326 |
contact_point(cp), |
contact_point(cp), |
327 |
normal(n), |
normal(n), |
328 |
is_collision(false), |
is_collision(false), |
329 |
fake_collision(false), |
fake_collision(false), |
330 |
|
new_collision(false), |
331 |
is_contact(true) |
is_contact(true) |
332 |
{} |
{} |
333 |
|
|
334 |
StoneContact::StoneContact() |
StoneContact::StoneContact() |
335 |
: is_collision(false), |
: is_collision(false), |
336 |
fake_collision(false), |
fake_collision(false), |
337 |
|
new_collision(false), |
338 |
is_contact(false) |
is_contact(false) |
339 |
{} |
{} |
340 |
|
|
519 |
// PUZZLE SCRAMBLING |
// PUZZLE SCRAMBLING |
520 |
//====================================================================== |
//====================================================================== |
521 |
|
|
522 |
void WorldImpl::add_scramble(GridPos p, Direction dir) { |
void WorldImpl::add_scramble(GridPos p, Direction dir) |
523 |
|
{ |
524 |
// fprintf(stderr, "scrambles.size()=%i ; add_scramble(%i, %i, %s)\n", scrambles.size(), p.x, p.y, to_suffix(dir).c_str()); |
// fprintf(stderr, "scrambles.size()=%i ; add_scramble(%i, %i, %s)\n", scrambles.size(), p.x, p.y, to_suffix(dir).c_str()); |
525 |
scrambles.push_back(Scramble(p, dir, scrambleIntensity)); |
scrambles.push_back(Scramble(p, dir, scrambleIntensity)); |
526 |
} |
} |
527 |
void WorldImpl::scramble_puzzles() { |
|
528 |
|
void WorldImpl::scramble_puzzles() |
529 |
|
{ |
530 |
// fprintf(stderr, "scramble_puzzles: scrambles.size()=%i\n", scrambles.size()); |
// fprintf(stderr, "scramble_puzzles: scrambles.size()=%i\n", scrambles.size()); |
531 |
while (!scrambles.empty()) { |
while (!scrambles.empty()) { |
532 |
list<Scramble>::iterator i = scrambles.begin(); |
list<Scramble>::iterator i = scrambles.begin(); |
620 |
} |
} |
621 |
|
|
622 |
ActorInfo *ai = a->get_actorinfo(); |
ActorInfo *ai = a->get_actorinfo(); |
623 |
f += ai->forceacc; |
f += ai->force; |
624 |
|
|
625 |
return f / ai->mass; |
return f / ai->mass; |
626 |
} |
} |
650 |
with information about the closest feature on the stone and |
with information about the closest feature on the stone and |
651 |
`is_contact' is set to false. */ |
`is_contact' is set to false. */ |
652 |
static void |
static void |
653 |
find_contact_with_stone(Actor *a, StoneContact &contact, int x, int y) |
find_contact_with_stone (Actor *a, StoneContact &contact, int x, int y) |
654 |
{ |
{ |
655 |
contact.actor = a; |
contact.actor = a; |
656 |
contact.stonepos = GridPos(x, y); |
contact.stonepos = GridPos(x, y); |
657 |
contact.is_contact = false; |
contact.is_contact = false; |
658 |
|
|
659 |
Stone *stone = world::GetStone(contact.stonepos); |
Stone *stone = world::GetStone(contact.stonepos); |
713 |
contact.normal = normalize(b); |
contact.normal = normalize(b); |
714 |
contact.contact_point = corner + contact.normal*erad; |
contact.contact_point = corner + contact.normal*erad; |
715 |
} |
} |
716 |
|
|
717 |
contact.is_collision = contact.normal*ai.vel < 0; |
contact.is_collision = contact.normal*ai.vel < 0; |
718 |
contact.response = stone->collision_response(contact); |
contact.response = stone->collision_response(contact); |
719 |
contact.sound = stone->collision_sound(); |
contact.sound = stone->collision_sound(); |
721 |
|
|
722 |
/* Enigma's stones have rounded corners; this leads to realistic |
/* Enigma's stones have rounded corners; this leads to realistic |
723 |
behaviour when a marble rebounds from the corner of a single stone. |
behaviour when a marble rebounds from the corner of a single stone. |
724 |
But it's annyoing when there are two adjacent stones and a marble |
But it's annoying when there are two adjacent stones and a marble |
725 |
rebounds from any one (or even both) corners, because it changes |
rebounds from any one (or even both) corners, because it changes |
726 |
the direction of the marble quite unpredictably. |
the direction of the marble quite unpredictably. |
727 |
|
|
796 |
these functions. (Incidentally, this is why StoneContact only |
these functions. (Incidentally, this is why StoneContact only |
797 |
contains a 'stonepos' and no 'stone' entry.) |
contains a 'stonepos' and no 'stone' entry.) |
798 |
*/ |
*/ |
799 |
static V2 |
static void |
800 |
handle_stone_contact(V2 normal, StoneContact &sc) |
handle_stone_contact (StoneContact &sc, double total_dtime) |
801 |
{ |
{ |
802 |
Actor *a = sc.actor; |
Actor *a = sc.actor; |
803 |
ActorInfo *ai = a->get_actorinfo(); |
ActorInfo &ai = *a->get_actorinfo(); |
804 |
|
|
805 |
if (sc.is_collision) { |
if (sc.is_collision) { |
806 |
if (Stone *stone = world::GetStone(sc.stonepos)) |
if (!sc.fake_collision && sc.response == STONE_REBOUND) { |
807 |
stone->actor_hit(sc); |
|
808 |
if (!sc.fake_collision && sc.response != STONE_PASS) { |
bool new_collision = false; |
809 |
normal += sc.normal; |
if (total_dtime != 0) { |
810 |
normal /= length(normal); |
double vn = (ai.pos - ai.last_pos)/total_dtime * sc.normal; |
811 |
|
new_collision = (vn < -0.5); |
812 |
if (length(ai->vel) > 0.1) { |
} |
813 |
|
|
814 |
|
if (new_collision) { |
815 |
|
if (Stone *stone = world::GetStone(sc.stonepos)) |
816 |
|
stone->actor_hit(sc); |
817 |
|
// if (length(ai->vel) > 0.1) { |
818 |
display::AddEffect (sc.contact_point, "ring-anim"); |
display::AddEffect (sc.contact_point, "ring-anim"); |
819 |
sound::PlaySound(sc.sound.c_str(), sc.contact_point); |
sound::PlaySound(sc.sound.c_str(), sc.contact_point); |
820 |
|
// } |
821 |
} |
} |
822 |
|
double restitution = 0.8; |
823 |
|
ai.vel -= (1 + restitution)*(ai.vel*sc.normal)*sc.normal; |
824 |
|
ai.pos = ai.oldpos; // Reset actor position |
825 |
} |
} |
826 |
} |
} |
827 |
else if (sc.is_contact) { |
else if (sc.is_contact) { |
828 |
if (Stone *stone = world::GetStone(sc.stonepos)) |
if (Stone *stone = world::GetStone(sc.stonepos)) |
829 |
stone->actor_contact(sc.actor); |
stone->actor_contact(sc.actor); |
830 |
} |
} |
|
return normal; |
|
831 |
} |
} |
832 |
|
|
833 |
static void |
static void |
834 |
handle_contacts(Actor *a) |
handle_contacts(Actor *a, double total_dtime) |
835 |
{ |
{ |
836 |
ActorInfo &a1 = *a->get_actorinfo(); |
ActorInfo &a1 = *a->get_actorinfo(); |
837 |
|
|
840 |
*/ |
*/ |
841 |
StoneContactList cl; |
StoneContactList cl; |
842 |
find_stone_contacts(a, cl); |
find_stone_contacts(a, cl); |
843 |
if (!cl.empty()) { |
for (StoneContactList::iterator i=cl.begin(); |
844 |
V2 normal = accumulate(cl.begin(), cl.end(), V2(), handle_stone_contact); |
i != cl.end(); ++i) |
845 |
|
{ |
846 |
double n = length(normal); |
handle_stone_contact (*i, total_dtime); |
|
if (n != 0) { |
|
|
double restitution = 0.8; |
|
|
normal /= n; |
|
|
a1.vel -= (1 + restitution)*(a1.vel*normal)*normal; |
|
|
} |
|
847 |
} |
} |
848 |
|
|
849 |
/* |
/* |
865 |
bool contactp = length(p1-p2) < r1+r2; |
bool contactp = length(p1-p2) < r1+r2; |
866 |
if (contactp) |
if (contactp) |
867 |
{ |
{ |
868 |
V2 n = normalize(p1-p2); // normal to contact surface |
V2 n = normalize(p1-p2); // normal to contact surface |
869 |
V2 v1 = n * (a1.vel*n); |
V2 v1 = n * (a1.vel*n); |
870 |
V2 v2 = n * (a2.vel*n); |
V2 v2 = n * (a2.vel*n); |
871 |
|
|
872 |
|
a1.pos = a1.oldpos; // Reset actor position |
873 |
|
|
874 |
bool collisionp = (v2-v1)*(p2-p1) < 0; |
bool collisionp = (v2-v1)*(p2-p1) < 0; |
875 |
if (collisionp) { |
if (collisionp) { |
876 |
double m2 = a2.mass; |
double m2 = a2.mass; |
893 |
tick_actor(Actor *a, double dtime) |
tick_actor(Actor *a, double dtime) |
894 |
{ |
{ |
895 |
ActorInfo &ai = * a->get_actorinfo(); |
ActorInfo &ai = * a->get_actorinfo(); |
896 |
|
|
897 |
|
ai.last_pos = ai.pos; |
898 |
ai.oldpos = ai.pos; |
ai.oldpos = ai.pos; |
899 |
ai.oldvel = ai.vel; |
ai.oldvel = ai.vel; |
900 |
|
|
901 |
double rest_time = dtime; |
double rest_time = dtime; |
902 |
double dt = 0.005; //dtime; |
double dt = 0.005; //dtime; |
903 |
|
|
904 |
handle_contacts(a); |
handle_contacts(a, 0.0); |
905 |
while (rest_time > 0 && !ai.grabbed) |
while (rest_time > 0 && !ai.grabbed) |
906 |
{ |
{ |
907 |
advance_actor(a, dtime-rest_time, dt); |
advance_actor(a, dtime-rest_time, dt); |
918 |
|
|
919 |
// ok, accept this step |
// ok, accept this step |
920 |
a->move(); |
a->move(); |
|
handle_contacts(a); |
|
|
|
|
921 |
rest_time -= dt; |
rest_time -= dt; |
922 |
|
|
923 |
|
handle_contacts(a, dtime-rest_time); |
924 |
|
|
925 |
ai.oldpos = ai.pos; |
ai.oldpos = ai.pos; |
926 |
ai.oldvel = ai.vel; |
ai.oldvel = ai.vel; |
927 |
|
|
1604 |
|
|
1605 |
timeaccu += dtime; |
timeaccu += dtime; |
1606 |
|
|
1607 |
while (timeaccu >= timestep) { |
while (timeaccu >= timestep) |
1608 |
|
{ |
1609 |
|
// Move actors |
1610 |
for (ActorList::iterator i=level->actorlist.begin(); i!=level->actorlist.end(); ++i) { |
for (ActorList::iterator i=level->actorlist.begin(); i!=level->actorlist.end(); ++i) { |
1611 |
ActorInfo *ai = (*i)->get_actorinfo(); |
ActorInfo *ai = (*i)->get_actorinfo(); |
1612 |
if (!ai->grabbed && !(*i)->is_dead()) |
if (!ai->grabbed && !(*i)->is_dead()) |
1613 |
{ |
{ |
1614 |
|
ai->force= ai->forceacc; |
1615 |
|
ai->forceacc = V2(); |
1616 |
tick_actor(*i, timestep); |
tick_actor(*i, timestep); |
1617 |
} |
} |
|
ai->forceacc = V2(); |
|
1618 |
} |
} |
1619 |
|
|
1620 |
/* Handle delayed impulses */ |
// Handle delayed impulses |
1621 |
for (ImpulseList::iterator i = delayed_impulses.begin(); i != delayed_impulses.end(); ) { |
for (ImpulseList::iterator i = delayed_impulses.begin(); i != delayed_impulses.end(); ) { |
1622 |
// should the impulse take effect now ? |
// should the impulse take effect now ? |
1623 |
if (i->tick(timestep)) { |
if (i->tick(timestep)) { |