Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reloading resources from "Debug" menu #2928

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/object/moving_sprite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ MovingSprite::change_sprite(const std::string& new_sprite_name)
m_sprite_name = new_sprite_name;
update_hitbox();

return SpriteManager::current()->last_load_successful();
return m_sprite->load_successful();
}

ObjectSettings
Expand Down
5 changes: 2 additions & 3 deletions src/sprite/sprite.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,6 @@ class Sprite final
/** Get current frame progress */
float get_current_frame_progress() const { return m_frame; }

/** Get sprite's name */
const std::string& get_name() const { return m_data.name; }

/** Get current action name */
const std::string& get_action() const { return m_action->name; }

Expand Down Expand Up @@ -127,6 +124,8 @@ class Sprite final
bool has_action (const std::string& name) const { return (m_data.get_action(name) != nullptr); }
size_t get_actions_count() const { return m_data.actions.size(); }

bool load_successful() const { return m_data.m_load_successful; }

private:
void update();

Expand Down
175 changes: 123 additions & 52 deletions src/sprite/sprite_data.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// SuperTux
// Copyright (C) 2006 Matthias Braun <[email protected]>
// 2023-2024 Vankata453
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -29,6 +30,7 @@
#include "util/reader_document.hpp"
#include "util/reader_mapping.hpp"
#include "util/reader_object.hpp"
#include "util/string_util.hpp"
#include "video/surface.hpp"
#include "video/texture_manager.hpp"

Expand All @@ -48,70 +50,141 @@ SpriteData::Action::Action() :
{
}

SpriteData::SpriteData(const ReaderMapping& mapping) :
actions(),
name()
void
SpriteData::Action::reset(SurfacePtr surface)
{
auto iter = mapping.get_iter();
while (iter.next())
{
if (iter.get_key() == "name") {
iter.get(name);
} else if (iter.get_key() == "action") {
parse_action(iter.as_mapping());
} else {
log_warning << "Unknown sprite field: " << iter.get_key() << std::endl;
}
}
if (actions.empty())
throw std::runtime_error("Error: Sprite without actions.");
x_offset = 0;
y_offset = 0;
hitbox_w = static_cast<float>(surface->get_width());
hitbox_h = static_cast<float>(surface->get_height());
hitbox_unisolid = false;
fps = 10;
loops = -1;
loop_frame = 1;
has_custom_loops = false;
family_name.clear();
surfaces = { surface };
}

