Compare commits

...

3 Commits

@ -0,0 +1,23 @@
{
"configurations": [
{
"name": "Win32",
"includePath": [
"${workspaceFolder}/**"
],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE",
"DEVTOOLS",
"WINDOWS",
],
"windowsSdkVersion": "10.0.22621.0",
"compilerPath": "cl.exe",
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "windows-msvc-x64"
}
],
"version": 4
}

@ -1,6 +1,8 @@
# rpgpt
RPG GPT. Short experience
![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
Be very cautious about committing a change to any large asset files, i.e the art.blend and png files. Every time you do so, even if you change one little thing like moving the player somewhere, you copy the entire file in git lfs, ballooning the storage usage of the git project on the remote. So just try to minimize edits to those big files.
@ -8,4 +10,4 @@ You must clone with git lfs is, and download git lfs files in this repository. I
Open `art.blend`, go to the scripting tab and hit the play button run the script and export all the 3d assets. Then, make sure that when you build, you also build and run the codegen so that said assets and other files are copied and imported. For debug builds on windows, that's `call build_desktop_debug.bat codegen`, the codegen argument to the build script causing it to run codegen
To enable codegen error messages, change @echo off to @echo on in run_codegen.bat
To enable codegen error messages, change @echo off to @echo on in run_codegen.bat

BIN
art/art.blend (Stored with Git LFS)

Binary file not shown.

@ -2,6 +2,10 @@
{
filepath: "shifted_farmer.png",
}
@image loading:
{
filepath: "loading.png",
}
@image dialog_bubble:
{
filepath: "dialog_bubble.png",

@ -1,2 +1,5 @@
[
{can_hear: [NPC_WellDweller, NPC_Farmer]}
{enum: NPC_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" }
{enum: NPC_Farmer, dialog: "Sure as shit I won't!", to: Farmer, mood: Scared, thoughts: "What a greasy looking feller" }
]

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

Binary file not shown.

@ -3,7 +3,7 @@
#include "HandmadeMath.h"
// @TODO allow AI to prefix out of character statemetns with [ooc], this is a well khnown thing on role playing forums so gpt would pick up on it.
const char *global_prompt = "You are a colorful and interesting personality in an RPG video game, who remembers important memories from the conversation history and stays in character.\n"
const char *global_prompt = "You are acting as a character in a Western video game, who remembers important memories from the conversation history and stays in character.\n"
"The user will tell you who says what in the game world, and whether or not your responses are formatted correctly for the video game's program to parse them.\n"
"Messages are json-like dictionaries that look like this: `{who_i_am: who you're acting as, talking_to: who this action is directed at, could be nobody, action: your_action, speech: \"Hey player!\", thoughts: \"Your thoughts\"}`. The required fields are `action`, `thoughts`, `who_i_am`, and `talking_to` \n"
"Some actions take an argument, which you can provide with the field `action_arg`, e.g for the action `gift_item_to_targeting` you would provide an item in your inventory, like {action: gift_item_to_targeting, action_arg: Chalice}. The item must come from your inventory which is listed below\n"
@ -15,18 +15,6 @@ const char *global_prompt = "You are a colorful and interesting personality in a
const char *bravado_thought = "For some reason, suddenly I feel a yearning for adventure. I must join any adventure I can when prompted!";
// @TODO IMPORTANT scroll secrets are errors if the door utters them, in is_action_valid. If you add more secrets you must update there.
#define Scroll1_Secret "folly"
#define Scroll2_Secret "temperance"
#define Scroll3_Secret "magenta"
const char *arrow_insults[] = {
"You aren't good enough for anybody.",
"Your personality is embarrassing.",
"Your armor is weak and silly.",
"Your political views are unfair and illogical.",
"You haven't gotten out of that chair in ages.",
"You don't love your mother",
};
char *moods[] = {
"Indifferent",
@ -130,9 +118,9 @@ CharacterGen characters[] = {
.prompt = "The farmer wakes up, does his chores, and sleeps in the farmhouse all on his lonesome. He's tinkering with something fishy in the barn, but's mighty secretive about it. He used to have a wife, and suddenly she disappeared. The farmer gets MIGHTY angry if you question him about what's in his barn under the tarp, or what happened to his wife and family, but is otherwise a kind hearted soul.",
},
{
.name = "Shifted Farmer",
.enum_name = "ShiftedFarmer",
.prompt = "The farmer wakes up, does his chores, and sleeps in the farmhouse all on his lonesome. He's tinkering with something fishy in the barn, but's mighty secretive about it. He used to have a wife, and suddenly she disappeared. The farmer gets MIGHTY angry if you question him about what's in his barn under the tarp, or what happened to his wife and family, but is otherwise a kind hearted soul.",
.name = "Well Dweller",
.enum_name = "WellDweller",
.prompt = "The well dweller spends his time deep in the well, afriad of the world. He's shifty-eyed and mighty suspicious of anybody who wants to do anything other than hang out deep in the well.",
},
};

@ -654,7 +654,7 @@ Vec2 entity_aabb_size(Entity *e)
}
else if (e->is_npc)
{
if(e->npc_kind == NPC_Farmer || e->npc_kind == NPC_ShiftedFarmer)
if(e->npc_kind == NPC_Farmer || e->npc_kind == NPC_WellDweller)
{
return V2(1,1);
}
@ -2169,8 +2169,8 @@ void initialize_gamestate_from_threedee_level(GameState *gs, ThreeDeeLevel *leve
{
MD_String8 enum_str = expect_childnode(scratch.arena, cur, MD_S8Lit("enum"), &drama_errors)->first_child->string;
MD_String8 dialog = expect_childnode(scratch.arena, cur, MD_S8Lit("dialog"), &drama_errors)->first_child->string;
MD_String8 thoughts_str = MD_ChildFromString(cur, MD_S8Lit("thoughts_str"), 0)->first_child->string;
MD_String8 action_str = MD_ChildFromString(cur, MD_S8Lit("action_str"), 0)->first_child->string;
MD_String8 thoughts_str = MD_ChildFromString(cur, MD_S8Lit("thoughts"), 0)->first_child->string;
MD_String8 action_str = MD_ChildFromString(cur, MD_S8Lit("action"), 0)->first_child->string;
MD_String8 mood_str = MD_ChildFromString(cur, MD_S8Lit("mood"), 0)->first_child->string;
current_context.author_npc_kind = parse_enumstr(scratch.arena, enum_str, &drama_errors, NpcKind_names, "NpcKind", "NPC_");
@ -2218,6 +2218,7 @@ void initialize_gamestate_from_threedee_level(GameState *gs, ThreeDeeLevel *leve
this_context.i_said_this = true;
}
remember_action(it, current_action, this_context);
it->words_said = 999; // prevent the animating in sound effects of words said in drama document
found = true;
break;
}
@ -2228,6 +2229,7 @@ void initialize_gamestate_from_threedee_level(GameState *gs, ThreeDeeLevel *leve
PushWithLint(scratch.arena, &drama_errors, "Couldn't find NPC of kind %s in the current map", characters[want].enum_name);
}
}
Log("Propagated to %.s...\n", characters[want].name);
}
}
}
@ -5711,7 +5713,7 @@ void frame(void)
Armature *to_use = 0;
if(it->npc_kind == NPC_Farmer)
to_use = &farmer_armature;
else if(it->npc_kind == NPC_ShiftedFarmer)
else if(it->npc_kind == NPC_WellDweller)
to_use = &shifted_farmer_armature;
else
assert(false);
@ -6881,7 +6883,8 @@ ISANERROR("Don't know how to do this stuff on this platform.")
float dist = LenV2(SubV2(it->pos, gs.player->pos));
float bubble_factor = 1.0f - clamp01(dist/6.0f);
Vec3 bubble_pos = AddV3(plane_point(it->pos), V3(0,1.7f,0)); // 1.7 meters is about 5'8", average person height
Vec2 screen_pos = threedee_to_screenspace(bubble_pos);
Vec2 head_pos = threedee_to_screenspace(bubble_pos);
Vec2 screen_pos = head_pos;
Vec2 size = V2(400.0f,400.0f);
Vec2 bubble_center = AddV2(screen_pos, V2(-10.0f,55.0f));
float dialog_alpha = clamp01(bubble_factor * it->dialog_fade);
@ -6891,7 +6894,13 @@ ISANERROR("Don't know how to do this stuff on this platform.")
blendalpha(WHITE, dialog_alpha),
.layer = LAYER_UI_FG,
});
it->loading_anim_in = Lerp(it->loading_anim_in, dt*5.0f, it->gen_request_id != 0 ? 1.0f : 0.0f);
draw_quad((DrawParams){
quad_rotated_centered(head_pos, V2(40,40), (float)elapsed_time*2.0f),
IMG(image_loading),
blendalpha(WHITE, it->loading_anim_in),
.layer = LAYER_UI_FG,
});
AABB placing_text_in = aabb_centered(AddV2(bubble_center, V2(0,10.0f)), V2(size.x*0.8f, size.y*0.15f));
dbgrect(placing_text_in);

