├── .gitignore
├── Makefile
├── README.md
├── assets
├── fonts
│ └── FONT.TTF
├── map.tmx
├── music
│ └── song.mp3
├── sprites
│ ├── character.png
│ ├── held_weapons.png
│ └── soldier.png
└── tiles.png
├── prettify
└── src
├── animation.c
├── animation.h
├── collision.c
├── collision.h
├── entity.c
├── entity.h
├── game.c
├── game.h
├── main.c
├── music.c
├── music.h
├── render.c
├── render.h
├── util.c
└── util.h
/.gitignore:
--------------------------------------------------------------------------------
1 | *.o
2 | game
3 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | CC=cc
2 | SRCDIR=src
3 | ASSETS="assets/"
4 | FLAGS=-Wall -Werror -std=c99 -DASSETS='$(ASSETS)' -c -g
5 | LIBS=-lSDL2 -lSDL2_ttf -lSDL2_mixer -lSDL2_image -ltmx -ljansson -lxml2 -lz -lm
6 | SRC=$(wildcard $(SRCDIR)/*.c)
7 | OBJS=$(notdir $(SRC:.c=.o))
8 | OUT=game
9 |
10 | all: $(OUT)
11 |
12 | $(OUT): $(OBJS)
13 | $(CC) $(OBJS) -o $(OUT) $(LIBS)
14 |
15 | %.o: $(SRCDIR)/%.c
16 | $(CC) $(FLAGS) $<
17 |
18 | clean:
19 | rm -f $(OUT) $(OBJS)
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | game
2 | ====
3 |
4 | A game made by members of [nixers](http://nixers.net). The current plan is a
5 | "top-down RPG with a cyberpunk setting". We are writing it in C and SDL. We
6 | have a forum thread [here](http://nixers.net/showthread.php?tid=1621), and
7 | regularly discuss development over at [#game](irc://irc.nixers.net/#game) on
8 | irc.nixers.net.
9 |
10 | Dependencies
11 | ------------
12 |
13 | * [tmx](https://github.com/baylej/tmx), which depends on
14 | * zlib
15 | * jansson
16 | * libxml2
17 | * sdl2 (might as well get the whole suite)
18 |
19 | Building
20 | --------
21 |
22 | To build the project, simply use
23 |
24 | ```Bash
25 | make
26 | ```
27 |
28 | And run the binary
29 |
30 | ```Bash
31 | ./game
32 | ```
33 |
34 | The name of the binary is subject to change, and we'll add install/deinstall
35 | targets in the future, so keep an eye out.
36 |
37 | Contributing
38 | ------------
39 |
40 | Generally we advise people make forks and pull requests. It allows us to
41 | discuss changes before it's too late and means we won't have issues with conflicting commits. Be sure to
42 | hop on IRC for the most up-to-date discussions.
43 |
44 | Style
45 | -----
46 |
47 | The `prettify` script uses [astyle](http://astyle.sourceforge.net) to
48 | automatically format the code, please use it. We will reject or at least ask
49 | you to run astyle on any pull requests that are not formatted correctly.
50 |
--------------------------------------------------------------------------------
/assets/fonts/FONT.TTF:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nixers-projects/game/ffd2eeb5ecd89bd653f0065bce107dc1ab30efb1/assets/fonts/FONT.TTF
--------------------------------------------------------------------------------
/assets/map.tmx:
--------------------------------------------------------------------------------
1 |
2 |
17 |
--------------------------------------------------------------------------------
/assets/music/song.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nixers-projects/game/ffd2eeb5ecd89bd653f0065bce107dc1ab30efb1/assets/music/song.mp3
--------------------------------------------------------------------------------
/assets/sprites/character.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nixers-projects/game/ffd2eeb5ecd89bd653f0065bce107dc1ab30efb1/assets/sprites/character.png
--------------------------------------------------------------------------------
/assets/sprites/held_weapons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nixers-projects/game/ffd2eeb5ecd89bd653f0065bce107dc1ab30efb1/assets/sprites/held_weapons.png
--------------------------------------------------------------------------------
/assets/sprites/soldier.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nixers-projects/game/ffd2eeb5ecd89bd653f0065bce107dc1ab30efb1/assets/sprites/soldier.png
--------------------------------------------------------------------------------
/assets/tiles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nixers-projects/game/ffd2eeb5ecd89bd653f0065bce107dc1ab30efb1/assets/tiles.png
--------------------------------------------------------------------------------
/prettify:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | astyle --style=knf -s4 -n src/*
3 |
--------------------------------------------------------------------------------
/src/animation.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include "animation.h"
3 | #include "util.h"
4 |
5 | #ifndef ASSETS
6 | #define ASSETS "assets/"
7 | #endif
8 |
9 |
10 | static SDL_Surface* loadImage(char *path)
11 | {
12 | SDL_Surface *img = IMG_Load(path);
13 | if(!img) {
14 | fprintf(stderr,"IMG_Load: %s\n", IMG_GetError());
15 | return NULL;
16 | }
17 | return img;
18 | }
19 |
20 |
21 | void animationLoadAssets(SDL_Renderer *ren)
22 | {
23 | char *path = buildPath(ASSETS, "sprites/held_weapons.png");
24 | SDL_Surface *img = loadImage(path);
25 |
26 | TextureWeapons = SDL_CreateTextureFromSurface(ren, img);
27 |
28 | path = buildPath(ASSETS, "sprites/soldier.png");
29 | img = loadImage(path);
30 | TextureSoldier = SDL_CreateTextureFromSurface(ren, img);
31 |
32 | WeaponSMG = (heldWeapon) {
33 | (SDL_Rect) { 64, 0, 64, 64 },
34 | 5, -15
35 | };
36 |
37 | free(path);
38 | SDL_FreeSurface(img);
39 | }
40 |
41 | animation* CreateAnimation(SDL_Renderer *ren, SDL_Texture *tex, SDL_Rect *frames,
42 | int numFrames, float timeBetweenFrames)
43 | {
44 | animation *anim = malloc(sizeof(animation));
45 | anim->tex = tex;
46 | anim->numFrames = numFrames;
47 | anim->currentFrame = 0;
48 | anim->elapsedTime = 0;
49 | anim->timeBetweenFrames = timeBetweenFrames;
50 | anim->frames = frames;
51 |
52 | return anim;
53 | }
54 |
55 | void updateAnimation(animation* anim, float deltaTimeS)
56 | {
57 | anim->elapsedTime += deltaTimeS;
58 | if(anim->elapsedTime >= anim->timeBetweenFrames) {
59 | anim->elapsedTime = 0;
60 | if (anim->currentFrame >= anim->numFrames) anim->currentFrame = 0;
61 | else anim->currentFrame += 1;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/animation.h:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #define ANIMATION_MAX_FRAMES 10
4 |
5 | // TODO: Load all textures globally!
6 | SDL_Texture *TextureWeapons;
7 | SDL_Texture *TextureSoldier;
8 |
9 | typedef struct animation {
10 | int numFrames;
11 | int currentFrame;
12 | SDL_Rect *frames;
13 | float elapsedTime,timeBetweenFrames;
14 | SDL_Texture *tex;
15 | } animation;
16 |
17 | typedef struct heldWeapon {
18 | SDL_Rect src;
19 | int x_offset, y_offset;
20 | } heldWeapon;
21 |
22 | heldWeapon WeaponSMG;
23 |
24 | void animationLoadAssets(SDL_Renderer *ren);
25 |
26 | // numFrames with zero index
27 | animation* CreateAnimation(SDL_Renderer *ren, SDL_Texture *tex, SDL_Rect *frames,
28 | int numFrames, float timeBetweenFrames);
29 | void updateAnimation(animation* e, float deltaTimeS);
30 |
--------------------------------------------------------------------------------
/src/collision.c:
--------------------------------------------------------------------------------
1 | #include "SDL2/SDL.h"
2 | #include "collision.h"
3 | #include "game.h"
4 |
5 | bool checkCollision(SDL_Rect *rect,entity* self)
6 | {
7 | for (int i = 0; i < MAX_ENTITIES; i++) {
8 | if(entities[i] != NULL && entities[i] != self) {
9 | SDL_Rect r;
10 | r.x = entities[i]->x;
11 | r.y = entities[i]->y;
12 | r.w = entities[i]->w;
13 | r.h = entities[i]->h;
14 | if(SDL_HasIntersection(rect,&r)) {
15 | return true;
16 | }
17 | }
18 | }
19 | // Check for map collision
20 | for (int i = 0; i < (int)collision_map.size; i++) {
21 | if(SDL_HasIntersection(rect, &collision_map.array[i])) {
22 | return true;
23 | }
24 | }
25 | return false;
26 | }
27 |
28 | void initMapObjectArr(map_object_arr *a, size_t initialSize)
29 | {
30 | a->array = (SDL_Rect *)malloc(initialSize * sizeof(SDL_Rect));
31 | a->used = 0;
32 | a->size = initialSize;
33 | }
34 |
35 | void insertMapObjectArr(map_object_arr *a, SDL_Rect element)
36 | {
37 | if (a->used == a->size) {
38 | a->size *= 2;
39 | a->array = (SDL_Rect *)realloc(a->array, a->size * sizeof(SDL_Rect));
40 | }
41 | a->array[a->used++] = element;
42 | }
43 |
44 | void freeMapObjectArr(map_object_arr *a)
45 | {
46 | free(a->array);
47 | a->array = NULL;
48 | a->used = a->size = 0;
49 | }
50 |
--------------------------------------------------------------------------------
/src/collision.h:
--------------------------------------------------------------------------------
1 | #ifndef COLLISION_H
2 | #define COLLISION_H
3 |
4 | #include
5 | #include "SDL2/SDL.h"
6 | #include "entity.h"
7 |
8 | typedef struct {
9 | SDL_Rect *array;
10 | size_t used;
11 | size_t size;
12 | } map_object_arr;
13 |
14 | // Saves collision points for map
15 | map_object_arr collision_map;
16 |
17 | bool checkCollision(SDL_Rect*,entity*);
18 |
19 | // collsision_map stuff
20 | void initMapObjectArr(map_object_arr *a, size_t initialSize);
21 | void insertMapObjectArr(map_object_arr *a, SDL_Rect element);
22 | void freeMapObjectArr(map_object_arr *a);
23 |
24 | #endif // COLLISION_H
25 |
--------------------------------------------------------------------------------
/src/entity.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "entity.h"
4 | #include "game.h"
5 | #include "render.h"
6 | #include "collision.h"
7 |
8 | entity* CreateEntity(int x, int y, int w, int h, animation legs,
9 | SDL_Rect torso)
10 | {
11 | entity *e = malloc(sizeof(entity));
12 | e->x = x;
13 | e->y = y;
14 | e->w = w;
15 | e->h = h;
16 | e->x_vel = 0;
17 | e->y_vel = 0;
18 | e->velocity = 70;
19 | e->legs = legs;
20 | e->torso = torso;
21 | e->torso_tex = TextureSoldier;
22 | e->type = ENTITY_TYPE_DEFAULT;
23 | e->torso_angle = 0.0f;
24 | e->torso_center = (SDL_Point) {
25 | 32, 32
26 | };
27 | e->weapon = NULL;
28 | return e;
29 | }
30 |
31 | void updateEntityTorsoToWeapon(SDL_Renderer *ren, entity *e, heldWeapon *weapon)
32 | {
33 | e->torso_tex = renderWeaponToTexture(ren, weapon,
34 | e->torso_tex, &e->torso);
35 | e->torso.x = 0;
36 | e->torso.y = 0;
37 | }
38 |
39 | void updateEntity(entity *e, float deltaTimeS)
40 | {
41 | updateAnimation(&e->legs, deltaTimeS);
42 | switch (e->type) {
43 | case ENTITY_TYPE_DEFAULT:
44 | if (character->x < e->x) entity_move_left(e, deltaTimeS);
45 | if (character->x > e->x) entity_move_right(e, deltaTimeS);
46 | if (character->y < e->y) entity_move_up(e, deltaTimeS);
47 | if (character->y > e->y) entity_move_down(e, deltaTimeS);
48 | entity_move(e, e->x + e->x_vel, e->y + e->y_vel);
49 | break;
50 | case ENTITY_TYPE_MAIN_CHARACTER:
51 | entity_move(e, e->x + e->x_vel, e->y + e->y_vel);
52 | break;
53 | case ENTITY_TYPE_PET:
54 | if (character->x - 25 < e->x) entity_move_left(e, deltaTimeS);
55 | if (character->x + 25 > e->x) entity_move_right(e, deltaTimeS);
56 | if (character->y - 25 < e->y) entity_move_up(e, deltaTimeS);
57 | if (character->y + 25 > e->y) entity_move_down(e, deltaTimeS);
58 | entity_move(e, e->x + e->x_vel, e->y + e->y_vel);
59 | break;
60 | default:
61 | printf("Invalid entity type");
62 | break;
63 | }
64 | }
65 |
66 | // Moves entity to new position if possible
67 | void entity_move(entity *e, float x, float y)
68 | {
69 | if(x != e->x) {
70 | SDL_Rect newRect;
71 | newRect.x = x;
72 | newRect.y = e->y;
73 | newRect.w = e->w;
74 | newRect.h = e->h;
75 | if(!checkCollision(&newRect,e)) e->x = x;
76 | }
77 | if(y != e->y) {
78 | SDL_Rect newRect;
79 | newRect.x = e->x;
80 | newRect.y = y;
81 | newRect.w = e->w;
82 | newRect.h = e->h;
83 | if(!checkCollision(&newRect,e)) e->y = y;
84 | }
85 | }
86 |
87 | void eventEntity(entity * e, SDL_Event event, float deltaTimeS)
88 | {
89 | if (event.type == SDL_KEYDOWN) {
90 | SDL_Scancode key = event.key.keysym.scancode;
91 | if (key == SDL_SCANCODE_A) {
92 | entity_move_left(e, deltaTimeS);
93 | } else if (key == SDL_SCANCODE_D) {
94 | entity_move_right(e, deltaTimeS);
95 | } else if (key == SDL_SCANCODE_S) {
96 | entity_move_down(e, deltaTimeS);
97 | } else if (key == SDL_SCANCODE_W) {
98 | entity_move_up(e, deltaTimeS);
99 | }
100 | } else if(event.type == SDL_KEYUP) {
101 | SDL_Scancode key = event.key.keysym.scancode;
102 | if (key == SDL_SCANCODE_A || key == SDL_SCANCODE_D) {
103 | character->x_vel = 0;
104 | } else if (key == SDL_SCANCODE_S || key == SDL_SCANCODE_W) {
105 | character->y_vel = 0;
106 | }
107 | } else if (event.type == SDL_MOUSEMOTION) {
108 | // Check main.c!
109 |
110 | /*int deltaX = event.motion.x + camera.x - (e->x + 32);*/
111 | /*int deltaY = event.motion.y + camera.y - (e->y + 32);*/
112 | /*e->torso_angle = atan2(deltaY, deltaX) * 180 / PI;*/
113 | }
114 | }
115 |
116 | void entity_move_left(entity *e, float deltaTimeS)
117 | {
118 | e->x_vel = e->velocity * -deltaTimeS;
119 | e->legs_angle = 270;
120 | }
121 |
122 | void entity_move_right(entity *e, float deltaTimeS)
123 | {
124 | e->x_vel = e->velocity * deltaTimeS;
125 | e->legs_angle = 90;
126 | }
127 | void entity_move_up(entity *e, float deltaTimeS)
128 | {
129 | e->y_vel = e->velocity * -deltaTimeS;
130 | e->legs_angle = 0;
131 | }
132 |
133 | void entity_move_down(entity *e, float deltaTimeS)
134 | {
135 | e->y_vel = e->velocity * deltaTimeS;
136 | e->legs_angle = 180;
137 | }
138 |
--------------------------------------------------------------------------------
/src/entity.h:
--------------------------------------------------------------------------------
1 | #ifndef ENTITY_H
2 | #define ENTITY_H
3 |
4 | #include
5 | #include "animation.h"
6 |
7 | #define ENTITY_TYPE_DEFAULT 0
8 | #define ENTITY_TYPE_MAIN_CHARACTER 1
9 | #define ENTITY_TYPE_PET 2
10 |
11 | typedef struct {
12 | // pos
13 | float x, y;
14 | // body size
15 | float w,h;
16 | float x_vel, y_vel;
17 | float velocity;
18 | int type;
19 | double torso_angle;
20 | double legs_angle;
21 | SDL_Point torso_center;
22 |
23 | // animation
24 | animation legs;
25 | SDL_Rect torso;
26 | SDL_Texture *torso_tex;
27 | heldWeapon *weapon;
28 | } entity;
29 |
30 | entity* CreateEntity(int x, int y, int w, int h, animation legs, SDL_Rect torso);
31 | void updateEntityTorsoToWeapon(SDL_Renderer *ren, entity *e, heldWeapon *weapon);
32 |
33 | void updateEntity(entity *e, float deltaTimeS);
34 | void eventEntity(entity * e, SDL_Event event, float deltaTimeS);
35 |
36 | // Use these functions to move the entity
37 | void entity_move_left(entity*, float);
38 | void entity_move_right(entity*, float);
39 | void entity_move_up(entity*, float);
40 | void entity_move_down(entity*, float);
41 |
42 | void entity_move(entity*, float, float);
43 | #endif // ENTITY_H
44 |
--------------------------------------------------------------------------------
/src/game.c:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include
4 | #include
5 |
6 | #include "game.h"
7 | #include "entity.h"
8 | #include "util.h"
9 | #include "collision.h"
10 | #include "render.h"
11 |
12 | #ifndef ASSETS
13 | #define ASSETS "assets/"
14 | #endif
15 |
16 | void game_init(SDL_Renderer *ren)
17 | {
18 | for (int i = 0; i < MAX_ENTITIES; i++) {
19 | entities[i] = NULL;
20 | }
21 |
22 | animationLoadAssets(ren);
23 |
24 | SDL_Rect *frames = malloc(sizeof(SDL_Rect) * 4);
25 | frames[0] = (SDL_Rect) {
26 | 0, 0, 64, 64
27 | };
28 | frames[1] = (SDL_Rect) {
29 | 64, 0, 64, 64
30 | };
31 | frames[2] = (SDL_Rect) {
32 | 128, 0, 64, 64
33 | };
34 | frames[3] = (SDL_Rect) {
35 | 0, 64, 64, 64
36 | };
37 |
38 | animation *legs = CreateAnimation(ren, TextureSoldier, frames, 3, 0.2);
39 |
40 | character = CreateEntity(100, 100, 24,32, *legs, (SDL_Rect) {
41 | 64, 64, 64, 64
42 | });
43 | character->type = ENTITY_TYPE_MAIN_CHARACTER;
44 | character->velocity = 80;
45 | character->w = 64;
46 | character->h = 64;
47 | entities[0] = character;
48 |
49 | updateEntityTorsoToWeapon(ren, character, &WeaponSMG);
50 |
51 | entity *e = CreateEntity(300, 300, 24,32, *legs, (SDL_Rect) {
52 | 64, 64, 64, 64
53 | });
54 | e->type = ENTITY_TYPE_DEFAULT;
55 | e->velocity = 60;
56 | e->w = 64;
57 | e->h = 64;
58 | entities[1] = e;
59 |
60 | char *path = buildPath(ASSETS, "map.tmx");
61 | /* You probably want to create a fuction that creates a SDL_Texture directly here */
62 | // https://github.com/baylej/tmx/blob/master/examples/sdl.c
63 | tmx_img_load_func = (void* (*)(const char*))IMG_Load;
64 | tmx_img_free_func = (void (*)(void*)) SDL_FreeSurface;
65 |
66 | if (!(map = tmx_load("assets/map.tmx")))
67 | tmx_perror("tmpx_err");
68 |
69 | map_rect.w = map->width * map->tile_width;
70 | map_rect.h = map->height * map->tile_height;
71 | map_rect.x = 0;
72 | map_rect.y = 0;
73 |
74 | free(path);
75 | }
76 |
--------------------------------------------------------------------------------
/src/game.h:
--------------------------------------------------------------------------------
1 | #ifndef GAME_H
2 | #define GAME_H
3 |
4 | #include
5 | #include "entity.h"
6 |
7 | #define MAX_ENTITIES 100
8 |
9 | tmx_map *map;
10 | SDL_Rect map_rect;
11 |
12 | entity *entities[MAX_ENTITIES];
13 | entity *character;
14 |
15 | void game_init();
16 |
17 | #endif // GAME_H
18 |
--------------------------------------------------------------------------------
/src/main.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include "SDL2/SDL.h"
5 | #include "SDL2/SDL_ttf.h"
6 | #include "SDL2/SDL_mixer.h"
7 | #include "SDL2/SDL_image.h"
8 |
9 | #include "entity.h"
10 | #include "game.h"
11 | #include "music.h"
12 | #include "util.h"
13 | #include "render.h"
14 |
15 | #define WINDOW_TITLE "Game"
16 | #define MAX_FPS 100
17 |
18 | #define PI 4*atan(1.0)
19 |
20 | #ifndef ASSETS
21 | #define ASSETS "assets/"
22 | #endif
23 |
24 | // Main window
25 | SDL_Window *screen;
26 | SDL_Renderer *renderer;
27 |
28 | TTF_Font *font;
29 |
30 | SDL_Texture *curr_buffer;
31 | SDL_Texture *map_tex;
32 |
33 | Sint32 mouse_x;
34 | Sint32 mouse_y;
35 |
36 | double deltaX;
37 | double deltaY;
38 | int debug_line_num = 0;
39 |
40 | void draw_debug_line(char *str, int len)
41 | {
42 | SDL_Color background = { 0, 0, 0, 0 };
43 | SDL_Color foreground = { 255, 255, 255, 0 };
44 |
45 | SDL_Surface *textSurface = TTF_RenderText(font, str, foreground, background);
46 | SDL_Rect textLocation = { 0 + camera.x, 0 + camera.y + debug_line_num * 25, len, 25 };
47 | SDL_Texture *text = SDL_CreateTextureFromSurface(renderer, textSurface);
48 | renderToBuffer(renderer, text, NULL, &textLocation);
49 |
50 | debug_line_num++;
51 | }
52 |
53 | void draw(int deltaTimeMs)
54 | {
55 | float deltaTimeS = (float) deltaTimeMs / 1000;
56 | float fps = (float) 1.0 / deltaTimeS;
57 |
58 | debug_line_num = 0;
59 |
60 | renderToBuffer(renderer, map_tex, NULL, &map_rect);
61 |
62 | for (int i = 0; i < MAX_ENTITIES; i++) {
63 | if (entities[i] != NULL)
64 | renderEntity(renderer, entities[i], (int[3])WORLD_COLOR_HARD);
65 | }
66 |
67 | char str[10];
68 | sprintf(str, "%3.2f fps", fps);
69 | draw_debug_line(str, 50);
70 |
71 | sprintf(str, "x: %d y: %d", mouse_x, mouse_y);
72 | draw_debug_line(str, 80);
73 |
74 | sprintf(str, "px: %2.0f py: %2.0f", character->x, character->y);
75 | draw_debug_line(str, 80);
76 |
77 | sprintf(str, "angle: %3.4f", character->torso_angle);
78 | draw_debug_line(str, 100);
79 |
80 | sprintf(str, "cx: %d cy: %d", camera.x, camera.y);
81 | draw_debug_line(str, 80);
82 |
83 | sprintf(str, "mx: %d my: %d", map_rect.x, map_rect.y);
84 | draw_debug_line(str, 80);
85 |
86 | sprintf(str, "dx: %3.1f dy: %3.1f", deltaX, deltaY);
87 | draw_debug_line(str, 100);
88 |
89 | double x = character->x + 32;
90 | double y = character->y + 32;
91 | SDL_RenderDrawLine(renderer, x, y, mouse_x, mouse_y);
92 | SDL_RenderDrawLine(renderer, x, y, mouse_x, y);
93 | SDL_RenderDrawLine(renderer, mouse_x, y, mouse_x, mouse_y);
94 | SDL_RenderDrawLine(renderer, x, y, x, mouse_y);
95 | SDL_RenderDrawLine(renderer, x, mouse_y, mouse_x, mouse_y);
96 | }
97 |
98 | void update(int deltaTimeMs)
99 | {
100 | float deltaTimeS = (float) deltaTimeMs / 1000;
101 | for (int i = 0; i < MAX_ENTITIES; i++) {
102 | if (entities[i] != NULL)
103 | updateEntity(entities[i], deltaTimeS);
104 | }
105 | updateCamera();
106 | }
107 |
108 | void event(SDL_Event e, int deltaTimeMs)
109 | {
110 | if (e.type == SDL_KEYDOWN) {
111 | SDL_Scancode key = e.key.keysym.scancode;
112 | if (key == SDL_SCANCODE_SPACE) {
113 | toggleMusic();
114 | } else if (key == SDL_SCANCODE_C) {
115 | if (curr_buffer == buffer) curr_buffer = collision_buffer;
116 | else curr_buffer = buffer;
117 | } else if (key == SDL_SCANCODE_E) {
118 | character->torso_angle += 5;
119 | if (character->torso_angle > 360) {
120 | character->torso_angle = 0;
121 | }
122 | } else if (key == SDL_SCANCODE_Q) {
123 | character->torso_angle -= 5;
124 | if (character->torso_angle < 0) {
125 | character->torso_angle = 360;
126 | }
127 | }
128 | } else if (e.type == SDL_MOUSEMOTION) {
129 | mouse_x = e.motion.x + camera.x;
130 | mouse_y = e.motion.y + camera.y;
131 |
132 | deltaX = mouse_x - character->x - character->w/2;
133 | deltaY = mouse_y - character->y - character->h/2;
134 |
135 | double rad = atan2(deltaY, deltaX);
136 | if (rad < 0) {
137 | rad += 2*PI;
138 | }
139 | character->torso_angle = 90 + (rad * 360 / 2*PI) / 10;
140 | }
141 | eventEntity(character, e, (float) deltaTimeMs / 1000);
142 | }
143 |
144 | int main(int argc, char **argv)
145 | {
146 |
147 | if (SDL_Init(SDL_INIT_EVERYTHING) != 0) {
148 | fprintf(stderr, "Failed to initialize SDL: %s\n", SDL_GetError());
149 | return 1;
150 | }
151 | if (TTF_Init() != 0) {
152 | fprintf(stderr, "Failed to initialize TTF: %s\n", SDL_GetError());
153 | return 1;
154 | }
155 | if (Mix_OpenAudio(22050, MIX_DEFAULT_FORMAT, 2, 4096)) {
156 | fprintf(stderr, "Failed to load Mixer: %s", SDL_GetError());
157 | }
158 | int imgFlags = IMG_INIT_JPG|IMG_INIT_PNG;
159 | if(!(IMG_Init(imgFlags) & imgFlags)) {
160 | fprintf(stderr,"Failed to initialize Image: %s",SDL_GetError());
161 | }
162 |
163 | screen = SDL_CreateWindow(WINDOW_TITLE,
164 | SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
165 | WINDOW_WIDTH, WINDOW_HEIGHT,
166 | SDL_WINDOW_BORDERLESS);
167 | if (screen == NULL) {
168 | fprintf(stderr, "Failed to create window: %s\n", SDL_GetError());
169 | return 1;
170 | }
171 |
172 | renderer = SDL_CreateRenderer(screen, -1, 0);
173 | if (renderer == NULL) {
174 | fprintf(stderr, "Failed to create renderer: %s\n", SDL_GetError());
175 | return 1;
176 | }
177 |
178 | char* path = buildPath(ASSETS,"fonts/FONT.TTF");
179 | font = TTF_OpenFont(path, 12);
180 | if (font == NULL) {
181 | fprintf(stderr, "Failed to load font: %s\n",TTF_GetError());
182 | return 1;
183 | }
184 | free(path);
185 |
186 | /*Mix_Music* music = loadMusic(buildPath(ASSETS,"music/song.mp3"));*/
187 | /*playMusic(music);*/
188 |
189 | // Black backround
190 | SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
191 |
192 | game_init();
193 |
194 | if (buffers_init(renderer) != 0) {
195 | fprintf(stderr, "Failed to craete buffers: %s", SDL_GetError());
196 | return 1;
197 | }
198 |
199 | curr_buffer = buffer;
200 | SDL_Event e;
201 | SDL_Rect render_rect;
202 | render_rect.x = 0;
203 | render_rect.y = 0;
204 | render_rect.w = WINDOW_WIDTH;
205 | render_rect.h = WINDOW_HEIGHT;
206 | bool quit = false;
207 | int deltaTime = 0;
208 | int currentFrame = SDL_GetTicks();
209 | int lastFrame;
210 | int fpsMs = 1000 / MAX_FPS;
211 |
212 | map_tex = renderMap(renderer, map);
213 |
214 | camera.x = 0;
215 | camera.y = 0;
216 | camera.w = WINDOW_WIDTH;
217 | camera.h = WINDOW_HEIGHT;
218 |
219 | while (!quit) {
220 | lastFrame = currentFrame;
221 | currentFrame = SDL_GetTicks();
222 | deltaTime = currentFrame - lastFrame;
223 | renderClear(renderer);
224 |
225 | update(deltaTime);
226 | draw(deltaTime);
227 |
228 | while (SDL_PollEvent(&e)) {
229 | if (e.type == SDL_QUIT)
230 | quit = true;
231 | else
232 | event(e, deltaTime);
233 | }
234 |
235 | // Reset the target
236 | SDL_SetRenderTarget(renderer, NULL);
237 | // Copy the buffer
238 | SDL_RenderCopy(renderer, curr_buffer, &camera, &render_rect);
239 | // Draw the buffer to window
240 | SDL_RenderPresent(renderer);
241 |
242 | // Delay if we are drawing more that 100 fps
243 | float delay = fpsMs - deltaTime / 1000;
244 | if (delay > 0) SDL_Delay(delay);
245 | }
246 |
247 | SDL_Quit();
248 | return 0;
249 | }
250 |
--------------------------------------------------------------------------------
/src/music.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | Mix_Music * loadMusic(char path[])
7 | {
8 | Mix_Music * music = Mix_LoadMUS(path);
9 |
10 | if (music == NULL) {
11 | errx(1, "Failed to load music\nSDL ERROR: %s", SDL_GetError());
12 | }
13 |
14 | return music;
15 | }
16 |
17 | void playMusic(Mix_Music * music)
18 | {
19 | Mix_PlayMusic(music, 1);
20 | }
21 |
22 | void toggleMusic()
23 | {
24 | if(Mix_PausedMusic()) {
25 | Mix_ResumeMusic();
26 | } else {
27 | Mix_PauseMusic();
28 | }
29 | }
30 |
31 | void cleanUp(Mix_Music * music)
32 | {
33 | Mix_FreeMusic(music);
34 | Mix_CloseAudio();
35 | SDL_Quit();
36 | }
37 |
--------------------------------------------------------------------------------
/src/music.h:
--------------------------------------------------------------------------------
1 | #ifndef MUSIC
2 | #define MUSIC
3 |
4 | Mix_Music * loadMusic(char path[]);
5 | void playMusic();
6 | void cleanUp();
7 | void toggleMusic();
8 |
9 | #endif
10 |
--------------------------------------------------------------------------------
/src/render.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "render.h"
4 | #include "game.h"
5 | #include "collision.h"
6 |
7 | int rmask = 0xff000000;
8 | int gmask = 0x00ff0000;
9 | int bmask = 0x0000ff00;
10 | int amask = 0x000000ff;
11 |
12 | SDL_Texture* renderWeaponToTexture(SDL_Renderer *ren, heldWeapon *weapon_src,
13 | SDL_Texture *target_tex, SDL_Rect *target_src)
14 | {
15 |
16 | SDL_Texture *t = SDL_CreateTexture(ren, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET,
17 | target_src->w, target_src->h);
18 |
19 |
20 | SDL_SetTextureBlendMode(t, SDL_BLENDMODE_BLEND);
21 |
22 | if (SDL_SetRenderTarget(ren, t) != 0) {
23 | puts("FUCK RENDERWEAPON");
24 | }
25 | SDL_SetRenderDrawColor(ren, 255, 255, 255, 0);
26 |
27 | SDL_RenderFillRect(ren, NULL);
28 |
29 | SDL_Rect dst = {0,0,target_src->w,target_src->h};
30 | SDL_RenderCopy(ren, target_tex, target_src, &dst);
31 | dst.x += weapon_src->x_offset;
32 | dst.y += weapon_src->y_offset;
33 | SDL_RenderCopy(ren, TextureWeapons, &weapon_src->src, &dst);
34 |
35 | if (SDL_SetRenderTarget(ren, buffer) != 0) {
36 | puts("FUCK RENDERWEAPON2");
37 | }
38 |
39 | return t;
40 | }
41 |
42 | void renderClear(SDL_Renderer *ren)
43 | {
44 | // Clear buffer
45 | setTargetToBuffer(ren);
46 | SDL_RenderClear(ren);
47 |
48 | // Clear collision buffer
49 | setTargetToCollisionBuffer(ren);
50 | SDL_RenderClear(ren);
51 |
52 | // Draw map collsion buffer to collision buffer
53 | SDL_RenderCopy(ren, map_collision_buffer, NULL, NULL);
54 | }
55 |
56 | void setColor(SDL_Renderer *ren, int color)
57 | {
58 | unsigned char r, g, b;
59 |
60 | r = (color >> 16) & 0xFF;
61 | g = (color >> 8) & 0xFF;
62 | b = (color) & 0xFF;
63 |
64 | SDL_SetRenderDrawColor(ren, r, g, b, SDL_ALPHA_OPAQUE);
65 | }
66 |
67 | void renderEntity(SDL_Renderer *ren, entity* e, int color[3])
68 | {
69 | SDL_Rect *legsRect = &e->legs.frames[e->legs.currentFrame];
70 | SDL_Rect *torsoRect = &e->torso;
71 | SDL_Rect bodyRect;
72 |
73 | bodyRect.x = e->x - map_rect.x;
74 | bodyRect.y = e->y - map_rect.y;
75 | bodyRect.w = e->w;
76 | bodyRect.h = e->h;
77 | SDL_Rect dstrect;
78 | dstrect.x = e->x;
79 | dstrect.y = e->y;
80 |
81 | // Legs
82 | dstrect.w = legsRect->w;
83 | dstrect.h = legsRect->h;
84 | renderToBufferEx(ren, e->legs.tex, legsRect, &dstrect,
85 | e->legs_angle, NULL);
86 |
87 | // Torso
88 | dstrect.w = torsoRect->w;
89 | dstrect.h = torsoRect->h;
90 | renderToBufferEx(ren, e->torso_tex, torsoRect, &dstrect,
91 | e->torso_angle, &e->torso_center);
92 |
93 | renderToCollisionBuffer(ren, NULL, &bodyRect, color);
94 | }
95 |
96 | void render(SDL_Renderer *ren, SDL_Texture *tex, SDL_Rect *srcrect,
97 | SDL_Rect *dstrect, int color[3])
98 | {
99 | // NOTE: This doesn't take into account the map's position
100 | // (see renderEntity)
101 | renderToBuffer(ren, tex, srcrect, dstrect);
102 | renderToCollisionBuffer(ren, srcrect, dstrect, color);
103 | }
104 |
105 | void renderToBufferEx(SDL_Renderer *ren, SDL_Texture *tex, SDL_Rect *srcrect,
106 | SDL_Rect *dstrect, double angle, SDL_Point *point)
107 | {
108 | if (SDL_SetRenderTarget(ren, buffer) != 0) {
109 | puts("FUCK BUFFER");
110 | }
111 | SDL_RenderCopyEx(ren, tex, srcrect, dstrect, angle, point, SDL_FLIP_NONE);
112 | }
113 |
114 | void renderToBuffer(SDL_Renderer *ren, SDL_Texture *tex, SDL_Rect *srcrect,
115 | SDL_Rect *dstrect)
116 | {
117 | if (SDL_SetRenderTarget(ren, buffer) != 0) {
118 | puts("FUCK BUFFER");
119 | }
120 | SDL_RenderCopy(ren, tex, srcrect, dstrect);
121 | }
122 |
123 | void renderToCollisionBuffer(SDL_Renderer *ren, SDL_Rect *srcrect,
124 | SDL_Rect *dstrect, int color[3])
125 | {
126 | if (SDL_SetRenderTarget(ren, collision_buffer) != 0) {
127 | puts("FUCK COLLISION");
128 | }
129 | SDL_Texture *t = fillRect(ren, dstrect, color);
130 | SDL_RenderCopy(ren, t, srcrect, dstrect);
131 | }
132 |
133 | SDL_Texture* fillRect(SDL_Renderer *ren, SDL_Rect *rect, int color[3])
134 | {
135 | SDL_Texture *t;
136 | SDL_Surface *s;
137 |
138 | s = SDL_CreateRGBSurface(0, rect->w, rect->h, 32, rmask, gmask, bmask, amask);
139 | SDL_FillRect(s, NULL, SDL_MapRGB(s->format,
140 | color[0], color[1], color[2]));
141 | if (s == NULL) printf("FUCK SURFACE %s", SDL_GetError());
142 |
143 | t = SDL_CreateTextureFromSurface(ren, s);
144 |
145 | return t;
146 | }
147 |
148 | int buffers_init(SDL_Renderer *ren)
149 | {
150 | buffer = NULL;
151 | collision_buffer = NULL;
152 | buffer = SDL_CreateTexture(ren, 0,
153 | SDL_TEXTUREACCESS_TARGET, map_rect.w, map_rect.h);
154 |
155 |
156 | collision_buffer = SDL_CreateTexture(ren, 0,
157 | SDL_TEXTUREACCESS_TARGET, map_rect.w, map_rect.h);
158 |
159 |
160 |
161 | map_collision_buffer = SDL_CreateTexture(ren, 0,
162 | SDL_TEXTUREACCESS_TARGET, map_rect.w, map_rect.h);
163 |
164 |
165 | if (buffer == NULL || collision_buffer == NULL
166 | || map_collision_buffer == NULL)
167 | return 1;
168 |
169 | return 0;
170 | }
171 |
172 | void setTargetToCollisionBuffer(SDL_Renderer *ren)
173 | {
174 | if (SDL_SetRenderTarget(ren, collision_buffer) != 0) {
175 | fprintf(stderr, "Failed to switch Target to collision_buffer");
176 | }
177 | }
178 |
179 | void setTargetToBuffer(SDL_Renderer *ren)
180 | {
181 | if (SDL_SetRenderTarget(ren, buffer) != 0) {
182 | fprintf(stderr, "Failed to switch Target to buffer");
183 | }
184 | }
185 |
186 | SDL_Texture* renderMap(SDL_Renderer *ren, tmx_map *map)
187 | {
188 | // Free and re-initialize the collision_map
189 | freeMapObjectArr(&collision_map);
190 | initMapObjectArr(&collision_map, 100);
191 |
192 | SDL_Texture *res;
193 | tmx_layer *layers = map->ly_head;
194 | int w, h;
195 |
196 | w = map->width * map->tile_width;
197 | h = map->height * map->tile_height;
198 |
199 | if (!(res = SDL_CreateTexture(ren, SDL_PIXELFORMAT_RGBA8888,
200 | SDL_TEXTUREACCESS_TARGET, w, h)))
201 | puts("Error while creating map texture");
202 |
203 | if (SDL_SetRenderTarget(ren, res) != 0) puts("FUCK TARGTE");
204 |
205 | setColor(ren, map->backgroundcolor);
206 | SDL_RenderClear(ren);
207 |
208 | while (layers) {
209 | if (layers->visible) {
210 | if (layers->type == L_OBJGR) {
211 | drawObjects(ren, layers->content.head, layers->color);
212 | } else if (layers->type == L_IMAGE) {
213 | drawImageLayer(ren, layers->content.image);
214 | } else if (layers->type == L_LAYER) {
215 | drawLayer(ren, res, map, layers);
216 | }
217 | }
218 | layers = layers->next;
219 | }
220 |
221 | return res;
222 | }
223 |
224 | void drawObjects(SDL_Renderer *ren, tmx_object *head, int color)
225 | {
226 | SDL_Rect rect;
227 | setColor(ren, color);
228 | /* FIXME line thickness */
229 | while (head) {
230 | if (head->visible) {
231 | if (head->shape == S_SQUARE) {
232 | rect.x = head->x;
233 | rect.y = head->y;
234 | rect.w = head->width;
235 | rect.h = head->height;
236 | SDL_RenderDrawRect(ren, &rect);
237 | } else if (head->shape == S_POLYGON) {
238 | drawPolygon(ren, head->points, head->x, head->y, head->points_len);
239 | } else if (head->shape == S_POLYLINE) {
240 | drawPolyline(ren, head->points, head->x, head->y, head->points_len);
241 | } else if (head->shape == S_ELLIPSE) {
242 | /* FIXME: no function in SDL2 */
243 | }
244 | }
245 | head = head->next;
246 | }
247 | }
248 |
249 | void drawPolyline(SDL_Renderer *ren, double **points, double x, double y, int pointsc)
250 | {
251 | int i;
252 | for (i=1; i 2) {
261 | SDL_RenderDrawLine(ren, x+points[0][0], y+points[0][1], x+points[pointsc-1][0], y+points[pointsc-1][1]);
262 | }
263 | }
264 |
265 | void drawLayer(SDL_Renderer *ren, SDL_Texture *res, tmx_map *map, tmx_layer *layer)
266 | {
267 | unsigned long i, j;
268 | tmx_tileset *ts;
269 | SDL_Texture *tex_ts;
270 | SDL_Rect srcrect, dstrect;
271 |
272 | // Clear the map collision buffer
273 | SDL_SetRenderTarget(ren, map_collision_buffer);
274 | SDL_RenderClear(ren);
275 | // Set the target back to map's texture
276 | SDL_SetRenderTarget(ren, res);
277 |
278 | for (i=0; iheight; i++) {
279 | for (j=0; jwidth; j++) {
280 | ts = tmx_get_tileset(map, layer->content.gids[(i*map->width)+j],
281 | // FIXME: This probably aint good way to go..
282 | (unsigned int *)&(srcrect.x),
283 | (unsigned int *)&(srcrect.y));
284 | if (ts) {
285 | /* TODO Opacity and Flips */
286 | srcrect.w = dstrect.w = ts->tile_width;
287 | srcrect.h = dstrect.h = ts->tile_height;
288 | dstrect.x = j*ts->tile_width;
289 | dstrect.y = i*ts->tile_height;
290 | tex_ts = SDL_CreateTextureFromSurface(ren, (SDL_Surface*)ts->image->resource_image);
291 | SDL_RenderCopy(ren, tex_ts, &srcrect, &dstrect);
292 | SDL_DestroyTexture(tex_ts);
293 |
294 | // Following if statement checks which layer we are on
295 | if (strcmp(layer->name, "solid") == 0) {
296 | // While toe following checks which tile (id) we have
297 | // if ((layer->content.gids[(i*map->width)+j] & TMX_FLIP_BITS_REMOVAL) == 5) {
298 |
299 | // Add collision point to collision_map
300 | insertMapObjectArr(&collision_map, dstrect);
301 |
302 | SDL_Texture *tex = fillRect(ren, &dstrect, (int[3])WORLD_COLOR_HARD);
303 |
304 | SDL_SetRenderTarget(ren, map_collision_buffer);
305 | SDL_RenderCopy(ren, tex, NULL, &dstrect);
306 |
307 | SDL_SetRenderTarget(ren, res);
308 | SDL_DestroyTexture(tex);
309 | }
310 | }
311 | }
312 | }
313 | }
314 |
315 | void drawImageLayer(SDL_Renderer *ren, tmx_image *img)
316 | {
317 | SDL_Surface *bmp;
318 | SDL_Texture *tex;
319 | SDL_Rect dim;
320 |
321 | bmp = (SDL_Surface*) img->resource_image;
322 |
323 | dim.x = dim.y = 0;
324 | dim.w = bmp->w;
325 | dim.h = bmp->h;
326 |
327 | if ((tex = SDL_CreateTextureFromSurface(ren, bmp))) {
328 | SDL_RenderCopy(ren, tex, NULL, &dim);
329 | SDL_DestroyTexture(tex);
330 | }
331 |
332 | }
333 |
334 | void updateCamera()
335 | {
336 | // Move camera
337 | camera.x = character->x - WINDOW_WIDTH / 2 + character->w / 2;
338 | camera.y = character->y - WINDOW_HEIGHT / 2;
339 |
340 | // Keep it in bounds
341 | if (camera.x < 0) camera.x = 0;
342 | else if (camera.x > map_rect.w - WINDOW_WIDTH)
343 | camera.x = map_rect.w - WINDOW_WIDTH;
344 | if (camera.y < 0) camera.y = 0;
345 | else if (camera.y > map_rect.h - WINDOW_HEIGHT)
346 | camera.y = map_rect.h - WINDOW_HEIGHT;
347 | }
348 |
--------------------------------------------------------------------------------
/src/render.h:
--------------------------------------------------------------------------------
1 | #ifndef RENDER_H
2 | #define RENDER_H
3 |
4 | #include
5 | #include
6 | #include "entity.h"
7 |
8 | #define WINDOW_WIDTH 640
9 | #define WINDOW_HEIGHT 480
10 |
11 | #define WORLD_COLOR_HARD { 0, 255, 0 }
12 |
13 | SDL_Rect camera;
14 |
15 | SDL_Texture *buffer;
16 | SDL_Texture *collision_buffer;
17 | SDL_Texture *map_collision_buffer;
18 |
19 | // 0 success value, negative on error
20 | int buffers_init(SDL_Renderer *ren);
21 |
22 | void setColor(SDL_Renderer*, int color);
23 |
24 | SDL_Texture* renderWeaponToTexture(SDL_Renderer *ren, heldWeapon *weapon_src,
25 | SDL_Texture *target_tex, SDL_Rect *target_src);
26 |
27 | void renderClear(SDL_Renderer*);
28 | void render(SDL_Renderer*, SDL_Texture*, SDL_Rect*, SDL_Rect*, int color[3]);
29 | void renderEntity(SDL_Renderer*, entity*, int color[3]);
30 | void renderToBuffer(SDL_Renderer*, SDL_Texture*, SDL_Rect*, SDL_Rect*);
31 | void renderToBufferEx(SDL_Renderer*, SDL_Texture*, SDL_Rect*, SDL_Rect*,
32 | double angle, SDL_Point *point);
33 | void renderToCollisionBuffer(SDL_Renderer*, SDL_Rect*, SDL_Rect*, int color[3]);
34 |
35 | SDL_Texture* fillRect(SDL_Renderer *ren, SDL_Rect *rect, int color[3]);
36 |
37 | // If occours any errors, prints to stderr
38 | void setTargetToCollisionBuffer(SDL_Renderer*);
39 | void setTargetToBuffer(SDL_Renderer*);
40 |
41 | SDL_Texture* renderMap(SDL_Renderer*, tmx_map*);
42 |
43 | void drawObjects(SDL_Renderer*, tmx_object*, int);
44 | void drawPolyline(SDL_Renderer *ren, double **points, double x, double y, int pointsc);
45 | void drawPolygon(SDL_Renderer *ren, double **points, double x, double y, int pointsc);
46 | void drawLayer(SDL_Renderer *ren, SDL_Texture *res, tmx_map *map, tmx_layer *layer);
47 | void drawImageLayer(SDL_Renderer *ren, tmx_image *img);
48 |
49 | void updateCamera();
50 |
51 | #endif // RENDER_H
52 |
--------------------------------------------------------------------------------
/src/util.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include "util.h"
5 |
6 | char* buildPath(char* root, char* path)
7 | {
8 | char* full = malloc(strlen(root) + strlen(path) + 1);
9 | strcpy(full, root);
10 | strcat(full, path);
11 | return full;
12 | }
13 |
--------------------------------------------------------------------------------
/src/util.h:
--------------------------------------------------------------------------------
1 | #ifndef UTIL_H
2 | #define UTIL_H
3 |
4 | char* buildPath(char* root, char* path);
5 |
6 | #endif
7 |
--------------------------------------------------------------------------------