Compare commits

..

22 Commits

Author SHA1 Message Date
Cameron Murphy Reikes b925dd70fe No angel screen for now, moar todo 1 year ago
Cameron Murphy Reikes 741ceb14f5 Merge remote-tracking branch 'origin/gles3' 1 year ago
Cameron Murphy Reikes 7605d37d72 Merge branch 'main' of https://github.com/creikey/rpgpt 1 year ago
Cameron Murphy Reikes dcf23ca8e4 Add angel and angel dialog, but can't talk to it yet 1 year ago
Cameron Murphy Reikes c45c5c4fb2 Significantly upgraded TODO with notes from people 1 year ago
Cameron Murphy Reikes e3118d2335 Make names into 'you' when it's the character in human readable prompt 1 year ago
Phillip Trudeau-Tavara bc1663006c Alpha-to-coverage foliage 1 year ago
Phillip Trudeau-Tavara 08df997396 Film grain, contrast, vignette, cross-processing 1 year ago
Phillip Trudeau-Tavara 75345ae684 WebGL2; update sokol; Web MSAA; AA Outlines 1 year ago
Phillip Trudeau-Tavara 12965a7818 Change engine title strings to "Dante's Cowboy" 1 year ago
Phillip Trudeau-Tavara 526856a112 Support toggling fullscreen on web 1 year ago
Phillip Trudeau-Tavara 8650329b7f Reduce console printouts 1 year ago
Phillip Trudeau-Tavara bfae0a25bc Create build_and_run_web.bat 1 year ago
Phillip Trudeau-Tavara 56187771ae Passthrough command line arguments to subscripts 1 year ago
Phillip Trudeau-Tavara c3511ba954 Accept command line arguments in build_web_common 1 year ago
Phillip Trudeau-Tavara 6c0a832f61 Link win32 libs in code; add /nologo /diagnostics:caret /noimplib /noexp 1 year ago
Phillip Trudeau-Tavara 92fc09d2d6 Set sample count to 1 on web and 4 on desktop 1 year ago
Phillip Trudeau-Tavara 68cdbaac08 Greatly improve & simplify assertions 1 year ago
Phillip Trudeau-Tavara d5cf3d9f90 Disable glGetError() and keep validation and asserts 1 year ago
Phillip Trudeau-Tavara 718a89a3af Oops teehee 1 year ago
Phillip Trudeau-Tavara 4ee067be9c Update README.md 1 year ago
Phillip Trudeau-Tavara 3df3e8e027 Add automated blender_export.bat script 1 year ago

2
.gitignore vendored

@ -109,3 +109,5 @@ modules.order
Module.symvers
Mkfile.old
dkms.conf
emsdk/

