Add SDL2_mixer support.
[runemen.git] / src / rune2.c
blobba88056e58d8e2509683d75051badc8a07ad99d6
1 #include "libs/savepng/savepng.h"
2 #include "libs/lazyass/lazyass.h"
3 #include "libs/binaryheap/binhl.h"
4 #include "libs/SDL_inprint/SDL2_inprint.h"
6 #include "rune.h"
7 #include "runeconf.h"
8 #include "mana.h"
9 #include "game.h"
10 #include "ui.h"
11 #include "draw.h"
12 #include "utils.h"
13 #include "ai.h"
14 #include "mainmenu.h"
16 void init_factions() {
18 memset(factions, 0, sizeof(faction_t) * 8);
20 strcpy(factions[0].title, "Your Kingdom");
21 factions[0].color.r = 0;
22 factions[0].color.g = 0;
23 factions[0].color.b = 0xff;
24 factions[0].gold = 10000;
26 strcpy(factions[1].title, "Reavers");
27 factions[1].color.r = 0xff;
28 factions[1].color.g = 0;
29 factions[1].color.b = 0;
30 factions[1].gold = 10000;
34 void clear_house(int id) {
35 house_t *h = &houses[id];
36 h->tile = 0;
37 h->w = 2;
38 h->h = 2;
40 memset(h->flag_id, -1, sizeof(Sint8) * MAX_FACTIONS);
42 /* Assign visual refs */
43 h->axis_refs[HAXIS_TYPE] = &h->tile;
44 h->axis_refs[HAXIS_STATE] = &h->built;
45 h->axis_refs[HAXIS_FRAME] = &h->frame;
48 void clear_unit(int id) {
49 unit_t *u = &units[id];
51 memset(u, 0, sizeof(unit_t));
53 u->refresh.path = 1;
55 memset(u->flag_id, -1, sizeof(Sint8) * MAX_FACTIONS);
57 u->axis_refs[UAXIS_TYPE] = &u->tile;
58 u->axis_refs[UAXIS_MODE] = &u->mode;
59 u->axis_refs[UAXIS_FRAME] = &u->frame;
60 u->axis_refs[UAXIS_FACEFRAME] = &u->faceframe;
63 void init_houses() {
64 int i;
65 for (i = 0; i < MAX_HOUSES; i++) {
66 clear_house(i);
68 num_houses = 0;
71 void init_units() {
72 int i;
73 for (i = 0; i < MAX_UNITS; i++) {
74 clear_unit(i);
76 num_units = 0;
79 void remove_flag(int id) {
80 flag_t *f = &your->flags[id];
82 if (f->house) {
83 f->house->flag_id[ui.faction] = -1;
85 if (f->unit) {
86 f->unit->flag_id[ui.faction] = -1;
89 if (id < your->num_flags) {
90 memcpy(&your->flags[id], &your->flags[your->num_flags - 1], sizeof(flag_t));
91 if (ui.flag == your->num_flags - 1) ui.flag = id;
93 your->num_flags--;
94 if (ui.flag == id) ui.flag = -1;
97 int add_flag(Uint8 tx, Uint8 ty, Uint8 type) {
99 int id = your->num_flags;
101 flag_t *f = &your->flags[id];
103 f->type = type;
104 f->reward = 100;
106 your->gold -= 100;
108 if (type == 0) {
109 f->x = tx;
110 f->y = ty;
111 f->w = 1;
112 f->h = 1;
113 } else {
115 if (ui.hover == overUnit) {
116 unit_t *u = &units[ui.hover_id];
117 f->unit = u;
119 f->w = bunits[u->tile].w;
120 f->h = bunits[u->tile].h;
122 u->flag_id[ui.faction] = id;
124 } else if (ui.hover == overHouse) {
126 f->house = &houses[ui.hover_id];
128 f->w = houses[ui.hover_id].w;
129 f->h = houses[ui.hover_id].h;
131 houses[ui.hover_id].flag_id[ui.faction] = id;
133 } else {
134 printf("Error!\n");
139 f->num_units = 0;
141 your->num_flags++;
143 return id;
145 void add_reward() {
147 flag_t *f = &your->flags[ui.flag];
149 if (your->gold < 100) {
151 log_add(&gamelog, "Not enough gold, your majesty!");
153 return;
156 your->gold -= 100;
157 f->reward += 100;
160 int add_house(Uint32 tx, Uint32 ty) {
161 int id = num_houses;
163 house_p *m = &bhouses[ui.builder];
165 house_t *h = &houses[id];
166 clear_house(id);
168 h->tile = ui.builder;//m->tile;
169 h->x = tx;
170 h->y = ty;
171 h->w = m->w;
172 h->h = m->h;
174 h->built = 0;
175 h->hp = 1;
176 h->max_hp = 100;
178 h->faction = 0;/* :( */
180 h->capacity = h->w * h->h;
182 your->gold -= m->gold_cost;
184 num_houses++;
186 return id;
189 void stress_unit(int id, int dmg) {
190 unit_t *u = &units[id];
191 u->dmg += dmg;
194 void kill_unit(int id) {
195 unit_t *u = &units[id];
196 u->tile = 0;
198 // remove_wtf(id);
199 /* When devotee dies, his link to lord is removed */
200 if (u->link) {
201 ///* Lord is alarmed */
202 //unit_t *l = u->link;
203 //while (l->link) l = l->link;
204 //l->baloon = 1;
206 u->link->ref_count[u->link_stat]--;
207 u->link->ref_counts--;
208 u->link = NULL;
210 /* When lord dies, his devotees are freed! */
211 if (u->ref_count[u->link_stat]) {
212 int i;
213 for (i = 0; i < num_units; i++) {
214 if (units[i].link == u) {
215 units[i].baloon = 1;
216 units[i].link = NULL;
217 u->ref_count[u->link_stat]--;
218 u->ref_counts--;
222 rebuild_pools();
225 int spawn_unit(Uint8 type, Uint8 faction, Uint8 x, Uint8 y) {
226 int i;
227 int id = num_units++; //TODO: proper handling
229 unit_t *u = &units[id];
230 unit_p *p = &bunits[type];
232 clear_unit(id);
234 generate_name(u);
236 u->tile = type;
237 u->faction = faction;
238 u->tx = u->x = x;
239 u->ty = u->y = y;
241 for (i = 0; i < MAX_STAT; i++) {
242 u->base_stat[i] = p->base_stat[i];
245 u->color = 0x6c441c;
247 return id;
250 int find_flagT(Uint8 tx, Uint8 ty) {
251 #if 0
252 int i;
253 for (i = 0; i < your->num_flags; i++) {
254 flag_t *f = &your->flags[i];
255 if (tx >= f->x && tx <= f->x+(f->w-1) &&
256 ty >= f->y && ty <= f->y+(f->h-1))
258 return i;
261 return -1;
262 #else
263 return flag_grid_i[ty][tx];
264 #endif
267 int find_houseT(Uint8 tx, Uint8 ty) {
268 #if 0
269 int i;
270 for (i = 0; i < num_houses; i++) {
271 house_t *h = &houses[i];
272 if (tx >= h->x && tx <= h->x+(h->w-1) &&
273 ty >= h->y && ty <= h->y+(h->h-1))
275 return i;
278 return -1;
279 #else
280 return house_grid_i[ty][tx];
281 #endif
284 int find_house(Uint32 x, Uint32 y) {
285 return find_houseT(
286 (x + ui.vx - game_map.x) / TILE_W,
287 (y + ui.vy - game_map.y) / TILE_H
291 int find_unitT(Uint8 tx, Uint8 ty) {
292 #if 0
293 int i;
294 for (i = 0; i < num_units; i++) {
295 unit_t *u = &units[i];
296 unit_p *p = &bunits[u->tile];
297 if (u->visiting) continue;
298 if (u->mode == ANIM_DEATH) continue;
299 if (tx >= u->x && tx <= u->x+(p->w-1) &&
300 ty <= u->y && ty >= u->y-(p->h-1) )
302 return i;
305 return -1;
306 #else
307 /*unit_t *u = unit_grid[ty][tx];
308 if (u != NULL) {
309 if (u->visiting) return -1;
310 if (u->mode == ANIM_DEATH) return -1;
312 return unit_grid_i[ty][tx];
313 #endif
316 int find_unit(Uint32 x, Uint32 y) {
317 x = x + ui.vx - game_map.x;
318 y = y + ui.vy - game_map.y;
320 int i;
321 for (i = 0; i < num_units; i++) {
322 unit_t *u = &units[i];
323 unit_p *p = &bunits[u->tile];
324 if (u->visiting) continue;
325 SDL_Rect pos;
327 pos.x = u->x * TILE_W + u->ox;
328 pos.y = (u->y-(p->h-1)) * TILE_H + u->oy;
330 pos.w = TILE_W * p->w;
331 pos.h = TILE_H * p->h;
333 if (SDL_InBounds(x, y, &pos))
335 return i;
338 return -1;
341 int xcollide(Uint8 tx, Uint8 ty, Uint8 w, Uint8 h) {
342 int i, j;
343 for (j = 0; j < h; j++) {
344 for (i = 0; i < w; i++) {
345 //if (fog[ty + j][tx + i]) return 1;
346 if (find_houseT(tx + i, ty + j) != -1) return 1;
347 if (find_unitT (tx + i, ty + j) != -1) return 1;
350 return 0;
353 #include "SDL2_particles.h"
355 particle_system psystems[128];
356 int num_psystems = 0;
358 void add_fire(Sint16 x, Sint16 y, Uint16 limit) {
359 int id = num_psystems++;
360 particle_system *ps = &psystems[id];
362 ps->x = x;
363 ps->y = y;
364 ps->limit = limit;
365 ps->init = &basic_particle_init;
366 ps->move = &basic_particle_move;
367 ps->reset = &basic_particle_reset;
368 ps->spawn = 0;
371 void update_particles() {
372 int i;
373 for (i = 0; i < num_psystems; i++) {
374 particle_system *ps = &psystems[i];
375 PS_Update(ps);
379 void draw_particles(SDL_Renderer *target, Uint32 bx, Uint32 by) {
381 int i;
382 for (i = 0; i < num_psystems; i++) {
383 particle_system *ps = &psystems[i];
384 if (!ps->limit) continue;
385 PS_Render(target, ps, reds, 1, bx, by);
390 void init_fog() {
391 int tx, ty;
393 for (ty = 0; ty < level_h; ty++) {
394 for (tx = 0; tx < level_w; tx++) {
395 fog[ty][tx] = 1;
396 scent_human[ty][tx] = 0;
401 void calc_fog() {
403 int tx, ty;
405 for (ty = 0; ty < level_h; ty++) {
406 for (tx = 0; tx < level_w; tx++) {
407 // fog[ty][tx] = 1;
408 if (scent_human[ty][tx] > 0) scent_human[ty][tx]--;
409 if (scent_human[ty][tx] > 255) scent_human[ty][tx] = 255;
412 int i;
414 for (i = 0; i < num_houses; i++) {
415 house_t *h = &houses[i];
416 int j, k, bl = h->h/2;
417 for (j = -h->h+bl; j < h->h+bl+1; j++) {
418 if (h->y + j < 0) continue;
419 if (h->y + j >= level_h) break;
420 for (k = -h->w; k < h->w+1; k++) {
421 if (h->x + k < 0) continue;
422 if (h->x + k >= level_w) break;
423 fog[h->y + j][h->x + k] = 0;
428 for (i = 0; i < num_units; i++) {
429 unit_t *u = &units[i];
430 unit_p *p = &bunits[u->tile];
431 int j, k;
432 for (j = -1; j < 2; j++) {
433 if (u->y + j < 0) continue;
434 if (u->y + j >= level_h) break;
435 for (k = -1; k < 2; k++) {
436 if (u->x + k < 0) continue;
437 if (u->x + k >= level_w) break;
438 if (fog[u->y + j][u->x + k] == 1) {
439 u->exp += 1;
441 fog[u->y + j][u->x + k] = 0;
442 scent_human[u->y + j][u->x + k] += p->base_scent[SCENT_HUMAN];
449 void add_peasant();
450 void init_game() {
452 level_w = LEVEL_W;
453 level_h = LEVEL_H;
455 init_factions();
457 your = &factions[0];
458 ui.faction = 0;
460 reset_ui();
462 log_reset(&gamelog);
463 log_add(&gamelog, "Started");
465 add_fire(100,100,25000);
467 num_pools = 0;
469 ui.vx = 0;
470 ui.vy = 0;
472 prepare_colors();
474 tiles = ASS_LoadTexture("data/gfx/runelord.bmp", &white);
476 cfg_load("data/rune.cfg");
478 uibg = ASS_LoadTexture("data/gfx/woodui.bmp", &white);
480 small_font = ASS_LoadTexture("data/fonts/oddball6x8.bmp", &magenta);
482 mid_font = ASS_LoadTexture("data/fonts/webby8.bmp", &black);
484 large_font = NULL; /* aka inline */
486 infont(large_font);
488 init_units();
490 init_houses();
492 init_fog();
494 int i;
495 for (i = 0; i < 50; i++) {
497 add_peasant();
501 units[0].x = units[0].y = units[0].tx = units[0].ty = 0;
503 units[1].tx = units[1].x = (units[1].x - 3);
504 units[1].ty = units[1].y = (units[1].y + 2);
505 units[2].tx = units[2].x = (units[2].x - 2);
506 units[2].ty = units[2].y = (units[2].y + 4);
507 // random_links();
510 void focus_on_unit() {
511 unit_t *u = &units[ui.unit];
512 int nvx = u->tx * TILE_W - ui.log_width / 2;
513 int nvy = u->ty * TILE_H - ui.log_height / 2;
515 ui.flingx = nvx - ui.vx;
516 ui.flingy = nvy - ui.vy;
519 void do_update_grids(void) {
520 int i, j, k;
522 for (j = 0; j < level_h; j++)
523 for (i = 0; i < level_w; i++) {
525 unit_grid[j][i] = NULL;
526 house_grid[j][i] = NULL;
528 flag_grid_i[j][i] = -1;
529 unit_grid_i[j][i] = -1;
530 house_grid_i[j][i] = -1;
534 for (i = 0; i < your->num_flags; i++) {
535 flag_t *flag = &your->flags[i];
536 for (k = 0; k < flag->h; k++) for (j = 0; j < flag->w; j++) {
538 flag_grid_i[flag->y + k][flag->x + j] = i;
543 for (i = 0; i < num_houses; i++) {
544 house_t *house = &houses[i];
545 for (k = 0; k < house->h; k++) for (j = 0; j < house->w; j++) {
547 house_grid[house->y + k][house->x + j] = house;
548 house_grid_i[house->y + k][house->x + j] = i;
553 for (i = 0; i < num_units; i++) {
554 unit_t *unit = &units[i];
555 unit_p *proto = &bunits[unit->tile];
556 if (unit->visiting) continue;
557 if (unit->mode == ANIM_DEATH) continue;
558 for (k = 0; k < proto->h; k++) for (j = 0; j < proto->w; j++) {
560 unit_grid[unit->y - (proto->h-1) + k][unit->x + j] = unit;
561 unit_grid_i[unit->y - (proto->h-1) + k][unit->x + j] = i;
566 void do_update_links(void) {
567 update_stats(1);
568 // rebuild_pools();
569 collect_pools();
570 distrib_pools();
571 update_stats(0);
574 void do_scroll_out() {
575 if (ui.unit == -1) return;
576 unit_t *su = &units[ui.unit];
578 int fs[MAX_UNITS];
580 int f = 0;
581 int i;
582 for (i = 0; i < num_units; i++) if (units[i].link == su) fs[f++] = i;
583 if (f > 0) {
584 i = rand() % f;
585 printf("Rand is: %d\n", i);
586 ui.unit = fs[i];
587 focus_on_unit();
591 void do_scroll_in() {
592 if (ui.unit == -1) return;
593 unit_t *u = &units[ui.unit];
595 if (u->link) {
596 ui.unit = u->link_id;
597 focus_on_unit();
601 void do_pin_click() {
603 if (ui.hover == overListName) {
605 ui.unit = ui.hover_id;
608 if (ui.hover == overListPin) {
610 units[ui.hover_id].pin = 0;
616 void do_minimap_click() {
617 int x = ui.x - minimap.x;
618 int y = ui.y - minimap.y;
620 int zY = (level_h * TILE_W) / minimap.h;
621 int zX = (level_w * TILE_H) / minimap.w;
623 int vx = x * zX;
624 int vy = y * zY;
626 ui.vx = vx - game_map.w / 2;
627 ui.vy = vy - game_map.h / 2;
629 ui.flingx = 0;
630 ui.flingy = 0;
634 void do_button() {
635 unit_t *u = &units[ui.unit];
636 #define U_PEASANT 1
637 #define U_MILITIA 3
638 #define U_ARCHER 4
639 #define U_KNIGHT 5
640 #define U_FLAMER 6
641 int btn = ui.btn;
642 if (u->tile == U_PEASANT) {
643 int cost = 0;
644 int next = U_PEASANT;
645 switch (btn) {
646 case 0: next = U_MILITIA; cost = 250; break;
647 case 1: next = U_ARCHER; u->base_stat[S_DEX]+=5; cost = 200; break;
648 case 2: next = U_KNIGHT; cost = 350; break;
649 case 3: next = U_FLAMER+rand()%4; cost = 500; break;
650 default: break;
652 if (cost) {
653 your->gold -= cost;
654 u->tile = next;
657 #undef U_PEASANT
658 #undef U_MILITIA
659 #undef U_ARCHER
660 #undef U_KNIGHT
661 #undef U_FLAMER
664 void do_event_cancel() {
665 if (ui.stat != -1) {
666 ui.stat = -1;
667 return;
669 ui.flag = -1;
670 ui.setflag = -1;
671 ui.unit = -1;
672 ui.builder = -1;
673 ui.house = -1;
674 ui.stat = -1;
675 ui.btn = -1;
676 return;
679 void do_event_click(Uint32 x, Uint32 y) {
681 //printf("Clicked on %d, %d\n", x, y);
683 /* Setting flags? */
684 if (ui.setflag != -1) {
686 if (ui.hover_top == overMap) {
688 if (ui.hover_xcollide != ui.setflag || ui.hover == overFlag) {
690 log_add(&gamelog, "Can't place here");
692 } else if (your->gold < 100) {
694 log_add(&gamelog, "Not enough gold");
696 } else {
698 /* erect flag */
699 ui.flag = add_flag(ui.hover_tx, ui.hover_ty, ui.setflag);
700 ui.setflag = -1;
706 return;
709 /* Building houses? */
710 if (ui.builder != -1) {
712 if (ui.hover_top == overMap) {
714 if (ui.hover_xcollide) {
715 log_add(&gamelog, "Can't place here");
716 return;
717 } else {
719 /* build house */
720 ui.house = add_house(ui.hover_tx, ui.hover_ty);
721 ui.builder = -1;
725 return;
728 switch (ui.hover) {
730 /* UI PANE */
732 case overMinimap:
734 do_minimap_click();
736 break;
737 case overFlagButton:
739 ui.setflag = ui.hover_id;
741 break;
742 case overBuildButton:
744 ui.builder = ui.hover_id;
746 break;
747 case overPinBox:
748 case overListName:
749 case overListPin:
751 do_pin_click();
753 break;
754 case overHouseTab:
756 ui.housetab = ui.hover_id;
758 break;
759 case overUnitTab:
761 ui.unittab = ui.hover_id;
763 break;
764 case overUnitPin:
766 units[ui.unit].pin = 1 - units[ui.unit].pin;
768 break;
769 case overHouseFlag:
771 ui.flag = houses[ui.unit].flag_id[ui.hover_id];
772 if (ui.flag != -1) ui.house = -1;
774 break;
775 case overUnitFlag:
777 ui.flag = units[ui.unit].flag_id[ui.hover_id];
778 if (ui.flag != -1) ui.unit = -1;
780 break;
781 case overUnitStat:
783 /* Start rune linking */
784 if (units[ui.unit].link == NULL) ui.stat = ui.hover_id;
786 break;
787 case overUnitSkill:
789 /* Start upgrade */
790 if (units[ui.unit].link == NULL) ui.btn = ui.hover_id;
792 do_button();
793 ui.btn = -1;
795 break;
796 case overFlagPlus:
798 add_reward();
800 break;
802 /* GAME MAP */
804 case overUnit:
806 /* rune-link */
807 if (ui.stat != -1) {
808 if (ui.hover_id != ui.unit) { // add other verefications, like factions, costs, etc
810 add_link(ui.unit, ui.hover_id, ui.stat);
812 ui.unit = ui.hover_id; /* Also select */
814 /* Stop rune-linking, if it's not possible */
815 if (units[ui.unit].link != NULL || units[ui.unit].ref_count[ui.stat] > 1) ui.stat = -1;
818 /* select */
819 else {
820 ui.house = -1;
821 ui.flag = -1;
822 ui.unit = ui.hover_id;
825 break;
826 case overHouse:
828 ui.unit = -1;
829 ui.flag = -1;
830 ui.house = ui.hover_id;
832 break;
833 case overFlag:
835 ui.unit = -1;
836 ui.house = -1;
837 ui.flag = ui.hover_id;
839 break;
840 default:
842 if (ui.no_mouse) {
843 do_event_cancel();
846 break;
851 void debug_dump_animation(animation_t *a) {
853 //SDL_Rect src = { a->plane.x, a->plane.y, a->plane.w, a->plane.h };
855 printf("Plane: %d, %d -- %d, %d\n", a->plane.x, a->plane.y, a->plane.w, a->plane.h );
857 int i;
858 //...offset by each modifier axis
859 for (i = 0; i < a->num_axises; i++) {
861 Uint8 ask = a->axis_modifier[i];
862 //Uint8 ref = *(u->axis_refs[ask]);
863 //src.x += ref * a->axis_offset[i].w + a->axis_offset[i].x;
864 //src.y += ref * a->axis_offset[i].h + a->axis_offset[i].y;
866 printf(" Axis %d -- modifier %d, mult by X %d Y %d, plus X %d Y %d\n",
867 i, ask,
868 a->axis_offset[i].w,
869 a->axis_offset[i].h,
870 a->axis_offset[i].x,
871 a->axis_offset[i].y );
875 void debug_dump_unitp(unit_p *p) {
877 printf("BODY:\n");
878 debug_dump_animation(&p->body);
879 printf("FACE:\n");
880 debug_dump_animation(&p->face);
883 void debug_dump_unit(unit_t *u) {
884 debug_dump_unitp(&bunits[u->tile]);
887 void add_reaver() {
889 int id = spawn_unit(22, 1, rand() % level_w, rand() % level_h);
890 unit_t *u = &units[id];
892 strcpy(u->name, "Path Finder");
896 void add_rat() {
897 int id = spawn_unit(17, 1, rand() % level_w, rand() % level_h);
898 unit_t *u = &units[id];
899 strcpy(u->name, "Ratticate");
902 void add_peasant() {
904 int id = spawn_unit(1, 0, rand() % level_w, rand() % level_h);
905 unit_t *u = &units[id];
907 generate_name(u);
911 void do_event_key(int sym, int scan) {
913 if (sym == SDLK_AC_BACK) do_event_cancel();
914 if (scan == SDL_SCANCODE_MENU) add_reaver();
915 if (sym == SDLK_AC_SEARCH) ui.draw_minimap = 1- ui.draw_minimap;
916 //if (scan == SDL_SCANCODE_MENU) show_menu();
918 if (sym == SDLK_m) debug_dump_unitp(&bunits[units[ui.unit].tile]);
920 if (sym == SDLK_1) ui.game_speed = 25;
921 if (sym == SDLK_5) ui.game_speed = 1;
923 if (sym == SDLK_s) ui.draw_scent = 1 - ui.draw_scent;
924 if (sym == SDLK_t) ui.draw_path = 1 - ui.draw_path;
925 if (sym == SDLK_f) ui.draw_fog = 1 - ui.draw_fog;
926 if (sym == SDLK_l) ui.draw_overlays = 1 - ui.draw_overlays;
927 if (sym == SDLK_p) ui.draw_pools = 1 - ui.draw_pools;
929 if (sym == SDLK_z) add_reaver();
930 if (sym == SDLK_x) add_rat();
931 if (sym == SDLK_a) add_peasant();
932 if (sym == SDLK_n) units[ui.unit].tile++;
933 if (sym == SDLK_q) { units[ui.unit].top_desire++; units[ui.unit].top_method = 0; }
934 if (sym == SDLK_k) stress_unit(ui.unit,1);
935 if (sym == SDLK_r) rebuild_pools();
936 if (sym == SDLK_c) collect_pools();
937 //if (sym == SDLK_x) prop_pools();
938 if (sym == SDLK_d) distrib_pools();
939 if (sym == SDLK_e && ui.unit) units[ui.unit].carry_gold += 100;
940 if (sym == SDLK_i) units[ui.unit].energy += 5;
941 if (sym == SDLK_b) {
942 if (ui.unit != -1) {
943 units[ui.unit].tx = units[ui.unit].x = ui.hover_tx;
944 units[ui.unit].ty = units[ui.unit].y = ui.hover_ty;
947 if (sym == SDLK_g) {
948 if (ui.unit != -1) {
949 units[ui.unit].tx = ui.x / 16;
950 units[ui.unit].ty = ui.y / 16;
952 if (ui.house != -1) {
953 houses[ui.house].hp += 20;
954 if (houses[ui.house].hp >= houses[ui.house].max_hp) {
955 houses[ui.house].hp = houses[ui.house].max_hp;
956 houses[ui.house].built = 1;
959 return;
963 void do_event_mouse(SDL_Event *e) {
965 int tracking_mouse = 0;
967 if (e->type == SDL_MOUSEMOTION) {
969 if (e->motion.which == SDL_TOUCH_MOUSEID) return;
971 ui.x = e->motion.x;
972 ui.y = e->motion.y;
974 if (ui.dragging) {
975 ui.flingx -= e->motion.xrel;
976 ui.flingy -= e->motion.yrel;
979 track_mouse();
981 if (e->type == SDL_MOUSEBUTTONDOWN || e->type == SDL_MOUSEBUTTONUP) {
983 if (e->button.which == SDL_TOUCH_MOUSEID) return;
985 ui.x = e->button.x;
986 ui.y = e->button.y;
988 tracking_mouse = 1;
989 track_mouse();
992 if (e->type == SDL_MOUSEBUTTONDOWN) {
993 if (e->button.button == 2) { /* hold middle button */
994 ui.dragging = 1;
996 if (e->button.button == 1) { /* left click (handled on mousedown for smoother UI animations) */
997 do_event_click(ui.x, ui.y);
998 ui.pushing = 1;
1002 if (e->type == SDL_MOUSEBUTTONUP) {
1003 if (e->button.button == 2) { /* release middle button */
1004 ui.dragging = 0;
1006 if (e->button.button == 3) { /* right click */
1007 do_event_cancel();
1009 if (e->button.button == 1) { /* release left button */
1010 ui.pushing = 0;
1014 /* Mouse moved */
1015 if (tracking_mouse) track_mouse();
1018 void do_event(SDL_Event *e) {
1020 if (e->type == SDL_FINGERDOWN) {
1022 ui.x = (int) (e->tfinger.x * ui.log_width);
1023 ui.y = (int) (e->tfinger.y * ui.log_height);
1025 track_mouse();
1026 do_event_click(ui.x, ui.y);
1029 if (e->type == SDL_FINGERMOTION) {
1031 if (e->tfinger.fingerId == 0) {
1033 ui.flingx -= (int) (e->tfinger.dx * ui.log_width);
1034 ui.flingy -= (int) (e->tfinger.dy * ui.log_height);
1039 if (e->type == SDL_MOUSEMOTION
1040 || e->type == SDL_MOUSEBUTTONDOWN
1041 || e->type == SDL_MOUSEBUTTONUP) {
1043 do_event_mouse(e);
1047 if (e->type == SDL_KEYDOWN) {
1049 do_event_key(e->key.keysym.sym, e->key.keysym.scancode);
1053 if (e->type == SDL_MOUSEWHEEL) {
1055 if (e->wheel.y > 0) do_scroll_in();
1056 else if (e->wheel.y < 0) do_scroll_out();
1063 void do_unit_in_house(unit_t *u, house_t *h) {
1065 /* Unloading gold */
1066 if (u->carry_gold >= 100 && h->tile == 7) {
1067 u->progress++;
1068 if (u->progress >= 10) {
1069 u->progress = 0;
1070 u->carry_gold -= 100;
1071 your->gold += 100;
1074 else
1075 /* Mining inside */
1076 if (h->tile == 2 && u->carry_gold < 100) {
1077 u->progress++;
1078 u->stat_bid[S_STR] = 1;
1079 if (u->progress >= 10) {
1080 u->progress = 0;
1081 int mined = u->calc_stat[S_STR] * 3;
1082 if (mined == 0) mined = 1;
1083 u->carry_gold += mined;
1086 else
1087 /* Seeking shelter? */
1088 if (u->dmg) {
1089 u->stat_bid[S_MET] = 1;
1090 u->progress++;
1091 if (u->progress >= 10) {
1092 u->progress = 0;
1093 int healed = u->calc_stat[S_MET] * 3;
1094 if (healed > u->dmg) healed = u->dmg;
1095 u->dmg -= healed;
1098 else
1099 /* Leaving */
1101 u->visiting = NULL;
1102 h->visitors--;
1106 void compute_stats() {
1107 int i;
1108 for (i = 0; i < num_units; i++) {
1109 unit_t *u = &units[i];
1111 u->max_hp = u->calc_stat[S_CON] * 10;
1112 if (u->max_hp < 1) u->max_hp = 1;
1114 /* TODO: better formulas than this PoS? */
1115 u->calc_skill[SKILL_BUILD] = u->calc_stat[S_STR] + u->base_skill[SKILL_BUILD];
1116 u->calc_skill[SKILL_REPAIR] = u->calc_stat[S_STR] + u->base_skill[SKILL_REPAIR];
1117 u->calc_skill[SKILL_MELEE] = u->calc_stat[S_STR] + u->base_skill[SKILL_MELEE];
1118 u->calc_skill[SKILL_RANGED] = u->calc_stat[S_STR] + u->base_skill[SKILL_RANGED];
1119 u->calc_skill[SKILL_RIDE] = u->calc_stat[S_STR] + u->base_skill[SKILL_RIDE];
1120 u->calc_skill[SKILL_MAGERY] = u->calc_stat[S_STR] + u->base_skill[SKILL_MAGERY];
1125 void unit_claim_flag(unit_t *u, int j) {
1126 flag_t *flag = &your->flags[j];
1128 if (flag->type == 0) {
1129 u->gold += flag->reward;
1130 u->baloon = 2;
1132 remove_flag(j);
1136 void do_unit_hit_unit(unit_t *u, int j) {
1137 unit_t *target = &units[j];
1139 if (target->faction == u->faction) return;
1141 u->mode = ANIM_HIT;
1143 stress_unit(j, 2);
1146 void do_unit_hit_house(unit_t *u, int k) {
1147 house_t *h = &houses[k];
1148 /* Attacking ? */
1149 if (u->faction != h->faction) {
1153 /* Building ? */
1154 if (h->built == 0) {
1155 u->progress++;
1156 if (u->progress >= 10) {
1157 u->progress = 0;
1158 h->hp++;
1160 if (h->hp >= h->max_hp) {
1161 h->built = 1;
1163 u->mode = ANIM_HIT;
1165 else
1166 /* Entering mine */
1167 if (h->tile == 2)
1169 if (!u->visiting && h->visitors < h->capacity)
1171 u->visiting = h;
1172 h->visitors++;
1175 else
1176 /* Mining ? */
1177 if (h->tile == 2) {
1178 u->progress++;
1179 if (u->progress >= 10) {
1180 u->progress = 0;
1181 u->gold++;
1184 else
1185 /* Visiting (to unload gold) ? */
1186 if (h->tile == 7 && u->carry_gold >= 100) {
1187 if (!u->visiting && h->visitors < h->capacity)
1189 u->visiting = h;
1190 h->visitors++;
1193 //printf("%s HITTING HOUSE %s\n", u->name, h->title);
1196 void do_unit_walk(unit_t *u) {
1197 unit_p *p = &bunits[u->tile];
1198 int next_x = 0;
1199 int next_y = 0;
1201 int h_ind, u2_ind, f_ind;
1202 int i, j;
1204 if (u->x < u->tx) next_x = + 1;
1205 if (u->x > u->tx) next_x = - 1;
1206 if (u->y < u->ty) next_y = + 1;
1207 if (u->y > u->ty) next_y = - 1;
1208 //printf("Walk: %d, %d\n", next_x, next_y);
1209 #ifndef ALLOW_DIAGONALS
1210 if (next_x && next_y) { if (rand() % 2) next_y = 0; else next_x = 0; }
1211 #endif
1213 for (j = 0; j < p->h; j++) {
1214 for (i = 0; i < p->w; i++) {
1216 f_ind = find_flagT(next_x + u->x + i, next_y + u->y - (p->h-1) + j);
1217 if (f_ind != -1) {
1218 unit_claim_flag(u, f_ind);
1221 h_ind = find_houseT(next_x + u->x + i, next_y + u->y - (p->h-1) + j);
1222 if (h_ind != -1) {
1223 do_unit_hit_house(u, h_ind);
1224 u->refresh.path = 1;
1225 return;
1228 u2_ind = find_unitT(next_x + u->x + i, next_y + u->y - (p->h-1) + j);
1229 if (u2_ind != -1) {
1230 if (&units[u2_ind] == u) {
1231 //printf("Found ownself :(\n");
1232 continue;
1234 do_unit_hit_unit(u, u2_ind);
1235 u->refresh.path = 1;
1236 return;
1241 u->ox = - next_x * TILE_W;
1242 u->oy = - next_y * TILE_H;
1244 next_x += u->x;
1245 next_y += u->y;
1247 u->x = next_x;
1248 u->y = next_y;
1250 if (fog[u->y][u->x] == 1) {
1251 u->exp += 1;
1252 fog[u->y][u->x] = 0;
1255 u->mode = ANIM_WALK;
1256 if (u->carry_gold >= 100) {
1257 u->mode = ANIM_CARRY;
1261 void progress_animation(animation_t *anim, Uint8 anim_id, Uint8 *frame, Sint8 *frame_counter) {
1263 Sint8 counter = *(frame_counter);
1265 if (counter >= anim->num_frames[anim_id]) {
1266 counter = 0;
1268 if (anim->frame[anim_id][counter] < 0) {
1269 counter += anim->frame[anim_id][counter];
1271 if (counter < 0) {
1272 counter = 0;
1275 *(frame) = anim->frame[anim_id][counter];
1276 *(frame_counter) = ++counter;
1279 void do_unit_anim(unit_t *u) {
1280 unit_p *p = &bunits[u->tile];
1282 Uint8 anim_id;
1284 if (u->carry_gold) {
1285 u->mode = ANIM_CARRY;
1288 anim_id = u->mode;
1290 progress_animation(&p->body, anim_id, &u->frame, &u->framecnt);
1292 u->faceframe = p->face.frame[ANIM_FACE][0]; /* faceframe is always first frame for now... */
1295 void do_house_anim(house_t *h) {
1297 house_p *p = &bhouses[h->tile];
1299 progress_animation(&p->body, 0, &h->frame, &h->framecnt);
1302 void do_map_scroll() {
1304 int step = 8;
1306 if (ui.flingy > 0) {
1307 if (step > ui.flingy) step = ui.flingy;
1308 ui.vy += step;
1309 ui.flingy -= step;
1311 if (ui.flingy < 0) {
1312 if (-step < ui.flingy) step = -ui.flingy;
1313 ui.vy -= step;
1314 ui.flingy += step;
1316 if (ui.flingx > 0) {
1317 if (step > ui.flingx) step = ui.flingx;
1318 ui.vx += step;
1319 ui.flingx -= step;
1321 if (ui.flingx < 0) {
1322 if (-step < ui.flingx) step = -ui.flingx;
1323 ui.vx -= step;
1324 ui.flingx += step;
1327 /* Edges */
1329 int view_w = game_map.w;
1330 int view_h = game_map.h;
1332 int max_w = level_w * TILE_W;
1333 int max_h = level_h * TILE_H;
1335 if (ui.y >= 480-1) ui.vy += step;
1336 if (ui.y <= 0) ui.vy -= step;
1337 if (ui.x >= 640-1) ui.vx += step;
1338 if (ui.x <= 0) ui.vx -= step;
1340 if (ui.vx > max_w - view_w) ui.vx = max_w - view_w;
1341 if (ui.vy > max_h - view_h) ui.vy = max_h - view_h;
1342 if (ui.vy < 0) ui.vy = 0;
1343 if (ui.vx < 0) ui.vx = 0;
1346 void do_tick() {
1348 binh_list unit_list = { 0 };
1350 int max_energy = 1000;
1351 int i;
1352 for (i = 0; i < num_units; i++) {
1353 binhl_push(&unit_list, i, max_energy - units[i].energy);
1356 ui.freq++;
1357 if (ui.freq > 1000) ui.freq = 0;
1359 //int i;
1360 int xunits = 0;
1361 //for (i = 0; i < num_units; i++) {
1362 while (unit_list.len) {
1363 xunits++;
1364 i = binhl_pop(&unit_list);
1365 unit_t *u = &units[i];
1366 if (u->energy == 0) break;
1367 if (u->visiting) {
1368 if (u->energy >= ECOST_VISIT) {
1369 do_unit_in_house(u, u->visiting);
1371 u->energy -= ECOST_VISIT;
1373 continue;
1375 if (u->ox != 0 || u->oy != 0) {
1376 if (u->energy >= ECOST_SLIDE) {
1377 #ifndef ALLOW_DIAGONALS
1378 if (u->ox) u->ox += (u->ox > 0 ? -1 : 1);
1379 else u->oy += (u->oy > 0 ? -1 : 1);
1380 #else
1381 u->ox += (u->ox > 0 ? -1 : 1);
1382 u->oy += (u->oy > 0 ? -1 : 1);
1383 #endif
1384 u->energy -= ECOST_SLIDE;
1386 continue;
1388 if (u->x != u->tx || u->y != u->ty) {
1390 if (u->energy >= ECOST_WALK) {
1391 do_unit_walk(u);
1392 do_update_grids();
1393 u->energy -= ECOST_WALK;
1395 } else u->mode = 0;
1397 update_particles();
1398 do_update_grids();
1399 if (ui.hover_top == overMap) track_mouse_map();
1402 void do_phase() {
1403 do_update_links();
1404 compute_stats();
1406 int i;
1407 for (i = 0; i < num_units; i++) {
1408 unit_t *u = &units[i];
1409 #ifndef DEBUG_AI
1410 u->energy += 5;
1411 u->energy += u->calc_stat[S_DEX] * 3;
1412 if (u->energy > (u->calc_stat[S_DEX]+1) * 10) u->energy = (u->calc_stat[S_DEX]+1) * 10;
1413 #endif
1414 if (u->dmg) {
1415 if (u->mode != ANIM_DEATH) u->framecnt = 0;
1416 u->mode = ANIM_DEATH;
1417 u->energy = 0;
1418 // continue;
1421 do_unit_anim(u);
1423 calc_fog();
1426 void do_turn() {
1427 int i;
1428 profile(0, NULL);
1429 for (i = 0; i < num_units; i++) {
1430 unit_t *u = &units[i];
1431 u->baloon = 0;
1432 if (u->mode == ANIM_DEATH) continue;
1433 unit_think(i);
1435 profile(0, "Unit think");
1438 void time_tick() {
1440 static Uint32 wait = 0;
1441 static Uint32 ticks = 0;
1442 static Uint32 phases = 0;
1444 Uint32 tick = SDL_GetTicks();
1445 Uint32 pass = tick - wait;
1447 do_map_scroll();
1449 if (pass > ui.game_speed) {
1450 wait = tick;
1451 do_tick();
1452 ticks++;
1454 if (ticks >= 10) {
1455 ticks = 0;
1456 do_phase();
1457 phases++;
1459 if (phases >= 10) {
1460 phases = 0;
1461 do_turn();
1462 turns++;
1466 int rune_main(int argc, char* argv[]) {
1467 int width = 640;
1468 int height = 480;
1470 SDL_Window* window;
1471 SDL_Renderer *screen;
1472 SDL_DisplayMode *display = NULL;
1473 SDL_DisplayMode basic;
1475 Uint32 fullscreen = 0;// SDL_WINDOW_FULLSCREEN;
1477 /* Init SDL */
1478 if (SDL_Init(SDL_INIT_VIDEO) < 0) {
1479 fprintf(stderr, "SDL initialization failed: %s\n", SDL_GetError());
1480 exit(-1);
1483 /* Pick resolution */
1484 basic.w = width;
1485 basic.h = height;
1486 #if 1
1487 if (fullscreen) display = getFullscreenSize( &basic);
1488 else display = getWindowSize( &basic);
1489 #endif
1490 if (display == NULL) {
1491 fprintf(stderr, "Warning: can't find suitable display mode, reverting to 640x480.\n");
1492 display = &basic;
1495 /* Create window */
1496 window = SDL_CreateWindow("RuneMen",
1497 600, SDL_WINDOWPOS_CENTERED,
1498 display->w, display->h,
1499 SDL_WINDOW_SHOWN | fullscreen);
1501 if (window == NULL) {
1502 fprintf(stderr, "Window creation failed: %s\n", SDL_GetError());
1503 SDL_Quit();
1504 exit(-1);
1507 SDL_GetWindowSize(window, &width, &height);
1509 /* Create renderer */
1510 screen = SDL_CreateRenderer(window, -1, 0);
1512 if (screen == NULL) {
1513 fprintf(stderr, "Renderer creation failed: %s\n", SDL_GetError());
1514 SDL_DestroyWindow(window);
1515 SDL_Quit();
1516 exit(-1);
1519 /* Scale the window / ui */
1520 init_ui(screen, width, height);
1522 /* Draw lines, points and rects with alpha blending */
1523 SDL_SetRenderDrawBlendMode(screen, SDL_BLENDMODE_BLEND);
1525 ASS_Init(screen);
1527 /* Turn on SDL mixer */
1528 init_sound();
1530 inrenderer(screen);
1531 prepare_inline_font();
1533 //test music
1534 ASS_Sound *music = ASS_LoadSound("data/music/reavers.ogg");
1535 Mix_PlayChannel(-1, music, 0);
1537 int next = mainmenu_loop(screen);
1538 if (next == -1) goto done;
1539 int loops = 300000;
1541 init_game();
1543 //log system info
1544 log_addf(&gamelog, "Window size: %d x %d", width, height);
1546 while (loops)
1548 SDL_Event event = { 0 };
1549 while (SDL_PollEvent(&event)) {
1550 if (event.type == SDL_QUIT) loops = 0;
1551 if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE) loops = 0;
1552 do_event(&event);
1555 time_tick();
1557 draw_screen(screen);
1559 ui.fps = count_fps();
1561 SDL_RenderPresent(screen);
1562 SDL_Delay(10);
1564 done:
1565 fprintf(stdout, "Normal termination...\n");
1567 ASS_Quit();
1568 kill_inline_font();
1569 SDL_DestroyRenderer(screen);
1570 SDL_DestroyWindow(window);
1571 SDL_Quit();
1572 return 0;