The Snivy Video Game Is Complete

This commit is contained in:
2025-12-29 05:10:56 -05:00
parent d0f9669b8b
commit 62b988a678
705 changed files with 210576 additions and 162 deletions

View File

@@ -9,6 +9,7 @@
#include "../resource/texture.h"
#include <glm/glm.hpp>
#include <iostream>
using namespace glm;
using namespace game::util;
@@ -16,20 +17,70 @@ using namespace game::anm2;
namespace game::resource
{
std::shared_ptr<void> texture_callback(const std::filesystem::path& path) { return std::make_shared<Texture>(path); }
std::shared_ptr<void> sound_callback(const std::filesystem::path& path) { return std::make_shared<Audio>(path); }
Actor::Actor(const std::filesystem::path& path, vec2 position) : anm2(path, texture_callback, sound_callback)
Actor::Actor(Anm2* _anm2, vec2 _position, Mode mode, float time) : anm2(_anm2), position(_position)
{
this->position = position;
play(anm2.animations.defaultAnimation);
if (anm2)
{
this->mode = mode;
this->startTime = time;
play(anm2->animations.defaultAnimation, mode, time);
}
}
anm2::Animation* Actor::animation_get() { return vector::find(anm2.animations.items, animationIndex); }
anm2::Item* Actor::item_get(anm2::Type type, int id)
anm2::Animation* Actor::animation_get(int index)
{
if (auto animation = animation_get())
if (!anm2) return nullptr;
if (index == -1) index = animationIndex;
if (anm2->animations.mapReverse.contains(index)) return &anm2->animations.items[index];
return nullptr;
}
anm2::Animation* Actor::animation_get(const std::string& name)
{
if (!anm2) return nullptr;
if (anm2->animations.map.contains(name)) return &anm2->animations.items[anm2->animations.map[name]];
return nullptr;
}
bool Actor::is_playing(const std::string& name)
{
if (!anm2) return false;
if (name.empty())
return isPlaying;
else
return isPlaying && anm2->animations.map[name] == animationIndex;
}
int Actor::animation_index_get(const std::string& name)
{
if (!anm2) return -1;
if (anm2->animations.map.contains(name)) return anm2->animations.map[name];
return -1;
}
int Actor::item_id_get(const std::string& name, anm2::Type type)
{
if (!anm2 || (type != anm2::LAYER && type != anm2::NULL_)) return -1;
if (type == anm2::LAYER)
{
for (int i = 0; i < anm2->content.layers.size(); i++)
if (anm2->content.layers.at(i).name == name) return i;
}
else if (type == anm2::NULL_)
{
for (int i = 0; i < anm2->content.nulls.size(); i++)
if (anm2->content.nulls.at(i).name == name) return i;
}
return -1;
}
anm2::Item* Actor::item_get(anm2::Type type, int id, int animationIndex)
{
if (!anm2) return nullptr;
if (animationIndex == -1) animationIndex = this->animationIndex;
if (auto animation = animation_get(animationIndex))
{
switch (type)
{
@@ -51,6 +102,16 @@ namespace game::resource
return nullptr;
}
int Actor::item_length(anm2::Item* item)
{
if (!item) return -1;
int duration{};
for (auto& frame : item->frames)
duration += frame.duration;
return duration;
}
anm2::Frame* Actor::trigger_get(int atFrame)
{
if (auto item = item_get(anm2::TRIGGER))
@@ -66,7 +127,14 @@ namespace game::resource
return nullptr;
}
anm2::Frame Actor::frame_generate(anm2::Item& item, float time)
bool Actor::is_event(const std::string& event)
{
if (!anm2) return false;
if (playedEventID == -1) return false;
return event == anm2->content.events.at(playedEventID).name;
}
anm2::Frame Actor::frame_generate(anm2::Item& item, float time, anm2::Type type, int id)
{
anm2::Frame frame{};
frame.isVisible = false;
@@ -76,6 +144,7 @@ namespace game::resource
time = time < 0.0f ? 0.0f : time;
anm2::Frame* frameNext = nullptr;
anm2::Frame frameNextCopy{};
int durationCurrent = 0;
int durationNext = 0;
@@ -90,7 +159,10 @@ namespace game::resource
if (time >= durationCurrent && time < durationNext)
{
if (i + 1 < (int)item.frames.size())
{
frameNext = &item.frames[i + 1];
frameNextCopy = *frameNext;
}
else
frameNext = nullptr;
break;
@@ -99,53 +171,110 @@ namespace game::resource
durationCurrent += frame.duration;
}
for (auto& override : overrides)
{
if (!override || !override->isEnabled) continue;
if (id == override->destinationID)
{
switch (override->mode)
{
case Override::FRAME_ADD:
if (override->frame.scale.has_value())
{
frame.scale += *override->frame.scale;
if (frameNext) frameNextCopy.scale += *override->frame.scale;
}
if (override->frame.rotation.has_value())
{
frame.rotation += *override->frame.rotation;
if (frameNext) frameNextCopy.rotation += *override->frame.rotation;
}
break;
case Override::FRAME_SET:
if (override->frame.scale.has_value())
{
frame.scale = *override->frame.scale;
if (frameNext) frameNextCopy.scale = *override->frame.scale;
}
if (override->frame.rotation.has_value())
{
frame.rotation = *override->frame.rotation;
if (frameNext) frameNextCopy.rotation = *override->frame.rotation;
}
break;
case Override::ITEM_SET:
default:
if (override->animationIndex == -1) break;
auto& animation = anm2->animations.items[override->animationIndex];
auto overrideFrame = frame_generate(animation.layerAnimations[override->sourceID], override->time,
anm2::LAYER, override->sourceID);
frame.crop = overrideFrame.crop;
break;
}
}
}
if (frame.isInterpolated && frameNext && frame.duration > 1)
{
auto interpolation = (time - durationCurrent) / (durationNext - durationCurrent);
frame.rotation = glm::mix(frame.rotation, frameNext->rotation, interpolation);
frame.position = glm::mix(frame.position, frameNext->position, interpolation);
frame.scale = glm::mix(frame.scale, frameNext->scale, interpolation);
frame.colorOffset = glm::mix(frame.colorOffset, frameNext->colorOffset, interpolation);
frame.tint = glm::mix(frame.tint, frameNext->tint, interpolation);
frame.rotation = glm::mix(frame.rotation, frameNextCopy.rotation, interpolation);
frame.position = glm::mix(frame.position, frameNextCopy.position, interpolation);
frame.scale = glm::mix(frame.scale, frameNextCopy.scale, interpolation);
frame.colorOffset = glm::mix(frame.colorOffset, frameNextCopy.colorOffset, interpolation);
frame.tint = glm::mix(frame.tint, frameNextCopy.tint, interpolation);
}
return frame;
}
void Actor::play(const std::string& name)
void Actor::play(int index, Mode mode, float time, float speedMultiplier)
{
for (int i = 0; i < anm2.animations.items.size(); i++)
{
if (anm2.animations.items[i].name == name)
{
animationIndex = i;
time = 0.0f;
isPlaying = true;
break;
}
}
this->playedEventID = -1;
this->playedTriggers.clear();
if (!anm2) return;
if (mode != FORCE_PLAY && this->animationIndex == index) return;
if (!vector::in_bounds(anm2->animations.items, index)) return;
this->speedMultiplier = speedMultiplier;
this->previousAnimationIndex = animationIndex;
this->animationIndex = index;
this->time = time;
if (mode == PLAY || mode == FORCE_PLAY) isPlaying = true;
}
void Actor::play(const std::string& name, Mode mode, float time, float speedMultiplier)
{
if (!anm2) return;
if (anm2->animations.map.contains(name))
play(anm2->animations.map.at(name), mode, time, speedMultiplier);
else
std::cout << "Animation \"" << name << "\" does not exist! Unable to play!\n";
}
void Actor::tick()
{
if (!anm2) return;
if (!isPlaying) return;
auto animation = animation_get();
if (!animation) return;
time += anm2.info.fps / 30.0f;
playedEventID = -1;
auto intTime = (int)time;
if (auto trigger = trigger_get(intTime))
for (auto& trigger : animation->triggers.frames)
{
if (!playedTriggers.contains(intTime))
if (!playedTriggers.contains(trigger.atFrame) && time >= trigger.atFrame)
{
if (auto sound = map::find(anm2.content.sounds, trigger->soundID)) sound->audio.play();
playedTriggers.insert(intTime);
if (auto sound = map::find(anm2->content.sounds, trigger.soundID)) sound->audio.play();
playedTriggers.insert((int)trigger.atFrame);
playedEventID = trigger.eventID;
}
}
auto increment = (anm2->info.fps / 30.0f) * speedMultiplier;
time += increment;
if (time >= animation->frameNum)
{
if (animation->isLoop)
@@ -155,43 +284,138 @@ namespace game::resource
playedTriggers.clear();
}
for (auto& override : overrides)
{
if (!override->isEnabled || override->length < 0) continue;
override->time += increment;
if (override->time > override->length) override->isLoop ? override->time = 0.0f : override->isEnabled = false;
}
}
void Actor::render(Shader& shader, Canvas& canvas)
glm::vec4 Actor::null_frame_rect(int nullID)
{
auto invalidRect = glm::vec4(0.0f / 0.0f);
if (!anm2 || nullID == -1) return invalidRect;
auto item = item_get(anm2::NULL_, nullID);
if (!item) return invalidRect;
auto animation = animation_get();
if (!animation) return invalidRect;
auto root = frame_generate(animation->rootAnimation, time, anm2::ROOT);
for (auto& override : overrides)
{
if (!override || !override->isEnabled || override->type != anm2::ROOT) continue;
switch (override->mode)
{
case Override::FRAME_ADD:
if (override->frame.scale.has_value()) root.scale += *override->frame.scale;
if (override->frame.rotation.has_value()) root.rotation += *override->frame.rotation;
break;
case Override::FRAME_SET:
if (override->frame.scale.has_value()) root.scale = *override->frame.scale;
if (override->frame.rotation.has_value()) root.rotation = *override->frame.rotation;
break;
default:
break;
}
}
auto frame = frame_generate(*item, time, anm2::NULL_, nullID);
if (!frame.isVisible) return invalidRect;
auto rootScale = math::to_unit(root.scale);
auto frameScale = math::to_unit(frame.scale);
auto combinedScale = rootScale * frameScale;
auto scaledSize = NULL_SIZE * glm::abs(combinedScale);
auto worldPosition = position + root.position + frame.position * rootScale;
auto halfSize = scaledSize * 0.5f;
return glm::vec4(worldPosition - halfSize, scaledSize);
}
void Actor::render(Shader& textureShader, Shader& rectShader, Canvas& canvas)
{
if (!anm2) return;
auto animation = animation_get();
if (!animation) return;
auto root = frame_generate(animation->rootAnimation, time);
auto rootModel = math::quad_model_parent_get(root.position + position, root.pivot,
math::percent_to_unit(root.scale), root.rotation);
auto root = frame_generate(animation->rootAnimation, time, anm2::ROOT);
for (auto& override : overrides)
{
if (!override || !override->isEnabled || override->type != anm2::ROOT) continue;
switch (override->mode)
{
case Override::FRAME_ADD:
if (override->frame.scale.has_value()) root.scale += *override->frame.scale;
if (override->frame.rotation.has_value()) root.rotation += *override->frame.rotation;
break;
case Override::FRAME_SET:
if (override->frame.scale.has_value()) root.scale = *override->frame.scale;
if (override->frame.rotation.has_value()) root.rotation = *override->frame.rotation;
break;
default:
break;
}
}
auto rootModel =
math::quad_model_parent_get(root.position + position, root.pivot, math::to_unit(root.scale), root.rotation);
for (auto& i : animation->layerOrder)
{
auto& layerAnimation = animation->layerAnimations[i];
if (!layerAnimation.isVisible) continue;
auto layer = map::find(anm2.content.layers, i);
auto layer = map::find(anm2->content.layers, i);
if (!layer) continue;
auto spritesheet = map::find(anm2.content.spritesheets, layer->spritesheetID);
auto spritesheet = map::find(anm2->content.spritesheets, layer->spritesheetID);
if (!spritesheet) continue;
auto frame = frame_generate(layerAnimation, time);
auto frame = frame_generate(layerAnimation, time, anm2::LAYER, i);
if (!frame.isVisible) continue;
auto model = math::quad_model_get(frame.size, frame.position, frame.pivot, math::percent_to_unit(frame.scale),
frame.rotation);
auto model =
math::quad_model_get(frame.size, frame.position, frame.pivot, math::to_unit(frame.scale), frame.rotation);
model = rootModel * model;
auto& texture = spritesheet->texture;
if (!texture.is_valid()) return;
auto tint = frame.tint * root.tint;
auto colorOffset = frame.colorOffset + root.colorOffset;
auto uvMin = frame.crop / vec2(texture.size);
auto uvMax = (frame.crop + frame.size) / vec2(texture.size);
auto uvVertices = math::uv_vertices_get(uvMin, uvMax);
canvas.texture_render(shader, texture.id, model, frame.tint, frame.colorOffset, uvVertices.data());
canvas.texture_render(textureShader, texture.id, model, tint, colorOffset, uvVertices.data());
}
if (isShowNulls)
{
for (int i = 0; i < animation->nullAnimations.size(); i++)
{
auto& nullAnimation = animation->nullAnimations[i];
if (!nullAnimation.isVisible) continue;
auto frame = frame_generate(nullAnimation, time, anm2::NULL_, i);
if (!frame.isVisible) continue;
auto model = math::quad_model_get(frame.scale, frame.position, frame.scale * 0.5f, vec2(1.0f), frame.rotation);
model = rootModel * model;
canvas.rect_render(rectShader, model);
}
}
}
void Actor::consume_played_event() { playedEventID = -1; }
};