@ -1,10 +1,10 @@
# rpgpt
RPG GPT. Short experience
# [Dante's Cowboy - Wishlist now on Steam!](https://store.steampowered.com/app/2501370/Dantes_Cowboy)
A fantasy western RPG with an immersive and natural dynamic dialogue system powered by GPT.
![Western Frontier](https://upload.wikimedia.org/wikipedia/commons/thumb/3/32/Distribution_of_US_Rural_Population_during_1900.pdf/page1-1280px-Distribution_of_US_Rural_Population_during_1900.pdf.jpg)
# Important Building Steps and Contribution Notes
Every time you checkin/clone the project, you have to unzip art.blend... If this is annoying to you, make a git hook
Every time you checkin/clone the project, make sure to call `blender_export.bat` at least once! This will auto-extract `art\art.blend` and run `art\Exporter.py`, thereby baking, validating, and exporting all the assets and the level.
When editing Exporter.py in either the blender editor, or in a text editor in the repo, you have to continually make sure blender's internal version of the script doesn't go out of date with the actual script on disk, by either saving consistently from blender to disk if you're editing from blender, or by reloading from disk in the blend file before each commit.

Binary file not shown.

@ -6,6 +6,10 @@
{
filepath: "maninblack.png",
}
@image angel:
{
filepath: "angel.png",
}
@image unread_triangle:
{
filepath: "unread_triangle.png",
@ -22,6 +26,10 @@
{
filepath: "simple_text_chirp.wav",
}
@sound angel_grunt_0:
{
filepath: "angel_grunt_0.wav",
}
@sound grunt_0:
{
filepath: "grunt_0.wav",

BIN
assets/angel.png (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

@ -32,6 +32,10 @@
{enum: Daniel, dialog: "Exactly what I'll do to you if you don't keep your mouth shut", to: Raphael, action: ACT_put_shotgun_away}
{enum: Raphael, dialog: "Y-y-y-yes sir.", to: Daniel}
{can_hear: [Devil, Angel]}
{enum: Devil, dialog: "You will fall!", to: Angel}
{enum: Angel, dialog: "Perhaps. And then I will rise as He has, from the corpse and ashes, the sun rises.", to: Devil}
/*
{can_hear: [WellDweller, Farmer, ManInBlack]},
{enum: WellDweller, dialog: "What a fearful farm you live in, come down to the well, the grass is damper down here.", to: Farmer, mood: Scared, thoughts: "Nobody can take me from my well"},

@ -0,0 +1,33 @@
@echo off
pushd %~dp0%\art
if not exist "art.blend" (
powershell Expand-Archive -Path art.zip -DestinationPath . || goto :error
)
set "blender="
if exist "%ProgramFiles%\Blender Foundation\Blender 3.5\blender.exe" (
echo Using Blender 3.5 detected
set "blender=%ProgramFiles%\Blender Foundation\Blender 3.5\blender.exe"
)
if exist "%ProgramFiles%\Blender Foundation\Blender 3.6\blender.exe" (
echo Using Blender 3.6 detected
set "blender=%ProgramFiles%\Blender Foundation\Blender 3.6\blender.exe"
)
if "%blender%" neq "" (
call "%blender%" --background art.blend --python Exporter.py || goto :error
) else (
goto :error
)
goto :success
:error
echo Blender export failed
:success
set "returncode=%ERRORLEVEL%"
popd
exit /B %returncode%

@ -1,5 +1,5 @@
@echo off
START /B remedybg.exe stop-debugging
call build_desktop_debug.bat
call build_desktop_debug.bat %*
remedybg.exe start-debugging

@ -0,0 +1,7 @@
@echo off
call build_web_debug.bat %* || goto :EOF
START "" "http://localhost:8000"
pushd %~dp0%\build_web
python -m http.server
popd

@ -2,10 +2,18 @@
@REM https://learn.microsoft.com/en-us/cpp/build/reference/compiler-options-listed-by-category?view=msvc-170
setlocal enableDelayedExpansion
set "do_blender_export=0"
set "do_codegen=0"
for %%A in (%*) do (
if "%%~A"=="blender_export" ( set "do_blender_export=1" )
if "%%~A"=="codegen" ( set "do_codegen=1" )
)
if "%do_blender_export%"=="1" ( call blender_export.bat || goto :error )
if "%do_codegen%"=="1" ( call run_codegen.bat || goto :error )
if "%1" == "codegen" ( call run_codegen.bat || goto :error ) else ( echo NOT RUNNING CODEGEN )
@REM start /B zig cc -DDEVTOOLS -Igen -Ithirdparty -lDbghelp -lGdi32 -lD3D11 -lOle32 -lwinhttp -gfull -gcodeview -o main_zig.exe main.c
cl /diagnostics:caret /DDEVTOOLS /Igen /Ithirdparty /Wall /FC /Zi /WX Dbghelp.lib winhttp.lib main.c || goto :error
cl /nologo /diagnostics:caret /DDEVTOOLS /Igen /Ithirdparty /Wall /FC /Zi /WX main.c /link /noimplib /noexp || goto :error
goto :EOF

@ -1,12 +1,21 @@
call run_codegen.bat || goto :error
setlocal enableDelayedExpansion
set "do_blender_export=0"
set "do_codegen=0"
for %%A in (%*) do (
if "%%~A"=="blender_export" ( set "do_blender_export=1" )
if "%%~A"=="codegen" ( set "do_codegen=1" )
)
if "%do_blender_export%"=="1" ( call blender_export.bat || goto :error )
if "%do_codegen%"=="1" ( call run_codegen.bat || goto :error )
copy marketing_page\favicon.ico %OUTPUT_FOLDER%\favicon.ico
@REM copy main.c %OUTPUT_FOLDER%\main.c || goto :error
@echo on
@echo off
emcc ^
-sEXPORTED_FUNCTIONS=_main,_end_text_input,_stop_controlling_input,_start_controlling_input,_read_from_save_data,_dump_save_data,_is_receiving_text_input^
-sEXPORTED_RUNTIME_METHODS=ccall,cwrap^
-s USE_WEBGL2=1^
-s INITIAL_MEMORY=62914560^
-s ALLOW_MEMORY_GROWTH -s TOTAL_STACK=15728640^
%FLAGS%^

@ -10,25 +10,16 @@ set FLAGS=-O0 -g -DDEVTOOLS
set OUTPUT_FOLDER=build_web
if "%1" == "NO_VALIDATION" (
echo Disabling graphics validation...
set FLAGS=%FLAGS% -DNDEBUG
)
call build_web_common.bat || goto :error
call build_web_common.bat %* || goto :error
@echo off
if "%1" == "NO_VALIDATION" (
echo Validation turned off
) else (
echo If you want to turn graphics validation off to make web debug build faster, provide a command line argument called "NO_VALIDATION" to this build script
)
goto :success
:error
echo Failed to build
:success
set "returncode=%ERRORLEVEL%"
popd
exit /B %ERRORLEVEL%
exit /B %returncode%

@ -5,17 +5,17 @@ pushd %~dp0%
rmdir /S /q build_web_release
mkdir build_web_release
call run_codegen.bat || goto :error
set FLAGS=-O0 -DNDEBUG
set OUTPUT_FOLDER=build_web_release
call build_web_common.bat || goto :error
call build_web_common.bat %* codegen || goto :error
goto :success
:error
echo Failed to build
:success
set "returncode=%ERRORLEVEL%"
popd
exit /B %ERRORLEVEL%
exit /B %returncode%

@ -29,6 +29,11 @@ ActionInfo actions[] = {
.description = "Do nothing",
NO_ARGUMENT,
},
{
.name = "approach",
.description = "There has been an error, please report to the developer. No NPCs should be able to do this",
ARGUMENT("Expects the argument to be who you're approaching"),
},
{
.name = "join",
.description = "Joins somebody else's party, so you follow them everywhere",
@ -101,4 +106,9 @@ CharacterGen characters[] = {
.enum_name = "Passerby",
.prompt = CHARACTER_PROMPT_PREFIX("Random Passerby") "random person, just passing by",
},
{
.name = "Angel",
.enum_name = "Angel",
.prompt = CHARACTER_PROMPT_PREFIX("Angel") "mysterious, radiant, mystical creature the player first talks to. You guide the entire game: deciding on an objective for the player to fulfill until you believe they've learned their lesson, whatever that means to them.",
},
};

381
main.c

@ -21,11 +21,13 @@
#define DESKTOP
#define WINDOWS
#define SOKOL_GLCORE33
#define SAMPLE_COUNT 4
#endif
#if defined(__EMSCRIPTEN__)
#define WEB
#define SOKOL_GLES2
#define SOKOL_GLES3
#define SAMPLE_COUNT 4
#endif
#define DRWAV_ASSERT game_assert
@ -41,8 +43,6 @@
#pragma warning(push, 3)
#include <Windows.h>
#include <processthreadsapi.h>
#include <dbghelp.h>
#include <stdint.h>
// https://developer.download.nvidia.com/devzone/devcenter/gamegraphics/files/OptimusRenderingPolicies.pdf
@ -53,13 +53,15 @@ __declspec(dllexport) uint32_t AmdPowerXpressRequestHighPerformance = 0x00000001
#endif
#define STRINGIZE(x) STRINGIZE2(x)
#define STRINGIZE2(x) #x
#include "buff.h"
#include "sokol_app.h"
#pragma warning(push)
#pragma warning(disable : 4191) // unsafe function calling
#ifdef WEB
# include <GLES3/gl3.h>
# undef glGetError
# define glGetError() (GL_NO_ERROR)
#endif
#include "sokol_gfx.h"
#pragma warning(pop)
#include "sokol_time.h"
@ -365,6 +367,7 @@ float float_rand(float min, float max)
float scale = rand() / (float) RAND_MAX; /* [0, 1.0] */
return min + scale * (max - min); /* [min, max] */
}
// always randomizes pitch
void play_audio(AudioSample *sample, float volume)
{
AudioPlayer *to_use = 0;
@ -466,6 +469,7 @@ LPCWSTR windows_string(MD_String8 s)
#ifdef DESKTOP
#ifdef WINDOWS
#pragma warning(push, 3)
#pragma comment(lib, "WinHttp")
#include <WinHttp.h>
#include <process.h>
#pragma warning(pop)
@ -835,18 +839,15 @@ sg_image load_image(MD_String8 path)
{
.width = png_width,
.height = png_height,
.pixel_format = SG_PIXELFORMAT_RGBA8,
.min_filter = SG_FILTER_LINEAR,
.pixel_format = sapp_sgcontext().color_format,
.num_mipmaps = 1,
.wrap_u = SG_WRAP_CLAMP_TO_EDGE,
.wrap_v = SG_WRAP_CLAMP_TO_EDGE,
.mag_filter = SG_FILTER_LINEAR,
.data.subimage[0][0] =
{
.ptr = pixels,
.size = (size_t)(png_width * png_height * num_channels),
}
});
loaded->image = to_return;
MD_ReleaseScratch(scratch);
return to_return;
@ -1225,14 +1226,6 @@ Armature load_armature(MD_Arena *arena, MD_String8 binary_file, MD_String8 armat
.width = to_return.bones_texture_width,
.height = to_return.bones_texture_height,
.pixel_format = SG_PIXELFORMAT_RGBA8,
.min_filter = SG_FILTER_NEAREST,
.mag_filter = SG_FILTER_NEAREST,
// for webgl NPOT texures https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Tutorial/Using_textures_in_WebGL
.wrap_u = SG_WRAP_CLAMP_TO_EDGE,
.wrap_v = SG_WRAP_CLAMP_TO_EDGE,
.wrap_w = SG_WRAP_CLAMP_TO_EDGE,
.usage = SG_USAGE_STREAM,
});
@ -2530,7 +2523,7 @@ GameState load_from_string(MD_Arena *arena, MD_Arena *error_arena, MD_String8 da
.arena = temp.arena,
.error_arena = error_arena,
};
GameState to_return = {0};
GameState to_return = { 0 };
ser_GameState(&ser, &to_return);
if(ser.cur_error.failed)
{
@ -2654,26 +2647,34 @@ Shadow_State init_shadow_state();
static struct
{
sg_pass_action clear_everything_pass_action;
sg_pass_action threedee_msaa_pass_action;
sg_pass_action clear_depth_buffer_pass_action;
sg_pipeline twodee_pip;
sg_bindings bind;
sg_pipeline threedee_pip;
sg_pipeline threedee_alpha_blended_pip;
sg_pipeline armature_pip;
sg_bindings threedee_bind;
sg_image outline_pass_image;
sg_image outline_pass_resolve_image;
sg_pass outline_pass;
sg_pipeline outline_mesh_pip;
sg_pipeline outline_armature_pip;
sg_pass threedee_pass; // is a pass so I can do post processing in a shader
sg_image threedee_pass_image;
sg_image threedee_pass_resolve_image;
sg_image threedee_pass_depth_image;
sg_pipeline twodee_outline_pip;
sg_pipeline twodee_colorcorrect_pip;
sg_sampler sampler_linear;
sg_sampler sampler_linear_border;
sg_sampler sampler_nearest;
Shadow_State shadows;
} state;
@ -2688,7 +2689,9 @@ void create_screenspace_gfx_state()
MAYBE_DESTROY(state.threedee_pass, sg_destroy_pass);
MAYBE_DESTROY(state.outline_pass_image, sg_destroy_image);
MAYBE_DESTROY(state.outline_pass_resolve_image, sg_destroy_image);
MAYBE_DESTROY(state.threedee_pass_image, sg_destroy_image);
MAYBE_DESTROY(state.threedee_pass_resolve_image, sg_destroy_image);
MAYBE_DESTROY(state.threedee_pass_depth_image, sg_destroy_image);
#undef MAYBE_DESTROY
@ -2702,7 +2705,7 @@ void create_screenspace_gfx_state()
.depth = {
0
},
.sample_count = 1,
.sample_count = SAMPLE_COUNT,
.layout = {
.attrs =
{
@ -2732,7 +2735,7 @@ void create_screenspace_gfx_state()
.depth = {
.pixel_format = SG_PIXELFORMAT_NONE,
},
.sample_count = 1,
.sample_count = SAMPLE_COUNT,
.layout = {
.attrs =
{
@ -2758,18 +2761,17 @@ void create_screenspace_gfx_state()
.render_target = true,
.width = sapp_width(),
.height = sapp_height(),
.pixel_format = SG_PIXELFORMAT_RGBA8,
.min_filter = SG_FILTER_LINEAR,
.mag_filter = SG_FILTER_LINEAR,
.wrap_u = SG_WRAP_CLAMP_TO_BORDER,
.wrap_v = SG_WRAP_CLAMP_TO_BORDER,
.border_color = SG_BORDERCOLOR_OPAQUE_WHITE,
.sample_count = 1,
.pixel_format = sapp_sgcontext().color_format,
.label = "outline-pass-render-target",
};
desc.sample_count = SAMPLE_COUNT;
state.outline_pass_image = sg_make_image(&desc);
desc.sample_count = 1;
state.outline_pass_resolve_image = sg_make_image(&desc);
state.outline_pass = sg_make_pass(&(sg_pass_desc){
.color_attachments[0].image = state.outline_pass_image,
.resolve_attachments[0].image = state.outline_pass_resolve_image,
.depth_stencil_attachment = {
0
},
@ -2779,14 +2781,21 @@ void create_screenspace_gfx_state()
desc.sample_count = 1;
desc.label = "threedee-pass-render-target";
desc.sample_count = SAMPLE_COUNT;
state.threedee_pass_image = sg_make_image(&desc);
desc.label = "threedee-pass-resolve-render-target";
desc.sample_count = 1;
state.threedee_pass_resolve_image = sg_make_image(&desc);
desc.label = "threedee-pass-depth-render-target";
desc.pixel_format = sapp_depth_format();
desc.pixel_format = SG_PIXELFORMAT_DEPTH;
desc.sample_count = SAMPLE_COUNT;
state.threedee_pass_depth_image = sg_make_image(&desc);
state.threedee_pass = sg_make_pass(&(sg_pass_desc){
.color_attachments[0].image = state.threedee_pass_image,
.resolve_attachments[0].image = state.threedee_pass_resolve_image,
.depth_stencil_attachment = (sg_pass_attachment_desc){
.image = state.threedee_pass_depth_image,
.mip_level = 0,
@ -3006,8 +3015,8 @@ Color blendalpha(Color c, float alpha)
// in pixels
Vec2 img_size(sg_image img)
{
sg_image_info info = sg_query_image_info(img);
return V2((float)info.width, (float)info.height);
sg_image_desc desc = sg_query_image_desc(img);
return V2((float)desc.width, (float)desc.height);
}
#ifdef DEVTOOLS
@ -3129,8 +3138,6 @@ LoadedFont load_font(MD_Arena *arena, MD_String8 font_filepath, float font_size)
.width = font_bitmap_width,
.height = font_bitmap_width,
.pixel_format = SG_PIXELFORMAT_RGBA8,
.min_filter = SG_FILTER_LINEAR,
.mag_filter = SG_FILTER_LINEAR,
.data.subimage[0][0] =
{
.ptr = font_bitmap_rgba,
@ -3158,6 +3165,7 @@ Armature player_armature = {0};
Armature farmer_armature = {0};
Armature shifted_farmer_armature = {0};
Armature man_in_black_armature = {0};
Armature angel_armature = {0};
// armatureanimations are processed once every visual frame from this list
Armature *armatures[] = {
@ -3165,6 +3173,7 @@ Armature *armatures[] = {
&farmer_armature,
&shifted_farmer_armature,
&man_in_black_armature,
&angel_armature,
};
Mesh mesh_player = {0};
@ -3205,6 +3214,7 @@ void init(void)
sg_setup(&(sg_desc) {
.context = sapp_sgcontext(),
.buffer_pool_size = 512,
.logger.func = slog_func,
});
stm_setup();
saudio_setup(&(saudio_desc) {
@ -3232,6 +3242,9 @@ void init(void)
man_in_black_armature = load_armature(persistent_arena, binary_file, MD_S8Lit("Man In Black"));
man_in_black_armature.image = image_man_in_black;
angel_armature = load_armature(persistent_arena, binary_file, MD_S8Lit("Angel"));
angel_armature.image = image_angel;
binary_file = MD_LoadEntireFile(frame_arena, MD_S8Lit("assets/exported_3d/Farmer.bin"));
farmer_armature = load_armature(persistent_arena, binary_file, MD_S8Lit("Farmer.bin"));
@ -3362,29 +3375,38 @@ void init(void)
assert(desc);
shd = sg_make_shader(desc);
state.threedee_pip = sg_make_pipeline(&(sg_pipeline_desc){
.shader = shd,
.layout = {.attrs = {
[ATTR_threedee_vs_pos_in].format = SG_VERTEXFORMAT_FLOAT3,
[ATTR_threedee_vs_uv_in].format = SG_VERTEXFORMAT_FLOAT2,
}},
.depth = {
.pixel_format = sapp_depth_format(),
.compare = SG_COMPAREFUNC_LESS_EQUAL,
.write_enabled = true,
},
.colors[0].blend = (sg_blend_state){
{
sg_pipeline_desc threedee_pip_desc = {
.shader = shd,
.layout = {.attrs = {
[ATTR_threedee_vs_pos_in].format = SG_VERTEXFORMAT_FLOAT3,
[ATTR_threedee_vs_uv_in].format = SG_VERTEXFORMAT_FLOAT2,
}},
.depth = {
.pixel_format = SG_PIXELFORMAT_DEPTH,
.compare = SG_COMPAREFUNC_LESS_EQUAL,
.write_enabled = true,
},
.alpha_to_coverage_enabled = true,
.sample_count = SAMPLE_COUNT,
.colors[0].blend.enabled = false,
.label = "threedee",
};
state.threedee_pip = sg_make_pipeline(&threedee_pip_desc);
threedee_pip_desc.depth.write_enabled = false;
threedee_pip_desc.alpha_to_coverage_enabled = false;
threedee_pip_desc.colors[0].blend = (sg_blend_state){
// allow transparency
.enabled = true,
.src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA,
.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,
.op_rgb = SG_BLENDOP_ADD,
.src_factor_alpha = SG_BLENDFACTOR_ONE,
.src_factor_alpha = SG_BLENDFACTOR_SRC_ALPHA,
.dst_factor_alpha = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,
.op_alpha = SG_BLENDOP_ADD,
},
.label = "threedee",
});
state.threedee_alpha_blended_pip = sg_make_pipeline(&threedee_pip_desc);
}
desc = threedee_armature_shader_desc(sg_query_backend());
assert(desc);
@ -3394,10 +3416,11 @@ void init(void)
{
.shader = shd,
.depth = {
.pixel_format = sapp_depth_format(),
.pixel_format = SG_PIXELFORMAT_DEPTH,
.compare = SG_COMPAREFUNC_LESS_EQUAL,
.write_enabled = true
},
.sample_count = SAMPLE_COUNT,
.layout = {
.attrs =
{
@ -3421,11 +3444,45 @@ void init(void)
state.clear_depth_buffer_pass_action = (sg_pass_action)
{
.colors[0] = { .action = SG_ACTION_LOAD },
.depth = { .action = SG_ACTION_CLEAR, .value = 1.0f },
.colors[0] = { .load_action = SG_LOADACTION_LOAD },
.depth = { .load_action = SG_LOADACTION_CLEAR, .clear_value = 1.0f },
};
state.clear_everything_pass_action = state.clear_depth_buffer_pass_action;
state.clear_everything_pass_action.colors[0] = (sg_color_attachment_action){ .action = SG_ACTION_CLEAR, .value = { clearcol.r, clearcol.g, clearcol.b, 1.0f } };
state.clear_everything_pass_action.colors[0] = (sg_color_attachment_action){ .load_action = SG_LOADACTION_CLEAR, .clear_value = { clearcol.r, clearcol.g, clearcol.b, 1.0f } };
state.threedee_msaa_pass_action = state.clear_everything_pass_action;
state.threedee_msaa_pass_action.colors[0].load_action = SG_LOADACTION_CLEAR;
state.threedee_msaa_pass_action.colors[0].store_action = SG_STOREACTION_DONTCARE;
state.sampler_linear = sg_make_sampler(&(sg_sampler_desc) {
.min_filter = SG_FILTER_LINEAR,
.mag_filter = SG_FILTER_LINEAR,
// .mipmap_filter = SG_FILTER_LINEAR,
.wrap_u = SG_WRAP_CLAMP_TO_EDGE,
.wrap_v = SG_WRAP_CLAMP_TO_EDGE,
// .max_anisotropy = 16,
.label = "sampler-linear",
});
state.sampler_linear_border = sg_make_sampler(&(sg_sampler_desc) {
.min_filter = SG_FILTER_LINEAR,
.mag_filter = SG_FILTER_LINEAR,
.wrap_u = SG_WRAP_CLAMP_TO_BORDER,
.wrap_v = SG_WRAP_CLAMP_TO_BORDER,
.border_color = SG_BORDERCOLOR_OPAQUE_WHITE,
.label = "sampler-linear-border",
});
state.sampler_nearest = sg_make_sampler(&(sg_sampler_desc) {
.min_filter = SG_FILTER_NEAREST,
.mag_filter = SG_FILTER_NEAREST,
// for webgl NPOT texures https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Tutorial/Using_textures_in_WebGL
.wrap_u = SG_WRAP_CLAMP_TO_EDGE,
.wrap_v = SG_WRAP_CLAMP_TO_EDGE,
.wrap_w = SG_WRAP_CLAMP_TO_EDGE,
.label = "sampler-nearest",
});
}
Vec2 screen_size()
@ -3716,9 +3773,16 @@ void flush_quad_batch()
state.bind.vertex_buffer_offsets[0] = sg_append_buffer(state.bind.vertex_buffers[0], &(sg_range) { cur_batch_data, cur_batch_data_index*sizeof(*cur_batch_data) });
state.bind.fs_images[SLOT_threedee_twodee_tex] = cur_batch_image;
state.bind.fs.images[SLOT_threedee_twodee_tex] = cur_batch_image; // NOTE that this might get FUCKED if a custom pipeline is provided with more/less texture slots!!!
state.bind.fs.samplers[SLOT_threedee_fs_twodee_smp] = state.sampler_linear; // NOTE that this might get FUCKED if a custom pipeline is provided with more/less sampler slots!!!
sg_apply_bindings(&state.bind);
cur_batch_params.tex_size = img_size(cur_batch_image);
cur_batch_params.screen_size = screen_size();
#if defined(SOKOL_GLCORE33) || defined(SOKOL_GLES3)
cur_batch_params.flip_and_swap_rgb = 0;
#else
cur_batch_params.flip_and_swap_rgb = 1;
#endif
sg_apply_uniforms(SG_SHADERSTAGE_FS, SLOT_threedee_twodee_fs_params, &SG_RANGE(cur_batch_params));
cur_batch_params.tex_size = V2(0,0); // unsure if setting the tex_size to something nonzero fucks up the batching so I'm just resetting it back here
assert(cur_batch_data_index % FLOATS_PER_VERTEX == 0);
@ -3731,10 +3795,11 @@ void flush_quad_batch()
typedef enum
{
LAYER_TILEMAP,
LAYER_INVALID,
LAYER_WORLD,
LAYER_UI,
LAYER_UI_FG,
LAYER_UI_TEXTINPUT,
LAYER_SCREENSPACE_EFFECTS,
LAYER_LAST
@ -3743,9 +3808,11 @@ typedef enum
typedef BUFF(char, 200) StacktraceElem;
typedef BUFF(StacktraceElem, 16) StacktraceInfo;
#if 0 // #ifdef WINDOWS
#include <dbghelp.h>
#pragma comment(lib, "DbgHelp")
StacktraceInfo get_stacktrace()
{
#ifdef WINDOWS
StacktraceInfo to_return = {0};
void *stack[ARRLEN(to_return.data)] = {0};
int captured = CaptureStackBackTrace(0, ARRLEN(to_return.data), stack, 0);
@ -3779,10 +3846,13 @@ StacktraceInfo get_stacktrace()
free(symbol);
}
return to_return;
}
#else
StacktraceInfo get_stacktrace()
{
return (StacktraceInfo){0};
#endif
}
#endif
typedef struct DrawParams
{
@ -3965,7 +4035,7 @@ void line(Vec2 from, Vec2 to, float line_width, Color color)
}
#ifdef DEVTOOLS
bool show_devtools = true;
bool show_devtools = false;
#ifdef PROFILING
extern bool profiling;
#else
@ -4204,6 +4274,7 @@ typedef struct TextParams
Color *colors; // color per character, if not null must be array of same length as text
bool do_clipping;
LoadedFont *use_font; // if null, uses default font
Layer layer;
} TextParams;
// returns bounds. To measure text you can set dry run to true and get the bounds
@ -4297,7 +4368,12 @@ AABB draw_text(TextParams t)
col = t.colors[i];
}
draw_quad((DrawParams) { to_draw, font.image, font_atlas_region, col, t.clip_to, .layer = LAYER_UI_FG, .do_clipping = t.do_clipping });
Layer to_use = LAYER_UI_FG;
if(t.layer != LAYER_INVALID)
{
to_use = t.layer;
}
draw_quad((DrawParams) { to_draw, font.image, font_atlas_region, col, t.clip_to, .layer = to_use, .do_clipping = t.do_clipping });
}
}
}
@ -4815,8 +4891,8 @@ Shadow_State init_shadow_state() {
shadows.pass_action = (sg_pass_action) {
.colors[0] = {
.action = SG_ACTION_CLEAR,
.value = { 1.0f, 1.0f, 1.0f, 1.0f }
.load_action = SG_LOADACTION_CLEAR,
.clear_value = { 1.0f, 1.0f, 1.0f, 1.0f }
}
};
@ -4826,17 +4902,13 @@ Shadow_State init_shadow_state() {
be pertinent to just dig into sokol and add the functionality we want later,
but as a first pass, we will just do as the romans do. I.e. have both a colour
and depth component. - Canada Day 2023.
TODO: with GLES3, depth only rendering passes are now supported
*/
sg_image_desc img_desc = {
.render_target = true,
.width = SHADOW_MAP_DIMENSION,
.height = SHADOW_MAP_DIMENSION,
.pixel_format = SG_PIXELFORMAT_RGBA8,
.min_filter = SG_FILTER_LINEAR,
.mag_filter = SG_FILTER_LINEAR,
.wrap_u = SG_WRAP_CLAMP_TO_BORDER,
.wrap_v = SG_WRAP_CLAMP_TO_BORDER,
.border_color = SG_BORDERCOLOR_OPAQUE_WHITE,
.pixel_format = sapp_sgcontext().color_format,
.sample_count = 1,
.label = "shadow-map-color-image"
};
@ -4867,7 +4939,7 @@ Shadow_State init_shadow_state() {
.compare = SG_COMPAREFUNC_LESS_EQUAL,
.write_enabled = true,
},
.colors[0].pixel_format = SG_PIXELFORMAT_RGBA8,
.colors[0].pixel_format = sapp_sgcontext().color_format,
.label = "shadow-map-pipeline"
};
@ -4875,7 +4947,7 @@ Shadow_State init_shadow_state() {
desc.label = "armature-shadow-map-pipeline";
desc.shader = sg_make_shader(threedee_armature_shadow_mapping_shader_desc(sg_query_backend()));
sg_vertex_attr_desc skeleton_vertex_attrs[] = {
sg_vertex_attr_state skeleton_vertex_attrs[] = {
[ATTR_threedee_vs_skeleton_pos_in].format = SG_VERTEXFORMAT_FLOAT3,
[ATTR_threedee_vs_skeleton_uv_in].format = SG_VERTEXFORMAT_FLOAT2,
[ATTR_threedee_vs_skeleton_indices_in].format = SG_VERTEXFORMAT_USHORT4N,
@ -5075,12 +5147,22 @@ void actually_draw_thing(DrawnThing *it, Mat4 light_space_matrix, bool for_outli
{
if(for_outline)
sg_apply_pipeline(state.outline_mesh_pip);
else if (it->alpha_blend)
sg_apply_pipeline(state.threedee_alpha_blended_pip);
else
sg_apply_pipeline(state.threedee_pip);
sg_bindings bindings = {0};
bindings.fs_images[SLOT_threedee_tex] = it->mesh->image;
if(!for_outline)
bindings.fs_images[SLOT_threedee_shadow_map] = state.shadows.color_img;
bindings.fs.images[SLOT_threedee_tex] = it->mesh->image;
if(for_outline)
{
bindings.fs.samplers[SLOT_threedee_fs_outline_smp] = state.sampler_linear;
}
else
{
bindings.fs.images[SLOT_threedee_shadow_map] = state.shadows.color_img;
bindings.fs.samplers[SLOT_threedee_fs_shadow_smp] = state.sampler_linear_border;
bindings.fs.samplers[SLOT_threedee_fs_smp] = state.sampler_linear;
}
bindings.vertex_buffers[0] = it->mesh->loaded_buffer;
sg_apply_bindings(&bindings);
@ -5106,10 +5188,19 @@ void actually_draw_thing(DrawnThing *it, Mat4 light_space_matrix, bool for_outli
else
sg_apply_pipeline(state.armature_pip);
sg_bindings bindings = {0};
bindings.vs_images[SLOT_threedee_bones_tex] = it->armature->bones_texture;
bindings.fs_images[SLOT_threedee_tex] = it->armature->image;
if(!for_outline)
bindings.fs_images[SLOT_threedee_shadow_map] = state.shadows.color_img;
bindings.vs.images[SLOT_threedee_bones_tex] = it->armature->bones_texture;
bindings.vs.samplers[SLOT_threedee_vs_skeleton_smp] = state.sampler_nearest;
bindings.fs.images[SLOT_threedee_tex] = it->armature->image;
if(for_outline)
{
bindings.fs.samplers[SLOT_threedee_fs_outline_smp] = state.sampler_linear;
}
else
{
bindings.fs.images[SLOT_threedee_shadow_map] = state.shadows.color_img;
bindings.fs.samplers[SLOT_threedee_fs_shadow_smp] = state.sampler_linear_border;
bindings.fs.samplers[SLOT_threedee_fs_smp] = state.sampler_linear;
}
bindings.vertex_buffers[0] = it->armature->loaded_buffer;
sg_apply_bindings(&bindings);
@ -5253,7 +5344,8 @@ void flush_all_drawn_things(Vec3 light_dir, Vec3 cam_pos, Vec3 cam_facing, Vec3
if(it->mesh)
{
sg_bindings bindings = {0};
bindings.fs_images[SLOT_threedee_tex] = it->mesh->image;
bindings.fs.images[SLOT_threedee_tex] = it->mesh->image;
bindings.fs.samplers[SLOT_threedee_fs_shadow_mapping_smp] = state.sampler_linear;
bindings.vertex_buffers[0] = it->mesh->loaded_buffer;
sg_apply_bindings(&bindings);
@ -5278,8 +5370,10 @@ void flush_all_drawn_things(Vec3 light_dir, Vec3 cam_pos, Vec3 cam_facing, Vec3
if(it->armature)
{
sg_bindings bindings = {0};
bindings.vs_images[SLOT_threedee_bones_tex] = it->armature->bones_texture;
bindings.fs_images[SLOT_threedee_tex] = it->armature->image;
bindings.vs.images[SLOT_threedee_bones_tex] = it->armature->bones_texture;
bindings.vs.samplers[SLOT_threedee_vs_skeleton_smp] = state.sampler_nearest;
bindings.fs.images[SLOT_threedee_tex] = it->armature->image;
bindings.fs.samplers[SLOT_threedee_fs_shadow_mapping_smp] = state.sampler_linear;
bindings.vertex_buffers[0] = it->armature->loaded_buffer;
sg_apply_bindings(&bindings);
@ -5305,8 +5399,8 @@ void flush_all_drawn_things(Vec3 light_dir, Vec3 cam_pos, Vec3 cam_facing, Vec3
{
sg_begin_pass(state.outline_pass, &(sg_pass_action) {
.colors[0] = {
.action = SG_ACTION_CLEAR,
.value = { 0.0f, 0.0f, 0.0f, 0.0f },
.load_action = SG_LOADACTION_CLEAR,
.clear_value = { 0.0f, 0.0f, 0.0f, 0.0f },
}
});
@ -5323,7 +5417,7 @@ void flush_all_drawn_things(Vec3 light_dir, Vec3 cam_pos, Vec3 cam_facing, Vec3
// actually draw, IMPORTANT after this drawn_this_frame is zeroed out!
{
sg_begin_pass(state.threedee_pass, &state.clear_everything_pass_action);
sg_begin_pass(state.threedee_pass, &state.threedee_msaa_pass_action);
// draw meshes
SLICE_ITER(DrawnThing, drawn_this_frame)
@ -5570,6 +5664,8 @@ void frame(void)
to_use = &farmer_armature;
else if(it->npc_kind == NPC_Raphael)
to_use = &man_in_black_armature;
else if(it->npc_kind == NPC_Angel)
to_use = &angel_armature;
else
assert(false);
@ -5662,8 +5758,8 @@ void frame(void)
flush_all_drawn_things(light_dir, cam_pos, facing, right);
// draw the 3d render
draw_quad((DrawParams){quad_at(V2(0.0, screen_size().y), screen_size()), IMG(state.threedee_pass_image), WHITE, .layer = LAYER_WORLD, .custom_pipeline = state.twodee_colorcorrect_pip });
draw_quad((DrawParams){quad_at(V2(0.0, screen_size().y), screen_size()), IMG(state.outline_pass_image), WHITE, .custom_pipeline = state.twodee_outline_pip, .layer = LAYER_UI});
draw_quad((DrawParams){quad_at(V2(0.0, screen_size().y), screen_size()), IMG(state.threedee_pass_resolve_image), WHITE, .layer = LAYER_WORLD, .custom_pipeline = state.twodee_colorcorrect_pip });
draw_quad((DrawParams){quad_at(V2(0.0, screen_size().y), screen_size()), IMG(state.outline_pass_resolve_image), WHITE, .custom_pipeline = state.twodee_outline_pip, .layer = LAYER_UI});
// 2d drawing TODO move this to when the drawing is flushed.
sg_begin_default_pass(&state.clear_depth_buffer_pass_action, sapp_width(), sapp_height());
@ -5671,15 +5767,15 @@ void frame(void)
// @Place(text input drawing)
#ifdef DESKTOP
draw_quad((DrawParams){quad_at(V2(0,screen_size().y), screen_size()), IMG(image_white_square), blendalpha(BLACK, text_input_fade*0.3f), .layer = LAYER_UI_FG});
draw_quad((DrawParams){quad_at(V2(0,screen_size().y), screen_size()), IMG(image_white_square), blendalpha(BLACK, text_input_fade*0.3f), .layer = LAYER_UI_TEXTINPUT});
Vec2 edge_of_text = MulV2F(screen_size(), 0.5f);
if(text_input_buffer_length > 0)
{
AABB bounds = draw_centered_text((TextParams){false, MD_S8(text_input_buffer, text_input_buffer_length), MulV2F(screen_size(), 0.5f), blendalpha(WHITE, text_input_fade), 1.0f, .use_font = &font_for_text_input});
AABB bounds = draw_centered_text((TextParams){false, MD_S8(text_input_buffer, text_input_buffer_length), MulV2F(screen_size(), 0.5f), blendalpha(WHITE, text_input_fade), 1.0f, .use_font = &font_for_text_input, .layer = LAYER_UI_TEXTINPUT});
edge_of_text = bounds.lower_right;
}
Vec2 cursor_center = V2(edge_of_text.x,screen_size().y/2.0f);
draw_quad((DrawParams){quad_centered(cursor_center, V2(3.0f, 80.0f)), IMG(image_white_square), blendalpha(WHITE, text_input_fade * (sinf((float)elapsed_time*8.0f)/2.0f + 0.5f)), .layer = LAYER_UI_FG});
draw_quad((DrawParams){quad_centered(cursor_center, V2(3.0f, 80.0f)), IMG(image_white_square), blendalpha(WHITE, text_input_fade * (sinf((float)elapsed_time*8.0f)/2.0f + 0.5f)), .layer = LAYER_UI_TEXTINPUT});
#endif
// Draw Tilemap draw tilemap tilemap drawing
@ -5877,10 +5973,8 @@ void frame(void)
static bool player_in_combat = false;
float speed_target = 1.0f;
gs.stopped_time = cur_unread_entity != 0;
gs.stopped_time = cur_unread_entity != 0 || (!gs.no_angel_screen);
if(gs.stopped_time) speed_target = 0.0f;
// pausing the game
speed_factor = Lerp(speed_factor, unwarped_dt*10.0f, speed_target);
@ -6851,6 +6945,76 @@ ISANERROR("Don't know how to do this stuff on this platform.")
draw_centered_text((TextParams){false, MD_S8Lit("The AI server is having technical difficulties..."), text_center, WHITE, 1.0f });
}
// angel screen
gs.no_angel_screen = true;
{
static float visible = 1.0f;
bool should_be_visible = !gs.no_angel_screen;
visible = Lerp(visible, unwarped_dt*2.0f, should_be_visible ? 1.0f : 0.0f);
Entity *angel_entity = 0;
ENTITIES_ITER(gs.entities)
{
if(it->is_npc && it->npc_kind == NPC_Angel)
{
assert(!angel_entity);
angel_entity = it;
}
}
assert(angel_entity);
if(should_be_visible) gs.player->talking_to = frome(angel_entity);
draw_quad((DrawParams) {quad_at(V2(0,screen_size().y), screen_size()), IMG(image_white_square), blendalpha(BLACK, visible), .layer = LAYER_UI_FG});
static MD_String8List to_say = {0};
static double cur_characters = 0;
if(to_say.node_count == 0)
{
to_say = split_by_word(persistent_arena, MD_S8Lit("You've been asleep for a long long time..."));
cur_characters = 0;
}
MD_String8 cur_word = {0};
MD_String8Node *cur_word_node = 0;
double chars_said = cur_characters;
for(MD_String8Node *cur = to_say.first; cur; cur = cur->next)
{
if((int)chars_said < cur->string.size)
{
cur_word = cur->string;
cur_word_node = cur;
break;
}
chars_said -= (double)cur->string.size;
}
if(!cur_word.str)
{
cur_word = to_say.last->string;
cur_word_node = to_say.last;
}
cur_characters += unwarped_dt*ANGEL_CHARACTERS_PER_SEC;
chars_said += unwarped_dt*ANGEL_CHARACTERS_PER_SEC;
if(chars_said > cur_word.size && cur_word_node->next)
{
play_audio(&sound_angel_grunt_0, 1.0f);
}
assert(cur_word_node);
MD_String8Node *prev_next = cur_word_node->next;
cur_word_node->next = 0;
MD_String8 without_unsaid = MD_S8ListJoin(frame_arena, to_say, &(MD_StringJoin){.mid = MD_S8Lit(" ")});
cur_word_node->next = prev_next;
draw_centered_text((TextParams){false, without_unsaid, V2(screen_size().x*0.5f, screen_size().y*0.75f), blendalpha(WHITE, visible), 1.0f, .use_font = &font_for_text_input});
draw_centered_text((TextParams){false, MD_S8Lit("(Press E to speak)"), V2(screen_size().x*0.5f, screen_size().y*0.25f), blendalpha(WHITE, visible*0.5f), 0.8f, .use_font = &font_for_text_input});
if(pressed.interact)
{
begin_text_input();
}
}
// win screen
{
static float visible = 0.0f;
@ -6937,7 +7101,7 @@ ISANERROR("Don't know how to do this stuff on this platform.")
{
Vec2 depth_size = V2(200.0f, 200.0f);
draw_quad((DrawParams){quad_at(V2(screen_size().x - depth_size.x, screen_size().y), depth_size), IMG(state.shadows.color_img), WHITE, .layer = LAYER_UI_FG});
draw_quad((DrawParams){quad_at(V2(0.0, screen_size().y/2.0f), MulV2F(screen_size(), 0.1f)), IMG(state.outline_pass_image), WHITE, .layer = LAYER_UI_FG});
draw_quad((DrawParams){quad_at(V2(0.0, screen_size().y/2.0f), MulV2F(screen_size(), 0.1f)), IMG(state.outline_pass_resolve_image), WHITE, .layer = LAYER_UI_FG});
Vec3 view_cam_pos = MulM4V4(InvGeneralM4(view), V4(0,0,0,1)).xyz;
//if(view_cam_pos.y >= 4.900f) // causes nan if not true... not good...
@ -7040,6 +7204,7 @@ ISANERROR("Don't know how to do this stuff on this platform.")
Vec2 *points = d.quad.points;
threedee_twodee_fs_params_t params = {
.tint = d.tint,
.time = (float)fmod(elapsed_time, 100),
};
params.alpha_clip_threshold = d.alpha_clip_threshold;
if (d.do_clipping)
@ -7098,10 +7263,10 @@ ISANERROR("Don't know how to do this stuff on this platform.")
};
// convert to uv space
sg_image_info info = sg_query_image_info(d.image);
sg_image_desc desc = sg_query_image_desc(d.image);
for (int i = 0; i < 4; i++)
{
tex_coords[i] = DivV2(tex_coords[i], V2((float)info.width, (float)info.height));
tex_coords[i] = DivV2(tex_coords[i], V2((float)desc.width, (float)desc.height));
}
for (int i = 0; i < 4; i++)
{
@ -7221,9 +7386,31 @@ void event(const sapp_event *e)
}
#endif
if (e->type == SAPP_EVENTTYPE_KEY_DOWN && e->key_code == SAPP_KEYCODE_F11)
if (e->type == SAPP_EVENTTYPE_KEY_DOWN &&
(e->key_code == SAPP_KEYCODE_F11 ||
e->key_code == SAPP_KEYCODE_ENTER && ((e->modifiers & SAPP_MODIFIER_ALT) || (e->modifiers & SAPP_MODIFIER_SHIFT))))
{
#ifdef DESKTOP
sapp_toggle_fullscreen();
#else
EM_ASM({
var elem = document.documentElement;
if (document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement)
{
if (document.exitFullscreen) document.exitFullscreen();
else if (document.webkitExitFullscreen) document.webkitExitFullscreen();
else if (document.mozCancelFullScreen) document.mozCancelFullScreen();
else if (document.msExitFullscreen) document.msExitFullscreen();
}
else
{
if (elem.requestFullscreen) elem.requestFullscreen();
else if (elem.webkitRequestFullscreen) elem.webkitRequestFullscreen();
else if (elem.mozRequestFullScreen) elem.mozRequestFullScreen();
else if (elem.msRequestFullscreen) elem.msRequestFullscreen();
}
});
#endif
}
#ifdef DEVTOOLS
@ -7447,13 +7634,13 @@ sapp_desc sokol_main(int argc, char* argv[])
.frame_cb = frame,
.cleanup_cb = cleanup,
.event_cb = event,
.sample_count = 1, // bumping this back to 4 is troublesome for web, because there's a mismatch in sample counts. Perhaps we must upgrade to gles3, in doing so, we should upgrade to the newest sokol gfx.
.sample_count = 1,
.width = 800,
.height = 600,
//.gl_force_gles2 = true, not sure why this was here in example, look into
.window_title = "RPGPT",
.window_title = "Dante's Cowboy",
.win32_console_attach = true,
.win32_console_create = true,
.icon.sokol_default = true,
.logger.func = slog_func,
};
}

@ -205,6 +205,11 @@ void chunk_from_s8(TextChunk *into, MD_String8 from)
into->text_length = (int)from.size;
}
typedef struct
{
NpcKind who_to_kill;
} GameObjective;
typedef struct Entity
{
bool exists;
@ -284,6 +289,8 @@ typedef struct GameState {
uint64_t tick;
bool won;
bool no_angel_screen;
// processing may still occur after time has stopped on the gamestate,
bool stopped_time;
@ -365,6 +372,18 @@ MD_String8 make_json_node(MD_Arena *arena, MessageType type, MD_String8 content)
return to_return;
}
MD_String8 npc_to_human_readable(Entity *me, NpcKind kind)
{
if(me->npc_kind == kind)
{
return MD_S8Lit("You");
}
else
{
return MD_S8CString(characters[kind].name);
}
}
// outputs json which is parsed by the server
MD_String8 generate_chatgpt_prompt(MD_Arena *arena, GameState *gs, Entity *e, CanTalkTo can_talk_to)
{
@ -427,24 +446,29 @@ MD_String8 generate_chatgpt_prompt(MD_Arena *arena, GameState *gs, Entity *e, Ca
{
switch(it->action_taken)
{
#define HUMAN(kind) MD_S8VArg(npc_to_human_readable(e, kind))
case ACT_none:
break;
case ACT_join:
AddFmt("%s joined %s\n", characters[it->context.author_npc_kind].name, characters[it->action_argument.targeting].name);
AddFmt("%.*s joined %.*s\n", HUMAN(it->context.author_npc_kind), HUMAN(it->action_argument.targeting));
break;
case ACT_leave:
AddFmt("%s left their party\n", characters[it->context.author_npc_kind].name);
AddFmt("%.*s left their party\n", HUMAN(it->context.author_npc_kind));
break;
case ACT_aim_shotgun:
AddFmt("%s aimed their shotgun at %s\n", characters[it->context.author_npc_kind].name, characters[it->action_argument.targeting].name);
AddFmt("%.*s aimed their shotgun at %.*s\n", HUMAN(it->context.author_npc_kind), HUMAN(it->action_argument.targeting));
break;
case ACT_fire_shotgun:
AddFmt("%s fired their shotgun at %s, brutally murdering them.\n", characters[it->context.author_npc_kind].name, characters[it->action_argument.targeting].name);
AddFmt("%.*s fired their shotgun at %.*s, brutally murdering them.\n", HUMAN(it->context.author_npc_kind), HUMAN(it->action_argument.targeting));
break;
case ACT_put_shotgun_away:
AddFmt("%s holstered their shotgun, no longer threatening anybody\n", characters[it->context.author_npc_kind].name);
AddFmt("%.*s holstered their shotgun, no longer threatening anybody\n", HUMAN(it->context.author_npc_kind));
break;
}
case ACT_approach:
AddFmt("%.*s approached %.*s\n", HUMAN(it->context.author_npc_kind), HUMAN(it->action_argument.targeting));
break;
#undef HUMAN
}
}
if(it->speech.text_length > 0)
{

@ -1,22 +1,22 @@
@echo on
@echo off
echo Running codegen...
@REM echo Running codegen...
if exist gen\ (
echo "Codegen folder already exists, not deleting because that messes with vscode intellisense..."
@REM echo "Codegen folder already exists, not deleting because that messes with vscode intellisense..."
) else (
mkdir gen
)
@REM shaders
thirdparty\sokol-shdc.exe --input threedee.glsl --output gen\threedee.glsl.h --slang glsl100:hlsl5:metal_macos:glsl330 || goto :error
thirdparty\sokol-shdc.exe --input threedee.glsl --output gen\threedee.glsl.h --slang glsl300es:hlsl5:glsl330 || goto :error
@REM metadesk codegen
cl /Ithirdparty /W3 /Zi /WX codegen.c || goto :error
cl /nologo /diagnostics:caret /Ithirdparty /W3 /Zi /WX codegen.c || goto :error
@REM zig cc -Ithirdparty -gfull -gcodeview codegen.c -o codegen.exe || goto error
codegen || goto :error
@REM cl /Ithirdparty /Igen /W3 /Zi /WX maketraining.c || goto :error
@REM cl /nologo /diagnostics:caret /Ithirdparty /Igen /W3 /Zi /WX maketraining.c || goto :error
@REM maketraining || goto :error
goto :EOF

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -118,7 +118,6 @@ SOKOL_API_IMPL sg_context_desc sapp_sgcontext(void) {
desc.color_format = (sg_pixel_format) sapp_color_format();
desc.depth_format = (sg_pixel_format) sapp_depth_format();
desc.sample_count = sapp_sample_count();
desc.gl.force_gles2 = sapp_gles2();
desc.metal.device = sapp_metal_get_device();
desc.metal.renderpass_descriptor_cb = sapp_metal_get_renderpass_descriptor;
desc.metal.drawable_cb = sapp_metal_get_drawable;

@ -82,7 +82,8 @@ uniform skeleton_vs_params {
vec2 bones_tex_size;
};
uniform sampler2D bones_tex;
uniform texture2D bones_tex;
uniform sampler vs_skeleton_smp;
float decode_normalized_float32(vec4 v)
{
@ -110,7 +111,7 @@ void main() {
{
for(int col = 0; col < 4; col++)
{
bone_mat[col][row] = decode_normalized_float32(texture(bones_tex, vec2((0.5 + col*4 + row)/bones_tex_size.x, y_coord)));
bone_mat[col][row] = decode_normalized_float32(texture(sampler2D(bones_tex, vs_skeleton_smp), vec2((0.5 + col*4 + row)/bones_tex_size.x, y_coord)));
}
}
@ -172,8 +173,10 @@ void main() {
@fs fs
uniform sampler2D tex;
uniform sampler2D shadow_map;
uniform texture2D tex;
uniform texture2D shadow_map;
uniform sampler fs_smp;
uniform samplerShadow fs_shadow_smp;
uniform fs_params {
int shadow_map_dimension;
@ -193,7 +196,7 @@ float decodeDepth(vec4 rgba) {
return dot(rgba, vec4(1.0, 1.0/255.0, 1.0/65025.0, 1.0/16581375.0));
}
float do_shadow_sample(sampler2D shadowMap, vec2 uv, float scene_depth, float n_dot_l) {
float do_shadow_sample(texture2D shadowMap, vec2 uv, float scene_depth, float n_dot_l) {
{
//WebGL does not support GL_CLAMP_TO_BORDER, or border colors at all it seems, so we have to check explicitly.
//This will probably slow down other versions which do support texture borders, but the current system does
@ -201,7 +204,7 @@ float do_shadow_sample(sampler2D shadowMap, vec2 uv, float scene_depth, float n_
if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0)
return 1.0;
}
float map_depth = decodeDepth(texture(shadowMap, uv));
float map_depth = decodeDepth(texture(sampler2D(shadowMap, fs_shadow_smp), uv));
// float bias = max(0.03f * (1.0f - n_dot_l), 0.005f);
// bias = clamp(bias, 0.0, 0.01);
@ -216,7 +219,7 @@ float do_shadow_sample(sampler2D shadowMap, vec2 uv, float scene_depth, float n_
}
float bilinear_shadow_sample(sampler2D shadowMap, vec2 uv, int texture_width, int texture_height, float scene_depth_light_space, float n_dot_l) {
float bilinear_shadow_sample(texture2D shadowMap, vec2 uv, int texture_width, int texture_height, float scene_depth_light_space, float n_dot_l) {
vec2 texture_dim = vec2(float(texture_width), float(texture_height));
vec2 texel_dim = vec2(1.0 / float(texture_width ), 1.0 / float(texture_height));
@ -244,7 +247,7 @@ float bilinear_shadow_sample(sampler2D shadowMap, vec2 uv, int texture_width, in
return result;
}
float calculate_shadow_factor(sampler2D shadowMap, vec4 light_space_fragment_position, float n_dot_l) {
float calculate_shadow_factor(texture2D shadowMap, vec4 light_space_fragment_position, float n_dot_l) {
float shadow = 1.0;
vec3 projected_coords = light_space_fragment_position.xyz / light_space_fragment_position.w;
@ -275,7 +278,7 @@ float calculate_shadow_factor(sampler2D shadowMap, vec4 light_space_fragment_pos
}
void main() {
vec4 col = texture(tex, uv);
vec4 col = texture(sampler2D(tex, fs_smp), uv);
bool alpha_blend = bool(alpha_blend_int);
@ -292,11 +295,6 @@ void main() {
//col.rgb = vec3(desertness, 0, 0);
if(col.a < 0.1 && !alpha_blend)
{
discard;
}
else
{
vec3 normal = normalize(cross(dFdx(world_space_frag_pos.xyz), dFdy(world_space_frag_pos.xyz)));
@ -307,23 +305,21 @@ void main() {
float lighting_factor = shadow_factor * n_dot_l;
lighting_factor = lighting_factor * 0.5 + 0.5;
if(alpha_blend)
{
frag_color = vec4(col.rgb*lighting_factor, col.a);
}
else
if (!alpha_blend)
{
frag_color = vec4(col.rgb*lighting_factor, 1.0);
float _Cutoff = 0.75; // Change this! it is tuned for existing bushes and TreeLayer leaves 2023-08-23
col.a = (col.a - _Cutoff) / max(fwidth(col.a), 0.0001) + 0.5;
}
//frag_color = vec4(col.rgb, 1.0);
frag_color = vec4(col.rgb*lighting_factor, col.a);
}
}
@end
@fs fs_shadow_mapping
uniform sampler2D tex;
uniform texture2D tex;
uniform sampler fs_shadow_mapping_smp;
in vec3 pos;
in vec2 uv;
@ -341,7 +337,7 @@ vec4 encodeDepth(float v) {
}
void main() {
vec4 col = texture(tex, uv);
vec4 col = texture(sampler2D(tex, fs_shadow_mapping_smp), uv);
if(col.a < 0.5)
{
discard;
@ -366,7 +362,9 @@ void main() {
@end
@fs fs_twodee
uniform sampler2D twodee_tex;
uniform texture2D twodee_tex;
uniform sampler fs_twodee_smp;
uniform twodee_fs_params {
vec4 tint;
@ -375,8 +373,12 @@ uniform twodee_fs_params {
vec2 clip_lr;
float alpha_clip_threshold;
float time;
vec2 tex_size;
vec2 screen_size;
float flip_and_swap_rgb;
};
in vec2 uv;
@ -387,7 +389,10 @@ out vec4 frag_color;
void main() {
// clip space is from [-1,1] [left, right]of screen on X, and [-1,1] [bottom, top] of screen on Y
if(pos.x < clip_ul.x || pos.x > clip_lr.x || pos.y < clip_lr.y || pos.y > clip_ul.y) discard;
frag_color = texture(twodee_tex, uv) * tint;
vec2 real_uv = uv;
if (flip_and_swap_rgb > 0) real_uv.y = 1 - real_uv.y;
frag_color = texture(sampler2D(twodee_tex, fs_twodee_smp), real_uv) * tint;
if (flip_and_swap_rgb > 0) frag_color.rgb = frag_color.bgr;
if(frag_color.a <= alpha_clip_threshold)
{
discard;
@ -397,7 +402,9 @@ void main() {
@end
@fs fs_twodee_outline
uniform sampler2D twodee_tex;
uniform texture2D twodee_tex;
uniform sampler fs_twodee_outline_smp;
uniform twodee_fs_params {
vec4 tint;
@ -406,8 +413,12 @@ uniform twodee_fs_params {
vec2 clip_lr;
float alpha_clip_threshold;
float time;
vec2 tex_size;
vec2 screen_size;
float flip_and_swap_rgb;
};
in vec2 uv;
@ -419,30 +430,44 @@ void main() {
// clip space is from [-1,1] [left, right]of screen on X, and [-1,1] [bottom, top] of screen on Y
if(pos.x < clip_ul.x || pos.x > clip_lr.x || pos.y < clip_lr.y || pos.y > clip_ul.y) discard;
float left = texture(twodee_tex, uv + vec2(-1, 0)/tex_size).a;
float right = texture(twodee_tex, uv + vec2(1, 0)/tex_size).a;
float up = texture(twodee_tex, uv + vec2(0, 1)/tex_size).a;
float down = texture(twodee_tex, uv + vec2(0, -1)/tex_size).a;
if(
false
|| left > 0.1 && right < 0.1
|| left < 0.1 && right > 0.1
|| up < 0.1 && down > 0.1
|| up > 0.1 && down < 0.1
)
{
frag_color = vec4(1.0);
}
else
{
frag_color = vec4(0.0);
}
vec2 real_uv = uv;
if (flip_and_swap_rgb > 0) real_uv.y = 1 - real_uv.y;
// 5-tap tent filter: centre, left, right, up, down
float c = texture(sampler2D(twodee_tex, fs_twodee_outline_smp), real_uv).a;
float l = texture(sampler2D(twodee_tex, fs_twodee_outline_smp), real_uv + vec2(-1, 0)/tex_size).a;
float r = texture(sampler2D(twodee_tex, fs_twodee_outline_smp), real_uv + vec2(+1, 0)/tex_size).a;
float u = texture(sampler2D(twodee_tex, fs_twodee_outline_smp), real_uv + vec2(0, +1)/tex_size).a;
float d = texture(sampler2D(twodee_tex, fs_twodee_outline_smp), real_uv + vec2(0, -1)/tex_size).a;
// if centre pixel is ~1, it is inside a shape.
// if centre pixel is ~0, it is outside a shape.
// if it is in the middle, it is near an MSAA-resolved edge.
// we parallel-compute the inside AND outside glows, and then lerp using c.
float lerp_t = c;
// buffer is linear-space to be ACES-corrected later, but MSAA happens in gamma space;
// I want a gamma-space blend; so I cheaply do pow(x, 2) by computing x*x.
c *= c; l *= l; r *= r; u *= u; d *= d;
float accum_o = (c + l + r + u + d);
float accum_i = 5 - (c + l + r + u + d);
accum_o = 0.3 * accum_o;
accum_i = 0.3 * accum_i;
accum_o = clamp(accum_o, 0, 1);
accum_i = clamp(accum_i, 0, 1);
accum_o = sqrt(accum_o); // cheap gamma-undo
accum_i = sqrt(accum_i); // cheap gamma-undo
float accum = mix(accum_o, accum_i, lerp_t);
frag_color = vec4(1, 1, 1, accum);
}
@end
@fs fs_twodee_color_correction
uniform sampler2D twodee_tex;
uniform texture2D twodee_tex;
uniform sampler fs_twodee_color_correction_smp;
uniform twodee_fs_params {
vec4 tint;
@ -451,8 +476,12 @@ uniform twodee_fs_params {
vec2 clip_lr;
float alpha_clip_threshold;
float time;
vec2 tex_size;
vec2 screen_size;
float flip_and_swap_rgb;
};
in vec2 uv;
@ -473,10 +502,32 @@ void main() {
// clip space is from [-1,1] [left, right]of screen on X, and [-1,1] [bottom, top] of screen on Y
if(pos.x < clip_ul.x || pos.x > clip_lr.x || pos.y < clip_lr.y || pos.y > clip_ul.y) discard;
vec4 col = texture(twodee_tex, uv);
vec2 real_uv = uv;
if (flip_and_swap_rgb > 0) real_uv.y = 1 - real_uv.y;
vec4 col = texture(sampler2D(twodee_tex, fs_twodee_color_correction_smp), real_uv);
if (flip_and_swap_rgb > 0) col.rgb = col.bgr;
col.rgb = acesFilm(col.rgb);
// Film grain
vec2 grain_uv = gl_FragCoord.xy / screen_size.xy;
float x = grain_uv.x * grain_uv.y * time * 24 + 100.0;
vec3 noise = vec3(mod((mod(x, 13.0) + 1.0) * (mod(x, 123.0) + 1.0), 0.01)) * 100.0;
col.rgb += (noise - 0.5) * 0.05;
col.rgb *= 0.95;
// Hard-clip contrast levels
float min = 11; float max = 204;
col.rgb -= min/255;
col.rgb *= 255/(max-min);
// Vignette
col.rgb *= clamp(1.5 - length(gl_FragCoord.xy / screen_size.xy - vec2(0.5)), 0, 1);
// Cross-process
float cross_process_strength = 0.5;
col.rg *= (col.rg * ((-cross_process_strength) * col.rg + (-1.5 * (-cross_process_strength))) + (0.5 * (-cross_process_strength) + 1));
col.b *= (col.b * ((+cross_process_strength) * col.b + (-1.5 * (+cross_process_strength))) + (0.5 * (+cross_process_strength) + 1));
col.rgb = clamp(col.rgb, 0, 1);
frag_color = col;
}
@end
@ -484,7 +535,8 @@ void main() {
@fs fs_outline
uniform sampler2D tex;
uniform texture2D tex;
uniform sampler fs_outline_smp;
in vec3 pos;
in vec2 uv;
@ -495,7 +547,7 @@ in vec4 world_space_frag_pos;
out vec4 frag_color;
void main() {
vec4 col = texture(tex, uv);
vec4 col = texture(sampler2D(tex, fs_outline_smp), uv);
if(col.a < 0.5)
{
discard;

@ -1,9 +1,29 @@
Urgent:
- REALLY look into characters not being able to shoot eachother
- Tag memories from drama as permanent so they're never forgotten and always seed character behavior
- gman character that gives you randomized goals such as (kill so and so) or (befriend blank) or (get john to go to other room), and decides itself with gpt when you're done with the game, when you've 'learned your lesson'.
- room system where characters can go to rooms. camera constrained to room bounds, and know which rooms are near them to go to
- make them understand they're holding their shotgun more
- Fix everything about the shotgun, put shotgun away
- can't interact with characters while they're thinking
- fix stencil image being resized working
- bubbles collide with eachother so they can't overlap
- set the game in oregon (suggestion by phillip)
- nocodegen instead of codegen argument
- camera pans to who is speaking and acting
- E keyboard hints (tutorializing)
- dot dot dot speech bubble to show that they heard your request, but are ignoring you.
Long distance:
- Design character creator
- Let ChatGPT file bug reports with something like !BugReport(This shouldn't be happening, developer. I killed them and they're still alive)
- Cylindrical colliders for the people
- System where I can upload any AI interaction into gpt playground and inspect the conversation
- Incrementally higher resolution system for all textures, configured in a #define. Also flips the images at build time
- Stop time when you talk to people
- Much higher move accel, almost instantaneously goes to
- Imgui Button doesn't move enough when highlighted
- Higher res text instead of text scaling
- Make space and shift also interact
- Maybe walk by default and shift to run

@ -6,6 +6,7 @@
#define PLAYER_SPEED 0.15f // in meters per second
#define PERCEPTION_HEARING_RAGE (TILE_SIZE*4.0f)
#define CHARACTERS_PER_SEC 45.0f
#define ANGEL_CHARACTERS_PER_SEC 35.0f
#define PROPAGATE_ACTIONS_RADIUS 10.0f
#define SWORD_SWIPE_RADIUS (TILE_SIZE*3.0f)
#define ARROW_SPEED 200.0f

@ -1,43 +1,24 @@
#pragma once
#include <stdio.h>
#include <stdbool.h>
#ifdef WEB
#include <signal.h>
#define game_debugbreak() raise(SIGTRAP)
#define game_assert_4127_push
#define game_assert_4127_pop
#define STRINGIZE(x) STRINGIZE2(x)
#define STRINGIZE2(x) #x
#ifdef WEB
#define assert_impl(cond, str) ((cond) || (EM_ASM({ assert(0, UTF8ToString($0) + UTF8ToString($1)); }, (__func__), (str)), 0))
#elif defined(DESKTOP)
#define game_debugbreak() __debugbreak()
#define game_assert_4127_push __pragma(warning(push)) __pragma(warning(disable:4127))
#define game_assert_4127_pop __pragma(warning(pop))
#define assert_impl(cond, str) ((cond) || (fputs("Assertion failed: " __FUNCTION__ str "\n", stderr), __debugbreak(), 0))
#else
#error "Don't know how to assert for current platform configuration"
#define game_debugbreak() (void)(0)
#endif
static void assert_impl(const char *func, const char *file, long line, const char *expression)
{
fprintf(stderr, "Assert fail in %s(%s:%ld):\n \"%s\"\n", func, file, line, expression);
}
#ifdef NDEBUG
#define game_assert(cond) game_assert_4127_push do { (void)0; } while (0) game_assert_4127_pop
#define game_assert(cond) ((void)0)
#else
#define game_assert(cond) game_assert_4127_push do { \
if (!(cond)) { \
assert_impl(__func__, __FILE__, __LINE__, #cond); \
game_debugbreak(); \
} \
} while (0) game_assert_4127_pop
#define game_assert(cond) assert_impl(cond, "(" __FILE__ ":" STRINGIZE(__LINE__) "): \"" #cond "\"")
#endif
#ifdef assert
#undef assert
#endif

@ -3,7 +3,7 @@
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>AI RPG</title>
<title>Dante's Cowboy</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<style type="text/css">

Loading…
Cancel
Save