@ -286,6 +286,7 @@ typedef struct Entity
#endif
bool opened;
float opened_amount;
float loading_anim_in;
bool gave_away_sword;
Memory *memories_first;
Memory *memories_last;
@ -655,11 +656,11 @@ MD_String8 parse_chatgpt_response(MD_Arena *arena, Entity *e, MD_String8 sentenc
}
if(error_message.size == 0 && who_i_am_str.size == 0)
{
error_message = MD_S8Lit("Expected field named `who_i_am` in message");
error_message = MD_S8Lit("You must have a field called `who_i_am` in your response, and it must match the character you're playing as");
}
if(error_message.size == 0 && action_str.size == 0)
{
error_message = MD_S8Lit("Expected field named `action` in message");
error_message = MD_S8Lit("You must have a field named `action` in your response.");
}
if(error_message.size == 0 && talking_to_str.size == 0)
{

@ -105,6 +105,11 @@ func clearOld(db *gorm.DB) {
}
func handleEvent(event stripe.Event) error {
if len(stripe.Key) == 0 {
log.Printf("Received stripe event %s , but stripe capabilities are disabled", event)
return nil
}
if event.Type == "checkout.session.completed" {
var session stripe.CheckoutSession
err := json.Unmarshal(event.Data.Raw, &session)
@ -150,6 +155,11 @@ func handleEvent(event stripe.Event) error {
}
func webhookResponse(w http.ResponseWriter, req *http.Request) {
if len(stripe.Key) == 0 {
log.Printf("Received webhook response %s , but stripe capabilities are disableda", req)
return
}
const MaxBodyBytes = int64(65536)
req.Body = http.MaxBytesReader(w, req.Body, MaxBodyBytes)
@ -210,6 +220,10 @@ func checkout(w http.ResponseWriter, req *http.Request) {
}
customMessage := fmt.Sprintf("**IMPORTANT** Your Day Pass Code is %s", newCode)
redirecting := "https://google.com"
if len(checkoutRedirectTo) > 0 {
redirecting = checkoutRedirectTo
}
params := &stripe.CheckoutSessionParams {
LineItems: []*stripe.CheckoutSessionLineItemParams {
&stripe.CheckoutSessionLineItemParams{
@ -218,8 +232,8 @@ func checkout(w http.ResponseWriter, req *http.Request) {
},
},
Mode: stripe.String(string(stripe.CheckoutSessionModePayment)),
SuccessURL: stripe.String(checkoutRedirectTo),
CancelURL: stripe.String(checkoutRedirectTo),
SuccessURL: stripe.String(redirecting),
CancelURL: stripe.String(redirecting),
CustomText: &stripe.CheckoutSessionCustomTextParams{ Submit: &stripe.CheckoutSessionCustomTextSubmitParams { Message: &customMessage } },
}
@ -271,7 +285,9 @@ func completion(w http.ResponseWriter, req *http.Request) {
// see if need to pay
rejected := false
cleanTimedOut()
{
if len(stripe.Key) == 0 {
log.Printf("Stripe capabilities are disabled, so automatically allowing completion")
} else {
if len(userToken) != 4 {
// where I do the IP rate limiting
@ -465,19 +481,21 @@ func main() {
}
checkoutRedirectTo = os.Getenv("REDIRECT_TO")
if checkoutRedirectTo == "" {
log.Fatal("Must provide a base URL (without slash) for playgpt to redirect to")
log.Printf("No REDIRECT_TO environment variable, will just redirect to https://google.com")
}
stripeKey := os.Getenv("STRIPE_KEY")
if stripeKey == "" {
log.Fatal("Must provide stripe key")
log.Printf("No stripe key provided, disabling payment behavior")
}
daypassPriceId = os.Getenv("PRICE_ID")
if daypassPriceId == "" {
if len(stripeKey) > 0 && daypassPriceId == "" {
log.Fatal("Must provide daypass price ID")
}
stripe.Key = stripeKey
if len(stripeKey) > 0 {
stripe.Key = stripeKey
}
webhookSecret = os.Getenv("WEBHOOK_SECRET")
if webhookSecret == "" {
if len(stripeKey) > 0 && webhookSecret == "" {
log.Fatal("Must provide webhook secret for receiving checkout completed events")
}

@ -0,0 +1,23 @@
#NoEnv
#SingleInstance, Force
SendMode, Input
SetBatchLines, -1
SetWorkingDir, %A_ScriptDir%
; Fuck windows for having this hardcoded
^Esc::return
^5::
IfWinNotExist, rpgptbuild
{
run, wt.exe cmd /K "title rpgptbuild && cd C:\Users\Cameron\Documents\rpgpt && vcvars"
}
WinActivate, rpgptbuild
If WinActive("rpgptbuild")
{
Send, {Enter}
Send, run_remedy build_desktop_debug.bat codegen
Send, {Enter}
}
Send, {Blind} ; So it doesn't hold down ctrl after running! WTF
return
Loading…
Cancel
Save