/***************************************************************************** * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * @(#) Colloidal for Ndless 2.0 (Colloidal Engine file) * Core engine file * * Copyright (C) August 15, 2011 Joe Kim (SolusIpse) * Contact: solusipse0x5@gmail.com * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. *****************************************************************************/ #include #include "utils.h" #include "Colloidal.h" #define C_BLACK 0x00 #define C_DGRAY 0x06 #define C_MGRAY 0x0A #define C_LGRAY 0x0D #define C_WHITE 0x0F #define MAX_BLOCKS 100 //self explanatory #define BLOCK_SIZE 16 //in pixels #define FP_FACTOR 8 //number of binary decimal places reserved for fractions #define SCORE_SCALE 2 //100hz timer, divide by 2^SCORE_SCALE to get close to 1 point earned per second #define NEW_BLOCK_FREQ_MASK 0x000003FF //Take a base 2 modulus so rougly every 100 points add another block #define DEG_SPEED 1 //levels of speed for each direction #define COLOR_CHANGE_DELAY_FACTOR 3 //2^FACTOR frames before tab and control really do anything #define PLAYER_SPEED 0 //bigger is slower /* returns score returns 0 if errors */ unsigned long colloidal(void) { /* *initilize data *Fixed point math - 24.8 seems appropriate *Nspire screen is so big!! Even travelling one pixel *at 100 fps means it takes 3.2 seconds to cross the screen! *It probably takes longer because of the wy idle() appears to work codewise. *Pity I can't set idle() to go at higher frame rates. *Don't ask me why I didn't malloc! *I was just lazy and I only had a day to write this contest entry. */ void *scrbuf = malloc(SCREEN_BYTES_SIZE); unsigned long int *xlist, *ylist, *exlist, *eylist; signed long int *vxlist, *vylist; unsigned int *color; xlist = malloc(sizeof(unsigned long)*MAX_BLOCKS); ylist = malloc(sizeof(unsigned long)*MAX_BLOCKS); vxlist = malloc(sizeof(signed long)*MAX_BLOCKS); vylist = malloc(sizeof(signed long)*MAX_BLOCKS); color = malloc(sizeof(unsigned int)*MAX_BLOCKS); exlist = malloc(sizeof(sizeof(unsigned int)*8)); eylist = malloc(sizeof(sizeof(unsigned int)*8)); //return if can't malloc enough memory if(xlist==NULL || ylist==NULL || vxlist==NULL || vylist==NULL || color==NULL || scrbuf==NULL || exlist == NULL || eylist ==NULL) return 0; unsigned int num_blocks = 1; xlist[0]=0; ylist[0]=0; vxlist[0]=1<<(FP_FACTOR-1); vylist[0]=1<<(FP_FACTOR); color[0]=0; unsigned long score = 0; unsigned long px = 151<>FP_FACTOR), (ylist[i]>>FP_FACTOR), color[i], scrbuf); } /* *GAMEOVER DETECTION *The reason why it's here is because it uses the drawn updated screen * and does a pixel test to see if something is already occupying that area. */ xtemp=px>>PLAYER_SPEED; //reduce back to whole screen coordinates ytemp=py>>PLAYER_SPEED; //the below is just lazy coding practice combined with not having a big enough screen. Damn 1024 pixel width! if(!gameover) { if((j=getPixel2(xtemp, ytemp, scrbuf))!=C_WHITE || (j=getPixel2(xtemp+(BLOCK_SIZE-1), ytemp, scrbuf))!=C_WHITE || (j=getPixel2(xtemp, ytemp+(BLOCK_SIZE-1), scrbuf))!=C_WHITE || (j=getPixel2(xtemp+(BLOCK_SIZE-1), ytemp+(BLOCK_SIZE-1), scrbuf))!=C_WHITE) { if (j!=pcolor) { gameover=TRUE; //if it's not the same color, game over exlist[0]=xtemp; eylist[0]=ytemp; exlist[1]=xtemp+6; eylist[1]=ytemp; exlist[2]=xtemp+12; eylist[2]=ytemp; exlist[3]=xtemp; eylist[3]=ytemp+6; exlist[4]=xtemp+12; eylist[4]=ytemp+6; exlist[5]=xtemp; eylist[5]=ytemp+12; exlist[6]=xtemp+6; eylist[6]=ytemp+12; exlist[7]=xtemp+12; eylist[7]=ytemp+12; } else {score=score>>1;} //half score as punishment } } //Draw player if(gameover==FALSE) { player_draw(px>>PLAYER_SPEED,py>>PLAYER_SPEED, pcolor, scrbuf); } else { for(i=8;i--;) { death_draw(exlist[i],eylist[i], pcolor, scrbuf); } } /* *Attempt to add another block *The reason why it's here is because it uses the drawn updated screen * and does a pixel test to see if we can add to the corners */ if(addblock==TRUE) { xtemp=(rand() & 0xFF); ytemp=(rand() & 0x7F); if((px+((BLOCK_SIZE*3)<((BLOCK_SIZE*6)<((BLOCK_SIZE*6)<>COLOR_CHANGE_DELAY_FACTOR) { case 0: pcolor = C_BLACK; break; case 1: pcolor = C_DGRAY; break; case 2: pcolor = C_MGRAY; break; case 3: pcolor = C_LGRAY; break; default: pcolorindex=0; pcolor = C_BLACK; } } else if (isKeyPressed(KEY_NSPIRE_CTRL)) { pcolorindex--; switch(pcolorindex>>COLOR_CHANGE_DELAY_FACTOR) { case 0: pcolor = C_BLACK; break; case 1: pcolor = C_DGRAY; break; case 2: pcolor = C_MGRAY; break; case 3: pcolor = C_LGRAY; break; default: pcolorindex=0; pcolor = C_BLACK; } } if(isKeyPressed(KEY_NSPIRE_0)) {pcolorindex=3<((SCREEN_WIDTH-BLOCK_SIZE)<(SCREEN_WIDTH<((SCREEN_HEIGHT-BLOCK_SIZE)<(SCREEN_HEIGHT<SCREEN_WIDTH || eylist[i]>SCREEN_HEIGHT) { j++; } } if(j==8) wait=TRUE; } //UPDATE for(i=num_blocks; i--;) { //local loop temps xtemp = (xlist[i]+vxlist[i]); ytemp = (ylist[i]+vylist[i]); /* *Collision with box *The fixed point offers some buffering room */ if (xtemp > ((SCREEN_WIDTH-BLOCK_SIZE)< ((SCREEN_WIDTH)< ((SCREEN_HEIGHT-BLOCK_SIZE)< ((SCREEN_HEIGHT)<(xtemp-xlist[j]+(BLOCK_SIZE<(30<<3)) break; } idle(); } clearScreen(); //free malloc'd memory free(xlist); free(ylist); free(vxlist); free(vylist); free(color); free(scrbuf); //divide by 64, this makes it so that you earn a little more than one point per second return score>>SCORE_SCALE; } /*Uses 1st timer which is 488hz, faster than 2nd timer (100hz) default for idle()*/ void idle2(void) { int irq_mask = *(volatile int *)0xDC000008; *(volatile int *)0xDC00000C = ~(1 << 18); // Disable all IRQs except timer __asm volatile("mcr p15, 0, %0, c7, c0, 4" : : "r"(0) ); // Wait for an interrupt to occur *(volatile int *)0x900A0018 = 1; // Acknowledge timer interrupt at source *(volatile int *)0xDC000028; // Make interrupt controller stop asserting nIRQ if there aren't any active IRQs left *(volatile int *)0xDC000008 = irq_mask; // Re-enable disabled IRQs } /* itoa: convert n to characters in s */ void itoa2(long n, char s[]) { long sign; int i; if ((sign = n) < 0) /* record sign */ n = -n; /* make n positive */ i = 0; do { /* generate digits in reverse order */ s[i++] = n % 10 + '0'; /* get next digit */ } while ((n /= 10) > 0); /* delete it */ if (sign < 0) s[i++] = '-'; s[i] = '\0'; reverse(s); } /* reverse: reverse string s in place */ void reverse(char s[]) { int i, j; char c; for (i = 0, j = strlen(s)-1; i> 1) + (y << 7) + (y << 5))); return ((x & 1) ? (*p & 0x0F) : (*p >> 4)); } inline void brick_draw(int x, int y, unsigned char color, unsigned char* screenbuf) { //counter, unsigned for speed unsigned int i; //Upper bits for a color, since 2 pixels per byte, hopefully improves speed unsigned char uppercolor = color<<4; unsigned char fullbytecolor = color | uppercolor; //get start address for sprite unsigned char* s = (unsigned char*)(screenbuf + ((x >> 1) + (y << 7) + (y << 5))); //in screen bounds? Testing only //if ((x < 0) || (x > (SCREEN_WIDTH - BLOCK_SIZE)) || (y < 0) || (y > (SCREEN_HEIGHT - BLOCK_SIZE))) return; if(x &1) { for(i=BLOCK_SIZE; i--;) { //Unrolled, hopefully faster *s = (*s & 0xF0) | color; *(s + 1) = fullbytecolor; *(s + 2) = fullbytecolor; *(s + 3) = fullbytecolor; *(s + 4) = fullbytecolor; *(s + 5) = fullbytecolor; *(s + 6) = fullbytecolor; *(s + 7) = fullbytecolor; *(s + 8) = (*(s + 8) & 0x0F) | uppercolor; s += (SCREEN_WIDTH >> 1); } } else { for(i=BLOCK_SIZE; i--;) { *s = fullbytecolor; *(s + 1) = fullbytecolor; *(s + 2) = fullbytecolor; *(s + 3) = fullbytecolor; *(s + 4) = fullbytecolor; *(s + 5) = fullbytecolor; *(s + 6) = fullbytecolor; *(s + 7) = fullbytecolor; s += (SCREEN_WIDTH >> 1); } } return; } inline void player_draw(int x, int y, unsigned char color, unsigned char* screenbuf) { //counter, unsigned for speed unsigned int i; //Upper bits for a color, since 2 pixels per byte, hopefully improves speed unsigned char uppercolor = color<<4; unsigned char fullbytecolor = color | uppercolor; //get start address for sprite unsigned char* s = (unsigned char*)(screenbuf + ((x >> 1) + (y << 7) + (y << 5))); //in screen bounds? Testing only //if ((x < 0) || (x > (SCREEN_WIDTH - BLOCK_SIZE)) || (y < 0) || (y > (SCREEN_HEIGHT - BLOCK_SIZE))) return; if(x &1) { for(i=BLOCK_SIZE; i--;) { //Unrolled, hopefully faster *s = (*s & 0xF0) | color; *(s + 1) = fullbytecolor; if(i-4>7) { *(s + 2) = fullbytecolor; *(s + 3) = fullbytecolor; *(s + 4) = fullbytecolor; *(s + 5) = fullbytecolor; *(s + 6) = fullbytecolor; } else { *(s + 2) = C_WHITE | uppercolor; *(s + 3) = C_WHITE | (C_WHITE<<4); *(s + 4) = C_WHITE | (C_WHITE<<4); *(s + 5) = C_WHITE | (C_WHITE<<4); *(s + 6) = color | (C_WHITE<<4); } *(s + 7) = fullbytecolor; *(s + 8) = (*(s + 8) & 0x0F) | uppercolor; s += (SCREEN_WIDTH >> 1); } } else { for(i=BLOCK_SIZE; i--;) { *s = fullbytecolor; *(s + 1) = fullbytecolor; if(i-4>7) { *(s + 2) = fullbytecolor; *(s + 3) = fullbytecolor; *(s + 4) = fullbytecolor; *(s + 5) = fullbytecolor; } else { *(s + 2) = C_WHITE | (C_WHITE<<4); *(s + 3) = C_WHITE | (C_WHITE<<4); *(s + 4) = C_WHITE | (C_WHITE<<4); *(s + 5) = C_WHITE | (C_WHITE<<4); } *(s + 6) = fullbytecolor; *(s + 7) = fullbytecolor; s += (SCREEN_WIDTH >> 1); } } return; } inline void death_draw(int x, int y, unsigned char color, unsigned char* screenbuf) { //counter, unsigned for speed unsigned int i; //Upper bits for a color, since 2 pixels per byte, hopefully improves speed unsigned char uppercolor = color<<4; unsigned char fullbytecolor = color | uppercolor; //get start address for sprite unsigned char* s = (unsigned char*)(screenbuf + ((x >> 1) + (y << 7) + (y << 5))); //in screen bounds? Testing only if ((x < 0) || (x > (SCREEN_WIDTH - 4)) || (y < 0) || (y > (SCREEN_HEIGHT - 4))) return; if(x &1) { for(i=4; i--;) { //Unrolled, hopefully faster *s = (*s & 0xF0) | color; *(s + 1) = fullbytecolor; *(s + 2) = (*(s + 2) & 0x0F) | uppercolor; s += (SCREEN_WIDTH >> 1); } } else { for(i=4; i--;) { *s = fullbytecolor; *(s + 1) = fullbytecolor; s += (SCREEN_WIDTH >> 1); } } return; }