Skip to content

Commit 2379967

Browse files
authored
Reloading resources from "Debug" menu (#2928)
The new "Reload Resources" option in the "Debug" menu allows for reloading all fonts, textures, sprites and tilesets. **Additionally:** * Removes the `name` field for sprites. I honestly didn't know it existed before, and don't know what the point of it is, considering it's not referenced anywhere in the code, other than in logs, where a filename can be used instead. Closes #2901.
1 parent b51c16a commit 2379967

26 files changed

+362
-199
lines changed

src/object/moving_sprite.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ MovingSprite::change_sprite(const std::string& new_sprite_name)
209209
m_sprite_name = new_sprite_name;
210210
update_hitbox();
211211

212-
return SpriteManager::current()->last_load_successful();
212+
return m_sprite->load_successful();
213213
}
214214

215215
ObjectSettings

src/sprite/sprite.hpp

+2-3
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,6 @@ class Sprite final
8585
/** Get current frame progress */
8686
float get_current_frame_progress() const { return m_frame; }
8787

88-
/** Get sprite's name */
89-
const std::string& get_name() const { return m_data.name; }
90-
9188
/** Get current action name */
9289
const std::string& get_action() const { return m_action->name; }
9390

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

127+
bool load_successful() const { return m_data.m_load_successful; }
128+
130129
private:
131130
void update();
132131

src/sprite/sprite_data.cpp

+123-52
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// SuperTux
22
// Copyright (C) 2006 Matthias Braun <[email protected]>
3+
// 2023-2024 Vankata453
34
//
45
// This program is free software: you can redistribute it and/or modify
56
// it under the terms of the GNU General Public License as published by
@@ -29,6 +30,7 @@
2930
#include "util/reader_document.hpp"
3031
#include "util/reader_mapping.hpp"
3132
#include "util/reader_object.hpp"
33+
#include "util/string_util.hpp"
3234
#include "video/surface.hpp"
3335
#include "video/texture_manager.hpp"
3436

@@ -48,70 +50,141 @@ SpriteData::Action::Action() :
4850
{
4951
}
5052

51-
SpriteData::SpriteData(const ReaderMapping& mapping) :
52-
actions(),
53-
name()
53+
void
54+
SpriteData::Action::reset(SurfacePtr surface)
5455
{
55-
auto iter = mapping.get_iter();
56-
while (iter.next())
57-
{
58-
if (iter.get_key() == "name") {
59-
iter.get(name);
60-
} else if (iter.get_key() == "action") {
61-
parse_action(iter.as_mapping());
62-
} else {
63-
log_warning << "Unknown sprite field: " << iter.get_key() << std::endl;
64-
}
65-
}
66-
if (actions.empty())
67-
throw std::runtime_error("Error: Sprite without actions.");
56+
x_offset = 0;
57+
y_offset = 0;
58+
hitbox_w = static_cast<float>(surface->get_width());
59+
hitbox_h = static_cast<float>(surface->get_height());
60+
hitbox_unisolid = false;
61+
fps = 10;
62+
loops = -1;
63+
loop_frame = 1;
64+
has_custom_loops = false;
65+
family_name.clear();
66+
surfaces = { surface };
6867
}
6968

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

78-
auto action = create_action_from_surface(surface);
79-
action->name = "default";
80-
actions[action->name] = std::move(action);
70+
SpriteData::SpriteData(const std::string& filename) :
71+
m_filename(filename),
72+
m_load_successful(false),
73+
actions()
74+
{
75+
load();
8176
}
8277

