Files
thepool/source/actor.cpp

673 lines
22 KiB
C++

#include "actor.h"
#include "jdraw.h"
#include "jinput.h"
#include "room.h"
namespace actor
{
uint8_t anims[2][4] = {
{0, 1, 0, 2},
{0, 1, 2, 3}
};
static uint8_t anim_frame=0;
actor_t *first = nullptr;
actor_t *dirty = nullptr;
actor_t *getFirst()
{
return first;
}
actor_t *create(const char *name, vec3_t p, vec3_t s, SDL_Rect r, SDL_Point o)
{
actor_t *act = (actor_t*)malloc(sizeof(actor_t));
strcpy(act->name, name);
act->pos = p;
act->size = s;
act->bmp_rect = r;
act->bmp_offset = o;
act->anim_cycle = act->orient = 0;
act->push = act->mov_push = PUSH_NONE;
act->below = act->above = nullptr;
act->prev = act->next = nullptr;
return act;
}
const bool check_2d_collision(actor_t *obj1, actor_t *obj2)
{
return (obj1->pos.x < obj2->pos.x + obj2->size.x) &&
(obj1->pos.x + obj1->size.x > obj2->pos.x ) &&
(obj1->pos.y < obj2->pos.y + obj2->size.y) &&
(obj1->pos.y + obj1->size.y > obj2->pos.y );
}
const bool is_above(actor_t *obj1, actor_t *obj2)
{
return check_2d_collision(obj1, obj2) && (obj1->pos.z==obj2->pos.z+obj2->size.z);
}
actor_t *any_above_me(actor_t *act)
{
actor_t *other = first;
while (other)
{
if (is_above(other, act)) return other;
other = other->next;
if (other == act) other = other->next;
}
other = dirty;
while (other)
{
if (is_above(other, act)) return other;
other = other->next;
if (other == act) other = other->next;
}
return nullptr;
}
actor_t *any_below_me(actor_t *act)
{
actor_t *other = first;
while (other)
{
if (is_above(act, other)) return other;
other = other->next;
if (other == act) other = other->next;
}
other = dirty;
while (other)
{
if (is_above(act, other)) return other;
other = other->next;
if (other == act) other = other->next;
}
return nullptr;
}
void setDirty(actor_t *act, const bool force)
{
if (act->prev==nullptr && act != first && !force) return;
if (act->prev) act->prev->next = act->next;
if (act->next) act->next->prev = act->prev;
if (act == first) first = act->next;
act->prev = nullptr;
act->next = dirty;
dirty = act;
}
const bool isInFront(actor_t *act1, actor_t *act2) {
if (act1->pos.x >= act2->pos.x+act2->size.x) { return true; }
else if (act2->pos.x >= act1->pos.x+act1->size.x) { return false; }
else if (act1->pos.y >= act2->pos.y+act2->size.y) { return true; }
else if (act2->pos.y >= act1->pos.y+act1->size.y) { return false; }
else if (act1->pos.z >= act2->pos.z+act2->size.z) { return true; }
else if (act2->pos.z >= act1->pos.z+act1->size.z) { return false; }
else { return false; }
}
void reorder()
{
anim_frame=(anim_frame+1)%4;
while (dirty)
{
if (first)
{
actor_t *current = first;
while (true)
{
if (isInFront(current, dirty))
{
dirty->prev = current->prev;
current->prev = dirty;
if (dirty->prev) dirty->prev->next = dirty;
dirty = dirty->next;
current->prev->next = current;
if (current==first) first=current->prev;
break;
}
else
{
if (current->next)
{
current = current->next;
}
else
{
current->next = dirty;
dirty = dirty->next;
current->next->prev = current;
current->next->next = nullptr;
break;
}
}
}
}
else
{
first = dirty;
dirty = dirty->next;
first->prev=first->next=nullptr;
}
}
}
void updateUserInput(actor_t *act)
{
vec3_t min = room::getMin();
vec3_t max = room::getMax();
bool moving = false;
if ( input::keyDown(SDL_SCANCODE_LEFT) )
{
act->orient=PUSH_XN;
if ( (act->pos.x>min.x && act->pos.y>=min.y && act->pos.y<=max.y) || ( (room::getDoors()&DOOR_XN) && (act->pos.y>=24) && (act->pos.y<=32) ) )
{
moving = true;
if ( (act->pos.x<=min.x) && (act->pos.z==room::getDoor(XN)*4) && (room::getDoors()&DOOR_XN) ) {
if (act->pos.y<28)
act->push |= PUSH_YP;
else if (act->pos.y>28)
act->push |= PUSH_YN;
else
act->push |= PUSH_XN;
} else
act->push |= PUSH_XN;
}
}
if ( input::keyDown(SDL_SCANCODE_RIGHT) )
{
act->orient=PUSH_XP;
if ( (act->pos.x<max.x && act->pos.y>=min.y && act->pos.y<=max.y) || ( (room::getDoors()&DOOR_XP) && (act->pos.y>=24) && (act->pos.y<=32) ) )
{
moving = true;
if ( (act->pos.x>=max.x) && (act->pos.z==room::getDoor(XP)*4) && (room::getDoors()&DOOR_XP) ) {
if (act->pos.y<28)
act->push |= PUSH_YP;
else if (act->pos.y>28)
act->push |= PUSH_YN;
else
act->push |= PUSH_XP;
} else
act->push |= PUSH_XP;
}
}
if ( input::keyDown(SDL_SCANCODE_UP) )
{
act->orient=PUSH_YN;
if ( (act->pos.y>min.y && act->pos.x>=min.x && act->pos.x<=max.x) || ( (room::getDoors()&DOOR_YN) && (act->pos.x>=24) && (act->pos.x<=32) ) )
{
moving = true;
if ( (act->pos.y<=min.y) && (act->pos.z==room::getDoor(YN)*4) && (room::getDoors()&DOOR_YN) ) {
if (act->pos.x<28)
act->push |= PUSH_XP;
else if (act->pos.x>28)
act->push |= PUSH_XN;
else
act->push |= PUSH_YN;
} else
act->push |= PUSH_YN;
}
}
if ( input::keyDown(SDL_SCANCODE_DOWN) )
{
act->orient=PUSH_YP;
if ( (act->pos.y<max.y && act->pos.x>=min.x && act->pos.x<=max.x) || ( (room::getDoors()&DOOR_YP) && (act->pos.x>=24) && (act->pos.x<=32) ) )
{
moving = true;
if ( (act->pos.y>=max.y) && (act->pos.z==room::getDoor(YP)*4) && (room::getDoors()&DOOR_YP) ) {
if (act->pos.x<28)
act->push |= PUSH_XP;
else if (act->pos.x>28)
act->push |= PUSH_XN;
else
act->push |= PUSH_YP;
} else
act->push |= PUSH_YP;
}
}
if ( input::keyDown(SDL_SCANCODE_SPACE) && act->pos.y<=max.y && act->pos.y>=min.y && act->pos.x<=max.x && act->pos.x>=min.x && act->react_mask==0 && (act->pos.z==0 || act->below))
{
act->react_mask=1;
act->react_push=0;
act->flags &= uint8_t(~FLAG_GRAVITY);
if (act->below)
{
act->below->above = nullptr;
act->below = nullptr;
}
}
if (input::keyDown(SDL_SCANCODE_Z) && act->pos.z>0) { act->push |= PUSH_ZN; moving = true; }
if (input::keyDown(SDL_SCANCODE_A) && act->pos.z<max.z) { act->push |= PUSH_ZP; moving = true; }
if (act->react_mask)
{
if (act->pos.x>max.x || act->pos.x<min.x || act->pos.y>max.y || act->pos.y<min.y) act->react_push=8;
if (act->react_push<8)
{
act->pos.z++;
act->react_push++;
}
else
{
act->react_mask=0;
act->flags |= FLAG_GRAVITY;
}
}
if (moving)
{
act->flags |= FLAG_ANIMATED;
}
else
{
act->flags &= uint8_t(~FLAG_ANIMATED);
}
}
void updateMoving(actor_t *act)
{
act->push |= act->mov_push;
}
void changeMoving(actor_t *act)
{
switch(act->movement)
{
case MOV_X:
act->mov_push = act->mov_push==PUSH_XP ? PUSH_XN : PUSH_XP;
break;
case MOV_Y:
act->mov_push = act->mov_push==PUSH_YP ? PUSH_YN : PUSH_YP;
break;
case MOV_Z:
act->mov_push = act->mov_push==PUSH_ZP ? PUSH_ZN : PUSH_ZP;
break;
case MOV_CW:
switch (act->mov_push)
{
case PUSH_XP: act->mov_push=PUSH_YN; break;
case PUSH_YN: act->mov_push=PUSH_XN; break;
case PUSH_XN: act->mov_push=PUSH_YP; break;
case PUSH_YP: act->mov_push=PUSH_XP; break;
}
break;
case MOV_CCW:
switch (act->mov_push)
{
case PUSH_XP: act->mov_push=PUSH_YP; break;
case PUSH_YP: act->mov_push=PUSH_XN; break;
case PUSH_XN: act->mov_push=PUSH_YN; break;
case PUSH_YN: act->mov_push=PUSH_XP; break;
}
break;
case MOV_RAND:
switch (rand()%4)
{
case 0: act->mov_push=PUSH_YP; break;
case 1: act->mov_push=PUSH_XN; break;
case 2: act->mov_push=PUSH_YN; break;
case 3: act->mov_push=PUSH_XP; break;
}
break;
case MOV_HUNT:
// TODO
break;
}
act->orient = act->mov_push;
}
void updatePushable(actor_t *act)
{
vec3_t min = room::getMin();
vec3_t max = room::getMax();
//if ((act->flags&FLAG_HERO) && (act->pos.x>max.x || act->pos.x<min.x || act->pos.y>max.y || act->pos.y<min.y) )
//return;
if (act->push & PUSH_ZP) {
if (act->pos.z>=max.z)
{
if (act->flags & FLAG_MOVING) changeMoving(act);
}
else
{
actor_t *now = act;
do {
actor::actor_t *other = actor::any_above_me(now);
now->pos.z++;
//if (other) other->pos.z++;
now = other;
} while (now);
actor::setDirty(act);
}
}
if (act->push & PUSH_XN) {
act->pos.x--;
actor::actor_t *other = actor::get_collision(act);
if (other || ( act->pos.x<min.x && ( !(room::getDoors()&DOOR_XN) || (act->pos.y!=28) || (act->pos.z!=room::getDoor(XN)*4) || !(act->flags&FLAG_HERO) ) ))
{
if (other && other->flags & FLAG_PUSHABLE) other->push |= PUSH_XN;
act->pos.x++;
if (act->flags & FLAG_MOVING) changeMoving(act);
}
else
{
if (act->above && act->above->flags & FLAG_PUSHABLE) {
if (act->above->push != 0) {
updatePushable(act->above);
act->above->push = PUSH_XN;
} else {
act->above->push = PUSH_XN;
updatePushable(act->above);
act->above->push = 0;
}
}
if (act->pos.x<min.x-4) {
act->pos.x = max.x;
act->pos.z = room::getDoor(XP)*4;
}
actor::setDirty(act);
}
}
if (act->push & PUSH_XP) {
act->pos.x++;
actor::actor_t *other = actor::get_collision(act);
if (other || (act->pos.x>max.x && ( !(room::getDoors()&DOOR_XP) || (act->pos.y!=28) || !(act->flags&FLAG_HERO) ) ))
{
if (other && other->flags & FLAG_PUSHABLE) other->push |= PUSH_XP;
act->pos.x--;
if (act->flags & FLAG_MOVING) changeMoving(act);
}
else
{
if (act->above && act->above->flags & FLAG_PUSHABLE) {
if (act->above->push != 0) {
updatePushable(act->above);
act->above->push = PUSH_XP;
} else {
act->above->push = PUSH_XP;
updatePushable(act->above);
act->above->push = 0;
}
}
if (act->pos.x>max.x+4) {
act->pos.x = min.x;
act->pos.z = room::getDoor(XN)*4;
}
actor::setDirty(act);
}
}
if (act->push & PUSH_YN) {
act->pos.y--;
actor::actor_t *other = actor::get_collision(act);
if (other || ( act->pos.y<min.y && ( !(room::getDoors()&DOOR_YN) || (act->pos.x!=28) || (act->pos.z!=room::getDoor(XN)*4) || !(act->flags&FLAG_HERO) ) ))
{
if (other && other->flags & FLAG_PUSHABLE) other->push |= PUSH_YN;
act->pos.y++;
if (act->flags & FLAG_MOVING) changeMoving(act);
}
else
{
if (act->above && act->above->flags & FLAG_PUSHABLE) {
if (act->above->push != 0) {
updatePushable(act->above);
act->above->push = PUSH_YN;
} else {
act->above->push = PUSH_YN;
updatePushable(act->above);
act->above->push = 0;
}
}
if (act->pos.y<min.y-4) {
act->pos.y = max.y;
act->pos.z = room::getDoor(YP)*4;
}
actor::setDirty(act);
}
}
if (act->push & PUSH_YP) {
act->pos.y++;
actor::actor_t *other = actor::get_collision(act);
if (other || ( act->pos.y>max.y && ( !(room::getDoors()&DOOR_YP) || (act->pos.x!=28) || !(act->flags&FLAG_HERO) ) ))
{
if (other && other->flags & FLAG_PUSHABLE) other->push |= PUSH_YP;
act->pos.y--;
if (act->flags & FLAG_MOVING) changeMoving(act);
}
else
{
if (act->above && act->above->flags & FLAG_PUSHABLE) {
if (act->above->push != 0) {
updatePushable(act->above);
act->above->push = PUSH_YP;
} else {
act->above->push = PUSH_YP;
updatePushable(act->above);
act->above->push = 0;
}
}
if (act->pos.y>max.y+4) {
act->pos.y = min.y;
act->pos.z = room::getDoor(YN)*4;
}
actor::setDirty(act);
}
}
if (act->push & PUSH_ZN) {
act->pos.z--;
actor::actor_t *other = actor::get_collision(act);
if (other || act->pos.z<0 || act->pos.x>max.x || act->pos.x<min.x || act->pos.y>max.y || act->pos.y<min.y)
{
//if (other && other->flags & FLAG_PUSHABLE) other->push |= PUSH_ZN;
act->pos.z++;
if (act->flags & FLAG_MOVING) changeMoving(act);
}
else
{
actor::setDirty(act);
}
}
}
void updateReactive(actor_t *act)
{
if (act->push & act->react_mask)
{
actor_t *other = nullptr;
if (act->push == PUSH_XP) { act->pos.x--; other = get_collision(act); act->pos.x++; }
else if (act->push == PUSH_XN) { act->pos.x++; other = get_collision(act); act->pos.x--; }
else if (act->push == PUSH_YP) { act->pos.y--; other = get_collision(act); act->pos.y++; }
else if (act->push == PUSH_YN) { act->pos.y++; other = get_collision(act); act->pos.y--; }
if (other) other->push |= act->react_push;
}
}
void updateGravity(actor_t *act)
{
if (act->pos.z == 0) return;
if (act->below)
{
if (is_above(act, act->below)) return;
act->below->above = nullptr;
act->below = nullptr;
}
actor_t *below = any_below_me(act);
if (below)
{
act->below = below;
below->above = act;
return;
}
vec3_t min = room::getMin();
vec3_t max = room::getMax();
if (act->flags&FLAG_HERO && (act->pos.x>max.x || act->pos.x<min.x || act->pos.y>max.y || act->pos.y<min.y) ) return;
act->pos.z--;
}
void update(actor_t *act, const bool update_all)
{
actor_t *next = act->next;
if (act->flags & FLAG_HERO) updateUserInput(act);
if (act->flags & FLAG_MOVING) updateMoving(act);
//if (act->flags & FLAG_PUSHABLE)
updatePushable(act);
if (act->flags & FLAG_GRAVITY) updateGravity(act);
if (act->flags & FLAG_REACTIVE) updateReactive(act);
act->push = PUSH_NONE;
if (update_all && next) update(next);
}
void print(int x, int y, int num)
{
int digits=0;
bool sign = num < 0;
num = SDL_abs(num);
int n = num;
while (n>0) {n=n/10;digits++;}
if (sign) digits++;
x=x+digits*4;
if (num==0) draw::draw(x+4,y,5,7,0,120);
while (num>0)
{
draw::draw(x,y,5,7,(num%10)*5,120);
num=num/10;
x=x-4;
}
if (sign) draw::draw(x,y,5,7,50,120);
}
//int order=0;
void draw(actor_t *act, const bool draw_all)
{
if (!act) return;
//if (act==first)order=0;
//order++;
const int x = 148-act->bmp_offset.x + act->pos.x*2 - act->pos.y*2;
const int y = 91-act->bmp_offset.y + act->pos.x + act->pos.y - act->pos.z*2;
const bool flip = ( (act->flags & FLAG_ORIENTABLE) && (act->orient==PUSH_XN || act->orient==PUSH_YP) ) ? DRAW_FLIP_HORIZONTAL : DRAW_FLIP_NONE;
const int oo = ( (act->flags & FLAG_ORIENTABLE) && (act->orient==PUSH_XN || act->orient==PUSH_YN) ) ? act->bmp_rect.h : 0;
const int ao = (act->flags & FLAG_ANIMATED) ? anims[act->anim_cycle][anim_frame]*act->bmp_rect.w : 0;
draw::draw(x, y, act->bmp_rect.w, act->bmp_rect.h, act->bmp_rect.x+ao, act->bmp_rect.y+oo, flip);
print(x+5,y,act->pos.x);
print(x+5,y+6,act->pos.y);
print(x+5,y+12,act->pos.z);
//print(x+5,y+12,order);
//print(x+5,y,act->flags);
if (draw_all && act->next) draw(act->next);
}
actor_t *find_at(const int x, const int y, const int z)
{
actor_t *act = first;
while (act)
{
if (act->pos.x==x && act->pos.y==y && act->pos.z==z)
{
return act;
}
}
return nullptr;
}
actor_t *get_collision(actor_t *act)
{
actor_t *other = first;
while (other)
{
if (other != act)
{
if (check_collision(act, other))
{
return other;
}
}
other = other->next;
}
other = dirty;
while (other)
{
if (other != act)
{
if (check_collision(act, other))
{
return other;
}
}
other = other->next;
}
return nullptr;
}
const bool check_collision(actor_t *obj1, actor_t *obj2)
{
return (obj1->pos.x < obj2->pos.x + obj2->size.x) &&
(obj1->pos.x + obj1->size.x > obj2->pos.x ) &&
(obj1->pos.y < obj2->pos.y + obj2->size.y) &&
(obj1->pos.y + obj1->size.y > obj2->pos.y ) &&
(obj1->pos.z < obj2->pos.z + obj2->size.z) &&
(obj1->pos.z + obj1->size.z > obj2->pos.z );
}
void clear()
{
actor_t *hero = nullptr;
actor_t *act = first;
while (act)
{
actor_t *tmp = act->next;
if (act->flags & FLAG_HERO) {
hero = act;
} else {
free(act);
}
act = tmp;
}
act = dirty;
while (act)
{
actor_t *tmp = act->next;
if (act->flags & FLAG_HERO) {
hero = act;
} else {
free(act);
}
act = tmp;
}
first = dirty = nullptr;
if (hero) {
hero->above = hero->below = hero->next = hero->prev = nullptr;
dirty = hero;
}
}
}