1 /* -*- C++ -*-
2 *
3 * Ponscripter_animation.cpp - Methods to manipulate AnimationInfo
4 *
5 * Copyright (c) 2001-2008 Ogapee (original ONScripter, of which this
6 * is a fork).
7 *
8 * ogapee@aqua.dti2.ne.jp
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License as
12 * published by the Free Software Foundation; either version 2 of the
13 * License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
23 * 02111-1307 USA
24 */
25
26 #include "PonscripterLabel.h"
27
proceedAnimation()28 int PonscripterLabel::proceedAnimation()
29 {
30 int i, minimum_duration = -1;
31 AnimationInfo* anim;
32
33 for (i = 0; i < 3; i++) {
34 anim = &tachi_info[i];
35 if (anim->showing() && anim->is_animatable) {
36 minimum_duration = estimateNextDuration(anim, anim->pos,
37 minimum_duration);
38 }
39 }
40
41 for (i = MAX_SPRITE_NUM - 1; i >= 0; i--) {
42 anim = &sprite_info[i];
43 if (anim->showing() && anim->is_animatable) {
44 minimum_duration = estimateNextDuration(anim, anim->pos,
45 minimum_duration);
46 }
47 }
48
49 for (i = MAX_SPRITE2_NUM - 1; i >= 0; i--) {
50 anim = &sprite2_info[i];
51 if (anim->showing() && anim->is_animatable) {
52 minimum_duration = estimateNextDuration(anim, anim->pos,
53 minimum_duration);
54 }
55 }
56
57 if (!textgosub_label
58 && (clickstr_state == CLICK_WAIT
59 || clickstr_state == CLICK_NEWPAGE)) {
60 if (clickstr_state == CLICK_WAIT)
61 anim = &cursor_info[CURSOR_WAIT_NO];
62 else if (clickstr_state == CLICK_NEWPAGE)
63 anim = &cursor_info[CURSOR_NEWPAGE_NO];
64
65 if (anim->showing() && anim->is_animatable) {
66 SDL_Rect dst_rect = anim->pos;
67 if (!anim->abs_flag) {
68 dst_rect.x += int(floor(sentence_font.GetX() * screen_ratio1 / screen_ratio2));
69 dst_rect.y += sentence_font.GetY() * screen_ratio1 / screen_ratio2;
70 }
71
72 minimum_duration = estimateNextDuration(anim, dst_rect,
73 minimum_duration);
74 }
75 }
76
77 if (minimum_duration == -1) minimum_duration = 0;
78
79 return minimum_duration;
80 }
81
82
estimateNextDuration(AnimationInfo * anim,SDL_Rect & rect,int minimum)83 int PonscripterLabel::estimateNextDuration(AnimationInfo* anim, SDL_Rect &rect, int minimum)
84 {
85 if (anim->remaining_time == 0) {
86 if (minimum == -1
87 || minimum > anim->duration_list[anim->current_cell])
88 minimum = anim->duration_list[anim->current_cell];
89
90 if (anim->proceedAnimation())
91 flushDirect(rect, refreshMode() | (draw_cursor_flag ? REFRESH_CURSOR_MODE : 0));
92 }
93 else {
94 if (minimum == -1
95 || minimum > anim->remaining_time)
96 minimum = anim->remaining_time;
97 }
98
99 return minimum;
100 }
101
102
resetRemainingTime(int t)103 void PonscripterLabel::resetRemainingTime(int t)
104 {
105 int i;
106 AnimationInfo* anim;
107
108 for (i = 0; i < 3; i++) {
109 anim = &tachi_info[i];
110 if (anim->showing() && anim->is_animatable) {
111 anim->remaining_time -= t;
112 }
113 }
114
115 for (i = MAX_SPRITE_NUM - 1; i >= 0; i--) {
116 anim = &sprite_info[i];
117 if (anim->showing() && anim->is_animatable) {
118 anim->remaining_time -= t;
119 }
120 }
121
122 for (i = MAX_SPRITE2_NUM - 1; i >= 0; i--) {
123 anim = &sprite2_info[i];
124 if (anim->showing() && anim->is_animatable) {
125 anim->remaining_time -= t;
126 }
127 }
128
129 if (!textgosub_label
130 && (clickstr_state == CLICK_WAIT
131 || clickstr_state == CLICK_NEWPAGE)) {
132 if (clickstr_state == CLICK_WAIT)
133 anim = &cursor_info[CURSOR_WAIT_NO];
134 else if (clickstr_state == CLICK_NEWPAGE)
135 anim = &cursor_info[CURSOR_NEWPAGE_NO];
136
137 if (anim->showing() && anim->is_animatable) {
138 anim->remaining_time -= t;
139 }
140 }
141 }
142
143
downscale4x(SDL_Surface * src,SDL_Rect * srcpos,SDL_Surface * dst,SDL_Rect * dstpos)144 void downscale4x(SDL_Surface* src, SDL_Rect* srcpos, SDL_Surface* dst, SDL_Rect* dstpos)
145 {
146 SDL_LockSurface(src);
147 SDL_LockSurface(dst);
148
149 const int sp = src->pitch;
150 const int dp = dst->pitch;
151
152 SDL_Rect sr, dr;
153 if (srcpos) sr = *srcpos;else { sr.x = 0; sr.y = 0; sr.w = src->w; sr.h = src->h; }
154
155 if (dstpos) dr = *dstpos;else { dr.x = 0; dr.y = 0; }
156
157 dr.w = sr.w / 4; dr.h = sr.h / 4;
158
159 Uint8* src_scan = (Uint8*) src->pixels + sr.y * sp + sr.x * 4;
160 Uint8* dst_scan = (Uint8*) dst->pixels + dr.y * dp + dr.x * 4;
161
162 for (int rows = dr.h; rows-- > 0; src_scan += sp * 4, dst_scan += dp) {
163 Uint32* src1 = (Uint32*) src_scan;
164 Uint32* src2 = (Uint32*) (src_scan + sp);
165 Uint32* src3 = (Uint32*) (src_scan + sp * 2);
166 Uint32* src4 = (Uint32*) (src_scan + sp * 3);
167 Uint32* dest = (Uint32*) dst_scan;
168 int cols = dr.w;
169 while (cols-- > 0) {
170 Uint32 a, b, c, d;
171 Uint32 p;
172 p = *src1++;
173 a = p >> 24;
174 b = (p >> 16) & 0xff;
175 c = (p >> 8) & 0xff;
176 d = p & 0xff;
177 #define AddPx(s) p = *s; a += p >> 24; b += (p >> 16) & 0xff; \
178 c += (p >> 8) & 0xff; d += p & 0xff
179 AddPx(src1++); AddPx(src1++); AddPx(src1++);
180 AddPx(src2++); AddPx(src2++); AddPx(src2++); AddPx(src2++);
181 AddPx(src3++); AddPx(src3++); AddPx(src3++); AddPx(src3++);
182 AddPx(src4++); AddPx(src4++); AddPx(src4++); AddPx(src4++);
183 #undef AddPx
184 a >>= 4; b >>= 4; c >>= 4; d >>= 4;
185 *dest++ = a << 24 | b << 16 | c << 8 | d;
186 }
187 }
188
189 SDL_UnlockSurface(src);
190 SDL_UnlockSurface(dst);
191 }
192
193
setupAnimationInfo(AnimationInfo * anim,Fontinfo * info)194 void PonscripterLabel::setupAnimationInfo(AnimationInfo* anim, Fontinfo* info)
195 {
196 anim->deleteImage();
197 anim->abs_flag = true;
198
199 if (anim->trans_mode == AnimationInfo::TRANS_STRING) {
200 Fontinfo f_info = info ? *info : sentence_font;
201
202 // parse ponscripter tags if a standard text string,
203 //otherwise don't; logsp strings are "predigested", as it were,
204 //so they shouldn't be parsed for tags again
205 // ...really should do some more explicit indication
206 //if something is a log string than using "skip_whitespace";
207 //moving parseTags out of this function could help too FIXME
208 if (anim->skip_whitespace)
209 anim->file_name = parseTags(anim->file_name);
210
211 if (anim->font_size_x >= 0) { // in case of Sprite, not rclick menu
212 f_info.top_x = anim->pos.x * screen_ratio2 / screen_ratio1;
213 f_info.top_y = anim->pos.y * screen_ratio2 / screen_ratio1;
214 f_info.setTateYoko(0);
215 f_info.style = Default;
216
217 f_info.set_size(anim->font_size_y);
218 f_info.set_mod_size(0);
219 if (anim->font_pitch >= 0)
220 f_info.pitch_x = anim->font_pitch;
221
222 if (anim->is_single_line) {
223 f_info.area_x = int(ceil(f_info.StringAdvance(anim->file_name)));
224 f_info.area_y = f_info.line_space();
225 }
226
227 if (anim->is_centered_text) {
228 anim->pos.x -= f_info.area_x / 2;
229 f_info.top_x = anim->pos.x * screen_ratio2 / screen_ratio1;
230 }
231 }
232
233 SDL_Rect pos;
234 if (anim->is_tight_region) {
235 drawString(anim->file_name, anim->color_list[anim->current_cell],
236 &f_info, false, NULL, &pos, NULL, anim->skip_whitespace);
237 }
238 else {
239 pos = f_info.getFullArea(screen_ratio1, screen_ratio2);
240 }
241
242 if (info) info->SetXY(f_info.GetXOffset(), f_info.GetYOffset());
243
244 anim->allocImage(pos.w * anim->num_of_cells, pos.h);
245 anim->fill(0, 0, 0, 0);
246
247 f_info.top_x = f_info.top_y = 0;
248 for (int i = 0; i < anim->num_of_cells; i++) {
249 f_info.clear();
250 f_info.style = Default;
251 drawString(anim->file_name, anim->color_list[i], &f_info, false,
252 NULL, NULL, anim, anim->skip_whitespace);
253 f_info.top_x += anim->pos.w * screen_ratio2 / screen_ratio1;
254 }
255 }
256 else {
257 bool has_alpha;
258 SDL_Surface *surface = loadImage( anim->file_name, &has_alpha, anim->twox, anim->isflipped );
259
260 SDL_Surface *surface_m = NULL;
261 if (anim->trans_mode == AnimationInfo::TRANS_MASK)
262 surface_m = loadImage( anim->mask_file_name, NULL, anim->twox, anim->isflipped);
263
264 anim->setupImage(surface, surface_m, has_alpha);
265 if (surface) SDL_FreeSurface(surface);
266 if (surface_m) SDL_FreeSurface(surface_m);
267 }
268 }
269
270
parseTaggedString(AnimationInfo * anim,bool is_mask)271 void PonscripterLabel::parseTaggedString(AnimationInfo* anim, bool is_mask)
272 {
273 if (!anim->image_name) return;
274
275 anim->removeTag();
276
277 int multiplier = multiplier_style <= ScriptHandler::UMINEKO ? 1 : res_multiplier;
278
279 int i;
280 const char* buffer = anim->image_name;
281 anim->num_of_cells = 1;
282 anim->current_cell = 0;
283 anim->trans_mode = trans_mode;
284 //use COPY as default trans_mode for masks
285 if (is_mask) anim->trans_mode = AnimationInfo::TRANS_COPY;
286
287 anim->twox = false;
288 anim->isflipped = false;
289 if (buffer[0] == ':') {
290 while (*++buffer == ' ') ;
291 if (buffer[0] == 'b') {
292 anim->twox = true;
293 buffer++;
294 }
295 if (buffer[0] == 'f') {
296 anim->isflipped = true;
297 buffer++;
298 }
299
300 if (buffer[0] == 'a') {
301 anim->trans_mode = AnimationInfo::TRANS_ALPHA;
302 buffer++;
303 }
304 else if (buffer[0] == 'l') {
305 anim->trans_mode = AnimationInfo::TRANS_TOPLEFT;
306 buffer++;
307 }
308 else if (buffer[0] == 'r') {
309 anim->trans_mode = AnimationInfo::TRANS_TOPRIGHT;
310 buffer++;
311 }
312 else if (buffer[0] == 'c') {
313 anim->trans_mode = AnimationInfo::TRANS_COPY;
314 buffer++;
315 }
316 else if (buffer[0] == 's' || buffer[0] == 'S') {
317 anim->trans_mode = AnimationInfo::TRANS_STRING;
318 anim->is_centered_text = buffer[0] == 'S';
319 buffer++;
320 anim->num_of_cells = 0;
321 if (*buffer == '/') {
322 buffer++;
323 script_h.getNext();
324
325 script_h.pushCurrent((char*) buffer); // FIXME: unsafe
326 anim->font_size_x = script_h.readIntValue() * multiplier;
327 anim->font_size_y = script_h.hasMoreArgs()
328 ? script_h.readIntValue() * multiplier
329 : anim->font_size_x;
330 anim->font_pitch = script_h.hasMoreArgs()
331 ? script_h.readIntValue()
332 : 0;
333 if (script_h.hasMoreArgs())
334 script_h.readIntValue(); // antialiasing mode (ignored)
335
336 buffer = script_h.getNext();
337 script_h.popCurrent();
338 }
339 else {
340 anim->font_size_x = sentence_font.size();
341 anim->font_size_y = sentence_font.size();
342 anim->font_pitch = sentence_font.pitch_x;
343 }
344
345 while (buffer[0] != '#' && buffer[0] != '\0') buffer++;
346 i = 0;
347 if (buffer[i] == '#' && buffer[i + 1] == '#') {
348 buffer += 2;
349 anim->num_of_cells = 2;
350 anim->color_list.clear();
351 anim->color_list.push_back(readColour("#FFFFFF"));
352 anim->color_list.push_back(readColour("#000000"));
353 }
354 else {
355 while (buffer[i] == '#') {
356 anim->num_of_cells++;
357 i += 7;
358 }
359 anim->color_list.clear();
360 for (i = 0; i < anim->num_of_cells; i++) {
361 anim->color_list.push_back(readColour(buffer));
362 buffer += 7;
363 }
364 }
365 }
366 else if (buffer[0] == 'm') {
367 anim->trans_mode = AnimationInfo::TRANS_MASK;
368 const char* start = ++buffer;
369 while (buffer[0] != ';' && buffer[0] != 0x0a && buffer[0])
370 buffer++;
371 if (buffer[0] == ';')
372 anim->mask_file_name = pstring(start, buffer - start);
373 }
374 else if (buffer[0] == '#') {
375 anim->trans_mode = AnimationInfo::TRANS_DIRECT;
376 anim->direct_color = readColour(buffer);
377 buffer += 7;
378 }
379 else if (buffer[0] == '!') {
380 anim->trans_mode = AnimationInfo::TRANS_PALETTE;
381 buffer++;
382 anim->palette_number = getNumberFromBuffer((const char**) &buffer);
383 }
384
385 if (anim->trans_mode != AnimationInfo::TRANS_STRING)
386 while (buffer[0] != '/' && buffer[0] != ';' && buffer[0])
387 buffer++;
388 }
389
390 if (buffer[0] == '/') {
391 buffer++;
392 anim->num_of_cells = getNumberFromBuffer((const char**) &buffer);
393 buffer++;
394 if (anim->num_of_cells == 0) {
395 fprintf(stderr, "PonscripterLabel::parseTaggedString The number of cells is 0\n");
396 return;
397 }
398
399 if (*buffer == '<') {
400 buffer++;
401 anim->duration_list.resize(anim->num_of_cells);
402 for (i = 0; i < anim->num_of_cells; i++) {
403 anim->duration_list[i] = getNumberFromBuffer((const char**) &buffer);
404 buffer++;
405 }
406
407 buffer++; // skip '>'
408 }
409 else {
410 anim->duration_list.assign(anim->num_of_cells,
411 getNumberFromBuffer((const char**) &buffer));
412 buffer++;
413 }
414
415 anim->loop_mode = *buffer++ - '0'; // 3...no animation
416 if (anim->loop_mode != 3) anim->is_animatable = true;
417
418 while (buffer[0] != ';' && buffer[0] != '\0') buffer++;
419 }
420
421 if (buffer[0] == ';') buffer++;
422
423 if (anim->trans_mode == AnimationInfo::TRANS_STRING && buffer[0] == '$') {
424 script_h.pushCurrent((char*) buffer); // FIXME: unsafe
425 anim->file_name = script_h.readStrValue();
426 script_h.popCurrent();
427 }
428 else {
429 anim->file_name = buffer;
430 }
431 }
432
433
drawTaggedSurface(SDL_Surface * dst_surface,AnimationInfo * anim,SDL_Rect & clip)434 void PonscripterLabel::drawTaggedSurface(SDL_Surface* dst_surface, AnimationInfo* anim, SDL_Rect &clip)
435 {
436 SDL_Rect poly_rect = anim->pos;
437 if (!anim->abs_flag) {
438 poly_rect.x += int (floor(sentence_font.GetX() * screen_ratio1 / screen_ratio2));
439 poly_rect.y += sentence_font.GetY() * screen_ratio1 / screen_ratio2;
440 }
441
442 if (anim->affine_flag)
443 anim->blendOnSurface2(dst_surface, poly_rect.x, poly_rect.y,
444 clip, anim->trans);
445 else
446 anim->blendOnSurface(dst_surface, poly_rect.x, poly_rect.y,
447 clip, anim->trans);
448 }
449
450
stopAnimation(int click)451 void PonscripterLabel::stopAnimation(int click)
452 {
453 int no;
454
455 if (!(event_mode & WAIT_TIMER_MODE)) return;
456
457 event_mode &= ~WAIT_TIMER_MODE;
458 remaining_time = -1;
459 if (textgosub_label) return;
460
461 if (click == CLICK_WAIT) no = CURSOR_WAIT_NO;
462 else if (click == CLICK_NEWPAGE) no = CURSOR_NEWPAGE_NO;
463 else return;
464
465 if (cursor_info[no].image_surface == NULL) return;
466
467 SDL_Rect dst_rect = cursor_info[no].pos;
468
469 if (!cursor_info[no].abs_flag) {
470 dst_rect.x += int (floor(sentence_font.GetX() * screen_ratio1 / screen_ratio2));
471 dst_rect.y += sentence_font.GetY() * screen_ratio1 / screen_ratio2;
472 }
473
474 flushDirect(dst_rect, refreshMode());
475 }
476