#include "actor.h" #include "jdraw.h" #include "jinput.h" #include "room.h" #include namespace actor { uint8_t anims[2][4] = { {0, 1, 0, 2}, {0, 1, 2, 3} }; actor_t *first = nullptr; actor_t *dirty = nullptr; actor_t *selected = nullptr; actor_t *getFirst() { return first; } actor_t *getSelected() { return selected; } actor_t *create(std::string name, vec3_t p, vec3_t s, std::string bmp, SDL_Rect r, SDL_Point o) { actor_t *act = (actor_t*)malloc(sizeof(actor_t)); strcpy(act->name, name.c_str()); strcpy(act->bmp, bmp.c_str()); act->pos = p; act->size = s; act->surface = draw::loadSurface(bmp.c_str()); 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; act->anim_wait = act->anim_wait_count = 0; act->anim_frame=0; act->react_mask = act->react_push = 0; 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; } void select(actor_t *act) { selected = 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() { 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; } } } uint8_t push(actor_t *act, uint8_t push) { uint8_t result = 0; if (act->flags & FLAG_PUSHABLE) act->push |= push; if ( (act->flags & FLAG_REACTIVE) && (act->react_mask & push) ) result = act->react_push; if (act->flags & FLAG_DEADLY) result |= PUSH_KILL; return result; } 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)) { // [RZC 14/05/2024] hack usant react_mask i react_push del heroi. Llegir més avall. act->react_mask=1; // =1 estic botant (anant cap amunt) act->react_push=0; // es el comptador de botant, seguirà pujant mentres siga < 8 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; } // [RZC 14/05/2024] Açò es un hack. estic usant react_mask i react_push del hero com a guarda // i contador per al bot. Supose que perque se suposa que el heroi no te raons // per a ser REACTIU. Però si arriba a vindre la guardia civil m'en pega més // que a un xixo. if (act->react_mask) { // Si topetem en una vora de l'habitació, s'acabat el bot if (act->pos.x>max.x || act->pos.xpos.y>max.y || act->pos.yreact_push=9; // Si encara està botant (react_push < 8)... if (act->react_push<9) { act->pos.z++; // seguim pujant act->react_push++; // augmentem el comptador de bot } else // Si ja ha acabat de botar... { act->react_mask=0; // desactivem la guarda (react_mask=0) act->flags |= FLAG_GRAVITY; // i reactivem el flag de gravetat } } // Que faça l'animació nomes si al final realment s'ha menejat 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_ZN ? PUSH_ZP : PUSH_ZN; 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; } if (act->flags & FLAG_ORIENTABLE) act->orient = act->mov_push; } void updatePush(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 { // Tractament especial del moviment cap amunt: Estem movent, directament, // TOTS els objectes des d'este cap amunt si son pushables, en compte de // enviar el flag de push. Ho vem així perque sino la gravetat els tiraria // avall. De totes formes [TODO] revisar. // Estem tenint en compte els MOVILS, pero no els REACT actor_t *now = act; do { actor::actor_t *other = actor::any_above_me(now); if (!other || (other->flags & FLAG_PUSHABLE)) now->pos.z++; now = other; if (now && !(now->flags & FLAG_PUSHABLE)) { if (act->flags & FLAG_MOVING) changeMoving(act); now = nullptr; } } while (now); actor::setDirty(act); } act->push &= ~PUSH_ZP; } 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) act->push |= push(other, PUSH_XN); act->pos.x++; if (act->flags & FLAG_MOVING) changeMoving(act); } else { // Si tenim a algú damunt, el movem també if (act->above && act->above->flags & FLAG_PUSHABLE) { push(act->above, PUSH_XN); } // Si ja havem atravesat la porta, ens movem a la porta de l'altra costat // [TODO] que es moga a l'habitació que toca!!! if (act->pos.xpos.x = max.x; act->pos.z = room::getDoor(XP)*4; } actor::setDirty(act); } act->push &= ~PUSH_XN; } 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) act->push |= push(other, PUSH_XP); act->pos.x--; if (act->flags & FLAG_MOVING) changeMoving(act); } else { if (act->above && act->above->flags & FLAG_PUSHABLE) { push(act->above, PUSH_XP); } if (act->pos.x>max.x+4) { act->pos.x = min.x; act->pos.z = room::getDoor(XN)*4; } actor::setDirty(act); } act->push &= ~PUSH_XP; } 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) act->push |= push(other, PUSH_YN); act->pos.y++; if (act->flags & FLAG_MOVING) changeMoving(act); } else { if (act->above && act->above->flags & FLAG_PUSHABLE) { push(act->above, PUSH_YN); } if (act->pos.ypos.y = max.y; act->pos.z = room::getDoor(YP)*4; } actor::setDirty(act); } act->push &= ~PUSH_YN; } 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) act->push |= push(other, PUSH_YP); act->pos.y--; if (act->flags & FLAG_MOVING) changeMoving(act); } else { if (act->above && act->above->flags & FLAG_PUSHABLE) { push(act->above, PUSH_YP); } if (act->pos.y>max.y+4) { act->pos.y = min.y; act->pos.z = room::getDoor(YN)*4; } actor::setDirty(act); } act->push &= ~PUSH_YP; } if (act->push & PUSH_ZN) { // Si estic sobre el piso, no faig res [TODO]: Si no hi ha piso ha de caure if (act->pos.z == 0) return; // Si tinc a algú baix... if (act->below) { // ...i encara està baix... if (is_above(act, act->below)) { // ...li pase a ell el push, neteje el meu flag, canvie direcció si pertoca i me ane act->push |= push(act->below, PUSH_ZN); act->push &= ~PUSH_ZN; if (act->flags & FLAG_MOVING) changeMoving(act); return; } // ... pero si ja no està baix, el desasociem, i seguim el proces act->below->above = nullptr; act->below = nullptr; } // Busquem si hi ha algú nou baix de mi actor_t *below = any_below_me(act); // [TODO] Jo crec que açò ho pot fer el propi get_collision() // Si sí que hi ha... if (below) { // ...el asociem... act->below = below; below->above = act; // ... i li passem el push, netejem el meu flag i gonnem act->push |= push(act->below, PUSH_ZN); act->push &= ~PUSH_ZN; if (act->flags & FLAG_MOVING) changeMoving(act); return; } // Si estem dins d'una porta, no caiguem. [TODO] Revisar if (act->flags&FLAG_HERO && (act->pos.x>max.x || act->pos.xpos.y>max.y || act->pos.ypush &= ~PUSH_ZN; act->pos.z--; actor::setDirty(act); } if ( (act->push & PUSH_KILL) && (act->flags & FLAG_HERO) ) { //[TODO] Matar al ruiseñor } /* 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_REACTIVE) other->push |= PUSH_ZN; act->pos.z++; if (act->flags & FLAG_MOVING) changeMoving(act); } else { actor::setDirty(act); } } */ } void update(actor_t *act, const bool update_all) { actor_t *next = act->next; // Actualitzem el frame de l'animació (si no te el flag de animat, no afectarà per a res) if (act->anim_wait_count==act->anim_wait) { act->anim_frame=(act->anim_frame+1)%4; act->anim_wait_count=0; } else { act->anim_wait_count++; } if (act->flags & FLAG_HERO) updateUserInput(act); if (act->flags & FLAG_MOVING) updateMoving(act); if (act->flags & FLAG_GRAVITY) act->push |= PUSH_ZN; //if (act->flags & FLAG_PUSHABLE) updatePush(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 updateEditor(actor_t *act, const bool update_all) { actor_t *next = act->next; if (act->anim_wait_count==act->anim_wait) { act->anim_frame=(act->anim_frame+1)%4; act->anim_wait_count=0; } else { act->anim_wait_count++; } if (update_all && next) updateEditor(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][act->anim_frame]*act->bmp_rect.w : 0; draw::pushSource(); draw::setSource(act->surface); if (act==selected) draw::swapcol(1, room::getColor()==9?11:9); // Si està seleccionat, que canvie de color draw::draw(x, y, act->bmp_rect.w, act->bmp_rect.h, act->bmp_rect.x+ao, act->bmp_rect.y+oo, flip); draw::swapcol(1, room::getColor()); // Tornem al color per defecte 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(std::string name) { actor_t *act = first; while (act) { if (name == act->name) { return act; } act = act->next; } act = dirty; while (act) { if (name == act->name) { return act; } act = act->next; } return nullptr; } 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 remove(actor_t *act) { if (!act) return; if (act->prev) act->prev->next = act->next; if (act==first) first = act->next; if (act==dirty) dirty = act->next; if (act->next) act->next->prev = act->prev; free(act); } 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; } } namespace templates { char tmp[255]; std::vector templates; const char *numToOrient(uint8_t value) { tmp[0]=0; if (value==0) return "NONE"; if (value&1) strcat(tmp, "XP "); if (value&2) strcat(tmp, "XN "); if (value&4) strcat(tmp, "YP "); if (value&8) strcat(tmp, "YN "); if (value&16) strcat(tmp, "ZP "); if (value&32) strcat(tmp, "ZN "); return tmp; } const char *numToFlags(uint16_t value) { tmp[0]=0; if (value==0) return "NONE"; if (value&1) strcat(tmp, "HERO "); if (value&2) strcat(tmp, "PUSHABLE "); if (value&4) strcat(tmp, "REACTIVE "); if (value&8) strcat(tmp, "MOVING "); if (value&16) strcat(tmp, "ANIMATED "); if (value&32) strcat(tmp, "ORIENTABLE "); if (value&64) strcat(tmp, "DEADLY "); if (value&128) strcat(tmp, "GRAVITY "); return tmp; } const char *numToMov(uint8_t value) { if (value==0) return "NONE"; if (value==1) return "X"; if (value==2) return "Y"; if (value==3) return "Z"; if (value==4) return "CW"; if (value==5) return "CCW"; if (value==6) return "RAND"; if (value==7) return "HUNT"; return "NONE"; } void load() { templates.clear(); FILE *f = fopen("data/templates.txt", "r"); if (!f) return; int size = 0; fscanf(f, "TEMPLATES %i\n", &size); fclose(f); } void save() { FILE *f = fopen("data/templates.txt", "w"); fprintf(f, "TEMPLATES %i\n", templates.size()); for (int i=0; iname); strcpy(new_template.bmp, actor->bmp); new_template.bmp_rect = actor->bmp_rect; new_template.bmp_offset = actor->bmp_offset; new_template.pos = actor->pos; new_template.size = actor->size; new_template.orient = actor->orient; new_template.anim_cycle = actor->anim_cycle; new_template.anim_wait = actor->anim_wait; new_template.flags = actor->flags; new_template.react_mask = actor->react_mask; new_template.react_push = actor->react_push; new_template.movement = actor->movement; templates.push_back(new_template); } } }