diff --git a/README.md b/README.md index ea22913..357876c 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,7 @@ That will generate the rom at `/build/portal64.z64`
## Current New Feature TODO List +- [ ] rumble pak support? - [ ] Change default controls - [ ] Add puzzle element connections and additional signs - [ ] Use a much nearer clipping plane when rendering the portal gun diff --git a/assets/test_chambers/test_chamber_00/test_chamber_00.yaml b/assets/test_chambers/test_chamber_00/test_chamber_00.yaml new file mode 100644 index 0000000..d1b822d --- /dev/null +++ b/assets/test_chambers/test_chamber_00/test_chamber_00.yaml @@ -0,0 +1,43 @@ +operators: + [] +cutscenes: + START_SECOND_ROOM: + - start_cutscene SECOND_ROOM_INSTRUCTIONS + - open_portal stationary_portal 1 + - label portal_loop + - open_portal button_room 0 + - delay 5 1 + - open_portal door_room 0 + - delay 5 2 + - open_portal cube_room 0 + - delay 5 0 + - goto portal_loop + SECOND_ROOM_INSTRUCTIONS: + - q_sound SOUNDS_00_PART2_ENTRY_1 CH_GLADOS + - wait_for_signal success_1 + - q_sound SOUNDS_00_PART2_SUCCESS_1 CH_GLADOS + INTRO: + - play_animation player player_intro + - play_animation glass_cover_0 glass_cover_0_open + - play_animation glass_cover_1 glass_cover_1_open + - delay 23 + - set_signal room_divider + - delay 5 21.34 + - q_sound SOUNDS_00_PART1_ENTRY_1 CH_GLADOS + - q_sound SOUNDS_00_PART1_ENTRY_2 CH_GLADOS + - q_sound SOUNDS_00_PART1_ENTRY_3 CH_GLADOS + - q_sound SOUNDS_00_PART1_ENTRY_4 CH_GLADOS + - q_sound SOUNDS_00_PART1_ENTRY_5 CH_GLADOS + - q_sound SOUNDS_00_PART1_ENTRY_6 CH_GLADOS + - q_sound SOUNDS_00_PART1_ENTRY_7 CH_GLADOS + - wait_for_channel CH_GLADOS + - open_portal portal_room 0 + - open_portal room_exit 1 + FIRST_PUZZLE: + - clear_signal room_divider + - delay 3 + - set_signal cube_dropper + - wait_for_signal success + - q_sound SOUNDS_00_PART1_SUCCESS_1 CH_GLADOS + - q_sound SOUNDS_00_PART1_SUCCESS_2 CH_GLADOS + - q_sound SOUNDS_00_PART1_SUCCESS_3 CH_GLADOS \ No newline at end of file diff --git a/assets/test_chambers/test_chamber_01/test_chamber_01.yaml b/assets/test_chambers/test_chamber_01/test_chamber_01.yaml new file mode 100644 index 0000000..91b199e --- /dev/null +++ b/assets/test_chambers/test_chamber_01/test_chamber_01.yaml @@ -0,0 +1,49 @@ +operators: + [] +cutscenes: + GET_GUN: + - set_signal room_0_exit + - "hide_pedestal " + - stop_cutscene portal_loop + - q_sound 01_PART1_GET_PORTAL_GUN_1 CH_GLADOS + - q_sound 01_PART1_GET_PORTAL_GUN_2 CH_GLADOS + - q_sound 01_PART1_GET_PORTAL_GUN_3 CH_GLADOS + - q_sound 01_PART1_GET_PORTAL_GUN_4 CH_GLADOS + - q_sound 01_PART1_GET_PORTAL_GUN_5 CH_GLADOS + - q_sound 01_PART1_GET_PORTAL_GUN_6 CH_GLADOS + - q_sound 01_PART1_GET_PORTAL_GUN_7 CH_GLADOS + - q_sound 01_PART1_GET_PORTAL_GUN_8 CH_GLADOS + MIND_THE_GAP: + - open_portal portal_gap 0 + - q_sound SOUNDS_01_PART2_ENTRY_1 CH_GLADOS + START: + - delay 1 + - start_cutscene portal_loop + - set_signal FIRST_ELEVATOR + FINISH_03: + - q_sound SOUNDS_01_PART2_SUCCESS_1 CH_GLADOS + INTRO_CUTSCENE: + - q_sound SOUNDS_01_PART1_ENTRY_1 CH_GLADOS + - q_sound SOUNDS_01_PART1_ENTRY_2 CH_GLADOS + - wait_for_channel CH_GLADOS + - set_signal room_0_entrance + portal_loop: + - open_portal portal_exit 0 + - label loop_start + - open_portal portal_0 1 + - delay 3 + - point_pedestal portal_1 + - delay 4 + - open_portal portal_1 1 + - delay 3 2 + - point_pedestal portal_2 + - delay 4 2 + - open_portal portal_2 1 + - delay 3 3 + - point_pedestal portal_3 + - delay 4 3 + - open_portal portal_3 1 + - delay 3 4 + - point_pedestal portal_0 + - delay 4 4 + - goto loop_start \ No newline at end of file diff --git a/assets/test_chambers/test_chamber_02/test_chamber_02.yaml b/assets/test_chambers/test_chamber_02/test_chamber_02.yaml new file mode 100644 index 0000000..cadbf3c --- /dev/null +++ b/assets/test_chambers/test_chamber_02/test_chamber_02.yaml @@ -0,0 +1,20 @@ +cutscenes: + DROP_CUBE: + - set_signal cube_dropper + ENTER_BUTTON_ROOM: + - open_portal portal_1 + ENTER_SMALL_ROOM: + - set_signal double_door + - open_portal portal_2 0 + - q_sound SOUNDS_02_PART2_SUCCESS_1 CH_GLADOS + - q_sound SOUNDS_02_PART2_SUCCESS_2 CH_GLADOS + INTRO: + - q_sound SOUNDS_02_PART1_ENTRY_1 CH_GLADOS + - wait_for_channel CH_GLADOS + - open_portal portal_0 0 + - q_sound SOUNDS_02_PART1_ENTRY_2 CH_GLADOS + - wait_for_signal success + - q_sound SOUNDS_02_PART1_SUCCESS_1 CH_GLADOS + - q_sound SOUNDS_02_PART1_SUCCESS_2 CH_GLADOS +operators: + - double_door = exit_2_0 and exit_2_1 \ No newline at end of file diff --git a/assets/test_chambers/test_chamber_03/test_chamber_03.yaml b/assets/test_chambers/test_chamber_03/test_chamber_03.yaml new file mode 100644 index 0000000..a6b92b7 --- /dev/null +++ b/assets/test_chambers/test_chamber_03/test_chamber_03.yaml @@ -0,0 +1,36 @@ +operators: + - not_on_piston = not on_piston +cutscenes: + BALL_2: + - delay 3 + - set_signal launch_ball_2 + - wait_for_signal horizontal_activiate + - label horizontal_loop + - play_animation horizontal horizontal_slide + - wait_for_animation horizontal + - goto horizontal_loop + INTRO_2: + - save_checkpoint 0.001 + - open_portal ground_portal_2 + - start_cutscene BALL_2 + - q_sound SOUNDS_03_PART2_ENTRY_1 CH_GLADOS + - wait_for_signal horizontal_activiate 2 + - q_sound 03_PART2_PLATFORM_ACTIVATED_1 CH_GLADOS + INTRO: + - save_checkpoint 0 + - open_portal ground_portal 0 + - q_sound SOUNDS_03_PART1_ENTRY_1 CH_GLADOS + - q_sound SOUNDS_03_PART1_ENTRY_2 CH_GLADOS + - wait_for_signal exit_activate 2 + - q_sound SOUNDS_03_PART1_SUCCESS_1 CH_GLADOS + START_BALL: + - set_signal launch_ball + - wait_for_signal exit_activate + - label piston_loop + - play_animation exit_piston piston_move_down + - wait_for_animation exit_piston 0 + - wait_for_signal on_piston + - play_animation exit_piston piston_move_up + - wait_for_animation exit_piston + - wait_for_signal not_on_piston + - goto piston_loop \ No newline at end of file diff --git a/assets/test_chambers/test_chamber_04/test_chamber_04.yaml b/assets/test_chambers/test_chamber_04/test_chamber_04.yaml new file mode 100644 index 0000000..ecfa033 --- /dev/null +++ b/assets/test_chambers/test_chamber_04/test_chamber_04.yaml @@ -0,0 +1,19 @@ +cutscenes: + SUCCESS: + - q_sound SOUNDS_04_PART1_SUCCESS_1 CH_GLADOS + INTRO_CUTSCENE: + - q_sound SOUNDS_04_PART1_ENTRY_1 CH_GLADOS + DROWN_PLAYER: + - kill_player water + OPEN_PORTAL: + - "save_checkpoint " + - delay 1 + - open_portal stationary_portal 0 + - set_signal launch_ball + - wait_for_signal exit_activate + - label horizontal_loop + - play_animation horizontal horizontal_slide + - wait_for_animation horizontal + - goto horizontal_loop +operators: + [] \ No newline at end of file diff --git a/assets/test_chambers/test_chamber_05/test_chamber_05.yaml b/assets/test_chambers/test_chamber_05/test_chamber_05.yaml new file mode 100644 index 0000000..b55918d --- /dev/null +++ b/assets/test_chambers/test_chamber_05/test_chamber_05.yaml @@ -0,0 +1,23 @@ +operators: + [] +cutscenes: + INTRO_CUTSCENE: + - set_signal cube_dropper + - open_portal portal_0 0 + - q_sound SOUNDS_05_PART1_ENTRY_1 CH_GLADOS + - q_sound SOUNDS_05_PART1_ENTRY_2 CH_GLADOS + - start_cutscene NAG + - wait_for_signal success + - stop_cutscene NAG + - q_sound SOUNDS_05_PART1_SUCCESS_1 CH_GLADOS + NAG: + - delay 28 + - q_sound SOUNDS_05_PART1_NAG1_1 CH_GLADOS + - delay 20 + - q_sound SOUNDS_05_PART1_NAG2_1 CH_GLADOS + - delay 25 + - q_sound SOUNDS_05_PART1_NAG3_1 CH_GLADOS + - delay 25.001 + - q_sound SOUNDS_05_PART1_NAG4_1 CH_GLADOS + - delay 26 + - q_sound SOUNDS_05_PART1_NAG5_1 CH_GLADOS \ No newline at end of file diff --git a/assets/test_chambers/test_chamber_06/test_chamber_06.yaml b/assets/test_chambers/test_chamber_06/test_chamber_06.yaml new file mode 100644 index 0000000..2a21743 --- /dev/null +++ b/assets/test_chambers/test_chamber_06/test_chamber_06.yaml @@ -0,0 +1,27 @@ +operators: + [] +cutscenes: + THIRD_ROOM: + - clear_signal room_1_exit + - open_portal third_room_portal 0 + - close_portal 1 room2 + - play_animation piston_top piston_top_0 + - play_animation piston_bottom piston_bottom_0 + FIRST_ROOM: + - q_sound SOUNDS_06_PART1_ENTRY_1 CH_GLADOS + - delay 4 + - open_portal first_room_portal 0 + OPEN_FIRST_DOOR: + - set_signal room_0_exit + SECOND_SUCCESS: + - q_sound SOUNDS_06_PART1_SUCCESS_2_1 CH_GLADOS + - play_animation piston_top piston_top_1 + FIRST_SUCCESS: + - q_sound SOUNDS_06_PART1_SUCCESS_1_1 CH_GLADOS + SECOND_ROOM: + - close_portal 0 + - close_portal 1 + - clear_signal room_0_exit + - delay 3 + - set_signal room_1_exit + - open_portal second_room_portal 0 \ No newline at end of file diff --git a/assets/test_chambers/test_chamber_07/test_chamber_07.yaml b/assets/test_chambers/test_chamber_07/test_chamber_07.yaml new file mode 100644 index 0000000..a4e64ef --- /dev/null +++ b/assets/test_chambers/test_chamber_07/test_chamber_07.yaml @@ -0,0 +1,72 @@ +cutscenes: + platform_ferry: + - wait_for_signal move_platform + - delay 1.5 pre_slide + - label anim_loop_start + - play_animation horizontal slide 1 + - wait_for_animation horizontal + - wait_for_signal move_to_gun + - play_animation horizontal slide -1 + - wait_for_signal not_move_to_gun + - goto anim_loop_start + platform_loop: + - label anim_platform_loop_start + - play_animation horizontal slide 1 loop + - wait_for_animation horizontal loop_out + - delay 3 anim_pause + - play_animation horizontal slide -1 slide_ + - wait_for_animation horizontal loop_in + - delay 3 anim_pause_end + - goto anim_platform_loop_start + DROWN_PLAYER: + - kill_player water + START: + - delay 1 + - start_cutscene portal_loop + - start_cutscene platform_ferry + - set_signal launch_ball + - q_sound 07_PART1_ENTRY_1 CH_GLADOS + - q_sound 07_PART1_ENTRY_2 CH_GLADOS + - q_sound 07_PART1_ENTRY_3 CH_GLADOS + TILT: + - delay 4 + - set_signal cube_dropper + - play_animation tilt tilt + START_1: + - q_sound 07_PART2_ENTRY_1 CH_GLADOS + - wait_for_signal weeeee + - q_sound 07_PART2_SUCCESS_1 CH_GLADOS + portal_loop: + - label loop_start + - open_portal portal_0 0 + - delay 3.001 + - point_pedestal portal_1 + - delay 4.001 + - open_portal portal_1 0 + - delay 3 2 + - point_pedestal portal_2 + - delay 4 2 + - open_portal portal_2 0 + - delay 3 3 + - point_pedestal portal_3 + - delay 4 3 + - point_pedestal portal_0 + - delay 4 4 + - goto loop_start + GET_GUN: + - "hide_pedestal " + - stop_cutscene portal_loop + - stop_cutscene platform_ferry + - start_cutscene platform_loop + - close_portal 0 + - q_sound 07_PART1_GET_DEVICE_COMPONENT_1 CH_GLADOS + - q_sound 07_PART1_GET_DEVICE_COMPONENT_2 CH_GLADOS + - q_sound 07_PART1_GET_DEVICE_COMPONENT_3 CH_GLADOS + - wait_for_signal trapped + - q_sound 07_PART1_TRAPPED_1 CH_GLADOS + - q_sound 07_PART1_TRAPPED_2 CH_GLADOS + - wait_for_channel CH_GLADOS + - set_signal trap_door +operators: + - weeeee = exit_1 and flying + - not_move_to_gun = not move_to_gun \ No newline at end of file diff --git a/skelatool64/src/lua_generator/LuaGenerator.cpp b/skelatool64/src/lua_generator/LuaGenerator.cpp index f119015..dfcaf26 100644 --- a/skelatool64/src/lua_generator/LuaGenerator.cpp +++ b/skelatool64/src/lua_generator/LuaGenerator.cpp @@ -8,6 +8,7 @@ #include "LuaMesh.h" #include "LuaDisplayListSettings.h" #include "LuaBasicTypes.h" +#include "LuaYaml.h" #include #include @@ -82,6 +83,7 @@ void generateFromLuaScript( lua_State *L = luaL_newstate(); luaL_openlibs(L); generateLuaTransform(L); + generateLuaYaml(L); populateLuaMesh(L, scene, fileDefinition, settings); lua_getglobal(L, "package"); diff --git a/skelatool64/src/lua_generator/LuaYaml.cpp b/skelatool64/src/lua_generator/LuaYaml.cpp new file mode 100644 index 0000000..05fafca --- /dev/null +++ b/skelatool64/src/lua_generator/LuaYaml.cpp @@ -0,0 +1,176 @@ +#include "LuaYaml.h" + +#include "yaml-cpp/yaml.h" +#include +#include +#include "./LuaUtils.h" + +void toLua(lua_State* L, const YAML::Node& node) { + if (!node.IsDefined() || node.IsNull()) { + lua_pushnil(L); + return; + } + + if (node.IsSequence()) { + lua_createtable(L, node.size(), 0); + + for (unsigned i = 0; i < node.size(); ++i) { + toLua(L, node[i]); + lua_seti(L, -2, i + 1); + } + + return; + } + + if (node.IsMap()) { + lua_createtable(L, 0, node.size()); + + for (auto it = node.begin(); it != node.end(); ++it) { + std::string name = it->first.as(); + toLua(L, node[name]); + lua_setfield(L, -2, name.c_str()); + } + + return; + } + + if (node.IsScalar()) { + std::string asString = node.as(); + + if (asString == "true") { + lua_pushboolean(L, true); + return; + } + + if (asString == "false") { + lua_pushboolean(L, false); + return; + } + + std::istringstream asStream(asString); + + lua_Integer asInt; + + if (!(asStream >> asInt).fail()) { + lua_pushinteger(L, asInt); + return; + } + + asStream = std::istringstream(asString); + + lua_Number asDouble; + if (!(asStream >> asDouble).fail()) { + lua_pushnumber(L, asDouble); + return; + } + + lua_pushstring(L, asString.c_str()); + return; + } + + lua_pushnil(L); +} + +bool luaIsSequence(lua_State* L, int idx) { + if (idx < 0) { + idx = lua_gettop(L) + idx + 1; + } + + lua_pushnil(L); + if (!lua_next(L, idx)) { + // empty sequence + return true; + } + + // first key is not a number + if (!lua_isnumber(L, -2)) { + lua_pop(L, 2); + return false; + } + + lua_pop(L, 2); + + // check iteration after end of sequence + lua_len(L, idx); + if (lua_next(L, idx)) { + // has additional key value pairs + lua_pop(L, 2); + return false; + } + + return true; +} + +void fromLua(lua_State* L, YAML::Node& node) { + switch (lua_type(L, -1)) + { + case LUA_TBOOLEAN: + node = YAML::Node(lua_toboolean(L, -1) ? true : false); + break; + case LUA_TNUMBER: + node = YAML::Node(lua_tonumber(L, -1)); + break; + case LUA_TSTRING: + node = YAML::Node(lua_tostring(L, -1)); + break; + case LUA_TTABLE: + if (luaIsSequence(L, -1)) { + node = YAML::Node(YAML::NodeType::Sequence); + lua_pushnil(L); + while (lua_next(L, -2)) { + YAML::Node element; + fromLua(L, element); + node.push_back(element); + } + } else { + node = YAML::Node(YAML::NodeType::Map); + lua_pushnil(L); + while (lua_next(L, -2)) { + YAML::Node element; + fromLua(L, element); + node[lua_tostring(L, -1)] = element; + } + } + + break; + default: + node = YAML::Node(YAML::NodeType::Null); + break; + } + + lua_pop(L, 1); +} + +int luaYamlParse(lua_State* L) { + const char* value = luaL_checkstring(L, 1); + + YAML::Node doc = YAML::Load(value); + toLua(L, doc); + return 1; +} + +int luaYamlStringify(lua_State* L) { + lua_settop(L, 1); + YAML::Node doc; + fromLua(L, doc); + std::string result = YAML::Dump(doc); + lua_pushstring(L, result.c_str()); + return 1; +} + +int buildYamlModule(lua_State* L) { + lua_newtable(L); + + lua_pushcfunction(L, luaYamlParse); + lua_setfield(L, -2, "parse"); + + lua_pushcfunction(L, luaYamlStringify); + lua_setfield(L, -2, "stringify"); + + return 1; +} + +void generateLuaYaml(lua_State* L) { + lua_pushcfunction(L, buildYamlModule); + luaSetModuleLoader(L, "yaml"); +} \ No newline at end of file diff --git a/skelatool64/src/lua_generator/LuaYaml.h b/skelatool64/src/lua_generator/LuaYaml.h new file mode 100644 index 0000000..97ceb11 --- /dev/null +++ b/skelatool64/src/lua_generator/LuaYaml.h @@ -0,0 +1,8 @@ +#ifndef __LUA_YAML_H__ +#define __LUA_YAML_H__ + +#include + +void generateLuaYaml(lua_State* L); + +#endif \ No newline at end of file diff --git a/tools/export_level.lua b/tools/export_level.lua index fb628f1..34da376 100644 --- a/tools/export_level.lua +++ b/tools/export_level.lua @@ -10,6 +10,14 @@ local entities = require('tools.level_scripts.entities') local signals = require('tools.level_scripts.signals') local animation = require('tools.level_scripts.animation') local dynamic_collision = require('tools.level_scripts.dynamic_collision_export') +local yaml_loader = require('tools.level_scripts.yaml_loader') + +local jsonFile = { + operators = signals.operator_json, + cutscenes = trigger.cutscene_json, +} + +yaml_loader.dump_json(jsonFile) sk_definition_writer.add_definition("level", "struct LevelDefinition", "_geo", { collisionQuads = sk_definition_writer.reference_to(collision_export.collision_objects, 1), diff --git a/tools/level_scripts/signals.lua b/tools/level_scripts/signals.lua index ca2779c..1274389 100644 --- a/tools/level_scripts/signals.lua +++ b/tools/level_scripts/signals.lua @@ -102,10 +102,28 @@ local function generate_operator_data(operator) } end +local function format_operation(operation) + local result = operation.output .. ' = ' + + if operation.type == 'SignalOperatorTypeNot' then + return result .. 'not ' .. operation.input[1] + elseif operation.type == 'SignalOperatorTypeAnd' then + return result .. table.concat(operation.input, ' and ') + elseif operation.type == 'SignalOperatorTypeOr' then + return result .. table.concat(operation.input, ' or ') + end + + return result; +end + local operators = {} +local operator_json = {} + for _, operation in pairs(ordered_operators) do table.insert(operators, generate_operator_data(operation)) + + table.insert(operator_json, format_operation(operation)) end sk_definition_writer.add_definition('signal_operations', 'struct SignalOperator[]', '_geo', operators) @@ -113,4 +131,5 @@ sk_definition_writer.add_definition('signal_operations', 'struct SignalOperator[ return { signal_index_for_name = signal_index_for_name, operators = operators, + operator_json = operator_json, } \ No newline at end of file diff --git a/tools/level_scripts/trigger.lua b/tools/level_scripts/trigger.lua index a616540..9357a6d 100644 --- a/tools/level_scripts/trigger.lua +++ b/tools/level_scripts/trigger.lua @@ -289,6 +289,8 @@ local function generate_cutscenes() local cutscenes_result = {} local cutscene_data = {} + local cutscene_json = {} + for _, cutscene in pairs(cutscenes) do local first_step = cutscene.steps[1] local other_steps = {table.unpack(cutscene.steps, 2)} @@ -297,6 +299,12 @@ local function generate_cutscenes() return distance_from_start(first_step, a) < distance_from_start(first_step, b) end) + local string_steps = {} + + for step_index, step in pairs(other_steps) do + table.insert(string_steps, step.command .. ' ' .. table.concat(step.args, ' ')) + end + local label_locations = find_label_locations(other_steps) local steps = {} @@ -305,6 +313,8 @@ local function generate_cutscenes() table.insert(steps, generate_cutscene_step(cutscene.name, step, step_index, label_locations, cutscenes)) end + cutscene_json[cutscene.name] = string_steps + sk_definition_writer.add_definition(cutscene.name .. '_steps', 'struct CutsceneStep[]', '_geo', steps) table.insert(cutscenes_result, { @@ -319,7 +329,7 @@ local function generate_cutscenes() }) end - return cutscenes_result, cutscene_data + return cutscenes_result, cutscene_data, cutscene_json end local function generate_triggers(cutscenes) @@ -342,7 +352,7 @@ local function generate_triggers(cutscenes) return result end -local cutscenes, cutscene_data = generate_cutscenes() +local cutscenes, cutscene_data, cutscene_json = generate_cutscenes() local triggers = generate_triggers(cutscenes) sk_definition_writer.add_definition("triggers", "struct Trigger[]", "_geo", triggers) @@ -355,6 +365,7 @@ end return { triggers = triggers, cutscene_data = cutscene_data, + cutscene_json = cutscene_json, location_data = location_data, find_location_index = find_location_index, find_cutscene_index = find_cutscene_index, diff --git a/tools/level_scripts/yaml_loader.lua b/tools/level_scripts/yaml_loader.lua new file mode 100644 index 0000000..1d528b5 --- /dev/null +++ b/tools/level_scripts/yaml_loader.lua @@ -0,0 +1,23 @@ +local yaml = require('yaml') +local sk_input = require('sk_input') + +local file_location = string.sub(sk_input.input_filename, 7, -4) .. 'yaml' + +local input_file = io.open(file_location, 'r') +local json_contents = yaml.parse(input_file:read('a')) +input_file:close() + +local function dump_json(value) + local file_contents = yaml.stringify(value) + print(file_contents) + print(file_location) + + local file = io.open(file_location, 'w'); + file:write(file_contents) + file:close() +end + +return { + dump_json = dump_json, + json_contents = json_contents, +} \ No newline at end of file