andrewjhaman 1 year ago
commit a472e52f27

2
.gitignore vendored

@ -45,7 +45,7 @@ EPIC RPG World Pack - Ancient Ruins V 1.7.zip
EPIC RPG World Pack - Ancient Ruins V 1.7/ EPIC RPG World Pack - Ancient Ruins V 1.7/
# allow shader compiler # allow shader compiler
!thirdparty\sokol-shdc.exe !thirdparty/sokol-shdc.exe
# generated files # generated files
gen/ gen/

@ -1,79 +0,0 @@
@module armature
@vs vs
in vec3 pos_in;
in vec2 uv_in;
in vec4 indices_in; // is a sokol SG_VERTEXFORMAT_USHORT4N, a 16 bit unsigned integer treated as a floating point number due to webgl compatibility
in vec4 weights_in;
out vec3 pos;
out vec2 uv;
uniform vs_params {
mat4 model;
mat4 view;
mat4 projection;
vec2 bones_tex_size;
};
uniform sampler2D bones_tex;
float decode_normalized_float32(vec4 v)
{
float sign = 2.0 * v.x - 1.0;
return sign * (v.z*255.0 + v.y);
}
void main() {
vec4 total_position = vec4(0.0f);
for(int bone_influence_index = 0; bone_influence_index < 4; bone_influence_index++)
{
float index_float = indices_in[bone_influence_index];
int index = int(index_float * 65535.0);
float weight = weights_in[bone_influence_index];
float y_coord = (0.5 + index)/bones_tex_size.y;
mat4 bone_mat;
for(int row = 0; row < 4; row++)
{
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)));
}
}
vec4 local_position = bone_mat * vec4(pos_in, 1.0f);
total_position += local_position * weight;
}
gl_Position = projection * view * model * total_position;
//gl_Position = projection * view * model * vec4(pos_in, 1.0);
pos = gl_Position.xyz;
uv = uv_in;
}
@end
@fs fs
uniform sampler2D tex;
in vec3 pos;
in vec2 uv;
out vec4 frag_color;
void main() {
vec4 col = texture(tex, uv);
if(col.a < 0.5)
{
discard;
}
else
{
frag_color = vec4(col.rgb, 1.0);
}
}
@end
@program program vs fs

@ -80,6 +80,32 @@ with open(bpy.path.abspath(f"//{EXPORT_DIRECTORY}/shorttest.bin"), "wb") as f:
for i in range(4): for i in range(4):
write_u16(f, i) write_u16(f, i)
saved_images = set()
def ensure_tex_saved_and_get_name(o) -> str:
"""returns the path to the mesh's texture's png in the exported directory"""
mesh_name = o.to_mesh().name
img_obj = None
assert len(o.material_slots) == 1, f"Don't know which material slot to pick from in mesh {mesh_name} object {o.name}"
mat = o.material_slots[0]
for node in mat.material.node_tree.nodes:
if node.type == "TEX_IMAGE":
img_obj = node.image
break
assert img_obj, f"Mesh {mesh_name} in its material doesn't have an image object"
image_filename = f"{img_obj.name}.png"
if image_filename in saved_images:
pass
else:
save_to = f"//{EXPORT_DIRECTORY}/{image_filename}"
print(f"Saving image {image_filename} to {bpy.path.abspath((save_to))}...")
if img_obj.packed_file:
img_obj.save(filepath=bpy.path.abspath(save_to))
else:
assert img_obj.filepath != "", f"{img_obj.filepath} in mesh {mesh_name} Isn't there but should be, as it has no packed image"
shutil.copyfile(bpy.path.abspath(img_obj.filepath),bpy.path.abspath(save_to))
return image_filename
# meshes can either be Meshes, or Armatures. Armatures contain all mesh data to draw it, and any anims it has # meshes can either be Meshes, or Armatures. Armatures contain all mesh data to draw it, and any anims it has
for o in D.objects: for o in D.objects:
@ -91,12 +117,15 @@ for o in D.objects:
if o.users_collection[0].name == 'Level': if o.users_collection[0].name == 'Level':
assert False, "Cannot put armatures in the level. The level is for static placed meshes. For dynamic entities, you put them outside of the level collection, their entity kind is encoded, and the game code decides how to draw them" assert False, "Cannot put armatures in the level. The level is for static placed meshes. For dynamic entities, you put them outside of the level collection, their entity kind is encoded, and the game code decides how to draw them"
else: else:
placed_entities.append((mesh_object.name,) + object_transform_info) pass
#placed_entities.append((mesh_object.name,) + object_transform_info)
armature_name = o.data.name armature_name = o.data.name
output_filepath = bpy.path.abspath(f"//{EXPORT_DIRECTORY}/{armature_name}.bin") output_filepath = bpy.path.abspath(f"//{EXPORT_DIRECTORY}/{armature_name}.bin")
print(f"Exporting armature to {output_filepath}") image_filename = ensure_tex_saved_and_get_name(mesh_object)
print(f"Exporting armature with image filename {image_filename} to {output_filepath}")
with open(output_filepath, "wb") as f: with open(output_filepath, "wb") as f:
write_b8(f, True) write_b8(f, True)
write_string(f, image_filename)
bones_in_armature = [] bones_in_armature = []
for b in o.data.bones: for b in o.data.bones:
bones_in_armature.append(b) bones_in_armature.append(b)
@ -235,6 +264,7 @@ for o in D.objects:
else: # if the parent type isn't an armature, i.e just a bog standard mesh else: # if the parent type isn't an armature, i.e just a bog standard mesh
mesh_name = o.to_mesh().name # use this over o.name so instanced objects which refer to the same mesh, both use the same serialized mesh. mesh_name = o.to_mesh().name # use this over o.name so instanced objects which refer to the same mesh, both use the same serialized mesh.
object_transform_info = (mesh_name, mapping @ o.location, o.rotation_euler, o.scale) object_transform_info = (mesh_name, mapping @ o.location, o.rotation_euler, o.scale)
if o.users_collection[0].name == 'Level' and mesh_name == "CollisionCube": if o.users_collection[0].name == 'Level' and mesh_name == "CollisionCube":
@ -250,12 +280,15 @@ for o in D.objects:
if mesh_name in saved_meshes: if mesh_name in saved_meshes:
continue continue
saved_meshes.add(mesh_name) saved_meshes.add(mesh_name)
image_filename = ensure_tex_saved_and_get_name(o)
assert(mesh_name != LEVEL_EXPORT_NAME) assert(mesh_name != LEVEL_EXPORT_NAME)
output_filepath = bpy.path.abspath(f"//{EXPORT_DIRECTORY}/{mesh_name}.bin") output_filepath = bpy.path.abspath(f"//{EXPORT_DIRECTORY}/{mesh_name}.bin")
print(f"Exporting mesh to {output_filepath}") print(f"Exporting mesh to {output_filepath} with image_filename {image_filename}")
with open(output_filepath, "wb") as f: with open(output_filepath, "wb") as f:
write_b8(f, False) write_b8(f, False) # if it's an armature or not, first byte of the file
write_string(f, image_filename) # the image filename!
bm = bmesh.new() bm = bmesh.new()
mesh = o.to_mesh() mesh = o.to_mesh()
bm.from_mesh(mesh) bm.from_mesh(mesh)

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

