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
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
105166void
106167SpriteData::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
382453const SpriteData::Action*
0 commit comments