From e283956e989dd9abdbbad474558ec7d25e76b220 Mon Sep 17 00:00:00 2001 From: "Valeriano A.R" Date: Sat, 26 Jan 2013 00:00:00 +0100 Subject: [PATCH] (20130126) --- Entity.c | 449 ----------- GameEnts.c | 51 +- GameEnts.h | 4 + Anim.c => GameLib/Anim.c | 18 +- Anim.h => GameLib/Anim.h | 7 + Audio.c => GameLib/Audio.c | 0 Audio.h => GameLib/Audio.h | 0 Draw.c => GameLib/Draw.c | 97 ++- Draw.h => GameLib/Draw.h | 9 +- GameLib/Entity.c | 761 ++++++++++++++++++ Entity.h => GameLib/Entity.h | 64 +- FontData.h => GameLib/FontData.h | 0 GameLib.c => GameLib/GameLib.c | 112 +-- GameLib.h => GameLib/GameLib.h | 30 +- Input.c => GameLib/Input.c | 0 Input.h => GameLib/Input.h | 0 Time.c => GameLib/Time.c | 3 +- Time.h => GameLib/Time.h | 0 Util.c => GameLib/Util.c | 87 +- Util.h => GameLib/Util.h | 22 +- GameMap.c | 12 + ....20120228.zip => Lonely_Ruins.20120309.zip | Bin 782066 -> 782148 bytes Makefile.common | 110 ++- NOTES.txt | 2 +- a.out | Bin 6544 -> 0 bytes data/._level_03.txt | Bin 0 -> 4096 bytes data/arrow_down.png | Bin 0 -> 790 bytes data/arrow_left.png | Bin 0 -> 864 bytes data/arrow_right.png | Bin 0 -> 881 bytes data/arrow_up.png | Bin 0 -> 800 bytes data/arrowshooter_down.png | Bin 0 -> 2138 bytes data/arrowshooter_left.png | Bin 0 -> 2132 bytes data/arrowshooter_right.png | Bin 0 -> 2116 bytes data/arrowshooter_up.png | Bin 0 -> 2052 bytes data/barrel.bmp | Bin 6454 -> 8246 bytes data/barrel.png | Bin 0 -> 2638 bytes data/barrel2.bmp | Bin 6454 -> 8246 bytes data/barrel2.png | Bin 0 -> 2598 bytes data/column.bmp | Bin 12342 -> 16438 bytes data/column.png | Bin 0 -> 4011 bytes data/column_faded.bmp | Bin 12342 -> 16438 bytes data/column_faded.png | Bin 0 -> 3113 bytes data/end.png | Bin 0 -> 42196 bytes data/end_point.bmp | Bin 6198 -> 8246 bytes data/end_point.png | Bin 0 -> 1901 bytes data/exit_point.png | Bin 0 -> 4296 bytes data/fire.png | Bin 0 -> 8381 bytes data/floor.png | Bin 0 -> 3264 bytes data/floor_center.png | Bin 0 -> 3234 bytes data/floor_left.png | Bin 0 -> 3231 bytes data/floor_right.png | Bin 0 -> 3260 bytes data/hole_lava.bmp | Bin 12342 -> 16438 bytes data/hole_lava.png | Bin 0 -> 4713 bytes data/hole_spiked.bmp | Bin 6198 -> 8246 bytes data/hole_spiked.png | Bin 0 -> 3269 bytes data/lamp.png | Bin 0 -> 3202 bytes data/level_00.txt | 4 +- data/logo.png | Bin 0 -> 35721 bytes data/maps.copy/level_00.txt | 23 + data/maps.copy/level_01.txt | 51 ++ data/maps.copy/level_02.txt | 23 + data/maps.copy/level_03.txt | 29 + data/maps.copy/level_04.txt | 26 + data/maps.copy/level_05.txt | 69 ++ data/maps.copy/level_06.txt | 21 + data/player_broken.png | Bin 0 -> 3956 bytes data/player_down.png | Bin 0 -> 3921 bytes data/player_left.png | Bin 0 -> 4140 bytes data/player_right.png | Bin 0 -> 4151 bytes data/player_up.png | Bin 0 -> 3763 bytes data/rock.bmp | Bin 6198 -> 8246 bytes data/rock.png | Bin 0 -> 2489 bytes data/save_point.png | Bin 0 -> 2274 bytes data/save_point_active.png | Bin 0 -> 5631 bytes game.exe | Bin 0 -> 166622 bytes game.save | Bin 8 -> 8 bytes main.c | 8 +- 77 files changed, 1489 insertions(+), 603 deletions(-) delete mode 100644 Entity.c rename Anim.c => GameLib/Anim.c (92%) rename Anim.h => GameLib/Anim.h (93%) rename Audio.c => GameLib/Audio.c (100%) rename Audio.h => GameLib/Audio.h (100%) rename Draw.c => GameLib/Draw.c (90%) rename Draw.h => GameLib/Draw.h (90%) create mode 100644 GameLib/Entity.c rename Entity.h => GameLib/Entity.h (66%) rename FontData.h => GameLib/FontData.h (100%) rename GameLib.c => GameLib/GameLib.c (86%) rename GameLib.h => GameLib/GameLib.h (80%) rename Input.c => GameLib/Input.c (100%) rename Input.h => GameLib/Input.h (100%) rename Time.c => GameLib/Time.c (97%) rename Time.h => GameLib/Time.h (100%) rename Util.c => GameLib/Util.c (60%) rename Util.h => GameLib/Util.h (69%) rename Lonely_Ruins.20120228.zip => Lonely_Ruins.20120309.zip (90%) delete mode 100644 a.out create mode 100644 data/._level_03.txt create mode 100644 data/arrow_down.png create mode 100644 data/arrow_left.png create mode 100644 data/arrow_right.png create mode 100644 data/arrow_up.png create mode 100644 data/arrowshooter_down.png create mode 100644 data/arrowshooter_left.png create mode 100644 data/arrowshooter_right.png create mode 100644 data/arrowshooter_up.png create mode 100644 data/barrel.png create mode 100644 data/barrel2.png create mode 100644 data/column.png create mode 100644 data/column_faded.png create mode 100644 data/end.png create mode 100644 data/end_point.png create mode 100644 data/exit_point.png create mode 100644 data/fire.png create mode 100644 data/floor.png create mode 100644 data/floor_center.png create mode 100644 data/floor_left.png create mode 100644 data/floor_right.png create mode 100644 data/hole_lava.png create mode 100644 data/hole_spiked.png create mode 100644 data/lamp.png create mode 100644 data/logo.png create mode 100644 data/maps.copy/level_00.txt create mode 100644 data/maps.copy/level_01.txt create mode 100644 data/maps.copy/level_02.txt create mode 100644 data/maps.copy/level_03.txt create mode 100644 data/maps.copy/level_04.txt create mode 100644 data/maps.copy/level_05.txt create mode 100644 data/maps.copy/level_06.txt create mode 100644 data/player_broken.png create mode 100644 data/player_down.png create mode 100644 data/player_left.png create mode 100644 data/player_right.png create mode 100644 data/player_up.png create mode 100644 data/rock.png create mode 100644 data/save_point.png create mode 100644 data/save_point_active.png create mode 100644 game.exe diff --git a/Entity.c b/Entity.c deleted file mode 100644 index 68f6c45..0000000 --- a/Entity.c +++ /dev/null @@ -1,449 +0,0 @@ -// Copyright (C) 2011 Valeriano Alfonso Rodriguez (Kableado) - -#include -#include -#include - -#include "Util.h" -#include "Draw.h" -#include "Anim.h" - -#include "Entity.h" - - -///////////////////////////// -// Entity_New -// -// -Entity *_free_entity=NULL; -Entity *Entity_New(){ - Entity *e; - - if(!_free_entity){ - e=malloc(sizeof(Entity)); - }else{ - e=_free_entity; - _free_entity=e->next; - } - - e->type=0; - vec2_set(e->pos,0.0f,0.0f); - e->flags=EntityFlag_Collision|EntityFlag_Overlap; - e->zorder=1; - - vec2_set(e->vel,0.0f,0.0f); - e->radius=1.0f; - e->mass=1.0f; - e->elast=0.0f; - e->fric_static=0.0f; - e->fric_dynamic=0.0f; - - AnimPlay_SetImg(&e->anim,NULL); - - e->color[0]=e->color[1]=e->color[2]=e->color[3]=1.0f; - e->light[0]=e->light[1]=e->light[2]=e->light[3]=1.0f; - - e->oncopy=NULL; - e->ondelete=NULL; - e->proc=NULL; - e->postproc=NULL; - e->collision=NULL; - e->overlap=NULL; - - e->A=0; - e->child=NULL; - - e->next=NULL; - - return(e); -} - - -///////////////////////////// -// Entity_Destroy -// -// -void Entity_Destroy(Entity *e){ - if(e->ondelete){ - e->ondelete(e); - } - e->next=_free_entity; - _free_entity=e; -} - - -///////////////////////////// -// Entity_Copy -// -// -Entity *Entity_Copy(Entity *e){ - Entity *n; - - n=Entity_New(); - - n->type=e->type; - vec2_set(n->pos,e->pos[0],e->pos[1]); - n->flags=e->flags; - n->zorder=e->zorder; - - vec2_set(n->vel,e->vel[0],e->vel[1]); - n->radius=e->radius; - n->mass=e->mass; - n->elast=e->elast; - n->fric_static=e->fric_static; - n->fric_dynamic=e->fric_dynamic; - - AnimPlay_Copy(&n->anim,&e->anim); - n->color[0]=e->color[0]; - n->color[1]=e->color[1]; - n->color[2]=e->color[2]; - n->color[3]=e->color[3]; - n->light[0]=e->light[0]; - n->light[1]=e->light[1]; - n->light[2]=e->light[2]; - n->light[3]=e->light[3]; - - n->oncopy=e->oncopy; - n->ondelete=e->ondelete; - n->proc=e->proc; - n->postproc=e->postproc; - n->collision=e->collision; - n->overlap=e->overlap; - - n->A=e->A; - n->child=e->child; - - // Call the copy event - if(n->oncopy){ - n->oncopy(n); - } - - return(n); -} - - -///////////////////////////// -// Entity_Draw -// -// -void Entity_Draw(Entity *e,int x,int y){ - Draw_SetColor(e->color[0],e->color[1],e->color[2],e->color[3]); - AnimPlay_Draw(&e->anim,e->pos[0]+x,e->pos[1]+y); -} - - -///////////////////////////// -// Entity_Process -// -// -void Entity_Process(Entity *b,int ft){ - b->flags&=~EntityFlag_UpdatedPos; - - // Launch method - if(b->proc){ - b->proc(b,ft); - } -} - - -///////////////////////////// -// Entity_PostProcess -// -// -void Entity_PostProcess(Entity *e,int ft){ - float qlen,len; - - // Launch method - if(e->postproc){ - e->postproc(e,ft); - } - - // Determine if there is movement - qlen=vec2_dot(e->vel,e->vel); - if(qlen>0.0f){ - - // Update position - vec2_plus(e->pos,e->pos,e->vel); - - // Aply friction - len=sqrtf(qlen); - if(lenfric_static){ - // Stopped by static friction - vec2_set(e->vel,0,0); - }else{ - // Aply dynamic friction - vec2_scale(e->vel,e->vel, - 1.0f-(e->fric_dynamic+(e->fric_static/len))); - } - - - e->flags|=EntityFlag_UpdatedPos; - } - - // Animate - AnimPlay_IncTime(&e->anim,ft); -} - - -///////////////////////////// -// Entity_CollisionResponse -// -// Normal response to a collision. -void Entity_CollisionResponse( - Entity *b1,Entity *b2,float t,vec2 n) -{ - float moment; - vec2 temp; - float elast; - - if(b1->mass>0.0f && b2->mass>0.0f){ - // Calculate elasticity - elast=(b1->mass*b1->elast+b2->mass*b2->elast)/ - (b1->mass+b2->mass); - - // Collision between two massed balls - moment=((1.0f+elast)*b1->mass*b2->mass* - (vec2_dot(b1->vel,n)+vec2_dot(b2->vel,n))) - /(b1->mass+b2->mass); - vec2_scale(temp,n,moment/b1->mass); - vec2_minus(b1->vel,b1->vel,temp); - vec2_scale(temp,n,moment/b2->mass); - vec2_plus(b2->vel,b2->vel,temp); - }else - if(b1->mass>0.0f && b2->mass<=0.0f){ - // Collision between a massed ball and a fixed ball - moment=(1.0f+b1->elast)* - (vec2_dot(b1->vel,n)); - vec2_scale(temp,n,moment); - vec2_minus(b1->vel,b1->vel,temp); - }else - if(b1->mass<=0.0f && b2->mass>0.0f){ - // Collision between a massed ball and a fixed ball - // (imposible, but better safe) - moment=(1.0f+b2->elast)* - (vec2_dot(b2->vel,n)); - vec2_scale(temp,n,moment); - vec2_plus(b2->vel,b2->vel,temp); - }else{ - // Collision between 2 fixed balls - // (imposible, but better safe) - vec2_set(b1->vel,0,0); - vec2_set(b2->vel,0,0); - } -} - - -///////////////////////////// -// Entity_Collide -// -// -int Entity_Collide(Entity *b1,Entity *b2){ - float t; - vec2 n; - vec2 vel; - - //if(!(b1->flags&EntityFlag_Collision) || !(b2->flags&EntityFlag_Collision)) - // return(0); - - // Test relative to b1 - vec2_minus(vel,b1->vel,b2->vel); - if(vec2_dot(vel,vel)<=0.0f) - return(0); - if(Colision_CircleCircle(b1->pos,b1->radius,vel,b2->pos,b2->radius,&t,n)){ - int response=1; - int rc; - - // Check the collision methods - if(b1->collision){ - rc=b1->collision(b1,b2,t,n); - if (rc==0) - response=0; - if (rc>1) - response=2; - } - if(b2->collision){ - vec2 n2; - vec2_scale(n2,n,-1.0f); - rc=b2->collision(b2,b1,t,n2); - if (rc==0) - response=0; - if (rc>1) - response=2; - } - - // Collision response - if(response==1){ - if(vec2_dot(b1->vel,b1->vel)>vec2_dot(b2->vel,b2->vel)){ - Entity_CollisionResponse(b1,b2,t,n); - }else{ - Entity_CollisionResponse(b2,b1,t,n); - } - return(1); - } - if (response==2) { - return(1); - } - return(0); - } - return(0); -} - - -///////////////////////////// -// Entity_Overlaps -// -// -void Entity_Overlaps(Entity *b1,Entity *b2){ - vec2 len; - -// if(!(b1->flags&EntityFlag_Overlap) || !(b2->flags&EntityFlag_Overlap)) -// return; - - vec2_minus(len,b1->pos,b2->pos); -#if 0 - if(fabs(len[0])>b1->radius) - return; - if(fabs(len[1])>b1->radius) - return; - if(fabs(len[0])>b2->radius) - return; - if(fabs(len[1])>b2->radius) - return; - dist=sqrtf(vec2_dot(len,len)); - - if(b1->radius>dist && b1->overlap){ - b1->overlap(b1,b2); - } - if(b2->radius>dist && b2->overlap){ - b2->overlap(b2,b1); - } -#else - vec2_set(len,fabs(b1->pos[0]-b2->pos[0]),fabs(b1->pos[1]-b2->pos[1])); - if(b1->overlap){ - if( len[0]<=b1->radius && - len[1]<=b1->radius) - { - b1->overlap(b1,b2); - } - } - if(b2->overlap){ - if( len[0]<=b2->radius && - len[1]<=b2->radius) - { - b2->overlap(b2,b1); - } - } -#endif -} - - -///////////////////////////// -// Entity_AddVelLimit -// -// -void Entity_AddVelLimit(Entity *e,vec2 vel,float limit){ - float vlen_orig,vlen; - vec2 dir,vel_temp; - - // Normalize vel getting vel - vlen_orig=sqrtf(vec2_dot(vel,vel)); - vec2_scale(dir,vel,1.0f/vlen_orig); - - // Limit velocity - vlen=vec2_dot(e->vel,dir); - if(vlenvlen_orig){ - vlen=vlen_orig; - } - vec2_scale(vel_temp,dir,vlen); - vec2_plus(e->vel,e->vel,vel_temp); - } -} - - -///////////////////////////// -// Entity_SetColor -// -// -void Entity_SetColor(Entity *e,float r,float g,float b,float a){ - e->color[0]=r; - e->color[1]=g; - e->color[2]=b; - e->color[3]=a; -} - - -///////////////////////////// -// Entity_AddColor -// -// -void Entity_AddColor(Entity *e,float r,float g,float b,float a){ - e->color[0]+=r; - if(e->color[0]>1.0f) - e->color[0]=1.0f; - e->color[1]+=g; - if(e->color[1]>1.0f) - e->color[1]=1.0f; - e->color[2]+=b; - if(e->color[2]>1.0f) - e->color[2]=1.0f; - e->color[3]+=a; - if(e->color[3]>1.0f) - e->color[3]=1.0f; -} - - -///////////////////////////// -// Entity_AddColor -// -// -void Entity_SetLight(Entity *e,float r,float g,float b,float rad){ - e->light[0]=r; - e->light[1]=g; - e->light[2]=b; - e->light[3]=rad; -} - - -///////////////////////////// -// Entity_AddColor -// -// -void Entity_Iluminate(Entity *e,Entity **elist,int n){ - int i; - vec2 vdist; - float qdist,f; - float qrad; - - if(!(e->flags&EntityFlag_Light)){ - Entity_SetColor(e, - e->light[0], - e->light[1], - e->light[2], - 1.0f); - }else{ - Entity_SetColor(e,1.0f,1.0f,1.0f,1.0f); - return; - } - - for(i=0;iflags&EntityFlag_Light)) - continue; - - vec2_minus(vdist,e->pos,elist[i]->pos); - qdist=vec2_dot(vdist,vdist); - qrad=elist[i]->light[3]*elist[i]->light[3]; - if(qdistlight[3]; - f=1.0f-qdist/qrad; - Entity_AddColor(e, - f*elist[i]->light[0], - f*elist[i]->light[1], - f*elist[i]->light[2], - 1.0f); - } - } -} - diff --git a/GameEnts.c b/GameEnts.c index d1d7e09..4d33c72 100644 --- a/GameEnts.c +++ b/GameEnts.c @@ -72,6 +72,9 @@ Entity *ent_arrow_right; Entity *ent_exitpoint; Entity *ent_endpoint; Entity *ent_savepoint; +Entity *ent_teleporter; +Entity *ent_teleporter_dest; + Entity *ent_fire; Entity *ent_player_broken; @@ -149,7 +152,7 @@ int player_collision(Entity *e1,Entity *e2,float t,vec2 n){ if(vlen>0.0f){ vec2_scale(vdir,e1->vel,1.0f/vlen); if(vec2_dot(vdir,n)>0.9){ - Entity_CollisionResponse(e1,e2,t,vdir); + Entity_CollisionResponseCircle(e1,e2,t,vdir); return(2); }else{ return(1); @@ -336,6 +339,29 @@ void timeoutent_proc(Entity *e,int ft){ } } +int teleporter_searchdest(Entity *ent,void *d){ + int a=*(int*)d; + if(ent->type!=Ent_Teleporter_Dest){ + return 0; + } + + if(ent->A==a){ + return 1; + } + return 0; +} + +void teleporter_overlap(Entity *e1,Entity *e2){ + Entity *dest=NULL; + + // Search the destination + dest=GameLib_SearchEnt(teleporter_searchdest,&e1->A); + + if(dest){ + vec2_copy(e2->pos,dest->pos); + } +} + void GameEnts_Init(){ Entity *ent; @@ -447,7 +473,7 @@ void GameEnts_Init(){ ent_player->type=Ent_Player; ent_player->radius=16.0f; ent_player->mass=70.0f; - ent_player->fric_static=0.5f; + ent_player->backFric_static=0.5f; ent_player->flags= EntityFlag_Collision|EntityFlag_Overlap|EntityFlag_Light; Entity_SetLight(ent_player,0.4f,0.4f,0.4f,3*32.0f); @@ -462,7 +488,7 @@ void GameEnts_Init(){ EntityFlag_Collision|EntityFlag_Overlap; ent_barrel->radius=16.0f; ent_barrel->mass=100.0f; - ent_barrel->fric_static=0.5f; + ent_barrel->backFric_static=0.5f; ent_barrel->proc=barrel_proc; AnimPlay_SetImg(&ent_barrel->anim,img_barrel); @@ -585,6 +611,25 @@ void GameEnts_Init(){ AnimPlay_SetImg(&ent_endpoint->anim,img_endpoint); ent_endpoint->overlap=endpoint_overlap; + ent_teleporter=Entity_Copy(ent); + ent_teleporter->zorder=0; + ent_teleporter->type=Ent_Teleporter; + ent_teleporter->flags=EntityFlag_Overlap|EntityFlag_Light; + Entity_SetLight(ent_teleporter,0.5f,0.5f,0.5f,5*32.0f); + ent_teleporter->radius=20; + AnimPlay_SetImg(&ent_teleporter->anim,img_savepoint); + ent_teleporter->overlap=teleporter_overlap; + + ent_teleporter_dest=Entity_Copy(ent); + ent_teleporter_dest->zorder=0; + ent_teleporter_dest->type=Ent_Teleporter_Dest; + ent_teleporter_dest->flags=0; + AnimPlay_SetImg(&ent_teleporter_dest->anim,img_savepoint); + + + + + ent_fire=Entity_Copy(ent); ent_fire->type=Ent_Effect; ent_fire->flags=EntityFlag_Light; diff --git a/GameEnts.h b/GameEnts.h index 1a86ba7..dea3ec9 100644 --- a/GameEnts.h +++ b/GameEnts.h @@ -17,6 +17,8 @@ enum { Ent_Arrow, Ent_SavePoint, Ent_ExitPoint, + Ent_Teleporter, + Ent_Teleporter_Dest, Ent_Effect } EntityType; extern Entity *ent_player; @@ -43,5 +45,7 @@ extern Entity *ent_arrow_right; extern Entity *ent_exitpoint; extern Entity *ent_endpoint; extern Entity *ent_savepoint; +extern Entity *ent_teleporter; +extern Entity *ent_teleporter_dest; #endif diff --git a/Anim.c b/GameLib/Anim.c similarity index 92% rename from Anim.c rename to GameLib/Anim.c index fa796fe..7b6b020 100644 --- a/Anim.c +++ b/GameLib/Anim.c @@ -125,6 +125,10 @@ void AnimPlay_SetImg(AnimPlay *ap,DrawImg img){ ap->time_ms=0; } void AnimPlay_SetAnim(AnimPlay *ap,Anim ani){ + ap->pause=0; + if(ap->anim==ani){ + return; + } ap->anim=ani; ap->img=NULL; ap->time_ms=0; @@ -168,13 +172,25 @@ void AnimPlay_GetSize(AnimPlay *ani,int *w,int *h){ } + +///////////////////////////// +// AnimPlay_SetPause +// +// +void AnimPlay_SetPause(AnimPlay *ani,int p){ + ani->pause=p; +} + + ///////////////////////////// // AnimPlay_IncTime // // void AnimPlay_IncTime(AnimPlay *ani,int t){ if(ani->anim){ - ani->time_ms+=t; + if(!ani->pause){ + ani->time_ms+=t; + } } } diff --git a/Anim.h b/GameLib/Anim.h similarity index 93% rename from Anim.h rename to GameLib/Anim.h index 2d42749..862fa83 100644 --- a/Anim.h +++ b/GameLib/Anim.h @@ -57,6 +57,7 @@ void Anim_Draw(Anim anim,int time_ms,int x,int y); typedef struct { Anim anim; DrawImg img; + int pause; int time_ms; } AnimPlay; @@ -93,6 +94,12 @@ void AnimPlay_GetOffset(AnimPlay *ani,int *x,int *y); void AnimPlay_GetSize(AnimPlay *ani,int *w,int *h); +///////////////////////////// +// AnimPlay_SetPause +// +// +void AnimPlay_SetPause(AnimPlay *ani,int p); + ///////////////////////////// // AnimPlay_IncTime // diff --git a/Audio.c b/GameLib/Audio.c similarity index 100% rename from Audio.c rename to GameLib/Audio.c diff --git a/Audio.h b/GameLib/Audio.h similarity index 100% rename from Audio.h rename to GameLib/Audio.h diff --git a/Draw.c b/GameLib/Draw.c similarity index 90% rename from Draw.c rename to GameLib/Draw.c index 2f121ce..fddf608 100644 --- a/Draw.c +++ b/GameLib/Draw.c @@ -33,14 +33,14 @@ SDL_Surface *_screen=NULL; int _width; int _height; -long long _t_frame=17000; -long long min_t_frame=16000; +long long proc_t_frame=33333; +long long draw_t_frame=16667; ///////////////////////////// // Draw_Init // // Initializes the game window. -int Draw_Init(int width,int height,char *title,int fps){ +int Draw_Init(int width,int height,char *title,int pfps,int fps){ #ifdef WIN32 // Stdout on the parent console AttachConsole(ATTACH_PARENT_PROCESS); @@ -81,7 +81,8 @@ int Draw_Init(int width,int height,char *title,int fps){ return(0); } SDL_WM_SetCaption(title, NULL); - _t_frame=1000000/fps; + proc_t_frame=1000000/pfps; + draw_t_frame=1000000/fps; _width=width; _height=height; @@ -137,17 +138,16 @@ void Draw_Loop(int (*proc)(),void (*draw)()){ int done=0; SDL_Event event; Uint8* keys; - long long time,time2; + long long time,time2,timed; long long t_frame=0; - t_frame=0; + t_frame=proc_t_frame; + time=Time_GetTime(); while(!done){ // Update screen - time=Time_GetTime(); SDL_GL_SwapBuffers(); - // Process Events while(SDL_PollEvent(&event) ){ if(event.type == SDL_QUIT ){ @@ -170,40 +170,38 @@ void Draw_Loop(int (*proc)(),void (*draw)()){ // Sound Frame Audio_Frame(); - // Measure time - time2=Time_GetTime(); - t_frame+=time2-time; - time=time2; - // FIX: Limit - if(t_frame>50000){ - t_frame=50000; - } - - // Process + // Process and draw if(proc){ - while(t_frame>_t_frame && !done){ - //while(t_frame>0 && !done){ + while(t_frame>=proc_t_frame && !done){ Input_Frame(); if(!proc()){ done=1; } - t_frame-=_t_frame; + t_frame-=proc_t_frame; } } - - // Draw - time2=Time_GetTime(); - draw(); - time2=Time_GetTime()-time2; - if(time250000){ + t_frame=50000; + } } } @@ -327,8 +325,10 @@ DrawImg Draw_LoadImage(char *filename){ image=malloc(sizeof(DrawImage)); image->surf=surf; image->tex=Draw_UploadGLTexture(surf); - image->x=0; - image->y=0; + //image->x=0; + //image->y=0; + image->x=-(surf->w/2); + image->y=-(surf->h/2); return((DrawImg)image); @@ -401,6 +401,39 @@ void Draw_DrawImg(DrawImg img,int x,int y){ } +///////////////////////////// +// Draw_DrawImgResized +// +// Draws an image, resizing. +void Draw_DrawImgResized(DrawImg img,int x,int y,float w,float h){ + DrawImage *image=img; + int x1,x2,y1,y2; + + // Prepare + x1=x+image->x; + y1=_height-(y+image->y); + x2=(x+image->x)+w; + y2=_height-((y+image->y)+h); + + // Draw a quad + glBindTexture(GL_TEXTURE_2D, image->tex); + glBegin (GL_QUADS); + glTexCoord2f (1, 0); + glVertex2i (x2, y1); + + glTexCoord2f (0, 0); + glVertex2i (x1, y1); + + glTexCoord2f (0, 1); + glVertex2i (x1, y2); + + glTexCoord2f (1, 1); + glVertex2i (x2, y2); + glEnd (); +} + + + ///////////////////////////// // Draw_DrawImgPart // diff --git a/Draw.h b/GameLib/Draw.h similarity index 90% rename from Draw.h rename to GameLib/Draw.h index eb62fbc..d54f141 100644 --- a/Draw.h +++ b/GameLib/Draw.h @@ -8,7 +8,7 @@ // Draw_Init // // Initializes the game window. -int Draw_Init(int width,int height,char *title,int fps); +int Draw_Init(int width,int height,char *title,int pfps,int fps); ///////////////////////////// @@ -65,6 +65,13 @@ void Draw_GetOffset(DrawImg img,int *x,int *y); void Draw_DrawImg(DrawImg img,int x,int y); +///////////////////////////// +// Draw_DrawImgResized +// +// Draws an image, resizing. +void Draw_DrawImgResized(DrawImg img,int x,int y,float w,float h); + + ///////////////////////////// // Draw_DrawImgPart // diff --git a/GameLib/Entity.c b/GameLib/Entity.c new file mode 100644 index 0000000..df016c3 --- /dev/null +++ b/GameLib/Entity.c @@ -0,0 +1,761 @@ +// Copyright (C) 2011 Valeriano Alfonso Rodriguez (Kableado) + +#include +#include +#include + +#include "Util.h" +#include "Draw.h" +#include "Anim.h" + +#include "Entity.h" + + + + +///////////////////////////// +// Entity_New +// +// +Entity *_free_entity=NULL; +Entity *Entity_New(){ + Entity *e; + + if(!_free_entity){ + e=malloc(sizeof(Entity)); + }else{ + e=_free_entity; + _free_entity=e->next; + } + + e->base=NULL; + e->type=0; + vec2_set(e->pos,0.0f,0.0f); + e->flags=EntityFlag_Collision|EntityFlag_Overlap; + e->zorder=1; + + vec2_set(e->dir,0.0f,0.0f); + + vec2_set(e->vel,0.0f,0.0f); + e->radius=1.0f; + e->width=1.0f; + e->height=1.0f; + e->mass=1.0f; + e->elast=0.0f; + e->backFric_static=0.0f; + e->backFric_dynamic=0.0f; + e->fric_static=0.0f; + e->fric_dynamic=0.0f; + + AnimPlay_SetImg(&e->anim,NULL); + + e->color[0]=e->color[1]=e->color[2]=e->color[3]=1.0f; + e->light[0]=e->light[1]=e->light[2]=e->light[3]=1.0f; + + e->oncopy=NULL; + e->ondelete=NULL; + e->proc=NULL; + e->postproc=NULL; + e->collision=NULL; + e->overlap=NULL; + + e->A=0; + e->B=0; + e->C=0; + e->D=0; + e->child=NULL; + + e->next=NULL; + + return(e); +} + + +///////////////////////////// +// Entity_Destroy +// +// +void Entity_Destroy(Entity *e){ + if(e->ondelete){ + e->ondelete(e); + } + e->next=_free_entity; + _free_entity=e; +} + + +///////////////////////////// +// Entity_Copy +// +// +Entity *Entity_Copy(Entity *e){ + Entity *n; + + n=Entity_New(); + + n->base=e; + n->type=e->type; + vec2_set(n->pos,e->pos[0],e->pos[1]); + n->flags=e->flags; + n->zorder=e->zorder; + + vec2_set(n->vel,e->vel[0],e->vel[1]); + n->radius=e->radius; + n->width=e->width; + n->height=e->height; + n->mass=e->mass; + n->elast=e->elast; + n->backFric_static=e->backFric_static; + n->backFric_dynamic=e->backFric_dynamic; + n->fric_static=e->fric_static; + n->fric_dynamic=e->fric_dynamic; + + AnimPlay_Copy(&n->anim,&e->anim); + n->color[0]=e->color[0]; + n->color[1]=e->color[1]; + n->color[2]=e->color[2]; + n->color[3]=e->color[3]; + n->light[0]=e->light[0]; + n->light[1]=e->light[1]; + n->light[2]=e->light[2]; + n->light[3]=e->light[3]; + + n->oncopy=e->oncopy; + n->ondelete=e->ondelete; + n->proc=e->proc; + n->postproc=e->postproc; + n->collision=e->collision; + n->overlap=e->overlap; + + n->A=e->A; + n->B=e->B; + n->C=e->C; + n->D=e->D; + n->child=e->child; + + // Call the copy event + if(n->oncopy){ + n->oncopy(n); + } + + return(n); +} + + +///////////////////////////// +// Entity_Draw +// +// +void Entity_Draw(Entity *e,int x,int y){ + Draw_SetColor(e->color[0],e->color[1],e->color[2],e->color[3]); + AnimPlay_Draw(&e->anim,e->pos[0]+x,e->pos[1]+y); +} + + + +///////////////////////////// +// Entity_IsVisible +// +// +int Entity_IsVisible(Entity *e,int x,int y,int w,int h){ + int xmax,xmin; + int ymax,ymin; + int ih,iw; + + AnimPlay_GetSize(&e->anim,&iw,&ih); + + xmin=x-iw; + xmax=x+w+iw; + ymin=y-ih; + ymax=y+h+ih; + + if(e->pos[0]pos[0]>xmax || + e->pos[1]pos[1]>ymax) + { + return(0); + } + return(1); +} + + + +///////////////////////////// +// Entity_Process +// +// +void Entity_Process(Entity *b,int ft){ + b->flags&=~EntityFlag_UpdatedPos; + + // Launch method + if(b->proc){ + b->proc(b,ft); + } +} + + +///////////////////////////// +// Entity_PostProcess +// +// +void Entity_PostProcess(Entity *e,int ft){ + float qlen,len; + + // Determine if there is movement + qlen=vec2_dot(e->vel,e->vel); + if(qlen>0.0f){ + + // Update position + vec2_plus(e->pos,e->pos,e->vel); + + // Apply friction + len=sqrtf(qlen); + if(lenbackFric_static){ + // Stopped by static friction + vec2_set(e->vel,0,0); + }else{ + // Apply dynamic friction + vec2_scale(e->vel,e->vel, + 1.0f-(e->backFric_dynamic+(e->backFric_static/len))); + } + + // Mark the update of the position. + vec2_copy(e->oldpos,e->pos); + e->flags|=EntityFlag_UpdatedPos; + } + + // Launch method + if(e->postproc){ + e->postproc(e,ft); + } + + // Animate + AnimPlay_IncTime(&e->anim,ft); +} + + +///////////////////////////// +// Entity_CollisionResponseClircle +// +// Normal response to a collision between circles. +void Entity_CollisionResponseCircle( + Entity *b1,Entity *b2,float t,vec2 n) +{ + float moment; + vec2 temp; + float elast; + + if(b1->mass>0.0f && b2->mass>0.0f){ + // Calculate elasticity + elast=(b1->mass*b1->elast+b2->mass*b2->elast)/ + (b1->mass+b2->mass); + + // Collision between two massed balls + moment=((1.0f+elast)*b1->mass*b2->mass* + (fabs(vec2_dot(b1->vel,n))+fabs(vec2_dot(b2->vel,n)))) + /(b1->mass+b2->mass); + vec2_scale(temp,n,moment/b1->mass); + vec2_minus(b1->vel,b1->vel,temp); + vec2_scale(temp,n,moment/b2->mass); + vec2_plus(b2->vel,b2->vel,temp); + }else + if(b1->mass>0.0f && b2->mass<=0.0f){ + // Collision between a massed ball and a fixed ball + moment=(1.0f+b1->elast)* + (vec2_dot(b1->vel,n)); + vec2_scale(temp,n,moment); + vec2_minus(b1->vel,b1->vel,temp); + }else + if(b1->mass<=0.0f && b2->mass>0.0f){ + // Collision between a massed ball and a fixed ball + // (imposible, but better safe) + moment=(1.0f+b2->elast)* + (vec2_dot(b2->vel,n)); + vec2_scale(temp,n,moment); + vec2_plus(b2->vel,b2->vel,temp); + }else{ + // Collision between 2 fixed balls + // (imposible, but better safe) + vec2_set(b1->vel,0,0); + vec2_set(b2->vel,0,0); + } +} + + +///////////////////////////// +// Entity_CollisionResponseLine +// +// Normal response to a collision with a line. +void Entity_CollisionResponseLine( + Entity *ent,Entity *ent2,float t,vec2 norm,int applyFriction) +{ + vec2 pos2,vel2,velFric,intersection; + float dist,fric_static,fric_dynamic,fricLen; + + // Calculate friction + fric_static=(ent->fric_static+ent2->fric_static)/2; + fric_dynamic=(ent->fric_dynamic+ent2->fric_dynamic)/2; + + // Calculate end position + vec2_scale(vel2,ent->vel,1.0f-t); + dist=-vec2_dot(norm,vel2); + vec2_plus(pos2,ent->pos,ent->vel); + vec2_scaleadd(pos2,pos2,norm,dist); + + // Calculate intersection + vec2_scaleadd(intersection,ent->pos,ent->vel,t); + + if(applyFriction){ + // Apply friction + vec2_minus(velFric,pos2,intersection); + fricLen=sqrtf(vec2_dot(velFric,velFric)); + if(fricLen0.0f){ + vec2_scaleadd(pos2,intersection,velFric, + 1.0f-(fric_dynamic+(fric_static/fricLen))); + }else{ + vec2_scaleadd(pos2,intersection,velFric, + 1.0f-fric_dynamic); + } + } + } + + // Apply to velocity + vec2_scaleadd(pos2,pos2,norm,0.1f); + vec2_minus(ent->vel,pos2,ent->pos); +} + + +///////////////////////////// +// Entity_Collide +// +// +int Entity_Collide(Entity *b1,Entity *b2){ + float t; + vec2 n,p; + vec2 vel; + int flags=b1->flags|b2->flags; + + if(flags&EntityFlag_Platform && !(flags&EntityFlag_Block)){ + // One of the entities is a platform and none is a block + Entity *ent,*ent_plat; + float plat_width; + vec2 p; + + // Decide who is the platform and who is the ent + if(b1->mass<=0.0f && b2->mass>0.0f){ + ent=b2; + ent_plat=b1; + }else + if(b2->mass<=0.0f && b1->mass>0.0f){ + ent=b1; + ent_plat=b2; + }else{ + // Two static or two dinamic entities?!? + return(0); + } + + + // Check Top + vec2_set(n,0,-1); + vec2_scaleadd(p,ent_plat->pos,n,(ent->height+ent_plat->height)/2); + plat_width=ent_plat->width+ent->width; + if(Intersect_RayEdge(ent->pos,ent->vel, + n,p,plat_width,&t)) + { + int response=1; + int rc; + + // Check the collision methods + if(ent->collision){ + rc=ent->collision(ent,ent_plat,t,n); + if (rc==0) + response=0; + if (rc>1) + response=2; + } + if(ent_plat->collision){ + vec2 n2; + vec2_scale(n2,n,-1.0f); + rc=ent_plat->collision(ent_plat,ent,t,n2); + if (rc==0) + response=0; + if (rc>1) + response=2; + } + + // Collision response + if(response==1){ + Entity_CollisionResponseLine(ent,ent_plat,t,n,1); + return(1); + } + if (response==2) { + return(1); + } + return(0); + } + + return(0); + } + + if(flags&EntityFlag_Block && !(flags&EntityFlag_Platform)){ + // One of the entities is a block and none is a platform + Entity *ent,*ent_block; + float auxT,block_len; + vec2 auxN,p; + int applyFriction; + + // Decide who is the block and who is the ent + if(b1->mass<=0.0f && b2->mass>0.0f){ + ent=b2; + ent_block=b1; + }else + if(b2->mass<=0.0f && b1->mass>0.0f){ + ent=b1; + ent_block=b2; + }else{ + // Two static or two dinamic entities?!? + return(0); + } + + // Prepare some variables + t=1.0f; + applyFriction=1; + + // Check Top + vec2_set(auxN,0,-1); + vec2_scaleadd(p,ent_block->pos,auxN,(ent->height+ent_block->height)/2); + block_len=ent_block->width+ent->width; + if(Intersect_RayEdge(ent->pos,ent->vel, + auxN,p,block_len,&auxT)) + { + if(auxTpos,auxN,(ent->height+ent_block->height)/2); + block_len=ent_block->width+ent->width; + if(Intersect_RayEdge(ent->pos,ent->vel, + auxN,p,block_len,&auxT)) + { + if(auxTpos,auxN,(ent->width+ent_block->width)/2); + block_len=ent_block->height+ent->height; + if(Intersect_RayEdge(ent->pos,ent->vel, + auxN,p,block_len,&auxT)) + { + if(auxTpos,auxN,(ent->width+ent_block->width)/2); + block_len=ent_block->height+ent->height; + if(Intersect_RayEdge(ent->pos,ent->vel, + auxN,p,block_len,&auxT)) + { + if(auxTcollision){ + rc=ent->collision(ent,ent_block,t,n); + if (rc==0) + response=0; + if (rc>1) + response=2; + } + if(ent_block->collision){ + vec2 n2; + vec2_scale(n2,n,-1.0f); + rc=ent_block->collision(ent_block,ent,t,n2); + if (rc==0) + response=0; + if (rc>1) + response=2; + } + + // Collision response + if(response==1){ + Entity_CollisionResponseLine(ent,ent_block,t,n,applyFriction); + return(1); + } + if (response==2) { + return(1); + } + return(0); + } + + return(0); + } + + + // Test relative to b1 + vec2_minus(vel,b1->vel,b2->vel); + if(Colision_CircleCircle(b1->pos,b1->radius,vel,b2->pos,b2->radius,&t,n)){ + int response=1; + int rc; + vec2 n2; + vec2_scale(n2,n,-1.0f); + + // Check the collision methods + if(b1->collision){ + rc=b1->collision(b1,b2,t,n2); + if (rc==0) + response=0; + if (rc>1) + response=2; + } + if(b2->collision){ + rc=b2->collision(b2,b1,t,n); + if (rc==0) + response=0; + if (rc>1) + response=2; + } + + // Collision response + if(response==1){ + if(vec2_dot(b1->vel,b1->vel)>vec2_dot(b2->vel,b2->vel)){ + Entity_CollisionResponseCircle(b1,b2,t,n); + }else{ + Entity_CollisionResponseCircle(b2,b1,t,n); + } + return(1); + } + if (response==2) { + return(1); + } + return(0); + } + return(0); +} + + +///////////////////////////// +// Entity_Overlaps +// +// +void Entity_Overlaps(Entity *b1,Entity *b2){ + vec2 len; + + vec2_minus(len,b1->pos,b2->pos); + + vec2_set(len,fabs(b1->pos[0]-b2->pos[0]),fabs(b1->pos[1]-b2->pos[1])); + if(b1->overlap){ + if( len[0]<=b1->radius && + len[1]<=b1->radius) + { + b1->overlap(b1,b2); + } + } + if(b2->overlap){ + if( len[0]<=b2->radius && + len[1]<=b2->radius) + { + b2->overlap(b2,b1); + } + } +} + + +///////////////////////////// +// Entity_GetPos +// +// +void Entity_GetPos(Entity *e,vec2 pos){ + vec2_copy(pos,e->pos); +} + +///////////////////////////// +// Entity_UpdatePos +// +// +void Entity_UpdatePos(Entity *e,vec2 pos){ + + // Mark the update of the position. + vec2_copy(e->oldpos,e->pos); + e->flags|=EntityFlag_UpdatedPos; + + vec2_copy(e->pos,pos); +} + +///////////////////////////// +// Entity_AddVelLimit +// +// +void Entity_AddVelLimit(Entity *e,vec2 vel,float limit){ + float vlen_orig,vlen; + vec2 dir,vel_temp; + + // Normalize vel getting vel + vlen_orig=sqrtf(vec2_dot(vel,vel)); + vec2_scale(dir,vel,1.0f/vlen_orig); + + // Limit velocity + vlen=vec2_dot(e->vel,dir); + if(vlenvlen_orig){ + vlen=vlen_orig; + } + vec2_scale(vel_temp,dir,vlen); + vec2_plus(e->vel,e->vel,vel_temp); + } +} + + +///////////////////////////// +// Entity_SetColor +// +// +void Entity_SetColor(Entity *e,float r,float g,float b,float a){ + e->color[0]=r; + e->color[1]=g; + e->color[2]=b; + e->color[3]=a; +} + + +///////////////////////////// +// Entity_AddColor +// +// +void Entity_AddColor(Entity *e,float r,float g,float b,float a){ + e->color[0]+=r; + if(e->color[0]>1.0f) + e->color[0]=1.0f; + e->color[1]+=g; + if(e->color[1]>1.0f) + e->color[1]=1.0f; + e->color[2]+=b; + if(e->color[2]>1.0f) + e->color[2]=1.0f; + e->color[3]+=a; + if(e->color[3]>1.0f) + e->color[3]=1.0f; +} + + +///////////////////////////// +// Entity_SetLight +// +// +void Entity_SetLight(Entity *e,float r,float g,float b,float rad){ + e->light[0]=r; + e->light[1]=g; + e->light[2]=b; + e->light[3]=rad; +} + + + +///////////////////////////// +// Entity_Iluminate +// +// +void Entity_Iluminate(Entity *e,Entity **elist,int n){ + int i; + vec2 vdist; + float qdist,f; + float qrad; + + if(!(e->flags&EntityFlag_Light)){ + Entity_SetColor(e, + e->light[0], + e->light[1], + e->light[2], + 1.0f); + }else{ + Entity_SetColor(e,1.0f,1.0f,1.0f,1.0f); + return; + } + + for(i=0;iflags&EntityFlag_Light)) + continue; + + vec2_minus(vdist,e->pos,elist[i]->pos); + qdist=vec2_dot(vdist,vdist); + qrad=elist[i]->light[3]*elist[i]->light[3]; + if(qdistlight[0], + f*elist[i]->light[1], + f*elist[i]->light[2], + 1.0f); + } + } +} + + +///////////////////////////// +// Entity_MarkUpdateLight +// +// +void Entity_MarkUpdateLight(Entity *e,Entity **elist,int n){ + if(e->flags&EntityFlag_Light){ + int i; + vec2 max,min; + + if(e->pos[0]oldpos[0]){ + min[0]=e->pos[0]-e->light[3]; + max[0]=e->oldpos[0]+e->light[3]; + }else{ + min[0]=e->oldpos[0]-e->light[3]; + max[0]=e->pos[0]+e->light[3]; + } + if(e->pos[1]oldpos[1]){ + min[1]=e->pos[1]-e->light[3]; + max[1]=e->oldpos[1]+e->light[3]; + }else{ + min[1]=e->oldpos[1]-e->light[3]; + max[1]=e->pos[1]+e->light[3]; + } + for(i=0;ipos[0] && + max[0]>=elist[i]->pos[0] && + min[1]<=elist[i]->pos[1] && + max[1]>=elist[i]->pos[1]) + { + elist[i]->flags|=EntityFlag_UpdateLight; + } + } + }else{ + e->flags|=EntityFlag_UpdateLight; + } +} + + diff --git a/Entity.h b/GameLib/Entity.h similarity index 66% rename from Entity.h rename to GameLib/Entity.h index 23fec96..7a044b3 100644 --- a/Entity.h +++ b/GameLib/Entity.h @@ -7,29 +7,40 @@ #include "Draw.h" #include "Anim.h" -#define EntityFlag_Collision 1 -#define EntityFlag_Overlap 2 -#define EntityFlag_Light 4 -#define EntityFlag_UpdateLight 8 -#define EntityFlag_UpdatedPos 16 - //////////////////////////////////////////////// // Entity // //////////// // +#define EntityFlag_Collision 1 +#define EntityFlag_Platform 2 +#define EntityFlag_Block 4 +#define EntityFlag_PlatformCollision 3 +#define EntityFlag_BlockCollision 5 +#define EntityFlag_Overlap 8 +#define EntityFlag_Light 16 +#define EntityFlag_UpdateLight 32 +#define EntityFlag_UpdatedPos 64 typedef struct Tag_Entity { + struct Tag_Entity *base; int type; + vec2 oldpos; vec2 pos; int flags; int zorder; + vec2 dir; + vec2 vel; vec2 bod_offset; float radius; + float width; + float height; float mass; float elast; + float backFric_static; + float backFric_dynamic; float fric_static; float fric_dynamic; @@ -51,6 +62,9 @@ typedef struct Tag_Entity { struct Tag_Entity *ent2); int A; + int B; + int C; + int D; struct Tag_Entity *child; void *next; @@ -84,6 +98,11 @@ Entity *Entity_Copy(Entity *e); // void Entity_Draw(Entity *e,int x,int y); +///////////////////////////// +// Entity_IsVisible +// +// +int Entity_IsVisible(Entity *e,int x,int y,int w,int h); ///////////////////////////// // Entity_Process @@ -99,13 +118,21 @@ void Entity_PostProcess(Entity *e,int ft); ///////////////////////////// -// Entity_CollisionResponse +// Entity_CollisionResponseClircle // -// Normal response to a collision. -void Entity_CollisionResponse( +// Normal response to a collision of spheres. +void Entity_CollisionResponseCircle( Entity *b1,Entity *b2,float t,vec2 n); +///////////////////////////// +// Entity_CollisionResponseLine +// +// Normal response to a collision with a line. +void Entity_CollisionResponseLine( + Entity *ent,Entity *ent2,float t,vec2 n,int applyFriction); + + ///////////////////////////// // Entity_Collide // @@ -120,6 +147,20 @@ int Entity_Collide(Entity *b1,Entity *b2); void Entity_Overlaps(Entity *b1,Entity *b2); +///////////////////////////// +// Entity_GetPos +// +// +void Entity_GetPos(Entity *e,vec2 pos); + + +///////////////////////////// +// Entity_UpdatePos +// +// +void Entity_UpdatePos(Entity *e,vec2 pos); + + ///////////////////////////// // Entity_AddVelLimit // @@ -153,6 +194,11 @@ void Entity_SetLight(Entity *e,float r,float g,float b,float rad); // void Entity_Iluminate(Entity *e,Entity **elist,int n); +///////////////////////////// +// Entity_MarkUpdateLight +// +// +void Entity_MarkUpdateLight(Entity *e,Entity **elist,int n); #endif diff --git a/FontData.h b/GameLib/FontData.h similarity index 100% rename from FontData.h rename to GameLib/FontData.h diff --git a/GameLib.c b/GameLib/GameLib.c similarity index 86% rename from GameLib.c rename to GameLib/GameLib.c index 179ba54..42635ae 100644 --- a/GameLib.c +++ b/GameLib/GameLib.c @@ -23,6 +23,7 @@ int _entities_lock=0; int _entities_compactate=0; void (*_gameproc)()=NULL; void (*_gamepostproc)()=NULL; +void (*_gamepredraw)()=NULL; void (*_gamedraw)()=NULL; int _ft; int _game_size[2]; @@ -43,8 +44,8 @@ int gamelib_debug=0; // GameLib_Init // // Initializes the game. -int GameLib_Init(int w,int h,char *title,int fps){ - if(!Draw_Init(w,h,title,fps)){ +int GameLib_Init(int w,int h,char *title,int pfps,int fps){ + if(!Draw_Init(w,h,title,pfps,fps)){ return(0); } if(!Input_Init()){ @@ -98,7 +99,7 @@ void GameLib_AddEntity(Entity *e){ _n_entities++; // Mark for light update - GameLib_EntityUpdateLight(e); + Entity_MarkUpdateLight(e,_entity,_n_entities); } @@ -120,7 +121,7 @@ int GameLib_UnrefEntity(Entity *e){ _entities_compactate=1; // Mark for light update - GameLib_EntityUpdateLight(e); + Entity_MarkUpdateLight(e,_entity,_n_entities); return(i); } } @@ -290,7 +291,7 @@ int GameLib_ProcLoop(){ for(i=0;i<_n_entities;i++){ Entity_PostProcess(_entity[i],_ft); if(_entity[i]->flags&EntityFlag_UpdatedPos){ - GameLib_EntityUpdateLight(_entity[i]); + Entity_MarkUpdateLight(_entity[i],_entity,_n_entities); } } if(_gamepostproc){ @@ -318,24 +319,28 @@ void GameLib_DrawLoop(){ // Update Lights //GameLib_UpdateIlumination(); - // Limpiar pantalla - Draw_Clean(0,0,0); + + + // Predibujado + if(_gamepredraw){ + _gamepredraw(); + }else{ + // Limpiar pantalla + Draw_Clean(0,0,0); + } // Draw entities GameLib_Compactate();_entities_lock=1; for(i=0;i<_n_entities;i++){ - Entity *e; + Entity *e=_entity[i]; - // FIXME: This is a hack - e=_entity[i]; - if(e->pos[0]<(_game_pos[0]-128)) - continue; - if(e->pos[0]>(_game_pos[0]+_game_size[0]+128)) - continue; - if(e->pos[1]<(_game_pos[1]-128)) - continue; - if(e->pos[1]>(_game_pos[1]+_game_size[1]+128)) + // Check visivility + if(!Entity_IsVisible(e, + _game_pos[0],_game_pos[1], + _game_size[0],_game_size[1])) + { continue; + } // Update ilumination of this entity if(e->flags&EntityFlag_UpdateLight){ @@ -345,6 +350,7 @@ void GameLib_DrawLoop(){ Entity_Draw(e,-_game_pos[0],-_game_pos[1]); } + Draw_SetColor(1,1,1,1); if(_gamedraw){ _gamedraw(); } @@ -364,12 +370,14 @@ void GameLib_DrawLoop(){ void GameLib_Loop( void (*gameproc)(), void (*gamepostproc)(), + void (*gamepredraw)(), void (*gamedraw)()) { _running=1; _gameproc=gameproc; _gamepostproc=gamepostproc; + _gamepredraw=gamepredraw; _gamedraw=gamedraw; t_proc=0; t_col=0; @@ -420,6 +428,28 @@ void GameLib_GetSize(int size[2]){ } + + +///////////////////////////// +// GameLib_MoveToPos +// GameLib_MoveToPosH +// GameLib_MoveToPosV +// +// +void GameLib_MoveToPos(vec2 pos,float f){ + GameLib_MoveToPosH(pos,f); + GameLib_MoveToPosV(pos,f); +} +void GameLib_MoveToPosH(vec2 pos,float f){ + _game_pos[0]=_game_pos[0]+(pos[0]-(_game_pos[0]+(_game_size[0]/2.0f)))*f; +} +void GameLib_MoveToPosV(vec2 pos,float f){ + _game_pos[1]=_game_pos[1]+(pos[1]-(_game_pos[1]+(_game_size[1]/2.0f)))*f; +} + + + + ///////////////////////////// // GameLib_ForEachEn // @@ -452,6 +482,24 @@ void GameLib_ForEachEnt(int (*func)(Entity *ent)){ } +///////////////////////////// +// GameLib_SearchEnt +// +// Searches throught the entities. +Entity *GameLib_SearchEnt(int (*func)(Entity *ent,void *d),void *d){ + int i; + Entity *ent=NULL; + for(i=0;i<_n_entities;i++){ + if(!_entity[i]) + continue; + if(func(_entity[i],d)){ + ent=_entity[i]; + break; + } + } + return ent; +} + ///////////////////////////// // GameLib_PlaySound // @@ -512,9 +560,9 @@ void GameLib_Iluminate(){ // void GameLib_EntitySetLight(Entity *e,float r,float g,float b,float rad){ if(e->flags&EntityFlag_Light){ - GameLib_EntityUpdateLight(e); + Entity_MarkUpdateLight(e,_entity,_n_entities); Entity_SetLight(e,r,g,b,rad); - GameLib_EntityUpdateLight(e); + Entity_MarkUpdateLight(e,_entity,_n_entities); }else{ Entity_SetLight(e,r,g,b,rad); e->flags|=EntityFlag_UpdateLight; @@ -522,32 +570,6 @@ void GameLib_EntitySetLight(Entity *e,float r,float g,float b,float rad){ } -///////////////////////////// -// GameLib_EntityUpdateLight -// -// -void GameLib_EntityUpdateLight(Entity *e){ - if(e->flags&EntityFlag_Light){ - int i; - vec2 max,min; - - vec2_set(max,e->pos[0]+e->light[3],e->pos[1]+e->light[3]); - vec2_set(min,e->pos[0]-e->light[3],e->pos[1]-e->light[3]); - for(i=0;i<_n_entities;i++){ - if( min[0]<=_entity[i]->pos[0] && - max[0]>=_entity[i]->pos[0] && - min[1]<=_entity[i]->pos[1] && - max[1]>=_entity[i]->pos[1]) - { - _entity[i]->flags|=EntityFlag_UpdateLight; - } - } - }else{ - e->flags|=EntityFlag_UpdateLight; - } -} - - ///////////////////////////// // GameLib_UpdateIlumination // diff --git a/GameLib.h b/GameLib/GameLib.h similarity index 80% rename from GameLib.h rename to GameLib/GameLib.h index 1230ef3..7040c1b 100644 --- a/GameLib.h +++ b/GameLib/GameLib.h @@ -16,7 +16,7 @@ // GameLib_Init // // Initializes the game. -int GameLib_Init(int w,int h,char *title,int fps); +int GameLib_Init(int w,int h,char *title,int pfps,int fps); ///////////////////////////// @@ -47,6 +47,7 @@ int GameLib_DelEntity(Entity *e); void GameLib_Loop( void (*gameproc)(), void (*gamepostproc)(), + void (*gamepredraw)(), void (*gamedraw)()); @@ -68,6 +69,17 @@ void GameLib_SetPos(int pos[2]); void GameLib_GetSize(int size[2]); +///////////////////////////// +// GameLib_MoveToPos +// GameLib_MoveToPosH +// GameLib_MoveToPosV +// +// +void GameLib_MoveToPos(vec2 pos,float f); +void GameLib_MoveToPosH(vec2 pos,float f); +void GameLib_MoveToPosV(vec2 pos,float f); + + ///////////////////////////// // GameLib_ForEachEn // @@ -76,12 +88,19 @@ void GameLib_DelEnts(); ///////////////////////////// -// GameLib_ForEachEn +// GameLib_ForEachEnt // // Iterates every entity. void GameLib_ForEachEnt(int (*func)(Entity *ent)); +///////////////////////////// +// GameLib_SearchEnt +// +// Searches throught the entities. +Entity *GameLib_SearchEnt(int (*func)(Entity *ent,void *d),void *d); + + ///////////////////////////// // GameLib_PlaySound // @@ -103,13 +122,6 @@ void GameLib_Iluminate(); void GameLib_EntitySetLight(Entity *e,float r,float g,float b,float rad); -///////////////////////////// -// GameLib_EntityUpdateLight -// -// -void GameLib_EntityUpdateLight(Entity *e); - - ///////////////////////////// // GameLib_UpdateIlumination // diff --git a/Input.c b/GameLib/Input.c similarity index 100% rename from Input.c rename to GameLib/Input.c diff --git a/Input.h b/GameLib/Input.h similarity index 100% rename from Input.h rename to GameLib/Input.h diff --git a/Time.c b/GameLib/Time.c similarity index 97% rename from Time.c rename to GameLib/Time.c index a1c9af8..7566670 100644 --- a/Time.c +++ b/GameLib/Time.c @@ -5,7 +5,6 @@ #include #include #include -#include #include "Time.h" @@ -38,7 +37,7 @@ void Time_Pause(int pausa){ do{ diff=tend-t; if(diff>1000){ - SDL_Delay(diff/1000); + Sleep(diff/1000); }else{ Sleep(0); } diff --git a/Time.h b/GameLib/Time.h similarity index 100% rename from Time.h rename to GameLib/Time.h diff --git a/Util.c b/GameLib/Util.c similarity index 60% rename from Util.c rename to GameLib/Util.c index 6b2844c..2939389 100644 --- a/Util.c +++ b/GameLib/Util.c @@ -4,6 +4,17 @@ #include "Util.h" +//////////////////////////////////////////////// +// vec2 // +////////// +// A 2D vector. +float vec2_norm(vec2 v){ + float len; + len=vec2_len(v); + vec2_scale(v,v,1.0f/len); + return(len); +} + ///////////////////////////// // SolveQuadratic @@ -30,6 +41,7 @@ int SolveQuadratic(float a,float b,float c,float *Rmin,float *Rmax){ return(1); } + ///////////////////////////// // Intersec_RayUnitCircle // @@ -57,7 +69,7 @@ int Intersec_RayUnitCircle(vec2 orig,vec2 vel,vec2 center,float *t){ *t=Rmin; return(1); } - if(Rmax>=-1.0f && Rmin>Rmax && Rmax<=1.0f){ + if(Rmax>=-0.0f && Rmin>Rmax && Rmax<=1.0f){ *t=Rmax; return(1); } @@ -102,7 +114,6 @@ int Colision_CircleCircle( return(0); // Convert to a unit circle vs ray - rads=rad1+rad2; invrads=1.0f/rads; vec2_scale(vel_a,vel,invrads); vec2_scale(orig_a,cir1,invrads); @@ -118,3 +129,75 @@ int Colision_CircleCircle( return(0); } + +///////////////////////////// +// Intersect_RayEdge +// +// Intersection between a ray and a edge. +int Intersect_RayEdge( + vec2 pos,vec2 vel, + vec2 norm,vec2 edgePos,float len, + float *t) +{ + vec2 pos2,intersection,perp,edgePos2; + float delta,d1,d2,hLen; + + vec2_plus(pos2,pos,vel); + hLen=len/2; + + // Check intersection against the line + delta=vec2_dot(norm,edgePos); + d1=vec2_dot(pos ,norm)-delta; + d2=vec2_dot(pos2,norm)-delta; + if(d1>=-0.0001f && d2<=0.0001f){ + // Intersection with line, Calculate intersection point + *t=d1/(d1-d2); + vec2_scaleadd(intersection,pos,vel,*t); + + // Perpendicular + vec2_perp(perp,norm); + + // Check sides + vec2_scaleadd(edgePos2,edgePos,perp,-hLen); + delta=-vec2_dot(perp,edgePos2); + d1=(-vec2_dot(perp,intersection))-delta; + + vec2_scaleadd(edgePos2,edgePos,perp,hLen); + delta=vec2_dot(perp,edgePos2); + d2=vec2_dot(perp,intersection)-delta; + + if(d1<=0.0f && d2<=0.0f){ + // Intersection inside Edge. + return(1); + } + } + return(0); +} + + + + +///////////////////////////// +// absmod +// +int absmod(int v,int d){ + if(v<0){ + v+=d*(((v/d)*(-1))+1); + return(v); + }else{ + return(v%d); + } +} + +float fabsmod(float v,int d){ + if(v<0){ + v+=d*((((int)(v/d))*(-1))+1); + return(v); + }else{ + v-=d*(((int)(v/d))+1); + return(v); + } +} + + + diff --git a/Util.h b/GameLib/Util.h similarity index 69% rename from Util.h rename to GameLib/Util.h index cb8a31c..1a6a74e 100644 --- a/Util.h +++ b/GameLib/Util.h @@ -22,6 +22,10 @@ typedef float vec2[2]; #define vec2_minus(v,v1,v2) (v)[0]=(v1)[0]-(v2)[0];(v)[1]=(v1)[1]-(v2)[1]; #define vec2_scale(v,v1,s) (v)[0]=(v1)[0]*(s);(v)[1]=(v1)[1]*(s); #define vec2_dot(v1,v2) ((v1)[0]*(v2)[0]+(v1)[1]*(v2)[1]) +#define vec2_len(v) sqrtf((v)[0]*(v)[0]+(v)[1]*(v)[1]) +#define vec2_perp(v,n) (v)[0]=-(n)[1];(v)[1]=(n)[0]; +#define vec2_scaleadd(v,v1,v2,s) (v)[0]=(v2)[0]*(s)+(v1)[0];(v)[1]=(v2)[1]*(s)+(v1)[1]; +float vec2_norm(vec2 v); ///////////////////////////// @@ -30,7 +34,6 @@ typedef float vec2[2]; // Intersection between a ray and a Unit Circle. int Intersec_RayUnitCircle(vec2 orig,vec2 vel,vec2 center,float *t); - ///////////////////////////// // Intersect_CircleCircle // @@ -40,5 +43,22 @@ int Colision_CircleCircle( vec2 cb,float rb, float *t,vec2 n); +///////////////////////////// +// Intersect_RayEdge +// +// Intersection between a ray and a edge. +int Intersect_RayEdge( + vec2 pos,vec2 vel, + vec2 norm,vec2 edgePos,float len, + float *t); + + + +///////////////////////////// +// absmod +// +int absmod(int v,int d); +float fabsmod(float v,int d); + #endif diff --git a/GameMap.c b/GameMap.c index ddfc91c..87bae53 100644 --- a/GameMap.c +++ b/GameMap.c @@ -65,6 +65,8 @@ int GameMapAux_IsFloor(char c){ c=='<' || c=='>' || c=='r' || + c=='T' || + c=='D' || c=='l' ) { return(1); @@ -185,6 +187,16 @@ int GameMap_CreateLevel(int level,int point){ // ArrowShooter up GameMapAux_CreateEnt(ent_arrowshooter_up,i,j); }else + if(line[i2]=='T'){ + // Teleporter + Entity *ent=GameMapAux_CreateEnt(ent_teleporter,i,j); + ent->A=line[i2+1]-'0'; + }else + if(line[i2]=='D'){ + // Teleporter Destination + Entity *ent=GameMapAux_CreateEnt(ent_teleporter_dest,i,j); + ent->A=line[i2+1]-'0'; + }else {} } diff --git a/Lonely_Ruins.20120228.zip b/Lonely_Ruins.20120309.zip similarity index 90% rename from Lonely_Ruins.20120228.zip rename to Lonely_Ruins.20120309.zip index 51c17f1626cb77e0ba8c23dc4d8a97fcf35fb239..77445f68489b67912fd5c53aee5f1e6918a177c2 100644 GIT binary patch delta 55679 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