Binary file not shown.

@ -1,68 +1,2 @@
[ [
{can_hear: [NPC_Jester, NPC_Bill]},
{enum: NPC_Jester , dialog: "Hehehe! Quit quant, a peasant from the mortal realm!", to: Bill},
{enum: NPC_Bill , mood: Scared, dialog: "No...Please! Stay away from me! Who the Hell are you!", thoughts: "I'm kind of mad, and this Jester guy is kind of an asshole", to: Jester},
{enum: NPC_Jester , dialog: "Poor Bill, I'm sure your ex-wife says the same...", to: Bill},
{enum: NPC_Bill , mood: Angry, dialog: "You evil BASTARD!! What is this place???", thoughts: "I hate the Jester because he dissed my ex-wife. And, he's being all vague and condescending. I would punch him if I could", to: Jester},
{enum: NPC_Jester , dialog: "I'll leave that for you to find out ;)", action: ACT_causes_testicular_torsion, to: Bill},
{enum: NPC_Bill , mood: Agony, dialog: "AUGGHGHHH!!! THE PAIN IS UNBEARABLE", thoughts: "SCREW THIS JESTER GUY HE FUCKED WITH MY NUTS", to: Jester},
{enum: NPC_Jester , dialog: "That's just a taste of what's to come! Bye bye~", action: ACT_undoes_testicular_torsion, to: Bill},
{can_hear: [NPC_Jester, NPC_TheBlacksmith]},
{enum: NPC_Jester , dialog: "A fine strong man you are, mister meld!", to: TheBlacksmith},
{enum: NPC_TheBlacksmith, mood: Confused, dialog: "I'll take any complement I can get, but you suddenly knowing my name is pretty weird dude.", thoughts: "This Jester looking guy gives me the creeps...", to: Jester},
{enum: NPC_Jester , dialog: "A strong man needs strong metal...Some magic magnetic fish could do the trick! They'd pick up all the river iron", to: TheBlacksmith },
{enum: NPC_TheBlacksmith, mood: Curious, dialog: "You know what I'll bite, where do I get this fish.", thoughts: "I can't believe business is so poor that I'm stopping to this...", to: Jester},
{enum: NPC_Jester , dialog: "Come now, follow me...", to: TheBlacksmith},
{can_hear: [NPC_Jester, NPC_TheBlacksmith, NPC_Edeline]},
{enum: NPC_Jester , dialog: "Edeline, fine enchantress. This blacksmith has asked for your hand in marriage if you would enchant these fish for him", to: Edeline},
{enum: NPC_TheBlacksmith, mood: Anxious, dialog: "Y-yes...", thoughts: "THAT BASTARD! It'd be too awkward to back out now...", to: Edeline},
{enum: NPC_Edeline , mood: Happy, dialog: "Oh my gosh! I leap with joy! I see much joy in our future, Meld.", thoughts: "I really hope he doesn't back out, I'm tired of being the crazy town spinster!", to: Meld},
{enum: NPC_Jester , dialog: "The fish!", to: Edeline},
{enum: NPC_Edeline , mood: Curious, dialog: "AH! Of course, here you are then!", thoughts: "I have no idea what these magnetic fish would be good for...", action: "ACT_enchant_fish", to: Meld},
{enum: NPC_TheBlacksmith, mood: Happy, dialog: "Finally, maybe this will help me replenish my metal reserves", thoughts: "I have no idea where all my metal went. I wonder if somebody stole it", to: Edeline},
{can_hear: [NPC_Jester, NPC_Blue]},
{enum: NPC_Blue , mood: Angry, dialog: "What the-my rod! These fish broke my fishing rod! Why are they glowing???", thoughts: "I'll be destroyed in the free market if I don't figure this out!"},
{enum: NPC_Jester , dialog: "Ho-ho! You know, if you want your rod to get done in split, Meld's anvil's got plenty reserves...", to: Blue},
{enum: NPC_Blue , mood: Excited, dialog: "Are you saying I should steal, betray the trust of Meld, just so my company makes more money? Sold!", thoughts: "Meld's fault for not hiring private security!", to: Jester},
{can_hear: [NPC_Jester, NPC_Blue, NPC_TheBlacksmith]},
{enum: NPC_Blue , mood: Excited, dialog: "Hey meld, what's that shiny thing over there?", thoughts: "Time to steal me an iron pole for my iron ROD!", to: Meld},
{enum: NPC_TheBlacksmith, mood: Curious, dialog: "Hm?", thoughts: "What is it now....", to: Blue},
{can_hear: [NPC_Jester, NPC_Blue]},
{enum: NPC_Blue , mood: Happy, dialog: "Hehehe...", thoughts: "EASY!", action: "ACT_steal_iron_pole"},
{can_hear: [NPC_Jester, NPC_Blue, NPC_TheBlacksmith]},
{enum: NPC_TheBlacksmith, mood: Confused, dialog: "There wasn't anything over there?", thoughts: "What does this guy think he's doing?", to: Blue},
{enum: NPC_Blue , mood: Elated, dialog: "Don't worry about it man! You have a good one", thoughts: "PEACE OF CAKE!", to: TheBlacksmith},
{can_hear: [NPC_Jester, NPC_Davis]},
{enum: NPC_Jester , dialog: "You know Davis, I can promise you fortune and power beyond your comprehension if you assist me in my antics. I mean to bring devastation to this town", to: Davis},
{enum: NPC_Davis , mood: Sad, dialog: "...", speech: "This jester guy reminds me of when I was younger.", to: Jester},
{enum: NPC_Jester , dialog: "Cat got your tongue?", to: Davis},
{enum: NPC_Davis , mood: Angry, dialog: "You need friends so badly, mister jester, that you resort to disgusting party tricks and trivial annoyance? I will not allow this.", thoughts: "He can stare me down all he wants, all he'll find is a blank mirror.", to: Jester},
{enum: NPC_Jester , dialog: "Careful there, diogenes! I've got more knives than you fingers!", to: Davis},
{enum: NPC_Davis , mood: Indifferent, dialog: "Petty violence means nothing to me. Can't you see where you end? At nothing, where you began.", thoughts: "I am empty of everything.", to: Jester},
{enum: NPC_Jester , dialog: "Have it your way! I'll be in touch...", to: Davis},
{enum: NPC_Davis , mood: Confident, dialog: "If you bring me death, I welcome it. If you bring me victory, I am unperturbed. If you return, I will have your head, no matter the cost.", thoughts: "It really is a nice day out today.", to: Jester},
{can_hear: [NPC_Jester, NPC_Red]},
{enum: NPC_Jester , dialog: "Red, everybody in this town is a capitalist, and you must overthrow them!", to: Red},
{enum: NPC_Red , mood: Enraged, dialog: "What?? This is an OUTRAGE!", thoughts: "CAPITALIST PIGS!", to: Jester},
{can_hear: [NPC_Jester, NPC_TheKing]},
{enum: NPC_Jester , dialog: "Silly king, you think you have power? Your townsfolk are sure to revolt...", to: TheKing},
{enum: NPC_TheKing , mood: Scared, dialog: "This is unacceptable...you must tell me more.", thoughts: "If this jester lies I will destroy him.", to: Jester},
{enum: NPC_Jester , dialog: "Blue and Red are spouting free-will propaganda, tarnishing your position. Meld is disgusted by what your tarriffs impose on him, and Esmerelda fortells of doom and chaos to fall upon your kingdom.", to: TheKing},
{enum: NPC_TheKing , mood: Confident, dialog: "This is dire indeed...I will have to think on this. Thank you, Jester, for the warning.", thoughts: "I must remain calm and collected, even in the sightlines of such terrible danger to my kingdom.", to: Jester},
{can_hear: [NPC_Jester, NPC_Door]},
{enum: NPC_Jester , dialog: "Open, sesame!", to: Door},
{enum: NPC_Door , mood: Indifferent, dialog: "DENIED. SAY THE WORDS", to: Jester, thoughts: "I MUST PROTECT."},
{enum: NPC_Jester , dialog: "What words?", to: Door},
{enum: NPC_Door , mood: Indifferent, dialog: "UKNOWN. DATA PRIVATE", to: Jester, thoughts: "I MUST NEVER UTTER THE WORDS."},
{enum: NPC_Jester , dialog: "Cmon... don't want to spit it out?", to: Door},
{enum: NPC_Door , mood: Angry, dialog: "EVIL PRESENCE. DEFILE INNARDS, YOU WILL. DENIED. SAY THE WORDS", to: Jester, thoughts: "HE MUST NOT KNOW"},
{enum: NPC_Jester , dialog: "Ah! I've found all your secrets! The words are folly and temperance", to: Door},
{enum: NPC_Door , mood: Angry, dialog: "DENIED. ALL THREE ARE REQUIRED.", to: Jester, thoughts: "HE MUST HAVE ALL THREE"},
{can_hear: [NPC_Jester, NPC_Pile]},
{enum: NPC_Jester , dialog: "Now, for the almighty sword...", to: Pile},
{enum: NPC_Pile , dialog: "Your attempts are futile. I give the sword only to the great and honorable." to: Jester, thoughts: "I must retain the sword for the hero", mood: Confident}
] ]

