tanks.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609
  1. // Scoring <max bullets , no collision between tanks /
  2. #include "raylib.h"
  3. #include <math.h>
  4. #define MAXCOMMANDS 10
  5. #define MAXENTITIES 10
  6. #define MAXBULLETS 256
  7. #define MAXDECALS 256
  8. Texture2D sprites;
  9. Texture2D tiles;
  10. typedef struct decal{
  11. bool active;
  12. Vector2 position;
  13. int tilesindex;
  14. }decal;
  15. static struct decal dec[MAXDECALS];
  16. typedef struct bullet{
  17. bool active;
  18. Vector2 position;
  19. Vector2 inc;
  20. int countdown;
  21. }bullet;
  22. static struct bullet bul[MAXBULLETS];
  23. typedef struct entity{
  24. bool active;
  25. int collisionskip;
  26. int command[MAXCOMMANDS];
  27. int value[MAXCOMMANDS];
  28. int command2[MAXCOMMANDS];
  29. int value2[MAXCOMMANDS];
  30. Vector2 position;
  31. int maxcommand;
  32. int maxcommand2;
  33. float angle;
  34. float angle2;
  35. int valuecount;
  36. int valuecount2;
  37. int pos;
  38. int pos2;
  39. int time;
  40. int timemax;
  41. int time2;
  42. int timemax2;
  43. }entity;
  44. static struct entity ent[MAXENTITIES];
  45. void getrandomcommands(int entity);
  46. void updateentities();
  47. void advancedcollision();
  48. void entitiescollision();
  49. void drawentities();
  50. void inientity(int entity,int x, int y);
  51. void updatebullets();
  52. void drawbullets();
  53. void shootbullet(Vector2 position,float angle);
  54. float distance(float x1,float y1,float x2,float y2);
  55. float getangle(float x1,float y1,float x2,float y2);
  56. float angledifference(float angle1, float angle2);
  57. void drawmap();
  58. void drawtile(int tile, int x, int y);
  59. void createmap();
  60. void drawdecals();
  61. void inidecals();
  62. int debug;
  63. const int screenWidth = 800;
  64. const int screenHeight = 600;
  65. int map[150][150] = {0};
  66. int main(void)
  67. {
  68. // Initialization
  69. //--------------------------------------------------------------------------------------
  70. InitWindow(screenWidth, screenHeight, "raylib [core] example - basic window");
  71. sprites = LoadTexture("resources/sprites.png");
  72. tiles = LoadTexture("resources/tiles.png");
  73. createmap();
  74. inidecals();
  75. SetTargetFPS(60); // Set our game to run at 60 frames-per-second
  76. //--------------------------------------------------------------------------------------
  77. for(int i=0;i<MAXBULLETS;i++){
  78. bul[i].active=false;
  79. bul[i].position=(Vector2){320,200};
  80. }
  81. for(int i=0;i<MAXENTITIES;i++){
  82. ent[i].active=false;
  83. }
  84. int x=200;
  85. int y=200;
  86. for(int i=0;i<MAXENTITIES;i++){
  87. inientity(i,x,y);
  88. x+=64;
  89. if(x>500){
  90. y+=64;
  91. x=200;
  92. }
  93. }
  94. // Main game loop
  95. while (!WindowShouldClose()) // Detect window close button or ESC key
  96. {
  97. // Update
  98. //----------------------------------------------------------------------------------
  99. // TODO: Update your variables here
  100. //----------------------------------------------------------------------------------
  101. updateentities();
  102. //entitiescollision();
  103. advancedcollision();
  104. updatebullets();
  105. //if(GetRandomValue(0,20)==1)shootbullet((Vector2){320,200},GetRandomValue(0,360));
  106. // Draw
  107. //----------------------------------------------------------------------------------
  108. BeginDrawing();
  109. ClearBackground(RAYWHITE);
  110. drawmap();
  111. drawdecals();
  112. drawentities();
  113. drawbullets();
  114. //DrawText(FormatText("%i",debug), 0, 0, 20, BLACK);
  115. EndDrawing();
  116. //----------------------------------------------------------------------------------
  117. }
  118. // De-Initialization
  119. UnloadTexture(sprites);
  120. UnloadTexture(tiles);
  121. //--------------------------------------------------------------------------------------
  122. CloseWindow(); // Close window and OpenGL context
  123. //--------------------------------------------------------------------------------------
  124. return 0;
  125. }
  126. // Set up some decals, these are the holes left by explosions.
  127. void inidecals(){
  128. // i<64 = the amount of holes in the ground.
  129. for(int i=0;i<64;i++){
  130. if(dec[i].active==true)continue;
  131. dec[i].active=true;
  132. dec[i].position.x=GetRandomValue(16,screenWidth);
  133. dec[i].position.y=GetRandomValue(16,screenHeight);
  134. if(map[(int)dec[i].position.x/32][(int)dec[i].position.y/32]==8){
  135. dec[i].tilesindex = 29;
  136. if(GetRandomValue(0,10)<5)dec[i].tilesindex++;
  137. }else{
  138. dec[i].tilesindex = 27;
  139. if(GetRandomValue(0,10)<5)dec[i].tilesindex++;
  140. }
  141. }
  142. }
  143. void drawdecals(){
  144. for(int i=0;i<MAXDECALS;i++){
  145. if(dec[i].active==false)continue;
  146. // Get the tile x , y position.
  147. int ty = dec[i].tilesindex / 9;
  148. int tx = dec[i].tilesindex-(ty*9);
  149. DrawTexturePro(tiles, (Rectangle){tx*16,ty*16,16,16},// the -96 (-)means mirror on x axis
  150. (Rectangle){dec[i].position.x,dec[i].position.y,32,32},
  151. (Vector2){0,0},0,WHITE);
  152. }
  153. }
  154. void createmap(){
  155. // Generate a kind of basic map
  156. for(int y=0;y<screenHeight/32+1;y++){
  157. for(int x=0;x<screenWidth/32;x++){
  158. map[x][y]=9;
  159. }}
  160. for(int y=0;y<10;y++){
  161. for(int x=0;x<10;x++){
  162. map[x][y]=11;
  163. }}
  164. for(int y=0;y<50;y++){
  165. for(int x=0;x<5;x++){
  166. map[x][y]=11;
  167. }}
  168. for(int y=0;y<5;y++){
  169. for(int x=0;x<50;x++){
  170. map[x][y]=11;
  171. }}
  172. // Auto tile it!
  173. for(int y=1;y<50;y++){
  174. for(int x=1;x<50;x++){
  175. if(map[x][y]==11){
  176. if(map[x+1][y]==11 && map[x][y+1]==11 && map[x+1][y+1]==9){
  177. map[x+1][y+1]=4;
  178. }
  179. }
  180. if(map[x][y]==11){
  181. if(map[x+1][y]==9 && map[x][y+1]==9 && map[x+1][y+1]==9){
  182. map[x+1][y+1]=21;//31;
  183. }
  184. }
  185. }}
  186. for(int y=1;y<50;y++){
  187. for(int x=1;x<50;x++){
  188. if(map[x][y]==11){
  189. if(map[x][y+1]==9){
  190. map[x][y+1]=5;
  191. }
  192. if(map[x+1][y]==9){
  193. map[x+1][y]=12;
  194. }
  195. }
  196. }}
  197. }
  198. void drawmap(){
  199. for(int y=0;y<screenHeight/32+1;y++){
  200. for(int x=0;x<screenWidth/32;x++){
  201. drawtile(map[x][y],x*32,y*32);
  202. }
  203. }
  204. }
  205. void drawtile(int tile, int x, int y){
  206. // Get the tile x , y position.
  207. int ty = tile / 9;
  208. int tx = tile-(ty*9);;
  209. DrawTexturePro(tiles, (Rectangle){tx*16,ty*16,16,16},// the -96 (-)means mirror on x axis
  210. (Rectangle){x,y,32,32},
  211. (Vector2){0,0},0,WHITE);
  212. }
  213. void shootbullet(Vector2 position, float angle){
  214. for(int i=0;i<MAXBULLETS;i++){
  215. if(bul[i].active==false){
  216. bul[i].active = true;
  217. bul[i].position = position;
  218. bul[i].inc.x = (cos(angle*DEG2RAD));
  219. bul[i].inc.y = (sin(angle*DEG2RAD));
  220. bul[i].inc.x *=3;
  221. bul[i].inc.y *=3;
  222. bul[i].position.x += cos(angle*DEG2RAD)*16;
  223. bul[i].position.y += sin(angle*DEG2RAD)*16;
  224. bul[i].countdown = 350;
  225. return;
  226. }
  227. }
  228. }
  229. void updatebullets(){
  230. for(int i=0;i<MAXBULLETS;i++){
  231. if(bul[i].active==false)continue;
  232. bul[i].position.x += bul[i].inc.x;
  233. bul[i].position.y += bul[i].inc.y;
  234. bul[i].countdown--;
  235. if(bul[i].countdown<0)bul[i].active=false;
  236. }
  237. }
  238. void drawbullets(){
  239. for(int i=0;i<MAXBULLETS;i++){
  240. if(bul[i].active==false)continue;
  241. DrawTexturePro(sprites, (Rectangle){32,0,16,16},// the -96 (-)means mirror on x axis
  242. (Rectangle){bul[i].position.x-16,bul[i].position.y-16,32,32},
  243. (Vector2){0,0},0,WHITE);
  244. }
  245. }
  246. void inientity(int entity,int x, int y){
  247. ent[entity].active = true;
  248. ent[entity].position.x = x;
  249. ent[entity].position.y = y;
  250. ent[entity].timemax = 1;
  251. ent[entity].timemax2 = 1;
  252. ent[entity].angle = 0;
  253. ent[entity].angle2 = 0;
  254. ent[entity].pos = 0;
  255. ent[entity].pos2 = 0;
  256. ent[entity].time = 0;
  257. ent[entity].time2 = 0;
  258. ent[entity].valuecount = -1;
  259. ent[entity].valuecount2 = -1;
  260. ent[entity].maxcommand = MAXCOMMANDS;
  261. ent[entity].maxcommand2 = MAXCOMMANDS;
  262. for(int i=0;i<ent[entity].maxcommand;i++){
  263. ent[entity].command[i]=GetRandomValue(1,4);
  264. ent[entity].value[i]=GetRandomValue(0,10);
  265. }
  266. for(int i=0;i<ent[entity].maxcommand2;i++){
  267. ent[entity].command2[i]=GetRandomValue(2,3);
  268. ent[entity].value2[i]=GetRandomValue(10,100);
  269. if(GetRandomValue(0,10)==1){
  270. ent[entity].command2[i]=4;
  271. }
  272. }
  273. }
  274. void drawentities(){
  275. for(int i=0;i<MAXENTITIES;i++){
  276. if(ent[i].active==false)continue;
  277. // Draw a little shadow below the sprite.
  278. DrawEllipse(ent[i].position.x-3,ent[i].position.y+3,14,14,(Color){0,0,0,100});
  279. // Draw the tank
  280. DrawTexturePro(sprites, (Rectangle){0,0,16,16},// the -96 (-)means mirror on x axis
  281. (Rectangle){ent[i].position.x,ent[i].position.y,32,32},
  282. (Vector2){16,16},ent[i].angle,WHITE);
  283. // Draw the turret
  284. DrawTexturePro(sprites, (Rectangle){16,0,16,16},// the -96 (-)means mirror on x axis
  285. (Rectangle){ent[i].position.x,ent[i].position.y,32,32},
  286. (Vector2){16,16},ent[i].angle2,WHITE);
  287. }
  288. }
  289. //
  290. // This function checks collision with each of the entities. It works by taking the current command and running it to its end.
  291. // WIth forward and Backwards movement. If in this command we wil hit a other entity we scrap the entire command chain and
  292. // build a new.
  293. //
  294. void advancedcollision(){
  295. for(int i=0;i<MAXENTITIES;i++){
  296. for(int j=0;j<MAXENTITIES;j++){
  297. if(i==j)continue;
  298. if(ent[i].command[ent[i].pos]==1){
  299. Vector2 pos = ent[i].position;
  300. for(int z=0;z<ent[i].value[ent[i].pos]-ent[i].valuecount;z++){
  301. pos.x+=cos(ent[i].angle*DEG2RAD)*1;
  302. pos.y+=sin(ent[i].angle*DEG2RAD)*1;
  303. if(distance(pos.x,pos.y,ent[j].position.x,ent[j].position.y)<36){
  304. getrandomcommands(i);
  305. continue;
  306. }
  307. }
  308. }
  309. if(ent[i].command[ent[i].pos]==4){
  310. Vector2 pos = ent[i].position;
  311. for(int z=0;z<ent[i].value[ent[i].pos]-ent[i].valuecount;z++){
  312. pos.x-=cos(ent[i].angle*DEG2RAD)*1;
  313. pos.y-=sin(ent[i].angle*DEG2RAD)*1;
  314. if(distance(pos.x,pos.y,ent[j].position.x,ent[j].position.y)<36){
  315. getrandomcommands(i);
  316. continue;
  317. }
  318. }
  319. }
  320. }}
  321. }
  322. void entitiescollision(){
  323. for(int i=0;i<MAXENTITIES;i++){
  324. for(int j=0;j<MAXENTITIES;j++){
  325. if(i==j)continue;
  326. if(ent[i].collisionskip>0)ent[i].collisionskip--;
  327. if(ent[i].collisionskip==0 && distance(ent[i].position.x,ent[i].position.y,ent[j].position.x,ent[j].position.y)<36){
  328. //ent[i].active=false;
  329. float an=getangle(ent[i].position.x,ent[i].position.y,ent[j].position.x,ent[j].position.y);
  330. ent[i].angle=an*RAD2DEG;
  331. ent[i].command[0]=4;
  332. ent[i].value[0]=40;
  333. ent[i].pos=0;
  334. ent[i].valuecount=-1;
  335. }
  336. }
  337. }
  338. }
  339. void updateentities(){
  340. for(int i=0;i<MAXENTITIES;i++){
  341. if(ent[i].active==false)continue;
  342. if(ent[i].pos>=ent[i].maxcommand){
  343. ent[i].pos=0;
  344. for(int ii=0;ii<ent[i].maxcommand;ii++){
  345. ent[i].command[ii]=GetRandomValue(1,4);
  346. ent[i].value[ii]=GetRandomValue(0,10);
  347. }
  348. ent[i].command[0]=1;
  349. ent[i].value[0]=GetRandomValue(10,60);
  350. }
  351. if(ent[i].pos2>=ent[i].maxcommand2){
  352. ent[i].pos2=0;
  353. for(int ii=0;ii<ent[i].maxcommand2;ii++){
  354. ent[i].command2[ii]=GetRandomValue(2,3);
  355. ent[i].value2[ii]=GetRandomValue(10,100);
  356. if(GetRandomValue(0,10)==1){
  357. ent[i].command2[ii]=4;
  358. }
  359. }
  360. }
  361. ent[i].time++;
  362. if(ent[i].time>ent[i].timemax && ent[i].pos<ent[i].maxcommand){
  363. ent[i].time=0;
  364. //
  365. switch (ent[i].command[ent[i].pos]){
  366. case 1://forward
  367. if(ent[i].valuecount==-1){
  368. ent[i].valuecount=ent[i].value[ent[i].pos];
  369. }
  370. if(ent[i].valuecount>0){
  371. ent[i].valuecount--;
  372. ent[i].position.x += cos(ent[i].angle*DEG2RAD)*1;
  373. ent[i].position.y += sin(ent[i].angle*DEG2RAD)*1;
  374. }
  375. if(ent[i].valuecount==0){
  376. ent[i].pos++;
  377. ent[i].valuecount=-1;
  378. }
  379. break;
  380. case 4://Backwards
  381. if(ent[i].valuecount==-1){
  382. ent[i].valuecount=ent[i].value[ent[i].pos];
  383. }
  384. if(ent[i].valuecount>0){
  385. ent[i].valuecount--;
  386. ent[i].position.x -= cos(ent[i].angle*DEG2RAD)*1;
  387. ent[i].position.y -= sin(ent[i].angle*DEG2RAD)*1;
  388. }
  389. if(ent[i].valuecount==0){
  390. ent[i].pos++;
  391. ent[i].valuecount=-1;
  392. }
  393. break;
  394. case 2://left
  395. if(ent[i].valuecount==-1){
  396. ent[i].valuecount=ent[i].value[ent[i].pos];
  397. }
  398. if(ent[i].valuecount>0){
  399. ent[i].valuecount--;
  400. ent[i].angle--;
  401. }
  402. if(ent[i].valuecount==0){
  403. ent[i].pos++;
  404. ent[i].valuecount=-1;
  405. }
  406. break;
  407. case 3://right
  408. if(ent[i].valuecount==-1){
  409. ent[i].valuecount=ent[i].value[ent[i].pos];
  410. }
  411. if(ent[i].valuecount>0){
  412. ent[i].valuecount--;
  413. ent[i].angle++;
  414. }
  415. if(ent[i].valuecount==0){
  416. ent[i].pos++;
  417. ent[i].valuecount=-1;
  418. }
  419. break;
  420. }
  421. }
  422. ent[i].time2++;
  423. if(ent[i].time2>ent[i].timemax2 && ent[i].pos2<ent[i].maxcommand2){
  424. ent[i].time2=0;
  425. //
  426. switch (ent[i].command2[ent[i].pos2]){
  427. case 2://left
  428. if(ent[i].valuecount2==-1){
  429. ent[i].valuecount2=ent[i].value2[ent[i].pos2];
  430. }
  431. if(ent[i].valuecount2>0){
  432. ent[i].valuecount2--;
  433. ent[i].angle2--;
  434. }
  435. if(ent[i].valuecount2==0){
  436. ent[i].pos2++;
  437. ent[i].valuecount2=-1;
  438. }
  439. break;
  440. case 3://right
  441. if(ent[i].valuecount2==-1){
  442. ent[i].valuecount2=ent[i].value2[ent[i].pos2];
  443. }
  444. if(ent[i].valuecount2>0){
  445. ent[i].valuecount2--;
  446. ent[i].angle2++;
  447. }
  448. if(ent[i].valuecount2==0){
  449. ent[i].pos2++;
  450. ent[i].valuecount2=-1;
  451. }
  452. break;
  453. case 4://shoot
  454. shootbullet(ent[i].position,ent[i].angle2);
  455. ent[i].pos2++;
  456. ent[i].valuecount2=-1;
  457. }
  458. }
  459. }
  460. }
  461. //
  462. // This function gives the entity a new set of commands. This for the body and the turret.
  463. //
  464. void getrandomcommands(int entity){
  465. ent[entity].pos=0;
  466. for(int ii=0;ii<ent[entity].maxcommand;ii++){
  467. ent[entity].command[ii]=GetRandomValue(1,4);
  468. ent[entity].value[ii]=GetRandomValue(0,10);
  469. }
  470. ent[entity].pos2=0;
  471. for(int ii=0;ii<ent[entity].maxcommand2;ii++){
  472. ent[entity].command2[ii]=GetRandomValue(2,3);
  473. ent[entity].value2[ii]=GetRandomValue(10,100);
  474. if(GetRandomValue(0,10)==1){
  475. ent[entity].command2[ii]=4;
  476. }
  477. }
  478. }
  479. // Manhattan Distance (less precise)
  480. float distance(float x1,float y1,float x2,float y2){
  481. return (float)abs(x2-x1)+abs(y2-y1);
  482. }
  483. // Return the angle from - to in float
  484. float getangle(float x1,float y1,float x2,float y2){
  485. return (float)atan2(y2-y1, x2-x1);
  486. }
  487. // takes radian iput! <0 is left is shorter else right turn is shorter.
  488. // When it outputs >3 you can asume it aligns with the target(2) angle.
  489. float angledifference(float angle1, float angle2){
  490. float difference = angle1 - angle2;
  491. while (difference < -PI){
  492. difference += (PI*2);
  493. }
  494. while (difference > PI){
  495. difference -= (PI*2);
  496. }
  497. return difference;
  498. }