aboutsummaryrefslogtreecommitdiff
path: root/src/crepe/api/Script.h
blob: b000d9de29afe3f0dcc6d398f25343de2ceb35cb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
#pragma once

#include <vector>

#include "../api/KeyCodes.h"
#include "../manager/EventManager.h"
#include "../manager/LoopTimerManager.h"
#include "../manager/Mediator.h"
#include "../manager/ReplayManager.h"
#include "../system/CollisionSystem.h"
#include "../system/InputSystem.h"
#include "../types.h"
#include "../util/Log.h"
#include "../util/OptionalRef.h"

namespace crepe {

class ScriptSystem;
class BehaviorScript;
class ComponentManager;

/**
 * \brief Script interface
 *
 * This class is used as a base class for user-defined scripts that can be added to game
 * objects using the \c BehaviorScript component.
 *
 * \note Additional *events* (like Unity's OnDisable and OnEnable) should be implemented as
 * member or lambda methods in derivative user script classes and registered in \c init().
 *
 * \warning Concrete scripts are allowed do create a custom constructor, but the utility
 * functions should not be called inside the constructor as they rely on late references that
 * are only available after the constructor returns.
 *
 * \see feature_script
 */
class Script {
protected:
	/**
	 * \name Interface functions
	 * \{
	 */
	/**
	 * \brief Script initialization function (empty by default)
	 *
	 * This function is called during the ScriptSystem::update() routine *before*
	 * Script::update() if it (a) has not yet been called and (b) the \c BehaviorScript component
	 * holding this script instance is active.
	 */
	virtual void init() {}
	/**
	 * \brief Script fixed update function (empty by default)
	 *
	 * \param delta_time Time since last fixed update
	 *
	 * \note This function is called during the ScriptSystem::update() routine if the \c
	 * BehaviorScript component holding this script instance is active.
	 */
	virtual void fixed_update(duration_t delta_time) {}
	/**
	 * \brief Script frame update function (empty by default)
	 *
	 * \param delta_time Time since last frame update
	 *
	 * \note This function is called during the ScriptSystem::update() routine if the \c
	 * BehaviorScript component holding this script instance is active.
	 */
	virtual void frame_update(duration_t delta_time) {}
	//! \}

	//! ScriptSystem calls \c init() and \c update()
	friend class crepe::ScriptSystem;

protected:
	/**
	 * \name Component query functions
	 * \see ComponentManager
	 * \{
	 */
	/**
	 * \brief Get single component of type \c T on this game object
	 * \tparam T Type of component
	 * \returns Reference to component
	 * \throws std::runtime_error if this game object does not have a component with type \c T
	 */
	template <typename T>
	T & get_component() const;
	/**
	 * \brief Get all components of type \c T on this game object
	 * \tparam T Type of component
	 * \returns List of component references
	 */
	template <typename T>
	RefVector<T> get_components() const;
	//! \copydoc ComponentManager::get_components_by_id
	template <typename T>
	RefVector<T> get_components_by_id(game_object_id_t id) const;
	//! \copydoc ComponentManager::get_components_by_name
	template <typename T>
	RefVector<T> get_components_by_name(const std::string & name) const;
	//! \copydoc ComponentManager::get_components_by_tag
	template <typename T>
	RefVector<T> get_components_by_tag(const std::string & tag) const;
	//! \}

	/**
	 * \name Logging functions
	 * \see Log
	 * \{
	 */
	//! \copydoc Log::logf
	template <class... Args>
	void logf(const Log::Level & level, std::format_string<Args...> fmt, Args &&... args);
	//! \copydoc Log::logf
	template <class... Args>
	void logf(std::format_string<Args...> fmt, Args &&... args);
	// \}

	/**
	 * \name Event manager functions
	 * \see EventManager
	 * \{
	 */
	//! \copydoc EventManager::subscribe
	template <typename EventType>
	void subscribe(const EventHandler<EventType> & callback, event_channel_t channel);
	//! \copydoc EventManager::subscribe
	template <typename EventType>
	void subscribe(const EventHandler<EventType> & callback);
	//! \copydoc EventManager::trigger_event
	template <typename EventType>
	void trigger_event(
		const EventType & event = {}, event_channel_t channel = EventManager::CHANNEL_ALL
	);
	//! \copydoc EventManager::queue_event
	template <typename EventType>
	void queue_event(
		const EventType & event = {}, event_channel_t channel = EventManager::CHANNEL_ALL
	);
	//! \}

	/**
	 * \name Scene-related functions
	 * \see SceneManager
	 * \{
	 */
	//! \copydoc SceneManager::set_next_scene
	void set_next_scene(const std::string & name);
	//! \}

	/**
	 * \name Save data management functions
	 * \see SaveManager
	 * \{
	 */
	//! Retrieve SaveManager reference
	SaveManager & get_save_manager() const;
	//! \}

	/**
	 * \name Timing functions
	 * \see LoopTimerManager
	 * \{
	 */
	//! Retrieve LoopTimerManager reference
	LoopTimerManager & get_loop_timer() const;
	//! \}

