2024-04-02 15:42:04 +08:00
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "app.h"
|
2024-04-14 15:08:24 +08:00
|
|
|
#include "entity.h"
|
|
|
|
#include "mapper_misc.h"
|
2024-04-11 17:01:22 +08:00
|
|
|
#include "particle.h"
|
2024-04-14 15:08:24 +08:00
|
|
|
#include "physics.h"
|
|
|
|
#include "render_component.h"
|
2024-04-02 15:42:04 +08:00
|
|
|
#include "types.h"
|
2024-04-14 15:08:24 +08:00
|
|
|
#include "util/vector.h"
|
|
|
|
#include "util/assert.h"
|
2024-04-02 15:42:04 +08:00
|
|
|
|
|
|
|
|
|
|
|
void app_QueueLoadLevel(App *app, const char *level_name) {
|
|
|
|
if (app->switch_level != NULL) {
|
|
|
|
WARN("previous switch_level \"%s\" not processed; purged", app->switch_level);
|
|
|
|
free(app->switch_level);
|
|
|
|
}
|
|
|
|
app->switch_level = copy_malloc(level_name);
|
|
|
|
}
|
|
|
|
|
2024-04-11 17:01:22 +08:00
|
|
|
|
|
|
|
#define CMD1(str) if (strcmp(cmd, str) == 0)
|
2024-04-14 15:08:24 +08:00
|
|
|
#define CMD(str) else if (strcmp(cmd, str) == 0)
|
2024-04-11 17:01:22 +08:00
|
|
|
#define TOKEN (strtok(NULL, " "))
|
|
|
|
#define TOKEN_INT (atoi(TOKEN))
|
2024-04-14 15:08:24 +08:00
|
|
|
#define TOKEN_DOUBLE (strtod(TOKEN, NULL))
|
|
|
|
|
|
|
|
|
|
|
|
static vector_Vector *charbuf;
|
|
|
|
|
|
|
|
static void _app_UnescapeTextbox(char *t) {
|
|
|
|
ASSERT(t && "only can be called with t!=NULL");
|
|
|
|
if (!charbuf)
|
|
|
|
charbuf = vector_Create(sizeof(char));
|
|
|
|
else
|
|
|
|
vector_Clear(charbuf);
|
|
|
|
size_t len = strlen(t);
|
|
|
|
|
|
|
|
const char bs = '\\', space = ' ', newline = '\n', tab = '\t', zero = '\0';
|
|
|
|
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
|
|
if (t[i] == '\\') {
|
|
|
|
if (i == len - 1)
|
|
|
|
vector_Push(charbuf, &bs); // Weird case
|
|
|
|
else {
|
|
|
|
if (t[i + 1] == 's')
|
|
|
|
vector_Push(charbuf, &space);
|
|
|
|
else if (t[i + 1] == 'n')
|
|
|
|
vector_Push(charbuf, &newline);
|
|
|
|
else if (t[i + 1] == 't')
|
|
|
|
vector_Push(charbuf, &tab);
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
vector_Push(charbuf, &t[i]);
|
|
|
|
}
|
|
|
|
vector_Push(charbuf, &zero);
|
|
|
|
}
|
|
|
|
|
2024-04-15 12:54:25 +08:00
|
|
|
static inline Vec2 readvec2() {
|
|
|
|
double a, b;
|
|
|
|
a = TOKEN_DOUBLE;
|
|
|
|
b = TOKEN_DOUBLE;
|
|
|
|
Vec2 v = {.x = a, .y = b};
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
static inline Box2 readbox2() {
|
|
|
|
Vec2 a, b;
|
|
|
|
a = readvec2();
|
|
|
|
b = readvec2();
|
|
|
|
Box2 box = {.lefttop = a, .size = b};
|
|
|
|
return box;
|
|
|
|
}
|
2024-04-15 16:21:24 +08:00
|
|
|
static inline uint32_t readcolor() {
|
|
|
|
int r, g, b;
|
|
|
|
r = TOKEN_INT;
|
|
|
|
g = TOKEN_INT;
|
|
|
|
b = TOKEN_INT;
|
|
|
|
return (uint32_t)((r) | (g << 8) | (b << 16));
|
|
|
|
}
|
2024-04-15 12:54:25 +08:00
|
|
|
|
2024-04-11 17:01:22 +08:00
|
|
|
|
|
|
|
// Subsequent tokens can be read by strtok(NULL, " ")
|
2024-04-14 15:08:24 +08:00
|
|
|
static void _app_LevelCommand(App *app, char *cmd) {
|
|
|
|
CMD1("HITBOX") {
|
2024-04-15 12:54:25 +08:00
|
|
|
Box2 box = readbox2();
|
2024-04-14 15:08:24 +08:00
|
|
|
|
2024-04-14 15:22:55 +08:00
|
|
|
Entity *e = entity_Create(app->entity, cmd);
|
2024-04-14 15:08:24 +08:00
|
|
|
ADD_COMPONENT(e, hitbox);
|
2024-04-15 12:54:25 +08:00
|
|
|
e->hitbox->box = box;
|
2024-04-14 15:08:24 +08:00
|
|
|
e->hitbox->fixed = true;
|
|
|
|
entity_Commit(app->entity, e);
|
|
|
|
}
|
|
|
|
|
|
|
|
CMD("PLAYER") {
|
2024-04-15 12:54:25 +08:00
|
|
|
Vec2 vec = readvec2();
|
2024-04-14 15:08:24 +08:00
|
|
|
|
2024-04-14 15:22:55 +08:00
|
|
|
Entity *e = entity_Create(app->entity, cmd);
|
2024-04-14 15:08:24 +08:00
|
|
|
ADD_COMPONENT(e, player);
|
2024-04-15 12:54:25 +08:00
|
|
|
e->player->hazardRespawn = vec;
|
2024-04-14 15:08:24 +08:00
|
|
|
ADD_COMPONENT(e, position);
|
2024-04-15 12:54:25 +08:00
|
|
|
e->position->position = vec;
|
2024-04-14 15:08:24 +08:00
|
|
|
e->position->velocity = vec2(0, 0);
|
|
|
|
ADD_COMPONENT(e, hitbox);
|
|
|
|
e->hitbox->box.lefttop = vec2(-20, -80);
|
|
|
|
e->hitbox->box.size = vec2(40, 80);
|
|
|
|
entity_Commit(app->entity, e);
|
|
|
|
}
|
|
|
|
|
|
|
|
CMD("HAZARD_RESPAWN") {
|
2024-04-15 12:54:25 +08:00
|
|
|
Box2 box = readbox2();
|
|
|
|
Vec2 vec = readvec2();
|
2024-04-14 15:08:24 +08:00
|
|
|
|
2024-04-15 12:54:25 +08:00
|
|
|
Entity *e = entity_Create(app->entity, cmd);
|
|
|
|
misc_InstantiateHazardRespawn(app, e, box, vec);
|
|
|
|
entity_Commit(app->entity, e);
|
2024-04-14 15:08:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
CMD("HAZARD") {
|
2024-04-15 12:54:25 +08:00
|
|
|
Box2 box = readbox2();
|
2024-04-14 15:08:24 +08:00
|
|
|
|
2024-04-14 15:22:55 +08:00
|
|
|
Entity *e = entity_Create(app->entity, cmd);
|
2024-04-15 12:54:25 +08:00
|
|
|
misc_InstantiateHazard(app, e, box);
|
2024-04-14 15:08:24 +08:00
|
|
|
entity_Commit(app->entity, e);
|
|
|
|
}
|
|
|
|
|
|
|
|
CMD("TEXTBOX") {
|
|
|
|
double a, b, c, d;
|
|
|
|
a = TOKEN_DOUBLE;
|
|
|
|
b = TOKEN_DOUBLE;
|
|
|
|
c = TOKEN_DOUBLE;
|
|
|
|
d = TOKEN_DOUBLE;
|
|
|
|
|
2024-04-14 15:22:55 +08:00
|
|
|
Entity *e = entity_Create(app->entity, cmd);
|
2024-04-14 15:08:24 +08:00
|
|
|
char *bundle = TOKEN;
|
|
|
|
if (bundle != NULL)
|
2024-04-16 10:30:37 +08:00
|
|
|
e->render = render_NewComponent(e, bundle);
|
2024-04-14 15:08:24 +08:00
|
|
|
|
|
|
|
// We need to compute a position element
|
|
|
|
Vec2 position = {
|
|
|
|
.x = a + c / 2.0,
|
|
|
|
.y = b + d};
|
|
|
|
ADD_COMPONENT(e, position);
|
|
|
|
e->position->position = position;
|
|
|
|
e->position->velocity = vec2(0.0, 0.0);
|
|
|
|
|
|
|
|
char *text_raw = TOKEN;
|
|
|
|
if (text_raw) {
|
|
|
|
_app_UnescapeTextbox(text_raw);
|
|
|
|
misc_InstantiateTextbox(app, e, vector_Data(charbuf), box2(-c / 2.0, -d, c, d), (-d - 40));
|
|
|
|
}
|
|
|
|
entity_Commit(app->entity, e);
|
|
|
|
}
|
|
|
|
|
|
|
|
CMD("LEVEL_TRANSITION") {
|
2024-04-15 12:54:25 +08:00
|
|
|
Box2 box = readbox2();
|
2024-04-14 15:22:55 +08:00
|
|
|
char *next_level = TOKEN;
|
|
|
|
if (next_level) {
|
|
|
|
Entity *e = entity_Create(app->entity, cmd);
|
2024-04-15 12:54:25 +08:00
|
|
|
misc_InstantiateChangeLevel(app, e, box, next_level);
|
2024-04-14 15:22:55 +08:00
|
|
|
entity_Commit(app->entity, e);
|
|
|
|
}
|
2024-04-14 15:08:24 +08:00
|
|
|
}
|
|
|
|
|
2024-04-15 14:18:12 +08:00
|
|
|
CMD("CAMERA_FOCUS") {
|
|
|
|
Box2 box = readbox2();
|
|
|
|
|
|
|
|
Entity *e = entity_Create(app->entity, cmd);
|
|
|
|
misc_InstantiateCameraFocus(app, e, box);
|
|
|
|
entity_Commit(app->entity, e);
|
|
|
|
}
|
|
|
|
|
2024-04-14 15:08:24 +08:00
|
|
|
CMD("CUTOFF") {
|
|
|
|
app->player->cutoff = TOKEN_DOUBLE;
|
|
|
|
}
|
2024-04-15 14:18:12 +08:00
|
|
|
|
2024-04-15 16:21:24 +08:00
|
|
|
CMD("BACKGROUND") {
|
|
|
|
app->clear_color = readcolor();
|
|
|
|
}
|
|
|
|
|
|
|
|
CMD("FILL") {
|
|
|
|
uint32_t color = readcolor();
|
|
|
|
Box2 box = readbox2();
|
|
|
|
|
|
|
|
Entity *e = entity_Create(app->entity, cmd);
|
|
|
|
ADD_COMPONENT(e, render);
|
|
|
|
e->render->fillbox = box;
|
|
|
|
e->render->fillcolor = color;
|
|
|
|
}
|
|
|
|
|
2024-04-15 14:18:12 +08:00
|
|
|
else {
|
|
|
|
WARN("unknown command \"%s\"", cmd);
|
|
|
|
}
|
2024-04-11 17:01:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Defined in render_bundle_file.c
|
|
|
|
extern char linebuf[512];
|
|
|
|
|
2024-04-02 15:42:04 +08:00
|
|
|
void _app_SwitchLevel(App *app) {
|
2024-04-11 17:01:22 +08:00
|
|
|
if (app->switch_level == NULL) {
|
|
|
|
WARN("called when switch_level is NULL", 0);
|
|
|
|
return;
|
|
|
|
}
|
2024-04-14 15:22:55 +08:00
|
|
|
INFO("Switching level to %s", app->switch_level);
|
2024-04-11 17:01:22 +08:00
|
|
|
|
|
|
|
FILE *f = fopen(app->switch_level, "r");
|
|
|
|
if (!f) {
|
|
|
|
WARN("failed to open file\"%s\"", app->switch_level);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clear the current level
|
|
|
|
entity_Clear(app->entity);
|
|
|
|
particle_Clear(app->particle);
|
|
|
|
|
|
|
|
// Read every line
|
|
|
|
while (!feof(f) && fgets(linebuf, sizeof(linebuf), f)) {
|
|
|
|
while (linebuf[strlen(linebuf) - 1] == '\n')
|
|
|
|
linebuf[strlen(linebuf) - 1] = '\0';
|
|
|
|
|
|
|
|
char *cmd = strtok(linebuf, " ");
|
|
|
|
if (cmd == NULL)
|
|
|
|
continue;
|
2024-04-14 15:08:24 +08:00
|
|
|
_app_LevelCommand(app, cmd);
|
2024-04-11 17:01:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
free(app->switch_level);
|
|
|
|
app->switch_level = NULL;
|
2024-04-14 15:08:24 +08:00
|
|
|
fclose(f);
|
2024-04-02 15:42:04 +08:00
|
|
|
}
|