#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, const char *bmp, 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->surface = draw::loadSurface(bmp); 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; } } int peiv = 0; void reorder() { // [TODO] QUE LA VELOCITAT DE ANIMACIÓ SIGA CONFIGURABLE //if (peiv==2) { anim_frame=(anim_frame+1)%4; // peiv=0; //} peiv++; 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.xpos.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.ypos.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.zpush |= PUSH_ZP; moving = true; } if (act->react_mask) { if (act->pos.x>max.x || act->pos.xpos.y>max.y || act->pos.yreact_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.xpos.y>max.y || act->pos.ypush & 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.xpos.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.xpos.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.ypos.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.ypos.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.xpos.y>max.y || act->pos.yflags & 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.xpos.y>max.y || act->pos.ypos.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::pushSource(); draw::setSource(act->surface); draw::draw(x, y, act->bmp_rect.w, act->bmp_rect.h, act->bmp_rect.x+ao, act->bmp_rect.y+oo, flip); draw::popSource(); //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 { draw::freeSurface(act->surface); free(act); } act = tmp; } act = dirty; while (act) { actor_t *tmp = act->next; if (act->flags & FLAG_HERO) { hero = act; } else { draw::freeSurface(act->surface); free(act); } act = tmp; } first = dirty = nullptr; if (hero) { hero->above = hero->below = hero->next = hero->prev = nullptr; dirty = hero; } } }