83-
SpriteData::SpriteData() :
84-
actions(),
85-
name()
78+
void
79+
SpriteData::load()
8680
{
87-
auto surface = Surface::from_texture(TextureManager::current()->create_dummy_texture());
88-
auto action = create_action_from_surface(surface);
89-
action->name = "default";
90-
actions[action->name] = std::move(action);
81+
// Reset all existing actions to a dummy texture
82+
if (!actions.empty())
83+
{
84+
auto surface = Surface::from_texture(TextureManager::current()->create_dummy_texture());
85+
for (const auto& action : actions)
86+
action.second->reset(surface);
87+
}
88+
89+
if (StringUtil::has_suffix(m_filename, ".sprite"))
90+
{
91+
try
92+
{
93+
auto doc = ReaderDocument::from_file(m_filename);
94+
auto root = doc.get_root();
95+
96+
if (root.get_name() != "supertux-sprite")
97+
{
98+
std::ostringstream msg;
99+
msg << "'" << m_filename << "' is not a 'supertux-sprite' file!";
100+
throw std::runtime_error(msg.str());
101+
}
102+
else
103+
{
104+
// Load ".sprite" file
105+
parse(root.get_mapping());
106+
}
107+
}
108+
catch (const std::exception& err)
109+
{
110+
log_warning << "Parse error when trying to load sprite '" << m_filename
111+
<< "': " << err.what() << std::endl;
112+
113+
// Load initial dummy texture
114+
if (actions.empty())
115+
{
116+
auto surface = Surface::from_texture(TextureManager::current()->create_dummy_texture());
117+
auto action = std::make_unique<Action>();
118+
action->name = "default";
119+
action->reset(surface);
120+
actions[action->name] = std::move(action);
121+
}
122+
123+
m_load_successful = false;
124+
return;
125+
}
126+
}
127+
else
128+
{
129+
// Load single image
130+
auto surface = Surface::from_file(m_filename);
131+
if (!TextureManager::current()->last_load_successful())
132+
throw std::runtime_error("Cannot load image.");
133+
134+
// Create action, if it doesn't exist
135+
{
136+
auto i = actions.find("default");
137+
if (i == actions.end())
138+
{
139+
auto action = std::make_unique<Action>();
140+
action->name = "default";
141+
actions["default"] = std::move(action);
142+
}
143+
}
144+
actions["default"]->reset(surface);
145+
}
146+
147+
m_load_successful = true;
91148
}
92149

93-
std::unique_ptr<SpriteData::Action>
94-
SpriteData::create_action_from_surface(SurfacePtr surface)
150+
void
151+
SpriteData::parse(const ReaderMapping& mapping)
95152
{
96-
auto action = std::make_unique<Action>();
97-
98-
action->hitbox_w = static_cast<float>(surface->get_width());
99-
action->hitbox_h = static_cast<float>(surface->get_height());
100-
action->surfaces.push_back(surface);
153+
auto iter = mapping.get_iter();
154+
while (iter.next())
155+
{
156+
if (iter.get_key() == "action")
157+
parse_action(iter.as_mapping());
158+
else
159+
log_warning << "Unknown sprite field: " << iter.get_key() << std::endl;
160+
}
101161

102-
return action;
162+
if (actions.empty())
163+
throw std::runtime_error("Error: Sprite without actions.");
103164
}
104165

