OSDN Git Service

日本語版
[nazghul-jp/nazghul-jp.git] / src / sky.c
1 //
2 // nazghul - an old-school RPG engine
3 // Copyright (C) 2002, 2003 Gordon McNutt
4 //
5 // Thi program is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU General Public License as published by the Free
7 // Software Foundation; either version 2 of the License, or (at your option)
8 // any later version.
9 //
10 // This program is distributed in the hope that it will be useful, but WITHOUT
11 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13 // more details.
14 //
15 // You should have received a copy of the GNU General Public License along with
16 // this program; if not, write to the Free Foundation, Inc., 59 Temple Place,
17 // Suite 330, Boston, MA 02111-1307 USA
18 //
19 // Gordon McNutt
20 // gmcnutt@users.sourceforge.net
21 //
22 #include "sky.h"
23 #include "screen.h"
24 #include "common.h"
25 #include "sprite.h"
26 #include "place.h"
27 #include "map.h"
28 #include "player.h"
29 #include "wq.h"
30 #include "clock.h"
31 #include "session.h"
32 #include "gob.h"
33
34 #include <assert.h>
35 #include <math.h>
36
37 // Amount by which we horizontally shift the light function to make noon
38 // produce maximum light
39 #define SKY_HORZ_SHIFT (NOON_DEGREE - 90)
40
41 // Amount by which we vertically shift the light function to make sunrise and
42 // sunset occur at the specified times. Since this involces taking the sin
43 // of a value we must do this at runtime.
44 static double SKY_VERT_SHIFT = 0.0;
45
46 // Amount by which we multiple the sine factor of the light function to make it
47 // ramp up faster. Again, must do this at runtime.
48 static double SKY_AMPLITUDE = 0.0;
49
50 // Conversion factors for deciding where in the sky window an astral body is
51 //
52 // W = width of sky window
53 // t = width of astral body sprite
54 // R = sunrise
55 // S = sunset
56 // P(x) = position in window of arc x
57 //
58 //                                     0
59 // +---+-------------------------------+---+
60 // | t |<------------ W -------------->| t |
61 // +---+-------------------------------+---+
62 // S                                   R
63 //
64 // P(R) = 0
65 // P(S) = W + t
66 // P(x) = mx + b
67 //
68 // And solving we get:
69 // m = (W + t)/(S - R)
70 // b = -R * m
71 //
72
73 // 1.06667
74 static double SKY_WIN_SLOPE;
75 static double SKY_WIN_OFFSET;
76
77 #define SKY_ARC_TO_PIXEL_OFFSET(arc) (int)(SKY_WIN_SLOPE * (double)(arc) + \
78                                            (SKY_WIN_OFFSET))
79
80 #define DEGREES_TO_RADIANS(deg) (double)((deg) * 0.0174603)
81
82 #define ASTRAL_BODY_ARC_WIDTH   ((double)SKY_SPRITE_W/(double)SKY_WIN_SLOPE)
83 #define ECLIPSE_FACTOR 0.75
84
85 //////////////////////////////////////////////////////////////////////////////
86 //
87 // Internal helper functions
88 //
89 //////////////////////////////////////////////////////////////////////////////
90
91 static int sky_get_light_from_astral_body(int arc, int maxlight)
92 {
93         //  light
94         // M |        ...................
95         //   |       .                   .
96         //   |      .                     .
97         // 0 |--------|--------|--------|--------| arc
98         //   0       90       180      270       0
99         //
100         // Start ramping up at sunrise, ramp down and hit zero at sunset. I
101         // think a simple way to simulate this is to take a horizontal slice
102         // out of a sine wave.
103         //
104         // Here's sin(arc):
105         //
106         //  |   . .
107         //  | .     .
108         //  |._______._______.
109         //  |         .     .
110         //  |           . .
111         //  r        s
112         //
113         // Point 'r' is sunrise, 's' is sunset. In our system the day and night
114         // are not equal, so to simulate this we modify the basic sine function
115         // like this:
116         //
117         // Here's sin(arc) + C
118         //
119         //  |   . .
120         //  | .     .
121         //  |.       .       .
122         //  |_________._____._
123         //  |           . .
124         //  r         s     r
125         //
126         // But in our system sunrise is not at zero degrees, so we need to
127         // shift the function:
128         //
129         // sin(arc - theta) + C:
130         //
131         //  |      . .
132         //  |    .     .
133         //  |   .       .         
134         //  |__._________.____.
135         //  |.             . .
136         //     r         s    r
137         //
138         // Since we don't want our function to produce a value greater than
139         // 1.0, we cap it, producing a flat top which represents max light.  If
140         // we stop here then it takes about four hours for the sun to go from
141         // zero to max output. I'd prefer it if this were more like an hour. So
142         // for this we need to multiply the result of the sin function to
143         // increase the amplitude.
144         //
145         // A * (sin(arc - theta) + C)
146         //
147         //  |            
148         //  |   .........
149         //  |  .         .        
150         //  |__._________.____.
151         //  |..           ....
152         //     r         s    r
153         // 
154         // 
155
156         int degrees = (arc - SKY_HORZ_SHIFT);
157         double radians = DEGREES_TO_RADIANS(degrees);
158         double factor = SKY_AMPLITUDE * (sin(radians) + SKY_VERT_SHIFT);
159         clamp(factor, 0.0, 1.0);
160         int light = (int)(factor * maxlight);
161         light = (light < 0) ? 0 : light;
162         return light;
163 }
164
165 static void sky_compute_factors(void)
166 {
167         // Called once at load time. The light function is derived above as:
168         //
169         // light = A * (sin(x - theta) + C)
170         //
171         // Here we solve for C by using the sunrise time 'R', where the
172         // function passes through 0. Since the amplitude does not
173         //
174         // A * (sin(R - theta) + C) = 0
175         // C = -sin(R - theta)
176         // 
177         SKY_VERT_SHIFT = -sin(DEGREES_TO_RADIANS(SUNRISE_DEGREE - 
178                                                  SKY_HORZ_SHIFT));
179
180         // Now solve for A, which is the amplitude, using the first value after
181         // sunrise for which the function produces 1.0. This value should be
182         // one hour after sunrise.
183         //
184         // A  * (sin(V - theta) + C) = 1
185         // A = 1 / (sin(V - theta) + C)
186
187         double inverse = sin(DEGREES_TO_RADIANS(SUNRISE_DEGREE + 
188                                                 DEGREES_PER_HOUR -  
189                                                 SKY_HORZ_SHIFT) +
190                              SKY_VERT_SHIFT);
191         assert(inverse != 0.0);
192         SKY_AMPLITUDE = 1 / inverse;
193 }
194
195 static void sky_paint_astral_body(struct sky *sky, int arc, 
196                                   struct sprite *sprite)
197 {
198         int x;
199         int pixels;
200
201         pixels = SKY_ARC_TO_PIXEL_OFFSET(arc);
202         if (pixels < 0 || pixels > SKY_W + SKY_SPRITE_W)
203                 return;
204         x = sky->screenRect.x + sky->screenRect.w - pixels;
205
206         sprite_paint(sprite, 0, x, sky->screenRect.y);
207 }
208
209 int astral_body_is_visible(int arc)
210 {
211         // SAM:
212         // I tested somewhat before, during, after sunset,
213         // and found that the sprite for the moon "left of the sun"
214         // became not visible (out of the drawing area?) before
215         // the AT command (which calls sky_astral_body_is_visible() )
216         // reported that a moon had set.
217         // 
218         // Why the difference in apparent "set" time?
219         if (arc < SUNRISE_DEGREE || arc > (SUNSET_DEGREE + SKY_SPRITE_W))
220                 return 0;
221         return 1;
222 }
223
224 //////////////////////////////////////////////////////////////////////////////
225 //
226 // Astral body api
227 //
228 //////////////////////////////////////////////////////////////////////////////
229
230 struct astral_body *astral_body_new(char *tag, char *name, int n_phases)
231 {
232         struct astral_body *body;
233
234         assert(n_phases);
235         assert(name);
236
237         body = (struct astral_body*)calloc(1, sizeof(*body));        
238         assert(body);
239
240         list_init(&body->list);
241         body->eclipse = 0;
242         body->n_phases = n_phases;
243         body->phases = (struct phase*)calloc(body->n_phases, 
244                                              sizeof(struct phase));
245         assert(body->phases);
246
247         body->tag = strdup(tag);
248         assert(body->tag);
249
250         body->name = strdup(name);
251         assert(body->name);
252
253         return body;
254 }
255
256 void astral_body_del(struct astral_body *body)
257 {
258         int i;
259
260         assert(body);
261         assert(body->tag);
262         assert(body->name);
263         assert(body->phases);
264
265         closure_unref_safe(body->gifc);
266         if (body->gob)
267                 gob_del(body->gob);
268         free(body->tag);
269         free(body->name);
270         for (i = 0; i < body->n_phases; i++)
271                 if (body->phases[i].name)
272                         free(body->phases[i].name);
273         free(body->phases);
274         free(body);
275 }
276
277 void astral_body_save(struct astral_body *body, struct save *save)
278 {
279         int i;
280
281         if (body->gob)
282                 save->enter(save, "(bind-astral-body\n");
283
284         save->enter(save, "(kern-mk-astral-body\n");        
285         save->write(save, "'%s\t; tag\n", body->tag);
286         save->write(save, "\"%s\"\t; name\n", body->name);
287         save->write(save, "%d\t; distance\n", body->distance);
288         save->write(save, "%d\t; minutes_per_phase\n", 
289                     body->minutes_per_phase);
290         save->write(save, "%d\t; minutes_per_degress\n", 
291                     body->minutes_per_degree);
292         save->write(save, "%d\t; initial_arc\n", body->initial_arc);
293         save->write(save, "%d\t; initial_phase\n", body->initial_phase);
294         //save->write(save, "%d\t; n_phases\n", body->n_phases);
295         if (body->gifc)
296                 closure_save(body->gifc, save);
297         else
298                 save->write(save, "nil\t; gifc\n");
299         assert(body->n_phases);
300         save->enter(save, "(list\n");
301         for (i = 0; i < body->n_phases; i++) {
302                 save->write(save, "(list %s %d \"%s\")\n", 
303                             sprite_get_tag(body->phases[i].sprite),
304                             body->phases[i].maxlight,
305                             body->phases[i].name);
306         }
307         save->exit(save, ")\n");
308         save->exit(save, ")\n");
309
310         if (body->gob) {
311                 gob_save(body->gob, save);
312                 save->exit(save, ") ;; bind-astral-body\n");
313         }
314 }
315
316 static void astral_body_advance_phase(struct astral_body *body)
317 {
318         int new_phase;
319
320         assert(body->n_phases);
321
322         // Calculate the new phase
323         new_phase = 0;
324         if (body->minutes_per_phase) {
325                 new_phase = clock_time() / body->minutes_per_phase;
326         }
327         new_phase += body->initial_phase;
328         new_phase %= body->n_phases;
329         
330         if (new_phase == body->phase)
331                 return;
332                 
333         // Run the phase-change handler in the script
334         if (body->gifc)
335                 closure_exec(body->gifc, "ypdd", "phase-change", 
336                              body, body->phase, new_phase);
337                 
338         body->phase = new_phase;
339 }
340
341 static void astral_body_advance_arc(struct astral_body *body)
342 {
343         int new_arc;
344         int original_light;
345
346         // Calculate the new arc
347         new_arc = (clock_time() / body->minutes_per_degree);
348         new_arc += body->initial_arc;
349         new_arc %= 360;
350
351         if (new_arc == body->arc)
352                 return;
353
354         body->arc = new_arc;
355                 
356         if (body->n_phases > 0) {
357                 astral_body_advance_phase(body);
358         }
359
360         // Change the body's light
361         original_light = body->light;
362         body->light = sky_get_light_from_astral_body(
363                 body->arc, 
364                 body->phases[body->phase].maxlight);
365         if (original_light != body->light)
366                 mapSetDirty();
367 }
368
369 typedef struct {
370         struct list list;
371         int x, w, ref;
372 } range_t;
373
374 static void range_init(range_t *range)
375 {
376         list_init(&range->list);
377         range->x=0;
378         range->w=0;
379         range->ref=0;
380 }
381
382 static range_t *range_new()
383 {
384         range_t *range = (range_t*)malloc(sizeof(*range));
385         range_init(range);
386         range->ref++;
387         return range;
388 }
389
390 static void range_unref(range_t *range)
391 {
392         assert(range->ref);
393         range->ref--;
394         if (!range->ref)
395                 free(range);
396 }
397
398 static void range_set_value(range_t *range, int x, int w)
399 {
400         range->x = x;
401         range->w = w;
402 }
403
404 static range_t * range_intersect(range_t *r1, range_t *r2)
405 {
406         // Note: this assumes positive values all around, no modulo
407         range_t *out = NULL;
408         int edge, overlap;
409
410         if (r1->x >= r2->x) {
411                 edge = r1->x - r1->w;
412                 if (edge > r2->x) {
413                         // no overlap:
414                         //   r1---->
415                         // --|-----|-----|-----|
416                         //              r2---->
417                         return NULL;
418                 }
419                 // r1 overlaps onto r2:
420                 //   r1---->
421                 // --|--|##|-----|
422                 //     r2------->
423                 out = range_new();
424                 overlap = r2->x - edge;
425                 range_set_value(out, r2->x, overlap);
426                 return out;
427         }
428
429         edge = r2->x - r2->w;
430         if (edge > r1->x) {
431                 // no overlap:
432                 //                r1---->
433                 // -----|-----|----|----|
434                 //     r2---->
435                 //
436                 return NULL;
437         }
438
439         // r2 overlaps onto r1:
440         //   r2---->
441         // --|--|##|-----|
442         //     r1------->
443         out = range_new();
444         overlap = r1->x - edge;
445         range_set_value(out, r1->x, overlap);
446         return out;
447 }
448
449 static range_t *range_merge(range_t *r1, range_t *r2)
450 {
451         // Note: this assumes positive values all around, no modulo
452         range_t *out = NULL;
453         int edge, overlap;
454
455         if (r1->x >= r2->x) {
456                 edge = r1->x - r1->w;
457                 if (edge > r2->x) {
458                         // no overlap:
459                         //   r1---->
460                         // --|-----|-----|-----|
461                         //              r2---->
462                         return NULL;
463                 }
464                 // r1 overlaps onto r2:
465                 //   r1---->
466                 // --|##|##|#####|
467                 //     r2------->
468                 out = range_new();
469                 overlap = r2->x - edge;
470                 range_set_value(out, r1->x, r1->w + r2->w - overlap);
471                 return out;
472         }
473
474         edge = r2->x - r2->w;
475         if (edge > r1->x) {
476                 // no overlap:
477                 //                r1---->
478                 // -----|-----|----|----|
479                 //     r2---->
480                 //
481                 return NULL;
482         }
483
484         // r2 overlaps onto r1:
485         //   r2---->
486         // --|##|##|#####|
487         //     r1------->
488         out = range_new();
489         overlap = r1->x - edge;
490         range_set_value(out, r2->x, r1->w + r2->w - overlap);
491         return out;
492 }
493
494 static void range_set_union(struct list *set, range_t *r1)
495 {
496         struct list *elem;
497
498         if (!r1) {
499                 //dbg("none\n");
500                 return;
501         }
502
503         //dbg("[%d %d]\n", r1->x-360, r1->w);
504
505         elem = set->next;
506
507         while (elem != set) {
508                 range_t *ru;
509                 range_t *r2 = outcast(elem, range_t, list);
510                 elem = elem->next;
511
512                 ru = range_merge(r1, r2);
513                 if (ru) {
514 /*                         dbg("   merged: [%d %d] + [%d %d] = [%d %d]\n", */
515 /*                             r1->x-360, r1->w,   */
516 /*                            r2->x-360, r2->w,  */
517 /*                             ru->x-360, ru->w); */
518                         range_unref(r1);
519                         list_remove(&r2->list);
520                         range_unref(r2);
521                         r1 = ru;
522                 }
523         }
524         
525         list_add(set, &r1->list);
526
527 }
528
529 static int range_set_sum(struct list *set)
530 {
531         struct list *elem;
532         int sum = 0;
533         list_for_each(set, elem) {
534                 range_t *rr=outcast(elem, range_t, list);
535                 sum+=rr->w;
536         }
537         return sum;
538 }
539
540 static void range_set_unref_elements(struct list *set)
541 {
542         struct list *elem;
543
544         elem = set->next;
545         while (elem != set) {
546                 range_t *range = outcast(elem, range_t, list);
547                 elem = elem->next;
548                 range_unref(range);
549         }
550 }
551
552 static void sky_get_eclipse(struct sky *sky, 
553                             struct astral_body *outer)
554 {
555         struct astral_body *inner = NULL;
556         struct list *elem = outer->list.next;
557         struct list ranges;
558         range_t r1;
559         int eclipse_arc;
560
561         //dbg(" sky_get_eclipse %s (%d)\n", outer->name, outer->arc);
562
563         outer->eclipse = 1.0;
564         list_init(&ranges);
565         range_set_value(&r1, outer->arc+360, (int)ASTRAL_BODY_ARC_WIDTH);
566
567         // Check each body listed after this one to see if it eclipses it.
568         while (elem != &sky->bodies) {
569
570                 range_t r2;
571
572                 inner = outcast(elem, struct astral_body,  list);
573                 elem = elem->next;
574                 
575                 //dbg("  check %s (%d)...", inner->name, inner->arc);
576
577                 range_set_value(&r2, inner->arc+360,
578                                 (int)ASTRAL_BODY_ARC_WIDTH);
579                 range_set_union(&ranges, range_intersect(&r1, &r2));
580         }
581
582         eclipse_arc = range_set_sum(&ranges);
583         outer->eclipse = eclipse_arc/ASTRAL_BODY_ARC_WIDTH * ECLIPSE_FACTOR;
584         range_set_unref_elements(&ranges);
585 }
586
587 //////////////////////////////////////////////////////////////////////////////
588 //
589 // Public sky api
590 //
591 //////////////////////////////////////////////////////////////////////////////
592
593 void sky_advance(struct sky *sky, int visible)
594 {
595         struct list *elem;
596         struct astral_body *body;
597
598         screenErase(&sky->screenRect);
599
600         //dbg("sky_advance\n");
601         list_for_each(&sky->bodies, elem) {
602                 body = outcast(elem, struct astral_body, list);
603                 astral_body_advance_arc(body);
604                 if (! visible)
605                         continue;
606                 sky_paint_astral_body(sky, body->arc, 
607                                       body->phases[body->phase].sprite);
608
609                 // Assume the bodies are listed in order from outermost to
610                 // innermost; outer bodies will be eclipsed by inner ones
611                 sky_get_eclipse(sky, body);
612         }
613
614         screenUpdate(&sky->screenRect);
615 }
616
617 void sky_init(struct sky *sky)
618 {
619         sky->screenRect.w = SKY_W;
620         sky->screenRect.x = SKY_X;
621         sky->screenRect.y = SKY_Y;
622         sky->screenRect.h = SKY_H;
623
624         SKY_WIN_SLOPE = ((double)SKY_W + (double)SKY_SPRITE_W) / 
625                 ((double)SUNSET_DEGREE - (double)SUNRISE_DEGREE);
626         SKY_WIN_OFFSET = -(double)SUNRISE_DEGREE * (double)SKY_WIN_SLOPE;
627
628         sky_compute_factors();
629         list_init(&sky->bodies);
630 }
631
632 void sky_start_session(struct sky *sky, int visible)
633 {
634         sky_advance(sky, visible);
635 }
636
637 void sky_end_session(struct sky *sky)
638 {
639         struct list *elem;
640         struct astral_body *body;
641
642         elem = sky->bodies.next;
643         while (elem != &sky->bodies) {
644                 body = outcast(elem, struct astral_body, list);
645                 elem = elem->next;
646                 astral_body_del(body);
647         }        
648 }
649
650 void sky_add_astral_body(struct sky *sky, struct astral_body *body)
651 {
652         // Need to keep them in order by astronomical distance, furthest to
653         // closest, so the sprites are rendered in the right order.
654         struct list *elem;
655         struct astral_body *other;
656
657         elem = sky->bodies.next;
658         while (elem != &sky->bodies) {
659                 other = outcast(elem, struct astral_body, list);
660                 // Find the first body closer than this one and insert this one
661                 // before it.
662                 if (other->distance < body->distance)
663                         break;
664                 elem = elem->next;
665         }
666
667         list_add(elem->prev, &body->list);
668 }
669
670 int sky_get_ambient_light(struct sky *sky)
671 {
672         struct list *elem;
673         struct astral_body *body;
674         int light = 0;
675
676         list_for_each(&sky->bodies, elem) {
677                 body = outcast(elem, struct astral_body, list);
678                 light += (int)((float)body->light * (1.0 - body->eclipse));
679         }
680
681         return clamp(light, 0, MAX_AMBIENT_LIGHT);
682 }
683
684 void sky_save(struct sky *sky, struct save *save)
685 {
686         struct list *elem;
687         struct astral_body *body;
688
689         save->write(save, ";; ---------\n");
690         save->write(save, ";; Astronomy\n");
691         save->write(save, ";; ---------\n");
692
693         list_for_each(&sky->bodies, elem) {
694                 body = outcast(elem, struct astral_body, list);
695                 astral_body_save(body, save);
696         }
697 }