SpriteData::SpriteData(const std::string& image) :
actions(),
name()
{
auto surface = Surface::from_file(image);
if (!TextureManager::current()->last_load_successful())
throw std::runtime_error("Cannot load image.");

auto action = create_action_from_surface(surface);
action->name = "default";
actions[action->name] = std::move(action);
SpriteData::SpriteData(const std::string& filename) :
m_filename(filename),
m_load_successful(false),
actions()
{
load();
}

SpriteData::SpriteData() :
actions(),
name()
void
SpriteData::load()
{
auto surface = Surface::from_texture(TextureManager::current()->create_dummy_texture());
auto action = create_action_from_surface(surface);
action->name = "default";
actions[action->name] = std::move(action);
// Reset all existing actions to a dummy texture
if (!actions.empty())
{
auto surface = Surface::from_texture(TextureManager::current()->create_dummy_texture());
for (const auto& action : actions)
action.second->reset(surface);
}

if (StringUtil::has_suffix(m_filename, ".sprite"))
{
try
{
auto doc = ReaderDocument::from_file(m_filename);
auto root = doc.get_root();

if (root.get_name() != "supertux-sprite")
{
std::ostringstream msg;
msg << "'" << m_filename << "' is not a 'supertux-sprite' file!";
throw std::runtime_error(msg.str());
}
else
{
// Load ".sprite" file
parse(root.get_mapping());
}
}
catch (const std::exception& err)
{
log_warning << "Parse error when trying to load sprite '" << m_filename
<< "': " << err.what() << std::endl;

// Load initial dummy texture
if (actions.empty())
{
auto surface = Surface::from_texture(TextureManager::current()->create_dummy_texture());
auto action = std::make_unique<Action>();
action->name = "default";
action->reset(surface);
actions[action->name] = std::move(action);
}

m_load_successful = false;
return;
}
}
else
{
// Load single image
auto surface = Surface::from_file(m_filename);
if (!TextureManager::current()->last_load_successful())
throw std::runtime_error("Cannot load image.");

// Create action, if it doesn't exist
{
auto i = actions.find("default");
if (i == actions.end())
{
auto action = std::make_unique<Action>();
action->name = "default";
actions["default"] = std::move(action);
}
}
actions["default"]->reset(surface);
}

m_load_successful = true;
}

std::unique_ptr<SpriteData::Action>
SpriteData::create_action_from_surface(SurfacePtr surface)
void
SpriteData::parse(const ReaderMapping& mapping)
{
auto action = std::make_unique<Action>();

action->hitbox_w = static_cast<float>(surface->get_width());
action->hitbox_h = static_cast<float>(surface->get_height());
action->surfaces.push_back(surface);
auto iter = mapping.get_iter();
while (iter.next())
{
if (iter.get_key() == "action")
parse_action(iter.as_mapping());
else
log_warning << "Unknown sprite field: " << iter.get_key() << std::endl;
}

return action;
if (actions.empty())
throw std::runtime_error("Error: Sprite without actions.");
}

void
SpriteData::parse_action(const ReaderMapping& mapping)
{
auto action = std::make_unique<Action>();
std::string name;
mapping.get("name", name);

if (!mapping.get("name", action->name))
// Create action, if it doesn't exist
{
if (!actions.empty())
throw std::runtime_error("If there are more than one action, they need names!");
auto i = actions.find(name);
if (i == actions.end())
{
auto action = std::make_unique<Action>();
action->name = name;
actions[name] = std::move(action);
}
}
Action* action = actions[name].get();

// Reset action
action->hitbox_w = 0;
action->hitbox_h = 0;
action->surfaces.clear();

std::vector<float> hitbox;
if (mapping.get("hitbox", hitbox))
Expand All @@ -128,7 +201,7 @@ SpriteData::parse_action(const ReaderMapping& mapping)
break;

default:
throw std::runtime_error("hitbox should specify 2/4 coordinates");
throw std::runtime_error("Hitbox should specify 2/4 coordinates!");
}
}
mapping.get("unisolid", action->hitbox_unisolid);
Expand All @@ -141,7 +214,7 @@ SpriteData::parse_action(const ReaderMapping& mapping)
{
if (action->loop_frame < 1)
{
log_warning << "'loop-frame' of action '" << action->name << "' in sprite '" << name << "' set to a value below 1." << std::endl;
log_warning << "'loop-frame' of action '" << action->name << "' in sprite '" << m_filename << "' set to a value below 1." << std::endl;
action->loop_frame = 1;
}
}
Expand Down Expand Up @@ -343,7 +416,7 @@ SpriteData::parse_action(const ReaderMapping& mapping)
else
{
std::stringstream msg;
msg << "Sprite '" << name << "' unknown tag in 'surfaces' << " << i.get_name();
msg << "Sprite '" << m_filename << "' unknown tag in 'surfaces' << " << i.get_name();
throw std::runtime_error(msg.str());
}
}
Expand All @@ -362,7 +435,7 @@ SpriteData::parse_action(const ReaderMapping& mapping)
else
{
std::stringstream msg;
msg << "Sprite '" << name << "' contains no images in action '"
msg << "Sprite '" << m_filename << "' contains no images in action '"
<< action->name << "'.";
throw std::runtime_error(msg.str());
}
Expand All @@ -372,11 +445,9 @@ SpriteData::parse_action(const ReaderMapping& mapping)
const int frames = static_cast<int>(action->surfaces.size());
if (action->loop_frame > frames && frames > 0)
{
log_warning << "'loop-frame' of action '" << action->name << "' in sprite '" << name << "' not-in-range of total frames." << std::endl;
log_warning << "'loop-frame' of action '" << action->name << "' in sprite '" << m_filename << "' not-in-range of total frames." << std::endl;
action->loop_frame = 1;
}

actions[action->name] = std::move(action);
}

const SpriteData::Action*
Expand Down
41 changes: 19 additions & 22 deletions src/sprite/sprite_data.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// SuperTux
// Copyright (C) 2006 Matthias Braun <[email protected]>
// 2023-2024 Vankata453
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
Expand All @@ -17,8 +18,8 @@
#ifndef HEADER_SUPERTUX_SPRITE_SPRITE_DATA_HPP
#define HEADER_SUPERTUX_SPRITE_SPRITE_DATA_HPP

#include <map>
#include <string>
#include <unordered_map>
#include <vector>

#include "video/surface_ptr.hpp"
Expand All @@ -30,26 +31,17 @@ class SpriteData final
friend class Sprite;

public:
/**
* Sprite from data.
* `mapping` has to be a pointer to data in the form of "((hitbox 5 10 0 0) ...)".
*/
SpriteData(const ReaderMapping& mapping);
/** Single-image sprite */
SpriteData(const std::string& image);
/** Dummy texture sprite */
SpriteData();

const std::string& get_name() const
{
return name;
}
SpriteData(const std::string& filename);

void load();

private:
struct Action
struct Action final
{
Action();

void reset(SurfacePtr surface);

std::string name;

/** Position correction */
Expand Down Expand Up @@ -87,17 +79,22 @@ class SpriteData final
std::vector<SurfacePtr> surfaces;
};

typedef std::map<std::string, std::unique_ptr<Action> > Actions;

static std::unique_ptr<Action> create_action_from_surface(SurfacePtr surface);

private:
void parse(const ReaderMapping& mapping);
void parse_action(const ReaderMapping& mapping);
/** Get an action */

const Action* get_action(const std::string& act) const;

private:
const std::string m_filename;
bool m_load_successful;

typedef std::unordered_map<std::string, std::unique_ptr<Action>> Actions;
Actions actions;
std::string name;

private:
SpriteData(const SpriteData& other);
SpriteData& operator=(const SpriteData&) = delete;
};

#endif
Expand Down
Loading