#Saurian Sorcery
4277 messages ยท Page 5 of 5 (latest)
@severe spruce
#questions message
my line drawing system draws a billboarded quad for line segments, and if there are two sequential segments, it also adds a bevel between them
this is before/after for non-sequential to sequential
assassin initial impl is done!
passive: after getting attacked, become untargetable for 1.5 seconds
attack: applies a poison which deals damage over time, stacks up to four times
spell: causes poison from attack to spread, this also stacks up to four times but is independent from the attack poison
i should say acid... would be hard for poison to do much to bots
new healthbars dropped
Your new health bars
fixed version is just me making them slightly less noisey
can't even notice that they're cylinders 
outside of the rare specular reflection if you look in exactly the right direction like at the end
lol I think people might think it's a bug
I like them though, they look a lot better
i could just have any diffuse lighting and itd prob be more visually clear
will try that later
yeah the flat shading isn't as clear on cylindrical objects, especially when they represent a UI element (which gamers will expect to be a 2D sprite already)
idea: make particles fly off the health bar when things take damage
you could put rim lighting on it or something
ye making diffuse not black will
i like this idea
but i guess you maybe don't actually want them lit
it may be difficult
so just some dot(view, N) term to make it cylinderistic
cylinderisms
deccer in jaker's walls (cell)
i played dredge over the weekend
it is very neat. one cool thing i noticed is how it does foliage
its shading is entirely flat, so it does things like palms by coloring the underside of the leafs
for the most part the only thing that matters though is the silhoutte, i think only observing them from a distance helps make that work
other than that, i really like the scope of the game
also sidenote, obviously havent done much on this, insert triangle meme of work/life/hobby, pick two
the srgb correct triangle of life
well i did something this weekend, can swap out now
I loved when they were stacked like
smooched
not necessarily a revival, but i have made some guys
unfortunately i have to learn unreal