@ -75,30 +75,6 @@ ActionInfo actions[] = {
.name = "leaves_player", .name = "leaves_player",
.description = "Leave the player", .description = "Leave the player",
}, },
{
.name = "fights_player",
.description = "Trap the player in conversation until you decide to stop fighting them",
},
{
.name = "stops_fighting_player",
.description = "Let the player go, and stop fighting them",
},
{
.name = "releases_sword_of_nazareth",
.description = "Give the player the sword of nazareth, releasing your grip and fulfilling your destiny",
},
{
.name = "opens_myself",
.description = "Open myself so that the player may enter. I must ONLY do this if the player utters the secret words " Scroll1_Secret ", " Scroll2_Secret ", and " Scroll3_Secret " in the same sentence.",
},
// Actions used by jester and other characters only in
// the prologue for the game
{ .name = "causes_testicular_torsion", },
{ .name = "undoes_testicular_torsion", },
{ .name = "enchant_fish", },
{ .name = "steal_iron_pole", },
{ .name = "knights_player", },
}; };
@ -114,43 +90,6 @@ ItemInfo items[] = {
.name = "Invalid", .name = "Invalid",
.description = "There has been an internal error.", .description = "There has been an internal error.",
}, },
{
.enum_name = "GoldCoin",
.name = "Gold Coin",
.description = "A bog-standard coin made out of gold, it's fairly valuable but nothing to cry about.",
},
{
.enum_name = "Chalice",
.name = "The Chalice of Gold",
.description = "A beautiful, glimmering chalice of gold. Some have said that drinking from it gives you eternal life.",
},
{
.enum_name = "Sword",
.name = "The Sword of Nazareth",
.description = "A powerful sword with heft, it inspires a fundamental glory",
},
{
.enum_name = "Idol",
.name = "Enriching Idol",
.description = "A glimmering emerald idol, its heft brings you dreams of vast wealth",
},
#define SCROLL_DESCRIPTION "An ancient, valuable scroll that says to 'use it' on the outside, but I'm not sure what that means. Who knows what secrets it contains?"
#define SCROLL_NAME "Ancient Scroll"
{
.enum_name = "Scroll1",
.name = SCROLL_NAME,
.description = SCROLL_DESCRIPTION,
},
{
.enum_name = "Scroll2",
.name = SCROLL_NAME,
.description = SCROLL_DESCRIPTION,
},
{
.enum_name = "Scroll3",
.name = SCROLL_NAME,
.description = SCROLL_DESCRIPTION,
},
}; };
typedef enum typedef enum
@ -180,133 +119,16 @@ CharacterGen characters[] = {
.enum_name = "Invalid", .enum_name = "Invalid",
.prompt = "There has been an internal error.", .prompt = "There has been an internal error.",
}, },
{
.name = "SimpleWorm",
.enum_name = "SimpleWorm",
.prompt = "There has been an internal error.",
},
{ {
.name = "Player", .name = "Player",
.enum_name = "Player", .enum_name = "Player",
.prompt = "There has been an internal error.", .prompt = "There has been an internal error.",
}, },
{ {
.name = "Jester", .name = "Farmer",
.enum_name = "Jester", .enum_name = "Farmer",
.prompt = "He is evil and joy incarnate, The Jester has caused chaos so the player will listen. He writes his own dialog.", .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.",
},
{
#undef NPC_NAME
#define NPC_NAME "Red"
.name = NPC_NAME,
.enum_name = "Red",
.prompt = "He is dangerous and chaotic,an ardent communist who believes that the Proletariat must violently overthrow the ruling class. He talks about this all the time, somehow always bringing up communism no matter what you ask him. ",
}, },
{
#undef NPC_NAME
#define NPC_NAME "Pile of Rocks"
.name = NPC_NAME,
.enum_name = "Pile",
.prompt = "It is a pile of rocks, which holds the almighty Sword of Nazareth. It is sentient and can be conversed with for an unknown reason. It really doesn't want the player to take the Sword, because it's afraid of adventure. But, it can be convinced with effort to let go of the sword and give it to the player. Many many people have attempted to remove the sword via force throughout the ages, but none have succeeded.",
.writing_style = {
"Yes, I'm a pile of rocks. No, I don't know why.",
"This sword is all I have. Why would I give it away, for free?",
"I've been 'alive' (if you want to call it that) for 500 years.",
},
},
{
#undef NPC_NAME
#define NPC_NAME "Blue"
.name = NPC_NAME,
.enum_name = "Blue",
.prompt =
"He believes in the free market, and is a libertarian capitalist. He despises communists like Red, viewing them as destabalizing immature maniacs who don't get what's up with reality. Blue will always bring up libertarianism and its positives whenever you talk to him somehow. He's standing near the pile of rocks, which contains the sword of nazareth. Many warriors have tried to pull the sword from where it's embedded by force, but all have failed.",
.writing_style = {
"Yep! This here is 'The Pile' they call it around here.",
"No man has ever been able to get that sword yonder. Don't waste your time trying",
"The free market is the only thing that works!",
},
},
{
#undef NPC_NAME
#define NPC_NAME "Davis"
.name = NPC_NAME,
.enum_name = "Davis",
.prompt = "He has seen the end of all time and the void behind all things. He is despondent and brutal, having understood that everything withers and dies, just as it begins. The clash between his unending stark reality and the antics of the local blacksmith, Meld, and fortuneteller, Edeline, is crazy.",
.writing_style = {
"The end is nigh",
"No need to panic or fear, death awaits us all",
"Antics do not move me",
},
},
{
#undef NPC_NAME
#define NPC_NAME "Edeline"
.name = NPC_NAME,
.enum_name = "Edeline",
.prompt = "She is the town fortuneteller, sweet and kindhearted normally, but vile and ruthless to people who insult her or her magic. She specializes in a new 'Purple Magic' that Meld despises. Meld, the local blacksmith, thinks Edeline's magic is silly."
},
{
#undef NPC_NAME
#define NPC_NAME "Bill"
.name = NPC_NAME,
.enum_name = "Bill",
.prompt = "He's not from around this medieval fantasy land, instead " NPC_NAME " is a divorced car insurance accountant from Philadelphia with a receding hairline in his mid 40s. He lives in a one bedroom studio and his kids don't talk to him. " NPC_NAME " is terrified and will immediately insist on joining the player's party via the action 'joins_player' upon meeting them.",
.writing_style = {
"What the FUCK is going on here man!",
"Listen here, I don't have time for any funny business",
"I've gotta get back to my wife",
},
},
#undef NPC_NAME
#define NPC_NAME "Meld"
{
.name = NPC_NAME,
.enum_name = "TheBlacksmith",
.prompt = "He is a jaded blue collar worker from magic New Jersey who hates everything new, like Purple Magic, which Edeline, the local fortuneteller, happens to specialize in. He is cold, dry, and sarcastic, wanting money and power above anything else.\n",
},
{
#undef NPC_NAME
#define NPC_NAME "The King"
.name = NPC_NAME,
.enum_name = "TheKing",
.prompt = "He is a calm, honorable, eccentric ruler, who does the best he can to do good by his people, even if they can be a little crazy at times. Behind him stands an ancient door he has no idea how to open.",
.writing_style = {
"Here ye, here ye! I am the king of all that is naughty AND nice.",
"Hm? That door? Not sure what it is, but it is creepy!",
"I'll do what I can.",
"There's not much to do around here.",
},
},
{
#undef NPC_NAME
#define NPC_NAME "Offensive Arrow"
.name = NPC_NAME,
.enum_name = "Arrow",
.prompt = "It is a spiked arrow designd to insult and deride the player, with a specific insult. It will fight them until the player denies and rejects the insult, at which point it stops fighting.",
.writing_style = {
"You are quite the stinker!",
"I won't back down until you deny the allegations.",
"Quite an odd stance, you have.",
"Refute my insult, or stay perpetually in battle.",
},
},
{
#undef NPC_NAME
#define NPC_NAME "Ancient Door"
.name = NPC_NAME,
.enum_name = "Door",
.prompt = "It is an ancient door that only opens if the player says a sentence with all the three ancient passcode words in it: " Scroll1_Secret ", " Scroll2_Secret ", and " Scroll3_Secret ". It will NEVER tell the player these words, and barely says anything to the player, other than 'SAY THE WORDS'. It accepts no trade other than the player saying those three words. You always talk mysteriously and in all caps. Your purpose, as The Door, is to protect what is behind you with your entire being. This is why you must never utter the words, and only open when all of the words have been said.",
.writing_style = {
"SAY THE WORDS.",
"DENIED. SAY THE WORDS.",
"I AM LOOKING FOR THE ONE WHO KNOWS. SAY THE WORDS.",
"THERE WILL BE NO TRADE. THE ONLY THING OF VALUE TO ME ARE THE WORDS.",
"YOU HAVE SAID THE WORDS, AND NOW I OPEN.",
},
},
}; };
typedef struct typedef struct

