- Al afegir un actor com a template, "neteja" el nom de numerets - El numeros del final de nom ara tenen el format "-XX"
1746 lines
59 KiB
C++
1746 lines
59 KiB
C++
#include "actor.h"
|
|
#include "jdraw.h"
|
|
#include "jinput.h"
|
|
#include "jfile.h"
|
|
#include "jutil.h"
|
|
#include "room.h"
|
|
#include "editor.h"
|
|
#include "debug.h"
|
|
#include "config.h"
|
|
#include <vector>
|
|
|
|
namespace actor
|
|
{
|
|
uint8_t anims[3][4] = {
|
|
{0, 1, 0, 2},
|
|
{0, 1, 2, 3},
|
|
{0, 1, 2, 0}
|
|
};
|
|
|
|
actor_t *first = nullptr;
|
|
actor_t *dirty = nullptr;
|
|
actor_t *selected = nullptr;
|
|
actor_t *picked = nullptr;
|
|
static bool room_changed = false;
|
|
int brilli_brilli=0;
|
|
draw::surface *brilli;
|
|
|
|
int current_tag = 0;
|
|
|
|
void resetTag()
|
|
{
|
|
current_tag = 0;
|
|
}
|
|
|
|
actor_t *getFirst()
|
|
{
|
|
return first;
|
|
}
|
|
|
|
actor_t *getLast()
|
|
{
|
|
if (!first) return nullptr;
|
|
actor_t *last = first;
|
|
while(last->next) last = last->next;
|
|
return last;
|
|
}
|
|
|
|
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));
|
|
act->tag = current_tag++;
|
|
strcpy(act->name, name.c_str());
|
|
strcpy(act->bmp, bmp.c_str());
|
|
act->pos = p;
|
|
act->size = s;
|
|
act->surface = draw::getSurface(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;
|
|
act->flags = FLAG_NONE;
|
|
return act;
|
|
}
|
|
|
|
actor_t *createEmptyActor()
|
|
{
|
|
actor_t *act = (actor_t*)malloc(sizeof(actor_t));
|
|
act->tag = current_tag++;
|
|
act->pos = {0, 0, 0};
|
|
act->size = {8,8,8};
|
|
act->bmp_rect = {0,0,32,32};
|
|
act->bmp_offset = {0,0};
|
|
act->anim_cycle = act->orient = act->movement = 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;
|
|
act->flags = FLAG_NONE;
|
|
act->template_category = 0;
|
|
return act;
|
|
}
|
|
|
|
actor_t *duplicate(actor_t *act)
|
|
{
|
|
actor_t *new_act = createEmptyActor();
|
|
actor::templates::copy(new_act, act);
|
|
new_act->surface = draw::getSurface(new_act->bmp);
|
|
new_act->prev = new_act->next = new_act->above = new_act->below = nullptr;
|
|
return new_act;
|
|
}
|
|
|
|
actor_t *createFromTemplate(const char *name)
|
|
{
|
|
actor_t *templ = actor::templates::getByName(name);
|
|
if (!templ) return nullptr;
|
|
return duplicate(templ);
|
|
}
|
|
|
|
actor_t *createFromFile(char **buffer)
|
|
{
|
|
if (*buffer)
|
|
{
|
|
actor_t *t = createEmptyActor();
|
|
while (**buffer != 0)
|
|
{
|
|
const char* key = file::readString(buffer);
|
|
|
|
if (util::strcomp(key, "name:")) {
|
|
const char *val = file::readString(buffer);
|
|
strcpy(t->name, val);
|
|
} else if (util::strcomp(key, "bmp:")) {
|
|
const char *val = file::readString(buffer);
|
|
strcpy(t->bmp, val);
|
|
t->surface = draw::getSurface(t->bmp);
|
|
} else if (util::strcomp(key, "bmp-rect:")) {
|
|
t->bmp_rect.x = file::readInt(buffer);
|
|
t->bmp_rect.y = file::readInt(buffer);
|
|
t->bmp_rect.w = file::readInt(buffer);
|
|
t->bmp_rect.h = file::readInt(buffer);
|
|
} else if (util::strcomp(key, "bmp-offset:")) {
|
|
t->bmp_offset.x = file::readInt(buffer);
|
|
t->bmp_offset.y = file::readInt(buffer);
|
|
} else if (util::strcomp(key, "pos:")) {
|
|
t->pos.x = file::readInt(buffer);
|
|
t->pos.y = file::readInt(buffer);
|
|
t->pos.z = file::readInt(buffer);
|
|
} else if (util::strcomp(key, "size:")) {
|
|
t->size.x = file::readInt(buffer);
|
|
t->size.y = file::readInt(buffer);
|
|
t->size.z = file::readInt(buffer);
|
|
} else if (util::strcomp(key, "orient:")) {
|
|
t->orient = util::stringToInt(file::readString(buffer), {"none", "xp", "xn", "yp", "yn", "zp", "zn"}, {0, 1, 2, 4, 8, 16, 32});
|
|
} else if (util::strcomp(key, "movement:")) {
|
|
t->movement = util::stringToInt(file::readString(buffer), {"none", "x", "y", "z", "cw", "ccw", "rand", "randv", "hunt"},{MOV_NONE, MOV_X, MOV_Y, MOV_Z, MOV_CW, MOV_CCW, MOV_RAND, MOV_RANDV, MOV_HUNT});
|
|
} else if (util::strcomp(key, "anim-cycle:")) {
|
|
t->anim_cycle = util::stringToInt(file::readString(buffer), {"walk", "seq", "min"},{0, 1, 2});
|
|
} else if (util::strcomp(key, "anim-wait:")) {
|
|
t->anim_wait = file::readInt(buffer);
|
|
} else if (util::strcomp(key, "flags:")) {
|
|
const char *str = file::readString(buffer, true);
|
|
int value = 0;
|
|
while (str)
|
|
{
|
|
value |= util::stringToInt(str, { "none", "hero", "pushable", "reactive", "moving", "animated", "orientable", "deadly", "gravity", "pickable", "special", "noeditor", "inertia" },{ FLAG_NONE, FLAG_HERO, FLAG_PUSHABLE, FLAG_REACTIVE, FLAG_MOVING, FLAG_ANIMATED, FLAG_ORIENTABLE, FLAG_DEADLY, FLAG_GRAVITY, FLAG_PICKABLE, FLAG_SPECIAL, FLAG_NOEDITOR, FLAG_INERTIA });
|
|
str = file::readString(buffer, true);
|
|
}
|
|
t->flags = value;
|
|
} else if (util::strcomp(key, "react-push:")) {
|
|
const char *str = file::readString(buffer, true);
|
|
int value = 0;
|
|
while (str)
|
|
{
|
|
value |= util::stringToInt(str, { "none", "xp", "xn", "yp", "yn", "zp", "zn" }, { PUSH_NONE, PUSH_XP, PUSH_XN, PUSH_YP, PUSH_YN, PUSH_ZP, PUSH_ZN });
|
|
str = file::readString(buffer, true);
|
|
}
|
|
t->react_push = value;
|
|
} else if (util::strcomp(key, "react-mask:")) {
|
|
const char *str = file::readString(buffer, true);
|
|
int value = 0;
|
|
while (str)
|
|
{
|
|
value |= util::stringToInt(str, { "none", "xp", "xn", "yp", "yn", "zp", "zn" }, { PUSH_NONE, PUSH_XP, PUSH_XN, PUSH_YP, PUSH_YN, PUSH_ZP, PUSH_ZN });
|
|
str = file::readString(buffer, true);
|
|
}
|
|
t->react_mask = value;
|
|
} else if (util::strcomp(key, "}")) {
|
|
return t;
|
|
}
|
|
}
|
|
if (t->flags&FLAG_MOVING) t->mov_push = t->orient;
|
|
return t;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
actor_t *replaceWithTemplate(actor_t *act, const char *name)
|
|
{
|
|
if (!act) return nullptr;
|
|
actor_t *newactor = createFromTemplate(name);
|
|
newactor->pos.x = act->pos.x;
|
|
newactor->pos.y = act->pos.y;
|
|
newactor->pos.z = act->pos.z;
|
|
if (act->prev) act->prev->next = newactor;
|
|
if (act==first) first = newactor;
|
|
if (act==dirty) dirty = newactor;
|
|
if (act->next) act->next->prev = newactor;
|
|
newactor->prev = act->prev;
|
|
newactor->next = act->next;
|
|
act->prev = act->next = nullptr;
|
|
remove(act);
|
|
return newactor;
|
|
}
|
|
|
|
char tmp[255];
|
|
|
|
void cleanName(actor_t *act)
|
|
{
|
|
int size = strlen(act->name);
|
|
if (act->name[size-3] == '-' && act->name[size-1] >= 48 && act->name[size-1] <= 57 && act->name[size-2] >= 48 && act->name[size-2] <= 57 )
|
|
{
|
|
act->name[size-3] = 0;
|
|
}
|
|
|
|
}
|
|
|
|
// Li donem al actor un nom únic
|
|
void setUniqueName(actor_t *act)
|
|
{
|
|
// Recorrem tots els actors
|
|
actor_t *other = first;
|
|
while (other != nullptr)
|
|
{
|
|
// Si no som eixe actor...
|
|
if (other != act)
|
|
{
|
|
// ... però tenim el mateix nom...
|
|
if (strcmp(act->name, other->name)==0)
|
|
{
|
|
// Si el nom actual no acaba en guió + dos digits, li afegim "-01" al final
|
|
int size = strlen(act->name);
|
|
if (act->name[size-3] != '-' || act->name[size-1] < 48 || act->name[size-1] > 57 || act->name[size-2] < 48 || act->name[size-2] > 57 )
|
|
{
|
|
strcat(other->name, "-00");
|
|
strcat(act->name, "-01");
|
|
} else {
|
|
// Si ja acaba en dos digits, agafem el numero, li sumem 1, i li'l tornem a ficar
|
|
int num = (act->name[size-1]-48) + (act->name[size-2]-48)*10;
|
|
num++;
|
|
act->name[size-1] = (num % 10)+48;
|
|
act->name[size-2] = int(num / 10)+48;
|
|
}
|
|
// I tornem a començar des del principi amb el nou nom
|
|
other = first;
|
|
}
|
|
}
|
|
other = other->next;
|
|
}
|
|
}
|
|
|
|
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 & FLAG_PICKABLE) strcat(tmp, "PICKABLE ");
|
|
if (value & FLAG_PUSHABLE) strcat(tmp, "PUSHABLE ");
|
|
if (value & FLAG_REACTIVE) strcat(tmp, "REACTIVE ");
|
|
if (value & FLAG_MOVING) strcat(tmp, "MOVING ");
|
|
if (value & FLAG_ANIMATED) strcat(tmp, "ANIMATED ");
|
|
if (value & FLAG_ORIENTABLE) strcat(tmp, "ORIENTABLE ");
|
|
if (value & FLAG_DEADLY) strcat(tmp, "DEADLY ");
|
|
if (value & FLAG_GRAVITY) strcat(tmp, "GRAVITY ");
|
|
if (value & FLAG_SPECIAL) strcat(tmp, "SPECIAL ");
|
|
if (value & FLAG_INERTIA) strcat(tmp, "INERTIA ");
|
|
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 "RANDV";
|
|
if (value==8) return "HUNT";
|
|
return "NONE";
|
|
}
|
|
|
|
actor_t *alpha = nullptr;
|
|
const bool a_is_greater_than_b(actor_t *a, actor_t *b)
|
|
{
|
|
return strcmp(a->name, b->name) >= 0;
|
|
}
|
|
|
|
void order(actor_t *act)
|
|
{
|
|
if (!act) return;
|
|
if (!a_is_greater_than_b(act, alpha))
|
|
{
|
|
act->next_alpha = alpha;
|
|
alpha = act;
|
|
}
|
|
else
|
|
{
|
|
actor_t *other = alpha;
|
|
actor_t *prev = nullptr;
|
|
while (a_is_greater_than_b(act, other))
|
|
{
|
|
if (other->next_alpha)
|
|
{
|
|
prev = other;
|
|
other = other->next_alpha;
|
|
}
|
|
else
|
|
{
|
|
other->next_alpha = act;
|
|
return;
|
|
}
|
|
}
|
|
prev->next_alpha = act;
|
|
act->next_alpha = other;
|
|
}
|
|
}
|
|
|
|
actor_t *alphaOrder(actor_t *act)
|
|
{
|
|
if (!act) return nullptr;
|
|
alpha = act;
|
|
act->next_alpha = nullptr;
|
|
act = act->next;
|
|
while (act)
|
|
{
|
|
act->next_alpha = nullptr;
|
|
order(act);
|
|
act = act->next;
|
|
}
|
|
return alpha;
|
|
}
|
|
|
|
void saveToFile(FILE *f, actor_t *act, bool tab)
|
|
{
|
|
char ws[5] = "";
|
|
if (tab) strcpy(ws, " ");
|
|
fprintf(f, "\n%sactor{\n", ws);
|
|
fprintf(f, " %sname: %s\n", ws, act->name);
|
|
fprintf(f, " %sbmp: %s\n", ws, act->bmp);
|
|
fprintf(f, " %sbmp-rect: %i %i %i %i\n", ws, act->bmp_rect.x, act->bmp_rect.y, act->bmp_rect.w, act->bmp_rect.h);
|
|
fprintf(f, " %sbmp-offset: %i %i\n", ws, act->bmp_offset.x, act->bmp_offset.y);
|
|
fprintf(f, " %spos: %i %i %i\n", ws, act->pos.x, act->pos.y, act->pos.z);
|
|
fprintf(f, " %ssize: %i %i %i\n", ws, act->size.x, act->size.y, act->size.z);
|
|
if (act->orient!=0) fprintf(f, " %sorient: %s\n", ws, numToOrient(act->orient));
|
|
if (act->anim_cycle!=0) fprintf(f, " %sanim-cycle: %s\n", ws, act->anim_cycle==0 ? "WALK" : act->anim_cycle==1 ? "SEQ" : "MIN");
|
|
if (act->anim_wait!=0) fprintf(f, " %sanim-wait: %i\n", ws, act->anim_wait);
|
|
if (act->flags!=0) fprintf(f, " %sflags: %s\n", ws, numToFlags(act->flags));
|
|
if (act->react_mask!=0) fprintf(f, " %sreact-mask: %s\n", ws, numToOrient(act->react_mask));
|
|
if (act->react_push!=0) fprintf(f, " %sreact-push: %s\n", ws, numToOrient(act->react_push));
|
|
if (act->movement!=0) fprintf(f, " %smovement: %s\n", ws, numToMov(act->movement));
|
|
fprintf(f, "%s}\n", ws);
|
|
}
|
|
|
|
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 && !act->next)
|
|
{
|
|
act->next = first;
|
|
if (first) first->prev = act;
|
|
first = act;
|
|
}
|
|
/*
|
|
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 overlap(actor_t *act1, actor_t *act2)
|
|
{
|
|
return act1->inner_x < act2->inner_x+act2->bmp_rect.w &&
|
|
act1->inner_x+act1->bmp_rect.w > act2->inner_x &&
|
|
act1->inner_y > act2->inner_y+act2->bmp_rect.h &&
|
|
act1->inner_y+act1->bmp_rect.h < act2->inner_y;
|
|
}
|
|
|
|
const bool isInFront(actor_t *act1, actor_t *act2)
|
|
{
|
|
return (act1->pos.x >= act2->pos.x+act2->size.x) ||
|
|
(act1->pos.y >= act2->pos.y+act2->size.y) ||
|
|
(act1->pos.z >= act2->pos.z+act2->size.z);
|
|
/*
|
|
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()
|
|
{
|
|
dirty = first;
|
|
first = nullptr;
|
|
|
|
while (dirty)
|
|
{
|
|
dirty->inner_x = 148-dirty->bmp_offset.x + dirty->pos.x*2 - dirty->pos.y*2;
|
|
dirty->inner_y = 91-dirty->bmp_offset.y + dirty->pos.x + dirty->pos.y - dirty->pos.z*2;
|
|
|
|
if (first)
|
|
{
|
|
actor_t *current = first;
|
|
while (true)
|
|
{
|
|
if (overlap(dirty, current) || isInFront(dirty, current))
|
|
{
|
|
if (current->next)
|
|
{
|
|
current = current->next;
|
|
}
|
|
else
|
|
{
|
|
current->next = dirty;
|
|
dirty = dirty->next;
|
|
current->next->prev = current;
|
|
current->next->next = nullptr;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
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
|
|
{
|
|
first = dirty;
|
|
dirty = dirty->next;
|
|
first->prev=first->next=nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
uint8_t push(actor_t *source, actor_t *act, uint8_t push)
|
|
{
|
|
uint8_t result = 0;
|
|
if ( (act->flags & FLAG_PUSHABLE) && ( !(source->flags&FLAG_HERO) || (hero::getSkills()&SKILL_GLOVES) ) ) act->push |= push;
|
|
if ( (act->flags & FLAG_REACTIVE) && (act->react_mask & push) )
|
|
{
|
|
if (act->flags & FLAG_DEADLY)
|
|
source->push |= PUSH_KILL;
|
|
else
|
|
result = act->react_push;
|
|
}
|
|
if (source->flags & FLAG_DEADLY) {
|
|
result |= PUSH_KILL;
|
|
}
|
|
|
|
if (source->flags&FLAG_HERO)
|
|
{
|
|
if (act->flags & FLAG_SPECIAL)
|
|
{
|
|
if (act->name[0]=='B') { // Es un booster
|
|
hero::collectBooster(act->name[1]-48, (act->name[3]-48)*10+(act->name[4]-48));
|
|
} else if (act->name[0]=='S') { // Es un skill
|
|
hero::giveSkill(&act->name[2]);
|
|
} else if ( (act->name[0]=='P') && (hero::getSkills()&SKILL_BAG) ) { // Es una part
|
|
hero::pickPart(&act->name[2]);
|
|
} else {
|
|
SDL_assert(false);
|
|
}
|
|
act = actor::replaceWithTemplate(act, "EXPLOSION");
|
|
act->name[0]='_';
|
|
room::cycleColor(1);
|
|
}
|
|
}
|
|
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) || input::keyDown(config::getKey(KEY_LEFT)) )
|
|
{
|
|
hero::useBoostRun();
|
|
act->orient=PUSH_XN;
|
|
if ( (act->pos.x>min.x && act->pos.y>=min.y && (act->pos.y+act->size.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;
|
|
}
|
|
}
|
|
else if ( input::keyDown(SDL_SCANCODE_RIGHT) || input::keyDown(config::getKey(KEY_RIGHT)) )
|
|
{
|
|
hero::useBoostRun();
|
|
act->orient=PUSH_XP;
|
|
if ( ((act->pos.x+act->size.x)<max.x && act->pos.y>=min.y && (act->pos.y+act->size.y)<=max.y) || ( (room::getDoors()&DOOR_XP) && (act->pos.y>=24) && (act->pos.y<=32) ) )
|
|
{
|
|
moving = true;
|
|
if ( ((act->pos.x+act->size.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;
|
|
}
|
|
}
|
|
else if ( input::keyDown(SDL_SCANCODE_UP) || input::keyDown(config::getKey(KEY_UP)) )
|
|
{
|
|
hero::useBoostRun();
|
|
act->orient=PUSH_YN;
|
|
if ( (act->pos.y>min.y && act->pos.x>=min.x && (act->pos.x+act->size.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;
|
|
}
|
|
}
|
|
else if ( input::keyDown(SDL_SCANCODE_DOWN) || input::keyDown(config::getKey(KEY_DOWN)) )
|
|
{
|
|
hero::useBoostRun();
|
|
act->orient=PUSH_YP;
|
|
if ( ((act->pos.y+act->size.y)<max.y && act->pos.x>=min.x && (act->pos.x+act->size.x)<=max.x) || ( (room::getDoors()&DOOR_YP) && (act->pos.x>=24) && (act->pos.x<=32) ) )
|
|
{
|
|
moving = true;
|
|
if ( ((act->pos.y+act->size.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::keyPressed(SDL_SCANCODE_RETURN) || input::keyPressed(config::getKey(KEY_PICK)) ) && (hero::getSkills()&SKILL_PANTS))
|
|
{
|
|
if (picked)
|
|
{
|
|
picked->pos.x = act->pos.x;
|
|
picked->pos.y = act->pos.y;
|
|
picked->pos.z = act->pos.z;
|
|
act->pos.z += picked->size.z;
|
|
actor::actor_t *above = act->above;
|
|
while (above) { above->pos.z += picked->size.z; above = above->above; }
|
|
if (act->below)
|
|
{
|
|
act->below->above = picked;
|
|
picked->below = act->below;
|
|
}
|
|
act->below = picked;
|
|
picked->above = act;
|
|
picked->next = picked->prev = nullptr;
|
|
actor::setDirty(picked, true);
|
|
picked = nullptr;
|
|
}
|
|
else if (act->below && act->below->flags&FLAG_PICKABLE)
|
|
{
|
|
const int height = act->below->size.z;
|
|
pick(act->below);
|
|
if (!input::keyDown(SDL_SCANCODE_SPACE) && !input::keyDown(config::getKey(KEY_JUMP))) act->pos.z -= height;
|
|
}
|
|
}
|
|
if (( input::keyDown(SDL_SCANCODE_SPACE) || input::keyDown(config::getKey(KEY_JUMP)) ) && (hero::getSkills()&SKILL_SHOES) && (act->pos.y+act->size.y)<=max.y && act->pos.y>=min.y && (act->pos.x+act->size.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=hero::getBoostJump()>0 ? 2 : 1; // =1 estic botant (anant cap amunt)
|
|
act->react_push=8; // es el comptador de botant, seguirà pujant mentres siga > 0
|
|
//act->react_push=hero::getBoostJump()>0?16:8; // es el comptador de botant, seguirà pujant mentres siga > 0
|
|
act->flags &= uint8_t(~FLAG_GRAVITY);
|
|
if (act->below)
|
|
{
|
|
act->below->above = nullptr;
|
|
act->below = nullptr;
|
|
}
|
|
hero::useBoostJump();
|
|
}
|
|
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; }
|
|
|
|
// [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+act->size.x)>max.x || act->pos.x<min.x || (act->pos.y+act->size.y)>max.y || act->pos.y<min.y) act->react_push=0;
|
|
|
|
// Si encara està botant (react_push > 0)...
|
|
if (act->react_push>0)
|
|
{
|
|
const int vel = act->react_mask;
|
|
if (!actor::any_above_me(act)) act->pos.z+=vel; // 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)
|
|
{
|
|
if (act->flags&FLAG_MOVING && act->mov_push==0) act->mov_push = act->orient; // Per a activar-lo si no ho està
|
|
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_RANDV:
|
|
switch (rand()%8)
|
|
{
|
|
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;
|
|
case 4: act->mov_push=PUSH_YP; act->mov_push=PUSH_XN; break;
|
|
case 5: act->mov_push=PUSH_XN; act->mov_push=PUSH_YN; break;
|
|
case 6: act->mov_push=PUSH_YN; act->mov_push=PUSH_XP; break;
|
|
case 7: act->mov_push=PUSH_XP; act->mov_push=PUSH_YP; break;
|
|
}
|
|
break;
|
|
case MOV_HUNT:
|
|
actor_t *heroi = find("HERO");
|
|
if (heroi)
|
|
{
|
|
int tombola[2]; int tombola_pos = 0;
|
|
if (heroi->pos.x < act->pos.x) tombola[tombola_pos++]=PUSH_XN;
|
|
if (heroi->pos.x > act->pos.x) tombola[tombola_pos++]=PUSH_XP;
|
|
if (heroi->pos.y < act->pos.y) tombola[tombola_pos++]=PUSH_YN;
|
|
if (heroi->pos.y > act->pos.y) tombola[tombola_pos++]=PUSH_YP;
|
|
act->mov_push=tombola[rand()%tombola_pos];
|
|
}
|
|
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->push & PUSH_KILL) && (act->flags & FLAG_HERO) ) {
|
|
const int lives = hero::getLives()-1;
|
|
hero::setLives(lives);
|
|
stats::loseLive();
|
|
// [TODO] If lives == 0 anar a la pantalla de game-over o cat's life
|
|
actor_t *act = actor::find("HERO");
|
|
act = actor::replaceWithTemplate(act, "EXPLOSION");
|
|
actor_t *act2 = actor::createFromTemplate("EXPLOSION");
|
|
act2->pos = act->pos; act2->pos.z += 8; act2->anim_frame=2;
|
|
act->anim_wait = act2->anim_wait = 1;
|
|
actor::setDirty(act2);
|
|
room::cycleColor(4);
|
|
return;
|
|
}
|
|
|
|
int vel = (act->flags&FLAG_HERO) && (hero::getBoostRun()>0) ? 2 : 1;
|
|
|
|
if (act->push & PUSH_ZP) {
|
|
if (act->pos.z>=max.z)
|
|
{
|
|
if (act->flags & FLAG_MOVING) changeMoving(act);
|
|
|
|
if ( ((act->flags&FLAG_HERO)!=0) && (room::getExit(ZP)!=-1) )
|
|
{
|
|
room::load(room::getExit(ZP));
|
|
act->pos.z = 4;
|
|
actor::setDirty(act);
|
|
hero::setFirstPos();
|
|
room_changed = true;
|
|
return;
|
|
}
|
|
}
|
|
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++;
|
|
if ( (now->pos.z>=max.z) && (now->flags&FLAG_HERO)!=0 && (room::getExit(ZP)!=-1) )
|
|
{
|
|
room::load(room::getExit(ZP));
|
|
now->pos.z = 4;
|
|
actor::setDirty(now);
|
|
hero::setFirstPos();
|
|
room_changed = true;
|
|
return;
|
|
}
|
|
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-=vel;
|
|
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->push |= push(act, other, PUSH_XN);
|
|
act->pos.x+=vel;
|
|
if (act->flags & FLAG_MOVING) changeMoving(act);
|
|
if (act->flags & FLAG_INERTIA) act->push = PUSH_NONE;
|
|
}
|
|
else
|
|
{
|
|
// Si tenim a algú damunt, el movem també
|
|
if (act->above && act->above->flags & FLAG_PUSHABLE) {
|
|
push(act, act->above, PUSH_XN);
|
|
}
|
|
|
|
// Si ja havem atravesat la porta, ens movem a la porta de l'altra costat
|
|
if (act->pos.x<min.x-4) {
|
|
room::load(room::getExit(XN));
|
|
act->pos.x = room::getMax().x-4;
|
|
act->pos.z = room::getDoor(XP)*4;
|
|
hero::setFirstPos();
|
|
room_changed = true;
|
|
}
|
|
|
|
actor::setDirty(act);
|
|
}
|
|
if ( !(act->flags & FLAG_INERTIA) ) act->push &= ~PUSH_XN;
|
|
}
|
|
|
|
if (act->push & PUSH_XP) {
|
|
act->pos.x+=vel;
|
|
actor::actor_t *other = actor::get_collision(act);
|
|
if (other || ((act->pos.x+act->size.x)>max.x && ( !(room::getDoors()&DOOR_XP) || (act->pos.y!=28) || !(act->flags&FLAG_HERO) ) ))
|
|
{
|
|
if (other) other->push |= push(act, other, PUSH_XP);
|
|
act->pos.x-=vel;
|
|
if (act->flags & FLAG_MOVING) changeMoving(act);
|
|
if (act->flags & FLAG_INERTIA) act->push = PUSH_NONE;
|
|
}
|
|
else
|
|
{
|
|
if (act->above && act->above->flags & FLAG_PUSHABLE) {
|
|
push(act, act->above, PUSH_XP);
|
|
}
|
|
|
|
if ((act->pos.x+act->size.x)>max.x+4) {
|
|
room::load(room::getExit(XP));
|
|
act->pos.x = room::getMin().x-3;
|
|
act->pos.z = room::getDoor(XN)*4;
|
|
hero::setFirstPos();
|
|
room_changed = true;
|
|
}
|
|
|
|
actor::setDirty(act);
|
|
}
|
|
if ( !(act->flags & FLAG_INERTIA) ) act->push &= ~PUSH_XP;
|
|
}
|
|
|
|
if (act->push & PUSH_YN) {
|
|
act->pos.y-=vel;
|
|
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(YN)*4) || !(act->flags&FLAG_HERO) ) ))
|
|
{
|
|
if (other) other->push |= push(act, other, PUSH_YN);
|
|
act->pos.y+=vel;
|
|
if (act->flags & FLAG_MOVING) changeMoving(act);
|
|
if (act->flags & FLAG_INERTIA) act->push = PUSH_NONE;
|
|
}
|
|
else
|
|
{
|
|
if (act->above && act->above->flags & FLAG_PUSHABLE) {
|
|
push(act, act->above, PUSH_YN);
|
|
}
|
|
|
|
if (act->pos.y<min.y-4) {
|
|
room::load(room::getExit(YN));
|
|
act->pos.y = room::getMax().y-4;
|
|
act->pos.z = room::getDoor(YP)*4;
|
|
hero::setFirstPos();
|
|
room_changed = true;
|
|
}
|
|
|
|
actor::setDirty(act);
|
|
}
|
|
if ( !(act->flags & FLAG_INERTIA) ) act->push &= ~PUSH_YN;
|
|
}
|
|
|
|
if (act->push & PUSH_YP) {
|
|
act->pos.y+=vel;
|
|
actor::actor_t *other = actor::get_collision(act);
|
|
if (other || ( (act->pos.y+act->size.y)>max.y && ( !(room::getDoors()&DOOR_YP) || (act->pos.x!=28) || !(act->flags&FLAG_HERO) ) ))
|
|
{
|
|
if (other) other->push |= push(act, other, PUSH_YP);
|
|
act->pos.y-=vel;
|
|
if (act->flags & FLAG_MOVING) changeMoving(act);
|
|
if (act->flags & FLAG_INERTIA) act->push = PUSH_NONE;
|
|
}
|
|
else
|
|
{
|
|
if (act->above && act->above->flags & FLAG_PUSHABLE) {
|
|
push(act, act->above, PUSH_YP);
|
|
}
|
|
|
|
if ((act->pos.y+act->size.y)>max.y+4) {
|
|
room::load(room::getExit(YP));
|
|
act->pos.y = room::getMin().y-3;
|
|
act->pos.z = room::getDoor(YN)*4;
|
|
hero::setFirstPos();
|
|
room_changed = true;
|
|
}
|
|
|
|
actor::setDirty(act);
|
|
}
|
|
if ( !(act->flags & FLAG_INERTIA) ) act->push &= ~PUSH_YP;
|
|
}
|
|
|
|
if (act->push & PUSH_ZN) {
|
|
// Si estic sobre el piso, no faig res
|
|
if (act->pos.z == 0)
|
|
{
|
|
if ( (act->flags & FLAG_MOVING) && (act->movement==MOV_Z) ) changeMoving(act);
|
|
act->push &= ~PUSH_ZN;
|
|
if ( ((act->flags&FLAG_HERO)==0) || (room::getExit(ZN)==-1) ) return;
|
|
|
|
room::load(room::getExit(ZN));
|
|
act->pos.z = room::getMax().z;
|
|
actor::setDirty(act);
|
|
hero::setFirstPos();
|
|
room_changed = true;
|
|
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->below->push |= push(act, act->below, PUSH_ZN);
|
|
act->push &= ~PUSH_ZN;
|
|
if ( (act->flags & FLAG_MOVING) && (act->movement==MOV_Z) ) 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->below->push |= push(act, act->below, PUSH_ZN);
|
|
act->push &= ~PUSH_ZN;
|
|
if ( (act->flags & FLAG_MOVING) && (act->movement==MOV_Z) ) changeMoving(act);
|
|
return;
|
|
}
|
|
|
|
// Si estem dins d'una porta, no caiguem. [TODO] Revisar
|
|
if (act->flags&FLAG_HERO && ((act->pos.x+act->size.x)>max.x || act->pos.x<min.x || (act->pos.y+act->size.y)>max.y || act->pos.y<min.y) ) return;
|
|
|
|
// Si arribem fins ací, podem moure la posició
|
|
act->push &= ~PUSH_ZN;
|
|
act->pos.z--;
|
|
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_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)
|
|
{
|
|
if (!act) return;
|
|
|
|
if (act==first) { brilli_brilli=(brilli_brilli+1)&0x7; }
|
|
|
|
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;
|
|
if (act->anim_frame==4) {
|
|
if (act->name[0]=='_') {
|
|
actor::remove(act);
|
|
return;
|
|
} else {
|
|
act->anim_frame=0;
|
|
}
|
|
}
|
|
if (act->anim_cycle==2 && act->anim_frame==3) act->anim_frame=0;
|
|
act->anim_wait_count=0;
|
|
} else {
|
|
act->anim_wait_count++;
|
|
}
|
|
|
|
if (act->flags & FLAG_HERO) updateUserInput(act);
|
|
if (act->flags & FLAG_MOVING) {
|
|
if (act->movement==MOV_HUNT && ( (act->pos.x&7)==0 || (act->pos.y&7)==0) ) changeMoving(act);
|
|
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 (!room_changed && update_all && next) update(next);
|
|
room_changed = false;
|
|
}
|
|
|
|
void updateEditor(actor_t *act, const bool update_all)
|
|
{
|
|
if (!act) return;
|
|
|
|
actor_t *next = act->next;
|
|
|
|
if (act->anim_wait_count==act->anim_wait) {
|
|
act->anim_frame=(act->anim_frame+1)%4;
|
|
if (act->anim_cycle==2 && act->anim_frame==3) act->anim_frame=0;
|
|
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);
|
|
}
|
|
|
|
void drawAt(actor_t * act, const int x, const int y)
|
|
{
|
|
act->inner_x = x;
|
|
act->inner_y = y;
|
|
draw(act, false);
|
|
}
|
|
|
|
void draw(actor_t *act, const bool draw_all)
|
|
{
|
|
if (!act) return;
|
|
|
|
if (!editor::isEditing() || ( (act->flags&FLAG_HERO)==0))
|
|
{
|
|
const int x = act->inner_x; // 148-act->bmp_offset.x + act->pos.x*2 - act->pos.y*2;
|
|
const int y = act->inner_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 (editor::isEditing() && (act==selected)) draw::swapcol(1, room::getColor(1)); // Si està seleccionat, que canvie de color
|
|
draw::stencil::set(act->tag);
|
|
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(0)); // Tornem al color per defecte
|
|
if (act->flags&FLAG_SPECIAL)
|
|
{
|
|
draw::setSource(brilli);
|
|
const int dx = (act->bmp_rect.w-22)>>1;
|
|
draw::draw(x+dx, y, 22, 24, brilli_brilli*22, 96, DRAW_FLIP_NONE);
|
|
}
|
|
draw::popSource();
|
|
|
|
if (debug::isEnabled(DEBUG_ACTOR_POS))
|
|
{
|
|
char tmp[100];
|
|
draw::print(SDL_itoa(act->inner_x, tmp, 10), x+9,y+20, LIGHT+WHITE, BLACK);
|
|
draw::print(SDL_itoa(act->inner_y, tmp, 10), x+9,y+26, LIGHT+WHITE, BLACK);
|
|
draw::swapcol(1, room::getColor(0)); // Tornem al color per defecte
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
actor_t *findByTag(const int tag)
|
|
{
|
|
actor_t *act = first;
|
|
while (act)
|
|
{
|
|
if (tag == act->tag)
|
|
{
|
|
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;
|
|
//draw::freeSurface(act->surface);
|
|
if (act==selected) selected = nullptr;
|
|
free(act);
|
|
}
|
|
|
|
void pick(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;
|
|
if (act==selected) selected = nullptr;
|
|
picked = act;
|
|
picked->pos.x=26;
|
|
picked->pos.y=84;
|
|
picked->inner_x = 148-act->bmp_offset.x + act->pos.x*2 - act->pos.y*2;
|
|
picked->inner_y = 91-act->bmp_offset.y + act->pos.x + act->pos.y - act->pos.z*2;
|
|
|
|
}
|
|
|
|
actor_t *getPicked() { return picked; }
|
|
|
|
void clear(const bool all)
|
|
{
|
|
resetTag();
|
|
actor_t *hero = nullptr;
|
|
actor_t *act = first;
|
|
while (act)
|
|
{
|
|
actor_t *tmp = act->next;
|
|
if (!all && (act->flags & FLAG_HERO)) {
|
|
hero = act;
|
|
} else {
|
|
free(act);
|
|
}
|
|
act = tmp;
|
|
}
|
|
if (picked)
|
|
{
|
|
free(picked);
|
|
picked = nullptr;
|
|
}
|
|
first = dirty = nullptr;
|
|
if (hero) {
|
|
hero->above = hero->below = hero->next = hero->prev = nullptr;
|
|
first = hero;
|
|
}
|
|
selected = nullptr;
|
|
}
|
|
|
|
namespace templates
|
|
{
|
|
std::vector<actor_t> templates;
|
|
std::vector<std::string> categories;
|
|
|
|
void load()
|
|
{
|
|
//newCategory("default");
|
|
categories.clear();
|
|
templates.clear();
|
|
char filename[] = "templates.txt";
|
|
int filesize=0;
|
|
char *buffer = file::getFileBuffer(filename, filesize, true);
|
|
char *original_buffer = buffer;
|
|
int current_category = 0;
|
|
|
|
if (buffer)
|
|
{
|
|
while (*buffer != 0)
|
|
{
|
|
const char* key = file::readString(&buffer);
|
|
|
|
if (util::strcomp(key, "category{"))
|
|
{
|
|
const char* key = file::readString(&buffer);
|
|
if (util::strcomp(key, "name:")) {
|
|
const char *val = file::readString(&buffer);
|
|
current_category = newCategory(val);
|
|
}
|
|
}
|
|
else if (util::strcomp(key, "actor{"))
|
|
{
|
|
actor_t *t = createFromFile(&buffer);
|
|
t->template_category = current_category;
|
|
templates.push_back(*t);
|
|
free(t);
|
|
}
|
|
else if (util::strcomp(key, "}")) {
|
|
current_category = 0;
|
|
}
|
|
|
|
}
|
|
free(original_buffer);
|
|
}
|
|
}
|
|
|
|
void save()
|
|
{
|
|
FILE *f = fopen("data/templates.txt", "w");
|
|
|
|
for (auto cat_name : categories)
|
|
{
|
|
|
|
fprintf(f, "\ncategory{\n");
|
|
fprintf(f, " name: %s\n", cat_name.c_str());
|
|
|
|
auto actors = getByCategory(cat_name.c_str());
|
|
for (auto t : actors )
|
|
{
|
|
saveToFile(f, &t, true);
|
|
}
|
|
|
|
fprintf(f, "\n}\n");
|
|
}
|
|
|
|
/*for (int i=0; i<templates.size(); ++i)
|
|
{
|
|
actor_t t = templates[i];
|
|
saveToFile(f, &t);
|
|
}*/
|
|
|
|
fclose(f);
|
|
}
|
|
|
|
/*const int size()
|
|
{
|
|
return templates.size();
|
|
}*/
|
|
|
|
std::vector<actor::actor_t> getByCategory(const char* category)
|
|
{
|
|
std::string catname = category;
|
|
std::vector<actor_t> actors;
|
|
for (auto act : templates)
|
|
{
|
|
if (act.template_category>=categories.size()) act.template_category = 0;
|
|
if (categories[act.template_category]==catname) actors.push_back(act);
|
|
}
|
|
return actors;
|
|
}
|
|
|
|
std::vector<std::string> getCategories()
|
|
{
|
|
return categories;
|
|
}
|
|
|
|
const int newCategory(const char *name)
|
|
{
|
|
categories.push_back(name);
|
|
return categories.size()-1;
|
|
}
|
|
|
|
actor_t *get(const int index)
|
|
{
|
|
return &templates[index];
|
|
}
|
|
|
|
actor_t *getByName(const char *name)
|
|
{
|
|
for (int i=0;i<templates.size();++i) if (util::strcomp(name, templates[i].name)) return &templates[i];
|
|
//for (auto t : templates) if (util::strcomp(name, t.name)) return &t;
|
|
return nullptr;
|
|
}
|
|
|
|
void copy(actor_t *dest, actor_t *source)
|
|
{
|
|
strcpy(dest->name, source->name);
|
|
strcpy(dest->bmp, source->bmp);
|
|
dest->bmp_rect = source->bmp_rect;
|
|
dest->bmp_offset = source->bmp_offset;
|
|
dest->pos = source->pos;
|
|
dest->size = source->size;
|
|
dest->orient = source->orient;
|
|
dest->anim_cycle = source->anim_cycle;
|
|
dest->anim_wait = source->anim_wait;
|
|
dest->flags = source->flags;
|
|
dest->react_mask = source->react_mask;
|
|
dest->react_push = source->react_push;
|
|
dest->movement = source->movement;
|
|
}
|
|
|
|
void add(actor_t *act)
|
|
{
|
|
// Fem una copia del actor
|
|
actor_t new_template;
|
|
copy(&new_template, act);
|
|
// Li fiquem la categoria per defecte
|
|
new_template.template_category = 0;
|
|
// Netejem el nom de numerets
|
|
cleanName(&new_template);
|
|
|
|
// Si ja hi ha una plantilla amb eixe nom...
|
|
if (actor::templates::getByName(act->name))
|
|
{
|
|
// ... la actualitzem amb les dades del actor seleccionat
|
|
actor_t *existing_template = actor::templates::getByName(act->name);
|
|
copy(existing_template, &new_template);
|
|
} else {
|
|
// ... i sinó, afegim el actor seleccionat a la llista de plantilles
|
|
templates.push_back(new_template);
|
|
}
|
|
save();
|
|
}
|
|
}
|
|
|
|
namespace hero
|
|
{
|
|
int lives = 8;
|
|
int boost_jumps = 0;
|
|
int boost_steps = 0;
|
|
int boost_god = 0;
|
|
int skills = SKILL_NONE;
|
|
int parts = PART_NONE;
|
|
bool boosters_collected[100];
|
|
vec3_t first_pos = {0,0,0};
|
|
int first_orient = 0;
|
|
bool dead = false;
|
|
|
|
void init(const bool complete)
|
|
{
|
|
actor::actor_t *hero = actor::create("HERO", {16,32,0}, {6,6,12}, "test.gif", {0,32,20,32}, {-6,38});
|
|
hero->flags = FLAG_HERO | FLAG_PUSHABLE | FLAG_GRAVITY | FLAG_ORIENTABLE | FLAG_ANIMATED;
|
|
actor::setDirty(hero, true);
|
|
|
|
boost_jumps = boost_steps = boost_god = 0;
|
|
dead = false;
|
|
|
|
if (complete)
|
|
{
|
|
stats::reset();
|
|
lives = 8;
|
|
skills = SKILL_NONE;
|
|
parts = PART_NONE;
|
|
for (int i=0; i<100; ++i) boosters_collected[i] = false;
|
|
brilli = draw::getSurface("objectes.gif");
|
|
} else {
|
|
hero->pos = first_pos;
|
|
hero->orient = first_orient;
|
|
}
|
|
}
|
|
|
|
int getLives()
|
|
{
|
|
return lives;
|
|
}
|
|
|
|
void setLives(int value)
|
|
{
|
|
lives = value;
|
|
}
|
|
|
|
void die()
|
|
{
|
|
dead = true;
|
|
}
|
|
|
|
bool isDead()
|
|
{
|
|
return dead;
|
|
}
|
|
|
|
const int getBoosterFromString(char *booster)
|
|
{
|
|
static const char *boostset_name[4] = {"RUN", "GOD", "JUMP", "LIVE"};
|
|
for (int i=0;i<4;++i)
|
|
{
|
|
if (strcmp(booster, boostset_name[i])==0)
|
|
{
|
|
return 1<<i;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool giveBooster(char *booster)
|
|
{
|
|
const int value = getBoosterFromString(booster);
|
|
switch (value)
|
|
{
|
|
case BOOST_GOD: boost_god = 99*2; break;
|
|
case BOOST_RUN: boost_steps = 99*2; break;
|
|
case BOOST_JUMP: boost_jumps = 10; break;
|
|
case BOOST_LIVE: lives++; break;
|
|
}
|
|
return value!=0;
|
|
}
|
|
|
|
void collectBooster(int booster, int id)
|
|
{
|
|
boosters_collected[id] = true;
|
|
switch (booster)
|
|
{
|
|
case BOOST_GOD: boost_god = 99*2; break;
|
|
case BOOST_RUN: boost_steps = 99*2; break;
|
|
case BOOST_JUMP: boost_jumps = 10; break;
|
|
case BOOST_LIVE: lives++; break;
|
|
}
|
|
}
|
|
|
|
bool wasBoosterCollected(int id)
|
|
{
|
|
return boosters_collected[id];
|
|
}
|
|
|
|
int getBoostGod()
|
|
{
|
|
return boost_god;
|
|
}
|
|
int getBoostRun()
|
|
{
|
|
return boost_steps;
|
|
}
|
|
|
|
int getBoostJump()
|
|
{
|
|
return boost_jumps;
|
|
}
|
|
|
|
void useBoostGod()
|
|
{
|
|
if (boost_god > 0) boost_god--;
|
|
}
|
|
|
|
void useBoostRun()
|
|
{
|
|
if (boost_steps > 0) boost_steps--;
|
|
}
|
|
|
|
void useBoostJump()
|
|
{
|
|
if (boost_jumps > 0) boost_jumps--;
|
|
|
|
}
|
|
|
|
|
|
const int getSkillFromString(char *skill)
|
|
{
|
|
static const char *skillset_name[4] = {"SHOES", "GLOVES", "PANTS", "BAG"};
|
|
for (int i=0;i<4;++i)
|
|
{
|
|
if (strcmp(skill, skillset_name[i])==0)
|
|
{
|
|
return 1<<i;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool giveSkill(int skill)
|
|
{
|
|
skills |= skill;
|
|
return skill!=0;
|
|
}
|
|
|
|
bool giveSkill(char *skill)
|
|
{
|
|
return giveSkill(getSkillFromString(skill));
|
|
}
|
|
|
|
bool dropSkill(int skill)
|
|
{
|
|
skills &= ~skill;
|
|
return skill!=0;
|
|
}
|
|
|
|
bool dropSkill(char *skill)
|
|
{
|
|
return dropSkill(getSkillFromString(skill));
|
|
}
|
|
|
|
bool wasSkillCollected(char *skill)
|
|
{
|
|
return skills & getSkillFromString(skill);
|
|
}
|
|
|
|
int getSkills()
|
|
{
|
|
return skills;
|
|
}
|
|
|
|
const int getPartFromString(char *part)
|
|
{
|
|
static const char *partset_name[6] = {"FILTER", "PUMP", "TIMER", "SALT", "PIPE", "ELBOW"};
|
|
for (int i=0;i<6;++i)
|
|
{
|
|
if (strcmp(part, partset_name[i])==0)
|
|
{
|
|
return 1<<i;
|
|
}
|
|
}
|
|
return PART_NONE;
|
|
}
|
|
|
|
bool pickPart(char *part)
|
|
{
|
|
const int value = getPartFromString(part);
|
|
parts |= value;
|
|
if (value!=0) stats::collectPart();
|
|
return value!=0;
|
|
}
|
|
|
|
bool dropPart(char *part)
|
|
{
|
|
const int value = getPartFromString(part);
|
|
parts &= ~value;
|
|
return value!=0;
|
|
}
|
|
|
|
bool wasPartCollected(char *part)
|
|
{
|
|
return parts & getPartFromString(part);
|
|
}
|
|
|
|
int getParts()
|
|
{
|
|
return parts;
|
|
}
|
|
|
|
void move(int *x, int *y, int *z)
|
|
{
|
|
actor_t *hero = actor::find("HERO");
|
|
if (x) hero->pos.x = *x;
|
|
if (y) hero->pos.y = *y;
|
|
if (z) hero->pos.z = *z;
|
|
}
|
|
|
|
void setFirstPos()
|
|
{
|
|
actor_t *hero = actor::find("HERO");
|
|
first_pos = hero->pos;
|
|
first_orient = hero->orient;
|
|
}
|
|
|
|
}
|
|
|
|
namespace stats
|
|
{
|
|
int partsCollected = 0;
|
|
bool roomVisited[MAX_ROOMS];
|
|
int livesLost = 0;
|
|
int catsLifeOdds = 5;
|
|
|
|
void reset()
|
|
{
|
|
partsCollected = livesLost = 0;
|
|
catsLifeOdds = 5;
|
|
for (int i=0; i<MAX_ROOMS; ++i) roomVisited[i] = false;
|
|
}
|
|
|
|
void collectPart() { partsCollected++; }
|
|
void visitRoom(int room) {roomVisited[room] = true; }
|
|
void loseLive() { livesLost++; }
|
|
|
|
int getNumPartsCollected() { return partsCollected; }
|
|
int getRoomsVisited()
|
|
{
|
|
int roomsVisited = 0;
|
|
for (int i=0; i<MAX_ROOMS; ++i) if (roomVisited[i]) roomsVisited++;
|
|
return roomsVisited;
|
|
}
|
|
|
|
int getLivesLost() { return livesLost; }
|
|
|
|
bool catsLife()
|
|
{
|
|
if ( (rand()%catsLifeOdds) == 0) {
|
|
catsLifeOdds *= 2;
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|