they be cute
i fixed a small bug with outlines
and it makes a lot of stuff look so much better, it was a pretty minor bug but when it's on outlines i guess it was more distracting than i realized
this is the old version, there are some non outlined pixels in there, like specifically the right corner it can be seen
vs now there's none of that
was kind of an interesting bug, i only write normal outline values if the g-buffer depth was less or equal to the traced water depth
but i early outed while tracing the water for the g-buffer depth, so actually the g-buffer depth was always a max for the traced depth
and for whatever reason that i do not understand, that would only cause artifacts on edges
also text rendering
weee
starting a basic hud
i have two layers for this text rendering, one which is "reusable" that handles the color and shadow tags
the other one is on the game level, which expands tags like {tip=haste1} into a yellow color, and it also has the base text layer return a bounding box for that text so the input can query it if it's hovered
oh you wrote the formatting bit?
i mean the tags into formatting
yes the only thing i'm using external code for here is glyph rendering
the formatting is bespoke
v nice
hopefully the rest isn't too hellish 
i'm worried about needing hierarchies
since they feel pretty mandatory for anything moving, but it's annoying to do because needing to reference elements means i can't just mindlessly spam thinly abstracted draw calls
Ay nice
up next: word wrapping, and making them look less like shit
Thankfully word wrapping is not a cursed problem or anything haha
(Tbf if you do it only on word boundaries it's fine)
im prob just gonna wrap on full words and not allow words longer than the text width
yeah
i am engineering and design so dumb constraints are my jam
also no non english
if someone doesnt speak english and wants to play my game, they must put up a PR ๐
Makes things much simpler indeed
yeah i was planning on doing that, i didnt have it entirely figured out yet though
You hover over your guy, tooltip tells you you have X of resource A, you hover over resource A on that tooltip, it tells you a bit about what that resource is, etc
Yeah
Never implemented it meself
it should be simple when i actually write code for it, but i just need to make sure that i implement a stack for active IDs
Yup
Sounds incompatible with the tooltip window following the cursor
If you wanted to do that
Oh ye the tooltips are static
nah the tooltip is just below the text yeag
and stays active if the source text or the tooltip region are hovered
It's an excellent solution imo
Cant believe it took this long to see games with it
i actually figured out a system i like for sourcing the tooltips, maybe its common, but i need to map something like "tip=haste1" to a color and a description
so the color and the description are just virtual methods on a base type, which i like from a code location perspective
i just confused myself
im doing something with enums that i'm too tired to remember
oh its the card instancing
So you make a new class for each of the tips?
Why not make it data driven
the classes are reused, theyre the same classes as i use for the actual effects
i will figure out wtf i was thinking of tomorrow
okay, the actual thing that i was thinking of was how i implement the get_attack_title() and such
this is an issue i've had before, where you have to implement a lot of some type of thing and there can be a lot of unnecessary overhead, like huge switch statements, needing to register implementations, whatever else
implementing type-specific (not instance specific) functions like get_color or get_attack_title is one example of the best way to do that not being exactly clear to me. do you...
- base class/derived class? this has the advantage of having compiler checking that your functions are implemented, but the disadvantage that if you have a value at runtime, converting that to the color isn't trivial, it'd be something like a switch statement that maps to an instance and you call it on the instance (this is what i'm doing with the hit effects that i mentioned above)
- static class functions? slight advantage of not having to instance classes, but has the same downside as above
- big switch blocks with an enum input? advantage of not having to instance classes, and you can convert a runtime-value into them trivially, but imo this has the downside that implementing new classes can be frustrating, because you have to find all the massive switch statements and plug your little thing in, you can't do separate files based on types
for the actual get_attack_title, what i'm doing that isn't any of those, is using an enum in a template parameter, then having a parent template that calls all of them, e.g.:
// lizard.hpp
enum LizardType {
LizardType_Assassin,
LizardType_Ranger,
};
template <LizardType T>
string get_name();
template <LizardType T>
string get_attack_title();
// assassin.hpp
template <>
string get_name<LizardType_Assassin>() {
return "Assassin";
}
template <>
string get_attack_title<LizardType_Assassin>() {
return AssassinAttack().get_name(); // by this pattern, this should be get_name<LizardType_Assassin, AbilityType_Attack>();
}```
// shop.hpp
template<LizardType T>
void add_lizard_card() {
CardGUI card(get_name<T>(), get_attack_title<T>(), get_spell_title<T>());
cards.push_back(card);
} ```
the reason that i like this is that if i add a new class, it feels easy to me, i just call add_lizard_card<LizardType_NewLizard>() wherever my shop generator is, and implement the functions. if i miss any functions, i get compiler errors, and there are only one place to add them anyways
i think this still has the problem of using runtime type values though
I would just do this in data
as in load a csv?
as in you have a big json file called "enemy" and you have a list of enemies
you don't need to make it fully data-driven, e.g. you could have a preset list of spells and such that they just refer to
instead of scripting or w/e
but you should consider making it data-driven to make your life easier tbh
thinking about this is for the intent of making my life easier, i'm not convinced it is though :P
e.g. being able to reload these things at runtime, being able to add one very easily, not needing to rebuild every time you change something
also it scales easier
e.g. if you need to add a new field
what's the linkage from data to function
e.g. 1 sec
say i have 5 functions on 2 abilities (setup, targeting, start_casting, cast, finish_cast), and one more on the lizard
the lizard table probably has 3 entries, one for each ability, and the extra one, but what's actually in it? then i convert that to a function ptr at runtime?
this?...
#define REGISTER_FUNCTION(value, function) get_function_database().value[hash_view(#function)] = function;
struct FunctionDatabase {
umap<uint64, AbilityTargetingFunction> ability_targeting_functions;
umap<uint64, AbilitySetupFunction> ability_setup_functions;
umap<uint64, AbilityStartCastFunction> ability_start_cast_functions;
umap<uint64, AbilityCastFunction> ability_cast_functions;
umap<uint64, AbilityEndCastFunction> ability_end_cast_functions;
umap<uint64, LizardSetupFunction> lizard_setup_functions;
};
void register_functions() {
REGISTER_FUNCTION(ability_setup_functions, assassin_attack_targeting_function);
REGISTER_FUNCTION(ability_setup_functions, assassin_spell_targeting_function);
REGISTER_FUNCTION(ability_targeting_functions, assassin_attack_targeting_function);
REGISTER_FUNCTION(ability_targeting_functions, assassin_spell_targeting_function);
REGISTER_FUNCTION(ability_start_cast_functions, assassin_attack_start_cast_function);
REGISTER_FUNCTION(ability_start_cast_functions, assassin_spell_start_cast_function);
REGISTER_FUNCTION(ability_cast_functions, assassin_attack_cast_function);
REGISTER_FUNCTION(ability_cast_functions, assassin_spell_cast_function);
REGISTER_FUNCTION(ability_end_cast_functions, assassin_attack_end_cast_function);
REGISTER_FUNCTION(ability_end_cast_functions, assassin_spell_end_cast_function);
REGISTER_FUNCTION(lizard_setup_functions, assassin_setup_function);
}```
this is worse than what i currently do, so what's the real way to do it
usually for stuff like this you map them to data (enum or string or w/e)
and you just do it in a factory function or w/e
most things are data anyways when you think about it
e.g. between spell A and spell B
what is really the difference
some damage, some VFX, stuff like that
some stuff is quite different and that you can represent as enums
or structs you pass to a variant
and then you have a factory function that just fills this spell description
and spell descriptions are processed all the same
loading data would probably work well for some aspects of it like names, but in the past i have not liked the more functional part of it
I mean, you're the only person on the project so it really doesn't matter
unless you want to support mods
if you do then this is the only way, but if you don't you don't need to care, carry on the way you're doin it
i still don't think i'll ever release this, but i do want to learn from it so figuring out a way that i'm happy with is worthwhile to me
right
well in a game with many people, you can't expect the guy who's setting up data to know code
so you have to make things easy for them too and that means data driving
in a game with few people you might do both the data and the code, in which case you can go with your approach, but it doesn't handle mods, scaling the team, stuff like that
see if abilities were scripts this would be really easy :)
and overall it's much dirtier, if you think about it you're embedding data into your code like magic values basically
yeah for sure
that's the next step I guess, but even then you might not want to make everything scriptable
surely there's some way to macro magic this to store it next to the function
instead of in the register_functions thing, have the register be next to the function definition
in AAA you usually have a data definition format that automagically generates stuff, and lets you define functions which you can then call from data
yeah
that's a whole thing tho lol
i've worked with those before, it is nice, but yeah
ah nice
ideally for me, to make a new lizard, i add an entry to the enum, then just add a single file and implement the functions in there
sry didn't mean to AAAxplain 
i only interact with those 2 files, and barely even with the first one
hmm right
so i can do
template<>
void targeting<LizardType_Assassin, AbilityFunctionType_Targeting>() {
Caster& caster_comp = scene->registry.get<Caster>(caster);
if (taunted(*this, caster_comp))
return;
plus_targeting(1, *this, entry_gather_function, entry_eval_function);
}
REGISTER_FUNCTION(ability_targeting_functions, targeting<LizardType_Assassin, AbilityFunctionType_Targeting>)
and i don't need any header that way
isn't there a macro way to make code run at startup ๐
like constructor of a new type or some balogna
is type just based off FILE/LINE, so like
#define HORROR(code)
struct HORROR_##__FILE__##__LINE__ { HORROR_##__FILE__##__LINE__() { code }
HORROR_##__FILE__##__LINE__ horror_instance;```
@severe spruce weren't you doing something like this? with the macros
oh cool that works (https://godbolt.org/z/f196EcYdP)
okay
i will try this
i don't think there's an issue, cause i'm doing
static Database database;
return database;
}```
first static init will instance the destination first
Meyers singleton my beloved
going 2 make so many people on stack overflow mad wit this one
one slightly unfortunate bit about this is that i don't get compiler feedback if a function isn't implemented and i expect it to be
but eh, that's minor
here's another issue about this
it's minor but in a high traffic system
template<>
void targeting<LizardType_Assassin, AbilityFunctionType_Targeting>() {
Caster& caster_comp = scene->registry.get<Caster>(caster);
if (taunted(*this, caster_comp))
return;
plus_targeting(1, *this, entry_gather_function, entry_eval_function);
}
REGISTER_FUNCTION(ability_targeting_functions, targeting<LizardType_Assassin, AbilityFunctionType_Targeting>)```
let's say i really do go with this
there's no base object, and a lot of abilities have state
so i probably have to pass state into it, which means almost every ability function has to deal with void* shenanigans
e.g. i have to deal with this
struct AssassinAttackPayload {
Scene* scene;
entt::entity caster;
int32 phase = 0;
};
template<>
void* setup<LizardType_Assassin, AbilityType_Attack>(Scene* scene, entt::entity caster) {
Health& health = scene->registry.get<Health>(caster);
entt::sink sink{health.damage_signal};
sink.connect<handle_assassin_damaged>();
return new AssassinAttackPayload{scene, caster, 0};
}
REGISTER_ABILITY_SETUP(LizardType_Assassin, AbilityType_Attack);
template<>
void cleanup<LizardType_Assassin, AbilityType_Attack>(void* data) {
AssassinAttackPayload& payload = *((AssassinAttackPayload*)data;
// ...
}
so i'm wondering if there's any better way to handle this
...lambdas
hmmm
what if i did actually make overriding the ability class optional
or even mandatory
actually making the ability class templated makes the most sense
definitely would be if i could add member variables at least
okay i'm keeping the inheritence based ability system, and just moving parameters to data
@cursive chasm i'm curious, if i have a table of abilities, and each ability has different parameters, do you have any suggestion for how to store them?
my two ideas are
- just have a list of slots that the ability uses in documented ways, e.g. slot1 is damage, slot2 is range, etc.
- alternatively, do something similar, but just alternate fields in each row so it's something like "damage, 80, range, 3, duration, 1.2", then the ability can reference those by name, and the table parsing handles unnamed columns as those special fielsd
Hmm yeah you could do it either way really, I'd recommend doing it in a structured, readable textual format though, e.g. json
First approach has the drawback that you may change the slots' semantics, which json covers (parameters are named)
The second approach also solves this but has the problem of being a bit less readable and very brittle
First approach also has the drawback of being unreadable
ok
i am satisfied
example for one character:
// lizards
{
"Type": "LizardType_Assassin",
"Name": "Empyrea",
"Model": "resources/models/empyrea/empyrea.sbjmod",
"Scale": 0.6,
"HurtEmitter": "",
"Color": "95bb25"
},
// abilities
{
"Lizard": "LizardType_Assassin",
"Type": "AbilityType_Attack",
"Pre": 0.5,
"Post": 0.5,
"CD": 0.1,
"Damage": 2.0,
"Duration": 1.5,
"Name": "Acid Torrent",
"Description": "{tip=Reaction}Reaction{\\tip}(Damage): Gain {tip=Ethereal}Ethereal{\\tip}({ref=Duration}s)\n{tip=Damage}{ref=Damage} Damage{\\tip}. Increase {tip=Acid}Acid{\\tip} on target (max 3)."
},
{
"Lizard": "LizardType_Assassin",
"Type": "AbilityType_Spell",
"Pre": 0.4,
"Post": 0.5,
"Duration": 6.0,
"UpdateDelay": 0.5,
"Name": "Catalysis",
"Description": "All {tip=Acid}Acid{\\tip} ticks gain {tip=Slow}Slow 1{\\tip}, {tip=Splash}Splash 2{\\tip}, and {ref=Attack} can increase up to {tip=Acid}Acid 4{\\tip} (6s)."
},
// effects
"Acid": {
"Level1": 1.0,
"Level2": 2.0,
"Level3": 4.0,
"Level4": 8.0,
"Description": "Acid 1: Deals {ref=Level1} damage every second\nAcid 2: Deals {ref=Level2} damage every second\nAcid 3: Deals {ref=Level3} damage every second\nAcid 4: Deals {ref=Level4} damage every second",
"Color": "8fd411"
},```
Wow, excellent
one very minor issue that i have which explaining might help
i have a {lift=n} option in my base text layer, which just vertically shifts text, i planned on using it for tooltips to just raise then a couple of pixels for responsiveness's sake
but tooltips are id based, and the gui manager only keeps track of which region and id is hovered
ex: "{tip=HoverMe}Hover this!{\tip}, or you could also hover {tip=HoverMe}this{\tip} and it'll show the same tooltip in a different spot"
both solutions i have for adding state to the hovered state, region or string index, both feel fragile
i will probably do the string index because it's slightly less fragile (changing the string is guaranteed to change the region, but slightly moving the text doesn't change the string index)
it's somewhat odd though because i have to make sure to use the source string, because i'm doing it based off inserting strings
man
i've had a growing problem of dealing with materials with a GUI system
i've learned a bunch about how i would want to design the interface for a renderer from this project, even having written multiple renderers before
like i was primarily writing my render function for my render scene to operate on one list of renderables, and then renderables have some state that would do things like skip themselves in a shadow pass
but i think it's probably not a smart idea to avoid multiple renderable lists
i currently have lists for normal renderables, rigged renderables, static renderables, and UI renderables
which means a lot of the code that iterates over renderable lists has bloated and duplicated
material and textures are also just weird
i think i thought of materials as assets, which is a mistake imo on the renderer level
there are too many cases where you are working on a system that needs a material and you just want to quickly create it
that's the main time where the interface to your material system matters--the case of actually loading a material asset from a disk is very unique and you're going to be able to make that work no matter what
for a concrete example, i am working on GUI objects, e.g. "Text" "Panel" "List Box", etc.
a panel is going to be something like
struct Panel : ItemGUI {
v2i padding;
unique_ptr<ItemGUI> item;
FilePath texture;
void draw() override;
void update() override;
range2i get_occupied_region() override;
};
void Panel::draw() {
Renderable r = {};
r.frame_allocated = true;
// background
r.mesh_id = upload_mesh(generate_rounded_quad(given_region, settings), true);
r.material_id = textured_mat;
r.sort_index = depth;
render_scene.ui_renderables.push_back(r);
// full outline
r.mesh_id = upload_mesh(generate_rounded_outline(given_region, settings), true);
r.material_id = untextured_mat;
r.sort_index = depth + 5;
render_scene.ui_renderables.push_back(r);
if (!item) return;
item->draw();
}```
then how do the materials work? is the panel in change of uploading the pipelines? are there some preset pipelines? do the preset pipelines have default materials? because untextured_mat is very widely used, but textured_mat is going to be specific to this panel
answer i will probably go with because it's easy is just functions to construct the types of materials i need, a lot of calls to them, and caching in those functions
on a briefer, less rubber-ducky rant note, i hope this abstraction works ๐ค (testing tomorrow)
Panel panel(*this);
panel.padding = {15, 15};
panel.given_region = {tooltip_pos, tooltip_pos + v2i{400, 800}};
TextGUI text(*this);
text.text = effect_entry.description;
text.ref_floats = &effect_entry.extra_floats;
text.ref_strings = &effect_entry.extra_strings;
panel.item = make_unique<TextGUI>(text);
gui_manager->add_root_item(make_unique<Panel>(panel), 600);
before 
this is what debugging feels like to me 90% of the time
yipee
need to get this readable and slightly better designed but i'm happy to have something now
oddly my original prototype design is really hard to implement
because the background aligning with the vertical divisions is not super natural with just a parent hierarchy
i'm probably just gonna do something placeholder now that i think about it, cause i don't really like the design enough to muddle up the abstraction yet
time to move on from these for now
debating if i'm gonna do padding on the atlas so i can get away with linear filtering
i probably should, the quality increase is much more rapid than increasing resolution of the actual text
you could do manual linear filtering 
i dont think i will ๐
padding is already done
and this is acceptable quality for me
the math doesn't quite add up here but this is what the 3d text is for
back to it, thinking about adding variations to each lizard to save me some time
i could really leverage the keywords i'm working on if i do this, and i dont have much for reasons not to do it, so its prob almost guarnateed
some examples, i would prob just do color variations for each
this is probably unreadable without tooltips
idea should be clear though
some updates
i am lowering the quality of some of the models
with the outline shader, and the scale of lizards, smaller details dont read very well, so simplifying is necessary
also removing baked textures, because it's too much work and if i want to do simple variants, it'll be even harder (uv map swap vs fully separate bakes)
here's an example, he looks worse in blender imo (new on right), but i think it'll be a slight improvement in game
also thickened his bow slightly for the same reasons
UV swap example (this literally takes like 30 seconds per color swap)
i'm also finishing up the first lizard implementation using the new hit effect system i wrote (and showed a bit above), but its not quite done yet
Jrpg designers when the game still has 3 more worlds but they ran out of enemy budget
i am them
i should really optimize my builds one of these days
i've seen a lot of complaints about the effect of magic_enum on compile times
i'm using it very heavily which simulatenously means it could be having a real impact, but also it would be a ton of effort to take out
i need to update my enemy ik to suck less
but its been like 6 months
and this code is entirely undocumented, and 200 lines of magical math with mediocre variable names 
i really like how my hit, hit effect, and hit reaction system is turning out
once i implement more guys (im at 2 right now), i will write about it
got inkbound yesterday, that game is really good, its like made for me
i do like realtime games but thats not a requirement for me enjoying it
i will probably be pulling some inspo from it :^)
sneak peek cause making these takes a very long time
comfy
i dont love the way it looks right now tbh but im hoping to fix it with some shader magic and decoration
way down the line
i'm happy with the level of variation from the desert tho (attached blender screenshot for ref)
merci โค๏ธ