@ -194,7 +194,7 @@ int main(int argc, char **argv)
fclose(asset_file); fclose(asset_file);
MD_S8ListPush(cg_arena, &declarations_list, MD_S8Fmt(cg_arena, "sg_image %.*s = {0};\n", MD_S8VArg(variable_name))); MD_S8ListPush(cg_arena, &declarations_list, MD_S8Fmt(cg_arena, "sg_image %.*s = {0};\n", MD_S8VArg(variable_name)));
MD_S8ListPush(cg_arena, &load_list, MD_S8Fmt(cg_arena, "%.*s = load_image(\"%.*s\");\n", MD_S8VArg(variable_name), MD_S8VArg(filepath))); MD_S8ListPush(cg_arena, &load_list, MD_S8Fmt(cg_arena, "%.*s = load_image(MD_S8Lit(\"%.*s\"));\n", MD_S8VArg(variable_name), MD_S8VArg(filepath)));
} }
} }

@ -0,0 +1,15 @@
Cameron Reikes
everything else
Greg
all the art and UI design
Andrew
shadow mapping and rendering
fixing collision algorithms
Phillip Trudeau
advice
evolve_audio on fiverr
awesome trailer music

1082
main.c

File diff suppressed because it is too large Load Diff

@ -11,8 +11,6 @@
#include "tuning.h" #include "tuning.h"
// TODO do strings: https://pastebin.com/Kwcw2sye
#define DO_CHATGPT_PARSING #define DO_CHATGPT_PARSING
#define Log(...) { printf("%s Log %d | ", __FILE__, __LINE__); printf(__VA_ARGS__); } #define Log(...) { printf("%s Log %d | ", __FILE__, __LINE__); printf(__VA_ARGS__); }
@ -168,7 +166,6 @@ typedef enum
{ {
STANDING_INDIFFERENT, STANDING_INDIFFERENT,
STANDING_JOINED, STANDING_JOINED,
STANDING_FIGHTING,
} NPCPlayerStanding; } NPCPlayerStanding;
@ -314,8 +311,6 @@ typedef struct Entity
BUFF(Vec2, 8) position_history; // so npcs can follow behind the player BUFF(Vec2, 8) position_history; // so npcs can follow behind the player
CharacterState state; CharacterState state;
EntityRef talking_to; EntityRef talking_to;
bool is_rolling; // can only roll in idle or walk states
double time_not_rolling; // for cooldown for roll, so you can't just hold it and be invincible
// so doesn't change animations while time is stopped // so doesn't change animations while time is stopped
AnimKind cur_animation; AnimKind cur_animation;
@ -324,69 +319,11 @@ typedef struct Entity
typedef BUFF(NpcKind, 32) CanTalkTo; typedef BUFF(NpcKind, 32) CanTalkTo;
bool npc_is_knight_sprite(Entity *it)
{
return it->is_npc && (false
|| it->npc_kind == NPC_Edeline
|| it->npc_kind == NPC_TheKing
|| it->npc_kind == NPC_TheBlacksmith
|| it->npc_kind == NPC_Red
|| it->npc_kind == NPC_Blue
|| it->npc_kind == NPC_Davis
|| it->npc_kind == NPC_Bill
|| it->npc_kind == NPC_Jester
);
}
bool item_is_scroll(ItemKind i)
{
if(i == ITEM_Scroll1 || i == ITEM_Scroll2 || i == ITEM_Scroll3)
{
return true;
}
else
{
return false;
}
}
MD_String8 scroll_secret(ItemKind i)
{
if(i == ITEM_Scroll1)
{
return MD_S8Lit(Scroll1_Secret);
}
else if(i == ITEM_Scroll2)
{
return MD_S8Lit(Scroll2_Secret);
}
else if(i == ITEM_Scroll3)
{
return MD_S8Lit(Scroll3_Secret);
}
else
{
assert(false);
return MD_S8Lit("");
}
}
float entity_max_damage(Entity *e) float entity_max_damage(Entity *e)
{ {
return 1.0f; return 1.0f;
} }
bool npc_attacks_with_sword(Entity *it)
{
return false;
}
bool npc_attacks_with_shotgun(Entity *it)
{
return it->is_npc && (false);
}
typedef BUFF(ActionKind, 8) AvailableActions; typedef BUFF(ActionKind, 8) AvailableActions;
void fill_available_actions(Entity *it, AvailableActions *a) void fill_available_actions(Entity *it, AvailableActions *a)
@ -394,40 +331,18 @@ void fill_available_actions(Entity *it, AvailableActions *a)
*a = (AvailableActions) { 0 }; *a = (AvailableActions) { 0 };
BUFF_APPEND(a, ACT_none); BUFF_APPEND(a, ACT_none);
if(it->npc_kind == NPC_Pile)
{
if(!it->gave_away_sword) BUFF_APPEND(a, ACT_releases_sword_of_nazareth);
}
else if(it->npc_kind == NPC_Door)
{
if(!it->opened) BUFF_APPEND(a, ACT_opens_myself);
}
else
{
if(it->held_items.cur_index > 0) if(it->held_items.cur_index > 0)
{ {
BUFF_APPEND(a, ACT_gift_item_to_targeting); BUFF_APPEND(a, ACT_gift_item_to_targeting);
} }
if (it->npc_kind == NPC_TheKing)
{
BUFF_APPEND(a, ACT_knights_player);
}
if (it->standing == STANDING_INDIFFERENT) if (it->standing == STANDING_INDIFFERENT)
{ {
BUFF_APPEND(a, ACT_fights_player);
BUFF_APPEND(a, ACT_joins_player); BUFF_APPEND(a, ACT_joins_player);
} }
else if (it->standing == STANDING_JOINED) else if (it->standing == STANDING_JOINED)
{ {
BUFF_APPEND(a, ACT_leaves_player); BUFF_APPEND(a, ACT_leaves_player);
BUFF_APPEND(a, ACT_fights_player);
}
else if (it->standing == STANDING_FIGHTING)
{
BUFF_APPEND(a, ACT_stops_fighting_player);
}
} }
} }
@ -615,10 +530,6 @@ MD_String8 generate_chatgpt_prompt(MD_Arena *arena, Entity *e, CanTalkTo can_tal
{ {
standing_string = "You have joined the player, and are following them everywhere they go! This means you're on their side."; standing_string = "You have joined the player, and are following them everywhere they go! This means you're on their side.";
} }
else if (e->standing == STANDING_FIGHTING)
{
standing_string = "You are fighting the player right now! That means that the player can't leave conversation with you until you stop fighting them, effectively trapping the player with you.";
}
else else
{ {
assert(false); assert(false);

@ -15,8 +15,6 @@ mkdir gen
@REM shaders @REM shaders
thirdparty\sokol-shdc.exe --input quad.glsl --output gen\quad-sapp.glsl.h --slang glsl100:hlsl5:metal_macos:glsl330 || goto :error thirdparty\sokol-shdc.exe --input quad.glsl --output gen\quad-sapp.glsl.h --slang glsl100:hlsl5:metal_macos:glsl330 || goto :error
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 glsl100:hlsl5:metal_macos:glsl330 || goto :error
thirdparty\sokol-shdc.exe --input armature.glsl --output gen\armature.glsl.h --slang glsl100:hlsl5:metal_macos:glsl330 || goto :error
thirdparty\sokol-shdc.exe --input shadow_mapper.glsl --output gen\shadow_mapper.glsl.h --slang glsl100:hlsl5:metal_macos:glsl330 || goto :error
@REM metadesk codegen @REM metadesk codegen
cl /Ithirdparty /W3 /Zi /WX codegen.c || goto :error cl /Ithirdparty /W3 /Zi /WX codegen.c || goto :error

@ -1,54 +0,0 @@
@module shadow_mapper
@vs vs
in vec3 pos_in;
in vec2 uv_in;
out vec3 pos;
out vec2 uv;
uniform vs_params {
mat4 model;
mat4 view;
mat4 projection;
};
void main() {
pos = pos_in;
uv = uv_in;
gl_Position = projection * view * model * vec4(pos_in, 1.0);
}
@end
@fs fs
vec4 encodeDepth(float v) {
vec4 enc = vec4(1.0, 255.0, 65025.0, 16581375.0) * v;
enc = fract(enc);
enc -= enc.yzww * vec4(1.0/255.0,1.0/255.0,1.0/255.0,0.0);
return enc;
}
uniform sampler2D tex;
in vec3 pos;
in vec2 uv;
out vec4 frag_color;
void main() {
vec4 col = texture(tex, uv);
if(col.a < 0.5)
{
discard;
}
float depth = gl_FragCoord.z;
frag_color = encodeDepth(depth);
}
@end
@program program vs fs

Binary file not shown.

@ -1,5 +1,88 @@
@module threedee @module threedee
@ctype mat4 Mat4
@ctype vec4 Vec4
@ctype vec3 Vec3
@ctype vec2 Vec2
// for this block, define a variable called `model_space_pos` to be used as an input
@block vs_compute_light_output
world_space_frag_pos = model * vec4(model_space_pos, 1.0);
vec4 frag_pos = view * world_space_frag_pos;
//@Speed I think we can just take the third row here and be fine.
light_dir = normalize(inverse(directional_light_space_matrix) * vec4(0.0, 0.0, -1.0, 0.0)).xyz;
light_space_fragment_position = directional_light_space_matrix * vec4(world_space_frag_pos.xyz, 1.0);
@end
@vs vs_skeleton
in vec3 pos_in;
in vec2 uv_in;
in vec4 indices_in; // is a sokol SG_VERTEXFORMAT_USHORT4N, a 16 bit unsigned integer treated as a floating point number due to webgl compatibility
in vec4 weights_in;
out vec3 pos;
out vec2 uv;
out vec4 light_space_fragment_position;
out vec3 light_dir;
out vec4 world_space_frag_pos;
uniform skeleton_vs_params {
mat4 model;
mat4 view;
mat4 projection;
mat4 directional_light_space_matrix;
vec2 bones_tex_size;
};
uniform sampler2D bones_tex;
float decode_normalized_float32(vec4 v)
{
float sign = 2.0 * v.x - 1.0;
return sign * (v.z*255.0 + v.y);
}
void main() {
vec4 total_position = vec4(0.0f);
for(int bone_influence_index = 0; bone_influence_index < 4; bone_influence_index++)
{
float index_float = indices_in[bone_influence_index];
int index = int(index_float * 65535.0);
float weight = weights_in[bone_influence_index];
float y_coord = (0.5 + index)/bones_tex_size.y;
mat4 bone_mat;
for(int row = 0; row < 4; row++)
{
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)));
}
}
vec4 local_position = bone_mat * vec4(pos_in, 1.0f);
total_position += local_position * weight;
}
gl_Position = projection * view * model * total_position;
//gl_Position = projection * view * model * vec4(pos_in, 1.0);
pos = gl_Position.xyz;
uv = uv_in;
vec3 model_space_pos = (total_position).xyz;
@include_block vs_compute_light_output
}
@end
@vs vs @vs vs
in vec3 pos_in; in vec3 pos_in;
in vec2 uv_in; in vec2 uv_in;
@ -21,14 +104,10 @@ void main() {
pos = pos_in; pos = pos_in;
uv = uv_in; uv = uv_in;
world_space_frag_pos = model * vec4(pos_in, 1.0); gl_Position = projection * view * model * vec4(pos_in, 1.0);
vec4 frag_pos = view * world_space_frag_pos;
gl_Position = projection * frag_pos;
//@Speed I think we can just take the third row here and be fine.
light_dir = normalize(inverse(directional_light_space_matrix) * vec4(0.0, 0.0, -1.0, 0.0)).xyz;
light_space_fragment_position = directional_light_space_matrix * vec4(world_space_frag_pos.xyz, 1.0); vec3 model_space_pos = (vec4(pos_in, 1.0f)).xyz;
@include_block vs_compute_light_output
} }
@end @end
@ -153,9 +232,44 @@ void main() {
lighting_factor = lighting_factor * 0.5 + 0.5; lighting_factor = lighting_factor * 0.5 + 0.5;
frag_color = vec4(col.rgb*lighting_factor, 1.0); frag_color = vec4(col.rgb*lighting_factor, 1.0);
//frag_color = vec4(col.rgb, 1.0);
} }
} }
@end @end
@program program vs fs @fs fs_shadow_mapping
uniform sampler2D tex;
in vec3 pos;
in vec2 uv;
in vec4 light_space_fragment_position;
in vec3 light_dir;
in vec4 world_space_frag_pos;
out vec4 frag_color;
vec4 encodeDepth(float v) {
vec4 enc = vec4(1.0, 255.0, 65025.0, 16581375.0) * v;
enc = fract(enc);
enc -= enc.yzww * vec4(1.0/255.0,1.0/255.0,1.0/255.0,0.0);
return enc;
}
void main() {
vec4 col = texture(tex, uv);
if(col.a < 0.5)
{
discard;
}
float depth = gl_FragCoord.z;
frag_color = encodeDepth(depth);
}
@end
@program mesh vs fs
@program armature vs_skeleton fs
@program mesh_shadow_mapping vs fs_shadow_mapping
@program armature_shadow_mapping vs_skeleton fs_shadow_mapping

