1 /* -*- C++ -*-
2 *
3 * ONScripter_animation.cpp - Methods to manipulate AnimationInfo
4 *
5 * Copyright (c) 2001-2020 Ogapee. All rights reserved.
6 *
7 * ogapee@aqua.dti2.ne.jp
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24 #include "ONScripter.h"
25
26 #define DEFAULT_CURSOR_WAIT ":l/3,160,2;cursor0.bmp"
27 #define DEFAULT_CURSOR_NEWPAGE ":l/3,160,2;cursor1.bmp"
28
calcDurationToNextAnimation()29 int ONScripter::calcDurationToNextAnimation()
30 {
31 int min = 0; // minimum next time
32
33 for (int i=0 ; i<3 ; i++){
34 AnimationInfo *anim = &tachi_info[i];
35 if (anim->visible && anim->is_animatable){
36 if (min == 0 || min > anim->next_time)
37 min = anim->next_time;
38 }
39 }
40
41 for (int i=MAX_SPRITE_NUM-1 ; i>=0 ; i--){
42 AnimationInfo *anim = &sprite_info[i];
43 if (anim->visible && anim->is_animatable){
44 if (min == 0 || min > anim->next_time)
45 min = anim->next_time;
46 }
47 }
48
49 if (!textgosub_label &&
50 (clickstr_state == CLICK_WAIT || clickstr_state == CLICK_NEWPAGE)){
51 AnimationInfo *anim = &cursor_info[0]; // CLICK_WAIT
52 if (clickstr_state == CLICK_NEWPAGE)
53 anim = &cursor_info[1];
54
55 if (anim->visible && anim->is_animatable){
56 if (min == 0 || min > anim->next_time)
57 min = anim->next_time;
58 }
59 }
60
61 #ifdef USE_LUA
62 if (lua_handler.is_animatable && !script_h.isExternalScript()){
63 if (min == 0 || min > lua_handler.next_time)
64 min = lua_handler.next_time;
65 }
66 #endif
67
68 return min;
69 }
70
proceedAnimation(int current_time)71 void ONScripter::proceedAnimation(int current_time)
72 {
73 for (int i=0 ; i<3 ; i++)
74 if (tachi_info[i].proceedAnimation(current_time))
75 flushDirect(tachi_info[i].pos, refreshMode());
76
77 for (int i=MAX_SPRITE_NUM-1 ; i>=0 ; i--)
78 if (sprite_info[i].proceedAnimation(current_time))
79 flushDirect(sprite_info[i].pos, refreshMode());
80
81 #ifdef USE_LUA
82 if (lua_handler.is_animatable && !script_h.isExternalScript()){
83 int tmp_event_mode = event_mode;
84 int tmp_next_time = next_time;
85 int tmp_string_buffer_offset = string_buffer_offset;
86
87 char *current = script_h.getCurrent();
88 while (lua_handler.next_time <= current_time){
89 if (lua_handler.isCallbackEnabled(LUAHandler::LUA_ANIMATION))
90 if (lua_handler.callFunction(true, "animation"))
91 errorAndExit( lua_handler.error_str );
92
93 if (lua_handler.duration_time <= 0){
94 lua_handler.next_time = current_time;
95 break;
96 }
97
98 // exit the loop not to decrease the performance
99 do{
100 lua_handler.next_time += lua_handler.duration_time;
101 }
102 while (lua_handler.next_time <= current_time);
103 }
104 script_h.setCurrent(current);
105 readToken();
106
107 string_buffer_offset = tmp_string_buffer_offset;
108 next_time = tmp_next_time;
109 event_mode = tmp_event_mode;
110 }
111 #endif
112
113 if (!textgosub_label &&
114 (clickstr_state == CLICK_WAIT || clickstr_state == CLICK_NEWPAGE)){
115 AnimationInfo *anim = &cursor_info[0]; // CLICK_WAIT
116 if (clickstr_state == CLICK_NEWPAGE)
117 anim = &cursor_info[1];
118
119 if (anim->proceedAnimation(current_time)){
120 SDL_Rect dst_rect = anim->pos;
121 if (!anim->abs_flag){
122 dst_rect.x += sentence_font.x() * screen_ratio1 / screen_ratio2;
123 dst_rect.y += sentence_font.y() * screen_ratio1 / screen_ratio2;
124 }
125 flushDirect( dst_rect, refreshMode());
126 }
127 }
128 }
129
setupAnimationInfo(AnimationInfo * anim,FontInfo * info)130 void ONScripter::setupAnimationInfo( AnimationInfo *anim, FontInfo *info )
131 {
132 if (anim->trans_mode != AnimationInfo::TRANS_STRING &&
133 anim->file_name && anim->surface_name &&
134 strcmp(anim->file_name, anim->surface_name) == 0 &&
135 ((!anim->mask_file_name && !anim->mask_surface_name) ||
136 (anim->mask_file_name && anim->mask_surface_name &&
137 strcmp(anim->mask_file_name, anim->mask_surface_name) == 0))) return;
138
139 anim->deleteSurface();
140 anim->abs_flag = true;
141
142 anim->surface_name = new char[ strlen(anim->file_name) + 1 ];
143 strcpy( anim->surface_name, anim->file_name );
144
145 if (anim->mask_file_name){
146 anim->mask_surface_name = new char[ strlen(anim->mask_file_name) + 1 ];
147 strcpy( anim->mask_surface_name, anim->mask_file_name );
148 }
149
150 if ( anim->trans_mode == AnimationInfo::TRANS_STRING ){
151 FontInfo f_info = sentence_font;
152 if (info) f_info = *info;
153 f_info.enc = &script_h.enc;
154 f_info.rubyon_flag = anim->is_ruby_drawable;
155
156 if ( anim->font_size_xy[0] >= 0 ){ // in case of Sprite, not rclick menu
157 f_info.setTateyokoMode(0);
158 f_info.top_xy[0] = anim->orig_pos.x;
159 f_info.top_xy[1] = anim->orig_pos.y;
160
161 if (anim->font_pitch[0] >= 0){
162 f_info.font_size_xy[0] = anim->font_size_xy[0];
163 f_info.pitch_xy[0] = anim->font_pitch[0];
164 }
165 if (anim->font_pitch[1] >= 0){
166 f_info.font_size_xy[1] = anim->font_size_xy[1];
167 f_info.pitch_xy[1] = anim->font_pitch[1];
168 }
169
170 f_info.ttf_font[0] = NULL;
171 f_info.ttf_font[1] = NULL;
172
173 if (anim->is_single_line){
174 openFont(&f_info);
175 f_info.setLineArea(anim->file_name);
176 }
177 f_info.clear();
178 }
179
180 if (f_info.ttf_font[0] == NULL)
181 f_info.openFont( font_file, screen_ratio1, screen_ratio2 );
182
183 SDL_Rect pos;
184 if (anim->is_tight_region){
185 drawString( anim->file_name, anim->color_list[ anim->current_cell ], &f_info, false, NULL, &pos );
186 }
187 else{
188 int xy_bak[2];
189 xy_bak[0] = f_info.xy[0];
190 xy_bak[1] = f_info.xy[1];
191
192 int xy[2] = {0, 0};
193 f_info.setXY(f_info.num_xy[0]-1, f_info.num_xy[1]-1);
194 pos = f_info.calcUpdatedArea(xy, screen_ratio1, screen_ratio2);
195
196 f_info.xy[0] = xy_bak[0];
197 f_info.xy[1] = xy_bak[1];
198 }
199
200 if (info != NULL){
201 info->xy[0] = f_info.xy[0];
202 info->xy[1] = f_info.xy[1];
203 }
204
205 anim->orig_pos.w = pos.w;
206 anim->orig_pos.h = pos.h;
207 anim->scalePosWH( screen_ratio1, screen_ratio2 );
208 anim->allocImage( anim->pos.w*anim->num_of_cells, anim->pos.h, texture_format );
209 anim->fill( 0, 0, 0, 0 );
210
211 f_info.top_xy[0] = f_info.top_xy[1] = 0;
212 for ( int i=0 ; i<anim->num_of_cells ; i++ ){
213 f_info.clear();
214 drawString( anim->file_name, anim->color_list[i], &f_info, false, NULL, NULL, anim );
215 f_info.top_xy[0] += anim->orig_pos.w;
216 }
217 }
218 else{
219 bool has_alpha;
220 int location;
221 SDL_Surface *surface = loadImage(anim->file_name, anim->is_flipped, &has_alpha, &location, &anim->default_alpha);
222
223 if (script_h.enc.getEncoding() == Encoding::CODE_UTF8 && has_alpha)
224 anim->trans_mode = AnimationInfo::TRANS_ALPHA;
225
226 SDL_Surface *surface_m = NULL;
227 if (anim->trans_mode == AnimationInfo::TRANS_MASK)
228 surface_m = loadImage(anim->mask_file_name, anim->is_flipped);
229
230 surface = anim->setupImageAlpha(surface, surface_m, has_alpha);
231
232 if (surface &&
233 ((screen_ratio2 != screen_ratio1) ||
234 (script_h.screen_width == 640 && anim->is_2x)) &&
235 (!disable_rescale_flag || location == BaseReader::ARCHIVE_TYPE_NONE))
236 {
237 SDL_Surface *src_s = surface;
238
239 int w = src_s->w * screen_ratio1 / screen_ratio2;
240 int h = src_s->h * screen_ratio1 / screen_ratio2;
241 if (script_h.screen_width == 640 && anim->is_2x){
242 // workaround to deal with hard-coded 2x mode
243 w /= 2;
244 h /= 2;
245 anim->orig_pos.w /= 2;
246 anim->orig_pos.h /= 2;
247 if (anim->orig_pos.w == 0) anim->orig_pos.w = 1;
248 if (anim->orig_pos.h == 0) anim->orig_pos.h = 1;
249 }
250 if (w == 0) w = 1;
251 if (h == 0) h = 1;
252 SDL_PixelFormat *fmt = image_surface->format;
253 surface = SDL_CreateRGBSurface( SDL_SWSURFACE, w, h,
254 fmt->BitsPerPixel, fmt->Rmask, fmt->Gmask, fmt->Bmask, fmt->Amask );
255
256 resizeSurface(src_s, surface);
257 SDL_FreeSurface(src_s);
258 }
259
260 anim->setImage( surface, texture_format );
261
262 if ( surface_m ) SDL_FreeSurface(surface_m);
263 }
264 }
265
parseTaggedString(AnimationInfo * anim)266 void ONScripter::parseTaggedString( AnimationInfo *anim )
267 {
268 if (anim->image_name == NULL) return;
269 anim->removeTag();
270
271 int i;
272 char *buffer = anim->image_name;
273 anim->num_of_cells = 1;
274 anim->trans_mode = trans_mode;
275
276 anim->is_2x = false;
277 anim->is_flipped = false;
278 if ( buffer[0] == ':' ){
279 while (*++buffer == ' ');
280
281 if (buffer[0] == 'b'){
282 anim->is_2x = true;
283 buffer++;
284 }
285
286 if (buffer[0] == 'f'){
287 anim->is_flipped = true;
288 buffer++;
289 }
290
291 if ( buffer[0] == 'a' ){
292 anim->trans_mode = AnimationInfo::TRANS_ALPHA;
293 buffer++;
294 }
295 else if ( buffer[0] == 'l' ){
296 anim->trans_mode = AnimationInfo::TRANS_TOPLEFT;
297 buffer++;
298 }
299 else if ( buffer[0] == 'r' ){
300 anim->trans_mode = AnimationInfo::TRANS_TOPRIGHT;
301 buffer++;
302 }
303 else if ( buffer[0] == 'c' ){
304 anim->trans_mode = AnimationInfo::TRANS_COPY;
305 buffer++;
306 }
307 else if ( buffer[0] == 's' ){
308 anim->trans_mode = AnimationInfo::TRANS_STRING;
309 buffer++;
310 anim->num_of_cells = 0;
311 if ( *buffer == '/' ){
312 buffer++;
313 script_h.getNext();
314
315 script_h.pushCurrent( buffer );
316 anim->font_size_xy[0] = script_h.readInt();
317 anim->font_size_xy[1] = script_h.readInt();
318 anim->font_pitch[0] = anim->font_size_xy[0];
319 anim->font_pitch[1] = anim->font_size_xy[1];
320 if ( script_h.getEndStatus() & ScriptHandler::END_COMMA ){
321 anim->font_pitch[0] += script_h.readInt();
322 if ( script_h.getEndStatus() & ScriptHandler::END_COMMA ){
323 script_h.readInt(); // 0 ... normal, 1 ... no anti-aliasing, 2 ... Fukuro
324 }
325 }
326 buffer = script_h.getNext();
327 script_h.popCurrent();
328 }
329 else{
330 anim->font_size_xy[0] = sentence_font.font_size_xy[0];
331 anim->font_size_xy[1] = sentence_font.font_size_xy[1];
332 anim->font_pitch[0] = sentence_font.pitch_xy[0];
333 anim->font_pitch[1] = sentence_font.pitch_xy[1];
334 }
335
336 while(buffer[0] != '#' && buffer[0] != '\0') buffer++;
337 i=0;
338 while( buffer[i] == '#' ){
339 anim->num_of_cells++;
340 i += 7;
341 }
342 anim->color_list = new uchar3[ anim->num_of_cells ];
343 for ( i=0 ; i<anim->num_of_cells ; i++ ){
344 readColor( &anim->color_list[i], buffer );
345 buffer += 7;
346 }
347 }
348 else if ( buffer[0] == 'm' ){
349 anim->trans_mode = AnimationInfo::TRANS_MASK;
350 char *start = ++buffer;
351 while(buffer[0] != ';' && buffer[0] != 0x0a && buffer[0] != '\0') buffer++;
352 if (buffer[0] == ';')
353 setStr( &anim->mask_file_name, start, buffer-start );
354 }
355 else if ( buffer[0] == '#' ){
356 anim->trans_mode = AnimationInfo::TRANS_DIRECT;
357 readColor( &anim->direct_color, buffer );
358 buffer += 7;
359 }
360 else if ( buffer[0] == '!' ){
361 anim->trans_mode = AnimationInfo::TRANS_PALLETTE;
362 buffer++;
363 anim->pallette_number = getNumberFromBuffer( (const char**)&buffer );
364 }
365
366 if (anim->trans_mode != AnimationInfo::TRANS_STRING)
367 while(buffer[0] != '/' && buffer[0] != ';' && buffer[0] != '\0') buffer++;
368 }
369
370 if ( buffer[0] == '/' && anim->trans_mode != AnimationInfo::TRANS_STRING){
371 buffer++;
372 anim->num_of_cells = getNumberFromBuffer( (const char**)&buffer );
373 if ( anim->num_of_cells == 0 ){
374 fprintf( stderr, "ONScripter::parseTaggedString The number of cells is 0\n");
375 return;
376 }
377
378 anim->duration_list = new int[ anim->num_of_cells ];
379
380 if (*buffer == ','){
381 buffer++;
382
383 if ( *buffer == '<' ){
384 buffer++;
385 for ( i=0 ; i<anim->num_of_cells ; i++ ){
386 anim->duration_list[i] = getNumberFromBuffer( (const char**)&buffer );
387 buffer++;
388 }
389 }
390 else{
391 anim->duration_list[0] = getNumberFromBuffer( (const char**)&buffer );
392 for ( i=1 ; i<anim->num_of_cells ; i++ )
393 anim->duration_list[i] = anim->duration_list[0];
394 }
395 anim->next_time = SDL_GetTicks() + anim->duration_list[0];
396
397 buffer++;
398 anim->loop_mode = *buffer++ - '0'; // 3...no animation
399 }
400 else{
401 for ( i=0 ; i<anim->num_of_cells ; i++ )
402 anim->duration_list[i] = 0;
403 anim->loop_mode = 3; // 3...no animation
404 }
405 if ( anim->loop_mode != 3 ) anim->is_animatable = true;
406
407 while(buffer[0] != ';' && buffer[0] != '\0') buffer++;
408 }
409
410 if ( buffer[0] == ';' && anim->trans_mode != AnimationInfo::TRANS_STRING) buffer++;
411
412 if ( anim->trans_mode == AnimationInfo::TRANS_STRING && buffer[0] == '$' ){
413 script_h.pushCurrent( buffer );
414 setStr( &anim->file_name, script_h.readStr() );
415 script_h.popCurrent();
416 }
417 else{
418 setStr( &anim->file_name, buffer );
419 }
420 }
421
drawTaggedSurface(SDL_Surface * dst_surface,AnimationInfo * anim,SDL_Rect & clip)422 void ONScripter::drawTaggedSurface( SDL_Surface *dst_surface, AnimationInfo *anim, SDL_Rect &clip )
423 {
424 SDL_Rect poly_rect = anim->pos;
425 if ( !anim->abs_flag ){
426 poly_rect.x += sentence_font.x() * screen_ratio1 / screen_ratio2;
427 poly_rect.y += sentence_font.y() * screen_ratio1 / screen_ratio2;
428 }
429
430 if (!anim->affine_flag)
431 anim->blendOnSurface( dst_surface, poly_rect.x, poly_rect.y, clip, layer_alpha_buf, anim->trans );
432 else
433 anim->blendOnSurface2( dst_surface, poly_rect.x, poly_rect.y, clip, layer_alpha_buf, anim->trans );
434 }
435
stopAnimation(int click)436 void ONScripter::stopAnimation( int click )
437 {
438 int no;
439
440 if ( textgosub_label ) return;
441
442 if ( click == CLICK_WAIT ) no = 0;
443 else if ( click == CLICK_NEWPAGE ) no = 1;
444 else return;
445
446 if (cursor_info[no].image_surface == NULL) return;
447
448 SDL_Rect dst_rect = cursor_info[ no ].pos;
449
450 if ( !cursor_info[ no ].abs_flag ){
451 dst_rect.x += sentence_font.x() * screen_ratio1 / screen_ratio2;
452 dst_rect.y += sentence_font.y() * screen_ratio1 / screen_ratio2;
453 }
454
455 flushDirect( dst_rect, refreshMode() );
456 }
457
loadCursor(int no,const char * str,int x,int y,bool abs_flag)458 void ONScripter::loadCursor(int no, const char *str, int x, int y, bool abs_flag)
459 {
460 AnimationInfo *ai = &cursor_info[no];
461
462 if (str){
463 ai->setImageName( str );
464 }
465 else{
466 if (no == 0) ai->setImageName( DEFAULT_CURSOR_WAIT );
467 else ai->setImageName( DEFAULT_CURSOR_NEWPAGE );
468 }
469 ai->orig_pos.x = x;
470 ai->orig_pos.y = y;
471 ai->scalePosXY( screen_ratio1, screen_ratio2 );
472
473 parseTaggedString( ai );
474 setupAnimationInfo( ai );
475
476 if ( filelog_flag )
477 script_h.findAndAddLog( script_h.log_info[ScriptHandler::FILE_LOG], ai->file_name, true ); // a trick for save file
478 ai->abs_flag = abs_flag;
479 if ( ai->image_surface )
480 ai->visible = true;
481 else
482 ai->remove();
483
484 if (str == NULL){
485 if (no == 0) ai->deleteImageName();
486 else ai->deleteImageName();
487 }
488 }
489