Pokitto turn based strategy game.
wars.cpp
- Committer:
- trelemar
- Date:
- 2017-12-05
- Revision:
- 1:189e78ff65b1
- Parent:
- 0:86d8ece873ca
- Child:
- 2:af768faf5329
File content as of revision 1:189e78ff65b1:
#include "Pokitto.h" #include "warsprites.h" #include "warfont.cpp" #include <vector> #include <stdint.h> float solids[64] { 0,1,1,1,1,0,0,0, 0,0,1,0,1,0,0,0, 0,0,1,1,1,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, }; //Sprite sheet:2x1 const uint8_t cursor_sprites [][244] ={ //[0] cell:0x0 { 22,22, 204,0,12,204,204,204,204,204,192,0,204, 192,255,240,204,204,204,204,204,15,255,12, 15,160,12,204,204,204,204,204,192,10,240, 15,12,204,204,204,204,204,204,204,192,240, 15,12,204,204,204,204,204,204,204,192,240, 192,204,204,204,204,204,204,204,204,204,12, 204,204,204,204,204,204,204,204,204,204,204, 204,204,204,204,204,204,204,204,204,204,204, 204,204,204,204,204,204,204,204,204,204,204, 204,204,204,204,204,204,204,204,204,204,204, 204,204,204,204,204,204,204,204,204,204,204, 204,204,204,204,204,204,204,204,204,204,204, 204,204,204,204,204,204,204,204,204,204,204, 204,204,204,204,204,204,204,204,204,204,204, 204,204,204,204,204,204,204,204,204,204,204, 204,204,204,204,204,204,204,204,204,204,204, 192,204,204,204,204,204,204,204,204,204,12, 15,12,204,204,204,204,204,204,204,192,240, 15,12,204,204,204,204,204,204,204,192,240, 15,160,12,204,204,204,204,204,192,10,240, 192,255,240,204,204,204,204,204,15,255,12, 204,0,12,204,204,204,204,204,192,0,204, }, //[1] cell:1x0 { 22,22, 204,204,204,204,204,204,204,204,204,204,204, 204,192,0,204,204,204,204,204,0,12,204, 204,15,255,12,204,204,204,192,255,240,204, 192,250,0,204,204,204,204,204,0,175,12, 192,240,204,204,204,204,204,204,204,15,12, 192,240,204,204,204,204,204,204,204,15,12, 204,12,204,204,204,204,204,204,204,192,204, 204,204,204,204,204,204,204,204,204,204,204, 204,204,204,204,204,204,204,204,204,204,204, 204,204,204,204,204,204,204,204,204,204,204, 204,204,204,204,204,204,204,204,204,204,204, 204,204,204,204,204,204,204,204,204,204,204, 204,204,204,204,204,204,204,204,204,204,204, 204,204,204,204,204,204,204,204,204,204,204, 204,204,204,204,204,204,204,204,204,204,204, 204,12,204,204,204,204,204,204,204,192,204, 192,240,204,204,204,204,204,204,204,15,12, 192,240,204,204,204,204,204,204,204,15,12, 192,250,0,204,204,204,204,204,0,175,12, 204,15,255,12,204,204,204,192,255,240,204, 204,192,0,204,204,204,204,204,0,12,204, 204,204,204,204,204,204,204,204,204,204,204, }, }; using namespace std; int clamp(int low, int n, int high) {return min(max(low, n), high);} float dist(int x1, int y1, int x2, int y2) { int x = x1 - x2; int y = y1 - y2; float dist; dist = pow((float)x,2)+pow((float)y,2); dist = sqrt((float)dist); return dist; } int lerp(int a, int b, float t) { return (1-t)*a + t*b; } enum GameState {MAIN, PAUSE, MENU} State = MENU; #define KEY_REPEAT 5 #define TASK_MOVE 0 #define TASK_ITEM 1 #define TASK_ATTACK 2 #define TASK_WAIT 3 #define TASK_NONE 4 #define TEAM_USER 0 #define TEAM_CPU 1 int current_team = TEAM_USER; uint8_t selected_task = 0; uint8_t current_task = TASK_NONE; static char task_names[4][7] { {"MOVE"}, {"ITEM"}, {"ATTACK"}, {"WAIT"}, }; int task_colors[4] { 11,9,6,8 }; Pokitto::Core game; Pokitto::Display disp; Pokitto::Buttons btn; #define SW disp.width #define SH disp.height int map_x, map_y, cursor_x, cursor_y; bool center_cursor = true; void centerCursor() { map_x = cursor_x - 3; map_y = cursor_y - 2; } static void printb(int x, int y, int text) { disp.setColor(0); disp.print(x,y-1,text); disp.print(x,y+1,text); disp.print(x+1,y,text); disp.print(x-1,y,text); disp.setColor(15); disp.print(x,y,text); } static void printb(int x, int y, char text[]) { disp.setColor(0); disp.print(x,y-1,text); disp.print(x,y+1,text); disp.print(x+1,y,text); disp.print(x-1,y,text); disp.setColor(15); disp.print(x,y,text); } struct Dialog { char text[20]; int start_time; int position; Dialog(char _text[20]) { strcpy(text, _text); position = 0; start_time = game.frameCount; }; void update() { position = min(position + 1, 19); }; void draw(int x, int y) { disp.setCursor(x, y); for (int p=0;p<position;p++) { disp.print(text[p]); } }; }; struct Transition { int on_val, off_val, val; bool enabled; Transition() {}; Transition(int _onv, int _offv, bool _enabled) { on_val = _onv; off_val = _offv; enabled = _enabled; if (enabled) val=on_val; else val = off_val; }; void toggle() {enabled=!enabled;}; void update() { if (enabled) val = lerp(val, on_val, 0.2); else val = lerp(val, off_val, 0.2); }; }; struct Vec2 { int x, y; Vec2(int _x, int _y) {x=_x; y=_y;}; }; struct Item { int id, x, y; Item(int _id, int _x, int _y) { id = _id; x = _x; y = _y; }; void Draw() { int dx = (x-map_x)*16; int dy = (y-map_y)*16; //disp.setColor(1); //disp.fillRectangle(dx+4,dy+12,8,3); //disp.drawBitmap(dx, dy+(sin(game.frameCount/4))*2, sprites[id]); }; }; #define ID_SOLDIER 0 #define ID_ORC 1 char id_names[][8] { {"SOLDIER"}, {"ORC"}, }; struct Hero { unsigned int index, team, id, x, y; uint8_t atk, def, hp, max_hp; float range; int spr; int hurt_time; bool tasks[4], flip; Hero(int _x, int _y, int _id, int _team) { x = _x; y = _y; flip = false; id = _id; team = _team; if (_team == TEAM_USER) { spr = 32; } else {spr = 26; flip=true; id = ID_ORC;} max_hp = 10; hp = max_hp; atk = random(4,6); def = random(1,3); range = 2.1; NewTurn(); }; void NewTurn() { for (int i=0;i<4;i++) {tasks[i]=true;} }; void update(int i) { index = i; }; void Draw() { int ox = 0; int oy = 0; int dx = (x-map_x)*16; int dy = (y-map_y)*16; bool draw = true; if (game.frameCount-hurt_time<10) { ox = random(-2,2); oy = random(-2,2); if (game.frameCount%2==0) {draw=false;} } int spr_id = spr + (game.frameCount/6)%2; disp.invisiblecolor = 13; if (draw) disp.drawBitmap((dx)+ox,(dy)+oy,sprites[spr_id], 0, flip); disp.invisiblecolor = 12; }; bool inRange(Hero * enemy) { return (dist(x, y, enemy->x, enemy->y)<=range); }; bool inRange(int dx, int dy) { return (dist(x, y, dx, dy)<=range); } }; void DrawCursorHeroUI (Hero * h, int x, int y) { int bw = 24; int hw = (bw/h->max_hp) * h->hp; disp.setColor(0); disp.drawRoundRect(x-1,y-1,bw+2,5,2); //HEALTH BAR disp.fillRoundRect(x-1, y+26,bw+2,7,2); //TEXT BG BOX disp.setColor(6); disp.fillRect(x, y, bw, 2); disp.setColor(11); disp.fillRect(x, y, hw+4, 2); disp.setColor(11); disp.print(x,y+27,"^"); disp.setColor(15); disp.print((int)h->atk); disp.setColor(6); disp.print("n"); disp.setColor(15); disp.print((int)h->def); } void DrawSelectedHeroUI(Hero * h, int y) { disp.setColor(0); //disp.fillRect(0,66,SW,88-66); disp.fillRoundRect(1,y,SW-2,88-67,3); //disp.setColor(15); if (!h) return; printb(3,y-3,id_names[h->id]); disp.setColor(8); disp.print(3,y+3,">"); disp.setColor(15); disp.print((int)h->atk); disp.print(" ATK"); disp.setColor(6); disp.print(3,y+9,"n"); disp.setColor(15); disp.print((int)h->def); disp.print(" DEF"); disp.setColor(11); disp.print(3,y+15,"^"); disp.setColor(15); disp.print((int)h->range); disp.print(" RNG"); printb(16,(-y)+68,task_names[selected_task]); for (int i=0;i<4;i++) { int spr = i+48; if (!h->tasks[i]) spr-=8; else if (selected_task==i) spr+=8; disp.drawBitmap(44+(i*16), y+4, sprites[spr]); } int bw = 62; int hw = (bw/h->max_hp) * h->hp; disp.setColor(0); disp.drawRoundRect(44,y-4,bw+2,7,3); disp.setColor(6); disp.fillRoundRect(45,y-3,bw,5,2); disp.setColor(11); disp.fillRoundRect(45,y-3,hw+2,5,2); disp.setColor(15); printb(50+(bw/2)-10,y-3,(int)h->hp); } void DrawItemList(Hero * h, int y) { disp.setColor(0); disp.fillRoundRect(32,y,SW-63,12,3); disp.setColor(15); for (int i = 0; i<4; i++) { disp.drawRoundRect(34+(i*11),y+1,10,10,2); } } void DrawTurnCounter(vector<Hero> &_heros) { disp.invisiblecolor = 13; int spr = 44; if (current_team==TEAM_CPU) spr = 45; disp.drawBitmap(0, 0, sprites[spr]); disp.invisiblecolor = 12; int count = 0; for (int i=0; i<_heros.size();i++) { if (_heros[i].team == current_team) count++; } printb(11,10,(int)count); } #define MAP_WIDTH 12 #define MAP_HEIGHT 12 int Map[MAP_HEIGHT][MAP_WIDTH]; void loadTileMap() { fileOpen("file_out.war",FILE_MODE_BINARY); for (int i = 0; i<144; i++) { int x = i%MAP_WIDTH; int y = (i-x)/MAP_WIDTH; //fileSetPosition(i); Map[y][x] = (int)filePeek(i); } //fileClose(); } void saveTileMap() { } void DrawTileMap(int8_t x, int8_t y, Hero * hero) { disp.setColor(15); for (int map_x=0; map_x<7; map_x++) { for (int map_y=0; map_y<6; map_y++) { int spr; if (map_x+x<0||x+map_x>MAP_WIDTH-1||y+map_y<0||y+map_y>MAP_HEIGHT-1) { spr = 9; } else { spr = Map[map_y+y][map_x+x]; } float distance; disp.drawBitmap((map_x*16), (map_y*16),sprites[spr]); //RANGE BOXES if (hero && (current_task == TASK_MOVE || current_task == TASK_ATTACK)) { distance = dist(map_x+x, map_y+y, hero->x, hero->y); if (distance<hero->range&&x+map_x<MAP_WIDTH&&y+map_y<MAP_HEIGHT) { disp.setColor(8); if (solids[Map[map_y+y][map_x+x]]) {disp.setColor(6);} disp.drawRoundRect(map_x*16+2, map_y*16+2, 12, 12,3); //disp.print(map_x*16,map_y*16,(float)distance); } } } } } void DrawCursor(int x, int y) { //uint8_t spr = 52 + (game.frameCount/12)%2; disp.drawBitmap(x*16-3, y*16-3,cursor_sprites[(game.frameCount/4)%2]); } Hero * getHeroAtCoords(int x, int y, vector<Hero> &herovect) { Hero *h = NULL; bool found = false; for (int i=0;i<herovect.size();i++) { if (herovect[i].x==x&&herovect[i].y==y) { h = &herovect[i]; break; } } return h; } void CursorInput() { int last_x=cursor_x; int last_y=cursor_y; if (btn.repeat(BTN_UP,KEY_REPEAT)) { if (cursor_y-map_y<=0) { map_y=max(map_y-1,0); } cursor_y=max(cursor_y-1,0); } if (btn.repeat(BTN_DOWN,KEY_REPEAT)) { if (cursor_y-map_y>=4) { map_y=min(map_y+1,MAP_HEIGHT-6); } cursor_y=min(cursor_y+1,MAP_HEIGHT-1); } if (btn.repeat(BTN_LEFT,KEY_REPEAT)) { if (cursor_x-map_x<=0) { map_x=max(map_x-1,0); } cursor_x=max(cursor_x-1,0); } if (btn.repeat(BTN_RIGHT,KEY_REPEAT)) { if (cursor_x-map_x>=6) { map_x=min(map_x+1,MAP_WIDTH-7); } cursor_x=min(cursor_x+1,MAP_WIDTH-1); } if (center_cursor) { centerCursor(); } } struct MainMenu { struct Transition trans; char items[3][10]; int selected_item; MainMenu() { trans.off_val=-16; trans.on_val = 24; trans.val=trans.off_val; trans.enabled=true; strcpy(items[0], "CAMPAIGN"); strcpy(items[1], "SKIRMISH"); strcpy(items[2], "OPTIONS"); selected_item = 0; }; void update() { if (btn.pressed(BTN_RIGHT)) { selected_item = min(selected_item + 1, 2); } if (btn.pressed(BTN_LEFT)) { selected_item = max(selected_item - 1, 0); } if (btn.pressed(BTN_A)) { switch (selected_item){ case (0): //CAMPAIGN break; case (1): //SKIRMISH disp.clear(); disp.print("LOADING"); pokInitSD(); loadTileMap(); State = MAIN; break; case (2): //OPTIONS break; } } }; void draw() { disp.clear(); trans.update(); disp.setColor(15); disp.print(2,trans.val,"POKIT WARS"); disp.print(2,SH/2, items[selected_item]); }; }; int main() { MainMenu main_menu; Transition tTrans(66,96,false); Transition itemlist(40,72,false); game.begin(); game.setFrameRate(30); disp.invisiblecolor = 12; disp.bgcolor=12; disp.persistence = 1; disp.load565Palette(sprite_pal); disp.setFont(fontWAR); map_x=0; map_y=0; cursor_x=4; cursor_y=6; vector<Hero> heros; pokInitSD(); loadTileMap(); /* heros.push_back(Hero(4,5,ID_SOLDIER, TEAM_USER)); heros.push_back(Hero(5,6,ID_SOLDIER, TEAM_USER)); heros.push_back(Hero(3,8,ID_SOLDIER, TEAM_USER)); heros.push_back(Hero(8,5,ID_SOLDIER, TEAM_CPU)); heros.push_back(Hero(8,7,ID_SOLDIER, TEAM_CPU)); heros.push_back(Hero(6,9,ID_SOLDIER, TEAM_CPU)); */ for (int i=0;i<6;i++) { int team = TEAM_USER; if (i>2) team = TEAM_CPU; heros.push_back(Hero(random(0,MAP_WIDTH-1),random(0,MAP_HEIGHT-1),ID_SOLDIER, team)); } Hero * cursor_hero = NULL; Hero * current_hero = NULL; Item itest(52, 3, 3); while (game.isRunning()) { if (game.update()) { switch (State) { case MENU: main_menu.update(); main_menu.draw(); break; case MAIN: tTrans.update(); itemlist.update(); //If no current hero or current task is to move our current hero, accept cursor movement. if (!current_hero||current_task==TASK_MOVE||current_task==TASK_ATTACK) { CursorInput(); cursor_hero = getHeroAtCoords(cursor_x,cursor_y, heros); } //This will call when there is a current hero and not in a current task else if (current_hero) { //TASK SELECTOR int dir = 0; if (btn.pressed(BTN_RIGHT)) { selected_task=min(selected_task+1,3); } if (btn.pressed(BTN_LEFT)) { selected_task=max(selected_task-1,0); } if (selected_task==TASK_ITEM) { itemlist.enabled = true; } else itemlist.enabled = false; //selected_task=selected_task%4; } DrawTileMap(map_x, map_y, current_hero); for (int i = 0; i<heros.size(); i++) { heros[i].Draw(); heros[i].update(i); } itest.Draw(); DrawCursor(cursor_x-map_x,cursor_y-map_y); disp.setColor(15); if (btn.pressed(BTN_A)) { if (current_hero&¤t_task!=TASK_NONE) { //THIS IS WITHIN A TASK, EX: SELECTING A MOVE LOCATION OR ATTACK TARGET. switch (current_task) { case TASK_MOVE: //MOVE HERO TO CURSOR, NO COLISSIONS YET. //ONLY IF IN RANGE AND NOT SOLID //FIX STACKABLE HEROS if (current_hero->inRange(cursor_x, cursor_y)&&!getHeroAtCoords(cursor_x, cursor_y, heros)&&!solids[Map[cursor_y][cursor_x]]) { if (cursor_x<current_hero->x) current_hero->flip=true; else if (cursor_x>current_hero->x) current_hero->flip=false; current_hero->x = cursor_x; current_hero->y = cursor_y; current_task = TASK_NONE; current_hero->tasks[TASK_MOVE] = false; } break; case TASK_ATTACK: if (cursor_hero&¤t_hero->inRange(cursor_hero)) { //FOUND TARGET AT CURSOR. cursor_x = current_hero->x; cursor_y = current_hero->y; int8_t result = cursor_hero->hp - (current_hero->atk-cursor_hero->def); cursor_hero->hp = result; cursor_hero->hurt_time=game.frameCount; if (result<=0) { heros.erase(heros.begin()+cursor_hero->index); } current_task = TASK_NONE; current_hero->tasks[TASK_ATTACK] = false; } else { //NO TARGET FOUND, ERROR SOUND SHOULD PLAY. } break; //DUMMY CODE, THESE ARE CALLED BELOW. NOT HERE. case TASK_ITEM: current_task = TASK_NONE; current_hero->tasks[TASK_ITEM] = false; break; case TASK_WAIT: current_task = TASK_NONE; current_hero->tasks[TASK_WAIT] = false; break; } } else if (current_hero&¤t_hero->tasks[selected_task]==true) { //TASK SWITCH HERE //THIS HAPPENS DIRECTLY AFTER PRESSING BTN_A ON A NEW TASK. current_task=selected_task; switch (selected_task) { case TASK_ITEM: current_task = TASK_NONE; current_hero->tasks[TASK_ITEM] = false; break; case TASK_WAIT: current_task = TASK_NONE; current_hero->tasks[TASK_WAIT] = false; break; } } else if (cursor_hero) { //NO CURRENT HERO, SELECT A HERO ONLY IF IN CURSOR HERE. if (cursor_hero->team == current_team) { current_hero = cursor_hero; selected_task = TASK_MOVE; tTrans.enabled = true; } } } if (btn.pressed(BTN_B)) { if (current_task!=TASK_NONE) { //BACK OUT OF CURRENT TASK. current_task=TASK_NONE; cursor_x = current_hero->x; cursor_y = current_hero->y; } else if (current_hero&¤t_task==TASK_NONE) { tTrans.enabled=false; current_hero = NULL; //UNSELECT HERO. } } if (btn.pressed(BTN_C)&& !current_hero) { //heros.push_back(Hero(cursor_x,cursor_y,0,0)); current_team=(current_team+1) % 2; for (int i=0;i<heros.size();i++) heros[i].NewTurn(); State = PAUSE; disp.print("PAUSED"); //tTrans.toggle(); } if (cursor_hero&&cursor_hero!=current_hero&¤t_task) { DrawCursorHeroUI(cursor_hero, (cursor_hero->x-map_x)*16-4,(cursor_hero->y-map_y)*16-7); } if (tTrans.val==tTrans.on_val) { DrawItemList(current_hero, itemlist.val); } DrawSelectedHeroUI(current_hero, tTrans.val); disp.setCursor(0,0); DrawTurnCounter(heros); //disp.print(0,32,dtest.text); //disp.print(1,SH-6,(int)game.battery.level); //disp.print(" "); break; case PAUSE: if (btn.pressed(BTN_C)) { State = MAIN; } disp.print(isThisFileOpen("file_out.war")); break; }//GAME STATE SWITCH }//UPDATE }//IS RUNNING return 1; }