	//! Replay management functions
	struct replay { // NOLINT
		//! \copydoc ReplayManager::record_start
		void record_start();
		//! \copydoc ReplayManager::record_end
		recording_t record_end();
		//! \copydoc ReplayManager::play
		void play(recording_t);
		//! \copydoc ReplayManager::release
		void release(recording_t);

	private:
		OptionalRef<Mediator> & mediator;
		replay(OptionalRef<Mediator> & mediator) : mediator(mediator) {}
		friend class Script;
	} replay {mediator};

	/**
	 * \brief Utility function to retrieve the keyboard state
	 * \see SDLContext::get_keyboard_state
	 * 
	 * \return current keyboard state map with Keycode as key and bool as value(true = pressed, false = not pressed)
	 */
	const keyboard_state_t & get_keyboard_state() const;
	/**
	 * \brief Utility function to retrieve a single key state.
	 * \see SDLContext::get_keyboard_state
	 * 
	 * \return Keycode state (true if pressed, false if not pressed).
	 */
	bool get_key_state(Keycode key) const noexcept;

private:
	/**
	 * \brief Internal subscribe function
	 *
	 * This function exists so certain template specializations of Script::subscribe can be
	 * explicitly deleted, and does the following:
	 * - Wrap the user-provided callback in a check that tests if the parent BehaviorScript
	 *   component is still active
	 * - Store the subscriber handle returned by the event manager so this listener is
	 *   automatically unsubscribed at the end of this Script instance's life
	 *
	 * \tparam EventType concrete Event class
	 * \param callback User-provided callback function
	 * \param channel Event channel (may have been overridden by template specializations)
	 */
	template <typename EventType>
	void subscribe_internal(const EventHandler<EventType> & callback, event_channel_t channel);

protected:
	// NOTE: This must be the only constructor on Script, see "Late references" below
	Script() = default;
	//! Only \c BehaviorScript instantiates Script
	friend class BehaviorScript;

public:
	// std::unique_ptr destroys script
	virtual ~Script();

private:
	Script(const Script &) = delete;
	Script(Script &&) = delete;
	Script & operator=(const Script &) = delete;
	Script & operator=(Script &&) = delete;

private:
	/**
	 * \name Late references
	 *
	 * These references are set by BehaviorScript immediately after calling the constructor of
	 * Script.
	 *
	 * \note Script must have a constructor without arguments so the game programmer doesn't need
	 * to manually add `using Script::Script` to their concrete script class if they want to
	 * implement a non-default constructor (e.g. for passing references to their own concrete
	 * Script classes).
	 *
	 * \{
	 */
	//! Game object ID of game object parent BehaviorScript is attached to
	game_object_id_t game_object_id;
	//! Reference to parent component
	OptionalRef<bool> active;
	//! Mediator reference
	OptionalRef<Mediator> mediator;
	//! \}

private:
	//! Flag to indicate if \c init() has been called already
	bool initialized = false;
	//! List of subscribed events
	std::vector<subscription_t> listeners;
};

/**
 * \brief Subscribe to CollisionEvent for the current GameObject
 *
 * This is a template specialization for Script::subscribe which automatically sets the event
 * channel so the callback handler is only called for CollisionEvent events that apply to the
 * current GameObject the parent BehaviorScript is attached to.
 */
template <>
void Script::subscribe(const EventHandler<CollisionEvent> & callback);
template <>
void Script::subscribe(const EventHandler<CollisionEvent> & callback, event_channel_t)
	= delete;
/**
 * \brief Subscribe to ButtonPressEvent for the current GameObject
 *
 * This is a template specialization for Script::subscribe which automatically sets the event
 * channel so the callback handler is only called for ButtonPressEvent events that apply to the
 * current GameObject the parent BehaviorScript is attached to.
 */
template <>
void Script::subscribe(const EventHandler<ButtonPressEvent> & callback);
template <>
void Script::subscribe(const EventHandler<ButtonPressEvent> & callback, event_channel_t)
	= delete;
/**
 * \brief Subscribe to ButtonExitEvent for the current GameObject
 *
 * This is a template specialization for Script::subscribe which automatically sets the event
 * channel so the callback handler is only called for ButtonExitEvent events that apply to the
 * current GameObject the parent BehaviorScript is attached to.
 */
template <>
void Script::subscribe(const EventHandler<ButtonExitEvent> & callback);
template <>
void Script::subscribe(const EventHandler<ButtonExitEvent> & callback, event_channel_t)
	= delete;
/**
 * \brief Subscribe to ButtonEnterEvent for the current GameObject
 *
 * This is a template specialization for Script::subscribe which automatically sets the event
 * channel so the callback handler is only called for ButtonEnterEvent events that apply to the
 * current GameObject the parent BehaviorScript is attached to.
 */
template <>
void Script::subscribe(const EventHandler<ButtonEnterEvent> & callback);
template <>
void Script::subscribe(const EventHandler<ButtonEnterEvent> & callback, event_channel_t)
	= delete;
} // namespace crepe

#include "Script.hpp"