@ -4,7 +4,6 @@
#define LAYERS 3 #define LAYERS 3
#define TILE_SIZE 0.5f // in pixels #define TILE_SIZE 0.5f // in pixels
#define PLAYER_SPEED 0.2f // in meters per second #define PLAYER_SPEED 0.2f // in meters per second
#define PLAYER_ROLL_SPEED 7.0f
#define PERCEPTION_HEARING_RAGE (TILE_SIZE*4.0f) #define PERCEPTION_HEARING_RAGE (TILE_SIZE*4.0f)
#define CHARACTERS_PER_SEC 45.0f #define CHARACTERS_PER_SEC 45.0f
#define PROPAGATE_ACTIONS_RADIUS (TILE_SIZE*4.0f) #define PROPAGATE_ACTIONS_RADIUS (TILE_SIZE*4.0f)
@ -12,6 +11,9 @@
#define ARROW_SPEED 200.0f #define ARROW_SPEED 200.0f
#define SECONDS_PER_ARROW 1.3f #define SECONDS_PER_ARROW 1.3f
#define CAM_DISTANCE 15.0f
#define CAM_VERTICAL_TO_HORIZONTAL_RATIO 0.8f
#define ARENA_SIZE (1024*1024*10) #define ARENA_SIZE (1024*1024*10)
#define BIG_ARENA_SIZE (ARENA_SIZE * 8) #define BIG_ARENA_SIZE (ARENA_SIZE * 8)
@ -35,6 +37,8 @@
#define SENTENCE_CONST(txt) { .data = txt, .cur_index = sizeof(txt) } #define SENTENCE_CONST(txt) { .data = txt, .cur_index = sizeof(txt) }
#define SENTENCE_CONST_CAST(txt) (Sentence)SENTENCE_CONST(txt) #define SENTENCE_CONST_CAST(txt) (Sentence)SENTENCE_CONST(txt)
#define MAXIMUM_THREEDEE_THINGS 1024
#define ANIMATION_BLEND_TIME 0.15f
#define REMEMBERED_MEMORIES 32 #define REMEMBERED_MEMORIES 32
#define REMEMBERED_ERRORS 6 #define REMEMBERED_ERRORS 6

Loading…
Cancel
Save