]> git.lizzy.rs Git - nothing.git/blob - src/game/level/level_editor/rect_layer.c
(#1043) Make layer part of undo context
[nothing.git] / src / game / level / level_editor / rect_layer.c
1 #include "game/camera.h"
2 #include "system/lt.h"
3 #include "system/stacktrace.h"
4 #include "system/nth_alloc.h"
5 #include "system/log.h"
6 #include "math/rect.h"
7 #include "color.h"
8 #include "rect_layer.h"
9 #include "dynarray.h"
10 #include "system/line_stream.h"
11 #include "color_picker.h"
12 #include "system/str.h"
13 #include "ui/edit_field.h"
14 #include "undo_history.h"
15
16 #define RECT_LAYER_ID_MAX_SIZE 36
17 #define RECT_LAYER_SELECTION_THICCNESS 10.0f
18 #define RECT_LAYER_ID_LABEL_SIZE vec(3.0f, 3.0f)
19 #define CREATE_AREA_THRESHOLD 10.0
20
21
22 typedef enum {
23     RECT_LAYER_IDLE = 0,
24     RECT_LAYER_CREATE,
25     // TODO(#955): Rectangles in Level Editor have only one resize anchor to work with
26     RECT_LAYER_RESIZE,
27     RECT_LAYER_MOVE,
28     RECT_LAYER_ID_RENAME,
29     RECT_LAYER_RECOLOR
30 } RectLayerState;
31
32 struct RectLayer {
33     Lt *lt;
34     RectLayerState state;
35     Dynarray *ids;
36     Dynarray *rects;
37     Dynarray *colors;
38     ColorPicker color_picker;
39     Vec create_begin;
40     Vec create_end;
41     int selection;
42     Vec move_anchor;
43     Edit_field *id_edit_field;
44     // TODO(#1043): RectLayer should use intermediate values instead of previous ones
45     Color inter_color;
46     Rect inter_rect;
47 };
48
49 typedef enum {
50     UNDO_ADD,
51     UNDO_DELETE,
52     UNDO_UPDATE
53 } UndoType;
54
55 typedef struct {
56     UndoType type;
57     RectLayer *layer;
58     Rect rect;
59     Color color;
60     char id[RECT_LAYER_ID_MAX_SIZE];
61     size_t index;
62 } UndoContext;
63
64 static
65 UndoContext create_undo_context(RectLayer *rect_layer, UndoType type)
66 {
67     trace_assert(rect_layer);
68
69     size_t index = type == UNDO_ADD ? dynarray_count(rect_layer->rects) - 1 : (size_t) rect_layer->selection;
70
71     UndoContext undo_context;
72     undo_context.type = type;
73     undo_context.layer = rect_layer;
74     dynarray_copy_to(rect_layer->rects, &undo_context.rect, index);
75     dynarray_copy_to(rect_layer->colors, &undo_context.color, index);
76     dynarray_copy_to(rect_layer->ids, undo_context.id, index);
77     undo_context.index = index;
78
79     return undo_context;
80 }
81
82 static
83 void rect_layer_undo(void *context, size_t context_size)
84 {
85     trace_assert(context);
86     trace_assert(sizeof(UndoContext) == context_size);
87
88     UndoContext *undo_context = context;
89     RectLayer *rect_layer = undo_context->layer;
90
91     switch (undo_context->type) {
92     case UNDO_ADD: {
93         dynarray_delete_at(rect_layer->rects, undo_context->index);
94         dynarray_delete_at(rect_layer->colors, undo_context->index);
95         dynarray_delete_at(rect_layer->ids, undo_context->index);
96         rect_layer->selection = -1;
97     } break;
98
99     case UNDO_DELETE: {
100         dynarray_insert_before(rect_layer->rects, undo_context->index, &undo_context->rect);
101         dynarray_insert_before(rect_layer->colors, undo_context->index, &undo_context->color);
102         dynarray_insert_before(rect_layer->ids, undo_context->index, &undo_context->id);
103         rect_layer->selection = -1;
104     } break;
105
106     case UNDO_UPDATE: {
107         dynarray_replace_at(rect_layer->rects, undo_context->index, &undo_context->rect);
108         dynarray_replace_at(rect_layer->colors, undo_context->index, &undo_context->color);
109         dynarray_replace_at(rect_layer->ids, undo_context->index, &undo_context->id);
110     } break;
111     }
112 }
113
114 #define UNDO_PUSH(LAYER, HISTORY, UNDO_TYPE)                            \
115     do {                                                                \
116         UndoContext context = create_undo_context(LAYER, UNDO_TYPE);    \
117         undo_history_push(                                              \
118             HISTORY,                                                    \
119             rect_layer_undo,                                            \
120             &context,                                                   \
121             sizeof(context));                                           \
122     } while(0)
123
124
125 static int rect_layer_add_rect(RectLayer *layer,
126                                Rect rect,
127                                Color color,
128                                UndoHistory *undo_history)
129 {
130     trace_assert(layer);
131
132     if (dynarray_push(layer->rects, &rect) < 0) {
133         return -1;
134     }
135
136     if (dynarray_push(layer->colors, &color) < 0) {
137         return -1;
138     }
139
140     char id[RECT_LAYER_ID_MAX_SIZE];
141     for (size_t i = 0; i < RECT_LAYER_ID_MAX_SIZE - 1; ++i) {
142         id[i] = (char) ('a' + rand() % ('z' - 'a' + 1));
143     }
144     id[RECT_LAYER_ID_MAX_SIZE - 1] = '\0';
145
146     if (dynarray_push(layer->ids, id)) {
147         return -1;
148     }
149
150     UNDO_PUSH(layer, undo_history, UNDO_ADD);
151
152     return 0;
153 }
154
155 // TODO(#956): rect_layer_rect_at doesn't return rectangles according to some z-order
156 static int rect_layer_rect_at(RectLayer *layer, Vec position)
157 {
158     trace_assert(layer);
159
160     const size_t n = dynarray_count(layer->rects);
161     Rect *rects = dynarray_data(layer->rects);
162
163     for (size_t i = 0; i < n; ++i) {
164         if (rect_contains_point(rects[i], position)) {
165             return (int) i;
166         }
167     }
168
169     return -1;
170 }
171
172 static Rect rect_layer_resize_anchor(const Camera *camera, Rect boundary_rect)
173 {
174     const Rect overlay_rect =
175         rect_scale(
176             camera_rect(camera, boundary_rect),
177             RECT_LAYER_SELECTION_THICCNESS * 0.5f);
178
179     return rect(
180         overlay_rect.x + overlay_rect.w,
181         overlay_rect.y + overlay_rect.h,
182         RECT_LAYER_SELECTION_THICCNESS * 2.0f,
183         RECT_LAYER_SELECTION_THICCNESS * 2.0f);
184 }
185
186 static int rect_layer_delete_rect_at(RectLayer *layer,
187                                      size_t i,
188                                      UndoHistory *undo_history)
189 {
190     trace_assert(layer);
191
192     UNDO_PUSH(layer, undo_history, UNDO_DELETE);
193
194     dynarray_delete_at(layer->rects, i);
195     dynarray_delete_at(layer->colors, i);
196     dynarray_delete_at(layer->ids, i);
197
198     return 0;
199 }
200
201 static int rect_layer_event_idle(RectLayer *layer,
202                                  const SDL_Event *event,
203                                  const Camera *camera,
204                                  UndoHistory *undo_history)
205 {
206     trace_assert(layer);
207     trace_assert(event);
208     trace_assert(camera);
209
210     int color_changed = 0;
211     if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
212         return -1;
213     }
214
215     if (color_changed && layer->selection >= 0) {
216         dynarray_copy_to(layer->colors, &layer->inter_color, (size_t)layer->selection);
217         layer->state = RECT_LAYER_RECOLOR;
218         return 0;
219     }
220
221     switch (event->type) {
222     case SDL_MOUSEBUTTONDOWN: {
223         switch (event->button.button) {
224         case SDL_BUTTON_LEFT: {
225             Point position = camera_map_screen(
226                 camera,
227                 event->button.x,
228                 event->button.y);
229             int rect_at_position =
230                 rect_layer_rect_at(layer, position);
231
232             Rect *rects = dynarray_data(layer->rects);
233             Color *colors = dynarray_data(layer->colors);
234
235             if (rect_at_position >= 0) {
236                 layer->selection = rect_at_position;
237                 layer->state = RECT_LAYER_MOVE;
238                 layer->move_anchor =
239                     vec_sub(
240                         position,
241                         vec(
242                             rects[layer->selection].x,
243                             rects[layer->selection].y));
244                 layer->color_picker =
245                     create_color_picker_from_rgba(colors[rect_at_position]);
246
247                 dynarray_copy_to(layer->rects, &layer->inter_rect, (size_t) rect_at_position);
248             } else if (layer->selection >= 0 && rect_contains_point(
249                            rect_layer_resize_anchor(
250                                camera,
251                                rects[layer->selection]),
252                            vec(
253                                (float) event->button.x,
254                                (float) event->button.y))) {
255                 layer->state = RECT_LAYER_RESIZE;
256                 dynarray_copy_to(layer->rects, &layer->inter_rect, (size_t) layer->selection);
257             } else {
258                 layer->selection = rect_at_position;
259
260                 if (layer->selection < 0) {
261                     layer->state = RECT_LAYER_CREATE;
262                     layer->create_begin = position;
263                     layer->create_end = position;
264                 }
265             }
266         } break;
267         }
268     } break;
269
270     case SDL_KEYDOWN: {
271         switch (event->key.keysym.sym) {
272         case SDLK_DELETE: {
273             if (layer->selection >= 0) {
274                 rect_layer_delete_rect_at(layer, (size_t) layer->selection, undo_history);
275                 layer->selection = -1;
276             }
277         } break;
278
279         case SDLK_F2: {
280             if (layer->selection >= 0) {
281                 const char *ids = dynarray_data(layer->ids);
282                 Color *colors = dynarray_data(layer->colors);
283
284                 edit_field_restyle(
285                     layer->id_edit_field,
286                     RECT_LAYER_ID_LABEL_SIZE,
287                     color_invert(colors[layer->selection]));
288
289                 layer->state = RECT_LAYER_ID_RENAME;
290                 edit_field_replace(
291                     layer->id_edit_field,
292                     ids + layer->selection * RECT_LAYER_ID_MAX_SIZE);
293                 SDL_StartTextInput();
294             }
295         } break;
296         }
297     } break;
298     }
299
300     return 0;
301 }
302
303 static int rect_layer_event_create(RectLayer *layer,
304                                    const SDL_Event *event,
305                                    const Camera *camera,
306                                    UndoHistory *undo_history)
307 {
308     trace_assert(layer);
309     trace_assert(event);
310     trace_assert(camera);
311
312     switch (event->type) {
313     case SDL_MOUSEBUTTONUP: {
314         switch (event->button.button) {
315         case SDL_BUTTON_LEFT: {
316             const Rect real_rect =
317                 rect_from_points(
318                     layer->create_begin,
319                     layer->create_end);
320             const float area = real_rect.w * real_rect.h;
321
322             if (area >= CREATE_AREA_THRESHOLD) {
323                 rect_layer_add_rect(
324                     layer,
325                     real_rect,
326                     color_picker_rgba(&layer->color_picker),
327                     undo_history);
328             } else {
329                 log_info("The area is too small %f. Such small box won't be created.\n", area);
330             }
331             layer->state = RECT_LAYER_IDLE;
332         } break;
333         }
334     } break;
335
336     case SDL_MOUSEMOTION: {
337         layer->create_end = camera_map_screen(
338             camera,
339             event->motion.x,
340             event->motion.y);
341     } break;
342     }
343     return 0;
344 }
345
346 static int rect_layer_event_resize(RectLayer *layer,
347                                    const SDL_Event *event,
348                                    const Camera *camera,
349                                    UndoHistory *undo_history)
350 {
351     trace_assert(layer);
352     trace_assert(event);
353     trace_assert(camera);
354     trace_assert(layer->selection >= 0);
355
356     switch (event->type) {
357     case SDL_MOUSEMOTION: {
358         layer->inter_rect = rect_from_points(
359             vec(layer->inter_rect.x, layer->inter_rect.y),
360             vec_sum(
361                 camera_map_screen(
362                     camera,
363                     event->button.x,
364                     event->button.y),
365                 vec(RECT_LAYER_SELECTION_THICCNESS * -0.5f,
366                     RECT_LAYER_SELECTION_THICCNESS * -0.5f)));
367     } break;
368
369     case SDL_MOUSEBUTTONUP: {
370         layer->state = RECT_LAYER_IDLE;
371         UNDO_PUSH(layer, undo_history, UNDO_UPDATE);
372         dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
373     } break;
374     }
375
376     return 0;
377 }
378
379 static int rect_layer_event_move(RectLayer *layer,
380                                  const SDL_Event *event,
381                                  const Camera *camera,
382                                  UndoHistory *undo_history)
383 {
384     trace_assert(layer);
385     trace_assert(event);
386     trace_assert(camera);
387     trace_assert(layer->selection >= 0);
388
389     switch (event->type) {
390     case SDL_MOUSEMOTION: {
391         Point position = vec_sub(
392             camera_map_screen(
393                 camera,
394                 event->button.x,
395                 event->button.y),
396             layer->move_anchor);
397
398         trace_assert(layer->selection >= 0);
399
400         layer->inter_rect.x = position.x;
401         layer->inter_rect.y = position.y;
402     } break;
403
404     case SDL_MOUSEBUTTONUP: {
405         layer->state = RECT_LAYER_IDLE;
406         UNDO_PUSH(layer, undo_history, UNDO_UPDATE);
407         dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
408     } break;
409     }
410     return 0;
411 }
412
413 static int rect_layer_event_id_rename(RectLayer *layer,
414                                       const SDL_Event *event,
415                                       const Camera *camera,
416                                       UndoHistory *undo_history)
417 {
418     trace_assert(layer);
419     trace_assert(event);
420     trace_assert(camera);
421     trace_assert(layer->selection >= 0);
422
423     switch (event->type) {
424     case SDL_KEYDOWN: {
425         switch (event->key.keysym.sym) {
426         case SDLK_RETURN: {
427             UNDO_PUSH(layer, undo_history, UNDO_UPDATE);
428
429             char *id = dynarray_pointer_at(layer->ids, (size_t)layer->selection);
430             memset(id, 0, RECT_LAYER_ID_MAX_SIZE);
431             memcpy(id, edit_field_as_text(layer->id_edit_field), RECT_LAYER_ID_MAX_SIZE - 1);
432             layer->state = RECT_LAYER_IDLE;
433             SDL_StopTextInput();
434         } break;
435
436         case SDLK_ESCAPE: {
437             layer->state = RECT_LAYER_IDLE;
438             SDL_StopTextInput();
439         } break;
440         }
441     } break;
442     }
443
444     return edit_field_event(layer->id_edit_field, event);
445 }
446
447 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
448 {
449     LayerPtr layer = {
450         .type = LAYER_RECT,
451         .ptr = rect_layer
452     };
453     return layer;
454 }
455
456 RectLayer *create_rect_layer(void)
457 {
458     Lt *lt = create_lt();
459
460     RectLayer *layer = PUSH_LT(lt, nth_calloc(1, sizeof(RectLayer)), free);
461     if (layer == NULL) {
462         RETURN_LT(lt, NULL);
463     }
464     layer->lt = lt;
465
466     layer->ids = PUSH_LT(
467         lt,
468         create_dynarray(sizeof(char) * RECT_LAYER_ID_MAX_SIZE),
469         destroy_dynarray);
470     if (layer->ids == NULL) {
471         RETURN_LT(lt, NULL);
472     }
473
474     layer->rects = PUSH_LT(
475         lt,
476         create_dynarray(sizeof(Rect)),
477         destroy_dynarray);
478     if (layer->rects == NULL) {
479         RETURN_LT(lt, NULL);
480     }
481
482     layer->colors = PUSH_LT(
483         lt,
484         create_dynarray(sizeof(Color)),
485         destroy_dynarray);
486     if (layer->colors == NULL) {
487         RETURN_LT(lt, NULL);
488     }
489
490     layer->id_edit_field = PUSH_LT(
491         lt,
492         create_edit_field(
493             RECT_LAYER_ID_LABEL_SIZE,
494             COLOR_BLACK),
495         destroy_edit_field);
496     if (layer->id_edit_field == NULL) {
497         RETURN_LT(lt, NULL);
498     }
499
500     layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
501     layer->selection = -1;
502
503     return layer;
504 }
505
506 RectLayer *create_rect_layer_from_line_stream(LineStream *line_stream)
507 {
508     trace_assert(line_stream);
509
510     RectLayer *layer = create_rect_layer();
511     if (layer == NULL) {
512         return NULL;
513     }
514
515     const char *line = line_stream_next(line_stream);
516     if (line == NULL) {
517         RETURN_LT(layer->lt, NULL);
518     }
519
520     size_t count = 0;
521     if (sscanf(line, "%zu", &count) < 0) {
522         RETURN_LT(layer->lt, NULL);
523     }
524
525     for (size_t i = 0; i < count; ++i) {
526         line = line_stream_next(line_stream);
527         if (line == NULL) {
528             RETURN_LT(layer->lt, NULL);
529         }
530
531         char hex[7];
532         Rect rect;
533         char id[RECT_LAYER_ID_MAX_SIZE];
534
535         if (sscanf(line,
536                    "%"STRINGIFY(RECT_LAYER_ID_MAX_SIZE)"s%f%f%f%f%6s\n",
537                    id,
538                    &rect.x, &rect.y,
539                    &rect.w, &rect.h,
540                    hex) < 0) {
541             RETURN_LT(layer->lt, NULL);
542         }
543
544         Color color = hexstr(hex);
545
546         dynarray_push(layer->rects, &rect);
547         dynarray_push(layer->ids, id);
548         dynarray_push(layer->colors, &color);
549     }
550
551     return layer;
552 }
553
554 void destroy_rect_layer(RectLayer *layer)
555 {
556     trace_assert(layer);
557     RETURN_LT0(layer->lt);
558 }
559
560 int rect_layer_render(const RectLayer *layer, Camera *camera, int active)
561 {
562     trace_assert(layer);
563     trace_assert(camera);
564
565     const size_t n = dynarray_count(layer->rects);
566     Rect *rects = dynarray_data(layer->rects);
567     Color *colors = dynarray_data(layer->colors);
568     const char *ids = dynarray_data(layer->ids);
569
570     // The Rectangles
571     for (size_t i = 0; i < n; ++i) {
572         Rect rect = rects[i];
573         Color color = colors[i];
574
575         if (layer->selection == (int) i) {
576             if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
577                 rect = layer->inter_rect;
578             }
579
580             if (layer->state == RECT_LAYER_RECOLOR) {
581                 color = layer->inter_color;
582             }
583         }
584
585         if (camera_fill_rect(
586                 camera,
587                 rect,
588                 color_scale(
589                     color,
590                     rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
591             return -1;
592         }
593
594         // Selection Overlay
595         if ((size_t) layer->selection == i) {
596             const Rect overlay_rect =
597                 rect_scale(
598                     camera_rect(camera, rect),
599                     RECT_LAYER_SELECTION_THICCNESS * 0.5f);
600             const Color overlay_color = color_invert(color);
601
602             // Main Rectangle
603             if (camera_fill_rect(
604                     camera,
605                     rect,
606                     color_scale(
607                         color,
608                         rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
609                 return -1;
610             }
611
612             if (camera_draw_thicc_rect_screen(
613                     camera,
614                     overlay_rect,
615                     overlay_color,
616                     RECT_LAYER_SELECTION_THICCNESS) < 0) {
617                 return -1;
618             }
619
620             // Rectangle Id
621             if (layer->state == RECT_LAYER_ID_RENAME) {
622                 // ID renaming Edit Field
623                 if (edit_field_render_world(
624                         layer->id_edit_field,
625                         camera,
626                         rect_position(rect)) < 0) {
627                     return -1;
628                 }
629             } else {
630                 // Id text
631                 if (camera_render_text(
632                         camera,
633                         ids + layer->selection * RECT_LAYER_ID_MAX_SIZE,
634                         RECT_LAYER_ID_LABEL_SIZE,
635                         color_invert(color),
636                         rect_position(rect)) < 0) {
637                     return -1;
638                 }
639             }
640
641             // Resize Anchor
642             if (camera_fill_rect_screen(
643                     camera,
644                     rect_layer_resize_anchor(camera, rect),
645                     overlay_color) < 0) {
646                 return -1;
647             }
648         }
649     }
650
651     // Proto Rectangle
652     const Color color = color_picker_rgba(&layer->color_picker);
653     if (layer->state == RECT_LAYER_CREATE) {
654         if (camera_fill_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
655             return -1;
656         }
657     }
658
659     if (active && color_picker_render(&layer->color_picker, camera) < 0) {
660         return -1;
661     }
662
663     return 0;
664 }
665
666 static
667 int rect_layer_event_recolor(RectLayer *layer,
668                              const SDL_Event *event,
669                              const Camera *camera,
670                              UndoHistory *undo_history)
671 {
672     trace_assert(layer);
673     trace_assert(event);
674     trace_assert(camera);
675     trace_assert(undo_history);
676     trace_assert(layer->selection >= 0);
677
678     int color_changed = 0;
679     if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
680         return -1;
681     }
682
683     if (color_changed) {
684         layer->inter_color = color_picker_rgba(&layer->color_picker);
685
686         if (!color_picker_drag(&layer->color_picker)) {
687             UNDO_PUSH(layer, undo_history, UNDO_UPDATE);
688             dynarray_replace_at(layer->colors, (size_t) layer->selection, &layer->inter_color);
689             layer->state = RECT_LAYER_IDLE;
690         }
691     }
692
693     return 0;
694 }
695
696 int rect_layer_event(RectLayer *layer,
697                      const SDL_Event *event,
698                      const Camera *camera,
699                      UndoHistory *undo_history)
700 {
701     trace_assert(layer);
702     trace_assert(event);
703     trace_assert(undo_history);
704
705     switch (layer->state) {
706     case RECT_LAYER_IDLE:
707         return rect_layer_event_idle(layer, event, camera, undo_history);
708
709     case RECT_LAYER_CREATE:
710         return rect_layer_event_create(layer, event, camera, undo_history);
711
712     case RECT_LAYER_RESIZE:
713         return rect_layer_event_resize(layer, event, camera, undo_history);
714
715     case RECT_LAYER_MOVE:
716         return rect_layer_event_move(layer, event, camera, undo_history);
717
718     case RECT_LAYER_ID_RENAME:
719         return rect_layer_event_id_rename(layer, event, camera, undo_history);
720
721     case RECT_LAYER_RECOLOR:
722         return rect_layer_event_recolor(layer, event, camera, undo_history);
723     }
724
725     return 0;
726 }
727
728 size_t rect_layer_count(const RectLayer *layer)
729 {
730     return dynarray_count(layer->rects);
731 }
732
733 const Rect *rect_layer_rects(const RectLayer *layer)
734 {
735     return dynarray_data(layer->rects);
736 }
737
738 const Color *rect_layer_colors(const RectLayer *layer)
739 {
740     return dynarray_data(layer->colors);
741 }
742
743 const char *rect_layer_ids(const RectLayer *layer)
744 {
745     return dynarray_data(layer->ids);
746 }
747
748 int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
749 {
750     trace_assert(layer);
751     trace_assert(filedump);
752
753     size_t n = dynarray_count(layer->ids);
754     char *ids = dynarray_data(layer->ids);
755     Rect *rects = dynarray_data(layer->rects);
756     Color *colors = dynarray_data(layer->colors);
757
758     fprintf(filedump, "%zd\n", n);
759     for (size_t i = 0; i < n; ++i) {
760         fprintf(filedump, "%s %f %f %f %f ",
761                 ids + RECT_LAYER_ID_MAX_SIZE * i,
762                 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
763         color_hex_to_stream(colors[i], filedump);
764         fprintf(filedump, "\n");
765     }
766
767     return 0;
768 }