JacksEscape/app_file.c

263 lines
5.9 KiB
C
Raw Normal View History

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);
2024-04-16 20:29:38 +08:00
e->hitbox->box.lefttop = vec2(-20, -50);
e->hitbox->box.size = vec2(40, 50);
2024-04-14 15:08:24 +08:00
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)
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);
}
CMD("PUT_CAMERA") {
Vec2 center = readvec2();
app->camera->cam = box2_FromCenter(center, app->camera->screen.size);
}
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-22 04:50:06 +08:00
CMD("FILLPOLY") {
uint32_t color = readcolor();
int n = TOKEN_INT;
// Allocate a Vector
vector_Vector *vec = vector_Create(sizeof(Vec2));
vector_Reserve(vec, n);
// Read N points
for (int i = 0; i < n; i++) {
Vec2 point = readvec2();
vector_Push(vec, &point);
}
Entity *e = entity_Create(app->entity, cmd);
ADD_COMPONENT(e, render);
e->render->fillpoly = vec; // Deallocated in render_DeleteComponent
e->render->fillcolor = color;
}
2024-04-15 16:21:24 +08:00
2024-04-15 14:18:12 +08:00
else {
WARN("unknown command \"%s\"", cmd);
}
2024-04-11 17:01:22 +08:00
}
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) {
2024-04-28 19:12:25 +08:00
WARN("called when switch_level is NULL");
2024-04-11 17:01:22 +08:00
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);
2024-04-24 22:23:01 +08:00
free(app->switch_level);
app->switch_level = NULL;
2024-04-11 17:01:22 +08:00
return;
}
// Clear the current level
entity_Clear(app->entity);
particle_Clear(app->particle);
app->camera->target = NULL;
app->level_playtime.microseconds = 0;
2024-04-11 17:01:22 +08:00
// Read every line
2024-04-28 19:12:25 +08:00
char linebuf[512];
memset(linebuf, 0, sizeof(linebuf));
2024-04-11 17:01:22 +08:00
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
}
if (app->current_level)
free(app->current_level);
app->current_level = 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
}