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