105166
void
106167
SpriteData::parse_action(const ReaderMapping& mapping)
107168
{
108-
auto action = std::make_unique<Action>();
169+
std::string name;
170+
mapping.get("name", name);
109171

110-
if (!mapping.get("name", action->name))
172+
// Create action, if it doesn't exist
111173
{
112-
if (!actions.empty())
113-
throw std::runtime_error("If there are more than one action, they need names!");
174+
auto i = actions.find(name);
175+
if (i == actions.end())
176+
{
177+
auto action = std::make_unique<Action>();
178+
action->name = name;
179+
actions[name] = std::move(action);
180+
}
114181
}
182+
Action* action = actions[name].get();
183+
184+
// Reset action
185+
action->hitbox_w = 0;
186+
action->hitbox_h = 0;
187+
action->surfaces.clear();
115188

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

130203
default:
131-
throw std::runtime_error("hitbox should specify 2/4 coordinates");
204+
throw std::runtime_error("Hitbox should specify 2/4 coordinates!");
132205
}
133206
}
134207
mapping.get("unisolid", action->hitbox_unisolid);
@@ -141,7 +214,7 @@ SpriteData::parse_action(const ReaderMapping& mapping)
141214
{
142215
if (action->loop_frame < 1)
143216
{
144-
log_warning << "'loop-frame' of action '" << action->name << "' in sprite '" << name << "' set to a value below 1." << std::endl;
217+
log_warning << "'loop-frame' of action '" << action->name << "' in sprite '" << m_filename << "' set to a value below 1." << std::endl;
145218
action->loop_frame = 1;
146219
}
147220
}
@@ -343,7 +416,7 @@ SpriteData::parse_action(const ReaderMapping& mapping)
343416
else
344417
{
345418
std::stringstream msg;
346-
msg << "Sprite '" << name << "' unknown tag in 'surfaces' << " << i.get_name();
419+
msg << "Sprite '" << m_filename << "' unknown tag in 'surfaces' << " << i.get_name();
347420
throw std::runtime_error(msg.str());
348421
}
349422
}
@@ -362,7 +435,7 @@ SpriteData::parse_action(const ReaderMapping& mapping)
362435
else
363436
{
364437
std::stringstream msg;
365-
msg << "Sprite '" << name << "' contains no images in action '"
438+
msg << "Sprite '" << m_filename << "' contains no images in action '"
366439
<< action->name << "'.";
367440
throw std::runtime_error(msg.str());
368441
}
@@ -372,11 +445,9 @@ SpriteData::parse_action(const ReaderMapping& mapping)
372445
const int frames = static_cast<int>(action->surfaces.size());
373446
if (action->loop_frame > frames && frames > 0)
374447
{
375-
log_warning << "'loop-frame' of action '" << action->name << "' in sprite '" << name << "' not-in-range of total frames." << std::endl;
448+
log_warning << "'loop-frame' of action '" << action->name << "' in sprite '" << m_filename << "' not-in-range of total frames." << std::endl;
376449
action->loop_frame = 1;
377450
}
378-
379-
actions[action->name] = std::move(action);
380451
}
381452

382453
const SpriteData::Action*

src/sprite/sprite_data.hpp

+19-22
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// SuperTux
22
// Copyright (C) 2006 Matthias Braun <[email protected]>
3+
// 2023-2024 Vankata453
34
//
45
// This program is free software: you can redistribute it and/or modify
56
// it under the terms of the GNU General Public License as published by
@@ -17,8 +18,8 @@
1718
#ifndef HEADER_SUPERTUX_SPRITE_SPRITE_DATA_HPP
1819
#define HEADER_SUPERTUX_SPRITE_SPRITE_DATA_HPP
1920

20-
#include <map>
2121
#include <string>
22+
#include <unordered_map>
2223
#include <vector>
2324

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

3233
public:
33-
/**
34-
* Sprite from data.
35-
* `mapping` has to be a pointer to data in the form of "((hitbox 5 10 0 0) ...)".
36-
*/
37-
SpriteData(const ReaderMapping& mapping);
38-
/** Single-image sprite */
39-
SpriteData(const std::string& image);
40-
/** Dummy texture sprite */
41-
SpriteData();
42-
43-
const std::string& get_name() const
44-
{
45-
return name;
46-
}
34+
SpriteData(const std::string& filename);
35+
36+
void load();
4737

4838
private:
49-
struct Action
39+
struct Action final
5040
{
5141
Action();
5242

43+
void reset(SurfacePtr surface);
44+
5345
std::string name;
5446

5547
/** Position correction */
@@ -87,17 +79,22 @@ class SpriteData final
8779
std::vector<SurfacePtr> surfaces;
8880
};
8981

90-
typedef std::map<std::string, std::unique_ptr<Action> > Actions;
91-
92-
static std::unique_ptr<Action> create_action_from_surface(SurfacePtr surface);
93-
82+
private:
83+
void parse(const ReaderMapping& mapping);
9484
void parse_action(const ReaderMapping& mapping);
95-
/** Get an action */
85+
9686
const Action* get_action(const std::string& act) const;
9787

9888
private:
89+
const std::string m_filename;
90+
bool m_load_successful;
91+
92+
typedef std::unordered_map<std::string, std::unique_ptr<Action>> Actions;
9993
Actions actions;
100-
std::string name;
94+
95+
private:
96+
SpriteData(const SpriteData& other);
97+
SpriteData& operator=(const SpriteData&) = delete;
10198
};
10299

103100
#endif

0 commit comments

Comments
 (0)