mirror of
https://github.com/Llennpie/Saturn.git
synced 2025-01-22 15:43:05 -05:00
custom levels :))))
This commit is contained in:
parent
2d8e4f0b8a
commit
aae9b7a7bc
14 changed files with 347 additions and 10 deletions
36
levels/sa/area_3/geo.inc.c
Normal file
36
levels/sa/area_3/geo.inc.c
Normal file
|
@ -0,0 +1,36 @@
|
|||
#include "src/game/envfx_snow.h"
|
||||
#include "src/game/custom_level.h"
|
||||
|
||||
const GeoLayout sa_area_3_geo[] = {
|
||||
GEO_NODE_START(),
|
||||
GEO_OPEN_NODE(),
|
||||
GEO_ASM(LAYER_OPAQUE, geo_custom_level),
|
||||
GEO_CLOSE_NODE(),
|
||||
GEO_RETURN(),
|
||||
};
|
||||
const GeoLayout sa_area_3[] = {
|
||||
GEO_NODE_SCREEN_AREA(10, SCREEN_WIDTH/2, SCREEN_HEIGHT/2, SCREEN_WIDTH/2, SCREEN_HEIGHT/2),
|
||||
GEO_OPEN_NODE(),
|
||||
GEO_ZBUFFER(0),
|
||||
GEO_OPEN_NODE(),
|
||||
GEO_NODE_ORTHO(100.0000),
|
||||
GEO_OPEN_NODE(),
|
||||
GEO_BACKGROUND_DYN(geo_skybox_main),
|
||||
GEO_CLOSE_NODE(),
|
||||
GEO_CLOSE_NODE(),
|
||||
GEO_ZBUFFER(1),
|
||||
GEO_OPEN_NODE(),
|
||||
GEO_CAMERA_FRUSTUM_WITH_FUNC(45.0000, 100, 30000, geo_camera_fov),
|
||||
GEO_OPEN_NODE(),
|
||||
GEO_CAMERA(CAMERA_MODE_CLOSE, 0, 0, 0, 0, -100, 0, geo_camera_main),
|
||||
GEO_OPEN_NODE(),
|
||||
GEO_BRANCH(1, sa_area_3_geo),
|
||||
GEO_RENDER_OBJ(),
|
||||
GEO_ASM(ENVFX_MODE_NONE, geo_envfx_main),
|
||||
GEO_CLOSE_NODE(),
|
||||
GEO_CLOSE_NODE(),
|
||||
GEO_CLOSE_NODE(),
|
||||
GEO_DISPLAY_LIST(LAYER_OPAQUE, sa_dl_material_revert_render_settings),
|
||||
GEO_CLOSE_NODE(),
|
||||
GEO_END(),
|
||||
};
|
4
levels/sa/area_3/macro.inc.c
Normal file
4
levels/sa/area_3/macro.inc.c
Normal file
|
@ -0,0 +1,4 @@
|
|||
const MacroObject sa_area_3_macro_objs[] = {
|
||||
MACRO_OBJECT_END(),
|
||||
};
|
||||
|
|
@ -1,2 +1,3 @@
|
|||
#include "levels/sa/area_2/geo.inc.c"
|
||||
#include "levels/sa/area_1/geo.inc.c"
|
||||
#include "levels/sa/area_3/geo.inc.c"
|
||||
|
|
|
@ -6,6 +6,9 @@ extern const GeoLayout sa_area_1_geo[];
|
|||
extern const GeoLayout sa_area_1[];
|
||||
extern const Collision sa_area_1_collision[];
|
||||
extern const MacroObject sa_area_1_macro_objs[];
|
||||
extern const GeoLayout sa_area_3_geo[];
|
||||
extern const GeoLayout sa_area_3[];
|
||||
extern const MacroObject sa_area_3_macro_objs[];
|
||||
extern Lights1 sa_dl_sm64_material_lights;
|
||||
extern Vtx sa_dl_Plane_001_mesh_vtx_0[4];
|
||||
extern Gfx sa_dl_Plane_001_mesh_tri_0[];
|
||||
|
|
|
@ -4,4 +4,5 @@
|
|||
#include "levels/sa/area_1/collision.inc.c"
|
||||
#include "levels/sa/area_1/macro.inc.c"
|
||||
#include "levels/sa/area_1/spline.inc.c"
|
||||
#include "levels/sa/area_3/macro.inc.c"
|
||||
#include "levels/sa/model.inc.c"
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "dialog_ids.h"
|
||||
#include "segment_symbols.h"
|
||||
#include "level_commands.h"
|
||||
#include "src/game/custom_level.h"
|
||||
|
||||
#include "game/level_update.h"
|
||||
|
||||
|
@ -68,6 +69,18 @@ const LevelScript level_sa_entry[] = {
|
|||
/* Fast64 end persistent block [area commands] */
|
||||
END_AREA(),
|
||||
|
||||
AREA(3, sa_area_3),
|
||||
WARP_NODE(0x0A, LEVEL_BOB, 0x01, 0x0A, WARP_NO_CHECKPOINT),
|
||||
WARP_NODE(0xF0, LEVEL_BOB, 0x01, 0x0A, WARP_NO_CHECKPOINT),
|
||||
WARP_NODE(0xF1, LEVEL_BOB, 0x01, 0x0A, WARP_NO_CHECKPOINT),
|
||||
OBJECT(MODEL_NONE, 0, 100, 0, 0, 0, 0, 0x000A0000, bhvSpinAirborneWarp),
|
||||
MARIO_POS(0x03, 0, 100, 0, 0),
|
||||
TERRAIN(custom_level_collision),
|
||||
MACRO_OBJECTS(sa_area_3_macro_objs),
|
||||
STOP_MUSIC(0),
|
||||
TERRAIN_TYPE(TERRAIN_GRASS),
|
||||
END_AREA(),
|
||||
|
||||
FREE_LEVEL_POOL(),
|
||||
MARIO_POS(0x02, 180, 0, 200, 0),
|
||||
CALL(0, lvl_init_or_update),
|
||||
|
|
|
@ -3482,6 +3482,7 @@ void update_camera(struct Camera *c) {
|
|||
}
|
||||
} else {
|
||||
if (gMarioState->action == ACT_DEBUG_FREE_MOVE) mode_custom_fly(c);
|
||||
else if (gCurrAreaIndex == 3 && gCurrLevelNum == LEVEL_SA) mode_8_directions_camera(c);
|
||||
else {
|
||||
switch (c->mode) {
|
||||
case CAMERA_MODE_BEHIND_MARIO:
|
||||
|
|
123
src/game/custom_level.c
Normal file
123
src/game/custom_level.c
Normal file
|
@ -0,0 +1,123 @@
|
|||
#include "custom_level.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "include/types.h"
|
||||
#include "include/surface_terrains.h"
|
||||
|
||||
Collision custom_level_collision[1024 * 64] = {
|
||||
COL_INIT(),
|
||||
COL_VERTEX_INIT(0),
|
||||
COL_TRI_INIT(0, 0),
|
||||
COL_TRI_STOP(),
|
||||
COL_END(),
|
||||
};
|
||||
Gfx* custom_level_dl;
|
||||
Gfx* geo_iter;
|
||||
Vtx* vertices;
|
||||
int* faces;
|
||||
char** textures;
|
||||
int vertex_iter = 0;
|
||||
int face_iter = 0;
|
||||
int used_vert = 0;
|
||||
int tex_iter = 0;
|
||||
|
||||
Lights1 lights = gdSPDefLights1(0x7F, 0x7F, 0x7F, 0xFF, 0xFF, 0xFF, 0x28, 0x28, 0x28);
|
||||
|
||||
void custom_level_new() {
|
||||
if (vertices != NULL) {
|
||||
free(vertices);
|
||||
free(faces);
|
||||
free(custom_level_dl);
|
||||
for (int i = 0; i < tex_iter; i++) {
|
||||
free(textures[i]);
|
||||
}
|
||||
free(textures);
|
||||
geo_iter = NULL;
|
||||
}
|
||||
vertices = malloc(sizeof(Vtx) * 8192);
|
||||
faces = malloc(sizeof(int) * 3 * 8192);
|
||||
custom_level_dl = malloc(sizeof(Gfx) * 16384);
|
||||
textures = (char**)malloc(sizeof(char*) * 128);
|
||||
for (int i = 0; i < 128; i++) {
|
||||
textures[i] = (char*)malloc(sizeof(char) * 256);
|
||||
}
|
||||
vertex_iter = 0;
|
||||
face_iter = 0;
|
||||
used_vert = 0;
|
||||
tex_iter = 0;
|
||||
geo_iter = custom_level_dl;
|
||||
gDPPipeSync(geo_iter++);
|
||||
gDPSetCombineLERP(geo_iter++, TEXEL0, 0, SHADE, 0, 0, 0, 0, ENVIRONMENT, TEXEL0, 0, SHADE, 0, 0, 0, 0, ENVIRONMENT);
|
||||
gSPTexture(geo_iter++, 65535, 65535, 0, 0, 1);
|
||||
gSPSetLights1(geo_iter++, lights);
|
||||
custom_level_texture("");
|
||||
}
|
||||
|
||||
void custom_level_vertex(float x, float y, float z, float u, float v) {
|
||||
if (vertex_iter == 8192) return;
|
||||
vertices[vertex_iter].v.ob[0] = x;
|
||||
vertices[vertex_iter].v.ob[1] = y;
|
||||
vertices[vertex_iter].v.ob[2] = z;
|
||||
vertices[vertex_iter].v.flag = 0;
|
||||
vertices[vertex_iter].v.tc[0] = u;
|
||||
vertices[vertex_iter].v.tc[1] = v;
|
||||
vertices[vertex_iter].v.cn[0] = 255;
|
||||
vertices[vertex_iter].v.cn[1] = 255;
|
||||
vertices[vertex_iter].v.cn[2] = 255;
|
||||
vertices[vertex_iter].v.cn[3] = 255;
|
||||
vertex_iter++;
|
||||
}
|
||||
|
||||
void custom_level_face() {
|
||||
int begin = used_vert;
|
||||
int num_vert = vertex_iter - used_vert;
|
||||
gSPVertex(geo_iter++, vertices + begin, num_vert, 0);
|
||||
for (int i = 2; i < num_vert; i++) {
|
||||
if (face_iter == 8192) break;
|
||||
faces[face_iter * 3 + 0] = begin;
|
||||
faces[face_iter * 3 + 1] = i - 1 + begin;
|
||||
faces[face_iter * 3 + 2] = i + begin;
|
||||
face_iter++;
|
||||
gSP1Triangle(geo_iter++, 0, i - 1, i, 0);
|
||||
}
|
||||
used_vert = vertex_iter;
|
||||
}
|
||||
|
||||
void custom_level_texture(char* texpath) {
|
||||
textures[tex_iter][0] = 1;
|
||||
memcpy(textures[tex_iter] + 1, texpath, strlen(texpath) + 1);
|
||||
gDPSetTextureImage(geo_iter++, G_IM_FMT_RGBA, G_IM_SIZ_16b_LOAD_BLOCK, 1, textures[tex_iter]);
|
||||
gDPSetTile(geo_iter++, G_IM_FMT_RGBA, G_IM_SIZ_16b_LOAD_BLOCK, 0, 0, 7, 0, G_TX_WRAP | G_TX_NOMIRROR, 5, 0, G_TX_WRAP | G_TX_NOMIRROR, 5, 0);
|
||||
gDPLoadBlock(geo_iter++, 7, 0, 0, 1023, 256);
|
||||
gDPSetTile(geo_iter++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 8, 0, 0, 0, G_TX_WRAP | G_TX_NOMIRROR, 5, 0, G_TX_WRAP | G_TX_NOMIRROR, 5, 0);
|
||||
gDPSetTileSize(geo_iter++, 0, 0, 0, 124, 124);
|
||||
tex_iter++;
|
||||
}
|
||||
|
||||
void custom_level_finish() {
|
||||
int iter = 0;
|
||||
custom_level_collision[iter++] = TERRAIN_LOAD_VERTICES;
|
||||
custom_level_collision[iter++] = vertex_iter;
|
||||
int lowest = 0x7FFF;
|
||||
for (int i = 0; i < vertex_iter; i++) {
|
||||
for (int j = 0; j < 3; j++) {
|
||||
if (lowest > vertices[i].v.ob[j]) lowest = vertices[i].v.ob[j];
|
||||
custom_level_collision[iter++] = vertices[i].v.ob[j];
|
||||
}
|
||||
}
|
||||
custom_level_collision[iter++] = 0;
|
||||
custom_level_collision[iter++] = face_iter;
|
||||
for (int i = 0; i < face_iter; i++) {
|
||||
for (int j = 0; j < 3; j++) {
|
||||
custom_level_collision[iter++] = faces[i * 3 + j];
|
||||
}
|
||||
}
|
||||
custom_level_collision[iter++] = TERRAIN_LOAD_CONTINUE;
|
||||
custom_level_collision[iter++] = TERRAIN_LOAD_END;
|
||||
gSPEndDisplayList(geo_iter++);
|
||||
}
|
||||
|
||||
Gfx *geo_custom_level(s32 sp, struct GraphNode* graphNode, UNUSED void* context) {
|
||||
return custom_level_dl;
|
||||
}
|
18
src/game/custom_level.h
Normal file
18
src/game/custom_level.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#ifndef CUSTOM_LEVEL_H
|
||||
#define CUSTOM_LEVEL_H
|
||||
|
||||
#include "include/types.h"
|
||||
#include "include/macros.h"
|
||||
|
||||
extern Collision custom_level_collision[];
|
||||
extern Gfx* custom_level_dl;
|
||||
|
||||
void custom_level_new();
|
||||
void custom_level_vertex(float x, float y, float z, float u, float v);
|
||||
void custom_level_face();
|
||||
void custom_level_texture(char* texture);
|
||||
void custom_level_finish();
|
||||
|
||||
Gfx* geo_custom_level(s32 sp, struct GraphNode* graphNode, UNUSED void* context);
|
||||
|
||||
#endif
|
|
@ -9,6 +9,7 @@
|
|||
#include <ctype.h>
|
||||
#ifdef _WIN32
|
||||
#include <direct.h>
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "macros.h"
|
||||
|
@ -108,6 +109,9 @@ bool fs_init(const char **rodirs, const char *gamedir, const char *writepath) {
|
|||
// as a special case, mount writepath itself
|
||||
fs_mount(fs_writepath);
|
||||
|
||||
// mount absolute path
|
||||
fs_mount("/");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
|
||||
#include "src/engine/geo_layout.h"
|
||||
|
||||
#include "src/game/area.h"
|
||||
|
||||
#define SUPPORT_CHECK(x) assert(x)
|
||||
|
||||
// SCALE_M_N: upscale/downscale M-bit integer to N-bit
|
||||
|
@ -626,8 +628,15 @@ static void import_texture(int tile) {
|
|||
// the "texture data" is actually a C string with the path to our texture in it
|
||||
// load it from an external image in our data path
|
||||
char texname[SYS_MAX_PATH];
|
||||
snprintf(texname, sizeof(texname), FS_TEXTUREDIR "/%s.png", (const char*)rdp.loaded_texture[tile].addr);
|
||||
load_texture(texname);
|
||||
char* texpath = rdp.loaded_texture[tile].addr;
|
||||
if (texpath[0] == 1) {
|
||||
texpath++;
|
||||
load_texture(texpath);
|
||||
}
|
||||
else {
|
||||
snprintf(texname, sizeof(texname), FS_TEXTUREDIR "/%s.png", texpath);
|
||||
load_texture(texname);
|
||||
}
|
||||
#else
|
||||
// the texture data is actual texture data
|
||||
int t0 = get_time();
|
||||
|
@ -1020,9 +1029,12 @@ static void gfx_sp_vertex(size_t n_vertices, size_t dest_index, const Vtx *verti
|
|||
V = (int32_t)((doty / 127.0f + 1.0f) / 4.0f * rsp.texture_scaling_factor.t);
|
||||
}
|
||||
} else {
|
||||
d->color.r = v->cn[0] / (world_light_dir4);
|
||||
d->color.g = v->cn[1] / (world_light_dir4);
|
||||
d->color.b = v->cn[2] / (world_light_dir4);
|
||||
d->color.r = v->cn[0] / (world_light_dir4) * gLightingColor[0];
|
||||
d->color.g = v->cn[1] / (world_light_dir4) * gLightingColor[1];
|
||||
d->color.b = v->cn[2] / (world_light_dir4) * gLightingColor[2];
|
||||
if (d->color.r > 255) d->color.r = 255;
|
||||
if (d->color.g > 255) d->color.g = 255;
|
||||
if (d->color.b > 255) d->color.b = 255;
|
||||
}
|
||||
|
||||
d->u = U;
|
||||
|
|
|
@ -511,12 +511,16 @@ void saturn_imgui_update() {
|
|||
saturn_load_project((char*)(std::string(saturnProjectFilename) + ".spj").c_str());
|
||||
}
|
||||
ImGui::SameLine(70);
|
||||
bool in_custom_level = gCurrLevelNum == LEVEL_SA && gCurrAreaIndex == 3;
|
||||
if (in_custom_level) ImGui::BeginDisabled();
|
||||
if (ImGui::Button(ICON_FA_SAVE " Save###project_file_save")) {
|
||||
saturn_save_project((char*)(std::string(saturnProjectFilename) + ".spj").c_str());
|
||||
saturn_load_project_list();
|
||||
}
|
||||
if (in_custom_level) ImGui::EndDisabled();
|
||||
ImGui::SameLine();
|
||||
imgui_bundled_help_marker("NOTE: Project files are currently EXPERIMENTAL and prone to crashing!");
|
||||
if (in_custom_level) ImGui::Text("Saving in a custom\nlevel isn't supported");
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::MenuItem(ICON_FA_UNDO " Load Autosaved")) {
|
||||
|
@ -660,8 +664,14 @@ void saturn_imgui_update() {
|
|||
ImGui::TextDisabled(ICON_FK_GITHUB " " GIT_BRANCH " " GIT_HASH);
|
||||
#endif
|
||||
#endif
|
||||
ImGui::SameLine(ImGui::GetWindowWidth() - 135);
|
||||
ImGui::Text("Autosaving in %ds", autosaveDelay / 30);
|
||||
if (gCurrLevelNum == LEVEL_SA && gCurrAreaIndex == 3) {
|
||||
ImGui::SameLine(ImGui::GetWindowWidth() - 280);
|
||||
ImGui::Text("Saving in custom level isn't supported");
|
||||
}
|
||||
else {
|
||||
ImGui::SameLine(ImGui::GetWindowWidth() - 140);
|
||||
ImGui::Text("Autosaving in %ds", autosaveDelay / 30);
|
||||
}
|
||||
ImGui::EndMenuBar();
|
||||
}
|
||||
ImGui::End();
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <fstream>
|
||||
|
||||
#include "saturn/libs/imgui/imgui.h"
|
||||
#include "saturn/libs/imgui/imgui_internal.h"
|
||||
|
@ -21,6 +22,7 @@
|
|||
#include <SDL2/SDL.h>
|
||||
|
||||
#include "icons/IconsForkAwesome.h"
|
||||
#include "saturn/libs/portable-file-dialogs.h"
|
||||
|
||||
#include "data/dynos.cpp.h"
|
||||
|
||||
|
@ -38,6 +40,7 @@ extern "C" {
|
|||
#include "src/game/interaction.h"
|
||||
#include "include/behavior_data.h"
|
||||
#include "game/object_helpers.h"
|
||||
#include "game/custom_level.h"
|
||||
}
|
||||
|
||||
using namespace std;
|
||||
|
@ -61,6 +64,12 @@ float gravity = 1;
|
|||
int current_location_index = 0;
|
||||
char location_name[256];
|
||||
|
||||
float custom_level_scale = 100.f;
|
||||
bool is_custom_level_loaded = false;
|
||||
std::string custom_level_path;
|
||||
std::string custom_level_filename;
|
||||
std::string custom_level_dirname;
|
||||
|
||||
s16 levelList[] = {
|
||||
LEVEL_SA, LEVEL_CASTLE_GROUNDS, LEVEL_CASTLE, LEVEL_CASTLE_COURTYARD, LEVEL_BOB,
|
||||
LEVEL_WF, LEVEL_PSS, LEVEL_TOTWC, LEVEL_JRB, LEVEL_CCM,
|
||||
|
@ -204,6 +213,75 @@ int get_saturn_level_id(int level) {
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> split(std::string input, char character) {
|
||||
std::vector<std::string> tokens = {};
|
||||
std::string token = "";
|
||||
for (int i = 0; i < input.length(); i++) {
|
||||
if (input[i] == '\r') continue;
|
||||
if (input[i] == character) {
|
||||
tokens.push_back(token);
|
||||
token = "";
|
||||
}
|
||||
else token += input[i];
|
||||
}
|
||||
tokens.push_back(token);
|
||||
return tokens;
|
||||
}
|
||||
std::vector<std::vector<std::string>> tokenize(std::string input) {
|
||||
std::vector<std::vector<std::string>> tokens = {};
|
||||
auto lines = split(input, '\n');
|
||||
for (auto line : lines) {
|
||||
tokens.push_back(split(line, ' '));
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
void parse_materials(char* data, std::map<std::string, filesystem::path>* materials) {
|
||||
auto tokens = tokenize(std::string(data));
|
||||
std::string matname = "";
|
||||
for (auto line : tokens) {
|
||||
if (line[0] == "newmtl") matname = line[1];
|
||||
if (line[0] == "map_Kd" && matname != "") materials->insert({ matname, filesystem::absolute(std::filesystem::path(custom_level_dirname) / line[1]) });
|
||||
}
|
||||
}
|
||||
|
||||
void parse_custom_level(char* data) {
|
||||
auto tokens = tokenize(std::string(data));
|
||||
custom_level_new();
|
||||
std::vector<std::array<float, 3>> vertices = {};
|
||||
std::vector<std::array<float, 2>> uv = {};
|
||||
std::map<std::string, filesystem::path> materials = {};
|
||||
for (auto line : tokens) {
|
||||
if (line.size() == 0) continue;
|
||||
if (line[0] == "mtllib") {
|
||||
filesystem::path path = filesystem::absolute(std::filesystem::path(custom_level_dirname) / line[1]);
|
||||
if (!filesystem::exists(path)) continue;
|
||||
auto size = filesystem::file_size(path);
|
||||
char* mtldata = (char*)malloc(size);
|
||||
std::ifstream file = std::ifstream(path, std::ios::binary);
|
||||
file.read(mtldata, size);
|
||||
parse_materials(mtldata, &materials);
|
||||
free(mtldata);
|
||||
}
|
||||
if (line[0] == "v") vertices.push_back({ std::stof(line[1]), std::stof(line[2]), std::stof(line[3]) });
|
||||
if (line[0] == "vt") uv.push_back({ std::stof(line[1]), std::stof(line[2]) });
|
||||
if (line[0] == "usemtl") {
|
||||
if (materials.find(line[1]) == materials.end()) continue;
|
||||
custom_level_texture((char*)materials[line[1]].c_str());
|
||||
}
|
||||
if (line[0] == "f") {
|
||||
for (int i = 1; i < line.size(); i++) {
|
||||
auto indexes = split(line[i], '/');
|
||||
int v = std::stoi(indexes[0]) - 1;
|
||||
int vt = std::stoi(indexes[1]) - 1;
|
||||
custom_level_vertex(vertices[v][0] * custom_level_scale, vertices[v][1] * custom_level_scale, vertices[v][2] * custom_level_scale, uv[vt][0] * 1024, uv[vt][1] * 1024);
|
||||
}
|
||||
custom_level_face();
|
||||
}
|
||||
}
|
||||
custom_level_finish();
|
||||
}
|
||||
|
||||
void smachinima_imgui_init() {
|
||||
Cheats.EnableCheats = true;
|
||||
Cheats.GodMode = true;
|
||||
|
@ -414,6 +492,37 @@ void imgui_machinima_quick_options() {
|
|||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Custom Level")) {
|
||||
bool in_custom_level = gCurrLevelNum == LEVEL_SA && gCurrAreaIndex == 3;
|
||||
ImGui::PushItemWidth(80);
|
||||
ImGui::InputFloat("Scale###cl_scale", &custom_level_scale);
|
||||
ImGui::PopItemWidth();
|
||||
if (!is_custom_level_loaded || in_custom_level) ImGui::BeginDisabled();
|
||||
if (ImGui::Button("Load Level")) {
|
||||
auto size = filesystem::file_size(custom_level_path);
|
||||
char* data = (char*)malloc(size);
|
||||
std::ifstream file = std::ifstream((char*)custom_level_path.c_str(), std::ios::binary);
|
||||
file.read(data, size);
|
||||
parse_custom_level(data);
|
||||
free(data);
|
||||
warp_to_level(0, 3);
|
||||
}
|
||||
if (!is_custom_level_loaded || in_custom_level) ImGui::EndDisabled();
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Load .obj")) {
|
||||
auto selection = pfd::open_file("Select a model", ".", { "Wavefront Model (.obj)", "*.obj" }, pfd::opt::none).result();
|
||||
if (selection.size() != 0) {
|
||||
std::string path = selection[0];
|
||||
is_custom_level_loaded = true;
|
||||
custom_level_path = path;
|
||||
custom_level_dirname = filesystem::path(path).parent_path();
|
||||
custom_level_filename = filesystem::path(path).filename();
|
||||
}
|
||||
}
|
||||
ImGui::Text(is_custom_level_loaded ? custom_level_filename.c_str() : "No model loaded!");
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -367,9 +367,11 @@ void saturn_update() {
|
|||
|
||||
// Autosave
|
||||
|
||||
if (autosaveDelay <= 0) autosaveDelay = 30 * configAutosaveDelay;
|
||||
autosaveDelay--;
|
||||
if (autosaveDelay == 0) saturn_save_project("autosave.spj");
|
||||
if (gCurrLevelNum != LEVEL_SA || gCurrAreaIndex != 3) {
|
||||
if (autosaveDelay <= 0) autosaveDelay = 30 * configAutosaveDelay;
|
||||
autosaveDelay--;
|
||||
if (autosaveDelay == 0) saturn_save_project("autosave.spj");
|
||||
}
|
||||
}
|
||||
|
||||
float saturn_keyframe_setup_interpolation(std::string id, int frame, int* keyframe, bool* last) {
|
||||
|
|
Loading…
Reference in a new issue