summaryrefslogtreecommitdiff
path: root/src/bonus-node.c
blob: 7eb2c9e2ed37959d575d0045bd29f1561f5c4bee (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
/*
 *  Copyright (C) 2022 - Vito Caputo - <vcaputo@pengaru.com>
 *
 *  This program is free software: you can redistribute it and/or modify it
 *  under the terms of the GNU General Public License version 3 as published
 *  by the Free Software Foundation.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/* Bonus nodes are for showing bonus quantities numerically which move along
 * with an entity, and when taken get released from the entity's position and
 * float away on their own.
 *
 * The current way this works is by providing a release counter pointer for
 * triggering the release, along with a release position pointer for storing
 * the last position when released.  So the entity needs some members for saving
 * these locations which point at members within the bonus_node_t.
 *
 * The release gets realised at render time by checking if the release counter
 * is non-zero.  Upon release, the render func takes over management of the
 * position and life-cycle of the node by returning STAGE_RENDER_FUNC_RET_FREE
 * when done.
 *
 * One particularly crufty aspect is the digit nodes are hung off a nested
 * stage within the bonus_node_t that's being explicitly rendered and freed,
 * rather than existing as direct descendants of the game stage.  This results
 * in some redundant stage_dirty()/stage_render()/stage_free() rigamarole here,
 * and is only being done because I want to vary the alpha in the render func
 * for fading the digits out as they decay - and the incoming stage_t on the
 * render func is a const so we can't set the alpha directly on the outer bonus
 * node before letting render naturally descend down to the children layers with
 * the adjusted alpha.
 *
 * It's no big deal for now, but it illustrates some of the unnecessary
 * friction remaining in the libstage API.  I'm not convinced constifying these
 * things is bringing enough value to justify these kinds of annoyances.  FIXME
 *
 * Another crufty point is for now I threw the release decay duration in
 * bonus-node.h so that the caller can accesss it when storing at the release
 * pointer, while making it available here for compile-time evaluation of (1.f
 * / BONUS_NODE_RELEASE_MS).  Also the counter isn't really applied in
 * normalized time, but is just a frame counter right now - the name lies.
 * TODO FIXME
 *
 * I'm not too concerned about these things since SARS is such a
 * silly thing, but it's a useful exercise to see how (un)usable these
 * libraries are.
 */

#include <assert.h>
#include <stddef.h>
#include <stdlib.h>
#include <stage.h>

#include "bonus-node.h"
#include "digit-node.h"
#include "m4f.h"
#include "m4f-3dx.h"
#include "v2f.h"

typedef struct bonus_node_t {
	stage_t		*stage;
	v2f_t		*position;
	float		scale;
	unsigned	release;
	v2f_t		release_position;
	unsigned	n_digits;
	m4f_t		digits_x[];
} bonus_node_t;

static stage_render_func_ret_t bonus_node_render(const stage_t *stage, void *object, float alpha, void *render_ctxt)
{
	bonus_node_t	*bonus_node = object;
	v3f_t		scale;

	assert(stage);
	assert(bonus_node);

	scale.x = scale.y = scale.z = bonus_node->scale;

	if (bonus_node->release) {
		float	t = 1.f - (1.f / BONUS_NODE_RELEASE_MS * (float)bonus_node->release);

		bonus_node->release--;
		if (!bonus_node->release)
			return STAGE_RENDER_FUNC_RET_FREE;

		for (int i = 0; i < bonus_node->n_digits; i++) {
			bonus_node->digits_x[i] = m4f_translate(NULL, &(v3f_t){
								.x = bonus_node->release_position.x + bonus_node->scale*2.f + (float)i * -bonus_node->scale*2.f + (t * sinf((float)bonus_node->release * .1f) * .1f),
								.y = bonus_node->release_position.y + bonus_node->scale*2.f + t * .5f,
								.z = 0.f
							});
			bonus_node->digits_x[i] = m4f_scale(&bonus_node->digits_x[i], &scale);
		}

		stage_set_alpha(bonus_node->stage, 1.f - t);
		stage_dirty(bonus_node->stage);
		stage_render(bonus_node->stage, render_ctxt);

		return STAGE_RENDER_FUNC_RET_CONTINUE;
	}

	/* update tranforms for the digits before they render */
	for (int i = 0; i < bonus_node->n_digits; i++) {
		bonus_node->digits_x[i] = m4f_translate(NULL, &(v3f_t){
								.x = bonus_node->position->x + bonus_node->scale * 2.f + (float)i * -bonus_node->scale * 2.f,
								.y = bonus_node->position->y + bonus_node->scale*2.f,
								.z = 0.f
							});
		bonus_node->digits_x[i] = m4f_scale(&bonus_node->digits_x[i], &scale);
	}
	stage_dirty(bonus_node->stage);
	stage_render(bonus_node->stage, render_ctxt);

	return STAGE_RENDER_FUNC_RET_CONTINUE;
}


static void bonus_node_free(const stage_t *stage, void *object)
{
	bonus_node_t	*bonus_node = object;

	assert(stage);
	stage_free(bonus_node->stage);
	free(bonus_node);
}


static const stage_ops_t bonus_node_ops = {
	.render_func = bonus_node_render,
	.free_func = bonus_node_free,
};


stage_t * bonus_node_new(stage_conf_t *conf, unsigned value, m4f_t *projection_x, v2f_t *position, float scale, unsigned **release, v2f_t **release_position)
{
	unsigned	v = value, n_digits = 0, i = 0;
	bonus_node_t	*bonus_node;
	stage_t		*s;

	assert(release);
	assert(position);
	assert(release_position);
	assert(projection_x);

	do {
		n_digits++;
		v /= 10;
	} while (v);

	bonus_node = calloc(1, sizeof(bonus_node_t) + n_digits * sizeof(bonus_node->digits_x[0]));
	assert(bonus_node);
	bonus_node->n_digits = n_digits;
	bonus_node->position = position;
	bonus_node->scale = scale;
	bonus_node->stage = stage_new(&(stage_conf_t){.name = "bonus-container", .active = 1, .alpha = 1.f}, NULL, NULL); /* use a discrete container stage for render_func alpha control */
	*release = &bonus_node->release;
	*release_position = &bonus_node->release_position;

	s = stage_new(conf, &bonus_node_ops, bonus_node);
	assert(s);

	v = value;
	do {
		/* TODO: model_x positioning of the digits */
		(void) digit_node_new(&(stage_conf_t){ .parent = bonus_node->stage, .name = "bonus-digit", .active = 1, .alpha = 1.f },
			v % 10, projection_x, &bonus_node->digits_x[i++]);
		v /= 10;
	} while (v);

	return s;
}
© All Rights Reserved