diff --git a/Minicraft3DS.zip b/Minicraft3DS.zip index 0f9939e..061ff02 100644 Binary files a/Minicraft3DS.zip and b/Minicraft3DS.zip differ diff --git a/data/icons2.png b/data/icons2.png index b9f6d6a..83c8034 100644 Binary files a/data/icons2.png and b/data/icons2.png differ diff --git a/data/player.png b/data/player.png new file mode 100644 index 0000000..4c6edaf Binary files /dev/null and b/data/player.png differ diff --git a/source/Crafting.c b/source/Crafting.c index 70eeedf..66bc3b8 100644 --- a/source/Crafting.c +++ b/source/Crafting.c @@ -1,5 +1,15 @@ #include "Crafting.h" +void cloneRecipeManager(RecipeManager *from, RecipeManager *to) { + //free old manager recipes + free(to->recipes); + + //copy over recipes + to->size = from->size; + to->recipes = (Recipe*)malloc(sizeof(Recipe) * to->size); + memcpy(to->recipes, from->recipes, sizeof(Recipe) * to->size); +} + void checkCanCraftRecipes(RecipeManager * rm, Inventory * inv){ int i, j; for(i = 0; i < rm->size; i++){ diff --git a/source/Crafting.h b/source/Crafting.h index 6104c9b..0b35f46 100644 --- a/source/Crafting.h +++ b/source/Crafting.h @@ -2,12 +2,12 @@ #include #include "Item.h" -typedef struct { +typedef struct _recipecost { int costItem; int costAmount; } Cost; -typedef struct { +typedef struct _recipe { bool canCraft; int itemResult; int itemAmountLevel; @@ -16,7 +16,7 @@ typedef struct { u8 order; // Used for stable sorting. } Recipe; -typedef struct { +typedef struct _recipeManager { int size; Recipe * recipes; } RecipeManager; @@ -31,6 +31,7 @@ RecipeManager enchanterRecipes; Recipe defineRecipe(int item, int amountOrLevel, int numArgs, ...); +void cloneRecipeManager(RecipeManager *from, RecipeManager *to); void checkCanCraftRecipes(RecipeManager * rm, Inventory * inv); void sortRecipes(RecipeManager * rm); bool craftItem(RecipeManager * rm, Recipe* r, Inventory* inv); diff --git a/source/Entity.c b/source/Entity.c index 5b0e056..b0ded0b 100644 --- a/source/Entity.c +++ b/source/Entity.c @@ -1,23 +1,6 @@ #include "Entity.h" -#define PI 3.141592654 -double gaussrand() -{ - static double U, V; - static int phase = 0; - double Z; - - if(phase == 0) { - U = (rand() + 1.) / (RAND_MAX + 2.); - V = rand() / (RAND_MAX + 1.); - Z = sqrt(-2 * log(U)) * sin(2 * PI * V); - } else - Z = sqrt(-2 * log(U)) * cos(2 * PI * V); - - phase = 1 - phase; - - return Z; -} +#include "Synchronizer.h" Entity newItemEntity(Item item, int x, int y, int level){ Entity e; @@ -34,8 +17,8 @@ Entity newItemEntity(Item item, int x, int y, int level){ e.entityItem.xx = x; e.entityItem.yy = y; e.entityItem.zz = 2; - e.entityItem.xa = gaussrand() * 0.1; - e.entityItem.ya = gaussrand() * 0.1; + e.entityItem.xa = gaussrand(false) * 0.1; + e.entityItem.ya = gaussrand(false) * 0.1; e.entityItem.za = ((float)rand() / RAND_MAX) * 0.45 + 1; return e; @@ -296,8 +279,8 @@ Entity newTextParticleEntity(char * str, u32 color, int x, int y, int level){ e.textParticle.xx = x; e.textParticle.yy = y; e.textParticle.zz = 2; - e.textParticle.xa = gaussrand() * 0.3; - e.textParticle.ya = gaussrand() * 0.2; + e.textParticle.xa = gaussrand(false) * 0.3; + e.textParticle.ya = gaussrand(false) * 0.2; e.textParticle.za = ((float)rand() / RAND_MAX) * 0.7 + 2; return e; @@ -310,7 +293,7 @@ Entity newSmashParticleEntity(int x, int y, int level){ e.x = x; e.y = y; e.canPass = true; - playSound(snd_monsterHurt); + playSoundPositioned(snd_monsterHurt, e.level, e.x, e.y); //TODO: This is a wierd location for the effect return e; } diff --git a/source/Entity.h b/source/Entity.h index 6f06509..437b2a7 100644 --- a/source/Entity.h +++ b/source/Entity.h @@ -27,6 +27,8 @@ typedef struct Entity Entity; +typedef struct _plrd PlayerData; //in order to not include Player.h and cause all sorts of problems + typedef struct { s8 ax; s8 ay; @@ -45,9 +47,8 @@ typedef struct { bool isCarrying; bool isSwimming; int swimTimer; - int score; - Inventory* inv; - Item* activeItem; + + PlayerData *data; } Player; @@ -65,9 +66,8 @@ typedef struct { typedef struct { s16 itemID; bool active; - s8 r; // light radius for lantern. window select for chests. + s8 r; // light radius for lantern. Inventory* inv; // Points to chest inventory. - s16 oSel; // other selection inside the chest inv. } EntityFurniture; typedef struct { @@ -215,18 +215,16 @@ struct Entity { typedef struct { Entity entities[6][1000]; s16 lastSlot[6]; - Inventory invs[301];//1 for the player, 300 for chests. + Inventory invs[300]; s16 nextInv; } EntityManager; EntityManager eManager; Entity nullEntity; -s8 currentLevel; -double gaussrand(); Entity newItemEntity(Item item, int x, int y, int level); -Entity newFurnitureEntity(int itemID,Inventory * invPtr, int x, int y, int level); +Entity newFurnitureEntity(int itemID, Inventory * invPtr, int x, int y, int level); Entity newPassiveEntity(int type, int x, int y, int level); Entity newZombieEntity(int lvl, int x, int y, int level); Entity newSkeletonEntity(int lvl, int x, int y, int level); diff --git a/source/Globals.c b/source/Globals.c index a60546d..009ea5b 100644 --- a/source/Globals.c +++ b/source/Globals.c @@ -1,23 +1,25 @@ #include "Globals.h" -char versionText[34] = "Version 1.3.0"; +#include "Synchronizer.h" + +char versionText[34] = "Version 1.5.0"; char fpsstr[34]; u8 currentMenu = 0; -void addItemsToWorld(Item item,int x, int y, int count){ +void addItemsToWorld(Item item, s8 level, int x, int y, int count){ int i; - for(i = 0;i x1 || e.y - e.yr > y1); } -int getEntities(Entity** result,int x0, int y0, int x1, int y1) { +int getEntities(Entity **result, s8 level, int x0, int y0, int x1, int y1) { int i, last = 0; - for (i = 0; i < eManager.lastSlot[currentLevel]; ++i) { - Entity* e = &eManager.entities[currentLevel][i]; - if (intersects(*e,x0, y0, x1, y1)){ + for (i = 0; i < eManager.lastSlot[level]; ++i) { + Entity* e = &eManager.entities[level][i]; + if (intersects(*e, x0, y0, x1, y1)){ result[last] = e; ++last; } @@ -25,16 +27,16 @@ int getEntities(Entity** result,int x0, int y0, int x1, int y1) { return last; } -void removeSimilarElements(Entity * arr1[], Entity * arr2[]){ +void removeSimilarElements(Entity **arr1, int arr1Size, Entity **arr2, int arr2Size){ int i,j; - for(i=0;ix) - e->xr) >> 4; int yto0 = ((e->y) - e->yr) >> 4; @@ -50,18 +52,18 @@ bool move2(Entity* e,int xa, int ya) { for (yt = yt0; yt <= yt1; ++yt) for (xt = xt0; xt <= xt1; ++xt) { if (xt >= xto0 && xt <= xto1 && yt >= yto0 && yt <= yto1) continue; - entityTileInteract(e, getTile(xt,yt), xt, yt); - if (!e->canPass && tileIsSolid(getTile(xt,yt),e)) { + entityTileInteract(e, getTile(e->level, xt, yt), e->level, xt, yt); + if (!e->canPass && tileIsSolid(getTile(e->level, xt, yt), e)) { blocked = true; return false; } } if (blocked) return false; - Entity * wasInside[eManager.lastSlot[currentLevel]]; - Entity * isInside[eManager.lastSlot[currentLevel]]; - getEntities(wasInside, e->x - e->xr, e->y - e->yr, e->x + e->xr, e->y + e->yr); - int isSize = getEntities(isInside, e->x + xa - e->xr, e->y + ya - e->yr, e->x + xa + e->xr, e->y + ya + e->yr); + Entity * wasInside[eManager.lastSlot[e->level]]; + Entity * isInside[eManager.lastSlot[e->level]]; + int wasSize = getEntities(wasInside, e->level, e->x - e->xr, e->y - e->yr, e->x + e->xr, e->y + e->yr); + int isSize = getEntities(isInside, e->level, e->x + xa - e->xr, e->y + ya - e->yr, e->x + xa + e->xr, e->y + ya + e->yr); int i; for (i = 0; i < isSize; ++i) { @@ -70,18 +72,20 @@ bool move2(Entity* e,int xa, int ya) { EntityVsEntity(e, ent); } if(e->type != ENTITY_PLAYER){ - if(intersects(player, e->x + xa - e->xr, e->y + ya - e->yr, e->x + xa + e->xr, e->y + ya + e->yr)){ - EntityVsEntity(e, &player); + for(int i=0; ilevel && intersects(players[i].entity, e->x + xa - e->xr, e->y + ya - e->yr, e->x + xa + e->xr, e->y + ya + e->yr)){ + EntityVsEntity(e, &(players[i].entity)); + } } } - removeSimilarElements(wasInside, isInside); + removeSimilarElements(wasInside, wasSize, isInside, isSize); for (i = 0; i < isSize; ++i) { Entity * ent = isInside[i]; if (ent == e || ent == NULL) continue; - if (EntityBlocksEntity(e,ent)) return false; + if (EntityBlocksEntity(e, ent)) return false; } - + if(e->x + xa > 0 && e->x + xa < 2048) e->x += xa; if(e->y + ya > 0 && e->y + ya < 2048) e->y += ya; return true; @@ -98,141 +102,43 @@ bool move(Entity* e, int xa, int ya) { } bool moveMob(Entity* e, int xa, int ya){ - if (e->xKnockback < 0) { - move2(e,-1, 0); - e->xKnockback++; - } - if (e->xKnockback > 0) { - move2(e,1, 0); - e->xKnockback--; - } - if (e->yKnockback < 0) { - move2(e,0, -1); - e->yKnockback++; - } - if (e->yKnockback > 0) { - move2(e,0, 1); - e->yKnockback--; - } - if (e->hurtTime > 0) return true; - return move(e, xa, ya); -} - -s16 lastTouchX = -1; -s16 lastTouchY = -1; -bool isDraggingMap = false; -bool isChangingSize = false; -void tickTouchMap(){ - if(shouldRenderMap){ - if(k_touch.px > 0 || k_touch.py > 0){ - // Plus/Minus zoom button - if(k_touch.py > 204 && k_touch.py < 232){ - if(k_touch.px > 284 && k_touch.px < 312){ - if(zoomLevel > 4) return; - if(!isChangingSize && !isDraggingMap){ - zoomLevel += 2; - mScrollX -= (50 * (zoomLevel/2)); - mScrollY -= (40 * (zoomLevel/2)); - isChangingSize = true; - sprintf(mapText,"x%d",zoomLevel); - } - if(mScrollX < 320-(128*zoomLevel)) mScrollX = 320-(128*zoomLevel); - else if(mScrollX > 0) mScrollX = 0; - if(mScrollY < 240-(128*zoomLevel)) mScrollY = 240-(128*zoomLevel); - else if(mScrollY > 0) mScrollY = 0; - return; - } else if(k_touch.px > 256 && k_touch.px < 284){ - if(zoomLevel < 4) return; - if(!isChangingSize && !isDraggingMap){ - mScrollX += (50 * (zoomLevel/2)); - mScrollY += (40 * (zoomLevel/2)); - zoomLevel -= 2; - isChangingSize = true; - sprintf(mapText,"x%d",zoomLevel); - } - if(mScrollX < 320-(128*zoomLevel)) mScrollX = 320-(128*zoomLevel); - else if(mScrollX > 0) mScrollX = 0; - if(mScrollY < 240-(128*zoomLevel)) mScrollY = 240-(128*zoomLevel); - else if(mScrollY > 0) mScrollY = 0; - return; - } - } else if(k_touch.py > 8 && k_touch.py < 40 && k_touch.px > 284 && k_touch.px < 312){ - // Exit Button - if(!isChangingSize && !isDraggingMap){ - shouldRenderMap = false; - return; - } - } - - if(!isDraggingMap){ - lastTouchX = k_touch.px; - lastTouchY = k_touch.py; - } - if(zoomLevel > 2){ - int dx = lastTouchX - k_touch.px; - if(dx > 1 || dx < -1){ - mScrollX -= dx; - if(mScrollX < 320-(128*zoomLevel)) mScrollX = 320-(128*zoomLevel); - else if(mScrollX > 0) mScrollX = 0; - } - lastTouchX = k_touch.px; - } - - int dy = lastTouchY - k_touch.py; - if(dy > 1 || dy < -1){ - mScrollY -= dy; - if(mScrollY < 240-(128*zoomLevel)) mScrollY = 240-(128*zoomLevel); - else if(mScrollY > 0) mScrollY = 0; - } - lastTouchY = k_touch.py; - isDraggingMap = true; - } else { - isDraggingMap = false; - isChangingSize = false; - } - } else { - // touch minimap to bring up zoomed map. - if(k_touch.py > 100 && k_touch.py < 228 && k_touch.px > 10 && k_touch.px < 142){ - shouldRenderMap = true; - } + if (e->xKnockback < 0) { + move2(e, -1, 0); + e->xKnockback++; } + if (e->xKnockback > 0) { + move2(e, 1, 0); + e->xKnockback--; + } + if (e->yKnockback < 0) { + move2(e, 0, -1); + e->yKnockback++; + } + if (e->yKnockback > 0) { + move2(e, 0, 1); + e->yKnockback--; + } + if (e->hurtTime > 0) return true; + return move(e, xa, ya); } -void tickTouchQuickSelect() { - if (currentMenu == 0 && !shouldRenderMap) { - int i = 0; - Inventory * inv = player.p.inv; - - for (i = 0; i < 8; ++i) { - if((inv->lastSlot) > i) { - int xip = i % 4; - int yip = i / 4; - - if(k_touch.py > 72*2+yip*21*2 && k_touch.py < 72*2+yip*21*2+21*2 && k_touch.px > 76*2+xip*21*2 && k_touch.px < 76*2+xip*21*2+21*2) { - playerSetActiveItem(&inv->items[i]); - } - } - } - } -} - -void hurtEntity(Entity* e, int damage, int dir, u32 hurtColor){ +void hurtEntity(Entity *e, int damage, int dir, u32 hurtColor, Entity *damager){ if (TESTGODMODE && e->type==ENTITY_PLAYER) return; if (e->hurtTime > 0) return; - int xd = player.x - e->x; - int yd = player.y - e->y; - if (xd * xd + yd * yd < 80 * 80) playSound(snd_monsterHurt); + playSoundPositioned(snd_monsterHurt, e->level, e->x, e->y); char hurtText[11]; sprintf(hurtText, "%d", damage); - addEntityToList(newTextParticleEntity(hurtText,hurtColor,e->x,e->y,currentLevel), &eManager); + addEntityToList(newTextParticleEntity(hurtText, hurtColor, e->x, e->y, e->level), &eManager); + + int i; // In hindsight I should've made a generic Mob struct, but whatever. ¯\_(-.-)_/¯ switch(e->type){ case ENTITY_PLAYER: e->p.health -= damage; if(e->p.health < 1){ - playSound(snd_bossdeath); + playSoundPositioned(snd_bossdeath, e->level, e->x, e->y); e->p.endTimer = 60; e->p.isDead = true; return; @@ -244,26 +150,26 @@ void hurtEntity(Entity* e, int damage, int dir, u32 hurtColor){ e->hostile.health -= damage; if(e->hostile.health < 1){ if(e->type == ENTITY_ZOMBIE) { - addItemsToWorld(newItem(ITEM_FLESH,1),e->x+8, e->y+8, (rand()%2) + 1); + addItemsToWorld(newItem(ITEM_FLESH,1), e->level, e->x+8, e->y+8, (rand()%2) + 1); } else if(e->type == ENTITY_SKELETON) { - addItemsToWorld(newItem(ITEM_BONE,1),e->x+8, e->y+8, (rand()%2) + 1); - if(rand()%2==0) addItemsToWorld(newItem(ITEM_ARROW_STONE,1),e->x+8, e->y+8, 1); + addItemsToWorld(newItem(ITEM_BONE,1), e->level, e->x+8, e->y+8, (rand()%2) + 1); + if(rand()%2==0) addItemsToWorld(newItem(ITEM_ARROW_STONE,1), e->level, e->x+8, e->y+8, 1); } else if(e->type == ENTITY_KNIGHT) { - addItemsToWorld(newItem(ITEM_IRONINGOT,1),e->x+8, e->y+8, (rand()%2) + 1); + addItemsToWorld(newItem(ITEM_IRONINGOT,1), e->level, e->x+8, e->y+8, (rand()%2) + 1); } - player.p.score += 50 * (e->hostile.lvl + 1); - removeEntityFromList(e,e->level,&eManager); - if(currentLevel != 5) trySpawn(3, currentLevel); + if(damager!=NULL && damager->type==ENTITY_PLAYER) damager->p.data->score += 50 * (e->hostile.lvl + 1); + removeEntityFromList(e, e->level, &eManager); + if(e->level != 5) trySpawn(3, e->level); return; } break; case ENTITY_SLIME: e->slime.health -= damage; if(e->slime.health < 1){ - addItemsToWorld(newItem(ITEM_SLIME,1),e->x+8, e->y+8, (rand()%2) + 1); - player.p.score += 25 * (e->slime.lvl + 1); - removeEntityFromList(e,e->level,&eManager); - if(currentLevel != 5) trySpawn(3, currentLevel); + addItemsToWorld(newItem(ITEM_SLIME,1), e->level, e->x+8, e->y+8, (rand()%2) + 1); + if(damager!=NULL && damager->type==ENTITY_PLAYER) damager->p.data->score += 25 * (e->slime.lvl + 1); + removeEntityFromList(e, e->level, &eManager); + if(e->level != 5) trySpawn(3, e->level); return; } break; @@ -271,13 +177,16 @@ void hurtEntity(Entity* e, int damage, int dir, u32 hurtColor){ e->wizard.health -= damage; airWizardHealthDisplay = e->wizard.health; if(e->wizard.health < 1){ - addItemsToWorld(newItem(ITEM_MAGIC_DUST,1),e->x+8, e->y+8, (rand()%2) + 2); + addItemsToWorld(newItem(ITEM_MAGIC_DUST,1), e->level, e->x+8, e->y+8, (rand()%2) + 2); removeEntityFromList(e,e->level,&eManager); playSound(snd_bossdeath); - player.p.score += 1000; - if(!player.p.hasWonSaved) player.p.endTimer = 60; - if(!player.p.hasWonSaved) player.p.hasWon = true; - player.p.hasWonSaved = true; + + for(i=0; ipassive.health -= damage; if(e->passive.health < 1){ if(e->passive.mtype==0) { - addItemsToWorld(newItem(ITEM_WOOL,1),e->x+8, e->y+8, (rand()%3) + 1); + addItemsToWorld(newItem(ITEM_WOOL,1), e->level, e->x+8, e->y+8, (rand()%3) + 1); } else if(e->passive.mtype==1) { - addItemsToWorld(newItem(ITEM_PORK_RAW,1),e->x+8, e->y+8, (rand()%2) + 1); + addItemsToWorld(newItem(ITEM_PORK_RAW,1), e->level, e->x+8, e->y+8, (rand()%2) + 1); } else if(e->passive.mtype==2) { - addItemsToWorld(newItem(ITEM_BEEF_RAW,1),e->x+8, e->y+8, (rand()%2) + 1); + addItemsToWorld(newItem(ITEM_BEEF_RAW,1), e->level, e->x+8, e->y+8, (rand()%2) + 1); if((rand()%2)==0) { - addItemsToWorld(newItem(ITEM_LEATHER,1),e->x+8, e->y+8, 1); + addItemsToWorld(newItem(ITEM_LEATHER,1), e->level, e->x+8, e->y+8, 1); } } - player.p.score += 10; - removeEntityFromList(e,e->level,&eManager); - if(currentLevel != 5) trySpawn(3, currentLevel); + if(damager!=NULL && damager->type==ENTITY_PLAYER) damager->p.data->score += 10; + removeEntityFromList(e, e->level, &eManager); + if(e->level != 5) trySpawn(3, e->level); return; } break; case ENTITY_DRAGON: e->dragon.health -= damage; if(e->dragon.health < 1){ - addItemsToWorld(newItem(ITEM_DRAGON_EGG,1),e->x+8, e->y+8, 1); - addItemsToWorld(newItem(ITEM_DRAGON_SCALE,1),e->x+8, e->y+8, (rand()%11) + 10); - removeEntityFromList(e,e->level,&eManager); + addItemsToWorld(newItem(ITEM_DRAGON_EGG,1), e->level, e->x+8, e->y+8, 1); + addItemsToWorld(newItem(ITEM_DRAGON_SCALE,1), e->level, e->x+8, e->y+8, (rand()%11) + 10); + removeEntityFromList(e, e->level, &eManager); playSound(snd_bossdeath); - player.p.score += 1000; + for(i=0; ihurtTime = 10; } -bool ItemVsEntity(Item* item, Entity* e, int dir){ +bool ItemVsEntity(PlayerData *pd, Item *item, Entity *e, int dir) { + //TODO: To much duplicated code switch(item->id){ case ITEM_POWGLOVE: if(e->type == ENTITY_FURNITURE){ + //Important: close all crafting windows using this furniture (only applies to chest) or else they will write invalid memory + for(int i=0; ientityFurniture.itemID,0); if(e->entityFurniture.itemID == ITEM_CHEST) nItem.chestPtr = e->entityFurniture.inv; - pushItemToInventoryFront(nItem, player.p.inv); - removeEntityFromList(e,currentLevel,&eManager); - player.p.activeItem = &player.p.inv->items[0]; - player.p.isCarrying = true; + pushItemToInventoryFront(nItem, &(pd->inventory)); + + removeEntityFromList(e, e->level, &eManager); + pd->activeItem = &(pd->inventory.items[0]); + pd->entity.p.isCarrying = true; return true; } break; case TOOL_AXE: @@ -381,12 +301,13 @@ bool ItemVsEntity(Item* item, Entity* e, int dir){ case ENTITY_SLIME: case ENTITY_AIRWIZARD: case ENTITY_DRAGON: - if(playerUseEnergy(4-item->countLevel)) hurtEntity(e,(item->countLevel + 1) * 2 + (rand()%4),dir,0xFF0000FF); - else hurtEntity(e,1+rand()%3,dir,0xFF0000FF); + if(playerUseEnergy(pd, 4-item->countLevel)) hurtEntity(e, (item->countLevel + 1) * 2 + (rand()%4), dir, 0xFF0000FF, &(pd->entity)); + else hurtEntity(e, 1+rand()%3, dir, 0xFF0000FF, &(pd->entity)); return true; case ENTITY_MAGIC_PILLAR: - playSound(snd_monsterHurt); + playSoundPositioned(snd_monsterHurt, e->level, e->x, e->y); + removeEntityFromList(e, e->level, &eManager); return true; } break; @@ -399,12 +320,13 @@ bool ItemVsEntity(Item* item, Entity* e, int dir){ case ENTITY_SLIME: case ENTITY_AIRWIZARD: case ENTITY_DRAGON: - if(playerUseEnergy(4-item->countLevel)) hurtEntity(e,(item->countLevel+1)*3+(rand()%(2+item->countLevel*item->countLevel*2)),dir,0xFF0000FF); - else hurtEntity(e,1+rand()%3,dir,0xFF0000FF); + if(playerUseEnergy(pd, 4-item->countLevel)) hurtEntity(e, (item->countLevel+1)*3+(rand()%(2+item->countLevel*item->countLevel*2)), dir, 0xFF0000FF, &(pd->entity)); + else hurtEntity(e, 1+rand()%3, dir, 0xFF0000FF, &(pd->entity)); return true; case ENTITY_MAGIC_PILLAR: - playSound(snd_monsterHurt); + playSoundPositioned(snd_monsterHurt, e->level, e->x, e->y); + removeEntityFromList(e, e->level, &eManager); return true; } break; @@ -417,11 +339,12 @@ bool ItemVsEntity(Item* item, Entity* e, int dir){ case ENTITY_SLIME: case ENTITY_AIRWIZARD: case ENTITY_DRAGON: - hurtEntity(e,1+rand()%3,dir,0xFF0000FF); + hurtEntity(e, 1+rand()%3, dir, 0xFF0000FF, &(pd->entity)); return true; case ENTITY_MAGIC_PILLAR: - playSound(snd_monsterHurt); + playSoundPositioned(snd_monsterHurt, e->level, e->x, e->y); + removeEntityFromList(e, e->level, &eManager); return true; } break; @@ -429,87 +352,17 @@ bool ItemVsEntity(Item* item, Entity* e, int dir){ return false; } -bool playerUseItem() { - if(player.p.activeItem->id == TOOL_BOW) { - int aitemID = 0; - Item * aitem; - - Item * item = getItemFromInventory(ITEM_ARROW_WOOD, player.p.inv); - if(item!=NULL) { - aitemID = ITEM_ARROW_WOOD; - aitem = item; - } - item = getItemFromInventory(ITEM_ARROW_STONE, player.p.inv); - if(item!=NULL) { - aitemID = ITEM_ARROW_STONE; - aitem = item; - } - item = getItemFromInventory(ITEM_ARROW_IRON, player.p.inv); - if(item!=NULL) { - aitemID = ITEM_ARROW_IRON; - aitem = item; - } - item = getItemFromInventory(ITEM_ARROW_GOLD, player.p.inv); - if(item!=NULL) { - aitemID = ITEM_ARROW_GOLD; - aitem = item; - } - item = getItemFromInventory(ITEM_ARROW_GEM, player.p.inv); - if(item!=NULL) { - aitemID = ITEM_ARROW_GEM; - aitem = item; - } - - if(aitemID!=0) { - --aitem->countLevel; - if (isItemEmpty(aitem)) { - removeItemFromInventory(aitem->slotNum, player.p.inv); - } - - switch(player.p.dir) { - case 0: - addEntityToList(newArrowEntity(&player, aitemID, 0, 2, currentLevel), &eManager); - break; - case 1: - addEntityToList(newArrowEntity(&player, aitemID, 0, -2, currentLevel), &eManager); - break; - case 2: - addEntityToList(newArrowEntity(&player, aitemID, -2, 0, currentLevel), &eManager); - break; - case 3: - addEntityToList(newArrowEntity(&player, aitemID, 2, 0, currentLevel), &eManager); - break; - } - return true; - } - } - return false; -} - -bool interact(int x0, int y0, int x1, int y1) { - Entity * es[eManager.lastSlot[currentLevel]]; - int eSize = getEntities(es, x0, y0, x1, y1); - int i; - for (i = 0; i < eSize; ++i) { - Entity * ent = es[i]; - if (ent != &player){ - if (ItemVsEntity(player.p.activeItem, ent, player.p.dir)) return true; - } - } - return false; -} - void EntityVsEntity(Entity* e1, Entity* e2){ int damage = 1; switch(e1->type){ - case ENTITY_PLAYER: playerEntityInteract(e2); break; + case ENTITY_PLAYER: playerEntityInteract(e1->p.data, e2); break; case ENTITY_ZOMBIE: case ENTITY_SKELETON: case ENTITY_KNIGHT: if(e2->type == ENTITY_PLAYER){ - if(e1->type == ENTITY_ZOMBIE) hurtEntity(e2, 2, e1->hostile.dir, 0xFFAF00FF); - else if(e1->type == ENTITY_SKELETON) hurtEntity(e2, 1, e1->hostile.dir, 0xFFAF00FF); - else if(e1->type == ENTITY_KNIGHT) hurtEntity(e2, 3, e1->hostile.dir, 0xFFAF00FF); + if(e1->type == ENTITY_ZOMBIE) hurtEntity(e2, 2, e1->hostile.dir, 0xFFAF00FF, e1); + else if(e1->type == ENTITY_SKELETON) hurtEntity(e2, 1, e1->hostile.dir, 0xFFAF00FF, e1); + else if(e1->type == ENTITY_KNIGHT) hurtEntity(e2, 3, e1->hostile.dir, 0xFFAF00FF, e1); switch(e1->hostile.dir){ case 0: e1->yKnockback = -4; break; case 1: e1->yKnockback = +4; break; @@ -520,7 +373,7 @@ void EntityVsEntity(Entity* e1, Entity* e2){ break; case ENTITY_SLIME: if(e2->type == ENTITY_PLAYER){ - hurtEntity(e2, 1, e1->slime.dir, 0xFFAF00FF); + hurtEntity(e2, 1, e1->slime.dir, 0xFFAF00FF, e1); switch(e1->slime.dir){ case 0: e1->yKnockback = -4; break; case 1: e1->yKnockback = +4; break; @@ -530,16 +383,16 @@ void EntityVsEntity(Entity* e1, Entity* e2){ } break; case ENTITY_AIRWIZARD: - if(e2->type == ENTITY_PLAYER) hurtEntity(e2, 3, e1->wizard.dir, 0xFFAF00FF); + if(e2->type == ENTITY_PLAYER) hurtEntity(e2, 3, e1->wizard.dir, 0xFFAF00FF, e1); break; case ENTITY_SPARK: - if(e2 != e1->spark.parent) hurtEntity(e2, 1, -1, 0xFFAF00FF); + if(e2 != e1->spark.parent) hurtEntity(e2, 1, -1, 0xFFAF00FF, e1); break; case ENTITY_DRAGON: - if(e2->type == ENTITY_PLAYER) hurtEntity(e2, 3, e1->dragon.dir, 0xFFAF00FF); + if(e2->type == ENTITY_PLAYER) hurtEntity(e2, 3, e1->dragon.dir, 0xFFAF00FF, e1); break; case ENTITY_DRAGONPROJECTILE: - if(e2 != e1->dragonFire.parent) hurtEntity(e2, 1, -1, 0xFFAF00FF); + if(e2 != e1->dragonFire.parent) hurtEntity(e2, 1, -1, 0xFFAF00FF, e1); break; case ENTITY_ARROW: switch(e1->arrow.itemID) { @@ -562,12 +415,12 @@ void EntityVsEntity(Entity* e1, Entity* e2){ if(e1->arrow.parent->type == ENTITY_PLAYER) { if(e2->type != ENTITY_PLAYER) { - hurtEntity(e2, damage, -1, 0xFF0000FF); + hurtEntity(e2, damage, -1, 0xFF0000FF, e1); removeEntityFromList(e1, e1->level, &eManager); } } else { if(e2->type == ENTITY_PLAYER) { - hurtEntity(e2, damage, -1, 0xFFAF00FF); + hurtEntity(e2, damage, -1, 0xFFAF00FF, e1); removeEntityFromList(e1, e1->level, &eManager); } } @@ -672,403 +525,309 @@ u32 getTileColor(int tile){ } } -void healPlayer(int amount){ - player.p.health += amount; - if(player.p.health > 10) player.p.health = 10; - char healText[11]; - sprintf(healText, "%d", amount); - addEntityToList(newTextParticleEntity(healText,0xFF00FF00,player.x,player.y,currentLevel), &eManager); -} - -s8 itemTileInteract(int tile, Item* item, int x, int y, int px, int py, int dir){ +s8 itemTileInteract(int tile, PlayerData *pd, Item *item, s8 level, int x, int y, int px, int py, int dir){ - // Furniture items + // Furniture items if(item->id > 27 && item->id < 51){ - if(!tileIsSolid(getTile(x,y), NULL)){ - addEntityToList(newFurnitureEntity(item->id,item->chestPtr, (x<<4)+8, (y<<4)+8, currentLevel), &eManager); + if(!tileIsSolid(getTile(level, x, y), NULL)){ + addEntityToList(newFurnitureEntity(item->id, item->chestPtr, (x<<4)+8, (y<<4)+8, level), &eManager); removeItemFromCurrentInv(item); - player.p.activeItem = &noItem; + pd->activeItem = &noItem; return 2; } return 0; } - // Health items - switch(item->id){ - case ITEM_APPLE: - if(player.p.health < 10 && playerUseEnergy(2)){ - healPlayer(1); - --item->countLevel; - } - return 0; - case ITEM_FLESH: - if(player.p.health < 10 && playerUseEnergy(4+(rand()%4))){ - healPlayer(1); - --item->countLevel; - } - return 0; - case ITEM_BREAD: - if(player.p.health < 10 && playerUseEnergy(3)){ - healPlayer(2); - --item->countLevel; - } - return 0; - case ITEM_PORK_RAW: - if(player.p.health < 10 && playerUseEnergy(4+(rand()%4))){ - healPlayer(1); - --item->countLevel; - } - return 0; - case ITEM_PORK_COOKED: - if(player.p.health < 10 && playerUseEnergy(3)){ - healPlayer(3); - --item->countLevel; - } - return 0; - case ITEM_BEEF_RAW: - if(player.p.health < 10 && playerUseEnergy(4+(rand()%4))){ - healPlayer(1); - --item->countLevel; - } - return 0; - case ITEM_BEEF_COOKED: - if(player.p.health < 10 && playerUseEnergy(3)){ - healPlayer(4); - --item->countLevel; - } - return 0; - //special item - case ITEM_WIZARD_SUMMON: - if(currentLevel==0) { - --item->countLevel; - - airWizardHealthDisplay = 2000; - addEntityToList(newAirWizardEntity(630, 820, 0), &eManager); - } - return 0; - } - switch(tile){ case TILE_TREE: - if(item->id == TOOL_AXE && playerUseEnergy(4-item->countLevel)){ - playerHurtTile(tile, x, y, (rand()%10) + (item->countLevel) * 5 + 10, player.p.dir); + if(item->id == TOOL_AXE && playerUseEnergy(pd, 4-item->countLevel)){ + playerHurtTile(pd, tile, level, x, y, (rand()%10) + (item->countLevel) * 5 + 10, pd->entity.p.dir); return 1; } break; case TILE_ROCK: - if(item->id == TOOL_PICKAXE && playerUseEnergy(4-item->countLevel)){ - playerHurtTile(tile, x, y, (rand()%10) + (item->countLevel) * 5 + 10, player.p.dir); - return 1; - } break; case TILE_HARDROCK: - if(item->id == TOOL_PICKAXE && playerUseEnergy(4-item->countLevel)){ - playerHurtTile(tile, x, y, (rand()%10) + (item->countLevel) * 5 + 10, player.p.dir); + if(item->id == TOOL_PICKAXE && playerUseEnergy(pd, 4-item->countLevel)){ + playerHurtTile(pd, tile, level, x, y, (rand()%10) + (item->countLevel) * 5 + 10, pd->entity.p.dir); return 1; } break; case TILE_IRONORE: - if(item->id == TOOL_PICKAXE && playerUseEnergy(4-item->countLevel)){ - playerHurtTile(tile, x, y, 1, player.p.dir); - return 1; - } break; case TILE_GOLDORE: - if(item->id == TOOL_PICKAXE && playerUseEnergy(4-item->countLevel)){ - playerHurtTile(tile, x, y, 1, player.p.dir); - return 1; - } break; case TILE_GEMORE: - if(item->id == TOOL_PICKAXE && playerUseEnergy(4-item->countLevel)){ - playerHurtTile(tile, x, y, 1, player.p.dir); - return 1; - } break; case TILE_CLOUDCACTUS: - if(item->id == TOOL_PICKAXE && playerUseEnergy(4-item->countLevel)){ - playerHurtTile(tile, x, y, 1, player.p.dir); + if(item->id == TOOL_PICKAXE && playerUseEnergy(pd, 4-item->countLevel)){ + playerHurtTile(pd, tile, level, x, y, 1, pd->entity.p.dir); return 1; } break; case TILE_GRASS: - if(item->id == TOOL_HOE && playerUseEnergy(4-item->countLevel)){ - setTile(TILE_FARM,x,y); + if(item->id == TOOL_HOE && playerUseEnergy(pd, 4-item->countLevel)){ + setTile(TILE_FARM, level, x, y); return 1; } else if(item->id == ITEM_ACORN){ - setTile(TILE_SAPLING_TREE,x,y); --item->countLevel; + setTile(TILE_SAPLING_TREE, level, x, y); --item->countLevel; return 1; } else if(item->id == ITEM_FLOWER){ - setTile(TILE_FLOWER,x,y); --item->countLevel; - setData(rand()%4,x,y); // determines mirroring. + setTile(TILE_FLOWER, level, x, y); --item->countLevel; + setData(rand()%4, level, x, y); // determines mirroring. return 1; } else if(item->id == ITEM_WALL_WOOD){ - setTile(TILE_WOOD_WALL,x,y); --item->countLevel; + setTile(TILE_WOOD_WALL, level, x, y); --item->countLevel; return 1; } else if(item->id == ITEM_WALL_STONE){ - setTile(TILE_STONE_WALL,x,y); --item->countLevel; + setTile(TILE_STONE_WALL, level, x, y); --item->countLevel; return 1; } else if(item->id == ITEM_WALL_IRON){ - setTile(TILE_IRON_WALL,x,y); --item->countLevel; + setTile(TILE_IRON_WALL, level, x, y); --item->countLevel; return 1; } else if(item->id == ITEM_WALL_GOLD){ - setTile(TILE_GOLD_WALL,x,y); --item->countLevel; + setTile(TILE_GOLD_WALL, level, x, y); --item->countLevel; return 1; } else if(item->id == ITEM_WALL_GEM){ - setTile(TILE_GEM_WALL,x,y); --item->countLevel; + setTile(TILE_GEM_WALL, level, x, y); --item->countLevel; return 1; } else if(item->id == ITEM_BOOKSHELVES){ - setTile(TILE_BOOKSHELVES,x,y); --item->countLevel; - data[currentLevel][x+y*128] = rand()%3; + setTile(TILE_BOOKSHELVES, level, x, y); --item->countLevel; + setData(rand()%3, level, x, y); //determines sprite return 1; } - else if(item->id == TOOL_SHOVEL && playerUseEnergy(4-item->countLevel)){ - if(rand()%5==0)addEntityToList(newItemEntity(newItem(ITEM_SEEDS,1),(x<<4)+8, (y<<4)+8,currentLevel),&eManager); - setTile(TILE_DIRT,x,y); + else if(item->id == TOOL_SHOVEL && playerUseEnergy(pd, 4-item->countLevel)){ + if(rand()%5==0) addItemsToWorld(newItem(ITEM_SEEDS,1), level, (x<<4)+8, (y<<4)+8, 1); + setTile(TILE_DIRT, level, x, y); return 1; } break; case TILE_SAND: if(item->id == ITEM_CACTUS){ - setTile(TILE_SAPLING_CACTUS,x,y); + setTile(TILE_SAPLING_CACTUS, level, x, y); --item->countLevel; return 1; } - else if(item->id == TOOL_SHOVEL && playerUseEnergy(4-item->countLevel)){ - addEntityToList(newItemEntity(newItem(ITEM_SAND,1), (x<<4)+8, (y<<4)+8, currentLevel), &eManager); - setTile(TILE_DIRT,x,y); + else if(item->id == TOOL_SHOVEL && playerUseEnergy(pd, 4-item->countLevel)){ + addItemsToWorld(newItem(ITEM_SAND,1), level, (x<<4)+8, (y<<4)+8, 1); + setTile(TILE_DIRT, level, x, y); return 1; } break; case TILE_DIRT: - if(item->id == TOOL_HOE && currentLevel==1 && playerUseEnergy(4-item->countLevel)){ - setTile(TILE_FARM,x,y); + if(item->id == TOOL_HOE && pd->entity.level==1 && playerUseEnergy(pd, 4-item->countLevel)){ + setTile(TILE_FARM, level, x, y); return 1; } else if(item->id == ITEM_WALL_WOOD){ - setTile(TILE_WOOD_WALL,x,y); --item->countLevel; + setTile(TILE_WOOD_WALL, level, x, y); --item->countLevel; return 1; } else if(item->id == ITEM_WALL_STONE){ - setTile(TILE_STONE_WALL,x,y); --item->countLevel; + setTile(TILE_STONE_WALL, level, x, y); --item->countLevel; return 1; } else if(item->id == ITEM_WALL_IRON){ - setTile(TILE_IRON_WALL,x,y); --item->countLevel; + setTile(TILE_IRON_WALL, level, x, y); --item->countLevel; return 1; } else if(item->id == ITEM_WALL_GOLD){ - setTile(TILE_GOLD_WALL,x,y); --item->countLevel; + setTile(TILE_GOLD_WALL, level, x, y); --item->countLevel; return 1; } else if(item->id == ITEM_WALL_GEM){ - setTile(TILE_GEM_WALL,x,y); --item->countLevel; + setTile(TILE_GEM_WALL, level, x, y); --item->countLevel; return 1; } else if(item->id == ITEM_BOOKSHELVES){ - setTile(TILE_BOOKSHELVES,x,y); --item->countLevel; - data[currentLevel][x+y*128] = rand()%3; + setTile(TILE_BOOKSHELVES, level, x, y); --item->countLevel; + setData(rand()%3, level, x, y); //determines sprite return 1; } else if(item->id == ITEM_WOOD) { - setTile(TILE_WOOD_FLOOR,x,y); --item->countLevel; + setTile(TILE_WOOD_FLOOR, level, x, y); --item->countLevel; return 1; } else if(item->id == ITEM_SAND){ - setTile(TILE_SAND,x,y); --item->countLevel; + setTile(TILE_SAND, level, x, y); --item->countLevel; return 1; } - else if(item->id == TOOL_SHOVEL && playerUseEnergy(4-item->countLevel)){ - addEntityToList(newItemEntity(newItem(ITEM_DIRT,1), (x<<4)+8, (y<<4)+8, currentLevel), &eManager); - setTile(TILE_HOLE,x,y); + else if(item->id == TOOL_SHOVEL && playerUseEnergy(pd, 4-item->countLevel)){ + addItemsToWorld(newItem(ITEM_DIRT,1), level, (x<<4)+8, (y<<4)+8, 1); + setTile(TILE_HOLE, level, x, y); return 1; } break; case TILE_HOLE: if(item->id == ITEM_DIRT){ - setTile(TILE_DIRT,x,y); + setTile(TILE_DIRT, level, x, y); --item->countLevel; return 1; } - else if(item->id == TOOL_BUCKET && item->countLevel == 1 && playerUseEnergy(4)) { - setTile(TILE_WATER,x,y); + else if(item->id == TOOL_BUCKET && item->countLevel == 1 && playerUseEnergy(pd, 4)) { + setTile(TILE_WATER, level, x, y); item->countLevel = 0; } - else if(item->id == TOOL_BUCKET && item->countLevel == 2 && playerUseEnergy(4)) { - setTile(TILE_LAVA,x,y); + else if(item->id == TOOL_BUCKET && item->countLevel == 2 && playerUseEnergy(pd, 4)) { + setTile(TILE_LAVA, level, x, y); item->countLevel = 0; } break; case TILE_WATER: if(item->id == ITEM_DIRT){ - setTile(TILE_DIRT,x,y); + setTile(TILE_DIRT, level, x, y); --item->countLevel; return 1; } - else if(item->id == TOOL_BUCKET && item->countLevel == 0 && playerUseEnergy(4)) { - setTile(TILE_HOLE,x,y); + else if(item->id == TOOL_BUCKET && item->countLevel == 0 && playerUseEnergy(pd, 4)) { + setTile(TILE_HOLE, level, x, y); item->countLevel = 1; } break; case TILE_LAVA: if(item->id == ITEM_DIRT){ - setTile(TILE_DIRT,x,y); + setTile(TILE_DIRT, level, x, y); --item->countLevel; return 1; } - else if(item->id == TOOL_BUCKET && item->countLevel == 0 && playerUseEnergy(4)) { - setTile(TILE_HOLE,x,y); + else if(item->id == TOOL_BUCKET && item->countLevel == 0 && playerUseEnergy(pd, 4)) { + setTile(TILE_HOLE, level, x, y); item->countLevel = 2; } break; case TILE_NULL: if(item->id == ITEM_CLOUD){ - setTile(TILE_CLOUD,x,y); + setTile(TILE_CLOUD, level, x, y); --item->countLevel; return 1; } break; case TILE_FARM: if(item->id == ITEM_SEEDS){ - setTile(TILE_WHEAT,x,y); - setData(0,x,y); + setTile(TILE_WHEAT, level, x, y); + setData(0, level, x, y); --item->countLevel; return 1; } break; case TILE_WHEAT: if(item->id == TOOL_HOE){ - if(getData(x,y) > -1){ - int age = getData(x,y); + if(getData(level, x, y) > -1){ + int age = getData(level, x, y); int count = (rand() % 2); if(age >= 80) count = (rand()%2) + 1; - addItemsToWorld(newItem(ITEM_SEEDS,1),(x<<4)+8,(y<<4)+8,count); + addItemsToWorld(newItem(ITEM_SEEDS,1), level, (x<<4)+8, (y<<4)+8, count); count = 0; - if(age == 100)count = (rand()%3) + 2; - else if(age >= 80)count = (rand()%2) + 1; - addItemsToWorld(newItem(ITEM_WHEAT,1),(x<<4)+8,(y<<4)+8,count); - setTile(TILE_DIRT,x,y); + if(age == 100) count = (rand()%3) + 2; + else if(age >= 80) count = (rand()%2) + 1; + addItemsToWorld(newItem(ITEM_WHEAT,1), level, (x<<4)+8, (y<<4)+8, count); + setTile(TILE_DIRT, level, x, y); } } break; case TILE_WOOD_WALL: - if(item->id == TOOL_AXE && playerUseEnergy(4-item->countLevel)){ - playerHurtTile(tile, x, y, (rand()%10) + (item->countLevel) * 5 + 10, player.p.dir); + case TILE_BOOKSHELVES: + if(item->id == TOOL_AXE && playerUseEnergy(pd, 4-item->countLevel)){ + playerHurtTile(pd, tile, level, x, y, (rand()%10) + (item->countLevel) * 5 + 10, pd->entity.p.dir); return 1; } break; case TILE_STONE_WALL: - if(item->id == TOOL_PICKAXE && playerUseEnergy(4-item->countLevel)){ - playerHurtTile(tile, x, y, (rand()%10) + (item->countLevel) * 5 + 10, player.p.dir); - return 1; - } break; case TILE_IRON_WALL: - if(item->id == TOOL_PICKAXE && playerUseEnergy(4-item->countLevel)){ - playerHurtTile(tile, x, y, (rand()%10) + (item->countLevel) * 5 + 10, player.p.dir); - return 1; - } break; case TILE_GOLD_WALL: - if(item->id == TOOL_PICKAXE && playerUseEnergy(4-item->countLevel)){ - playerHurtTile(tile, x, y, (rand()%10) + (item->countLevel) * 5 + 10, player.p.dir); - return 1; - } break; case TILE_GEM_WALL: - if(item->id == TOOL_PICKAXE && playerUseEnergy(4-item->countLevel)){ - playerHurtTile(tile, x, y, (rand()%10) + (item->countLevel) * 5 + 10, player.p.dir); - return 1; - } break; - case TILE_BOOKSHELVES: - if(item->id == TOOL_AXE && playerUseEnergy(4-item->countLevel)){ - playerHurtTile(tile, x, y, (rand()%10) + (item->countLevel) * 5 + 10, player.p.dir); + if(item->id == TOOL_PICKAXE && playerUseEnergy(pd, 4-item->countLevel)){ + playerHurtTile(pd, tile, level, x, y, (rand()%10) + (item->countLevel) * 5 + 10, pd->entity.p.dir); return 1; } break; case TILE_WOOD_FLOOR: - if(item->id == TOOL_AXE && playerUseEnergy(4-item->countLevel)){ - addEntityToList(newItemEntity(newItem(ITEM_WOOD,1), (x<<4)+8, (y<<4)+8, currentLevel), &eManager); - setTile(TILE_DIRT,x,y); + if(item->id == TOOL_AXE && playerUseEnergy(pd, 4-item->countLevel)){ + addItemsToWorld(newItem(ITEM_WOOD,1), level, (x<<4)+8, (y<<4)+8, 1); + setTile(TILE_DIRT, level, x, y); } break; } return 0; } -void tickTile(int x, int y){ - int tile = getTile(x,y); - int data = getData(x,y); +void tickTile(s8 level, int x, int y){ + int tile = getTile(level, x, y); + int data = getData(level, x, y); switch(tile){ case TILE_SAPLING_TREE: - if(season!=3) { - setData(++data,x,y); - if(data>100){setData(0,x,y); setTile(TILE_TREE,x,y);} + if(worldData.season!=3) { + setData(++data, level, x, y); + if(data>100){setData(0, level, x, y); setTile(TILE_TREE, level, x, y);} } break; case TILE_TREE: - if(eManager.lastSlot[currentLevel]<800 && (daytime>18000 || daytime<5000) && rand()%800==0) { + if(eManager.lastSlot[level]<800 && (worldData.daytime>18000 || worldData.daytime<5000) && rand()%800==0) { //check for nearby glowworms + //TODO: This should really use getEntities int i = 0; - for (i = 0; i < eManager.lastSlot[currentLevel]; ++i) { - Entity * e = &eManager.entities[currentLevel][i]; + for (i = 0; i < eManager.lastSlot[level]; ++i) { + Entity * e = &eManager.entities[level][i]; if(e->type==ENTITY_GLOWWORM && ((e->x)-(x<<4))*((e->x)-(x<<4))+((e->y)-(y<<4))*((e->y)-(y<<4)) < (2<<4)*(2<<4)) return; } - addEntityToList(newGlowwormEntity((x<<4)+8,(y<<4)+8,currentLevel), &eManager); + addEntityToList(newGlowwormEntity((x<<4)+8, (y<<4)+8, level), &eManager); } break; case TILE_SAPLING_CACTUS: - if(season!=3) { - setData(++data,x,y); - if(data>100){setData(0,x,y); setTile(TILE_CACTUS,x,y);} + if(worldData.season!=3) { + setData(++data, level, x, y); + if(data>100){setData(0, level, x, y); setTile(TILE_CACTUS, level, x, y);} } break; case TILE_WHEAT: - if(data<100 && season!=3) setData(++data,x,y); + if(data<100 && worldData.season!=3) setData(++data, level, x, y); break; case TILE_WATER: - if(getTile(x+1,y)==TILE_HOLE) setTile(TILE_WATER,x+1,y); - if(getTile(x-1,y)==TILE_HOLE) setTile(TILE_WATER,x-1,y); - if(getTile(x,y+1)==TILE_HOLE) setTile(TILE_WATER,x,y+1); - if(getTile(x,y-1)==TILE_HOLE) setTile(TILE_WATER,x,y-1); - if(currentLevel==1 && season==3 && rand()%12==0) { - if(getTile(x+1,y)!=TILE_WATER) setTile(TILE_ICE,x,y); - if(getTile(x-1,y)!=TILE_WATER) setTile(TILE_ICE,x,y); - if(getTile(x,y+1)!=TILE_WATER) setTile(TILE_ICE,x,y); - if(getTile(x,y-1)!=TILE_WATER) setTile(TILE_ICE,x,y); + if(getTile(level, x+1, y)==TILE_HOLE) setTile(TILE_WATER, level, x+1, y); + if(getTile(level, x-1, y)==TILE_HOLE) setTile(TILE_WATER, level, x-1, y); + if(getTile(level, x, y+1)==TILE_HOLE) setTile(TILE_WATER, level, x, y+1); + if(getTile(level, x, y-1)==TILE_HOLE) setTile(TILE_WATER, level, x, y-1); + if(level==1 && worldData.season==3 && rand()%12==0) { + if(getTile(level, x+1, y)!=TILE_WATER) setTile(TILE_ICE, level, x, y); + if(getTile(level, x-1, y)!=TILE_WATER) setTile(TILE_ICE, level, x, y); + if(getTile(level, x, y+1)!=TILE_WATER) setTile(TILE_ICE, level, x, y); + if(getTile(level, x, y-1)!=TILE_WATER) setTile(TILE_ICE, level, x, y); } break; case TILE_LAVA: - if(getTile(x+1,y)==TILE_HOLE) setTile(TILE_LAVA,x+1,y); - if(getTile(x-1,y)==TILE_HOLE) setTile(TILE_LAVA,x-1,y); - if(getTile(x,y+1)==TILE_HOLE) setTile(TILE_LAVA,x,y+1); - if(getTile(x,y-1)==TILE_HOLE) setTile(TILE_LAVA,x,y-1); + if(getTile(level, x+1, y)==TILE_HOLE) setTile(TILE_LAVA, level, x+1, y); + if(getTile(level, x-1, y)==TILE_HOLE) setTile(TILE_LAVA, level, x-1, y); + if(getTile(level, x, y+1)==TILE_HOLE) setTile(TILE_LAVA, level, x, y+1); + if(getTile(level, x, y-1)==TILE_HOLE) setTile(TILE_LAVA, level, x, y-1); - if(getTile(x+1,y)==TILE_WATER || getTile(x-1,y)==TILE_WATER || getTile(x,y+1)==TILE_WATER || getTile(x,y-1)==TILE_WATER) { - setTile(TILE_ROCK,x,y); + if(getTile(level, x+1, y)==TILE_WATER || getTile(level, x-1, y)==TILE_WATER || getTile(level, x, y+1)==TILE_WATER || getTile(level, x, y-1)==TILE_WATER) { + setTile(TILE_ROCK, level, x, y); } break; case TILE_HOLE: // This makes water flow slightly faster than lava - if(getTile(x+1,y)==TILE_WATER) setTile(TILE_WATER,x,y); - if(getTile(x-1,y)==TILE_WATER) setTile(TILE_WATER,x,y); - if(getTile(x,y+1)==TILE_WATER) setTile(TILE_WATER,x,y); - if(getTile(x,y-1)==TILE_WATER) setTile(TILE_WATER,x,y); + if(getTile(level, x+1, y)==TILE_WATER) setTile(TILE_WATER, level, x, y); + if(getTile(level, x-1, y)==TILE_WATER) setTile(TILE_WATER, level, x, y); + if(getTile(level, x, y+1)==TILE_WATER) setTile(TILE_WATER, level, x, y); + if(getTile(level, x, y-1)==TILE_WATER) setTile(TILE_WATER, level, x, y); break; case TILE_GRASS: - if(getTile(x+1,y)==TILE_DIRT) if((rand()%25) == 0) setTile(TILE_GRASS,x+1,y); - if(getTile(x-1,y)==TILE_DIRT) if((rand()%25) == 0) setTile(TILE_GRASS,x-1,y); - if(getTile(x,y+1)==TILE_DIRT) if((rand()%25) == 0) setTile(TILE_GRASS,x,y+1); - if(getTile(x,y-1)==TILE_DIRT) if((rand()%25) == 0) setTile(TILE_GRASS,x,y-1); + if(getTile(level, x+1, y)==TILE_DIRT) if((rand()%25) == 0) setTile(TILE_GRASS, level, x+1, y); + if(getTile(level, x-1, y)==TILE_DIRT) if((rand()%25) == 0) setTile(TILE_GRASS, level, x-1, y); + if(getTile(level, x, y+1)==TILE_DIRT) if((rand()%25) == 0) setTile(TILE_GRASS, level, x, y+1); + if(getTile(level, x, y-1)==TILE_DIRT) if((rand()%25) == 0) setTile(TILE_GRASS, level, x, y-1); break; case TILE_SAND: - if(data > 0) setData(--data,x,y); + if(data > 0) setData(--data, level, x, y); break; case TILE_CLOUD: - if((rand()%24000)==0) setTile(TILE_CLOUDCACTUS,x,y); + if((rand()%24000)==0) setTile(TILE_CLOUDCACTUS, level, x, y); break; case TILE_MAGIC_BARRIER: data = 0; int i = 0; - for (i = 0; i < eManager.lastSlot[currentLevel]; ++i) { - Entity * e = &eManager.entities[currentLevel][i]; + for (i = 0; i < eManager.lastSlot[level]; ++i) { + Entity * e = &eManager.entities[level][i]; if(e->type == ENTITY_MAGIC_PILLAR) { ++data; } } - if(data==0) setTile(TILE_DUNGEON_FLOOR,x,y); - setData(rand()%2,x,y); + if(data==0) setTile(TILE_DUNGEON_FLOOR, level, x, y); + setData(rand()%2, level, x, y); break; case TILE_ICE: - if(season!=3) { - setTile(TILE_WATER,x,y); + if(worldData.season!=3) { + setTile(TILE_WATER, level, x, y); } break; } @@ -1076,60 +835,61 @@ void tickTile(int x, int y){ } void tickEntityItem(Entity* e){ - ++e->entityItem.age; - if(e->entityItem.age == 630){ - removeEntityFromList(e,e->level,&eManager); - /* - Programming pro tip: - Remember to put a return statement after you remove the entity, - or else your going to have a very bad time like I did. - */ - return; - } - e->entityItem.xx += e->entityItem.xa; - e->entityItem.yy += e->entityItem.ya; - e->entityItem.zz += e->entityItem.za; - if (e->entityItem.zz < 0) { - e->entityItem.zz = 0; - e->entityItem.za *= -0.5; - e->entityItem.xa *= 0.6; - e->entityItem.ya *= 0.6; - } - e->entityItem.za -= 0.15; - int ox = e->x; - int oy = e->y; - int nx = (int) e->entityItem.xx; - int ny = (int) e->entityItem.yy; - int expectedx = nx - e->x; - int expectedy = ny - e->y; - move(e, nx - e->x, ny - e->y); - int gotx = e->x - ox; - int goty = e->y - oy; - e->entityItem.xx += gotx - expectedx; - e->entityItem.yy += goty - expectedy; + ++e->entityItem.age; + if(e->entityItem.age == 630){ + removeEntityFromList(e, e->level, &eManager); + /* + Programming pro tip: + Remember to put a return statement after you remove the entity, + or else your going to have a very bad time like I did. + */ + return; + } + e->entityItem.xx += e->entityItem.xa; + e->entityItem.yy += e->entityItem.ya; + e->entityItem.zz += e->entityItem.za; + if (e->entityItem.zz < 0) { + e->entityItem.zz = 0; + e->entityItem.za *= -0.5; + e->entityItem.xa *= 0.6; + e->entityItem.ya *= 0.6; + } + e->entityItem.za -= 0.15; + int ox = e->x; + int oy = e->y; + int nx = (int) e->entityItem.xx; + int ny = (int) e->entityItem.yy; + int expectedx = nx - e->x; + int expectedy = ny - e->y; + move(e, nx - e->x, ny - e->y); + int gotx = e->x - ox; + int goty = e->y - oy; + e->entityItem.xx += gotx - expectedx; + e->entityItem.yy += goty - expectedy; } void tickEntityTextParticle(Entity* e){ - ++e->textParticle.age; - if(e->textParticle.age == 60){ - removeEntityFromList(e,e->level,&eManager); - return; - } - e->textParticle.xx += e->textParticle.xa; - e->textParticle.yy += e->textParticle.ya; - e->textParticle.zz += e->textParticle.za; - if (e->textParticle.zz < 0) { - e->textParticle.zz = 0; - e->textParticle.za *= -0.5; - e->textParticle.xa *= 0.6; - e->textParticle.ya *= 0.6; - } - e->textParticle.za -= 0.15; - e->x = (int) e->textParticle.xx; - e->y = (int) e->textParticle.yy; + ++e->textParticle.age; + if(e->textParticle.age == 60){ + removeEntityFromList(e, e->level, &eManager); + return; + } + e->textParticle.xx += e->textParticle.xa; + e->textParticle.yy += e->textParticle.ya; + e->textParticle.zz += e->textParticle.za; + if (e->textParticle.zz < 0) { + e->textParticle.zz = 0; + e->textParticle.za *= -0.5; + e->textParticle.xa *= 0.6; + e->textParticle.ya *= 0.6; + } + e->textParticle.za -= 0.15; + e->x = (int) e->textParticle.xx; + e->y = (int) e->textParticle.yy; } void tickEntity(Entity* e){ + PlayerData *nearestPlayer = getNearestPlayer(e->level, e->x, e->y); switch(e->type){ case ENTITY_ITEM: tickEntityItem(e); return; case ENTITY_FURNITURE: return; @@ -1138,9 +898,9 @@ void tickEntity(Entity* e){ case ENTITY_SKELETON: case ENTITY_KNIGHT: if (e->hurtTime > 0) e->hurtTime--; - if (e->hostile.randWalkTime == 0 && e->type != ENTITY_SKELETON) { - int xd = player.x - e->x; - int yd = player.y - e->y; + if (e->hostile.randWalkTime == 0 && e->type != ENTITY_SKELETON && nearestPlayer!=NULL) { + int xd = nearestPlayer->entity.x - e->x; + int yd = nearestPlayer->entity.y - e->y; int dist = 50 * 50; if(e->type == ENTITY_KNIGHT) dist = 80 * 80; @@ -1169,15 +929,17 @@ void tickEntity(Entity* e){ if(e->hostile.lvl >= 2) aitemID = ITEM_ARROW_STONE; //turn to player when attacking - int xd = player.x - e->x; - int yd = player.y - e->y; - if(xd*xd > yd*yd) { - if (xd < 0) e->hostile.dir = 2; - if (xd > 0) e->hostile.dir = 3; - } else { - if (yd < 0) e->hostile.dir = 1; - if (yd > 0) e->hostile.dir = 0; - } + if(nearestPlayer!=NULL) { + int xd = nearestPlayer->entity.x - e->x; + int yd = nearestPlayer->entity.y - e->y; + if(xd*xd > yd*yd) { + if (xd < 0) e->hostile.dir = 2; + if (xd > 0) e->hostile.dir = 3; + } else { + if (yd < 0) e->hostile.dir = 1; + if (yd > 0) e->hostile.dir = 0; + } + } switch(e->hostile.dir) { case 0: @@ -1198,7 +960,7 @@ void tickEntity(Entity* e){ if(e->hostile.xa != 0 || e->hostile.ya != 0) e->hostile.walkDist++; - int speed = tickCount & 1; + int speed = syncTickCount & 1; if (!moveMob(e, e->hostile.xa * speed, e->hostile.ya * speed) || (rand()%100) == 0) { e->hostile.randWalkTime = 60; e->hostile.xa = ((rand()%3) - 1) * (rand()%2); @@ -1214,14 +976,16 @@ void tickEntity(Entity* e){ e->slime.xa = ((rand()%3) - 1); e->slime.ya = ((rand()%3) - 1); - int xd = player.x - e->x; - int yd = player.y - e->y; - if (xd * xd + yd * yd < 50 * 50) { - if (xd < 0) e->slime.xa = -1; - if (xd > 0) e->slime.xa = +1; - if (yd < 0) e->slime.ya = -1; - if (yd > 0) e->slime.ya = +1; - } + if(nearestPlayer!=NULL) { + int xd = nearestPlayer->entity.x - e->x; + int yd = nearestPlayer->entity.y - e->y; + if (xd * xd + yd * yd < 50 * 50) { + if (xd < 0) e->slime.xa = -1; + if (xd > 0) e->slime.xa = +1; + if (yd < 0) e->slime.ya = -1; + if (yd > 0) e->slime.ya = +1; + } + } if (e->slime.xa != 0 || e->slime.ya != 0) e->slime.jumpTime = 10; } @@ -1263,9 +1027,9 @@ void tickEntity(Entity* e){ return; } - if (e->wizard.randWalkTime == 0) { - int xd = player.x - e->x; - int yd = player.y - e->y; + if (e->wizard.randWalkTime == 0 && nearestPlayer!=NULL) { + int xd = nearestPlayer->entity.x - e->x; + int yd = nearestPlayer->entity.y - e->y; int dist = xd * xd + yd * yd; if (dist > 80 * 80) { e->wizard.xa = 0; @@ -1284,7 +1048,7 @@ void tickEntity(Entity* e){ } } - int wSpeed = (tickCount % 4) == 0 ? 0 : 1; + int wSpeed = (syncTickCount % 4) == 0 ? 0 : 1; if (!moveMob(e, e->wizard.xa * wSpeed, e->wizard.ya * wSpeed) || (rand()%100) == 0) { e->wizard.randWalkTime = 30; e->wizard.xa = ((rand()%3) - 1) * (rand()%2); @@ -1304,9 +1068,9 @@ void tickEntity(Entity* e){ if (e->wizard.randWalkTime > 0) { e->wizard.randWalkTime--; - if (e->wizard.randWalkTime == 0) { - int xd = player.x - e->x; - int yd = player.y - e->y; + if (e->wizard.randWalkTime == 0 && nearestPlayer!=NULL) { + int xd = nearestPlayer->entity.x - e->x; + int yd = nearestPlayer->entity.y - e->y; if (rand()%4 == 0 && xd * xd + yd * yd < 50 * 50) { if (e->wizard.attackDelay == 0 && e->wizard.attackTime == 0) e->wizard.attackDelay = 120; } @@ -1317,7 +1081,7 @@ void tickEntity(Entity* e){ case ENTITY_SPARK: e->spark.age++; if (e->spark.age >= 260) { - removeEntityFromList(e,e->level,&eManager); + removeEntityFromList(e, e->level, &eManager); return; } e->spark.xx += e->spark.xa; @@ -1325,9 +1089,9 @@ void tickEntity(Entity* e){ e->x = (int) e->spark.xx; e->y = (int) e->spark.yy; - if(intersects(player, e->x + e->spark.xa - e->xr, e->y + e->spark.ya - e->yr, e->x + e->spark.xa + e->xr, e->y + e->spark.ya + e->yr)){ - EntityVsEntity(e, &player); - removeEntityFromList(e,e->level,&eManager); + if(nearestPlayer!=NULL && intersects(nearestPlayer->entity, e->x + e->spark.xa - e->xr, e->y + e->spark.ya - e->yr, e->x + e->spark.xa + e->xr, e->y + e->spark.ya + e->yr)){ + EntityVsEntity(e, &(nearestPlayer->entity)); + removeEntityFromList(e, e->level, &eManager); } return; case ENTITY_DRAGON: @@ -1352,15 +1116,17 @@ void tickEntity(Entity* e){ e->dragon.attackTime--; //turn to player when attacking - int xd = player.x - e->x; - int yd = player.y - e->y; - if(xd*xd > yd*yd) { - if (xd < 0) e->dragon.dir = 2; - if (xd > 0) e->dragon.dir = 3; - } else { - if (yd < 0) e->dragon.dir = 1; - if (yd > 0) e->dragon.dir = 0; - } + if(nearestPlayer!=NULL) { + int xd = nearestPlayer->entity.x - e->x; + int yd = nearestPlayer->entity.y - e->y; + if(xd*xd > yd*yd) { + if (xd < 0) e->dragon.dir = 2; + if (xd > 0) e->dragon.dir = 3; + } else { + if (yd < 0) e->dragon.dir = 1; + if (yd > 0) e->dragon.dir = 0; + } + } switch(e->dragon.attackType) { case 0: //Firebreathing @@ -1394,9 +1160,9 @@ void tickEntity(Entity* e){ } //TODO - movement copied from airwizard, adjust to better fit dragon - if (e->dragon.randWalkTime == 0) { - int xd = player.x - e->x; - int yd = player.y - e->y; + if (e->dragon.randWalkTime == 0 && nearestPlayer!=NULL) { + int xd = nearestPlayer->entity.x - e->x; + int yd = nearestPlayer->entity.y - e->y; int dist = xd * xd + yd * yd; if (dist > 64 * 64) { e->dragon.xa = 0; @@ -1415,7 +1181,7 @@ void tickEntity(Entity* e){ } } - int dSpeed = (tickCount % 4) == 0 ? 0 : 1; + int dSpeed = (syncTickCount % 4) == 0 ? 0 : 1; if (!moveMob(e, e->dragon.xa * dSpeed, e->dragon.ya * dSpeed) || (rand()%120) == 0) { e->dragon.randWalkTime = 30; e->dragon.xa = ((rand()%3) - 1) * (rand()%2); @@ -1434,11 +1200,13 @@ void tickEntity(Entity* e){ //if (e->dragon.randWalkTime > 0) { // e->dragon.randWalkTime--; // if (e->dragon.randWalkTime == 0) { - int xd = player.x - e->x; - int yd = player.y - e->y; + if(nearestPlayer!=NULL) { + int xd = nearestPlayer->entity.x - e->x; + int yd = nearestPlayer->entity.y - e->y; if (rand()%12 == 0 && xd * xd + yd * yd < 50 * 50) { if (e->dragon.attackDelay == 0 && e->dragon.attackTime == 0) e->dragon.attackDelay = 40; } + } // } //} @@ -1446,7 +1214,7 @@ void tickEntity(Entity* e){ case ENTITY_DRAGONPROJECTILE: e->dragonFire.age++; if (e->dragonFire.age >= 30) { - removeEntityFromList(e,e->level,&eManager); + removeEntityFromList(e, e->level, &eManager); return; } e->dragonFire.xx += e->dragonFire.xa; @@ -1454,25 +1222,25 @@ void tickEntity(Entity* e){ e->x = (int) e->dragonFire.xx; e->y = (int) e->dragonFire.yy; - if(intersects(player, e->x + e->dragonFire.xa - e->xr, e->y + e->dragonFire.ya - e->yr, e->x + e->dragonFire.xa + e->xr, e->y + e->dragonFire.ya + e->yr)){ - EntityVsEntity(e, &player); - removeEntityFromList(e,e->level,&eManager); + if(nearestPlayer!=NULL && intersects(nearestPlayer->entity, e->x + e->dragonFire.xa - e->xr, e->y + e->dragonFire.ya - e->yr, e->x + e->dragonFire.xa + e->xr, e->y + e->dragonFire.ya + e->yr)){ + EntityVsEntity(e, &(nearestPlayer->entity)); + removeEntityFromList(e, e->level, &eManager); } return; case ENTITY_ARROW: e->arrow.age++; if (e->arrow.age >= 260 || !move(e, e->arrow.xa, e->arrow.ya)) { //only drop arrows shot by player - if(e->arrow.parent->type == ENTITY_PLAYER) addItemsToWorld(newItem(e->arrow.itemID,1),e->x+4, e->y+4, 1); - removeEntityFromList(e,e->level,&eManager); + if(e->arrow.parent->type == ENTITY_PLAYER) addItemsToWorld(newItem(e->arrow.itemID,1), e->level, e->x+4, e->y+4, 1); + removeEntityFromList(e, e->level, &eManager); return; } return; case ENTITY_PASSIVE: if (e->hurtTime > 0) e->hurtTime--; - if (e->passive.randWalkTime == 0) { - int xd = player.x - e->x; - int yd = player.y - e->y; + if (e->passive.randWalkTime == 0 && nearestPlayer!=NULL) { + int xd = nearestPlayer->entity.x - e->x; + int yd = nearestPlayer->entity.y - e->y; //flee from player if (xd * xd + yd * yd < 40 * 40) { e->passive.xa = 0; @@ -1491,7 +1259,7 @@ void tickEntity(Entity* e){ if(e->passive.xa != 0 || e->passive.ya != 0) e->passive.walkDist++; - int pspeed = tickCount & 1; + int pspeed = syncTickCount & 1; if (!moveMob(e, e->passive.xa * pspeed, e->passive.ya * pspeed) || (rand()%100) == 0) { e->passive.randWalkTime = 60; e->passive.xa = ((rand()%3) - 1) * (rand()%2); @@ -1500,22 +1268,22 @@ void tickEntity(Entity* e){ if (e->passive.randWalkTime > 0) e->passive.randWalkTime--; return; case ENTITY_GLOWWORM: - if(daytime>5000 && daytime<6000) { + if(worldData.daytime>5000 && worldData.daytime<6000) { if(rand()%200==0) { - removeEntityFromList(e,e->level,&eManager); + removeEntityFromList(e, e->level, &eManager); return; } - } else if(daytime>6000 && daytime<18000) { - removeEntityFromList(e,e->level,&eManager); + } else if(worldData.daytime>6000 && worldData.daytime<18000) { + removeEntityFromList(e, e->level, &eManager); return; } - int gspeed = (((tickCount & 0x3) == 3) ? 1 : 0); + int gspeed = (((syncTickCount & 0x3) == 3) ? 1 : 0); if (!moveMob(e, e->glowworm.xa * gspeed, e->glowworm.ya * gspeed) || (e->glowworm.randWalkTime==0) || (rand()%20) == 0) { if(e->glowworm.randWalkTime != 0) { e->glowworm.waitTime = 20 + (rand()%60); } - if(e->glowworm.waitTime == 0 || getTile((e->x)>>4,(e->y)>>4)!=TILE_TREE) { + if(e->glowworm.waitTime == 0 || getTile(e->level, (e->x)>>4, (e->y)>>4)!=TILE_TREE) { e->glowworm.randWalkTime = 20; e->glowworm.xa = ((rand()%3) - 1) * (rand()%2); e->glowworm.ya = ((rand()%3) - 1) * (rand()%2); @@ -1536,13 +1304,13 @@ void tickEntity(Entity* e){ case ENTITY_TEXTPARTICLE: tickEntityTextParticle(e); return; case ENTITY_SMASHPARTICLE: ++e->smashParticle.age; - if(e->smashParticle.age > 10) removeEntityFromList(e,e->level,&eManager); + if(e->smashParticle.age > 10) removeEntityFromList(e, e->level, &eManager); return; } } void trySpawn(int count, int level) { - int i; + int i, j; for (i = 0; i < count; i++) { if(eManager.lastSlot[level] > 900) continue; Entity e; @@ -1562,9 +1330,13 @@ void trySpawn(int count, int level) { int ex = (rx<<4)+8; int ey = (ry<<4)+8; - if(level == currentLevel && (ex > player.x-160 && ey > player.y-125 && ex < player.x+160 && ey < player.y+125)) continue; + //do not spawn near players + for(j = 0; j players[j].entity.x-160 && ey > players[j].entity.y-125 && ex < players[j].entity.x+160 && ey < players[j].entity.y+125)) continue; + } - if (!tileIsSolid(map[level][rx+ry*128],&e)) { + //spawn if tile is free + if (!tileIsSolid(worldData.map[level][rx+ry*128],&e)) { if(level==1 && (rand()%2)==0) { //passive entities on overworld e = newPassiveEntity(rand()%3, ex, ey, level); } else { @@ -1594,426 +1366,241 @@ void trySpawn(int count, int level) { } } -int getTile(int x, int y){ +int getTile(s8 level, int x, int y){ if(x < 0 || y < 0 || x > 128 || y > 128) return -1; - return map[currentLevel][x+y*128]; + return worldData.map[level][x+y*128]; } -void setTile(int id, int x, int y){ +void setTile(int id, s8 level, int x, int y){ if(x < 0 || y < 0 || x > 128 || y > 128) return; - map[currentLevel][x+y*128] = id; - data[currentLevel][x+y*128] = 0; //reset data(set again if needed, hopefully this breaks nothing) - sf2d_set_pixel(minimap[currentLevel], x, y, getMinimapColor(currentLevel,x,y)); + worldData.map[level][x+y*128] = id; + worldData.data[level][x+y*128] = 0; //reset data(set again if needed, hopefully this breaks nothing) + + sf2d_set_pixel(minimap[level], x, y, getMinimapColor(getLocalPlayer(), level, x, y)); } -int getData(int x, int y){ + +int getData(s8 level, int x, int y){ if(x < 0 || y < 0 || x > 128 || y > 128) return -1; - return data[currentLevel][x+y*128]; + return worldData.data[level][x+y*128]; } -void setData(int id, int x, int y){ +void setData(int id, s8 level, int x, int y){ if(x < 0 || y < 0 || x > 128 || y > 128) return; - data[currentLevel][x+y*128] = id; + worldData.data[level][x+y*128] = id; } -void spawnPlayer(){ - while(true){ - int rx = rand()%128; - int ry = rand()%128; - if(getTile(rx,ry) == TILE_GRASS){ - player.x = (rx << 4) + 8; - player.y = (ry << 4) + 8; - break; - } - } - +void addSmashParticles(s8 level, int x, int y, int damage) { + char hurtText[11]; + sprintf(hurtText, "%d", damage); + addEntityToList(newTextParticleEntity(hurtText, 0xFF0000FF, x, y, level), &eManager); + addEntityToList(newSmashParticleEntity(x, y, level), &eManager); } -void initPlayer(){ - player.type = ENTITY_PLAYER; - spawnPlayer(); - player.xr = 4; - player.yr = 3; - player.canSwim = true; - player.p.ax = 0; - player.p.ay = 0; - player.p.health = 10; - player.p.stamina = 10; - player.p.score = 0; - player.p.walkDist = 0; - player.p.attackTimer = 0; - player.p.dir = 0; - player.p.inv = &eManager.invs[0]; - eManager.nextInv++; - player.p.inv->lastSlot = 0; - player.p.activeItem = &noItem; - player.p.isDead = false; - player.p.hasWon = false; - - addItemToInventory(newItem(ITEM_WORKBENCH,0), player.p.inv); - addItemToInventory(newItem(ITEM_POWGLOVE,0), player.p.inv); - - /* - addItemToInventory(newItem(TOOL_SHOVEL,4), player.p.inv); - addItemToInventory(newItem(TOOL_HOE,4), player.p.inv); - addItemToInventory(newItem(TOOL_SWORD,4), player.p.inv); - addItemToInventory(newItem(TOOL_PICKAXE,4), player.p.inv); - addItemToInventory(newItem(TOOL_AXE,4), player.p.inv); - - addItemToInventory(newItem(ITEM_ANVIL,0), player.p.inv); - addItemToInventory(newItem(ITEM_CHEST,0), player.p.inv); - addItemToInventory(newItem(ITEM_OVEN,0), player.p.inv); - addItemToInventory(newItem(ITEM_FURNACE,0), player.p.inv); - addItemToInventory(newItem(ITEM_LANTERN,0), player.p.inv); - +void damageAndBreakTile(s8 level, int xt, int yt, int damage, int maxDamage, int replaceTile, int numItems, ...) { int i; - for (i = 7;i < 28;++i) addItemToInventory(newItem(i,50), player.p.inv); - //*/ + + //damage indicator + addSmashParticles(level, xt<<4, yt<<4, damage); + + //do damage + setData(getData(level, xt, yt)+damage, level, xt, yt); + + //tile has been destroyed + if(getData(level, xt, yt)>maxDamage) { + setTile(replaceTile, level, xt, yt); + + //drop items + va_list al; + numItems<<=1; //each item is the item+count, moved up here to get rid of warning + va_start(al, numItems); + numItems>>=1; + for(i=0; i 20){ - setTile(TILE_GRASS,xt,yt); - addItemsToWorld(newItem(ITEM_WOOD,1),(xt<<4)+8,(yt<<4)+8,rand()%2+1); - addItemsToWorld(newItem(ITEM_ACORN,1),(xt<<4)+8,(yt<<4)+8,rand()%2); - } + if(rand()%120==0)addEntityToList(newItemEntity(newItem(ITEM_APPLE,1), (xt<<4)+8,(yt<<4)+8, level), &eManager); + damageAndBreakTile(level, xt, yt, damage, 20, TILE_GRASS, 2, newItem(ITEM_WOOD,1), rand()%2+1, newItem(ITEM_ACORN,1), rand()%2); break; case TILE_CACTUS: - sprintf(hurtText, "%d", damage); - addEntityToList(newTextParticleEntity(hurtText,0xFF0000FF,xt<<4,yt<<4,currentLevel), &eManager); - addEntityToList(newSmashParticleEntity(xt<<4,yt<<4,currentLevel), &eManager); - setData(getData(xt,yt)+damage,xt,yt); - if(getData(xt,yt) > 10){ - setTile(TILE_SAND,xt,yt); - addItemsToWorld(newItem(ITEM_CACTUS,1),(xt<<4)+8,(yt<<4)+8,rand()%2+1); - } + damageAndBreakTile(level, xt, yt, damage, 10, TILE_SAND, 1, newItem(ITEM_CACTUS,1), rand()%2+1); break; case TILE_ROCK: - sprintf(hurtText, "%d", damage); - addEntityToList(newTextParticleEntity(hurtText,0xFF0000FF,xt<<4,yt<<4,currentLevel), &eManager); - addEntityToList(newSmashParticleEntity(xt<<4,yt<<4,currentLevel), &eManager); - setData(getData(xt,yt)+damage,xt,yt); - if(getData(xt,yt) > 50){ - setTile(TILE_DIRT,xt,yt); - addItemsToWorld(newItem(ITEM_STONE,1),(xt<<4)+8,(yt<<4)+8,rand()%4+1); - addItemsToWorld(newItem(ITEM_COAL,1),(xt<<4)+8,(yt<<4)+8,rand()%2); - } + damageAndBreakTile(level, xt, yt, damage, 50, TILE_DIRT, 2, newItem(ITEM_STONE,1), rand()%4+1, newItem(ITEM_COAL,1), rand()%2); break; case TILE_HARDROCK: - if(player.p.activeItem->id != TOOL_PICKAXE || player.p.activeItem->countLevel < 4) damage = 0; - sprintf(hurtText, "%d", damage); - addEntityToList(newTextParticleEntity(hurtText,0xFF0000FF,xt<<4,yt<<4,currentLevel), &eManager); - addEntityToList(newSmashParticleEntity(xt<<4,yt<<4,currentLevel), &eManager); - setData(getData(xt,yt)+damage,xt,yt); - if(getData(xt,yt) > 200){ - setTile(TILE_DIRT,xt,yt); - addItemsToWorld(newItem(ITEM_STONE,1),(xt<<4)+8,(yt<<4)+8,rand()%4+1); - addItemsToWorld(newItem(ITEM_COAL,1),(xt<<4)+8,(yt<<4)+8,rand()%2); - } + if((pd->activeItem->id != TOOL_PICKAXE || pd->activeItem->countLevel < 4) && !TESTGODMODE) damage = 0; + damageAndBreakTile(level, xt, yt, damage, 200, TILE_DIRT, 2, newItem(ITEM_STONE,1), rand()%4+1, newItem(ITEM_COAL,1), rand()%2); break; case TILE_IRONORE: - if(player.p.activeItem->id != TOOL_PICKAXE) damage = 0; - sprintf(hurtText, "%d", damage); - addEntityToList(newTextParticleEntity(hurtText,0xFF0000FF,xt<<4,yt<<4,currentLevel), &eManager); - addEntityToList(newSmashParticleEntity(xt<<4,yt<<4,currentLevel), &eManager); - setData(getData(xt,yt)+damage,xt,yt); - if(getData(xt,yt) > 0){ - int count = rand() & 1; - if (getData(xt,yt) >= (rand()%10) + 3) { - if(currentLevel!=5) setTile(TILE_DIRT,xt,yt); - else setTile(TILE_DUNGEON_FLOOR,xt,yt); - count += 2; - } - addItemsToWorld(newItem(ITEM_IRONORE,1),(xt<<4)+8,(yt<<4)+8,count); - } break; case TILE_GOLDORE: - if(player.p.activeItem->id != TOOL_PICKAXE) damage = 0; - sprintf(hurtText, "%d", damage); - addEntityToList(newTextParticleEntity(hurtText,0xFF0000FF,xt<<4,yt<<4,currentLevel), &eManager); - addEntityToList(newSmashParticleEntity(xt<<4,yt<<4,currentLevel), &eManager); - setData(getData(xt,yt)+damage,xt,yt); - if(getData(xt,yt) > 0){ - int count = rand() & 1; - if (getData(xt,yt) >= (rand()%10) + 3) { - if(currentLevel!=5) setTile(TILE_DIRT,xt,yt); - else setTile(TILE_DUNGEON_FLOOR,xt,yt); - count += 2; - } - addItemsToWorld(newItem(ITEM_GOLDORE,1),(xt<<4)+8,(yt<<4)+8,count); - } break; case TILE_GEMORE: - if(player.p.activeItem->id != TOOL_PICKAXE) damage = 0; - sprintf(hurtText, "%d", damage); - addEntityToList(newTextParticleEntity(hurtText,0xFF0000FF,xt<<4,yt<<4,currentLevel), &eManager); - addEntityToList(newSmashParticleEntity(xt<<4,yt<<4,currentLevel), &eManager); - setData(getData(xt,yt)+damage,xt,yt); - if(getData(xt,yt) > 0){ + if(pd->activeItem->id != TOOL_PICKAXE) damage = 0; + addSmashParticles(level, xt<<4, yt<<4, damage); + setData(getData(level, xt, yt)+damage, level, xt, yt); + if(getData(level, xt, yt) > 0){ int count = rand() & 1; - if (getData(xt,yt) >= (rand()%10) + 3) { - if(currentLevel!=5) setTile(TILE_DIRT,xt,yt); - else setTile(TILE_DUNGEON_FLOOR,xt,yt); + if (getData(level, xt, yt) >= (rand()%10) + 3) { + if(level!=5) setTile(TILE_DIRT, level, xt, yt); + else setTile(TILE_DUNGEON_FLOOR, level, xt, yt); count += 2; } - addItemsToWorld(newItem(ITEM_GEM,1),(xt<<4)+8,(yt<<4)+8,count); + if(tile==TILE_IRONORE) addItemsToWorld(newItem(ITEM_IRONORE,1), level, (xt<<4)+8, (yt<<4)+8, count); + if(tile==TILE_GOLDORE) addItemsToWorld(newItem(ITEM_GOLDORE,1), level, (xt<<4)+8, (yt<<4)+8, count); + if(tile==TILE_GEMORE) addItemsToWorld(newItem(ITEM_GEM,1), level, (xt<<4)+8, (yt<<4)+8, count); } break; case TILE_CLOUDCACTUS: - if(player.p.activeItem->id != TOOL_PICKAXE) damage = 0; - sprintf(hurtText, "%d", damage); - addEntityToList(newTextParticleEntity(hurtText,0xFF0000FF,xt<<4,yt<<4,currentLevel), &eManager); - addEntityToList(newSmashParticleEntity(xt<<4,yt<<4,currentLevel), &eManager); - setData(getData(xt,yt)+damage,xt,yt); - if(getData(xt,yt) > 0){ + if(pd->activeItem->id != TOOL_PICKAXE) damage = 0; + addSmashParticles(level, xt<<4, yt<<4, damage); + setData(getData(level, xt, yt)+damage, level, xt, yt); + if(getData(level, xt, yt) > 0){ int count = rand() % 3; - if (getData(xt,yt) >= (rand()%10) + 3) { - setTile(TILE_CLOUD,xt,yt); + if (getData(level, xt, yt) >= (rand()%10) + 3) { + setTile(TILE_CLOUD, level, xt, yt); count += 3; } - addItemsToWorld(newItem(ITEM_CLOUD,1),(xt<<4)+8,(yt<<4)+8,count); + addItemsToWorld(newItem(ITEM_CLOUD,1), level, (xt<<4)+8, (yt<<4)+8, count); } break; case TILE_FARM: - setTile(TILE_DIRT,xt,yt); + setTile(TILE_DIRT, level, xt, yt); break; case TILE_SAPLING_TREE: - setTile(TILE_GRASS,xt,yt); + setTile(TILE_GRASS, level, xt, yt); break; case TILE_SAPLING_CACTUS: - setTile(TILE_SAND,xt,yt); + setTile(TILE_SAND, level, xt, yt); break; case TILE_WHEAT: - if(getData(xt,yt) > -1){ - int age = getData(xt,yt); + if(getData(level, xt, yt) > -1){ + int age = getData(level, xt, yt); int count = (rand() % 2); if(age >= 80) count = (rand()%2) + 1; - addItemsToWorld(newItem(ITEM_SEEDS,1),(xt<<4)+8,(yt<<4)+8,count); + addItemsToWorld(newItem(ITEM_SEEDS,1), level, (xt<<4)+8, (yt<<4)+8, count); count = 0; - if(age == 100)count = (rand()%3) + 2; - else if(age >= 80)count = (rand()%2) + 1; - addItemsToWorld(newItem(ITEM_WHEAT,1),(xt<<4)+8,(yt<<4)+8,count); - setTile(TILE_DIRT,xt,yt); + if(age == 100) count = (rand()%3) + 2; + else if(age >= 80) count = (rand()%2) + 1; + addItemsToWorld(newItem(ITEM_WHEAT,1), level, (xt<<4)+8, (yt<<4)+8, count); + setTile(TILE_DIRT, level, xt, yt); } break; case TILE_FLOWER: - setTile(TILE_GRASS,xt,yt); - addEntityToList(newItemEntity(newItem(ITEM_FLOWER,1), (xt<<4)+8,(yt<<4)+8, currentLevel), &eManager); + setTile(TILE_GRASS, level, xt,yt); + addItemsToWorld(newItem(ITEM_FLOWER,1), level, (xt<<4)+8, (yt<<4)+8, 1); break; case TILE_WOOD_WALL: - sprintf(hurtText, "%d", damage); - addEntityToList(newTextParticleEntity(hurtText,0xFF0000FF,xt<<4,yt<<4,currentLevel), &eManager); - addEntityToList(newSmashParticleEntity(xt<<4,yt<<4,currentLevel), &eManager); - setData(getData(xt,yt)+damage,xt,yt); - if(getData(xt,yt) > 20){ - setTile(TILE_DIRT,xt,yt); - addItemsToWorld(newItem(ITEM_WALL_WOOD,1),(xt<<4)+8,(yt<<4)+8,1); - } + damageAndBreakTile(level, xt, yt, damage, 20, TILE_DIRT, 1, newItem(ITEM_WALL_WOOD,1), 1); break; case TILE_STONE_WALL: - sprintf(hurtText, "%d", damage); - addEntityToList(newTextParticleEntity(hurtText,0xFF0000FF,xt<<4,yt<<4,currentLevel), &eManager); - addEntityToList(newSmashParticleEntity(xt<<4,yt<<4,currentLevel), &eManager); - setData(getData(xt,yt)+damage,xt,yt); - if(getData(xt,yt) > 30){ - setTile(TILE_DIRT,xt,yt); - addItemsToWorld(newItem(ITEM_WALL_STONE,1),(xt<<4)+8,(yt<<4)+8,1); - } + damageAndBreakTile(level, xt, yt, damage, 30, TILE_DIRT, 1, newItem(ITEM_WALL_STONE,1), 1); break; case TILE_IRON_WALL: - sprintf(hurtText, "%d", damage); - addEntityToList(newTextParticleEntity(hurtText,0xFF0000FF,xt<<4,yt<<4,currentLevel), &eManager); - addEntityToList(newSmashParticleEntity(xt<<4,yt<<4,currentLevel), &eManager); - setData(getData(xt,yt)+damage,xt,yt); - if(getData(xt,yt) > 40){ - setTile(TILE_DIRT,xt,yt); - addItemsToWorld(newItem(ITEM_WALL_IRON,1),(xt<<4)+8,(yt<<4)+8,1); - } + damageAndBreakTile(level, xt, yt, damage, 40, TILE_DIRT, 1, newItem(ITEM_WALL_IRON,1), 1); break; case TILE_GOLD_WALL: - sprintf(hurtText, "%d", damage); - addEntityToList(newTextParticleEntity(hurtText,0xFF0000FF,xt<<4,yt<<4,currentLevel), &eManager); - addEntityToList(newSmashParticleEntity(xt<<4,yt<<4,currentLevel), &eManager); - setData(getData(xt,yt)+damage,xt,yt); - if(getData(xt,yt) > 50){ - setTile(TILE_DIRT,xt,yt); - addItemsToWorld(newItem(ITEM_WALL_GOLD,1),(xt<<4)+8,(yt<<4)+8,1); - } + damageAndBreakTile(level, xt, yt, damage, 50, TILE_DIRT, 1, newItem(ITEM_WALL_GOLD,1), 1); break; case TILE_GEM_WALL: - sprintf(hurtText, "%d", damage); - addEntityToList(newTextParticleEntity(hurtText,0xFF0000FF,xt<<4,yt<<4,currentLevel), &eManager); - addEntityToList(newSmashParticleEntity(xt<<4,yt<<4,currentLevel), &eManager); - setData(getData(xt,yt)+damage,xt,yt); - if(getData(xt,yt) > 60){ - setTile(TILE_DIRT,xt,yt); - addItemsToWorld(newItem(ITEM_WALL_GEM,1),(xt<<4)+8,(yt<<4)+8,1); - } + damageAndBreakTile(level, xt, yt, damage, 60, TILE_DIRT, 1, newItem(ITEM_WALL_GEM,1), 1); break; case TILE_BOOKSHELVES: - sprintf(hurtText, "%d", damage); - addEntityToList(newTextParticleEntity(hurtText,0xFF0000FF,xt<<4,yt<<4,currentLevel), &eManager); - addEntityToList(newSmashParticleEntity(xt<<4,yt<<4,currentLevel), &eManager); - - if(currentLevel!=5) setTile(TILE_DIRT,xt,yt); - else setTile(TILE_DUNGEON_FLOOR,xt,yt); - addItemsToWorld(newItem(ITEM_BOOKSHELVES,1),(xt<<4)+8,(yt<<4)+8,1); + addSmashParticles(level, xt<<4, yt<<4, damage); + if(level!=5) setTile(TILE_DIRT, level, xt, yt); + else setTile(TILE_DUNGEON_FLOOR, level, xt, yt); + addItemsToWorld(newItem(ITEM_BOOKSHELVES,1), level, (xt<<4)+8, (yt<<4)+8, 1); break; } } -bool playerUseEnergy(int amount){ - if(TESTGODMODE) return true; - if(amount > player.p.stamina) return false; - player.p.stamina -= amount; - return true; -} -void playerAttack(){ - bool done = false; - player.p.attackTimer = 5; - int yo = -2; - int range = 12; - - if(playerUseItem()) return; +void switchLevel(PlayerData *pd, s8 change){ + pd->entity.level+=change; + if(pd->entity.level > 4) pd->entity.level = 0; else if(pd->entity.level < 0) pd->entity.level = 4; - switch(player.p.dir){ - case 0: if(interact(player.x - 8, player.y + 4 + yo, player.x + 8, player.y + range + yo)) return; break; - case 1: if(interact(player.x - 8, player.y - range + yo, player.x + 8, player.y - 4 + yo)) return; break; - case 2: if(interact(player.x - range, player.y - 8 + yo, player.x - 4, player.y + 8 + yo)) return; break; - case 3: if(interact(player.x + 4, player.y - 8 + yo, player.x + range, player.y + 8 + yo)) return; break; - } - - int xt = player.x >> 4; - int yt = (player.y + yo) >> 4; - int r = 12; - if (player.p.dir == 0) yt = (player.y + r + yo) >> 4; - if (player.p.dir == 1) yt = (player.y - r + yo) >> 4; - if (player.p.dir == 2) xt = (player.x - r) >> 4; - if (player.p.dir == 3) xt = (player.x + r) >> 4; - - if (xt >= 0 && yt >= 0 && xt < 128 && yt < 128) { - s8 itract = itemTileInteract(getTile(xt,yt),player.p.activeItem,xt,yt,player.x,player.y,player.p.dir); - if(itract > 0){ - if(itract==2)player.p.isCarrying = false; - done = true; - } - - if (isItemEmpty(player.p.activeItem)) { - removeItemFromInventory(player.p.activeItem->slotNum, player.p.inv); - player.p.activeItem = &noItem; - } + if(pd==getLocalPlayer()) { + if(pd->entity.level == 1) sf2d_set_clear_color(0xFF6C6D82); + else if(pd->entity.level > 1) sf2d_set_clear_color(0xFF666666); + else sf2d_set_clear_color(0xFF007F00); } - - if(done) return; - if (player.p.activeItem == &noItem || player.p.activeItem->id == TOOL_SWORD || player.p.activeItem->id == TOOL_AXE) { - xt = player.x >> 4; - yt = (player.y + yo) >> 4; - r = 12; - if (player.p.dir == 0) yt = (player.y + r + yo) >> 4; - if (player.p.dir == 1) yt = (player.y - r + yo) >> 4; - if (player.p.dir == 2) xt = (player.x - r) >> 4; - if (player.p.dir == 3) xt = (player.x + r) >> 4; - - if (xt >= 0 && yt >= 0 && xt < 128 && 128) { - playerHurtTile(getTile(xt,yt), xt, yt, (rand()%3) + 1, player.p.dir); - } - } -} - - -void switchLevel(s8 change){ - currentLevel+=change; - if(currentLevel > 4) currentLevel = 0; else if(currentLevel < 0) currentLevel = 4; - if(currentLevel == 1) sf2d_set_clear_color(0xFF6C6D82); //sf2d_set_clear_color(RGBA8(0x82, 0x6D, 0x6C, 0xFF)); - else if(currentLevel > 1) sf2d_set_clear_color(0xFF666666); //sf2d_set_clear_color(RGBA8(0x66, 0x66, 0x66, 0xFF)); - else sf2d_set_clear_color(0xFF007F00); //sf2d_set_clear_color(RGBA8(0x00, 0x7F, 0x00, 0xFF)); - - updateMusic(currentLevel, daytime); - //for level 0 background updateLevel1Map(); } -bool playerIntersectsEntity(Entity* e){ - return (player.x < e->x + e->xr && player.x + 4 > e->x && player.y < e->y + e->yr && player.y + 4 > e->y); -} - -void playerEntityInteract(Entity* e){ +void playerEntityInteract(PlayerData *pd, Entity* e){ switch(e->type){ case ENTITY_ITEM: - if(e->entityItem.age > 30){//30 - addItemToInventory(e->entityItem.item, player.p.inv); - removeEntityFromList(e,currentLevel,&eManager); - playSound(snd_pickup); - player.p.score++; + if(e->entityItem.age > 30){ + addItemToInventory(e->entityItem.item, &(pd->inventory)); + removeEntityFromList(e, e->level, &eManager); + playSoundPositioned(snd_pickup, pd->entity.level, pd->entity.x, pd->entity.y); + pd->score++; } break; case ENTITY_FURNITURE: - switch(player.p.dir){ - case 0: if(player.y < e->y) move(e,0,2); break; - case 1: if(player.y > e->y) move(e,0,-2); break; - case 2: if(player.x > e->x) move(e,-2,0); break; - case 3: if(player.x < e->x) move(e,2,0); break; + switch(pd->entity.p.dir){ + case 0: if(pd->entity.y < e->y) move(e, 0, 2); break; + case 1: if(pd->entity.y > e->y) move(e, 0, -2); break; + case 2: if(pd->entity.x > e->x) move(e, -2, 0); break; + case 3: if(pd->entity.x < e->x) move(e, 2, 0); break; } break; - } - } -void entityTileInteract(Entity*e, int tile, int x, int y){ +void entityTileInteract(Entity*e, int tile, s8 level, int x, int y){ switch(tile){ case TILE_STAIRS_DOWN: if(e->type == ENTITY_PLAYER){ - switchLevel(1); - player.x = (x << 4) + 8; - player.y = (y << 4) + 8; + switchLevel(e->p.data, 1); + e->x = (x << 4) + 8; + e->y = (y << 4) + 8; } return; case TILE_STAIRS_UP: if(e->type == ENTITY_PLAYER){ - switchLevel(-1); - player.x = (x << 4) + 8; - player.y = (y << 4) + 8; + switchLevel(e->p.data, -1); + e->x = (x << 4) + 8; + e->y = (y << 4) + 8; } return; - case TILE_CACTUS: if(e->type == ENTITY_PLAYER)hurtEntity(e,1,-1,0xFFAF00FF); return; - case TILE_LAVA: if(e->type == ENTITY_PLAYER)hurtEntity(e,1,-1,0xFFAF00FF); return; + case TILE_CACTUS: if(e->type == ENTITY_PLAYER) hurtEntity(e ,1, -1, 0xFFAF00FF, NULL); return; + case TILE_LAVA: if(e->type == ENTITY_PLAYER) hurtEntity(e, 1, -1, 0xFFAF00FF, NULL); return; case TILE_WHEAT: if(e->type == ENTITY_PLAYER || e->type == ENTITY_ZOMBIE){ - if(getData(x,y) > -1 && rand()%20 == 0){ - int age = getData(x,y); + if(getData(level, x, y) > -1 && rand()%20 == 0){ + int age = getData(level, x, y); int count = (rand() % 2); if(age >= 80) count = (rand()%2) + 1; - addItemsToWorld(newItem(ITEM_SEEDS,1),(x<<4)+8,(y<<4)+8,count); + addItemsToWorld(newItem(ITEM_SEEDS,1), level, (x<<4)+8, (y<<4)+8, count); count = 0; - if(age == 100)count = (rand()%3) + 2; - else if(age >= 80)count = (rand()%2) + 1; - addItemsToWorld(newItem(ITEM_WHEAT,1),(x<<4)+8,(y<<4)+8,count); - setTile(TILE_DIRT,x,y); + if(age == 100) count = (rand()%3) + 2; + else if(age >= 80) count = (rand()%2) + 1; + addItemsToWorld(newItem(ITEM_WHEAT,1), level, (x<<4)+8, (y<<4)+8, count); + setTile(TILE_DIRT, level, x, y); } } return; case TILE_FARM: if(e->type == ENTITY_PLAYER || e->type == ENTITY_ZOMBIE){ - if(rand()%20 == 0)setTile(TILE_DIRT,x,y); + if(rand()%20 == 0) setTile(TILE_DIRT, level, x, y); } return; case TILE_SAND: if(e->type != ENTITY_ARROW && e->type != ENTITY_ITEM) { - setData(10,x,y); + setData(10, level, x, y); } return; case TILE_DUNGEON_ENTRANCE: if(e->type == ENTITY_PLAYER) { - currentMenu = MENU_DUNGEON; + e->p.data->ingameMenu = MENU_DUNGEON; } return; } @@ -2023,338 +1610,144 @@ bool intersectsEntity(int x, int y, int r, Entity* e){ return (x < e->x + e->xr && x + r > e->x && y < e->y + e->yr && y + r > e->y); } -bool isPlayerInsideEntity(int x, int y){ +void openCraftingMenu(PlayerData *pd, RecipeManager *rm, char *title) { + pd->currentCraftTitle = title; + pd->ingameMenu = MENU_CRAFTING; + + cloneRecipeManager(rm, &(pd->currentRecipes)); + checkCanCraftRecipes(&(pd->currentRecipes), &(pd->inventory)); + sortRecipes(&(pd->currentRecipes)); +} + +bool useEntity(PlayerData *pd, Entity* e) { + if(e->type == ENTITY_FURNITURE){ + switch(e->entityFurniture.itemID){ + case ITEM_WORKBENCH: + openCraftingMenu(pd, &workbenchRecipes, "Crafting"); + return true; + case ITEM_FURNACE: + openCraftingMenu(pd, &furnaceRecipes, "Smelting"); + return true; + case ITEM_OVEN: + openCraftingMenu(pd, &ovenRecipes, "Cooking"); + return true; + case ITEM_ANVIL: + openCraftingMenu(pd, &anvilRecipes, "Smithing"); + return true; + case ITEM_LOOM: + openCraftingMenu(pd, &loomRecipes, "Crafting"); + return true; + case ITEM_ENCHANTER: + openCraftingMenu(pd, &enchanterRecipes, "Crafting"); + return true; + case ITEM_CHEST: + pd->curChestEntity = e; + pd->ingameMenuInvSel = 0; + pd->ingameMenuInvSelOther = 0; + pd->curChestEntityR = 0; + pd->ingameMenu = MENU_CONTAINER; + return true; + } + } else if(e->type == ENTITY_NPC) { + openNPCMenu(pd, e->npc.type); + return true; + } + return false; +} + + +bool isWater(s8 level, int xt, int yt){ + return getTile(level, xt, yt)==TILE_WATER; +} + +bool dungeonActive() { + //check if dungeon already exists (ie someone is in there) int i; - for(i = 0; i < eManager.lastSlot[currentLevel];++i){ - Entity e = eManager.entities[currentLevel][i]; - if(!e.canPass && intersectsEntity(x-16,y-16,16,&e)){ - playerEntityInteract(&eManager.entities[currentLevel][i]); + for(i = 0; i < playerCount; i++) { + if(players[i].entity.level==5) { return true; } } return false; } -bool useEntity(Entity* e) { - if(e->type == ENTITY_FURNITURE){ - switch(e->entityFurniture.itemID){ - case ITEM_WORKBENCH: - currentRecipes = &workbenchRecipes; - currentCraftTitle = "Crafting"; - currentMenu = MENU_CRAFTING; - checkCanCraftRecipes(currentRecipes, player.p.inv); - sortRecipes(currentRecipes); - return true; - case ITEM_FURNACE: - currentRecipes = &furnaceRecipes; - currentCraftTitle = "Crafting"; - currentMenu = MENU_CRAFTING; - checkCanCraftRecipes(currentRecipes, player.p.inv); - sortRecipes(currentRecipes); - return true; - case ITEM_OVEN: - currentRecipes = &ovenRecipes; - currentCraftTitle = "Crafting"; - currentMenu = MENU_CRAFTING; - checkCanCraftRecipes(currentRecipes, player.p.inv); - sortRecipes(currentRecipes); - return true; - case ITEM_ANVIL: - currentRecipes = &anvilRecipes; - currentCraftTitle = "Crafting"; - currentMenu = MENU_CRAFTING; - checkCanCraftRecipes(currentRecipes, player.p.inv); - sortRecipes(currentRecipes); - return true; - case ITEM_CHEST: - curChestEntity = e; - curInvSel = 0; - curChestEntity->entityFurniture.r = 0; - curChestEntity->entityFurniture.oSel = 0; - currentMenu = MENU_CONTAINER; - return true; - case ITEM_LOOM: - currentRecipes = &loomRecipes; - currentCraftTitle = "Crafting"; - currentMenu = MENU_CRAFTING; - checkCanCraftRecipes(currentRecipes, player.p.inv); - sortRecipes(currentRecipes); - return true; - case ITEM_ENCHANTER: - currentRecipes = &enchanterRecipes; - currentCraftTitle = "Crafting"; - currentMenu = MENU_CRAFTING; - checkCanCraftRecipes(currentRecipes, player.p.inv); - sortRecipes(currentRecipes); - return true; +void enterDungeon(PlayerData *pd) { + //create new one if needed + if(!dungeonActive()) { + //reset Entities + (&eManager)->lastSlot[5] = 0; + (&eManager)->entities[5][0] = nullEntity; + + //create map + createAndValidateDungeonMap(128, 128, 5, worldData.map[5], worldData.data[5]); + + //reset minimap clear state + int xd,yd; + for(xd = 0; xd < 128; ++xd) { + for(yd = 0; yd < 128; ++yd) { + setMinimapVisible(pd, 5, xd, yd, false); + } } - } else if(e->type == ENTITY_NPC) { - openNPCMenu(e->npc.type); - return true; - } - return false; -} - -bool use(int x0, int y0, int x1, int y1) { - Entity * entities[eManager.lastSlot[currentLevel]]; - int i; - int ae = getEntities(entities, x0, y0, x1, y1); - for(i = 0; i < ae; ++i){ - if(useEntity(entities[i])) return true; - } - return false; -} - -bool playerUse() { - int yo = -2; - if (player.p.dir == 0 && use(player.x - 8, player.y + 4 + yo, player.x + 8, player.y + 12 + yo)) return true; - if (player.p.dir == 1 && use(player.x - 8, player.y - 12 + yo, player.x + 8, player.y - 4 + yo)) return true; - if (player.p.dir == 3 && use(player.x + 4, player.y - 8 + yo, player.x + 12, player.y + 8 + yo)) return true; - if (player.p.dir == 2 && use(player.x - 12, player.y - 8 + yo, player.x - 4, player.y + 8 + yo)) return true; - return false; -} - -void tickPlayer(){ - if (player.hurtTime > 0) player.hurtTime--; - bool swimming = isSwimming(); - if (player.p.stamina <= 0 && player.p.staminaRechargeDelay == 0 && player.p.staminaRecharge == 0) { - player.p.staminaRechargeDelay = 40; - } - - if (player.p.staminaRechargeDelay > 0) { - --player.p.staminaRechargeDelay; - } - - if (player.p.staminaRechargeDelay == 0) { - ++player.p.staminaRecharge; - if (swimming) player.p.staminaRecharge = 0; - - while (player.p.staminaRecharge > 10) { - player.p.staminaRecharge -= 10; - if (player.p.stamina < 10) ++player.p.stamina; - } - } - - player.p.ax = 0; - player.p.ay = 0; - - if (k_left.down){ - player.p.ax -= 1; - player.p.dir = 2; - ++player.p.walkDist; - } - if (k_right.down){ - player.p.ax += 1; - player.p.dir = 3; - ++player.p.walkDist; - } - if (k_up.down){ - player.p.ay -= 1; - player.p.dir = 1; - ++player.p.walkDist; - } - if (k_down.down){ - player.p.ay += 1; - player.p.dir = 0; - ++player.p.walkDist; - } - if (player.p.staminaRechargeDelay % 2 == 0) moveMob(&player, player.p.ax, player.p.ay); - - - if (swimming && player.p.swimTimer % 60 == 0) { - if (player.p.stamina > 0) { - if(!TESTGODMODE) --player.p.stamina; - } else { - hurtEntity(&player,1,-1,0xFFAF00FF); - } - } - - if (k_pause.clicked){ - currentSelection = 0; - currentMenu = MENU_PAUSED; + initMinimapLevel(pd, 5); + + //spawn new entities + trySpawn(500, 5); } - if(k_attack.clicked){ - if (player.p.stamina != 0) { - if(!TESTGODMODE) player.p.stamina--; - player.p.staminaRecharge = 0; - playerAttack(); - //addEntityToList(newSlimeEntity(1,200,600,1), &eManager); - } + + pd->entity.level = 5; + pd->entity.x = ((128/2) << 4) + 8; + pd->entity.y = ((128/2) << 4) + 8; +} + +void leaveDungeon(PlayerData *pd) { + pd->entity.level = 4; + pd->entity.x = ((128/2) << 4) + 8; + pd->entity.y = ((128/2) << 4) + 8; + + //clear dungeon if empty + if(!dungeonActive()) { + //reset Entities + (&eManager)->lastSlot[5] = 0; + (&eManager)->entities[5][0] = nullEntity; } - - if (k_menu.clicked){ - curInvSel = 0; - if(!playerUse()) currentMenu = MENU_INVENTORY; - } - - if(isSwimming()) ++player.p.swimTimer; - if(player.p.attackTimer > 0) --player.p.attackTimer; - - //TODO - maybe move to own function - //Update Minimap - int xp; - int yp; - for(xp = (player.x>>4)-5; xp<(player.x>>4)+5; ++xp) { - for(yp = (player.y>>4)-5; yp<(player.y>>4)+5; ++yp) { - if(xp>=0 && xp<128 && yp>=0 && yp<128) { - if(!getMinimapVisible(currentLevel,xp,yp)) { - setMinimapVisible(currentLevel,xp,yp,true); - } - } - } - } } -bool isSwimming(){ - return getTile(player.x>>4,player.y>>4)==TILE_WATER; -} - -void playerSetActiveItem(Item * item) { - player.p.activeItem = item; - if(player.p.activeItem->id > 27 && player.p.activeItem->id < 51) player.p.isCarrying = true; - else player.p.isCarrying = false; -} - -void enterDungeon() { - //reset Entities - (&eManager)->lastSlot[5] = 0; - (&eManager)->entities[5][0] = nullEntity; - - //create map - currentLevel = 5; - createAndValidateDungeonMap(128, 128, 5, map[5], data[5]); - - //reset minimap clear state - int xd,yd; - for(xd = 0; xd < 128; ++xd) { - for(yd = 0; yd < 128; ++yd) { - setMinimapVisible(5, xd, yd, false); - } - } - initMinimapLevel(5, false); - newSeed(); - - player.x = ((128/2) << 4) + 8; - player.y = ((128/2) << 4) + 8; - - //spawn new entities - trySpawn(500, 5); - - updateMusic(currentLevel, daytime); -} - -void leaveDungeon() { - currentLevel = 4; - - //reset Entities - (&eManager)->lastSlot[5] = 0; - (&eManager)->entities[5][0] = nullEntity; - - player.x = ((128/2) << 4) + 8; - player.y = ((128/2) << 4) + 8; - - updateMusic(currentLevel, daytime); -} - -void setMinimapVisible(int level, int x, int y, bool visible) { +void setMinimapVisible(PlayerData *pd, int level, int x, int y, bool visible) { if(visible) { - minimapData[x + y * 128] = minimapData[x + y * 128] | (1 << level); + pd->minimapData[x + y * 128] = pd->minimapData[x + y * 128] | (1 << level); } else { - minimapData[x + y * 128] = minimapData[x + y * 128] & (0xFF - (1 << level)); + pd->minimapData[x + y * 128] = pd->minimapData[x + y * 128] & (0xFF - (1 << level)); } - sf2d_set_pixel(minimap[level], x, y, getMinimapColor(level, x, y)); + + if(pd==getLocalPlayer()) sf2d_set_pixel(minimap[level], x, y, getMinimapColor(pd, level, x, y)); } -bool getMinimapVisible(int level, int x, int y) { - return (minimapData[x + y * 128] & (1 << level)) > 0; +bool getMinimapVisible(PlayerData *pd, int level, int x, int y) { + return (pd->minimapData[x + y * 128] & (1 << level)) > 0; } -u32 getMinimapColor(int level, int x, int y) { - if(getMinimapVisible(level, x, y) || (currentLevel==0 && level==1)) return getTileColor(map[level][x + y * 128]); - else return getTileColor(map[level][x + y * 128]) & 0xFFFFFF00; +u32 getMinimapColor(PlayerData *pd, int level, int x, int y) { + if(getMinimapVisible(pd, level, x, y) || (pd->entity.level==0 && level==1)) return getTileColor(worldData.map[level][x + y * 128]); + else return getTileColor(worldData.map[level][x + y * 128]) & 0xFFFFFF00; } -void initMinimapLevel(int level, bool loadUpWorld) { +void initMinimapLevel(PlayerData *pd, int level) { int x; int y; - bool calculateCompass; - calculateCompass = ((!loadUpWorld) || (compassData[level][2] = 0)) && level<5; - - //Create Dungeon entrance(not located in mapgen, so it can also be created in old worlds) - if(level==4) { - map[level][64 + 64 * 128] = TILE_DUNGEON_ENTRANCE; - - map[level][63 + 64 * 128] = TILE_DIRT; - map[level][65 + 64 * 128] = TILE_DIRT; - map[level][64 + 63 * 128] = TILE_DIRT; - map[level][64 + 65 * 128] = TILE_DIRT; - - map[level][63 + 63 * 128] = TILE_DUNGEON_WALL; - map[level][63 + 65 * 128] = TILE_DUNGEON_WALL; - map[level][65 + 63 * 128] = TILE_DUNGEON_WALL; - map[level][65 + 65 * 128] = TILE_DUNGEON_WALL; - } + if(pd!=getLocalPlayer()) return; for (x = 0; x < 128; ++x) { for (y = 0; y < 128; ++y) { - - if (!loadUpWorld) { // generate stairs up when making a new world. - switch (map[level][x + y * 128]) { - case TILE_STAIRS_DOWN: - if(level < 4) { - map[level + 1][x + y * 128] = TILE_STAIRS_UP; - if (level == 0) { - map[level + 1][(x + 1) + y * 128] = TILE_HARDROCK; - map[level + 1][x + (y + 1) * 128] = TILE_HARDROCK; - map[level + 1][(x - 1) + y * 128] = TILE_HARDROCK; - map[level + 1][x + (y - 1) * 128] = TILE_HARDROCK; - map[level + 1][(x + 1) + (y + 1) * 128] = TILE_HARDROCK; - map[level + 1][(x - 1) + (y - 1) * 128] = TILE_HARDROCK; - map[level + 1][(x - 1) + (y + 1) * 128] = TILE_HARDROCK; - map[level + 1][(x + 1) + (y - 1) * 128] = TILE_HARDROCK; - } else { - map[level + 1][(x + 1) + y * 128] = TILE_DIRT; - map[level + 1][x + (y + 1) * 128] = TILE_DIRT; - map[level + 1][(x - 1) + y * 128] = TILE_DIRT; - map[level + 1][x + (y - 1) * 128] = TILE_DIRT; - map[level + 1][(x + 1) + (y + 1) * 128] = TILE_DIRT; - map[level + 1][(x - 1) + (y - 1) * 128] = TILE_DIRT; - map[level + 1][(x - 1) + (y + 1) * 128] = TILE_DIRT; - map[level + 1][(x + 1) + (y - 1) * 128] = TILE_DIRT; - } - } - } - } - if(calculateCompass) { - //choose one stair down and store for magic compass - switch (map[level][x + y * 128]) { - case TILE_STAIRS_DOWN: - case TILE_DUNGEON_ENTRANCE: - compassData[level][2] = compassData[level][2] + 1; - if((compassData[level][2]==1) || (rand()%(compassData[level][2])==0)) { - compassData[level][0] = x; - compassData[level][1] = y; - } - } - } - /* Minimaps */ - sf2d_set_pixel(minimap[level], x, y, getMinimapColor(level, x, y)); + sf2d_set_pixel(minimap[level], x, y, getMinimapColor(pd, level, x, y)); } } } void updateLevel1Map() { - int x; - int y; - - for (x = 0; x < 128; ++x) { - for (y = 0; y < 128; ++y) { - sf2d_set_pixel(minimap[1], x, y, getMinimapColor(1, x, y)); - } - } + initMinimapLevel(getLocalPlayer(), 1); } diff --git a/source/Globals.h b/source/Globals.h index 8c884e9..314136b 100644 --- a/source/Globals.h +++ b/source/Globals.h @@ -1,11 +1,14 @@ #pragma once + #include <3ds.h> -#include "SaveLoad.h" +#include "Entity.h" +#include "Player.h" #include "Input.h" #include "MapGen.h" #include "Quests.h" #include "icons2_png.h" +#include "player_png.h" #include "Font_png.h" #include "bottombg_png.h" @@ -17,18 +20,24 @@ #define MENU_TUTORIAL 2 #define MENU_ABOUT 3 #define MENU_SETTINGS 4 -#define MENU_INVENTORY 5 -#define MENU_CRAFTING 6 -#define MENU_CONTAINER 7 -#define MENU_WIN 8 -#define MENU_LOSE 9 -#define MENU_PAUSED 10 -#define MENU_LOADGAME 11 -#define MENU_SETTINGS_REBIND 12 -#define MENU_SETTINGS_TP 13 -#define MENU_DUNGEON 14 -#define MENU_NPC 15 -#define MENU_MULTIPLAYER 16 +#define MENU_LOADGAME 5 +#define MENU_SETTINGS_REBIND 6 +#define MENU_SETTINGS_TP 7 +#define MENU_MULTIPLAYER_HOST 8 +#define MENU_MULTIPLAYER_JOIN 9 +#define MENU_MULTIPLAYER_WAIT 10 +#define MENU_LOADING 11 + +#define MENU_PAUSED 100 +#define MENU_INVENTORY 101 +#define MENU_CRAFTING 102 +#define MENU_CONTAINER 103 +#define MENU_WIN 104 +#define MENU_LOSE 105 +#define MENU_DUNGEON 106 +#define MENU_NPC 107 +#define MENU_CHARACTER_CUSTOMIZE 108 + #define NPC_GIRL 0 #define NPC_PRIEST 1 @@ -78,33 +87,25 @@ #define SWAP_UINT32(x) (((x) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | ((x) << 24)) -//TODO: Dont forget to change back -#define TESTGODMODE true +//WARNING: Having this set to different values in different clients will break multiplayer! +#define TESTGODMODE false + +u32 localUID; -bool screenShot; int loadedtp; u8 MODEL_3DS; extern char versionText[34]; -Entity player; - bool shouldRenderDebug; bool shouldSpeedup; -bool shouldRenderMap; -u8 zoomLevel; -char mapText[32]; -s16 mScrollX, mScrollY; sf2d_texture *icons; +sf2d_texture *playerSprites; sf2d_texture *font; sf2d_texture *bottombg; -sf2d_texture * minimap[6]; -u8 map[6][128*128]; -u8 data[6][128*128]; -u8 minimapData[128*128]; -u8 compassData[6][3]; +sf2d_texture *minimap[6]; u32 dirtColor[5]; u32 grassColor; @@ -126,65 +127,72 @@ char currentFileName[256]; extern u8 currentMenu; extern char fpsstr[]; u8 initGame; +u8 initMPGame; u8 initBGMap; Item noItem; int airWizardHealthDisplay; s16 awX, awY; -u32 tickCount; -RecipeManager* currentRecipes; -Entity* curChestEntity; -char* currentCraftTitle; -s16 curInvSel; bool quitGame; s8 currentSelection; -bool isRemote; -u16 daytime; -int day; -u8 season; -bool rain; +typedef struct _worldData { + u8 map[6][128*128]; + u8 data[6][128*128]; + + u16 daytime; + int day; + u8 season; + bool rain; + + u8 compassData[6][3]; +} WorldData; -void tickTile(int x, int y); +WorldData worldData; + +//TODO: cleanup the order +int getEntities(Entity** result, s8 level, int x0, int y0, int x1, int y1); + +bool moveMob(Entity* e, int xa, int ya); +void hurtEntity(Entity *e, int damage, int dir, u32 hurtColor, Entity *damager); + +void tickTile(s8 level, int x, int y); bool tileIsSolid(int tile, Entity * e); -s8 itemTileInteract(int tile, Item* item, int x, int y, int px, int py, int dir); +s8 itemTileInteract(int tile, PlayerData *pd, Item *item, s8 level, int x, int y, int px, int py, int dir); void tickEntity(Entity* e); -void tickTouchMap(); -void tickTouchQuickSelect(); - void trySpawn(int count, int level); -int getTile(int x, int y); +int getTile(s8 level, int x, int y); +void setTile(int id, s8 level, int x, int y); +int getData(s8 level, int x, int y); +void setData(int id, s8 level, int x, int y); u32 getTileColor(int tile); -void setTile(int id, int x, int y); -int getData(int x, int y); -void setData(int id, int x, int y); bool intersectsEntity(int x, int y, int r, Entity* e); bool EntityBlocksEntity(Entity* e1, Entity* e2); void EntityVsEntity(Entity* e1, Entity* e2); -void entityTileInteract(Entity* e, int tile,int x, int y); +bool ItemVsEntity(PlayerData *pd, Item *item, Entity *e, int dir); +void entityTileInteract(Entity* e, int tile, s8 level, int x, int y); -void initPlayer(); -void tickPlayer(); -void playerAttack(); -bool isSwimming(); -bool playerUseEnergy(int amount); -void playerHurtTile(int tile, int xt, int yt, int damage, int dir); -bool playerIntersectsEntity(Entity* e); -void playerEntityInteract(Entity* e); -void playerSetActiveItem(Item * item); +void openCraftingMenu(PlayerData *pd, RecipeManager *rm, char *title); +bool useEntity(PlayerData *pd, Entity* e); -void enterDungeon(); -void leaveDungeon(); +bool isWater(s8 level, int xt, int yt); -void setMinimapVisible(int level, int x, int y, bool visible); -bool getMinimapVisible(int level, int x, int y); -u32 getMinimapColor(int level, int x, int y); -void initMinimapLevel(int level, bool loadUpWorld); +void playerHurtTile(PlayerData *pd, int tile, s8 level, int xt, int yt, int damage, int dir); +void playerEntityInteract(PlayerData *pd, Entity* e); + +bool dungeonActive(); +void enterDungeon(PlayerData *pd); +void leaveDungeon(PlayerData *pd); + +void setMinimapVisible(PlayerData *pd, int level, int x, int y, bool visible); +bool getMinimapVisible(PlayerData *pd, int level, int x, int y); +u32 getMinimapColor(PlayerData *pd, int level, int x, int y); +void initMinimapLevel(PlayerData *pd, int level); void updateLevel1Map(); void reloadColors(); \ No newline at end of file diff --git a/source/Ingame.c b/source/Ingame.c new file mode 100644 index 0000000..1263f47 --- /dev/null +++ b/source/Ingame.c @@ -0,0 +1,292 @@ +#include "Ingame.h" + +#include "Globals.h" +#include "Player.h" +#include "Entity.h" +#include "IngameMenu.h" +#include "Render.h" +#include "MapGen.h" +#include "Synchronizer.h" +#include "SaveLoad.h" +#include "Network.h" + +//generates stairs up and creates compass data +void generatePass2() { + int level, x, y; + + for (level = 0; level < 5; ++level) { + for (x = 0; x < 128; ++x) { + for (y = 0; y < 128; ++y) { + + //generate stairs up matching stairs down + switch (worldData.map[level][x + y * 128]) { + case TILE_STAIRS_DOWN: + if(level < 4) { + worldData.map[level + 1][x + y * 128] = TILE_STAIRS_UP; + if (level == 0) { + worldData.map[level + 1][(x + 1) + y * 128] = TILE_HARDROCK; + worldData.map[level + 1][x + (y + 1) * 128] = TILE_HARDROCK; + worldData.map[level + 1][(x - 1) + y * 128] = TILE_HARDROCK; + worldData.map[level + 1][x + (y - 1) * 128] = TILE_HARDROCK; + worldData.map[level + 1][(x + 1) + (y + 1) * 128] = TILE_HARDROCK; + worldData.map[level + 1][(x - 1) + (y - 1) * 128] = TILE_HARDROCK; + worldData.map[level + 1][(x - 1) + (y + 1) * 128] = TILE_HARDROCK; + worldData.map[level + 1][(x + 1) + (y - 1) * 128] = TILE_HARDROCK; + } else { + worldData.map[level + 1][(x + 1) + y * 128] = TILE_DIRT; + worldData.map[level + 1][x + (y + 1) * 128] = TILE_DIRT; + worldData.map[level + 1][(x - 1) + y * 128] = TILE_DIRT; + worldData.map[level + 1][x + (y - 1) * 128] = TILE_DIRT; + worldData.map[level + 1][(x + 1) + (y + 1) * 128] = TILE_DIRT; + worldData.map[level + 1][(x - 1) + (y - 1) * 128] = TILE_DIRT; + worldData.map[level + 1][(x - 1) + (y + 1) * 128] = TILE_DIRT; + worldData.map[level + 1][(x + 1) + (y - 1) * 128] = TILE_DIRT; + } + } + } + + //calculate compass data + //choose one stair down and store for magic compass + switch (worldData.map[level][x + y * 128]) { + case TILE_STAIRS_DOWN: + case TILE_DUNGEON_ENTRANCE: + worldData.compassData[level][2] = worldData.compassData[level][2] + 1; + if((worldData.compassData[level][2]==1) || (rand()%(worldData.compassData[level][2])==0)) { + worldData.compassData[level][0] = x; + worldData.compassData[level][1] = y; + } + } + } + } + } +} + +void initNewMap() { + createAndValidateSkyMap(128, 128, 0, worldData.map[0], worldData.data[0]); + createAndValidateTopMap(128, 128, 1, worldData.map[1], worldData.data[1]); + createAndValidateUndergroundMap(128, 128, 1, 2, worldData.map[2], worldData.data[2]); + createAndValidateUndergroundMap(128, 128, 2, 3, worldData.map[3], worldData.data[3]); + createAndValidateUndergroundMap(128, 128, 3, 4, worldData.map[4], worldData.data[4]); + generatePass2(); +} + +void initMiniMap(PlayerData *pd) { + int i; + for (i = 0; i < 5; ++i) { + initMinimapLevel(pd, i); + } +} + +void startGame(bool load, char *filename) { + // Reset entity manager. + memset(&eManager, 0, sizeof(eManager)); + + // Reset players + for(int i=0; ientity.level, getLocalPlayer()->entity.x, getLocalPlayer()->entity.y); + + //win/death menus + for(i=0; i=24000) { + worldData.daytime -= 24000; + ++worldData.day; + //TODO: maybe make season length not as hardcoded + make the transition better (fade to black and back maybe?) + if(worldData.day%7==0) { + ++worldData.season; + if(worldData.season==4) worldData.season = 0; + } + worldData.rain = false; + if(worldData.season!=3 && rand()%5==0) worldData.rain = true; + } + + //update music + updateMusic(getLocalPlayer()->entity.level, worldData.daytime); + + //for every active level + s8 level; + for(level = 0; level < 6; level++) { + bool hasPlayer = false; + for(i=0; ix, e->y); + + //should never happen, but just for safety to prevent hard crashes + if(p==NULL) continue; + + if ((e->type != ENTITY_ZOMBIE && e->type != ENTITY_SKELETON && e->type != ENTITY_KNIGHT && e->type != ENTITY_SLIME && e->type != ENTITY_PASSIVE && e->type != ENTITY_GLOWWORM) + || (e->type == ENTITY_GLOWWORM && (worldData.daytime>6000 || worldData.daytime<18000)) + || (e->x > p->entity.x - 160 && e->y > p->entity.y - 125 && e->x < p->entity.x + 160 && e->y < p->entity.y + 125)) { + tickEntity(e); + } + } + } +} + +void tickGame() { + synchronizerTick(&syncedTick); +} + +//for rendering -> move to a better place +int xscr = 0, yscr = 0; + +void renderGame() { + //Important: all code called from this function should never affect game state! + sf2d_start_frame(GFX_TOP, GFX_LEFT); + + int xscr = getLocalPlayer()->entity.x - 100; + int yscr = getLocalPlayer()->entity.y - 56; + if (xscr < 16) + xscr = 16; + else if (xscr > 1832) + xscr = 1832; + if (yscr < 16) + yscr = 16; + else if (yscr > 1912) + yscr = 1912; + + + offsetX = xscr; + offsetY = yscr; + sf2d_draw_rectangle(0, 0, 400, 240, 0xFF0C0C0C); //RGBA8(12, 12, 12, 255)); //You might think "real" black would be better, but it actually looks better that way + + renderLightsToStencil(getLocalPlayer(), false, false, true); + + renderBackground(getLocalPlayer()->entity.level, xscr, yscr); + renderEntities(getLocalPlayer()->entity.level, getLocalPlayer()->entity.x, getLocalPlayer()->entity.y, &eManager); + for(int i=0; ientity.level, xscr, yscr); + + resetStencilStuff(); + + renderDayNight(getLocalPlayer()); + + offsetX = 0; + offsetY = 0; + + if(shouldRenderDebug){ + sprintf(fpsstr, " FPS: %.0f, X:%d, Y:%d, E:%d", sf2d_get_fps(), getLocalPlayer()->entity.x, getLocalPlayer()->entity.y, eManager.lastSlot[getLocalPlayer()->entity.level]); + drawText(fpsstr, 2, 225); + } + + if(getLocalPlayer()->ingameMenu != MENU_NONE) { + ingameMenuRender(getLocalPlayer(), getLocalPlayer()->ingameMenu); + } + + sf2d_end_frame(); + + sf2d_start_frame(GFX_BOTTOM, GFX_LEFT); + if(!players[playerLocalID].mapShouldRender){ + sf2d_draw_texture(bottombg, 0, 0); + renderGui(getLocalPlayer()); + } else { + renderZoomedMap(getLocalPlayer()); + } + sf2d_end_frame(); +} + diff --git a/source/Ingame.h b/source/Ingame.h new file mode 100644 index 0000000..538fa04 --- /dev/null +++ b/source/Ingame.h @@ -0,0 +1,7 @@ +#pragma once + +#include <3ds.h> + +void startGame(bool load, char *filename); +void tickGame(); +void renderGame(); diff --git a/source/IngameMenu.c b/source/IngameMenu.c new file mode 100644 index 0000000..62c8120 --- /dev/null +++ b/source/IngameMenu.c @@ -0,0 +1,528 @@ +#include "IngameMenu.h" + +#include "Globals.h" +#include "Menu.h" +#include "Player.h" +#include "SaveLoad.h" +#include "Synchronizer.h" + +char pOptions[][24] = {"Return to game", "Save Progress", "Exit to title"}; + +void ingameMenuTick(PlayerData *pd, int menu) { + switch(menu) { + case MENU_PAUSED: + if(!pd->ingameMenuAreYouSure && !pd->ingameMenuAreYouSureSave){ + if (pd->ingameMenuTimer > 0) --pd->ingameMenuTimer; + if (pd->inputs.k_pause.clicked || pd->inputs.k_decline.clicked) pd->ingameMenu = MENU_NONE; + if (pd->inputs.k_up.clicked) { --pd->ingameMenuSelection; if(pd->ingameMenuSelection < 0) pd->ingameMenuSelection=2; } + if (pd->inputs.k_down.clicked) { ++pd->ingameMenuSelection; if(pd->ingameMenuSelection > 2) pd->ingameMenuSelection=0; } + if (pd->inputs.k_accept.clicked){ + switch(pd->ingameMenuSelection){ + case 0: + pd->ingameMenu = MENU_NONE; + break; + case 1: + if(!dungeonActive()) pd->ingameMenuAreYouSureSave = true; + break; + case 2: + pd->ingameMenuAreYouSure = true; + break; + } + } + } else if(pd->ingameMenuAreYouSureSave) { + if (pd->inputs.k_accept.clicked){ + pd->ingameMenuTimer = 60; + + if(playerLocalID==0) { + saveWorld(currentFileName, &eManager, &worldData, players, playerCount); + } + pd->ingameMenuAreYouSureSave = false; + pd->ingameMenuAreYouSure = false; + } else if (pd->inputs.k_decline.clicked){ + pd->ingameMenuAreYouSureSave = false; + pd->ingameMenuAreYouSure = false; + } + } else if(pd->ingameMenuAreYouSure) { + if (pd->inputs.k_accept.clicked){ + pd->ingameMenuAreYouSure = false; + pd->ingameMenuAreYouSureSave = false; + + networkDisconnect(); + synchronizerReset(); + + sf2d_set_clear_color(0xFF); + currentSelection = 0; + currentMenu = MENU_TITLE; + + playMusic(&music_menu); + } else if (pd->inputs.k_decline.clicked){ + pd->ingameMenuAreYouSure = false; + pd->ingameMenuAreYouSureSave = false; + } + } + break; + case MENU_INVENTORY: + if (pd->inputs.k_menu.clicked || pd->inputs.k_decline.clicked){ + pd->ingameMenu = MENU_NONE; + pd->activeItem = &noItem; + pd->entity.p.isCarrying = false; + } + if (pd->inputs.k_accept.clicked){ // Select item from inventory + if(pd->inventory.lastSlot!=0){ + Item median = pd->inventory.items[pd->ingameMenuInvSel]; // create copy of item. + removeItemFromInventory(pd->ingameMenuInvSel, &(pd->inventory)); // remove original + pushItemToInventoryFront(median, &(pd->inventory)); // add copy to front + playerSetActiveItem(pd, &(pd->inventory.items[0])); // active item = copy. + } + pd->ingameMenu = MENU_NONE; + } + if (pd->inputs.k_up.clicked) { --pd->ingameMenuInvSel; if(pd->ingameMenuInvSel < 0)pd->ingameMenuInvSel=pd->inventory.lastSlot-1; } + if (pd->inputs.k_down.clicked) { ++pd->ingameMenuInvSel; if(pd->ingameMenuInvSel > pd->inventory.lastSlot-1)pd->ingameMenuInvSel=0; } + break; + + case MENU_CRAFTING: + if (pd->inputs.k_menu.clicked || pd->inputs.k_decline.clicked) pd->ingameMenu = MENU_NONE; + if (pd->inputs.k_accept.clicked){ + if(craftItem(&(pd->currentRecipes), &(pd->currentRecipes.recipes[pd->ingameMenuInvSel]), &(pd->inventory))){ + playSoundPositioned(snd_craft, pd->entity.level, pd->entity.x, pd->entity.y); + //reset active item pointer, because it could posibly point to garbage now + pd->activeItem = &noItem; + } + } + if (pd->inputs.k_up.clicked) { --pd->ingameMenuInvSel; if(pd->ingameMenuInvSel < 0)pd->ingameMenuInvSel=pd->currentRecipes.size-1; } + if (pd->inputs.k_down.clicked) { ++pd->ingameMenuInvSel; if(pd->ingameMenuInvSel > pd->currentRecipes.size-1)pd->ingameMenuInvSel=0; } + break; + + case MENU_WIN: + if (pd->inputs.k_accept.clicked){ + pd->ingameMenu = MENU_NONE; + } + break; + case MENU_LOSE: + if (pd->inputs.k_accept.clicked){ + pd->ingameMenu = MENU_NONE; + playerSpawn(pd); + //TODO: This canceled to main menu, but what should I do in multiplayer? + } + break; + + case MENU_CONTAINER: + if (pd->inputs.k_menu.clicked || pd->inputs.k_decline.clicked) pd->ingameMenu = MENU_NONE; + + if (pd->inputs.k_left.clicked) { + pd->curChestEntityR = 0; + int tmp = pd->ingameMenuInvSel; + pd->ingameMenuInvSel = pd->ingameMenuInvSelOther; + pd->ingameMenuInvSelOther = tmp; + } + if (pd->inputs.k_right.clicked) { + pd->curChestEntityR = 1; + int tmp = pd->ingameMenuInvSel; + pd->ingameMenuInvSel = pd->ingameMenuInvSelOther; + pd->ingameMenuInvSelOther = tmp; + } + + Inventory* i1 = pd->curChestEntityR == 1 ? &(pd->inventory) : pd->curChestEntity->entityFurniture.inv; + Inventory* i2 = pd->curChestEntityR == 0 ? &(pd->inventory) : pd->curChestEntity->entityFurniture.inv; + int len = i1->lastSlot; + if (pd->ingameMenuInvSel < 0) pd->ingameMenuInvSel = 0; + if (pd->ingameMenuInvSel >= len) pd->ingameMenuInvSel = len - 1; + if (pd->inputs.k_up.clicked) --pd->ingameMenuInvSel; + if (pd->inputs.k_down.clicked) ++pd->ingameMenuInvSel; + if (len == 0) pd->ingameMenuInvSel = 0; + if (pd->ingameMenuInvSel < 0) pd->ingameMenuInvSel += len; + if (pd->ingameMenuInvSel >= len) pd->ingameMenuInvSel -= len; + + if(pd->inputs.k_accept.clicked && len > 0){ + Item* pullItem = &i1->items[pd->ingameMenuInvSel]; + Item pushItem = newItem(pullItem->id, pullItem->countLevel); + pushItem.chestPtr = pullItem->chestPtr; + pushItemToInventoryFront(pushItem, i2); + if(i2 == &(pd->inventory)){ + int newslot = pd->activeItem->slotNum + 1; + pd->activeItem = &(pd->inventory.items[newslot]); + } else if(pullItem == pd->activeItem){ + pd->activeItem = &noItem; + } + removeItemFromCurrentInv(pullItem); + if (pd->ingameMenuInvSel >= i1->lastSlot) pd->ingameMenuInvSel = i1->lastSlot - 1; + } + break; + + case MENU_DUNGEON: + if (pd->inputs.k_menu.clicked || pd->inputs.k_decline.clicked) pd->ingameMenu = MENU_NONE; + + if(pd->inputs.k_accept.clicked) { + if(pd->entity.level!=5) { + Item * item = getItemFromInventory(ITEM_DUNGEON_KEY, &(pd->inventory)); + if(item!=NULL) { + --item->countLevel; + if(item->countLevel==0) { + removeItemFromCurrentInv(item); + } + + enterDungeon(pd); + } else if(TESTGODMODE) { + enterDungeon(pd); + } + } else { + leaveDungeon(pd); + } + + pd->ingameMenu = MENU_NONE; + } + break; + + case MENU_NPC: + tickNPCMenu(pd); + break; + + case MENU_CHARACTER_CUSTOMIZE: + if (pd->inputs.k_up.clicked) { --pd->ingameMenuSelection; if(pd->ingameMenuSelection < 0) pd->ingameMenuSelection=6; } + if (pd->inputs.k_down.clicked) { ++pd->ingameMenuSelection; if(pd->ingameMenuSelection > 6) pd->ingameMenuSelection=0; } + + u8 wrap = 0; + wrap = wrap - 1; + + pd->entity.p.health = 10; + + //head + if(pd->ingameMenuSelection==0) { + if (pd->inputs.k_left.clicked) { --pd->sprite.head; if(pd->sprite.head == wrap) pd->sprite.head=PLAYER_SPRITE_HEAD_COUNT-1; } + if (pd->inputs.k_right.clicked) { ++pd->sprite.head; if(pd->sprite.head > PLAYER_SPRITE_HEAD_COUNT-1) pd->sprite.head=0; } + //eyes + } else if(pd->ingameMenuSelection==1) { + if (pd->inputs.k_left.clicked) { --pd->sprite.eyes; if(pd->sprite.eyes == wrap) pd->sprite.eyes=PLAYER_SPRITE_EYES_COUNT-1; } + if (pd->inputs.k_right.clicked) { ++pd->sprite.eyes; if(pd->sprite.eyes > PLAYER_SPRITE_EYES_COUNT-1) pd->sprite.eyes=0; } + //body + } else if(pd->ingameMenuSelection==2) { + if (pd->inputs.k_left.clicked) { --pd->sprite.body; if(pd->sprite.body == wrap) pd->sprite.body=PLAYER_SPRITE_BODY_COUNT-1; } + if (pd->inputs.k_right.clicked) { ++pd->sprite.body; if(pd->sprite.body > PLAYER_SPRITE_BODY_COUNT-1) pd->sprite.body=0; } + //arms + } else if(pd->ingameMenuSelection==3) { + if (pd->inputs.k_left.clicked) { --pd->sprite.arms; if(pd->sprite.arms == wrap) pd->sprite.arms=PLAYER_SPRITE_ARMS_COUNT-1; } + if (pd->inputs.k_right.clicked) { ++pd->sprite.arms; if(pd->sprite.arms > PLAYER_SPRITE_ARMS_COUNT-1) pd->sprite.arms=0; } + //legs + } else if(pd->ingameMenuSelection==4) { + if (pd->inputs.k_left.clicked) { --pd->sprite.legs; if(pd->sprite.legs == wrap) pd->sprite.legs=PLAYER_SPRITE_LEGS_COUNT-1; } + if (pd->inputs.k_right.clicked) { ++pd->sprite.legs; if(pd->sprite.legs > PLAYER_SPRITE_LEGS_COUNT-1) pd->sprite.legs=0; } + //rotation + } else if(pd->ingameMenuSelection==5) { + if (pd->inputs.k_left.clicked) { --pd->entity.p.dir; if(pd->entity.p.dir == wrap) pd->entity.p.dir=3; } + if (pd->inputs.k_right.clicked) { ++pd->entity.p.dir; if(pd->entity.p.dir > 3) pd->entity.p.dir=0; } + //done + } else if(pd->ingameMenuSelection==6) { + if(pd->inputs.k_accept.clicked) { + //TODO: are you sure dialog? + pd->ingameMenu = 0; + pd->ingameMenuSelection = 0; + pd->sprite.choosen = true; + } + } + break; + } +} + + +u8 opacity = 255; +bool rev = true; +char scoreText[15]; + +void ingameMenuRender(PlayerData *pd, int menu) { + int i; + + switch(menu) { + case MENU_PAUSED: + renderFrame(1,1,24,14,0xFF1010AF); + drawText("Paused",164,32); + for(i = 3; i >= 0; --i){ + char* msg = pOptions[i]; + u32 color = 0xFF7F7F7F; + if(i == pd->ingameMenuSelection) color = 0xFFFFFFFF; + if((i == 1 && dungeonActive())) { + color = 0xFF7F7FFF; + if(i == pd->ingameMenuSelection) color = 0xFFAFAFFF; + } + drawTextColor(msg,(400 - (strlen(msg) * 12))/2, (i * 24) + 88, color); + } + + if(pd->ingameMenuTimer > 0) drawTextColor("Game Saved!", (400-(11*12))/2, 64,0xFF20FF20); + + if(pd->ingameMenuAreYouSure || pd->ingameMenuAreYouSureSave){ + if(pd->ingameMenuAreYouSure)renderFrame(6,5,19,10,0xFF10108F); + else renderFrame(6,5,19,10,0xFF108F10); + + drawText("Are you sure?",122,96); + drawText(" Yes", 164, 117); + renderButtonIcon(localInputs.k_accept.input & -localInputs.k_accept.input, 166, 114, 1); + drawText(" No", 170, 133); + renderButtonIcon(localInputs.k_decline.input & -localInputs.k_decline.input, 166, 130, 1); + } + break; + case MENU_WIN: + renderFrame(5,3,21,12,0xFFFF1010); + if(!rev){ opacity+=5; if(opacity == 255) rev = true; } + else { opacity-=5; if(opacity == 100) rev = false; } + sprintf(scoreText,"Score: %d", pd->score); + drawTextColor("You Win!",158,76,0x0000AFAF + (opacity << 24)); + drawText(scoreText, 200-((strlen(scoreText)-1)*6), 100); + drawText("Press to continue", 96, 150); + renderButtonIcon(localInputs.k_attack.input & -localInputs.k_attack.input, 166, 148, 1); + + //printf("0x%08X",localInputs.k_attack.input & -localInputs.k_attack.input); + break; + case MENU_LOSE: + renderFrame(5,3,21,12,0xFFFF1010); + if(!rev){ opacity+=5; if(opacity == 255) rev = true; } + else { opacity-=5; if(opacity == 100) rev = false; } + sprintf(scoreText,"Score: %d", pd->score); + drawTextColor("You DIED!",158,76,0x000000AF + (opacity << 24)); + drawText(scoreText, 200-((strlen(scoreText)-1)*6), 100); + drawText("Press to continue", 96, 150); + renderButtonIcon(localInputs.k_attack.input & -localInputs.k_attack.input, 166, 148, 1); + //printf("0x%08X",localInputs.k_attack.input & -localInputs.k_attack.input); + break; + case MENU_INVENTORY: + renderFrame(1,1,24,14,0xFFFF1010); + drawTextColor("Inventory",24+1,14+1,0xFF000000); + drawTextColor("Inventory",24,14,0xFF6FE2E2); + renderItemList(&(pd->inventory), 1,1,24,14, pd->ingameMenuInvSel); + break; + case MENU_CRAFTING: + renderFrame(15,1,24,4,0xFFFF1010); + drawTextColor("Have",248+1,14+1,0xFF000000); + drawTextColor("Have",248,14,0xFF6FE2E2); + renderFrame(15,5,24,14,0xFFFF1010); + drawTextColor("Cost",248+1,78+1,0xFF000000); + drawTextColor("Cost",248,78,0xFF6FE2E2); + renderFrame(1,1,14,14,0xFFFF1010); + drawTextColor(pd->currentCraftTitle,24+1,14+1,0xFF000000); + drawTextColor(pd->currentCraftTitle,24,14,0xFF6FE2E2); + renderRecipes(&(pd->currentRecipes), 1, 1, 14, 14, pd->ingameMenuInvSel); + + Recipe* rec = &(pd->currentRecipes.recipes[pd->ingameMenuInvSel]); + renderItemIcon(rec->itemResult,rec->itemAmountLevel,128,16); + char craftText[12]; + sprintf(craftText, "%d", countItemInv(rec->itemResult, rec->itemAmountLevel, &(pd->inventory))); + drawText(craftText,274,34); + + if(rec->numOfCosts > 0){ + int i; + for(i = 0; i < rec->numOfCosts; i++){ + int amnt = countItemInv(rec->costs[i].costItem,0, &(pd->inventory)); + int ttlCst = rec->costs[i].costAmount; + int col = 0xFFFFFFFF; if(amntcosts[i].costItem,1,128,48+(i*8)); + sprintf(craftText,"%d/%d",amnt,ttlCst); + drawTextColor(craftText,274,96+(i*18),col); + } + } + break; + + case MENU_CONTAINER: + if (pd->curChestEntityR == 1){ offsetX = 48; offsetY = 0;} + else {offsetX = 0; offsetY = 0;} + + renderFrame(1,1,15,14,0xFFFF1010); + drawTextColor("Chest",24+1,14+1,0xFF000000); + drawTextColor("Chest",24,14,0xFF6FE2E2); + renderItemList(pd->curChestEntity->entityFurniture.inv,1,1,15,14, + pd->curChestEntityR == 0 ? pd->ingameMenuInvSel : -pd->ingameMenuInvSelOther - 1); + renderFrame(16,1,30,14,0xFFFF1010); + drawTextColor("Inventory",264+1,14+1,0xFF000000); + drawTextColor("Inventory",264,14,0xFF6FE2E2); + renderItemList(&(pd->inventory),16,1,30,14, + pd->curChestEntityR == 1 ? pd->ingameMenuInvSel : -pd->ingameMenuInvSelOther - 1); + offsetX = 0;offsetY = 0; + break; + + case MENU_DUNGEON: + renderFrame(1,1,24,14,0xFFFF1010); + if(pd->entity.level!=5) { + drawTextColor("Dungeon Entrance",24+1,14+1,0xFF000000); + drawTextColor("Dungeon Entrance",24,14,0xFF6FE2E2); + + drawText("Warning: ", 32, 32); + drawText("You need a Dungeon Key to ", 32, 56); + drawText("enter and cannot save while ", 32, 72); + drawText("being in the Dungeon! ", 32, 88); + drawText("After leaving you will need ", 32, 112); + drawText("a new Dungeon Key for ", 32, 128); + drawText("entering another Dungeon! ", 32, 144); + + drawText(" Enter", 148, 171); + } else { + drawTextColor("Dungeon Exit",24+1,14+1,0xFF000000); + drawTextColor("Dungeon Exit",24,14,0xFF6FE2E2); + + drawText("Warning: ", 32, 32); + drawText("The Dungeon and everything ", 32, 56); + drawText("in it will disappear when ", 32, 72); + drawText("you leave it! ", 32, 88); + drawText("You will need a new Dungeon ", 32, 112); + drawText("Key for entering another ", 32, 128); + drawText("Dungeon again! ", 32, 144); + + drawText(" Leave", 148, 171); + } + + renderButtonIcon(localInputs.k_accept.input & -localInputs.k_accept.input, 150, 168, 1); + drawText(" Stay", 148, 195); + renderButtonIcon(localInputs.k_decline.input & -localInputs.k_decline.input, 150, 192, 1); + break; + + case MENU_NPC: + renderNPCMenu(&(pd->npcMenuData)); + break; + + case MENU_CHARACTER_CUSTOMIZE: + renderFrame(1,1,24,14,0xFFFF1010); + drawTextColor("Character",24+1,14+1,0xFF000000); + drawTextColor("Character",24,14,0xFF6FE2E2); + + drawText("Head: ", 32, 56); + drawText("Eyes: ", 32, 72); + drawText("Body: ", 32, 88); + drawText("Arms: ", 32, 104); + drawText("Legs: ", 32, 120); + drawText("Rot.: ", 32, 144); + + //for the dynamic part + char display[30]; + + sprintf(display, pd->ingameMenuSelection==0 ? "< %02i/%02i >" : " %02i/%02i ", pd->sprite.head+1, PLAYER_SPRITE_HEAD_COUNT); + drawText(display, 96, 56); + sprintf(display, pd->ingameMenuSelection==1 ? "< %02i/%02i >" : " %02i/%02i ", pd->sprite.eyes+1, PLAYER_SPRITE_EYES_COUNT); + drawText(display, 96, 72); + sprintf(display, pd->ingameMenuSelection==2 ? "< %02i/%02i >" : " %02i/%02i ", pd->sprite.body+1, PLAYER_SPRITE_BODY_COUNT); + drawText(display, 96, 88); + sprintf(display, pd->ingameMenuSelection==3 ? "< %02i/%02i >" : " %02i/%02i ", pd->sprite.arms+1, PLAYER_SPRITE_ARMS_COUNT); + drawText(display, 96, 104); + sprintf(display, pd->ingameMenuSelection==4 ? "< %02i/%02i >" : " %02i/%02i ", pd->sprite.legs+1, PLAYER_SPRITE_LEGS_COUNT); + drawText(display, 96, 120); + sprintf(display, pd->ingameMenuSelection==5 ? "< %02i/%02i >" : " %02i/%02i ", pd->entity.p.dir+1, 4); + drawText(display, 96, 144); + sprintf(display, pd->ingameMenuSelection==6 ? "< Done >" : " Done "); + drawText(display, 96, 172); + + int oox = offsetX; + int ooy = offsetY; + int osx = playerScale; + + renderFrame(13,3,22,12,0xFF909090); + //move player sprite to 0/0 + offsetX = pd->entity.x - 8; + offsetY = pd->entity.y - 8; + //move to where I want it + offsetX -= 108; + offsetY -= 28; + playerScale = 8; + renderPlayer(pd); + + offsetX = oox; + offsetY = ooy; + playerScale = osx; + break; + } +} + + +//touch menu +void tickTouchMap(PlayerData *pd){ + if(pd->mapShouldRender){ + if(pd->inputs.k_touch.px > 0 || pd->inputs.k_touch.py > 0){ + // Plus/Minus zoom button + if(pd->inputs.k_touch.py > 204 && pd->inputs.k_touch.py < 232){ + if(pd->inputs.k_touch.px > 284 && pd->inputs.k_touch.px < 312){ + if(pd->mapZoomLevel > 4) return; + if(!pd->touchIsChangingSize && !pd->touchIsDraggingMap){ + pd->mapZoomLevel += 2; + pd->mapScrollX -= (50 * (pd->mapZoomLevel/2)); + pd->mapScrollY -= (40 * (pd->mapZoomLevel/2)); + pd->touchIsChangingSize = true; + sprintf(pd->mapText, "x%d", pd->mapZoomLevel); + } + if(pd->mapScrollX < 320-(128*pd->mapZoomLevel)) pd->mapScrollX = 320-(128*pd->mapZoomLevel); + else if(pd->mapScrollX > 0) pd->mapScrollX = 0; + if(pd->mapScrollY < 240-(128*pd->mapZoomLevel)) pd->mapScrollY = 240-(128*pd->mapZoomLevel); + else if(pd->mapScrollY > 0) pd->mapScrollY = 0; + return; + } else if(pd->inputs.k_touch.px > 256 && pd->inputs.k_touch.px < 284){ + if(pd->mapZoomLevel < 4) return; + if(!pd->touchIsChangingSize && !pd->touchIsDraggingMap){ + pd->mapScrollX += (50 * (pd->mapZoomLevel/2)); + pd->mapScrollY += (40 * (pd->mapZoomLevel/2)); + pd->mapZoomLevel -= 2; + pd->touchIsChangingSize = true; + sprintf(pd->mapText, "x%d", pd->mapZoomLevel); + } + if(pd->mapScrollX < 320-(128*pd->mapZoomLevel)) pd->mapScrollX = 320-(128*pd->mapZoomLevel); + else if(pd->mapScrollX > 0) pd->mapScrollX = 0; + if(pd->mapScrollY < 240-(128*pd->mapZoomLevel)) pd->mapScrollY = 240-(128*pd->mapZoomLevel); + else if(pd->mapScrollY > 0) pd->mapScrollY = 0; + return; + } + } else if(pd->inputs.k_touch.py > 8 && pd->inputs.k_touch.py < 40 && pd->inputs.k_touch.px > 284 && pd->inputs.k_touch.px < 312){ + // Exit Button + if(!pd->touchIsChangingSize && !pd->touchIsDraggingMap){ + pd->mapShouldRender = false; + return; + } + } + + if(!pd->touchIsDraggingMap){ + pd->touchLastX = pd->inputs.k_touch.px; + pd->touchLastY = pd->inputs.k_touch.py; + } + if(pd->mapZoomLevel > 2){ + int dx = pd->touchLastX - pd->inputs.k_touch.px; + if(dx > 1 || dx < -1){ + pd->mapScrollX -= dx; + if(pd->mapScrollX < 320-(128*pd->mapZoomLevel)) pd->mapScrollX = 320-(128*pd->mapZoomLevel); + else if(pd->mapScrollX > 0) pd->mapScrollX = 0; + } + pd->touchLastX = pd->inputs.k_touch.px; + } + + int dy = pd->touchLastY - pd->inputs.k_touch.py; + if(dy > 1 || dy < -1){ + pd->mapScrollY -= dy; + if(pd->mapScrollY < 240-(128*pd->mapZoomLevel)) pd->mapScrollY = 240-(128*pd->mapZoomLevel); + else if(pd->mapScrollY > 0) pd->mapScrollY = 0; + } + pd->touchLastY = pd->inputs.k_touch.py; + pd->touchIsDraggingMap = true; + } else { + pd->touchIsDraggingMap = false; + pd->touchIsChangingSize = false; + } + } else { + // touch minimap to bring up zoomed map. + if(pd->inputs.k_touch.py > 100 && pd->inputs.k_touch.py < 228 && pd->inputs.k_touch.px > 10 && pd->inputs.k_touch.px < 142){ + pd->mapShouldRender = true; + } + } +} + +void tickTouchQuickSelect(PlayerData *pd) { + if (pd->ingameMenu == MENU_NONE && !pd->mapShouldRender) { + int i = 0; + Inventory * inv = &(pd->inventory); + + for (i = 0; i < 8; ++i) { + if((inv->lastSlot) > i) { + int xip = i % 4; + int yip = i / 4; + + if(pd->inputs.k_touch.py > 72*2+yip*21*2 && pd->inputs.k_touch.py < 72*2+yip*21*2+21*2 && pd->inputs.k_touch.px > 76*2+xip*21*2 && pd->inputs.k_touch.px < 76*2+xip*21*2+21*2) { + playerSetActiveItem(pd, &inv->items[i]); + } + } + } + } +} + +void ingameMenuTickTouch(PlayerData *pd) { + tickTouchMap(pd); + tickTouchQuickSelect(pd); +} diff --git a/source/IngameMenu.h b/source/IngameMenu.h new file mode 100644 index 0000000..d3e168f --- /dev/null +++ b/source/IngameMenu.h @@ -0,0 +1,8 @@ +#pragma once + +#include "Player.h" + +void ingameMenuTick(PlayerData *pd, int menu); +void ingameMenuRender(PlayerData *pd, int menu); + +void ingameMenuTickTouch(PlayerData *pd); diff --git a/source/Input.c b/source/Input.c index 662cb69..d35bfc0 100644 --- a/source/Input.c +++ b/source/Input.c @@ -5,19 +5,51 @@ void toggleKey(Key* key, bool held, bool down){ key->clicked = down; } -void tickKeys(u32 held, u32 down){ - hidTouchRead(&k_touch); // Update touch position - toggleKey(&k_up, held & k_up.input, down & k_up.input); - toggleKey(&k_down, held & k_down.input, down & k_down.input); - toggleKey(&k_left, held & k_left.input, down & k_left.input); - toggleKey(&k_right, held & k_right.input, down & k_right.input); - toggleKey(&k_pause, held & k_pause.input, down & k_pause.input); - toggleKey(&k_attack, held & k_attack.input, down & k_attack.input); - toggleKey(&k_menu, held & k_menu.input, down & k_menu.input); - toggleKey(&k_accept, held & k_accept.input, down & k_accept.input); - toggleKey(&k_decline, held & k_decline.input, down & k_decline.input); - toggleKey(&k_delete, held & k_delete.input, down & k_delete.input); - toggleKey(&k_menuNext, held & k_menuNext.input, down & k_menuNext.input); - toggleKey(&k_menuPrev, held & k_menuPrev.input, down & k_menuPrev.input); +void tickKeys(Inputs *inputs, u32 held, u32 down){ + hidTouchRead(&(inputs->k_touch)); // Update touch position + toggleKey(&(inputs->k_up), held & localInputs.k_up.input, down & localInputs.k_up.input); + toggleKey(&(inputs->k_down), held & localInputs.k_down.input, down & localInputs.k_down.input); + toggleKey(&(inputs->k_left), held & localInputs.k_left.input, down & localInputs.k_left.input); + toggleKey(&(inputs->k_right), held & localInputs.k_right.input, down & localInputs.k_right.input); + toggleKey(&(inputs->k_pause), held & localInputs.k_pause.input, down & localInputs.k_pause.input); + toggleKey(&(inputs->k_attack), held & localInputs.k_attack.input, down & localInputs.k_attack.input); + toggleKey(&(inputs->k_menu), held & localInputs.k_menu.input, down & localInputs.k_menu.input); + toggleKey(&(inputs->k_accept), held & localInputs.k_accept.input, down & localInputs.k_accept.input); + toggleKey(&(inputs->k_decline), held & localInputs.k_decline.input, down & localInputs.k_decline.input); + toggleKey(&(inputs->k_delete), held & localInputs.k_delete.input, down & localInputs.k_delete.input); + toggleKey(&(inputs->k_menuNext), held & localInputs.k_menuNext.input, down & localInputs.k_menuNext.input); + toggleKey(&(inputs->k_menuPrev), held & localInputs.k_menuPrev.input, down & localInputs.k_menuPrev.input); } +void resetKeys(Inputs *inputs) { + (inputs->k_touch).px = -1; + (inputs->k_touch).py = -1; + + toggleKey(&(inputs->k_up), false, false); + toggleKey(&(inputs->k_down), false, false); + toggleKey(&(inputs->k_left), false, false); + toggleKey(&(inputs->k_right), false, false); + toggleKey(&(inputs->k_pause), false, false); + toggleKey(&(inputs->k_attack), false, false); + toggleKey(&(inputs->k_menu), false, false); + toggleKey(&(inputs->k_accept), false, false); + toggleKey(&(inputs->k_decline), false, false); + toggleKey(&(inputs->k_delete), false, false); + toggleKey(&(inputs->k_menuNext), false, false); + toggleKey(&(inputs->k_menuPrev), false, false); +} + +void resetClicked(Inputs *inputs) { + inputs->k_up.clicked = false; + inputs->k_down.clicked = false; + inputs->k_left.clicked = false; + inputs->k_right.clicked = false; + inputs->k_pause.clicked = false; + inputs->k_attack.clicked = false; + inputs->k_menu.clicked = false; + inputs->k_accept.clicked = false; + inputs->k_decline.clicked = false; + inputs->k_delete.clicked = false; + inputs->k_menuNext.clicked = false; + inputs->k_menuPrev.clicked = false; +} diff --git a/source/Input.h b/source/Input.h index 761a315..ad11742 100644 --- a/source/Input.h +++ b/source/Input.h @@ -1,24 +1,33 @@ +#pragma once + #include <3ds.h> +//only down and clicked need to be send, input is for config stuff typedef struct { bool down, clicked; int input; } Key; -Key k_null; -Key k_up; -Key k_down; -Key k_left; -Key k_right; -Key k_attack; -Key k_menu; -Key k_pause; -Key k_accept; -Key k_decline; -Key k_delete; -Key k_menuNext; -Key k_menuPrev; -touchPosition k_touch; +typedef struct { + Key k_null; + Key k_up; + Key k_down; + Key k_left; + Key k_right; + Key k_attack; + Key k_menu; + Key k_pause; + Key k_accept; + Key k_decline; + Key k_delete; + Key k_menuNext; + Key k_menuPrev; + touchPosition k_touch; +} Inputs; -void tickKeys(u32 held, u32 down); +Inputs localInputs; + +void tickKeys(Inputs *inputs, u32 held, u32 down); +void resetKeys(Inputs *inputs); +void resetClicked(Inputs *inputs); bool clicked(Key key); diff --git a/source/Item.c b/source/Item.c index a1613ba..7287074 100644 --- a/source/Item.c +++ b/source/Item.c @@ -2,13 +2,13 @@ char currentName[16]; -bool isItemEmpty(Item* item){ +bool isItemEmpty(Item* item) { if(item->id < 6 || item->id > 100 || item->onlyOne == true) return false; if(item->countLevel < 1) return true; return false; } -void pushItemToInventoryFront(Item item, Inventory * inv){ +void pushItemToInventoryFront(Item item, Inventory * inv) { if(inv->lastSlot < 300) ++inv->lastSlot; int i; for(i = inv->lastSlot;i > 0;--i){ @@ -21,7 +21,7 @@ void pushItemToInventoryFront(Item item, Inventory * inv){ } -void addItemToInventory(Item item, Inventory * inv){ +void addItemToInventory(Item item, Inventory * inv) { if(!item.onlyOne){ int i; for(i = 0;i < inv->lastSlot;++i){ //Search inventory if item already exists. @@ -38,12 +38,12 @@ void addItemToInventory(Item item, Inventory * inv){ ++inv->lastSlot; } -void removeItemFromCurrentInv(Item* item){ +void removeItemFromCurrentInv(Item* item) { removeItemFromInventory(item->slotNum, (Inventory*)item->invPtr); } Item nullItem; -void removeItemFromInventory(int slot, Inventory * inv){ +void removeItemFromInventory(int slot, Inventory * inv) { int i; for(i = slot;i < inv->lastSlot - 1;++i){ inv->items[i] = inv->items[i + 1]; // Move the items down. @@ -53,7 +53,7 @@ void removeItemFromInventory(int slot, Inventory * inv){ inv->items[inv->lastSlot] = nullItem; // Make the last slot null. } -Item newItem(int id, int cLevel){ +Item newItem(int id, int cLevel) { Item item; item.id = id; if(id != ITEM_NULL){ @@ -66,7 +66,7 @@ Item newItem(int id, int cLevel){ return item; } -Item* getItemFromInventory(int itemID, Inventory * inv){ +Item* getItemFromInventory(int itemID, Inventory * inv) { int i; for(i = 0;i < inv->lastSlot;++i){ if(inv->items[i].id == itemID){ @@ -76,7 +76,7 @@ Item* getItemFromInventory(int itemID, Inventory * inv){ return (Item*)NULL; } -int countItemInv(int itemID, int level, Inventory* inv){ +int countItemInv(int itemID, int level, Inventory* inv) { int i, count = 0; for(i = 0;i < inv->lastSlot;++i){ if(inv->items[i].id == itemID){ @@ -88,7 +88,7 @@ int countItemInv(int itemID, int level, Inventory* inv){ return count; } -char* getItemName(int itemID, int countLevel){ +char* getItemName(int itemID, int countLevel) { switch(itemID){ case TOOL_SHOVEL: switch(countLevel){ @@ -198,7 +198,7 @@ char* getItemName(int itemID, int countLevel){ } } -char* getBasicItemName(int itemID, int countLevel){ +char* getBasicItemName(int itemID, int countLevel) { switch(itemID){ case TOOL_SHOVEL: switch(countLevel){ diff --git a/source/MapGen.c b/source/MapGen.c index aa8ce3f..e2e6bb9 100644 --- a/source/MapGen.c +++ b/source/MapGen.c @@ -15,56 +15,52 @@ double sample(double * values, int x, int y) { } double * Noise(int width, int height, int featureSize) { - w = width; - h = height; - double * values = malloc(sizeof(double) * w * h); - int x, y; - for(x = 0; x < w; x+=featureSize){ - for(y = 0; y < w; y+=featureSize){ - values[(x & (w - 1)) + (y & (h - 1)) * w] = nextFloat() * 2 - 1; - } - } + w = width; + h = height; + double * values = malloc(sizeof(double) * w * h); + int x, y; + for(x = 0; x < w; x+=featureSize){ + for(y = 0; y < w; y+=featureSize){ + values[(x & (w - 1)) + (y & (h - 1)) * w] = nextFloat() * 2 - 1; + } + } - int stepSize = featureSize; - double scale = 1.0 / w; - double scaleMod = 1; - do { - int halfStep = stepSize / 2; - for(x = 0; x < w; x+=stepSize){ - for(y = 0; y < w; y+=stepSize){ - double a = sample(values,x, y); - double b = sample(values,x + stepSize, y); - double c = sample(values,x, y + stepSize); - double d = sample(values,x + stepSize, y + stepSize); + int stepSize = featureSize; + double scale = 1.0 / w; + double scaleMod = 1; + do { + int halfStep = stepSize / 2; + for(x = 0; x < w; x+=stepSize){ + for(y = 0; y < w; y+=stepSize){ + double a = sample(values,x, y); + double b = sample(values,x + stepSize, y); + double c = sample(values,x, y + stepSize); + double d = sample(values,x + stepSize, y + stepSize); - double e = (a + b + c + d) / 4.0 + (nextFloat() * 2 - 1) * stepSize * scale; - values[((x + halfStep) & (w - 1)) + ((y + halfStep) & (h - 1)) * w] = e; - } + double e = (a + b + c + d) / 4.0 + (nextFloat() * 2 - 1) * stepSize * scale; + values[((x + halfStep) & (w - 1)) + ((y + halfStep) & (h - 1)) * w] = e; } - for(x = 0; x < w; x+=stepSize){ - for(y = 0; y < w; y+=stepSize){ - double a = sample(values,x, y); - double b = sample(values,x + stepSize, y); - double c = sample(values,x, y + stepSize); - double d = sample(values,x + halfStep, y + halfStep); - double e = sample(values,x + halfStep, y - halfStep); - double f = sample(values,x - halfStep, y + halfStep); - double H = (a + b + d + e) / 4.0 + (nextFloat() * 2 - 1) * stepSize * scale * 0.5; - double g = (a + c + d + f) / 4.0 + (nextFloat() * 2 - 1) * stepSize * scale * 0.5; - values[((x + halfStep) & (w - 1)) + (y & (h - 1)) * w] = H; - values[(x & (w - 1)) + ((y + halfStep) & (h - 1)) * w] = g; - } + } + for(x = 0; x < w; x+=stepSize){ + for(y = 0; y < w; y+=stepSize){ + double a = sample(values,x, y); + double b = sample(values,x + stepSize, y); + double c = sample(values,x, y + stepSize); + double d = sample(values,x + halfStep, y + halfStep); + double e = sample(values,x + halfStep, y - halfStep); + double f = sample(values,x - halfStep, y + halfStep); + double H = (a + b + d + e) / 4.0 + (nextFloat() * 2 - 1) * stepSize * scale * 0.5; + double g = (a + c + d + f) / 4.0 + (nextFloat() * 2 - 1) * stepSize * scale * 0.5; + values[((x + halfStep) & (w - 1)) + (y & (h - 1)) * w] = H; + values[(x & (w - 1)) + ((y + halfStep) & (h - 1)) * w] = g; } - - stepSize /= 2; - scale *= (scaleMod + 0.8); - scaleMod *= 0.3; - } while (stepSize > 1); - return values; - } - -void newSeed(){ - srand(time(NULL)); + } + + stepSize /= 2; + scale *= (scaleMod + 0.8); + scaleMod *= 0.3; + } while (stepSize > 1); + return values; } //TODO: Will need to reset entity manager if generation is retried @@ -331,8 +327,10 @@ void createUndergroundMap(int w, int h, int depthLevel, int level, u8 * map, u8 } } + //generate dwarf house if(depthLevel==3) { createDwarfHouse(w, h, level, map, data); + //generate mushroom patches } else if(depthLevel==2) { for (i = 0; i < w * h / 5400; ++i) { int xs = rand()%w; @@ -361,6 +359,7 @@ void createUndergroundMap(int w, int h, int depthLevel, int level, u8 * map, u8 } } + //generate ores for (i = 0; i < w * h / 400; ++i) { int x = rand()%w; int y = rand()%h; @@ -374,6 +373,8 @@ void createUndergroundMap(int w, int h, int depthLevel, int level, u8 * map, u8 } } } + + //generate stairs down if (depthLevel < 3){ int sCount, attempts = 0; for (sCount = 0; sCount < 4;) { @@ -396,6 +397,22 @@ void createUndergroundMap(int w, int h, int depthLevel, int level, u8 * map, u8 if(attempts < (w*h/100)) ++attempts; else break; } } + + //generate dungeon entrance + if(depthLevel==3) { + map[w/2+0 + (h/2+0) * w] = TILE_DUNGEON_ENTRANCE; + + map[w/2-1 + (h/2+0) * w] = TILE_DIRT; + map[w/2+1 + (h/2+0) * w] = TILE_DIRT; + map[w/2+0 + (h/2-1) * w] = TILE_DIRT; + map[w/2+1 + (h/2+1) * w] = TILE_DIRT; + + map[w/2-1 + (h/2-1) * w] = TILE_DUNGEON_WALL; + map[w/2-1 + (h/2+1) * w] = TILE_DUNGEON_WALL; + map[w/2+1 + (h/2-1) * w] = TILE_DUNGEON_WALL; + map[w/2+1 + (h/2+1) * w] = TILE_DUNGEON_WALL; + } + free(mnoise1); free(mnoise2); free(mnoise3); diff --git a/source/MapGen.h b/source/MapGen.h index cf98530..22fe6e3 100644 --- a/source/MapGen.h +++ b/source/MapGen.h @@ -2,7 +2,6 @@ #include #include #include -#include #include <3ds.h> #include "Globals.h" @@ -10,7 +9,6 @@ float nextFloat(); double sample(double * values, int x, int y); double * Noise(int w, int h, int featureSize); -void newSeed(); void createAndValidateTopMap(int w, int h, int level, u8 * map, u8 * data); void createTopMap(int w, int h, int level, u8 * map, u8 * data); void createAndValidateUndergroundMap(int w, int h, int depthLevel, int level, u8 * map, u8 * data); diff --git a/source/Menu.c b/source/Menu.c index 17ed172..8a4e4ab 100644 --- a/source/Menu.c +++ b/source/Menu.c @@ -1,7 +1,9 @@ #include "Menu.h" -char options[][12] = {"Start Game", "Join Game", "How To Play","Settings", "About", "Exit"}; -char pOptions[][24] = {"Return to game", "Save Progress", "Host World", "Exit to title"}; +#include "PacketHandler.h" +#include "SaveLoad.h" + +char options[][12] = {"Start Game", "Host Game", "Join Game", "How To Play", "Settings", "About", "Exit"}; char keybOptions[][24] = {"Exit and Save", "Exit and Don't save","Reset to default"}; char setOptions[][24] = {"Rebind Buttons", "Texture packs", "Debug Text: ", "N3DS Speedup: ", "Return to title"}; @@ -22,6 +24,7 @@ bool left = false; bool selBut = false; s8 errorBut = -1; s8 curSaveSel = 0; +int menuScanTimer = 0; // Load Game Menu (Start Game) char fileNames[1000][256]; @@ -33,6 +36,7 @@ s8 touchDelay = 0; bool isTouching = false; int touchX = 0, touchY = 0, touchW = 0, touchH = 0; s8 errorFileName = 0; +bool toMultiplayer; // Load Texturepacks Menu char tpFileNames[1000][256]; @@ -40,8 +44,6 @@ char tpFileComment[1000][60]; s16 tpFileCount = 0; s8 isLoadingTP = 0; -s16 pauseSaveDisplayTimer = 0; - void readFiles(){ memset(&fileNames, 0, sizeof(fileNames)); // reset fileNames worldFileCount = 0; @@ -50,12 +52,16 @@ void readFiles(){ d = opendir("."); if (d){ while ((dir = readdir(d)) != NULL) { - if (strstr(dir->d_name, ".wld") != NULL) { // Check if filename contains ".wld" + if (strncmp(dir->d_name+strlen(dir->d_name)-4, ".msv", 4) == 0) { // Check if filename contains ".msv" strncpy(fileNames[worldFileCount], dir->d_name, strlen(dir->d_name)-4); - FILE * file = fopen(dir->d_name, "rb"); - fread(&fileScore[worldFileCount],sizeof(int), 1, file); - fread(&fileWin[worldFileCount],sizeof(bool), 1, file); - fclose(file); + //TODO: This no longer works, update for new format: + //FILE * file = fopen(dir->d_name, "rb"); + //fread(&fileScore[worldFileCount],sizeof(int), 1, file); + //fread(&fileWin[worldFileCount],sizeof(bool), 1, file); + //fclose(file); + fileScore[worldFileCount] = 0; + fileWin[worldFileCount] = false; + ++worldFileCount; } } @@ -105,7 +111,7 @@ s8 checkFileNameForErrors(){ d = opendir("."); if (d){ while ((dir = readdir(d)) != NULL) { - if (strstr(dir->d_name, ".wld") != NULL) { // Check if filename contains ".wld" + if (strstr(dir->d_name, ".msv") != NULL) { // Check if filename contains ".msv" char cmprFile[256]; strncpy(cmprFile, dir->d_name, strlen(dir->d_name)-4); if(strncmp(fileNames[worldFileCount],cmprFile,strlen(fileNames[worldFileCount])) == 0) return 3; // Error: Filename cannot already exist. @@ -123,7 +129,7 @@ void addToFileName(char * c){ /* Keypad */ void doTouchButton(){ - int xVal = k_touch.px, yVal = k_touch.py; + int xVal = localInputs.k_touch.px, yVal = localInputs.k_touch.py; int strLength = strlen(fileNames[worldFileCount]); if(yVal >= 60 && yVal < 80){ // 0 to 9 if(xVal >= 4 && xVal < 4+16){ touchX = 4; if(strLength < 24)addToFileName("1");} @@ -281,7 +287,7 @@ void initMenus() { if(worldFileCount>0) { memset(¤tFileName, 0, 255); // reset currentFileName - sprintf(currentFileName,"%s.wld",fileNames[currentSelection]); + sprintf(currentFileName,"%s.msv",fileNames[currentSelection]); initBGMap = 1; } else { @@ -289,8 +295,8 @@ void initMenus() { } menuHasMapLoaded = true; - menuxa = (rand()%3 - 1) * 0.25; - menuya = (rand()%3 - 1) * 0.25; + while(menuxa==0) menuxa = (rand()%3 - 1) * 0.25; + while(menuya==0) menuya = (rand()%3 - 1) * 0.25; } Item median; @@ -299,48 +305,48 @@ void tickMenu(int menu){ case MENU_SETTINGS_REBIND: if(!bindOpt){ if(!selBut){ - if (k_up.clicked){ --currentSelection; if(currentSelection < 0)currentSelection=21;} - if (k_down.clicked){ ++currentSelection; if(currentSelection > 21)currentSelection=0;} - if (k_left.clicked){ left = true;} - if (k_right.clicked){ left = false;} + if (localInputs.k_up.clicked){ --currentSelection; if(currentSelection < 0)currentSelection=21;} + if (localInputs.k_down.clicked){ ++currentSelection; if(currentSelection > 21)currentSelection=0;} + if (localInputs.k_left.clicked){ left = true;} + if (localInputs.k_right.clicked){ left = false;} } else { - if (k_left.clicked){ + if (localInputs.k_left.clicked){ if(left)switchGameBut(true,keys[currentSelection]); else switchMenuBut(true,keys[currentSelection]); - } else if (k_right.clicked) { + } else if (localInputs.k_right.clicked) { if(left)switchGameBut(false,keys[currentSelection]); else switchMenuBut(false,keys[currentSelection]); } } - if (k_accept.clicked) selBut = !selBut; - if (k_decline.clicked){ + if (localInputs.k_accept.clicked) selBut = !selBut; + if (localInputs.k_decline.clicked){ bindOpt = true; curSaveSel = 0; } } else { - if (k_up.clicked){ --curSaveSel; if(curSaveSel < 0)curSaveSel=2;} - if (k_down.clicked){ ++curSaveSel; if(curSaveSel > 2)curSaveSel=0;} - if (k_decline.clicked){ + if (localInputs.k_up.clicked){ --curSaveSel; if(curSaveSel < 0)curSaveSel=2;} + if (localInputs.k_down.clicked){ ++curSaveSel; if(curSaveSel > 2)curSaveSel=0;} + if (localInputs.k_decline.clicked){ bindOpt = false; errorBut = -1; } - if (k_accept.clicked){ + if (localInputs.k_accept.clicked){ switch(curSaveSel){ case 0: // Exit and save if(checkPropButtons() == -1){ - k_up.input = keyProp[0]; - k_down.input = keyProp[1]; - k_left.input = keyProp[2]; - k_right.input = keyProp[3]; - k_attack.input = keyProp[4]; - k_menu.input = keyProp[5]; - k_pause.input = keyProp[6]; - k_accept.input = keyProp[7]; - k_decline.input = keyProp[8]; - k_delete.input = keyProp[9]; - k_menuNext.input = keyProp[10]; - k_menuPrev.input = keyProp[11]; + localInputs.k_up.input = keyProp[0]; + localInputs.k_down.input = keyProp[1]; + localInputs.k_left.input = keyProp[2]; + localInputs.k_right.input = keyProp[3]; + localInputs.k_attack.input = keyProp[4]; + localInputs.k_menu.input = keyProp[5]; + localInputs.k_pause.input = keyProp[6]; + localInputs.k_accept.input = keyProp[7]; + localInputs.k_decline.input = keyProp[8]; + localInputs.k_delete.input = keyProp[9]; + localInputs.k_menuNext.input = keyProp[10]; + localInputs.k_menuPrev.input = keyProp[11]; FILE *fs=fopen("btnSave.bin","wb"); fwrite(keyProp,sizeof(int),12,fs); @@ -376,246 +382,90 @@ void tickMenu(int menu){ } } break; - case MENU_PAUSED: - if(!areYouSure && !areYouSureSave){ - if(pauseSaveDisplayTimer > 0) --pauseSaveDisplayTimer; - if (k_pause.clicked || k_decline.clicked) currentMenu = MENU_NONE; - if (k_up.clicked){ --currentSelection; if(currentSelection < 0)currentSelection=3;} - if (k_down.clicked){ ++currentSelection; if(currentSelection > 3)currentSelection=0;} - if (k_accept.clicked){ - switch(currentSelection){ - case 0: - currentMenu = MENU_NONE; - break; - case 1: - if(currentLevel!=5) areYouSureSave = true; - break; - case 2: - networkHost(); - currentMenu = MENU_NONE; - break; - case 3: - areYouSure = true; - break; - } - } - } else if(areYouSureSave) { - if (k_accept.clicked){ - pauseSaveDisplayTimer = 60; - saveCurrentWorld(currentFileName, &eManager, &player, (u8*)map, (u8*)data); - areYouSureSave = false; - areYouSure = false; - } else if (k_decline.clicked){ - areYouSureSave = false; - areYouSure = false; - } - } else { - if (k_accept.clicked){ - areYouSure = false; - areYouSureSave = false; - sf2d_set_clear_color(0xFF); - currentSelection = 0; - currentMenu = MENU_TITLE; - - networkDisconnect(); - - playMusic(music_menu); - } else if (k_decline.clicked){ - areYouSure = false; - areYouSureSave = false; - } - } - break; - case MENU_INVENTORY: - if (k_menu.clicked || k_decline.clicked){ - currentMenu = MENU_NONE; - player.p.activeItem = &noItem; - player.p.isCarrying = false; - } - if (k_accept.clicked){ // Select item from inventory - if(player.p.inv->lastSlot!=0){ - median = player.p.inv->items[curInvSel]; // create copy of item. - removeItemFromInventory(curInvSel, player.p.inv); // remove original - pushItemToInventoryFront(median, player.p.inv); // add copy to front - playerSetActiveItem(&player.p.inv->items[0]); // active item = copy. - } - currentMenu = MENU_NONE; - } - if (k_up.clicked){ --curInvSel; if(curInvSel < 0)curInvSel=player.p.inv->lastSlot-1;} - if (k_down.clicked){ ++curInvSel; if(curInvSel > player.p.inv->lastSlot-1)curInvSel=0;} - break; - case MENU_CRAFTING: - if (k_menu.clicked || k_decline.clicked) currentMenu = MENU_NONE; - if (k_accept.clicked){ - int newslot = player.p.activeItem->slotNum + 1; - if(craftItem(currentRecipes, ¤tRecipes->recipes[curInvSel], player.p.inv)){ - playSound(snd_craft); - if(player.p.activeItem != &noItem)player.p.activeItem = &player.p.inv->items[newslot]; - } - } - if (k_up.clicked){ --curInvSel; if(curInvSel < 0)curInvSel=currentRecipes->size-1;} - if (k_down.clicked){ ++curInvSel; if(curInvSel > currentRecipes->size-1)curInvSel=0;} - break; - - case MENU_WIN: - if (k_accept.clicked){ - sf2d_set_clear_color(0xFF); - currentSelection = 0; - currentMenu = MENU_TITLE; - saveCurrentWorld(currentFileName, &eManager, &player, (u8*)map, (u8*)data); - - playMusic(music_menu); - } - break; - case MENU_LOSE: - if (k_accept.clicked){ - sf2d_set_clear_color(0xFF); - currentSelection = 0; - currentMenu = MENU_TITLE; - - playMusic(music_menu); - } - break; case MENU_ABOUT: - if (k_decline.clicked) currentMenu = MENU_TITLE; + if (localInputs.k_decline.clicked) currentMenu = MENU_TITLE; break; - case MENU_CONTAINER: - if (k_menu.clicked || k_decline.clicked) currentMenu = MENU_NONE; - - if (k_left.clicked) { - curChestEntity->entityFurniture.r = 0; - int tmp = curInvSel; - curInvSel = curChestEntity->entityFurniture.oSel; - curChestEntity->entityFurniture.oSel = tmp; - } - if (k_right.clicked) { - curChestEntity->entityFurniture.r = 1; - int tmp = curInvSel; - curInvSel = curChestEntity->entityFurniture.oSel; - curChestEntity->entityFurniture.oSel = tmp; - } - - Inventory* i1 = curChestEntity->entityFurniture.r == 1 ? player.p.inv : curChestEntity->entityFurniture.inv; - Inventory* i2 = curChestEntity->entityFurniture.r == 0 ? player.p.inv : curChestEntity->entityFurniture.inv; - int len = i1->lastSlot; - if (curInvSel < 0) curInvSel = 0; - if (curInvSel >= len) curInvSel = len - 1; - if (k_up.clicked) --curInvSel; - if (k_down.clicked) ++curInvSel; - if (len == 0) curInvSel = 0; - if (curInvSel < 0) curInvSel += len; - if (curInvSel >= len) curInvSel -= len; - - if(k_accept.clicked && len > 0){ - Item* pullItem = &i1->items[curInvSel]; - Item pushItem = newItem(pullItem->id,pullItem->countLevel); - pushItem.chestPtr = pullItem->chestPtr; - pushItemToInventoryFront(pushItem, i2); - if(i2 == player.p.inv){ - int newslot = player.p.activeItem->slotNum + 1; - player.p.activeItem = &player.p.inv->items[newslot]; - } else if(pullItem == player.p.activeItem){ - player.p.activeItem = &noItem; - } - removeItemFromCurrentInv(pullItem); - if (curInvSel >= i1->lastSlot) curInvSel = i1->lastSlot - 1; - } - break; - - case MENU_DUNGEON: - if (k_menu.clicked || k_decline.clicked) currentMenu = MENU_NONE; - - if(k_accept.clicked) { - if(currentLevel!=5) { - Item * item = getItemFromInventory(ITEM_DUNGEON_KEY, player.p.inv); - if(item!=NULL) { - --item->countLevel; - if(item->countLevel==0) { - removeItemFromCurrentInv(item); - } - - enterDungeon(); - } else if(TESTGODMODE) { - enterDungeon(); - } - } else { - leaveDungeon(); - } - - currentMenu = MENU_NONE; - } - break; - case MENU_LOADGAME: if(!enteringName && !areYouSure){ // World select - if (k_decline.clicked){ - currentMenu = MENU_TITLE; - currentSelection = 0; + if (localInputs.k_decline.clicked){ + if(toMultiplayer) { + currentMenu = MENU_MULTIPLAYER_HOST; + } else { + currentMenu = MENU_TITLE; + currentSelection = 0; + } } - if (k_up.clicked){ --currentSelection; if(currentSelection < 0)currentSelection = worldFileCount;} - if (k_down.clicked){ ++currentSelection; if(currentSelection > worldFileCount)currentSelection=0;} + if (localInputs.k_up.clicked){ --currentSelection; if(currentSelection < 0)currentSelection = worldFileCount;} + if (localInputs.k_down.clicked){ ++currentSelection; if(currentSelection > worldFileCount)currentSelection=0;} - if(k_delete.clicked){ + if(localInputs.k_delete.clicked){ if(currentSelection < worldFileCount) areYouSure = true; } - if(k_accept.clicked){ + if(localInputs.k_accept.clicked){ if(currentSelection == worldFileCount){ enteringName = true; } else { memset(¤tFileName, 0, 255); // reset currentFileName - sprintf(currentFileName,"%s.wld",fileNames[currentSelection]); + sprintf(currentFileName, "%s.msv", fileNames[currentSelection]); playSound(snd_test); - initGame = 1; - currentMenu = MENU_NONE; - isRemote = false; + if(toMultiplayer) { + initMPGame = 2; + } else { + initGame = 1; + } + currentMenu = MENU_LOADING; } } } else if (areYouSure){ - if (k_decline.clicked || k_delete.clicked) areYouSure = false; - if (k_accept.clicked){ - sprintf(currentFileName,"%s.wld",fileNames[currentSelection]); + if (localInputs.k_decline.clicked || localInputs.k_delete.clicked) areYouSure = false; + if (localInputs.k_accept.clicked){ + sprintf(currentFileName,"%s.msv",fileNames[currentSelection]); remove(currentFileName); readFiles(); enteringName = false; areYouSure = false; memset(¤tFileName, 0, 255); // reset currentFileName } - }else { // Enter new world name. - if(k_decline.clicked) enteringName = false; - if(k_accept.clicked && errorFileName == 0){ + } else { // Enter new world name. + if(localInputs.k_decline.clicked) enteringName = false; + if(localInputs.k_accept.clicked && errorFileName == 0){ errorFileName = checkFileNameForErrors(); if(errorFileName == 0){ // If no errors are found with the filename, then start a new game! memset(¤tFileName, 0, 255); // reset currentFileName - sprintf(currentFileName,"%s.wld",fileNames[worldFileCount]); - currentMenu = MENU_NONE; + sprintf(currentFileName, "%s.msv", fileNames[worldFileCount]); + currentMenu = MENU_LOADING; playSound(snd_test); - initGame = 2; + if(toMultiplayer) { + initMPGame = 2; + } else { + initGame = 1; + } ++worldFileCount; - isRemote = false; + currentMenu = MENU_LOADING; } } - if((k_touch.px != 0 || k_touch.py != 0) && touchDelay == 0){ + if((localInputs.k_touch.px != 0 || localInputs.k_touch.py != 0) && touchDelay == 0){ if(!isTouching)doTouchButton(); } - else if(k_touch.px == 0 || k_touch.py == 0) isTouching = false; + else if(localInputs.k_touch.px == 0 || localInputs.k_touch.py == 0) isTouching = false; if(touchDelay > 0) --touchDelay; } break; case MENU_SETTINGS_TP: - if (k_up.clicked){ --currentSelection; if(currentSelection < 0)currentSelection = tpFileCount-1;} - if (k_down.clicked){ ++currentSelection; if(currentSelection > tpFileCount-1)currentSelection=0;} - if (k_decline.clicked){ + if (localInputs.k_up.clicked){ --currentSelection; if(currentSelection < 0)currentSelection = tpFileCount-1;} + if (localInputs.k_down.clicked){ ++currentSelection; if(currentSelection > tpFileCount-1)currentSelection=0;} + if (localInputs.k_decline.clicked){ if(isLoadingTP < 1){ currentMenu = MENU_SETTINGS; currentSelection = 1; } } - if (k_accept.clicked){ + if (localInputs.k_accept.clicked){ if(currentSelection > 0){ isLoadingTP = 4; @@ -632,35 +482,35 @@ void tickMenu(int menu){ break; case MENU_SETTINGS: - if (k_up.clicked){ + if (localInputs.k_up.clicked){ --currentSelection; if(currentSelection == 3 && !((MODEL_3DS & 6) != 0)) --currentSelection; if(currentSelection < 0)currentSelection=4; } - if (k_down.clicked){ + if (localInputs.k_down.clicked){ ++currentSelection; if(currentSelection == 3 && !((MODEL_3DS & 6) != 0)) ++currentSelection; if(currentSelection > 4)currentSelection=0; } - if(k_decline.clicked){ + if(localInputs.k_decline.clicked){ currentMenu = MENU_TITLE; - currentSelection = 3; + currentSelection = 4; } - if(k_accept.clicked){ + if(localInputs.k_accept.clicked){ switch(currentSelection){ case 0: - keyProp[0] = k_up.input; - keyProp[1] = k_down.input; - keyProp[2] = k_left.input; - keyProp[3] = k_right.input; - keyProp[4] = k_attack.input; - keyProp[5] = k_menu.input; - keyProp[6] = k_pause.input; - keyProp[7] = k_accept.input; - keyProp[8] = k_decline.input; - keyProp[9] = k_delete.input; - keyProp[10] = k_menuNext.input; - keyProp[11] = k_menuPrev.input; + keyProp[0] = localInputs.k_up.input; + keyProp[1] = localInputs.k_down.input; + keyProp[2] = localInputs.k_left.input; + keyProp[3] = localInputs.k_right.input; + keyProp[4] = localInputs.k_attack.input; + keyProp[5] = localInputs.k_menu.input; + keyProp[6] = localInputs.k_pause.input; + keyProp[7] = localInputs.k_accept.input; + keyProp[8] = localInputs.k_decline.input; + keyProp[9] = localInputs.k_delete.input; + keyProp[10] = localInputs.k_menuNext.input; + keyProp[11] = localInputs.k_menuPrev.input; left = true; selBut = false; bindOpt = false; @@ -716,12 +566,13 @@ void tickMenu(int menu){ } } - if (k_up.clicked){ --currentSelection; if(currentSelection < 0)currentSelection=5;} - if (k_down.clicked){ ++currentSelection; if(currentSelection > 5)currentSelection=0;} + if (localInputs.k_up.clicked){ --currentSelection; if(currentSelection < 0)currentSelection=6;} + if (localInputs.k_down.clicked){ ++currentSelection; if(currentSelection > 6)currentSelection=0;} - if(k_accept.clicked){ + if(localInputs.k_accept.clicked){ switch(currentSelection){ case 0: + toMultiplayer = false; currentMenu = MENU_LOADGAME; readFiles(); currentSelection = 0; @@ -729,21 +580,29 @@ void tickMenu(int menu){ areYouSure = false; break; case 1: - currentMenu = MENU_MULTIPLAYER; - currentSelection = 0; + if(networkHost()) { + toMultiplayer = true; + currentMenu = MENU_MULTIPLAYER_HOST; + currentSelection = 0; + } break; case 2: + currentMenu = MENU_MULTIPLAYER_JOIN; + currentSelection = 0; + menuScanTimer = 0; + break; + case 3: sprintf(pageText,"Page: %d/%d",pageNum+1,maxPageNum+1); currentMenu = MENU_TUTORIAL; break; - case 3: + case 4: currentSelection = 0; currentMenu = MENU_SETTINGS; break; - case 4: + case 5: currentMenu = MENU_ABOUT; break; - case 5: + case 6: quitGame = true; break; } @@ -751,17 +610,17 @@ void tickMenu(int menu){ } break; case MENU_TUTORIAL: - if(k_decline.clicked){ - currentSelection = 2; + if(localInputs.k_decline.clicked){ + currentSelection = 3; currentMenu = MENU_TITLE; } - if(k_menuNext.clicked){ + if(localInputs.k_menuNext.clicked){ if(pageNum < maxPageNum){ ++pageNum; sprintf(pageText,"Page: %d/%d",pageNum+1,maxPageNum+1); } } - if(k_menuPrev.clicked){ + if(localInputs.k_menuPrev.clicked){ if(pageNum > 0){ --pageNum; sprintf(pageText,"Page: %d/%d",pageNum+1,maxPageNum+1); @@ -769,34 +628,65 @@ void tickMenu(int menu){ } break; - case MENU_NPC: - tickNPCMenu(); - break; - case MENU_MULTIPLAYER: - networkScan(); - - if (k_decline.clicked){ + case MENU_MULTIPLAYER_HOST: + if (localInputs.k_decline.clicked){ + networkDisconnect(); + currentMenu = MENU_TITLE; currentSelection = 1; } - if (k_up.clicked){ --currentSelection; if(currentSelection < 0) currentSelection = (networkGetScanCount()>0 ? networkGetScanCount()-1 : 0);} - if (k_down.clicked){ ++currentSelection; if(currentSelection >= networkGetScanCount()) currentSelection=0;} - - if(k_accept.clicked){ - //TODO: Join World - if(networkConnect(currentSelection)) { - initGame = 1; - currentMenu = MENU_NONE; - isRemote = true; + + if(localInputs.k_accept.clicked){ + if(networkGetNodeCount()>1) { + currentMenu = MENU_LOADGAME; + readFiles(); + currentSelection = 0; + enteringName = false; + areYouSure = false; } } break; + case MENU_MULTIPLAYER_JOIN: + if(menuScanTimer>0) { + menuScanTimer--; + } else { + networkScan(); + menuScanTimer = 30; + } + if(currentSelection >= networkGetScanCount()) currentSelection=networkGetScanCount()-1; + if(currentSelection < 0) currentSelection = 0; + + if (localInputs.k_decline.clicked){ + currentMenu = MENU_TITLE; + currentSelection = 2; + } + if (localInputs.k_up.clicked){ --currentSelection; if(currentSelection < 0) currentSelection = (networkGetScanCount()>0 ? networkGetScanCount()-1 : 0);} + if (localInputs.k_down.clicked){ ++currentSelection; if(currentSelection >= networkGetScanCount()) currentSelection=0;} + + if(localInputs.k_accept.clicked){ + if(networkGetScanCount()!=0) { + for(int t=0; t<10; t++) { //try to connect multiple times, because it will not work the first try every time + if(networkConnect(currentSelection)) { + currentMenu = MENU_MULTIPLAYER_WAIT; + currentSelection = 0; + break; + } + } + } + } + break; + case MENU_MULTIPLAYER_WAIT: + if (localInputs.k_decline.clicked){ + networkDisconnect(); + + currentMenu = MENU_TITLE; + currentSelection = 2; + } + break; } } -u8 opacity = 255; -bool rev = true; char scoreText[15]; char * getButtonFunctionGame(int key){ @@ -898,9 +788,9 @@ void renderMenu(int menu,int xscr,int yscr){ sf2d_draw_rectangle(0, 0, 320, 240, 0xFF0C0C0C); //You might think "real" black would be better, but it actually looks better that way drawText("Press to select", 58, 100); - renderButtonIcon(k_accept.input & -k_accept.input, 128, 98, 1); + renderButtonIcon(localInputs.k_accept.input & -localInputs.k_accept.input, 128, 98, 1); drawText("Press to return", 58, 150); - renderButtonIcon(k_decline.input & -k_decline.input, 128, 148, 1); + renderButtonIcon(localInputs.k_decline.input & -localInputs.k_decline.input, 128, 148, 1); sf2d_end_frame(); break; case MENU_LOADGAME: @@ -955,22 +845,22 @@ void renderMenu(int menu,int xscr,int yscr){ if(!areYouSure){ drawTextColor("Load World",100,12,0xFF3FFFFF); drawText("Press or to scroll", 28, 50); - renderButtonIcon(k_up.input & -k_up.input, 98, 48, 1); - renderButtonIcon(k_down.input & -k_down.input, 160, 48, 1); + renderButtonIcon(localInputs.k_up.input & -localInputs.k_up.input, 98, 48, 1); + renderButtonIcon(localInputs.k_down.input & -localInputs.k_down.input, 160, 48, 1); drawText("Press to load world", (320-21*12)/2, 100); - renderButtonIcon(k_accept.input & -k_accept.input, 104, 98, 1); + renderButtonIcon(localInputs.k_accept.input & -localInputs.k_accept.input, 104, 98, 1); drawText("Press to return", 58, 150); - renderButtonIcon(k_decline.input & -k_decline.input, 128, 148, 1); + renderButtonIcon(localInputs.k_decline.input & -localInputs.k_decline.input, 128, 148, 1); if(currentSelection != worldFileCount){ drawText("Press to delete",(320-17*12)/2, 200); - renderButtonIcon(k_delete.input & -k_delete.input, 128, 198, 1); + renderButtonIcon(localInputs.k_delete.input & -localInputs.k_delete.input, 128, 198, 1); } } else { drawTextColor("Delete File?",88,12,0xFF3F3FFF); drawText("Press to confirm", (320-18*12)/2, 100); - renderButtonIcon(k_accept.input & -k_accept.input, 122, 98, 1); + renderButtonIcon(localInputs.k_accept.input & -localInputs.k_accept.input, 122, 98, 1); drawText("Press to return", 58, 150); - renderButtonIcon(k_decline.input & -k_decline.input, 128, 148, 1); + renderButtonIcon(localInputs.k_decline.input & -localInputs.k_decline.input, 128, 148, 1); } } else { // Draw the "keyboard" @@ -989,9 +879,9 @@ void renderMenu(int menu,int xscr,int yscr){ drawSizedText(guiText4,12, 140, 2); drawText("Press to confirm", (320-18*12)/2, 180); - renderButtonIcon(k_accept.input & -k_accept.input, 122, 178, 1); + renderButtonIcon(localInputs.k_accept.input & -localInputs.k_accept.input, 122, 178, 1); drawText("Press to return", 58, 210); - renderButtonIcon(k_decline.input & -k_decline.input, 128, 208, 1); + renderButtonIcon(localInputs.k_decline.input & -localInputs.k_decline.input, 128, 208, 1); } sf2d_end_frame(); break; @@ -1041,7 +931,7 @@ void renderMenu(int menu,int xscr,int yscr){ drawTextColor(msg,(400 - (strlen(msg) * 12))/2, (i * 24) + 92, color); } drawText("Press to return", 98, 190); - renderButtonIcon(k_decline.input & -k_decline.input, 168, 188, 1); + renderButtonIcon(localInputs.k_decline.input & -localInputs.k_decline.input, 168, 188, 1); if(errorBut >= 0 && errorBut < 12){ char errorText[30]; @@ -1069,224 +959,20 @@ void renderMenu(int menu,int xscr,int yscr){ if(!selBut){ drawText("Press to select", 58, 80); - renderButtonIcon(k_accept.input & -k_accept.input, 128, 78, 1); + renderButtonIcon(localInputs.k_accept.input & -localInputs.k_accept.input, 128, 78, 1); drawText("Press to return", 58, 130); - renderButtonIcon(k_decline.input & -k_decline.input, 128, 128, 1); + renderButtonIcon(localInputs.k_decline.input & -localInputs.k_decline.input, 128, 128, 1); } else { drawText("Press or to scroll", 28, 50); - renderButtonIcon(k_left.input & -k_left.input, 98, 48, 1); - renderButtonIcon(k_right.input & -k_right.input, 160, 48, 1); + renderButtonIcon(localInputs.k_left.input & -localInputs.k_left.input, 98, 48, 1); + renderButtonIcon(localInputs.k_right.input & -localInputs.k_right.input, 160, 48, 1); drawText("Press to unselect", 46, 100); - renderButtonIcon(k_accept.input & -k_accept.input, 118, 98, 1); + renderButtonIcon(localInputs.k_accept.input & -localInputs.k_accept.input, 118, 98, 1); drawText("Press to return", 58, 150); - renderButtonIcon(k_decline.input & -k_decline.input, 128, 148, 1); + renderButtonIcon(localInputs.k_decline.input & -localInputs.k_decline.input, 128, 148, 1); } sf2d_end_frame(); break; - - case MENU_PAUSED: - sf2d_start_frame(GFX_TOP, GFX_LEFT); - if(currentLevel == 0){ - sf2d_draw_texture_part_scale(minimap[1],(-xscr/3)-256,(-yscr/3)-32,0,0,128,128,12.5,7.5); - sf2d_draw_rectangle(0,0,400,240, 0xAFDFDFDF); - } - offsetX = xscr;offsetY = yscr; - renderMenuBackground(xscr,yscr); - offsetX = 0;offsetY = 0; - renderFrame(1,1,24,14,0xFF1010AF); - drawText("Paused",164,32); - for(i = 3; i >= 0; --i){ - char* msg = pOptions[i]; - u32 color = 0xFF7F7F7F; - if(i == currentSelection) color = 0xFFFFFFFF; - if(i == 1 && currentLevel==5) { - color = 0xFF7F7FFF; - if(i == currentSelection) color = 0xFFAFAFFF; - } - drawTextColor(msg,(400 - (strlen(msg) * 12))/2, (i * 24) + 88, color); - } - - if(pauseSaveDisplayTimer > 0) drawTextColor("Game Saved!", (400-(11*12))/2, 64,0xFF20FF20); - - if(areYouSure || areYouSureSave){ - if(areYouSure)renderFrame(6,5,19,10,0xFF10108F); - else renderFrame(6,5,19,10,0xFF108F10); - - drawText("Are you sure?",122,96); - drawText(" Yes", 164, 117); - renderButtonIcon(k_accept.input & -k_accept.input, 166, 114, 1); - drawText(" No", 170, 133); - renderButtonIcon(k_decline.input & -k_decline.input, 166, 130, 1); - } - - sf2d_end_frame(); - break; - case MENU_WIN: - sf2d_start_frame(GFX_TOP, GFX_LEFT); - if(currentLevel == 0){ - sf2d_draw_texture_part_scale(minimap[1],(-xscr/3)-256,(-yscr/3)-32,0,0,128,128,12.5,7.5); - sf2d_draw_rectangle(0,0,400,240, 0xAFDFDFDF); - } - offsetX = xscr;offsetY = yscr; - renderMenuBackground(xscr,yscr); - offsetX = 0;offsetY = 0; - renderFrame(5,3,21,12,0xFFFF1010); - if(!rev){ opacity+=5; if(opacity == 255) rev = true; } - else { opacity-=5; if(opacity == 100) rev = false; } - sprintf(scoreText,"Score: %d", player.p.score); - drawTextColor("You Win!",158,76,0x0000AFAF + (opacity << 24)); - drawText(scoreText, 200-((strlen(scoreText)-1)*6), 100); - drawText("Press to continue", 96, 150); - renderButtonIcon(k_attack.input & -k_attack.input, 166, 148, 1); - - //printf("0x%08X",k_attack.input & -k_attack.input); - sf2d_end_frame(); - break; - case MENU_LOSE: - sf2d_start_frame(GFX_TOP, GFX_LEFT); - if(currentLevel == 0){ - sf2d_draw_texture_part_scale(minimap[1],(-xscr/3)-256,(-yscr/3)-32,0,0,128,128,12.5,7.5); - sf2d_draw_rectangle(0,0,400,240, 0xAFDFDFDF); - } - offsetX = xscr;offsetY = yscr; - renderMenuBackground(xscr,yscr); - offsetX = 0;offsetY = 0; - renderFrame(5,3,21,12,0xFFFF1010); - if(!rev){ opacity+=5; if(opacity == 255) rev = true; } - else { opacity-=5; if(opacity == 100) rev = false; } - sprintf(scoreText,"Score: %d", player.p.score); - drawTextColor("You DIED!",158,76,0x000000AF + (opacity << 24)); - drawText(scoreText, 200-((strlen(scoreText)-1)*6), 100); - drawText("Press to continue", 96, 150); - renderButtonIcon(k_attack.input & -k_attack.input, 166, 148, 1); - //printf("0x%08X",k_attack.input & -k_attack.input); - sf2d_end_frame(); - break; - case MENU_INVENTORY: - sf2d_start_frame(GFX_TOP, GFX_LEFT); - if(currentLevel == 0){ - sf2d_draw_texture_part_scale(minimap[1],(-xscr/3)-256,(-yscr/3)-32,0,0,128,128,12.5,7.5); - sf2d_draw_rectangle(0,0,400,240, 0xAFDFDFDF); - } - offsetX = xscr;offsetY = yscr; - renderMenuBackground(xscr,yscr); - offsetX = 0;offsetY = 0; - renderFrame(1,1,24,14,0xFFFF1010); - drawTextColor("Inventory",24+1,14+1,0xFF000000); - drawTextColor("Inventory",24,14,0xFF6FE2E2); - renderItemList(player.p.inv, 1,1,24,14, curInvSel); - sf2d_end_frame(); - break; - case MENU_CRAFTING: - sf2d_start_frame(GFX_TOP, GFX_LEFT); - if(currentLevel == 0){ - sf2d_draw_texture_part_scale(minimap[1],(-xscr/3)-256,(-yscr/3)-32,0,0,128,128,12.5,7.5); - sf2d_draw_rectangle(0,0,400,240, 0xAFDFDFDF); - } - offsetX = xscr;offsetY = yscr; - renderMenuBackground(xscr,yscr); - offsetX = 0;offsetY = 0; - - renderFrame(15,1,24,4,0xFFFF1010); - drawTextColor("Have",248+1,14+1,0xFF000000); - drawTextColor("Have",248,14,0xFF6FE2E2); - renderFrame(15,5,24,14,0xFFFF1010); - drawTextColor("Cost",248+1,78+1,0xFF000000); - drawTextColor("Cost",248,78,0xFF6FE2E2); - renderFrame(1,1,14,14,0xFFFF1010); - drawTextColor(currentCraftTitle,24+1,14+1,0xFF000000); - drawTextColor(currentCraftTitle,24,14,0xFF6FE2E2); - renderRecipes(currentRecipes, 1, 1, 14, 14, curInvSel); - - Recipe* rec = ¤tRecipes->recipes[curInvSel]; - renderItemIcon(rec->itemResult,rec->itemAmountLevel,128,16); - char craftText[12]; - sprintf(craftText,"%d",countItemInv(rec->itemResult,rec->itemAmountLevel, player.p.inv)); - drawText(craftText,274,34); - - if(rec->numOfCosts > 0){ - int i; - for(i = 0; i < rec->numOfCosts; i++){ - int amnt = countItemInv(rec->costs[i].costItem,0, player.p.inv); - int ttlCst = rec->costs[i].costAmount; - int col = 0xFFFFFFFF; if(amntcosts[i].costItem,1,128,48+(i*8)); - sprintf(craftText,"%d/%d",amnt,ttlCst); - drawTextColor(craftText,274,96+(i*18),col); - } - } - - sf2d_end_frame(); - break; - - case MENU_CONTAINER: - sf2d_start_frame(GFX_TOP, GFX_LEFT); - if(currentLevel == 0){ - sf2d_draw_texture_part_scale(minimap[1],(-xscr/3)-256,(-yscr/3)-32,0,0,128,128,12.5,7.5); - sf2d_draw_rectangle(0,0,400,240, 0xAFDFDFDF); - } - offsetX = xscr;offsetY = yscr; - renderMenuBackground(xscr,yscr); - if (curChestEntity->entityFurniture.r == 1){ offsetX = 48; offsetY = 0;} - else {offsetX = 0;offsetY = 0;} - - renderFrame(1,1,15,14,0xFFFF1010); - drawTextColor("Chest",24+1,14+1,0xFF000000); - drawTextColor("Chest",24,14,0xFF6FE2E2); - renderItemList(curChestEntity->entityFurniture.inv,1,1,15,14, - curChestEntity->entityFurniture.r == 0 ? curInvSel : -curChestEntity->entityFurniture.oSel - 1); - renderFrame(16,1,30,14,0xFFFF1010); - drawTextColor("Inventory",264+1,14+1,0xFF000000); - drawTextColor("Inventory",264,14,0xFF6FE2E2); - renderItemList(player.p.inv,16,1,30,14, - curChestEntity->entityFurniture.r == 1 ? curInvSel : -curChestEntity->entityFurniture.oSel - 1); - offsetX = 0;offsetY = 0; - sf2d_end_frame(); - break; - - case MENU_DUNGEON: - sf2d_start_frame(GFX_TOP, GFX_LEFT); - if(currentLevel == 0){ - sf2d_draw_texture_part_scale(minimap[1],(-xscr/3)-256,(-yscr/3)-32,0,0,128,128,12.5,7.5); - sf2d_draw_rectangle(0,0,400,240, 0xAFDFDFDF); - } - offsetX = xscr;offsetY = yscr; - renderMenuBackground(xscr,yscr); - offsetX = 0;offsetY = 0; - renderFrame(1,1,24,14,0xFFFF1010); - if(currentLevel!=5) { - drawTextColor("Dungeon Entrance",24+1,14+1,0xFF000000); - drawTextColor("Dungeon Entrance",24,14,0xFF6FE2E2); - - drawText("Warning: ", 32, 32); - drawText("You need a Dungeon Key to ", 32, 56); - drawText("enter and cannot save while ", 32, 72); - drawText("being in the Dungeon! ", 32, 88); - drawText("After leaving you will need ", 32, 112); - drawText("a new Dungeon Key for ", 32, 128); - drawText("entering another Dungeon! ", 32, 144); - - drawText(" Enter", 148, 171); - } else { - drawTextColor("Dungeon Exit",24+1,14+1,0xFF000000); - drawTextColor("Dungeon Exit",24,14,0xFF6FE2E2); - - drawText("Warning: ", 32, 32); - drawText("The Dungeon and everything ", 32, 56); - drawText("in it will disappear when ", 32, 72); - drawText("you leave it! ", 32, 88); - drawText("You will need a new Dungeon ", 32, 112); - drawText("Key for entering another ", 32, 128); - drawText("Dungeon again! ", 32, 144); - - drawText(" Leave", 148, 171); - } - - renderButtonIcon(k_accept.input & -k_accept.input, 150, 168, 1); - drawText(" Stay", 148, 195); - renderButtonIcon(k_decline.input & -k_decline.input, 150, 192, 1); - sf2d_end_frame(); - break; case MENU_ABOUT: sf2d_start_frame(GFX_TOP, GFX_LEFT); @@ -1317,7 +1003,7 @@ void renderMenu(int menu,int xscr,int yscr){ drawSizedTextColor("generic-8-bit-jrpg-soundtrack",48,180,1.0,0xFF20FF20); drawText("Press to return", 58, 220); - renderButtonIcon(k_decline.input & -k_decline.input, 128, 218, 1); + renderButtonIcon(localInputs.k_decline.input & -localInputs.k_decline.input, 128, 218, 1); sf2d_end_frame(); break; case MENU_SETTINGS: @@ -1366,9 +1052,9 @@ void renderMenu(int menu,int xscr,int yscr){ break; } drawText("Press to select", 58, 100); - renderButtonIcon(k_accept.input & -k_accept.input, 128, 98, 1); + renderButtonIcon(localInputs.k_accept.input & -localInputs.k_accept.input, 128, 98, 1); drawText("Press to return", 58, 150); - renderButtonIcon(k_decline.input & -k_decline.input, 128, 148, 1); + renderButtonIcon(localInputs.k_decline.input & -localInputs.k_decline.input, 128, 148, 1); sf2d_end_frame(); break; case MENU_TITLE: @@ -1378,20 +1064,20 @@ void renderMenu(int menu,int xscr,int yscr){ //map BG if(menuHasMapLoaded) { offsetX = (int) mxscr; offsetY = (int) myscr; - renderBackground((int) mxscr, (int) myscr); + renderBackground(1, (int) mxscr, (int) myscr); offsetX = 0; offsetY = 0; sf2d_draw_rectangle(0, 0, 400, 240, 0xAA0C0C0C); //You might think "real" black would be better, but it actually looks better that way } - renderTitle(76,16); + renderTitle(76,8); - for(i = 5; i >= 0; --i){ + for(i = 6; i >= 0; --i){ char* msg = options[i]; u32 color = 0xFF7F7F7F; if(i == currentSelection) color = 0xFFFFFFFF; - drawSizedTextColor(msg,((200 - (strlen(msg) * 8))/2) + 1, (((8 + i) * 20 - 58) >> 1) + 1,2.0, 0xFF000000); - drawSizedTextColor(msg,(200 - (strlen(msg) * 8))/2, ((8 + i) * 20 - 58) >> 1,2.0, color); + drawSizedTextColor(msg,((200 - (strlen(msg) * 8))/2) + 1, (((8 + i) * 20 - 74) >> 1) + 1,2.0, 0xFF000000); + drawSizedTextColor(msg,(200 - (strlen(msg) * 8))/2, ((8 + i) * 20 - 74) >> 1,2.0, color); } drawText(versionText,2,225); @@ -1403,7 +1089,7 @@ void renderMenu(int menu,int xscr,int yscr){ //map BG if(menuHasMapLoaded) { offsetX = (int) mxscr + 20; offsetY = (int) myscr + 120; - renderBackground((int) mxscr + 20, (int) myscr + 120); + renderBackground(1, (int) mxscr + 20, (int) myscr + 120); offsetX = 0; offsetY = 0; sf2d_draw_rectangle(0, 0, 320, 240, 0xAA0C0C0C); //You might think "real" black would be better, but it actually looks better that way @@ -1413,9 +1099,13 @@ void renderMenu(int menu,int xscr,int yscr){ switch(currentSelection){ case 0: // "Start Game" break; - case 1: // "Join Game" + case 1: // "Host Game" + drawTextColor("Host local multiplayer",(320 - (22 * 12))/2,24,0xFF7FFFFF); break; - case 2: // "How To Play" + case 2: // "Join Game" + drawTextColor("Join local multiplayer",(320 - (22 * 12))/2,24,0xFF7FFFFF); + break; + case 3: // "How To Play" startX = 72;startY = 54; render16(startX,startY,96,208,0);//C-PAD startX = 72;startY = 37; @@ -1432,11 +1122,11 @@ void renderMenu(int menu,int xscr,int yscr){ render16(startX,startY,160,208,0);//C-PAD right drawTextColor("Learn the basics",64,24,0xFF7FFFFF); break; - case 3: // "Settings" + case 4: // "Settings" drawTextColor("Modify the game's feel",(320 - (22 * 12))/2,24,0xFF7FFFFF); renderc(48,48,0,112,64,32,0); break; - case 4: // "About" + case 5: // "About" drawTextColor("Who made this game?",(320 - (19 * 12))/2,24,0xFF7FFFFF); // Secret code ;) @@ -1452,27 +1142,48 @@ void renderMenu(int menu,int xscr,int yscr){ //drawSizedText("(Totally not a secret code or anything)",4,160,1); break; - case 5: // "Exit" - drawTextColor("Exit to the homebrew menu",(320 - (25 * 12))/2,24,0xFF7FFFFF); + case 6: // "Exit" + drawTextColor("Exit to the home menu",(320 - (21 * 12))/2,24,0xFF7FFFFF); drawTextColor("(bye-bye)",(320 - (9 * 12))/2,100,0xFF7FFFFF); break; } sf2d_end_frame(); break; - case MENU_NPC: - sf2d_start_frame(GFX_TOP, GFX_LEFT); - if(currentLevel == 0){ - sf2d_draw_texture_part_scale(minimap[1],(-xscr/3)-256,(-yscr/3)-32,0,0,128,128,12.5,7.5); - sf2d_draw_rectangle(0,0,400,240, 0xAFDFDFDF); + case MENU_MULTIPLAYER_HOST: + sf2d_start_frame(GFX_TOP, GFX_LEFT); + sf2d_draw_rectangle(0, 0, 400, 240, 0xFF0C0C0C); //You might think "real" black would be better, but it actually looks better that way + + networkUpdateStatus(); + drawText("Connected Players",98,8); + int j = 0; + int lastj = 0; + for(i = 0; i", 64, 110); - renderButtonIcon(biasedCirclePad(k_up.input), 44, 92, 1); - renderButtonIcon(k_accept.input & -k_accept.input, 44, 108, 1); - renderButtonIcon(biasedCirclePad(k_down.input), 44, 125, 1); + renderButtonIcon(biasedCirclePad(localInputs.k_up.input), 44, 92, 1); + renderButtonIcon(localInputs.k_accept.input & -localInputs.k_accept.input, 44, 108, 1); + renderButtonIcon(biasedCirclePad(localInputs.k_down.input), 44, 125, 1); break; case 3: // Furniture sf2d_draw_rectangle(64, 48, 192, 32, grassColor); @@ -238,13 +238,13 @@ void renderTutorialPage(bool topScreen){ drawText(pageText,(320-(strlen(pageText))*12)/2,12); if(pageNum > 0){ drawText("<",2,16); - renderButtonIcon(k_menuPrev.input & -k_menuPrev.input, 8, 2, 2); + renderButtonIcon(localInputs.k_menuPrev.input & -localInputs.k_menuPrev.input, 8, 2, 2); } if(pageNum < maxPageNum){ drawText(">",306,16); - renderButtonIcon(k_menuNext.input & -k_menuNext.input, 136, 2, 2); + renderButtonIcon(localInputs.k_menuNext.input & -localInputs.k_menuNext.input, 136, 2, 2); } drawText("Press to exit",(320-(15*12))/2,218); - renderButtonIcon(k_decline.input & -k_decline.input, 140, 216, 1); + renderButtonIcon(localInputs.k_decline.input & -localInputs.k_decline.input, 140, 216, 1); } } diff --git a/source/Network.c b/source/Network.c index c0fdcde..affd98f 100644 --- a/source/Network.c +++ b/source/Network.c @@ -14,11 +14,216 @@ bool isConnected; bool isServer; size_t networkBufferSize; -u32 *networkBuffer; +void *networkBuffer; udsNetworkStruct networkStruct; udsBindContext networkBindCtx; +udsConnectionStatus networkStatus; + +//new code +//structure in buffer is u16(seqID),u16(size),size(data), ... +void *networkSendBuffer; +size_t networkSendBufferStartPos; +size_t networkSendBufferEndPos; +size_t networkSendBufferWrapPos; + +u16 networkSeqSendNext; +u16 networkSeqSendConf[UDS_MAXNODES+1]; +u16 networkSeqRecvLast[UDS_MAXNODES+1]; +void *networkAckBuffer; + +//async internal send/recieve handling +Thread networkThread; +volatile bool networkRunThread; +LightLock sendBufferLock; + +void networkHandleSend(); +void networkHandleRecieve(); +void clearSendAckedBuffer(); +bool sendAck(u16 target, u16 ack); + +void networkThreadMain(void *arg) { + while(networkRunThread) { + if(udsRunning && isConnected) { + networkUpdateStatus(); + networkHandleRecieve(); + networkHandleSend(); + } + + //TODO: Set meaningfull value, WARNING: Setting this near 1ms (1000*1000) will make everything super laggy, higher values actually work better! + svcSleepThread(10000 * 1000); + } +} + +void networkUpdateStatus() { + udsGetConnectionStatus(&networkStatus); +} + +void networkHandleRecieve() { + bool recieved = false; + do { + recieved = false; + + size_t actualSize = 0; + u16 sourceNetworkNodeID; + u32 ackToSend = 0; + + memset(networkBuffer, 0, networkBufferSize); + + Result ret = udsPullPacket(&networkBindCtx, networkBuffer, networkBufferSize, &actualSize, &sourceNetworkNodeID); + if(R_FAILED(ret)) { + //TODO: what do? + + //actualSize will be 0 if no packet is available + } else if(actualSize) { + void *readPointer = networkBuffer; + + //ack frame + if(actualSize==sizeof(u16)) { + networkSeqSendConf[sourceNetworkNodeID] = *((u16*) readPointer); + clearSendAckedBuffer(); + //normal frame + } else { + while(actualSize>0) { + //read seqID and size + u16 seqID = *((u16*) readPointer); + readPointer += sizeof(u16); + actualSize -= sizeof(u16); + + u16 size = *((u16*) readPointer); + readPointer += sizeof(u16); + actualSize -= sizeof(u16); + + //if the seq id was expected handle the packet + u32 nextID = networkSeqRecvLast[sourceNetworkNodeID]+1; + if(seqID==nextID) { + networkSeqRecvLast[sourceNetworkNodeID] = seqID; + ackToSend = seqID; + + //handle data + processPacket(readPointer, size); + } else if(seqID<=nextID-1) { + ackToSend = nextID-1; + } + readPointer += size; + actualSize -= size; + } + + if(ackToSend!=0) { + if(sendAck(sourceNetworkNodeID, ackToSend)) { + } + } + } + + recieved = true; + } + } while(recieved); +} + +void networkHandleSend() { + if(networkSendBufferStartPos!=networkSendBufferEndPos) { + LightLock_Lock(&sendBufferLock); + + //determine send size + size_t currentSize = 0; + while(networkSendBufferStartPos+currentSize0) { + //send frame + Result ret = udsSendTo(UDS_BROADCAST_NETWORKNODEID, NETWORK_CHANNEL, UDS_SENDFLAG_Default, networkSendBuffer+networkSendBufferStartPos, currentSize); + if(UDS_CHECK_SENDTO_FATALERROR(ret)) { + //TODO: what do? + } else if(R_FAILED(ret)) { + //TODO: what do? + } + } + + LightLock_Unlock(&sendBufferLock); + } +} + +void clearSendAckedBuffer() { + //find last ack recieved from all com partners + u16 ackID = 0; + for(int i=1; i<=UDS_MAXNODES; i++) { + if(i!=networkGetLocalNodeID() && networkIsNodeConnected(i)) { + if(networkSeqSendConf[i]==0) { + ackID = 0; + return; + } + + if(ackID==0) { + ackID = networkSeqSendConf[i]; + } else if(networkSeqSendConf[i]<100) { + if(ackID > networkSeqSendConf[i] && ackID<65535-100) ackID = networkSeqSendConf[i]; + } else if(networkSeqSendConf[i]>65535-100) { + if(ackID > networkSeqSendConf[i] || ackID<100) ackID = networkSeqSendConf[i]; + } else { + if(ackID > networkSeqSendConf[i]) ackID = networkSeqSendConf[i]; + } + } + } + if(ackID==0) return; + + LightLock_Lock(&sendBufferLock); + + //clear buffer of acknowledgt packets + while(networkSendBufferStartPos!=networkSendBufferEndPos) { + //find current seqid and size + u16 seqID = *((u16*) (networkSendBuffer+networkSendBufferStartPos)); + u16 size = *((u16*) (networkSendBuffer+networkSendBufferStartPos+sizeof(u16))); + + if(seqID<=ackID || (ackID<100 && seqID>65535-100)) { + + size_t currentSize = sizeof(u16)*2 + size; + + //adjust buffer "pointers" + networkSendBufferStartPos += currentSize; + if(networkSendBufferStartPos==networkSendBufferEndPos) { + networkSendBufferStartPos = 0; + networkSendBufferEndPos = 0; + networkSendBufferWrapPos = 0; + } + + //wrap + if(networkSendBufferStartPos==networkSendBufferWrapPos) { + networkSendBufferStartPos = 0; + networkSendBufferWrapPos = networkSendBufferEndPos; + } + } else { + break; + } + } + LightLock_Unlock(&sendBufferLock); +} + +bool sendAck(u16 target, u16 ack) { + Result ret = udsSendTo(target, NETWORK_CHANNEL, UDS_SENDFLAG_Default, &ack, sizeof(u16)); + if(UDS_CHECK_SENDTO_FATALERROR(ret)) { + //TODO: what do? + return false; + } else if(R_FAILED(ret)) { + //TODO: what do? + return false; + } else { + return true; + } +} + void networkInit() { Result ret = udsInit(0x3000, NULL); if(R_FAILED(ret)) { @@ -31,19 +236,78 @@ void networkInit() { isConnected = false; isServer = false; + networkWriteBuffer = malloc(NETWORK_MAXDATASIZE); + if(networkWriteBuffer==NULL) { + networkExit(); + return; + } + networkBufferSize = 0x4000; networkBuffer = malloc(networkBufferSize); + if(networkBuffer==NULL) { + networkExit(); + return; + } + + networkSendBufferStartPos = 0; + networkSendBufferEndPos = 0; + networkSendBufferWrapPos = 0; + networkSendBuffer = malloc(NETWORK_SENDBUFFERSIZE); + if(networkSendBuffer==NULL) { + networkExit(); + return; + } + + networkSeqSendNext = 1; + for(int i=0; i=scannedNetworksCount) return false; - Result ret = udsGetNodeInfoUsername(&scannedNetworks[pos].nodes[0], name); + Result ret = udsGetNodeInfoUsername(&(scannedNetworks[pos].nodes[0]), name); if(R_FAILED(ret)) { //TODO: what do? return false; @@ -121,6 +391,7 @@ bool networkConnect(int pos) { if(R_FAILED(ret)) { return false; } else { + if(udsWaitConnectionStatusEvent(false, false)) {} isConnected = true; isServer = false; return true; @@ -130,18 +401,31 @@ bool networkConnect(int pos) { } void networkDisconnect() { - //TODO: For clients this just means disconnect, for the server it means destroy the network + //For clients this just means disconnect, for the server it means destroy the network if(udsRunning && isConnected) { //TODO if(isServer) { - udsDisconnectNetwork(); - } else { //TODO: Clients need to cleanup too, how can I tell they got disconnected udsDestroyNetwork(); + } else { + udsDisconnectNetwork(); } udsUnbind(&networkBindCtx); isConnected = false; + isServer = false; + + //reset send buffer + networkSendBufferStartPos = 0; + networkSendBufferEndPos = 0; + networkSendBufferWrapPos = 0; + + //reset ack status + networkSeqSendNext = 1; + for(int i=0; ianalyze.type, sourceNetworkNodeID); - } + return true; } -} + return false; +} + + +int fitInSendBuffer(size_t size) { + //add "header" length + size += sizeof(u16)*2; + + //we have no wrap currently + if(networkSendBufferStartPos<=networkSendBufferEndPos) { + //and can fit without wrap + if(networkSendBufferEndPos+sizesize) { + networkSendBufferEndPos += size; + + return networkSendBufferEndPos-size; + } + } + + return -1; +} + +void networkSend(void *packet, size_t size) { + //search for fit in buffer (and BLOCK until free space is found) + LightLock_Lock(&sendBufferLock); + int pos = fitInSendBuffer(size); + while(pos==-1) { + LightLock_Unlock(&sendBufferLock); + svcSleepThread(4500 * 1000); //TODO: Set meaningfull value + LightLock_Lock(&sendBufferLock); + + pos = fitInSendBuffer(size); + } + + //fit found -> space is allready "reserved" -> write packet to buffer + void *writePointer = networkSendBuffer + pos; + + //write seq number + *((u16*) writePointer) = networkSeqSendNext; + networkSeqSendNext++; + if(networkSeqSendNext==0) { + networkSeqSendNext = 1; + } + writePointer += sizeof(u16); + + //write size + *((u16*) writePointer) = (u16) size; + writePointer += sizeof(u16); + + //write data + memcpy(writePointer, packet, size); + writePointer += size; + + LightLock_Unlock(&sendBufferLock); +} + +void networkSendWaitFlush() { + while(networkSendBufferStartPos!=networkSendBufferEndPos) { + svcSleepThread(4500 * 1000); + } +} diff --git a/source/Network.h b/source/Network.h index fcbc70d..ab470ac 100644 --- a/source/Network.h +++ b/source/Network.h @@ -3,45 +3,19 @@ #include <3ds.h> #define NETWORK_WLANCOMMID 0x11441850 -#define NETWORK_PASSPHRASE "minicraft3ds localplay passphrase" +#define NETWORK_PASSPHRASE "minicraft3dsLP" #define NETWORK_CHANNEL 1 #define NETWORK_RECVBUFSIZE UDS_DEFAULT_RECVBUFSIZE -#define NETWORK_MAXPLAYERS UDS_MAXNODES +#define NETWORK_MAXDATASIZE 1024 +#define NETWORK_SENDBUFFERSIZE ((NETWORK_MAXDATASIZE+256)*10) -//packet type ids -#define PACKET_REQUEST_MAPDATA 1 -#define PACKET_MAPDATA 2 +#define NETWORK_STACKSIZE (8*1024) -//TODO: Every other packet struct should start with a u8 type to match this -typedef struct { - u8 type; -} packetAnalyze; +#define NETWORK_MAXPLAYERS 8 -typedef struct { - u8 type; - - u8 level; -} packetRequestMapData; - -typedef struct { - u8 type; - - u8 level; - u8 offset; //-> data is at offset*128 to offset*128+127 - - u8 map[128]; - u8 data[128]; -} packetMapData; - -typedef struct { - union { - packetAnalyze analyze; - packetRequestMapData requestMapData; - packetMapData mapData; - }; -} networkPacket; +void *networkWriteBuffer; void networkInit(); void networkExit(); @@ -49,15 +23,20 @@ void networkExit(); bool networkAvailable(); bool networkHost(); +void networkHostStopConnections(); void networkScan(); int networkGetScanCount(); -bool networkGetScanName(char *name, int pos); //TODO: Name should be long enough to handle all allowed names (char[256]) +bool networkGetScanName(char *name, int pos); bool networkConnect(int pos); void networkDisconnect(); +void networkUpdateStatus(); bool networkConnected(); -bool networkIsServer(); -void networkSend(networkPacket *packet, size_t size); //TODO: Should this be a pointer? Calling function needs to cleanup itself -void networkSendTo(networkPacket *packet, size_t size, u16 reciever); //TODO: Should this be a pointer? Calling function needs to cleanup itself -void networkRecieve(); //TODO: Should recieve actually handle all the packets or just return them? +int networkGetNodeCount(); +u16 networkGetLocalNodeID(); +bool networkIsNodeConnected(u16 id); +bool networkGetNodeName(u16 id, char *name); + +void networkSend(void *packet, size_t size); +void networkSendWaitFlush(); diff --git a/source/PacketHandler.c b/source/PacketHandler.c index a1f2da5..82a8f94 100644 --- a/source/PacketHandler.c +++ b/source/PacketHandler.c @@ -1,48 +1,364 @@ #include "PacketHandler.h" -#include "Globals.h" +#include +#include "Synchronizer.h" -void processPacket(networkPacket *packet, u8 type, u16 sender) { - //TODO: Differenciate the packets and process them - if(networkIsServer()) { - if(type==PACKET_REQUEST_MAPDATA) { - u8 level = packet->requestMapData.level; - - if(level>=0 && level<=5) { - //send back tile data - for(int y=0; y<128; y++) { - networkPacket packet = { - .mapData = { - .type = PACKET_MAPDATA, - .level = level, - .offset = y - } - }; - for(int x=0; x<128; x++) { - packet.mapData.map[x] = map[level][x+y*128]; - packet.mapData.data[x] = data[level][x+y*128]; - } - networkSendTo(&packet, sizeof(packetMapData), sender); - } - } - } else { - //TODO: Unknown packet - how to handle? - } - } else { - if(type==PACKET_MAPDATA) { - u8 level = packet->mapData.level; - - if(level>=0 && level<=5) { - //recieve tile data - //TODO: This should really check whether the values are in valid range - int y = packet->mapData.offset; - for(int x=0; x<128; x++) { - map[level][x+y*128] = packet->mapData.map[x]; - data[level][x+y*128] = packet->mapData.data[x]; - } - } - } else { - //TODO: Unknown packet - how to handle? - } - } +FILE *recvFile; +size_t recvFileSize; + + + + +void * writeBool(void *buffer, size_t *size, bool value) { + *((bool*) buffer) = value; + *(size) += sizeof(bool); + return buffer + sizeof(bool); +} + +void * writeU8(void *buffer, size_t *size, u8 value) { + *((u8*) buffer) = value; + *(size) += sizeof(u8); + return buffer + sizeof(u8); +} + +void * writeU16(void *buffer, size_t *size, u16 value) { + *((u16*) buffer) = value; + *(size) += sizeof(u16); + return buffer + sizeof(u16); +} + +void * writeU32(void *buffer, size_t *size, u32 value) { + *((u32*) buffer) = value; + *(size) += sizeof(u32); + return buffer + sizeof(u32); +} + +void * writeSizeT(void *buffer, size_t *size, size_t value) { + *((size_t*) buffer) = value; + *(size) += sizeof(size_t); + return buffer + sizeof(size_t); +} + + + + +void * readBool(void *buffer, size_t *size, bool *value) { + *value = *((bool*) buffer); + *(size) -= sizeof(bool); + return buffer + sizeof(bool); +} + +void * readU8(void *buffer, size_t *size, u8 *value) { + *value = *((u8*) buffer); + *(size) -= sizeof(u8); + return buffer + sizeof(u8); +} + +void * readU16(void *buffer, size_t *size, u16 *value) { + *value = *((u16*) buffer); + *(size) -= sizeof(u16); + return buffer + sizeof(u16); +} + +void * readU32(void *buffer, size_t *size, u32 *value) { + *value = *((u32*) buffer); + *(size) -= sizeof(u32); + return buffer + sizeof(u32); +} + +void * readSizeT(void *buffer, size_t *size, size_t *value) { + *value = *((size_t*) buffer); + *(size) -= sizeof(size_t); + return buffer + sizeof(size_t); +} + + + + +void processPacket(void *packet, size_t size) { + //Differenciate the packets and process them + switch(packetGetID(packet)) { + case PACKET_START: { + void *buffer = packetGetDataStart(packet); + size = packetGetDataSize(size); + + //find player index based on network node id + //and set player uuid in synchronizer + u32 seed; + u32 playerCount = 1; + int playerIndex = 0; + + buffer = readU32(buffer, &size, &seed); + buffer = readU32(buffer, &size, &playerCount); + for(int i=0; ik_touch.px); + buffer = writeU16(buffer, &size, inputs->k_touch.py); + + buffer = writeBool(buffer, &size, inputs->k_up.down); buffer = writeBool(buffer, &size, inputs->k_up.clicked); + buffer = writeBool(buffer, &size, inputs->k_down.down); buffer = writeBool(buffer, &size, inputs->k_down.clicked); + buffer = writeBool(buffer, &size, inputs->k_left.down); buffer = writeBool(buffer, &size, inputs->k_left.clicked); + buffer = writeBool(buffer, &size, inputs->k_right.down); buffer = writeBool(buffer, &size, inputs->k_right.clicked); + buffer = writeBool(buffer, &size, inputs->k_attack.down); buffer = writeBool(buffer, &size, inputs->k_attack.clicked); + buffer = writeBool(buffer, &size, inputs->k_menu.down); buffer = writeBool(buffer, &size, inputs->k_menu.clicked); + buffer = writeBool(buffer, &size, inputs->k_pause.down); buffer = writeBool(buffer, &size, inputs->k_pause.clicked); + buffer = writeBool(buffer, &size, inputs->k_accept.down); buffer = writeBool(buffer, &size, inputs->k_accept.clicked); + buffer = writeBool(buffer, &size, inputs->k_decline.down); buffer = writeBool(buffer, &size, inputs->k_decline.clicked); + buffer = writeBool(buffer, &size, inputs->k_delete.down); buffer = writeBool(buffer, &size, inputs->k_delete.clicked); + buffer = writeBool(buffer, &size, inputs->k_menuNext.down); buffer = writeBool(buffer, &size, inputs->k_menuNext.clicked); + buffer = writeBool(buffer, &size, inputs->k_menuPrev.down); buffer = writeBool(buffer, &size, inputs->k_menuPrev.clicked); + + return size; +} + +bool readInputPacketData(void *buffer, size_t size, Inputs *inputs) { + buffer = readU16(buffer, &size, &(inputs->k_touch.px)); + if(size<=0) return false; + buffer = readU16(buffer, &size, &(inputs->k_touch.py)); + if(size<=0) return false; + + buffer = readBool(buffer, &size, &(inputs->k_up.down)); buffer = readBool(buffer, &size, &(inputs->k_up.clicked)); + if(size<=0) return false; + buffer = readBool(buffer, &size, &(inputs->k_down.down)); buffer = readBool(buffer, &size, &(inputs->k_down.clicked)); + if(size<=0) return false; + buffer = readBool(buffer, &size, &(inputs->k_left.down)); buffer = readBool(buffer, &size, &(inputs->k_left.clicked)); + if(size<=0) return false; + buffer = readBool(buffer, &size, &(inputs->k_right.down)); buffer = readBool(buffer, &size, &(inputs->k_right.clicked)); + if(size<=0) return false; + buffer = readBool(buffer, &size, &(inputs->k_attack.down)); buffer = readBool(buffer, &size, &(inputs->k_attack.clicked)); + if(size<=0) return false; + buffer = readBool(buffer, &size, &(inputs->k_menu.down)); buffer = readBool(buffer, &size, &(inputs->k_menu.clicked)); + if(size<=0) return false; + buffer = readBool(buffer, &size, &(inputs->k_pause.down)); buffer = readBool(buffer, &size, &(inputs->k_pause.clicked)); + if(size<=0) return false; + buffer = readBool(buffer, &size, &(inputs->k_accept.down)); buffer = readBool(buffer, &size, &(inputs->k_accept.clicked)); + if(size<=0) return false; + buffer = readBool(buffer, &size, &(inputs->k_decline.down)); buffer = readBool(buffer, &size, &(inputs->k_decline.clicked)); + if(size<=0) return false; + buffer = readBool(buffer, &size, &(inputs->k_delete.down)); buffer = readBool(buffer, &size, &(inputs->k_delete.clicked)); + if(size<=0) return false; + buffer = readBool(buffer, &size, &(inputs->k_menuNext.down)); buffer = readBool(buffer, &size, &(inputs->k_menuNext.clicked)); + if(size<=0) return false; + buffer = readBool(buffer, &size, &(inputs->k_menuPrev.down)); buffer = readBool(buffer, &size, &(inputs->k_menuPrev.clicked)); + + return size==0; +} + +void sendFile(FILE *file, u8 fileType, u8 id) { + fseek(file, 0, SEEK_END); // seek to end of file + size_t fsize = ftell(file); // get current file pointer + fseek(file, 0, SEEK_SET); // seek back to beginning of file + + //send file header + void *buffer = networkWriteBuffer; + size_t size = 0; + buffer = writeU8(buffer, &size, PACKET_START_FILEHEADER); + buffer = writeU8(buffer, &size, 0); + buffer = writeU32(buffer, &size, 0); + buffer = writeU8(buffer, &size, fileType); + buffer = writeU8(buffer, &size, id); + buffer = writeSizeT(buffer, &size, fsize); + networkSend(networkWriteBuffer, size); + + //send file data + while(fsize>0) { + buffer = networkWriteBuffer; + size = 0; + buffer = writeU8(buffer, &size, PACKET_START_FILEDATA); + buffer = writeU8(buffer, &size, 0); + buffer = writeU32(buffer, &size, 0); + + //read file data + size_t towrite = NETWORK_MAXDATASIZE - size; + if(towrite>fsize) towrite = fsize; + + fread(buffer, 1, towrite, file); + + size += towrite; + fsize -= towrite; + + //send file data + networkSend(networkWriteBuffer, size); + } +} + +void sendIDPacket(u8 playerID, u32 uid) { + void *buffer = networkWriteBuffer; + size_t size = 0; + + buffer = writeU8(buffer, &size, PACKET_START_ID); + buffer = writeU8(buffer, &size, playerID); + buffer = writeU32(buffer, &size, 0); + + buffer = writeU32(buffer, &size, uid); + + networkSend(networkWriteBuffer, size); +} + +void sendStartReadyPacket(u8 playerID) { + void *buffer = networkWriteBuffer; + size_t size = 0; + + buffer = writeU8(buffer, &size, PACKET_START_READY); + buffer = writeU8(buffer, &size, playerID); + buffer = writeU32(buffer, &size, 0); + + networkSend(networkWriteBuffer, size); +} + +void sendStartSyncPacket() { + void *buffer = networkWriteBuffer; + size_t size = 0; + + buffer = writeU8(buffer, &size, PACKET_TURN_START); + buffer = writeU8(buffer, &size, 0); + buffer = writeU32(buffer, &size, 0); + + networkSend(networkWriteBuffer, size); } diff --git a/source/PacketHandler.h b/source/PacketHandler.h index 6566a5f..0001e79 100644 --- a/source/PacketHandler.h +++ b/source/PacketHandler.h @@ -1,5 +1,34 @@ #pragma once +#include #include "Network.h" +#include "Player.h" -void processPacket(networkPacket *packet, u8 type, u16 sender); +#define PACKET_START 0 +#define PACKET_START_FILEHEADER 1 +#define PACKET_START_FILEDATA 2 +#define PACKET_START_REQUEST_IDS 3 +#define PACKET_START_ID 4 +#define PACKET_START_READY 5 + +#define PACKET_TURN_START 10 +#define PACKET_TURN_INPUT 11 + +void processPacket(void *packet, size_t size); + +u8 packetGetID(void *packet); +u8 packetGetSender(void *packet); +u32 packetGetTurn(void *packet); +void * packetGetDataStart(void *packet); +size_t packetGetDataSize(size_t size); + +size_t writeStartPacket(void *buffer, u32 seed); +size_t writeStartRequestPacket(void *buffer); + +size_t writeInputPacket(void *buffer, Inputs *inputs, u8 playerID, u32 turnNumber); +bool readInputPacketData(void *buffer, size_t size, Inputs *inputs); + +void sendFile(FILE *file, u8 fileType, u8 id); +void sendIDPacket(u8 playerID, u32 uid); +void sendStartReadyPacket(u8 playerID); +void sendStartSyncPacket(); diff --git a/source/Player.c b/source/Player.c new file mode 100644 index 0000000..5085699 --- /dev/null +++ b/source/Player.c @@ -0,0 +1,492 @@ +#include "Player.h" + +#include +#include "Globals.h" + +void initPlayers() { + for(int i=0; ientity.type = ENTITY_PLAYER; + pd->entity.level = 1; + pd->entity.xr = 4; + pd->entity.yr = 3; + pd->entity.canSwim = true; + pd->entity.p.ax = 0; + pd->entity.p.ay = 0; + pd->entity.p.health = 10; + pd->entity.p.stamina = 10; + pd->entity.p.walkDist = 0; + pd->entity.p.attackTimer = 0; + pd->entity.p.dir = 0; + pd->entity.p.isDead = false; + pd->entity.p.hasWon = false; + + pd->entity.p.data = pd; +} + +void playerInitInventory(PlayerData *pd) { + //reset inventory + pd->inventory.lastSlot = 0; + pd->activeItem = &noItem; + + addItemToInventory(newItem(ITEM_WORKBENCH,0), &(pd->inventory)); + addItemToInventory(newItem(ITEM_POWGLOVE,0), &(pd->inventory)); + + if(TESTGODMODE) { + addItemToInventory(newItem(TOOL_SHOVEL,4), &(pd->inventory)); + addItemToInventory(newItem(TOOL_HOE,4), &(pd->inventory)); + addItemToInventory(newItem(TOOL_SWORD,4), &(pd->inventory)); + addItemToInventory(newItem(TOOL_PICKAXE,4), &(pd->inventory)); + addItemToInventory(newItem(TOOL_AXE,4), &(pd->inventory)); + + addItemToInventory(newItem(ITEM_ANVIL,0), &(pd->inventory)); + addItemToInventory(newItem(ITEM_CHEST,0), &(pd->inventory)); + addItemToInventory(newItem(ITEM_OVEN,0), &(pd->inventory)); + addItemToInventory(newItem(ITEM_FURNACE,0), &(pd->inventory)); + addItemToInventory(newItem(ITEM_LANTERN,0), &(pd->inventory)); + + addItemToInventory(newItem(TOOL_MAGIC_COMPASS,1), &(pd->inventory)); + + int i; + for (i = 7;i < 28;++i) addItemToInventory(newItem(i,50), &(pd->inventory)); + } +} + +void playerInitSprite(PlayerData *pd) { + pd->sprite.choosen = false; + + pd->sprite.legs = 0; + pd->sprite.body = 0; + pd->sprite.arms = 0; + pd->sprite.head = 0; + pd->sprite.eyes = 0; +} + +void playerInitMenus(PlayerData *pd) { + pd->ingameMenu = MENU_NONE; + pd->ingameMenuSelection = 0; + pd->ingameMenuInvSel = 0; + pd->ingameMenuInvSelOther = 0; + pd->ingameMenuAreYouSure = false; + pd->ingameMenuAreYouSureSave = false; + pd->ingameMenuTimer = 0; + + resetNPCMenuData(&(pd->npcMenuData)); + + pd->mapShouldRender = false; + pd->mapScrollX = 0; + pd->mapScrollY = 0; + pd->mapZoomLevel = 2; + sprintf(pd->mapText,"x%d", pd->mapZoomLevel); + + pd->touchLastX = -1; + pd->touchLastY = -1; + pd->touchIsDraggingMap = false; + pd->touchIsChangingSize = false; +} + +void initPlayer(PlayerData *pd) { + pd->isSpawned = false; + + playerInitMiniMapData(pd->minimapData); + playerInitEntity(pd); + playerInitInventory(pd); + + playerInitSprite(pd); + + initQuests(&(pd->questManager)); + resetQuests(&(pd->questManager)); + + playerInitMenus(pd); + + pd->score = 0; +} + +void freePlayer(PlayerData *pd) { + freeQuests(&(pd->questManager)); +} + +PlayerData* getNearestPlayer(s8 level, s16 x, s16 y) { + int nearest = -1; + unsigned int nearestDist = UINT_MAX; + + for(int i=0; iactiveItem->id){ + //shooting arrows + case TOOL_BOW: + item = getItemFromInventory(ITEM_ARROW_WOOD, &(pd->inventory)); + if(item!=NULL) { + aitemID = ITEM_ARROW_WOOD; + aitem = item; + } + item = getItemFromInventory(ITEM_ARROW_STONE, &(pd->inventory)); + if(item!=NULL) { + aitemID = ITEM_ARROW_STONE; + aitem = item; + } + item = getItemFromInventory(ITEM_ARROW_IRON, &(pd->inventory)); + if(item!=NULL) { + aitemID = ITEM_ARROW_IRON; + aitem = item; + } + item = getItemFromInventory(ITEM_ARROW_GOLD, &(pd->inventory)); + if(item!=NULL) { + aitemID = ITEM_ARROW_GOLD; + aitem = item; + } + item = getItemFromInventory(ITEM_ARROW_GEM, &(pd->inventory)); + if(item!=NULL) { + aitemID = ITEM_ARROW_GEM; + aitem = item; + } + + if(aitemID!=0) { + --aitem->countLevel; + if (isItemEmpty(aitem)) { + removeItemFromInventory(aitem->slotNum, &(pd->inventory)); + } + + switch(pd->entity.p.dir) { + case 0: + addEntityToList(newArrowEntity(&(pd->entity), aitemID, 0, 2, pd->entity.level), &eManager); + break; + case 1: + addEntityToList(newArrowEntity(&(pd->entity), aitemID, 0, -2, pd->entity.level), &eManager); + break; + case 2: + addEntityToList(newArrowEntity(&(pd->entity), aitemID, -2, 0, pd->entity.level), &eManager); + break; + case 3: + addEntityToList(newArrowEntity(&(pd->entity), aitemID, 2, 0, pd->entity.level), &eManager); + break; + } + return true; + } + break; + + // Health items + case ITEM_APPLE: + if(pd->entity.p.health < 10 && playerUseEnergy(pd, 2)){ + playerHeal(pd, 1); + --(pd->activeItem->countLevel); + } + break; + case ITEM_FLESH: + if(pd->entity.p.health < 10 && playerUseEnergy(pd, 4+(rand()%4))){ + playerHeal(pd, 1); + --(pd->activeItem->countLevel); + } + break; + case ITEM_BREAD: + if(pd->entity.p.health < 10 && playerUseEnergy(pd, 3)){ + playerHeal(pd, 2); + --(pd->activeItem->countLevel); + } + break; + case ITEM_PORK_RAW: + if(pd->entity.p.health < 10 && playerUseEnergy(pd, 4+(rand()%4))){ + playerHeal(pd, 1); + --(pd->activeItem->countLevel); + } + break; + case ITEM_PORK_COOKED: + if(pd->entity.p.health < 10 && playerUseEnergy(pd, 3)){ + playerHeal(pd, 3); + --(pd->activeItem->countLevel); + } + break; + case ITEM_BEEF_RAW: + if(pd->entity.p.health < 10 && playerUseEnergy(pd, 4+(rand()%4))){ + playerHeal(pd, 1); + --(pd->activeItem->countLevel); + } + break; + case ITEM_BEEF_COOKED: + if(pd->entity.p.health < 10 && playerUseEnergy(pd, 3)){ + playerHeal(pd, 4); + --(pd->activeItem->countLevel); + } + break; + //special item + case ITEM_WIZARD_SUMMON: + if(pd->entity.level==0) { + --(pd->activeItem->countLevel); + + airWizardHealthDisplay = 2000; + addEntityToList(newAirWizardEntity(630, 820, 0), &eManager); + } + break; + } + + if (isItemEmpty(pd->activeItem)) { + removeItemFromInventory(pd->activeItem->slotNum, &(pd->inventory)); + pd->activeItem = &noItem; + } + + return false; +} + +bool playerInteract(PlayerData *pd, int x0, int y0, int x1, int y1) { + Entity * es[eManager.lastSlot[pd->entity.level]]; + int eSize = getEntities(es, pd->entity.level, x0, y0, x1, y1); + int i; + for (i = 0; i < eSize; ++i) { + Entity * ent = es[i]; + if (ent != &(pd->entity)){ + if (ItemVsEntity(pd, pd->activeItem, ent, pd->entity.p.dir)) return true; + } + } + return false; +} + +void playerAttack(PlayerData *pd) { + bool done = false; + pd->entity.p.attackTimer = 5; + int yo = -2; + int range = 12; + + //directly using an item + if(playerUseItem(pd)) return; + + //interacting with entities + switch(pd->entity.p.dir){ + case 0: if(playerInteract(pd, pd->entity.x - 8, pd->entity.y + 4 + yo, pd->entity.x + 8, pd->entity.y + range + yo)) return; break; + case 1: if(playerInteract(pd, pd->entity.x - 8, pd->entity.y - range + yo, pd->entity.x + 8, pd->entity.y - 4 + yo)) return; break; + case 2: if(playerInteract(pd, pd->entity.x - range, pd->entity.y - 8 + yo, pd->entity.x - 4, pd->entity.y + 8 + yo)) return; break; + case 3: if(playerInteract(pd, pd->entity.x + 4, pd->entity.y - 8 + yo, pd->entity.x + range, pd->entity.y + 8 + yo)) return; break; + } + + int xt = pd->entity.x >> 4; + int yt = (pd->entity.y + yo) >> 4; + int r = 12; + if (pd->entity.p.dir == 0) yt = (pd->entity.y + r + yo) >> 4; + if (pd->entity.p.dir == 1) yt = (pd->entity.y - r + yo) >> 4; + if (pd->entity.p.dir == 2) xt = (pd->entity.x - r) >> 4; + if (pd->entity.p.dir == 3) xt = (pd->entity.x + r) >> 4; + + //interacting with tiles + if (xt >= 0 && yt >= 0 && xt < 128 && yt < 128) { + s8 itract = itemTileInteract(getTile(pd->entity.level, xt, yt), pd, pd->activeItem, pd->entity.level, xt, yt, pd->entity.x, pd->entity.y, pd->entity.p.dir); + if(itract > 0){ + if(itract==2) pd->entity.p.isCarrying = false; + done = true; + } + + if (pd->activeItem != &noItem && isItemEmpty(pd->activeItem)) { + removeItemFromInventory(pd->activeItem->slotNum, &(pd->inventory)); + pd->activeItem = &noItem; + } + } + + if(done) return; + + //breaking tiles + if (pd->activeItem == &noItem || pd->activeItem->id == TOOL_SWORD || pd->activeItem->id == TOOL_AXE) { + if (xt >= 0 && yt >= 0 && xt < 128 && 128) { + playerHurtTile(pd, getTile(pd->entity.level, xt, yt), pd->entity.level, xt, yt, (rand()%3) + 1, pd->entity.p.dir); + } + } +} + +bool playerUseArea(PlayerData *pd, int x0, int y0, int x1, int y1) { + Entity * entities[eManager.lastSlot[pd->entity.level]]; + int i; + int ae = getEntities(entities, pd->entity.level, x0, y0, x1, y1); + for(i = 0; i < ae; ++i){ + if(useEntity(pd, entities[i])) return true; + } + return false; +} + +bool playerUse(PlayerData *pd) { + int yo = -2; + if (pd->entity.p.dir == 0 && playerUseArea(pd, pd->entity.x - 8, pd->entity.y + 4 + yo, pd->entity.x + 8, pd->entity.y + 12 + yo)) return true; + if (pd->entity.p.dir == 1 && playerUseArea(pd, pd->entity.x - 8, pd->entity.y - 12 + yo, pd->entity.x + 8, pd->entity.y - 4 + yo)) return true; + if (pd->entity.p.dir == 3 && playerUseArea(pd, pd->entity.x + 4, pd->entity.y - 8 + yo, pd->entity.x + 12, pd->entity.y + 8 + yo)) return true; + if (pd->entity.p.dir == 2 && playerUseArea(pd, pd->entity.x - 12, pd->entity.y - 8 + yo, pd->entity.x - 4, pd->entity.y + 8 + yo)) return true; + return false; +} + +void tickPlayer(PlayerData *pd, bool inmenu) { + //invincibility time + if (pd->entity.hurtTime > 0) pd->entity.hurtTime--; + + //stamina recharging + bool swimming = isWater(pd->entity.level, pd->entity.x>>4, pd->entity.y>>4); + if (pd->entity.p.stamina <= 0 && pd->entity.p.staminaRechargeDelay == 0 && pd->entity.p.staminaRecharge == 0) { + pd->entity.p.staminaRechargeDelay = 40; + } + + if (pd->entity.p.staminaRechargeDelay > 0) { + --pd->entity.p.staminaRechargeDelay; + } + + if (pd->entity.p.staminaRechargeDelay == 0) { + ++pd->entity.p.staminaRecharge; + if (swimming) pd->entity.p.staminaRecharge = 0; + + while (pd->entity.p.staminaRecharge > 10) { + pd->entity.p.staminaRecharge -= 10; + if (pd->entity.p.stamina < 10) ++pd->entity.p.stamina; + } + } + + if(!inmenu) { + if(!pd->sprite.choosen) { + pd->ingameMenu = MENU_CHARACTER_CUSTOMIZE; + pd->ingameMenuSelection = 0; + return; + } + + //movement + pd->entity.p.ax = 0; + pd->entity.p.ay = 0; + + if (pd->inputs.k_left.down){ + pd->entity.p.ax -= 1; + pd->entity.p.dir = 2; + ++pd->entity.p.walkDist; + } + if (pd->inputs.k_right.down){ + pd->entity.p.ax += 1; + pd->entity.p.dir = 3; + ++pd->entity.p.walkDist; + } + if (pd->inputs.k_up.down){ + pd->entity.p.ay -= 1; + pd->entity.p.dir = 1; + ++pd->entity.p.walkDist; + } + if (pd->inputs.k_down.down){ + pd->entity.p.ay += 1; + pd->entity.p.dir = 0; + ++pd->entity.p.walkDist; + } + if (pd->entity.p.staminaRechargeDelay % 2 == 0) moveMob(&(pd->entity), pd->entity.p.ax, pd->entity.p.ay); + + //"pausing", TODO: since multiplayer this will no longer pause + if (pd->inputs.k_pause.clicked){ + pd->ingameMenuSelection = 0; + pd->ingameMenu = MENU_PAUSED; + } + + //attacking + if(pd->inputs.k_attack.clicked){ + if (pd->entity.p.stamina != 0) { + if(!TESTGODMODE) pd->entity.p.stamina--; + pd->entity.p.staminaRecharge = 0; + + playerAttack(pd); + } + } + + if (pd->inputs.k_menu.clicked){ + pd->ingameMenuInvSel = 0; + if(!playerUse(pd)) pd->ingameMenu = MENU_INVENTORY; + } + } + + //swimming stamina and drowning + if (swimming && pd->entity.p.swimTimer % 60 == 0) { + if (pd->entity.p.stamina > 0) { + if(!TESTGODMODE) --pd->entity.p.stamina; + } else { + hurtEntity(&(pd->entity), 1, -1, 0xFFAF00FF, NULL); + } + } + + if(isWater(pd->entity.level, pd->entity.x>>4, pd->entity.y>>4)) ++pd->entity.p.swimTimer; + if(pd->entity.p.attackTimer > 0) --pd->entity.p.attackTimer; + + //TODO - maybe move to own function + //Update Minimap + int xp; + int yp; + for(xp = (pd->entity.x>>4)-5; xp<(pd->entity.x>>4)+5; ++xp) { + for(yp = (pd->entity.y>>4)-5; yp<(pd->entity.y>>4)+5; ++yp) { + if(xp>=0 && xp<128 && yp>=0 && yp<128) { + if(!getMinimapVisible(pd, pd->entity.level, xp, yp)) { + setMinimapVisible(pd, pd->entity.level, xp, yp, true); + } + } + } + } +} + +void playerSetActiveItem(PlayerData *pd, Item *item) { + pd->activeItem = item; + if(pd->activeItem->id > 27 && pd->activeItem->id < 51) pd->entity.p.isCarrying = true; + else pd->entity.p.isCarrying = false; +} + +bool playerUseEnergy(PlayerData *pd, int amount) { + if(TESTGODMODE) return true; + if(amount > pd->entity.p.stamina) return false; + pd->entity.p.stamina -= amount; + return true; +} + +void playerHeal(PlayerData *pd, int amount) { + pd->entity.p.health += amount; + if(pd->entity.p.health > 10) pd->entity.p.health = 10; + char healText[11]; + sprintf(healText, "%d", amount); + addEntityToList(newTextParticleEntity(healText,0xFF00FF00, pd->entity.x, pd->entity.y, pd->entity.level), &eManager); +} + +void playerSpawn(PlayerData *pd) { + while(true){ + int rx = rand()%128; + int ry = rand()%128; + if(getTile(pd->entity.level, rx, ry) == TILE_GRASS){ + pd->entity.x = (rx << 4) + 8; + pd->entity.y = (ry << 4) + 8; + pd->isSpawned = true; + break; + } + } +} diff --git a/source/Player.h b/source/Player.h new file mode 100644 index 0000000..db05f1d --- /dev/null +++ b/source/Player.h @@ -0,0 +1,96 @@ +#pragma once + +#include "Input.h" +#include "Entity.h" +#include "QuestsData.h" +#include "Crafting.h" + +#define MAX_PLAYERS 8 +#define MAX_INPUT_BUFFER 3 + +#define PLAYER_SPRITE_HEAD_COUNT 4 +#define PLAYER_SPRITE_EYES_COUNT 5 +#define PLAYER_SPRITE_BODY_COUNT 6 +#define PLAYER_SPRITE_ARMS_COUNT 6 +#define PLAYER_SPRITE_LEGS_COUNT 4 + + +typedef struct _plrsp { + bool choosen; + + u8 legs; + u8 body; + u8 arms; + u8 head; + u8 eyes; +} PlayerSprite; + +typedef struct _plrd { + //for identification in save data and sync game start + u32 id; + bool idSet; + bool ready; + + //input/multiplayer/synchronization + Inputs inputs; + Inputs nextInputs[MAX_INPUT_BUFFER]; + bool nextTurnReady[MAX_INPUT_BUFFER]; + + // + bool isSpawned; + u8 minimapData[128*128]; + + int score; + QuestlineManager questManager; + + Entity entity; + Inventory inventory; + Item *activeItem; + + PlayerSprite sprite; + + //menu data + u8 ingameMenu; + s8 ingameMenuSelection; + s16 ingameMenuInvSel; + s16 ingameMenuInvSelOther; + bool ingameMenuAreYouSure; + bool ingameMenuAreYouSureSave; + s16 ingameMenuTimer; + NPC_MenuData npcMenuData; + + RecipeManager currentRecipes; + char *currentCraftTitle; + Entity *curChestEntity; + s8 curChestEntityR; + + bool mapShouldRender; + u8 mapZoomLevel; + s16 mapScrollX; + s16 mapScrollY; + char mapText[32]; + + s16 touchLastX; + s16 touchLastY; + bool touchIsDraggingMap; + bool touchIsChangingSize; +} PlayerData; + +PlayerData players[MAX_PLAYERS]; +int playerCount; +int playerLocalID; + +void initPlayers(); +void freePlayers(); + +void initPlayer(PlayerData *pd); +void freePlayer(PlayerData *pd); + +PlayerData* getNearestPlayer(s8 level, s16 x, s16 y); +PlayerData* getLocalPlayer(); + +void tickPlayer(PlayerData *pd, bool inmenu); +void playerSetActiveItem(PlayerData *pd, Item * item); +bool playerUseEnergy(PlayerData *pd, int amount); +void playerHeal(PlayerData *pd, int amount); +void playerSpawn(PlayerData *pd); diff --git a/source/Quests.c b/source/Quests.c index a34eeaf..8e5af1b 100644 --- a/source/Quests.c +++ b/source/Quests.c @@ -1,27 +1,9 @@ #include "Quests.h" -u8 currentNPC; +#include "Globals.h" +#include "Render.h" -int currentNPCMenu; -int currentNPCVal; - -int currentTalkSel; -bool currentTalkDone; -int currentTalkOptions; -char * currentTalkOption0; -char * currentTalkOption1; -char * currentTalkOption2; -char * currentTalk0; -char * currentTalk1; -char * currentTalk2; -char * currentTalk3; -char * currentTalk4; -char * currentTalk5; - -void initQuests() { - questManager.size = 2; - questManager.questlines = (Questline*)malloc(sizeof(Questline) * (questManager.size)); - +void initTrades() { priestTrades.size = 5; priestTrades.recipes = (Recipe*)malloc(sizeof(Recipe) * (priestTrades.size)); priestTrades.recipes[0] = defineRecipe(ITEM_DUNGEON_KEY,1,1,ITEM_MAGIC_DUST,2); @@ -47,326 +29,364 @@ void initQuests() { //TODO: Trade Dragon Scales for something really nice } -void resetQuests() { - int i; - for(i=0; iquestlines!=NULL) { + freeQuests(questManager); + } - currentTalkSel = 0; - currentTalkDone = false; - currentTalkOptions = 1; - currentTalkOption0 = "Bye"; - currentTalkOption1 = ""; - currentTalkOption2 = ""; - currentTalk0 = ""; - currentTalk1 = ""; - currentTalk2 = ""; - currentTalk3 = ""; - currentTalk4 = ""; - currentTalk5 = ""; + questManager->size = 2; + questManager->questlines = (Questline*)malloc(sizeof(Questline) * (questManager->size)); +} + +void resetQuests(QuestlineManager *questManager) { + int i; + for(i=0; isize; ++i) { + questManager->questlines[i].currentQuest = 0; + questManager->questlines[i].currentQuestDone = false; + } +} + +void freeQuests(QuestlineManager *questManager) { + free(questManager->questlines); + questManager->questlines = NULL; +} + +void resetNPCMenuData(NPC_MenuData *data) { + data->currentNPC = 0; + data->currentNPCMenu = 0; + data->currentNPCVal = 0; + + data->currentTalkSel = 0; + data->currentTalkDone = false; + data->currentTalkOptions = 0; + + data->currentTalkOption0 = ""; + data->currentTalkOption1 = ""; + data->currentTalkOption2 = ""; + data->currentTalk0 = ""; + data->currentTalk1 = ""; + data->currentTalk2 = ""; + data->currentTalk3 = ""; + data->currentTalk4 = ""; + data->currentTalk5 = ""; +} + +void openNPCMenu(PlayerData *pd, int npc) { + pd->ingameMenu = MENU_NPC; + + NPC_MenuData *data = &(pd->npcMenuData); + QuestlineManager *questManager = &(pd->questManager); + + data->currentNPC = npc; + data->currentNPCVal = 0; + data->currentNPCMenu = NPC_MENU_TALK; + + data->currentTalkSel = 0; + data->currentTalkDone = false; + data->currentTalkOptions = 1; + data->currentTalkOption0 = "Bye"; + data->currentTalkOption1 = ""; + data->currentTalkOption2 = ""; + data->currentTalk0 = ""; + data->currentTalk1 = ""; + data->currentTalk2 = ""; + data->currentTalk3 = ""; + data->currentTalk4 = ""; + data->currentTalk5 = ""; //TODO: Handle upon currentNPC as well as the fitting quest progress - switch(currentNPC) { + switch(data->currentNPC) { case NPC_GIRL: + data->currentTalkOptions = 1; + data->currentTalkOption0 = "..."; + data->currentTalk0 = "Hello?"; + data->currentTalk1 = "I have a feeling of having"; + data->currentTalk2 = "forgotten something very"; + data->currentTalk3 = "important."; + data->currentTalk4 = "Hopefully I will remember"; + data->currentTalk5 = "it soon..."; break; case NPC_PRIEST: - currentTalkOptions = 3; - currentTalkOption1 = "Trade"; - currentTalkOption2 = "Why are you so few?"; + data->currentTalkOptions = 3; + data->currentTalkOption1 = "Trade"; + data->currentTalkOption2 = "Why are you so few?"; - currentTalk0 = "Welcome to our small village"; - currentTalk1 = "I am the leader of our group."; - currentTalk2 = "If you have anything usefull"; - currentTalk3 = "for us I might be able to"; - currentTalk4 = "provide something nice in"; - currentTalk5 = "exchange."; + data->currentTalk0 = "Welcome to our small village"; + data->currentTalk1 = "I am the leader of our group."; + data->currentTalk2 = "If you have anything usefull"; + data->currentTalk3 = "for us I might be able to"; + data->currentTalk4 = "provide something nice in"; + data->currentTalk5 = "exchange."; break; case NPC_FARMER: - currentTalkOptions = 2; - currentTalkOption0 = "Maybe some other time"; - currentTalkOption1 = "Trade"; + data->currentTalkOptions = 2; + data->currentTalkOption0 = "Maybe some other time"; + data->currentTalkOption1 = "Trade"; - currentTalk0 = "Hello friend!"; - currentTalk1 = "Nice seeing somebody else"; - currentTalk2 = "visit my little farm."; - currentTalk3 = "Interested in buying some"; - currentTalk4 = "fresh farm goods?"; - currentTalk5 = ""; + data->currentTalk0 = "Hello friend!"; + data->currentTalk1 = "Nice seeing somebody else"; + data->currentTalk2 = "visit my little farm."; + data->currentTalk3 = "Interested in buying some"; + data->currentTalk4 = "fresh farm goods?"; + data->currentTalk5 = ""; break; case NPC_LIBRARIAN: - currentTalkOptions = 2; - currentTalkOption0 = "Nothing"; - currentTalkOption1 = "What are you doing here?"; - if(questManager.questlines[1].currentQuest==1) { - currentTalkOptions = 3; - currentTalkOption2 = "Dwarvish language"; + data->currentTalkOptions = 2; + data->currentTalkOption0 = "Nothing"; + data->currentTalkOption1 = "What are you doing here?"; + if(questManager->questlines[1].currentQuest==1) { + data->currentTalkOptions = 3; + data->currentTalkOption2 = "Dwarvish language"; } - currentTalk0 = "Oh hello?"; - currentTalk1 = "You must be quite brave"; - currentTalk2 = "or stupid to be walking"; - currentTalk3 = "around in this dungeon."; - currentTalk4 = ""; - currentTalk5 = "How can I help you?"; + data->currentTalk0 = "Oh hello?"; + data->currentTalk1 = "You must be quite brave"; + data->currentTalk2 = "or stupid to be walking"; + data->currentTalk3 = "around in this dungeon."; + data->currentTalk4 = ""; + data->currentTalk5 = "How can I help you?"; break; case NPC_DWARF: - if(questManager.questlines[1].currentQuest<=1) { - questManager.questlines[1].currentQuest = 1; + if(questManager->questlines[1].currentQuest<=1) { + questManager->questlines[1].currentQuest = 1; - currentTalkOptions = 1; - currentTalkOption0 = "?"; + data->currentTalkOptions = 1; + data->currentTalkOption0 = "?"; - currentTalk0 = "Dwo neal bet reck da lo"; - currentTalk1 = "dhum don lir lugn at el"; - currentTalk2 = "nur tor erno ur yo trad"; - currentTalk3 = "thra so tir kho ukk tin"; - currentTalk4 = "hel dro ic"; - currentTalk5 = ""; + data->currentTalk0 = "Dwo neal bet reck da lo"; + data->currentTalk1 = "dhum don lir lugn at el"; + data->currentTalk2 = "nur tor erno ur yo trad"; + data->currentTalk3 = "thra so tir kho ukk tin"; + data->currentTalk4 = "hel dro ic"; + data->currentTalk5 = ""; //TODO: set to 2 once translation book has been bought from librarian(can only be done once it is 1, so the dwarf has been found once) - } else if(questManager.questlines[1].currentQuest==2) { - currentTalkOptions = 2; - currentTalkOption0 = "Not really"; - currentTalkOption1 = "Trade"; + } else if(questManager->questlines[1].currentQuest==2) { + data->currentTalkOptions = 2; + data->currentTalkOption0 = "Not really"; + data->currentTalkOption1 = "Trade"; - currentTalk0 = "How are ya?"; - currentTalk1 = "Pretty unusal meeting a"; - currentTalk2 = "human down here."; - currentTalk3 = ""; - currentTalk4 = "have something valuable"; - currentTalk5 = "to trade?"; + data->currentTalk0 = "How are ya?"; + data->currentTalk1 = "Pretty unusal meeting a"; + data->currentTalk2 = "human down here."; + data->currentTalk3 = ""; + data->currentTalk4 = "have something valuable"; + data->currentTalk5 = "to trade?"; } break; } } -void tickTalkMenu() { - if (k_menu.clicked || k_decline.clicked) currentMenu = MENU_NONE; +void tickTalkMenu(PlayerData *pd, NPC_MenuData *data) { + if (pd->inputs.k_menu.clicked || pd->inputs.k_decline.clicked) pd->ingameMenu = MENU_NONE; - if (k_up.clicked){ ++currentTalkSel; if(currentTalkSel >= currentTalkOptions) currentTalkSel=0;} - if (k_down.clicked){ --currentTalkSel; if(currentTalkSel < 0) currentTalkSel=currentTalkOptions-1;} + if (pd->inputs.k_up.clicked){ ++data->currentTalkSel; if(data->currentTalkSel >= data->currentTalkOptions) data->currentTalkSel=0;} + if (pd->inputs.k_down.clicked){ --data->currentTalkSel; if(data->currentTalkSel < 0) data->currentTalkSel=data->currentTalkOptions-1;} - if(k_accept.clicked){ - currentTalkDone = true; + if(pd->inputs.k_accept.clicked){ + data->currentTalkDone = true; } } -void tickNPCMenu() { +void tickNPCMenu(PlayerData *pd) { + NPC_MenuData *data = &(pd->npcMenuData); + QuestlineManager *questManager = &(pd->questManager); + //TODO: Handle upon currentNPC as well as the fitting quest progress - if(currentNPCMenu==NPC_MENU_TALK) tickTalkMenu(); + if(data->currentNPCMenu==NPC_MENU_TALK) tickTalkMenu(pd, data); - switch(currentNPC) { + switch(data->currentNPC) { case NPC_GIRL: - + if(data->currentNPCMenu==NPC_MENU_TALK && data->currentTalkDone) { + if(data->currentNPCVal==0) pd->ingameMenu = MENU_NONE; + } break; case NPC_PRIEST: - if(currentNPCMenu==NPC_MENU_TALK && currentTalkDone) { - if(currentNPCVal==0) { - if(currentTalkSel==0) { - currentMenu = MENU_NONE; - } else if(currentTalkSel==1) { - currentRecipes = &priestTrades; - currentCraftTitle = "Trading"; - currentMenu = MENU_CRAFTING; - checkCanCraftRecipes(currentRecipes, player.p.inv); - sortRecipes(currentRecipes); - } else if(currentTalkSel==2) { - currentNPCVal = 1; + if(data->currentNPCMenu==NPC_MENU_TALK && data->currentTalkDone) { + if(data->currentNPCVal==0) { + if(data->currentTalkSel==0) { + pd->ingameMenu = MENU_NONE; + } else if(data->currentTalkSel==1) { + openCraftingMenu(pd, &priestTrades, "Trading"); + } else if(data->currentTalkSel==2) { + data->currentNPCVal = 1; - currentTalkSel = 0; - currentTalkDone = false; - currentTalkOptions = 1; - currentTalkOption0 = "..."; + data->currentTalkSel = 0; + data->currentTalkDone = false; + data->currentTalkOptions = 1; + data->currentTalkOption0 = "..."; - currentTalk0 = "For quite some time now this"; - currentTalk1 = "village has been tyrannized"; - currentTalk2 = "by a powerfull Air Wizard."; - currentTalk3 = "We are the only ones who"; - currentTalk4 = "still have not given up"; - currentTalk5 = "our old homes."; + data->currentTalk0 = "For quite some time now this"; + data->currentTalk1 = "village has been tyrannized"; + data->currentTalk2 = "by a powerfull Air Wizard."; + data->currentTalk3 = "We are the only ones who"; + data->currentTalk4 = "still have not given up"; + data->currentTalk5 = "our old homes."; } - } else if(currentNPCVal==1) { - if(currentTalkSel==0) { - currentNPCVal = 2; + } else if(data->currentNPCVal==1) { + if(data->currentTalkSel==0) { + data->currentNPCVal = 2; - currentTalkSel = 0; - currentTalkDone = false; - currentTalkOptions = 1; - currentTalkOption0 = "..."; + data->currentTalkSel = 0; + data->currentTalkDone = false; + data->currentTalkOptions = 1; + data->currentTalkOption0 = "..."; - currentTalk0 = "Most of the time the wizard"; - currentTalk1 = "hides somewhere in the"; - currentTalk2 = "cloudes. They can only be"; - currentTalk3 = "reached by a stairwell"; - currentTalk4 = "protected by an almost"; - currentTalk5 = "undestroyable stone barrier."; + data->currentTalk0 = "Most of the time the wizard"; + data->currentTalk1 = "hides somewhere in the"; + data->currentTalk2 = "cloudes. They can only be"; + data->currentTalk3 = "reached by a stairwell"; + data->currentTalk4 = "protected by an almost"; + data->currentTalk5 = "undestroyable stone barrier."; } - } else if(currentNPCVal==2) { - if(currentTalkSel==0) { - currentNPCVal = 3; + } else if(data->currentNPCVal==2) { + if(data->currentTalkSel==0) { + data->currentNPCVal = 3; - currentTalkSel = 0; - currentTalkDone = false; - currentTalkOptions = 1; - currentTalkOption0 = "..."; + data->currentTalkSel = 0; + data->currentTalkDone = false; + data->currentTalkOptions = 1; + data->currentTalkOption0 = "..."; - currentTalk0 = "I am guessing you would "; - currentTalk1 = "need tools atleast as"; - currentTalk2 = "strong as diamonds to be"; - currentTalk3 = "able to destroy it."; - currentTalk4 = ""; - currentTalk5 = ""; + data->currentTalk0 = "I am guessing you would "; + data->currentTalk1 = "need tools atleast as"; + data->currentTalk2 = "strong as diamonds to be"; + data->currentTalk3 = "able to destroy it."; + data->currentTalk4 = ""; + data->currentTalk5 = ""; } - } else if(currentNPCVal==3) { - if(currentTalkSel==0) { - currentNPCVal = 4; + } else if(data->currentNPCVal==3) { + if(data->currentTalkSel==0) { + data->currentNPCVal = 4; - currentTalkSel = 0; - currentTalkDone = false; - currentTalkOptions = 2; - currentTalkOption0 = "Let me do it!"; - currentTalkOption1 = "I am not sure"; + data->currentTalkSel = 0; + data->currentTalkDone = false; + data->currentTalkOptions = 2; + data->currentTalkOption0 = "Let me do it!"; + data->currentTalkOption1 = "I am not sure"; - currentTalk0 = "I am willing to give an"; - currentTalk1 = "ancient artifact passed"; - currentTalk2 = "down over generations to"; - currentTalk3 = "anybody who manages to"; - currentTalk4 = "chase the wizard away and"; - currentTalk5 = "come back with proof."; + data->currentTalk0 = "I am willing to give an"; + data->currentTalk1 = "ancient artifact passed"; + data->currentTalk2 = "down over generations to"; + data->currentTalk3 = "anybody who manages to"; + data->currentTalk4 = "chase the wizard away and"; + data->currentTalk5 = "come back with proof."; } - } else if(currentNPCVal==4) { - currentMenu = MENU_NONE; + } else if(data->currentNPCVal==4) { + pd->ingameMenu = MENU_NONE; } } break; case NPC_FARMER: - if(currentNPCMenu==NPC_MENU_TALK && currentTalkDone) { - if(currentNPCVal==0) { - if(currentTalkSel==0) { - currentMenu = MENU_NONE; - } else if(currentTalkSel==1) { - currentRecipes = &farmerTrades; - currentCraftTitle = "Trading"; - currentMenu = MENU_CRAFTING; - checkCanCraftRecipes(currentRecipes, player.p.inv); - sortRecipes(currentRecipes); + if(data->currentNPCMenu==NPC_MENU_TALK && data->currentTalkDone) { + if(data->currentNPCVal==0) { + if(data->currentTalkSel==0) { + pd->ingameMenu = MENU_NONE; + } else if(data->currentTalkSel==1) { + openCraftingMenu(pd, &farmerTrades, "Trading"); } } } break; case NPC_LIBRARIAN: - if(currentNPCMenu==NPC_MENU_TALK && currentTalkDone) { - if(currentNPCVal==0) { - if(currentTalkSel==0) { - currentMenu = MENU_NONE; - } else if(currentTalkSel==1) { - currentNPCVal = 2; + if(data->currentNPCMenu==NPC_MENU_TALK && data->currentTalkDone) { + if(data->currentNPCVal==0) { + if(data->currentTalkSel==0) { + pd->ingameMenu = MENU_NONE; + } else if(data->currentTalkSel==1) { + data->currentNPCVal = 2; - currentTalkSel = 0; - currentTalkDone = false; - currentTalkOptions = 1; - currentTalkOption0 = "Ok"; + data->currentTalkSel = 0; + data->currentTalkDone = false; + data->currentTalkOptions = 1; + data->currentTalkOption0 = "Ok"; - currentTalk0 = "The books in this dungeon"; - currentTalk1 = "house secrets that cannot be"; - currentTalk2 = "found anywhere else in the"; - currentTalk3 = "world. So I came to study"; - currentTalk4 = "them. Most are written in"; - currentTalk5 = "an ancient language."; - } else if(currentTalkSel==2) { - currentNPCVal = 1; + data->currentTalk0 = "The books in this dungeon"; + data->currentTalk1 = "house secrets that cannot be"; + data->currentTalk2 = "found anywhere else in the"; + data->currentTalk3 = "world. So I came to study"; + data->currentTalk4 = "them. Most are written in"; + data->currentTalk5 = "an ancient language."; + } else if(data->currentTalkSel==2) { + data->currentNPCVal = 1; - currentTalkSel = 0; - currentTalkDone = false; - currentTalkOptions = 2; - currentTalkOption0 = "I need to think about it"; - currentTalkOption1 = "Here they are"; + data->currentTalkSel = 0; + data->currentTalkDone = false; + data->currentTalkOptions = 2; + data->currentTalkOption0 = "I need to think about it"; + data->currentTalkOption1 = "Here they are"; - currentTalk0 = "So you have met a dwarf but"; - currentTalk1 = "had a little communication"; - currentTalk2 = "problem? I do have a dwarvish"; - currentTalk3 = "translation book but I havent"; - currentTalk4 = "read it yet. For 10 Gold bars"; - currentTalk5 = "I will give it to you anyway."; + data->currentTalk0 = "So you have met a dwarf but"; + data->currentTalk1 = "had a little communication"; + data->currentTalk2 = "problem? I do have a dwarvish"; + data->currentTalk3 = "translation book but I havent"; + data->currentTalk4 = "read it yet. For 10 Gold bars"; + data->currentTalk5 = "I will give it to you anyway."; } - } else if(currentNPCVal==1) { - if(currentTalkSel==0) { - currentMenu = MENU_NONE; - } else if(currentTalkSel==1) { - currentNPCVal = 2; + } else if(data->currentNPCVal==1) { + if(data->currentTalkSel==0) { + pd->ingameMenu = MENU_NONE; + } else if(data->currentTalkSel==1) { + data->currentNPCVal = 2; - currentTalkSel = 0; - currentTalkDone = false; - currentTalkOptions = 1; - currentTalkOption0 = ""; + data->currentTalkSel = 0; + data->currentTalkDone = false; + data->currentTalkOptions = 1; + data->currentTalkOption0 = ""; - if(countItemInv(ITEM_GOLDINGOT,0,player.p.inv)>=10) { + if(countItemInv(ITEM_GOLDINGOT, 0, &(pd->inventory))>=10) { //remove gold from player inventory //TODO: Maybe I should make a generic substract items method sometime - Item* item = getItemFromInventory(ITEM_GOLDINGOT, player.p.inv); + Item* item = getItemFromInventory(ITEM_GOLDINGOT, &(pd->inventory)); item->countLevel -= 10; - if(item->countLevel < 1) removeItemFromInventory(item->slotNum, player.p.inv); + if(item->countLevel < 1) removeItemFromInventory(item->slotNum, &(pd->inventory)); - questManager.questlines[1].currentQuest = 2; + questManager->questlines[1].currentQuest = 2; - currentTalk0 = "Thank you these will be"; - currentTalk1 = "really helpfull."; - currentTalk2 = "Here take this book with"; - currentTalk3 = "it you should be able to"; - currentTalk4 = "easily understand anything"; - currentTalk5 = "a dwarf can say."; + data->currentTalk0 = "Thank you these will be"; + data->currentTalk1 = "really helpfull."; + data->currentTalk2 = "Here take this book with"; + data->currentTalk3 = "it you should be able to"; + data->currentTalk4 = "easily understand anything"; + data->currentTalk5 = "a dwarf can say."; - currentTalkOption0 = "Thanks"; + data->currentTalkOption0 = "Thanks"; } else { - currentTalk0 = "You do not seem to have"; - currentTalk1 = "enough Gold Bars with you."; - currentTalk2 = ""; - currentTalk3 = "Ask again when you have"; - currentTalk4 = "collected the 10 Bars."; - currentTalk5 = ""; + data->currentTalk0 = "You do not seem to have"; + data->currentTalk1 = "enough Gold Bars with you."; + data->currentTalk2 = ""; + data->currentTalk3 = "Ask again when you have"; + data->currentTalk4 = "collected the 10 Bars."; + data->currentTalk5 = ""; - currentTalkOption0 = "Ok"; + data->currentTalkOption0 = "Ok"; } } - } else if(currentNPCVal==2) { - if(currentTalkSel==0) { - currentMenu = MENU_NONE; + } else if(data->currentNPCVal==2) { + if(data->currentTalkSel==0) { + pd->ingameMenu = MENU_NONE; } } } break; case NPC_DWARF: - if(questManager.questlines[1].currentQuest<=1) { - if(currentNPCMenu==NPC_MENU_TALK && currentTalkDone) { - if(currentNPCVal==0) currentMenu = MENU_NONE; + if(questManager->questlines[1].currentQuest<=1) { + if(data->currentNPCMenu==NPC_MENU_TALK && data->currentTalkDone) { + if(data->currentNPCVal==0) pd->ingameMenu = MENU_NONE; } - } else if(questManager.questlines[1].currentQuest==2) { - if(currentNPCMenu==NPC_MENU_TALK && currentTalkDone) { - if(currentTalkSel==0) { - currentMenu = MENU_NONE; - } else if(currentTalkSel==1) { - currentRecipes = &dwarfTrades; - currentCraftTitle = "Trading"; - currentMenu = MENU_CRAFTING; - checkCanCraftRecipes(currentRecipes, player.p.inv); - sortRecipes(currentRecipes); + } else if(questManager->questlines[1].currentQuest==2) { + if(data->currentNPCMenu==NPC_MENU_TALK && data->currentTalkDone) { + if(data->currentTalkSel==0) { + pd->ingameMenu = MENU_NONE; + } else if(data->currentTalkSel==1) { + openCraftingMenu(pd, &dwarfTrades, "Trading"); } } } @@ -374,44 +394,44 @@ void tickNPCMenu() { } } -void renderTalkMenu(char * name) { +void renderTalkMenu(NPC_MenuData *data, char * name) { renderFrame(1,1,24,14,0xFFFF1010); drawTextColor(name,24+1,14+1,0xFF000000); drawTextColor(name,24,14,0xFF6FE2E2); - drawText(currentTalk0, 32, 32); - drawText(currentTalk1, 32, 48); - drawText(currentTalk2, 32, 64); - drawText(currentTalk3, 32, 80); - drawText(currentTalk4, 32, 96); - drawText(currentTalk5, 32, 112); + drawText(data->currentTalk0, 32, 32); + drawText(data->currentTalk1, 32, 48); + drawText(data->currentTalk2, 32, 64); + drawText(data->currentTalk3, 32, 80); + drawText(data->currentTalk4, 32, 96); + drawText(data->currentTalk5, 32, 112); - if(currentTalkOptions>=3) drawText(currentTalkOption2, 64, 147); - if(currentTalkOptions>=2) drawText(currentTalkOption1, 64, 171); - if(currentTalkOptions>=1) drawText(currentTalkOption0, 64, 195); + if(data->currentTalkOptions>=3) drawText(data->currentTalkOption2, 64, 147); + if(data->currentTalkOptions>=2) drawText(data->currentTalkOption1, 64, 171); + if(data->currentTalkOptions>=1) drawText(data->currentTalkOption0, 64, 195); - if(currentTalkOptions>=3 && currentTalkSel==2) drawText(">", 48, 147); - if(currentTalkOptions>=2 && currentTalkSel==1) drawText(">", 48, 171); - if(currentTalkOptions>=1 && currentTalkSel==0) drawText(">", 48, 195); + if(data->currentTalkOptions>=3 && data->currentTalkSel==2) drawText(">", 48, 147); + if(data->currentTalkOptions>=2 && data->currentTalkSel==1) drawText(">", 48, 171); + if(data->currentTalkOptions>=1 && data->currentTalkSel==0) drawText(">", 48, 195); } -void renderNPCMenu(int xscr, int yscr) { +void renderNPCMenu(NPC_MenuData *data) { //TODO: Handle upon currentNPC as well as the fitting quest progress - switch(currentNPC) { + switch(data->currentNPC) { case NPC_GIRL: - if(currentNPCMenu==NPC_MENU_TALK) renderTalkMenu("TODO"); + if(data->currentNPCMenu==NPC_MENU_TALK) renderTalkMenu(data, "Maria"); break; case NPC_PRIEST: - if(currentNPCMenu==NPC_MENU_TALK) renderTalkMenu("Priest Brom"); + if(data->currentNPCMenu==NPC_MENU_TALK) renderTalkMenu(data, "Priest Brom"); break; case NPC_FARMER: - if(currentNPCMenu==NPC_MENU_TALK) renderTalkMenu("Farmer Garrow"); + if(data->currentNPCMenu==NPC_MENU_TALK) renderTalkMenu(data, "Farmer Garrow"); break; case NPC_LIBRARIAN: - if(currentNPCMenu==NPC_MENU_TALK) renderTalkMenu("Librarian Ajihad"); + if(data->currentNPCMenu==NPC_MENU_TALK) renderTalkMenu(data, "Librarian Ajihad"); break; case NPC_DWARF: - if(currentNPCMenu==NPC_MENU_TALK) renderTalkMenu("Dwarf Orik"); + if(data->currentNPCMenu==NPC_MENU_TALK) renderTalkMenu(data, "Dwarf Orik"); break; } } diff --git a/source/Quests.h b/source/Quests.h index 19b78b9..05a581e 100644 --- a/source/Quests.h +++ b/source/Quests.h @@ -1,32 +1,25 @@ #pragma once + #include #include -#include "render.h" +#include "QuestsData.h" +#include "Player.h" #include "Crafting.h" #define NPC_MENU_TALK 0 -typedef struct { - int currentQuest; - bool currentQuestDone; -} Questline; - -typedef struct { - int size; - Questline * questlines; -} QuestlineManager; - -QuestlineManager questManager; - RecipeManager priestTrades; RecipeManager farmerTrades; RecipeManager dwarfTrades; -void initQuests(); -void resetQuests(); -void freeQuests(); +void initTrades(); +void freeTrades(); -void openNPCMenu(int npc); +void initQuests(QuestlineManager *questManager); +void resetQuests(QuestlineManager *questManager); +void freeQuests(QuestlineManager *questManager); -void renderNPCMenu(int xscr, int yscr); -void tickNPCMenu(); +void resetNPCMenuData(NPC_MenuData *data); +void openNPCMenu(PlayerData *pd, int npc); +void tickNPCMenu(PlayerData *pd); +void renderNPCMenu(NPC_MenuData *data); diff --git a/source/QuestsData.h b/source/QuestsData.h new file mode 100644 index 0000000..6972aed --- /dev/null +++ b/source/QuestsData.h @@ -0,0 +1,35 @@ +#pragma once + +#include <3ds.h> + +typedef struct _questline { + int currentQuest; + bool currentQuestDone; +} Questline; + +typedef struct _questlineManager { + int size; + Questline * questlines; +} QuestlineManager; + +typedef struct _npcMenuData { + u8 currentNPC; + + int currentNPCMenu; + int currentNPCVal; + + int currentTalkSel; + bool currentTalkDone; + int currentTalkOptions; + char * currentTalkOption0; + char * currentTalkOption1; + char * currentTalkOption2; + char * currentTalk0; + char * currentTalk1; + char * currentTalk2; + char * currentTalk3; + char * currentTalk4; + char * currentTalk5; +} NPC_MenuData; + +//TODO: Actually move the data here diff --git a/source/Render.c b/source/Render.c index b06c6d0..5a190ba 100644 --- a/source/Render.c +++ b/source/Render.c @@ -1,5 +1,7 @@ #include "Render.h" +extern u32 syncTickCount; + void render(s32 xp, s32 yp, u32 xTile, u32 yTile, u8 bits) { xp -= offsetX; yp -= offsetY; @@ -158,10 +160,18 @@ void render32(s32 xp, s32 yp, u32 xTile, u32 yTile, u8 bits) { scaleX, scaleY); } +int playerScale = 2; +void renderp(s32 xp, s32 yp, u32 xTile, u32 yTile) { + xp -= offsetX; + yp -= offsetY; + int scaleX = playerScale, scaleY = playerScale; + sf2d_draw_texture_part_scale(playerSprites, xp << 1, yp << 1, xTile, yTile, 16, 16, + scaleX, scaleY); +} + void renderTitle(int x, int y) { - sf2d_draw_texture_part_scale(icons, (x - 26) << 1, y << 1, 0, 240, 104, 16, - 2.0, 2.0); // MINICRAFT - sf2d_draw_texture_part(icons, x + 48, y + 52, 104, 240, 152, 16); // 3DS HOMEBREW EDITION + sf2d_draw_texture_part_scale(icons, (x - 26) << 1, y << 1, 0, 240, 104, 16, 2.0, 2.0); // MINICRAFT + sf2d_draw_texture_part(icons, x + 48, y + 44, 104, 240, 152, 16); // 3DS HOMEBREW EDITION } void renderButtonIcon(u32 keyIcon, int x, int y, float scale) { @@ -294,24 +304,23 @@ void freeLightBakes() { sf2d_free_texture(glowwormBigLightBake); } -void renderLightsToStencil(bool force, bool invert, bool rplayer) { - if (force || (currentLevel > 1 && currentLevel != 5)) { +void renderLightsToStencil(PlayerData *pd, bool force, bool invert, bool rplayer) { + if (force || (pd->entity.level > 1 && pd->entity.level != 5)) { C3D_DepthTest(true, GPU_NEVER, 0); C3D_StencilTest(true, GPU_NEVER, 1, 0xFF, 0xFF); C3D_StencilOp(GPU_STENCIL_REPLACE, GPU_STENCIL_KEEP, GPU_STENCIL_KEEP); C3D_AlphaTest(true, GPU_GREATER, 0); - - if(player.p.activeItem->id == ITEM_LANTERN) renderLight(player.x, player.y, lanternLightBake); - else if(rplayer) renderLight(player.x, player.y, playerLightBake); + if(pd->activeItem->id == ITEM_LANTERN) renderLight(pd->entity.x, pd->entity.y, lanternLightBake); + else if(rplayer) renderLight(pd->entity.x, pd->entity.y, playerLightBake); int i; - for (i = 0; i < eManager.lastSlot[currentLevel]; ++i) { - Entity e = eManager.entities[currentLevel][i]; + for (i = 0; i < eManager.lastSlot[pd->entity.level]; ++i) { + Entity e = eManager.entities[pd->entity.level][i]; if (e.type == ENTITY_FURNITURE) { - if (e.entityFurniture.itemID == ITEM_LANTERN && e.x > player.x - 160 && e.y > player.y - 125 && e.x < player.x + 160 && e.y < player.y + 125) + if (e.entityFurniture.itemID == ITEM_LANTERN && e.x > pd->entity.x - 160 && e.y > pd->entity.y - 125 && e.x < pd->entity.x + 160 && e.y < pd->entity.y + 125) renderLight(e.x, e.y, lanternLightBake); - } else if(e.type == ENTITY_GLOWWORM && e.x > player.x - 160 && e.y > player.y - 125 && e.x < player.x + 160 && e.y < player.y + 125) { //TODO could be made smaller becuase of smaller light radius + } else if(e.type == ENTITY_GLOWWORM && e.x > pd->entity.x - 160 && e.y > pd->entity.y - 125 && e.x < pd->entity.x + 160 && e.y < pd->entity.y + 125) { //TODO could be made smaller becuase of smaller light radius if(rand()%10==0) continue; else if(rand()%100==0) renderLight(e.x+20, e.y-20, glowwormBigLightBake); else renderLight(e.x+8, e.y-8, glowwormLightBake); @@ -325,9 +334,9 @@ void renderLightsToStencil(bool force, bool invert, bool rplayer) { //TODO: Even this is not performant enough for old 3DS, when there is a lot of lava on screen for (x = xo-2; x <= 13 + xo+2; ++x) { for (y = yo-2; y <= 8 + yo+2; ++y) { - if(getTile(x, y) == TILE_LAVA) { + if(getTile(pd->entity.level, x, y) == TILE_LAVA) { //experimental "speedhack" - if(getTile(x+1,y)==TILE_LAVA && getTile(x-1,y)==TILE_LAVA && getTile(x,y+1)==TILE_LAVA && getTile(x,y-1)==TILE_LAVA) { + if(getTile(pd->entity.level, x+1,y)==TILE_LAVA && getTile(pd->entity.level, x-1,y)==TILE_LAVA && getTile(pd->entity.level, x,y+1)==TILE_LAVA && getTile(pd->entity.level, x,y-1)==TILE_LAVA) { if((x+y)%2 == 0) continue; } renderLight((x << 4) + 8, (y << 4) + 8, playerLightBake); @@ -348,10 +357,8 @@ void renderLightsToStencil(bool force, bool invert, bool rplayer) { } void resetStencilStuff() { - //if (currentLevel > 1) { - C3D_StencilTest(false, GPU_ALWAYS, 0x00, 0xFF, 0x00); - C3D_StencilOp(GPU_STENCIL_KEEP, GPU_STENCIL_KEEP, GPU_STENCIL_KEEP); - //} + C3D_StencilTest(false, GPU_ALWAYS, 0x00, 0xFF, 0x00); + C3D_StencilOp(GPU_STENCIL_KEEP, GPU_STENCIL_KEEP, GPU_STENCIL_KEEP); } void renderLight(int x, int y, sf2d_texture* texture) { @@ -433,60 +440,60 @@ void resetSurrTiles() { tdr = false; } -void checkSurrTiles8(int xt, int yt, int id) { - if (getTile(xt, yt - 1) == id) +void checkSurrTiles8(u8 level, int xt, int yt, int id) { + if (getTile(level, xt, yt - 1) == id) tu = true; - if (getTile(xt - 1, yt) == id) + if (getTile(level, xt - 1, yt) == id) tl = true; - if (getTile(xt + 1, yt) == id) + if (getTile(level, xt + 1, yt) == id) tr = true; - if (getTile(xt, yt + 1) == id) + if (getTile(level, xt, yt + 1) == id) td = true; - if (getTile(xt - 1, yt - 1) == id) + if (getTile(level, xt - 1, yt - 1) == id) tul = true; - if (getTile(xt + 1, yt - 1) == id) + if (getTile(level, xt + 1, yt - 1) == id) tur = true; - if (getTile(xt - 1, yt + 1) == id) + if (getTile(level, xt - 1, yt + 1) == id) tdl = true; - if (getTile(xt + 1, yt + 1) == id) + if (getTile(level, xt + 1, yt + 1) == id) tdr = true; } -void checkSurrTiles4(int xt, int yt, int id) { - if (getTile(xt, yt - 1) == id) +void checkSurrTiles4(u8 level, int xt, int yt, int id) { + if (getTile(level, xt, yt - 1) == id) tu = true; - if (getTile(xt - 1, yt) == id) + if (getTile(level, xt - 1, yt) == id) tl = true; - if (getTile(xt + 1, yt) == id) + if (getTile(level, xt + 1, yt) == id) tr = true; - if (getTile(xt, yt + 1) == id) + if (getTile(level, xt, yt + 1) == id) td = true; } u8 tData = 0; -void renderTile(int i, int d, int x, int y) { +void renderTile(int i, int d, u8 level, int x, int y) { int age = 0; switch (i) { case TILE_GRASS: - checkSurrTiles4(x >> 4, y >> 4, TILE_GRASS); - checkSurrTiles4(x >> 4, y >> 4, TILE_TREE); - checkSurrTiles4(x >> 4, y >> 4, TILE_FLOWER); - checkSurrTiles4(x >> 4, y >> 4, TILE_SAPLING_TREE); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_GRASS); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_TREE); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_FLOWER); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_SAPLING_TREE); - if(currentLevel==1 && season==3) renderConnectedTile4(x, y, 256, 112); - else if(currentLevel==1 && season==2) renderConnectedTile4(x, y, 256, 128); + if(level==1 && worldData.season==3) renderConnectedTile4(x, y, 256, 112); + else if(level==1 && worldData.season==2) renderConnectedTile4(x, y, 256, 128); else renderConnectedTile4(x, y, 256, 0); break; case TILE_TREE: - renderTile(TILE_GRASS, 0, x, y); + renderTile(TILE_GRASS, 0, level, x, y); - checkSurrTiles8(x >> 4, y >> 4, TILE_TREE); + checkSurrTiles8(level, x >> 4, y >> 4, TILE_TREE); - if(season==2) { + if(worldData.season==2) { render(x, y, 352+((tu && tl && tul) ? 16 : 0), 96, 0); render(x+8, y, 360+((tu && tr && tur) ? 16 : 0), 96, 0); render(x, y+8, 352+((td && tl && tdl) ? 16 : 0), 104, 0); render(x+8, y+8, 360+((td && tr && tdr) ? 16 : 0), 104, 0); - } else if(season==3) { + } else if(worldData.season==3) { render(x, y, 352+((tu && tl && tul) ? 16 : 0), 112, 0); render(x+8, y, 360+((tu && tr && tur) ? 16 : 0), 112, 0); render(x, y+8, 352+((td && tl && tdl) ? 16 : 0), 120, 0); @@ -500,28 +507,28 @@ void renderTile(int i, int d, int x, int y) { break; case TILE_ROCK: - checkSurrTiles8(x >> 4, y >> 4, TILE_ROCK); - if(currentLevel>1) + checkSurrTiles8(level, x >> 4, y >> 4, TILE_ROCK); + if(level>1) renderConnectedTile8(x, y, 256, 96); else renderConnectedTile8(x, y, 336, 64); break; case TILE_HARDROCK: - checkSurrTiles8(x >> 4, y >> 4, TILE_HARDROCK); + checkSurrTiles8(level, x >> 4, y >> 4, TILE_HARDROCK); renderConnectedTile8(x, y, 416, 64); break; case TILE_DIRT: // render dots. - if (currentLevel > 1) + if (level > 1) render16(x, y, 320, 80, 0); else render16(x, y, 336, 80, 0); break; case TILE_SAND: - checkSurrTiles4(x >> 4, y >> 4, TILE_SAND); - checkSurrTiles4(x >> 4, y >> 4, TILE_CACTUS); - checkSurrTiles4(x >> 4, y >> 4, TILE_SAPLING_CACTUS); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_SAND); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_CACTUS); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_SAPLING_CACTUS); - if(currentLevel==1 && season==3) { + if(level==1 && worldData.season==3) { renderConnectedTile4(x, y, 256, 112); } else { renderConnectedTile4(x, y, 320, 0); @@ -532,43 +539,43 @@ void renderTile(int i, int d, int x, int y) { } break; case TILE_WATER: - checkSurrTiles4(x >> 4, y >> 4, TILE_WATER); - checkSurrTiles4(x >> 4, y >> 4, TILE_HOLE); - checkSurrTiles4(x >> 4, y >> 4, TILE_ICE); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_WATER); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_HOLE); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_ICE); renderConnectedTile4(x, y, 384, 0); - srand((tickCount + (x / 2 - y) * 4311) / 10); + srand((syncTickCount + (x / 2 - y) * 4311) / 10); renderDots(x, y, rand() & 3, rand() & 3, rand() & 3, rand() & 3, 288, 64); break; case TILE_LAVA: - checkSurrTiles4(x >> 4, y >> 4, TILE_LAVA); - checkSurrTiles4(x >> 4, y >> 4, TILE_HOLE); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_LAVA); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_HOLE); renderConnectedTile4(x, y, 448, 0); - srand((tickCount + (x / 2 - y) * 4311) / 10); + srand((syncTickCount + (x / 2 - y) * 4311) / 10); renderDots(x, y, rand() & 3, rand() & 3, rand() & 3, rand() & 3, 304, 64); break; case TILE_HOLE: - checkSurrTiles4(x >> 4, y >> 4, TILE_HOLE); - checkSurrTiles4(x >> 4, y >> 4, TILE_WATER); - checkSurrTiles4(x >> 4, y >> 4, TILE_LAVA); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_HOLE); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_WATER); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_LAVA); renderConnectedTile4(x, y, 256, 16); break; case TILE_CACTUS: - renderTile(TILE_SAND, 0, x, y); + renderTile(TILE_SAND, 0, level, x, y); render16(x, y, 304, 48, 0); break; case TILE_FLOWER: - renderTile(TILE_GRASS, 0, x, y); - if(currentLevel==1 && season==3) render16(x, y, 320, 112, getData(x >> 4, y >> 4)); - else render16(x, y, 320, 48, getData(x >> 4, y >> 4)); + renderTile(TILE_GRASS, 0, level, x, y); + if(level==1 && worldData.season==3) render16(x, y, 320, 112, d); + else render16(x, y, 320, 48, d); break; case TILE_STAIRS_DOWN: - if (currentLevel == 0) - renderTile(TILE_CLOUD, 0, x, y); + if (level == 0) + renderTile(TILE_CLOUD, 0, level, x, y); render16(x, y, 256, 64, 0); break; case TILE_STAIRS_UP: @@ -584,60 +591,60 @@ void renderTile(int i, int d, int x, int y) { render16(x, y, 496, 48, 0); break; case TILE_CLOUD: - checkSurrTiles4(x >> 4, y >> 4, TILE_CLOUD); - checkSurrTiles4(x >> 4, y >> 4, TILE_STAIRS_DOWN); - checkSurrTiles4(x >> 4, y >> 4, TILE_CLOUDCACTUS); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_CLOUD); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_STAIRS_DOWN); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_CLOUDCACTUS); renderConnectedTile4(x, y, 320, 16); break; case TILE_CLOUDCACTUS: - renderTile(TILE_CLOUD, 0, x, y); + renderTile(TILE_CLOUD, 0, level, x, y); render16(x, y, 496, 64, 0); break; case TILE_SAPLING_TREE: - renderTile(TILE_GRASS, 0, x, y); + renderTile(TILE_GRASS, 0, level, x, y); render16(x, y, 288, 48, 0); break; case TILE_SAPLING_CACTUS: - renderTile(TILE_SAND, 0, x, y); + renderTile(TILE_SAND, 0, level, x, y); render16(x, y, 288, 48, 0); break; case TILE_FARM: render16(x, y, 352, 48, 0); break; case TILE_WHEAT: - age = getData(x >> 4, y >> 4) / 20; + age = d / 20; if (age > 5) age = 5; render16(x, y, 368 + (age << 4), 48, 0); break; case TILE_WOOD_WALL: - checkSurrTiles4(x >> 4, y >> 4, TILE_WOOD_WALL); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_WOOD_WALL); renderConnectedTile4(x, y, 384, 16); break; case TILE_STONE_WALL: - checkSurrTiles4(x >> 4, y >> 4, TILE_STONE_WALL); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_STONE_WALL); renderConnectedTile4(x, y, 256, 80); break; case TILE_IRON_WALL: - checkSurrTiles4(x >> 4, y >> 4, TILE_IRON_WALL); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_IRON_WALL); renderConnectedTile4(x, y, 448, 16); break; case TILE_GOLD_WALL: - checkSurrTiles4(x >> 4, y >> 4, TILE_GOLD_WALL); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_GOLD_WALL); renderConnectedTile4(x, y, 256, 32); break; case TILE_GEM_WALL: - checkSurrTiles4(x >> 4, y >> 4, TILE_GEM_WALL); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_GEM_WALL); renderConnectedTile4(x, y, 320, 32); break; case TILE_DUNGEON_WALL: - checkSurrTiles8(x >> 4, y >> 4, TILE_DUNGEON_WALL); + checkSurrTiles8(level, x >> 4, y >> 4, TILE_DUNGEON_WALL); renderConnectedTile8(x, y, 384, 32); break; @@ -645,21 +652,22 @@ void renderTile(int i, int d, int x, int y) { render16(x, y, 464 + d*16, 32, 0); break; case TILE_DUNGEON_ENTRANCE: - render16(x, y, 352 + (currentLevel==5 ? 16 : 0), 80, 0); + render16(x, y, 352 + (level==5 ? 16 : 0), 80, 0); break; case TILE_MAGIC_BARRIER: - renderTile(TILE_DUNGEON_FLOOR, 0, x, y); - render16(x, y, 320, 64, getData(x >> 4, y >> 4)); + renderTile(TILE_DUNGEON_FLOOR, 0, level, x, y); + render16(x, y, 320, 64, d); //draw remaining pillar count - if((player.x - (x+8))*(player.x - (x+8)) + (player.y - (y+8))*(player.y - (y+8)) <= 24*24) { + PlayerData *lp = getLocalPlayer(); + if((lp->entity.x - (x+8))*(lp->entity.x - (x+8)) + (lp->entity.y - (y+8))*(lp->entity.y - (y+8)) <= 24*24) { x -= offsetX; y -= offsetY; int data = 0; int i = 0; - for (i = 0; i < eManager.lastSlot[currentLevel]; ++i) { - Entity * e = &eManager.entities[currentLevel][i]; + for (i = 0; i < eManager.lastSlot[level]; ++i) { + Entity * e = &eManager.entities[level][i]; if(e->type == ENTITY_MAGIC_PILLAR) { ++data; @@ -667,7 +675,7 @@ void renderTile(int i, int d, int x, int y) { } char currentCount[3]; - sprintf(currentCount,"%d", data); + sprintf(currentCount, "%d", data); drawSizedTextColor(currentCount, x+4 + 1, y+4 + 1, 2, dungeonColor[1]); drawSizedTextColor(currentCount, x+4, y+4, 2, dungeonColor[0]); @@ -675,7 +683,7 @@ void renderTile(int i, int d, int x, int y) { break; case TILE_BOOKSHELVES: - checkSurrTiles4(x >> 4, y >> 4, TILE_BOOKSHELVES); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_BOOKSHELVES); renderConnectedTile4(x, y, 384, 80 + d*16); break; @@ -683,26 +691,26 @@ void renderTile(int i, int d, int x, int y) { render16(x, y, 336, 96, 0); break; case TILE_MYCELIUM: - checkSurrTiles4(x >> 4, y >> 4, TILE_MYCELIUM); - checkSurrTiles4(x >> 4, y >> 4, TILE_MUSHROOM_BROWN); - checkSurrTiles4(x >> 4, y >> 4, TILE_MUSHROOM_RED); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_MYCELIUM); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_MUSHROOM_BROWN); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_MUSHROOM_RED); - if(currentLevel==1 && season==3) renderConnectedTile4(x, y, 256, 112); + if(level==1 && worldData.season==3) renderConnectedTile4(x, y, 256, 112); else renderConnectedTile4(x, y, 448, 80); break; case TILE_MUSHROOM_BROWN: - renderTile(TILE_MYCELIUM, 0, x, y); + renderTile(TILE_MYCELIUM, 0, level, x, y); render16(x, y, 448 + (d&0x1)*16, 96, 0); break; case TILE_MUSHROOM_RED: - renderTile(TILE_MYCELIUM, 0, x, y); + renderTile(TILE_MYCELIUM, 0, level, x, y); render16(x, y, 480 + (d&0x1)*16, 96, 0); break; case TILE_ICE: - renderTile(TILE_WATER, 0, x, y); + renderTile(TILE_WATER, 0, level, x, y); //checkSurrTiles4(x >> 4, y >> 4, TILE_WATER); //checkSurrTiles4(x >> 4, y >> 4, TILE_HOLE); - checkSurrTiles4(x >> 4, y >> 4, TILE_ICE); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_ICE); renderConnectedTile4(x, y, 448, 112); break; @@ -747,70 +755,78 @@ void renderConnectedTile8(int x, int y, u32 xTile, u32 yTile) { render(x+8, y+8, xTile+8+r+d+((tr && td && tdr) ? 16 : 0), yTile+8, 0); } -void renderZoomedMap() { +void renderZoomedMap(PlayerData *pd) { sf2d_draw_rectangle(0, 0, 320, 240, 0xFF0C0C0C); //You might think "real" black would be better, but it actually looks better that way - int mx = mScrollX; - int my = mScrollY; - if(zoomLevel == 2) mx = 32; - sf2d_draw_texture_scale(minimap[currentLevel], mx, my, zoomLevel, zoomLevel); // zoomed map + int mx = pd->mapScrollX; + int my = pd->mapScrollY; + if(pd->mapZoomLevel == 2) mx = 32; + sf2d_draw_texture_scale(minimap[pd->entity.level], mx, my, pd->mapZoomLevel, pd->mapZoomLevel); // zoomed map + // Airwizard on zoomed map - if(currentLevel == 0){ + if(pd->entity.level == 0){ if(awX != 0 && awY != 0){ render16c( - (mx+((awX/16)*zoomLevel)-16)/2, - (my+((awY/16)*zoomLevel)-16)/2, + (mx+((awX/16)*pd->mapZoomLevel)-16)/2, + (my+((awY/16)*pd->mapZoomLevel)-16)/2, 160, 112, - ((player.p.walkDist >> 6) & 1) == 0 ? 0 : 1, + ((pd->entity.p.walkDist >> 6) & 1) == 0 ? 0 : 1, 2, 2 ); } } // Player on zoomed map + //TODO: Maybe also render other players? render16c( - (mx+((player.x/16)*zoomLevel)-16)/2, - (my+((player.y/16)*zoomLevel)-16)/2, + (mx+((pd->entity.x/16)*pd->mapZoomLevel)-16)/2, + (my+((pd->entity.y/16)*pd->mapZoomLevel)-16)/2, 0, 112, - ((player.p.walkDist >> 6) & 1) == 0 ? 0 : 1, + ((pd->entity.p.walkDist >> 6) & 1) == 0 ? 0 : 1, 2, 2 ); - drawText(mapText,224, 214); // "x2"/"x4"/"x6" + drawText(pd->mapText,224, 214); // "x2"/"x4"/"x6" render16(142, 2, 72, 208, 0); // Exit button renderc(126, 102, 40, 208, 32, 16, 0); // Plus/Minus zoom buttons - if(zoomLevel < 3) sf2d_draw_rectangle(258, 210, 26, 20, 0x7F4F4F4F); // gray out minus button - else if(zoomLevel > 5) sf2d_draw_rectangle(284, 210, 26, 20, 0x7F4F4F4F); // gray out minus button + if(pd->mapZoomLevel < 3) sf2d_draw_rectangle(258, 210, 26, 20, 0x7F4F4F4F); // gray out minus button + else if(pd->mapZoomLevel > 5) sf2d_draw_rectangle(284, 210, 26, 20, 0x7F4F4F4F); // gray out minus button } char scoreT[32]; -void renderGui() { +void renderGui(PlayerData *pd) { int i; + //health and stamina for (i = 0; i < 10; ++i) { - if (i < player.p.health) + if (i < pd->entity.p.health) render(i * 8 + 6, 5, 168, 152, 0); else render(i * 8 + 6, 5, 176, 152, 0); - if (i < player.p.stamina) + if (i < pd->entity.p.stamina) render(i * 8 + 6, 14, 184, 152, 0); else render(i * 8 + 6, 14, 191, 152, 0); } - sf2d_draw_texture(minimap[currentLevel], 10, 102); - renderItemWithTextCentered(player.p.activeItem, 320, 66); - itoa(player.p.score, scoreT, 10); // integer to base10 string + + //minimap + sf2d_draw_texture(minimap[pd->entity.level], 10, 102); + + //active item + renderItemWithTextCentered(pd->activeItem, 320, 66); + itoa(pd->score, scoreT, 10); // integer to base10 string drawText("Score:",214,12); drawText(scoreT,(140-(strlen(scoreT)*12))/2 + 180,29); - if(currentLevel == 0){ + if(pd->entity.level == 0){ if(awX != 0 && awY != 0){ renderc(1 + (awX/32), 47 + (awY/32), 88, 216, 8, 8, 0); // Mini-AWizard head. } } - renderc(1 + (player.x/32), 47 + (player.y/32), 88, 208, 8, 8, 0); // Mini-Player head. + //TODO: Maybe also render other players? + renderc(1 + (pd->entity.x/32), 47 + (pd->entity.y/32), 88, 208, 8, 8, 0); // Mini-Player head. //quick select drawText("Quickselect:",164,118); - Inventory * inv = player.p.inv; + Inventory * inv = &(pd->inventory); Item * item; for (i = 0; i < 8; ++i) { if((inv->lastSlot) > i) { @@ -823,103 +839,112 @@ void renderGui() { } } -void renderPlayer() { - if (player.p.isDead) +void renderPlayer(PlayerData *pd) { + if (pd->entity.level!=getLocalPlayer()->entity.level) { + return; + } + if (pd->entity.p.isDead) { return; - int xo = player.x - 8; - int yo = player.y - 8; + } + int xo = pd->entity.x - 8; + int yo = pd->entity.y - 8; - if (player.p.attackTimer > 0 && player.p.dir == 1) { + //attack animation upwards + if (pd->entity.p.attackTimer > 0 && pd->entity.p.dir == 1) { renderc(xo, yo - 4, 16, 160, 16, 8, 0); - renderItemIcon(player.p.activeItem->id, player.p.activeItem->countLevel, - xo + 4, yo - 4); + renderItemIcon(pd->activeItem->id, pd->activeItem->countLevel, xo + 4, yo - 4); } - u8 walk = (player.p.walkDist >> 4) & 1; - bool swimming = isSwimming(); - switch (player.p.dir) { - case 0: // down - if (swimming) - renderc(xo, yo + 4, 48, - 160 + (((player.p.swimTimer >> 4) & 1) << 3), 16, 8, 0); - else - renderc(xo, yo + 8, 0, 120 + (player.p.isCarrying ? 16 : 0), 16, 8, - walk == 0 ? 0 : 1); - renderc(xo, yo, 0, 112 + (player.p.isCarrying ? 16 : 0), 16, 8, - walk == 0 ? 0 : 1); - break; - case 1: // up - if (swimming) - renderc(xo, yo + 4, 48, - 160 + (((player.p.swimTimer >> 4) & 1) << 3), 16, 8, 0); - else - renderc(xo, yo + 8, 16, 120 + (player.p.isCarrying ? 16 : 0), 16, 8, - walk == 0 ? 0 : 1); - renderc(xo, yo, 16, 112 + (player.p.isCarrying ? 16 : 0), 16, 8, - walk == 0 ? 0 : 1); - break; - case 2: // left - if (swimming) - renderc(xo, yo + 4, 48, - 160 + (((player.p.swimTimer >> 4) & 1) << 3), 16, 8, 0); - else - renderc(xo, yo + 8, 32 + (walk << 4), - 120 + (player.p.isCarrying ? 16 : 0), 16, 8, 1); - renderc(xo, yo, 32 + (walk << 4), 112 + (player.p.isCarrying ? 16 : 0), - 16, 8, 1); - break; - case 3: // right - if (swimming) - renderc(xo, yo + 4, 48, - 160 + (((player.p.swimTimer >> 4) & 1) << 3), 16, 8, 0); - else - renderc(xo, yo + 8, 32 + (walk << 4), - 120 + (player.p.isCarrying ? 16 : 0), 16, 8, 0); - renderc(xo, yo, 32 + (walk << 4), 112 + (player.p.isCarrying ? 16 : 0), - 16, 8, 0); - break; + + //find basic indices + int aIndexBig = 0; + int aIndexSmall = 0; + switch(pd->entity.p.dir) { + case 0: //down + aIndexBig = 0; + aIndexSmall = 0; + break; + case 1: //up + aIndexBig = 2; + aIndexSmall = 1; + break; + case 2: //left + aIndexBig = 7; + aIndexSmall = 3; + break; + case 3: //right + aIndexBig = 4; + aIndexSmall = 2; + break; + } + + //find index offset based on walk state + u8 walkingOffset = (pd->entity.p.walkDist >> 4) % 2; + if(pd->entity.p.dir==2 || pd->entity.p.dir==3) { + walkingOffset = (pd->entity.p.walkDist >> 4) % 4; + if(walkingOffset==2) walkingOffset = 0; + if(walkingOffset==3) walkingOffset = 2; + } + + bool swimming = isWater(pd->entity.level, pd->entity.x>>4, pd->entity.y>>4); + + //render water anim when swimming + if (swimming) { + renderc(xo, yo + 5, 48, 160 + (((pd->entity.p.swimTimer >> 4) & 1) << 3), 16, 8, 0); + } + + + //render the different parts + //legs + if(!swimming) { + renderp(xo, yo, (0+aIndexBig+walkingOffset)*16, pd->sprite.legs*16); + } + //body + renderp(xo, yo, (10+aIndexBig+walkingOffset)*16, pd->sprite.body*16); + //arms (normal) + if(!(pd->entity.p.isCarrying)) { + renderp(xo, yo, (20+aIndexBig+walkingOffset)*16, pd->sprite.arms*16); + } + //head + renderp(xo, yo, (30+aIndexSmall)*16, pd->sprite.head*16); + //eyes + renderp(xo, yo, (34+aIndexSmall)*16, pd->sprite.eyes*16); + //arms (carrying) + if(pd->entity.p.isCarrying) { + renderp(xo, yo, (38+aIndexSmall)*16, pd->sprite.arms*16); + } + + + //furniture + if (pd->entity.p.isCarrying) { + renderFurniture(pd->activeItem->id, xo, yo - 12); } - if (player.p.isCarrying) { - renderFurniture(player.p.activeItem->id, xo, yo - 12); - } - - if (player.p.attackTimer > 0) { - switch (player.p.dir) { + //attack animation (other directios) + if (pd->entity.p.attackTimer > 0) { + switch (pd->entity.p.dir) { case 0: - renderc(xo - player.p.ax, yo - player.p.ay + 12, 16, 168, 16, 8, 0); - renderItemIcon(player.p.activeItem->id, - player.p.activeItem->countLevel, xo + 4, yo + 12); + renderc(xo - pd->entity.p.ax, yo - pd->entity.p.ay + 12, 16, 168, 16, 8, 0); + renderItemIcon(pd->activeItem->id, pd->activeItem->countLevel, xo + 4, yo + 12); break; case 2: - renderc(xo - player.p.ax - 4, yo - player.p.ay, 32, 160, 8, 16, 0); - renderItemIcon(player.p.activeItem->id, - player.p.activeItem->countLevel, xo - 4, yo + 4); + renderc(xo - pd->entity.p.ax - 4, yo - pd->entity.p.ay, 32, 160, 8, 16, 0); + renderItemIcon(pd->activeItem->id, pd->activeItem->countLevel, xo - 4, yo + 4); break; case 3: - renderc(xo - player.p.ax + 12, yo - player.p.ay, 40, 160, 8, 16, 0); - renderItemIcon(player.p.activeItem->id, - player.p.activeItem->countLevel, xo + 12, yo + 4); + renderc(xo - pd->entity.p.ax + 12, yo - pd->entity.p.ay, 40, 160, 8, 16, 0); + renderItemIcon(pd->activeItem->id, pd->activeItem->countLevel, xo + 12, yo + 4); break; } } } -void renderMenuBackground(int xScroll, int yScroll) { - sf2d_draw_rectangle(0, 0, 400, 240, 0xFF0C0C0C); //You might think "real" black would be better, but it actually looks better that way - renderLightsToStencil(false, false, true); - renderBackground(xScroll, yScroll); - resetStencilStuff(); - - renderDayNight(); -} - -void renderWeather(int xScroll, int yScroll) { - if(currentLevel==1) { - if(season==3) { - int xp = -128 + ((tickCount>>2) - xScroll*2)%128; - int yp = -128 + ((tickCount>>1) - yScroll*2)%128; - int xp2 = 0 - ((tickCount>>2) + xScroll*2)%128; - int yp2 = -128 + ((tickCount>>1)+64 - yScroll*2)%128; +void renderWeather(u8 level, int xScroll, int yScroll) { + if(level==1) { + if(worldData.season==3) { + int xp = -128 + ((syncTickCount>>2) - xScroll*2)%128; + int yp = -128 + ((syncTickCount>>1) - yScroll*2)%128; + int xp2 = 0 - ((syncTickCount>>2) + xScroll*2)%128; + int yp2 = -128 + ((syncTickCount>>1)+64 - yScroll*2)%128; int xt; int yt; for(xt=0; xt<4; ++xt) { @@ -930,11 +955,11 @@ void renderWeather(int xScroll, int yScroll) { } } - if(rain) { + if(worldData.rain) { int xp = -((xScroll*2)%128); - int yp = -128 + ((tickCount<<2) - yScroll*2)%128; + int yp = -128 + ((syncTickCount<<2) - yScroll*2)%128; int xp2 = -((xScroll*2+8)%128); - int yp2 = -128 + ((tickCount<<1)+64 - yScroll*2)%128; + int yp2 = -128 + ((syncTickCount<<1)+64 - yScroll*2)%128; int xt; int yt; for(xt=0; xt<4; ++xt) { @@ -947,46 +972,46 @@ void renderWeather(int xScroll, int yScroll) { } } -void renderDayNight() { - if(currentLevel==1 && (daytime<6000 || daytime>18000)) { +void renderDayNight(PlayerData *pd) { + if(pd->entity.level==1 && (worldData.daytime<6000 || worldData.daytime>18000)) { int color1 = 0x000C0C0C; int color2 = 0x00100C0C; int alpha1 = 0x88; int alpha2 = 0xDD; - if(daytime>5000 && daytime<6000) { - alpha1 = (alpha1 * (1000-(daytime-5000)))/1000; - alpha2 = (alpha2 * (1000-(daytime-5000)))/1000; - } else if(daytime>18000 && daytime<19000) { - alpha1 = (alpha1 * (daytime-18000))/1000; - alpha2 = (alpha2 * (daytime-18000))/1000; + if(worldData.daytime>5000 && worldData.daytime<6000) { + alpha2 = (alpha2 * (1000-(worldData.daytime-5000)))/1000; + alpha1 = (alpha1 * (1000-(worldData.daytime-5000)))/1000; + } else if(worldData.daytime>18000 && worldData.daytime<19000) { + alpha1 = (alpha1 * (worldData.daytime-18000))/1000; + alpha2 = (alpha2 * (worldData.daytime-18000))/1000; } color1 = color1 | (alpha1 << 24); color2 = color2 | (alpha2 << 24); sf2d_draw_rectangle(0, 0, 400, 240, color1); //You might think "real" black would be better, but it actually looks better that way - renderLightsToStencil(true, true, false); + renderLightsToStencil(pd, true, true, false); sf2d_draw_rectangle(0, 0, 400, 240, color2); //You might think "real" black would be better, but it actually looks better that way resetStencilStuff(); } } -void renderBackground(int xScroll, int yScroll) { - if(currentLevel == 0) { +void renderBackground(s8 level, int xScroll, int yScroll) { + if(level == 0) { sf2d_draw_texture_part_scale(minimap[1], (-xScroll / 3) - 256, (-yScroll / 3) - 32, 0, 0, 128, 128, 12.5, 7.5); sf2d_draw_rectangle(0, 0, 400, 240, 0xAFDFDFDF); - } else if(currentLevel == 5) { + } else if(level == 5) { sf2d_draw_rectangle(0, 0, 400, 240, dungeonColor[1]); } else { - sf2d_draw_rectangle(0, 0, 400, 240, dirtColor[currentLevel]); // dirt color + sf2d_draw_rectangle(0, 0, 400, 240, dirtColor[level]); // dirt color } int xo = xScroll >> 4; int yo = yScroll >> 4; int x, y; for (x = xo; x <= 13 + xo; ++x) { for (y = yo; y <= 8 + yo; ++y) - renderTile(getTile(x, y), getData(x, y), x << 4, y << 4); + renderTile(getTile(level, x, y), getData(level, x, y), level, x << 4, y << 4); } } @@ -1163,10 +1188,10 @@ void renderEntity(Entity* e, int x, int y) { case ENTITY_AIRWIZARD: e->wizard.spriteAdjust = 0; if (e->wizard.health < 200) { - if (tickCount / 4 % 3 < 2) + if (syncTickCount / 4 % 3 < 2) e->wizard.spriteAdjust = 16; } else if (e->wizard.health < 1000) { - if (tickCount / 5 % 4 < 2) + if (syncTickCount / 5 % 4 < 2) e->wizard.spriteAdjust = 16; } switch (e->wizard.dir) { @@ -1291,10 +1316,10 @@ void renderEntity(Entity* e, int x, int y) { } } -void renderEntities(int x, int y, EntityManager* em) { +void renderEntities(u8 level, int x, int y, EntityManager* em) { int i; - for (i = 0; i < em->lastSlot[currentLevel]; ++i) { - Entity e = em->entities[currentLevel][i]; + for (i = 0; i < em->lastSlot[level]; ++i) { + Entity e = em->entities[level][i]; if (e.x > x - 200 && e.y > y - 125 && e.x < x + 200 && e.y < y + 125) renderEntity(&e, e.x, e.y); } @@ -1599,8 +1624,8 @@ void renderItemIcon(int itemID, int countLevel, int x, int y) { render(x, y, 64, 168, 0); break; case TOOL_MAGIC_COMPASS: - xd = compassData[currentLevel][0] - (player.x>>4); - yd = compassData[currentLevel][1] - (player.y>>4); + xd = worldData.compassData[getLocalPlayer()->entity.level][0] - (getLocalPlayer()->entity.x>>4); + yd = worldData.compassData[getLocalPlayer()->entity.level][1] - (getLocalPlayer()->entity.y>>4); if(abs(yd)>abs(xd)) { if(yd<0) render(x, y, 112, 168, 0); else render(x, y, 120, 168, 0); diff --git a/source/Render.h b/source/Render.h index e7b157c..cef1da0 100644 --- a/source/Render.h +++ b/source/Render.h @@ -12,6 +12,7 @@ sf2d_texture *lanternLightBake; sf2d_texture *glowwormLightBake; sf2d_texture *glowwormBigLightBake; int offsetX, offsetY; +int playerScale; void render(s32 xp, s32 yp, u32 xTile, u32 yTile, u8 bits); void renderb(s32 xp, s32 yp, u32 xTile, u32 yTile, u8 bits, u32 color); @@ -26,25 +27,24 @@ void render32(s32 xp, s32 yp, u32 xTile, u32 yTile, u8 bits); void renderTitle(int x, int y); void renderFrame(int x1, int y1, int x2, int y2, u32 bgColor); -void renderTile(int i, int d, int x, int y); +void renderTile(int i, int d, u8 level, int x, int y); void renderConnectedTile4(int x, int y, u32 xTile, u32 yTile); void renderConnectedTile8(int x, int y, u32 xTile, u32 yTile); -void renderBackground(int xScroll, int yScroll); -void renderMenuBackground(int xScroll, int yScroll); //Renders the darkness -void renderWeather(int xScroll, int yScroll); -void renderDayNight(); +void renderBackground(s8 level, int xScroll, int yScroll); +void renderWeather(u8 level, int xScroll, int yScroll); +void renderDayNight(PlayerData *pd); void renderButtonIcon(u32 icon, int x, int y, float scale); void bakeLights(); void freeLightBakes(); -void renderLightsToStencil(bool force, bool invert, bool rplayer); +void renderLightsToStencil(PlayerData *pd, bool force, bool invert, bool rplayer); void resetStencilStuff(); void bakeLight(sf2d_texture* texture, int x, int y, int r); void renderLight(int x, int y, sf2d_texture* texture); -void renderGui(); -void renderZoomedMap(); -void renderPlayer(); +void renderGui(PlayerData *pd); +void renderZoomedMap(PlayerData *pd); +void renderPlayer(PlayerData *pd); void drawText(char * msg, u32 x, u32 y); void drawSizedText(char * msg, u32 x, u32 y, float size); @@ -54,7 +54,7 @@ void drawSizedTextColor(char * msg, int x, int y, float size, u32 color); void renderFurniture(int itemID, int x, int y); void renderEntity(Entity* e, int x, int y); -void renderEntities(int x, int y, EntityManager* em); +void renderEntities(u8 level, int x, int y, EntityManager* em); void renderRecipes(RecipeManager * r, int xo, int yo, int x1, int y1, int selected); void renderItemList(Inventory * inv, int xo, int yo, int x1, int y1, int selected); diff --git a/source/SaveLoad.c b/source/SaveLoad.c index 60766d0..4ac99b9 100644 --- a/source/SaveLoad.c +++ b/source/SaveLoad.c @@ -1,5 +1,7 @@ #include "SaveLoad.h" +#include "ZipHelper.h" + bool entityIsImportant(Entity * e){ switch(e->type){ case ENTITY_AIRWIZARD: @@ -30,39 +32,118 @@ s16 calculateImportantEntites(EntityManager * eManager, int level){ return count; } -void saveCurrentWorld(char * filename, EntityManager * eManager, Entity * player, u8 * map, u8 * mapData){ - FILE * file = fopen(filename, "wb"); - int i,j; +//helper methods +char **files; +int fileCount; - // Player Data - fwrite(&player->p.score, sizeof(int), 1, file); - fwrite(&player->p.hasWonSaved, sizeof(bool), 1, file); - fwrite(&player->p.health, sizeof(s16), 1, file); - fwrite(&player->x, sizeof(s16), 1, file); - fwrite(&player->y, sizeof(s16), 1, file); - fwrite(¤tLevel, sizeof(s8), 1, file); +void saveTrackFileReset() { + if(files!=NULL) { + for(int i=0; i0) { + fwrite(buffer, 1, size, out); + } + } while(size>0); + + fclose(in); + fclose(out); + + return true; +} + +//internal save methods +void saveInventory(Inventory *inv, EntityManager *eManager, FILE *file) { + fwrite(&inv->lastSlot, sizeof(s16), 1, file); // write amount of items in inventory; + for(int j = 0; j < inv->lastSlot; ++j) { + fwrite(&(inv->items[j].id), sizeof(s16), 1, file); // write ID of item + fwrite(&(inv->items[j].countLevel), sizeof(s16), 1, file); // write count/level of item + fwrite(&(inv->items[j].onlyOne), sizeof(bool), 1, file); + if(inv->items[j].id == ITEM_CHEST){ + int invIndex = inv->items[j].chestPtr - eManager->invs; + fwrite(&invIndex, sizeof(int), 1, file); + } + } +} + +void saveWorldInternal(char *filename, EntityManager *eManager, WorldData *worldData) { + FILE * file = fopen(filename, "wb"); //TODO: should be checked + + int i, j; + + //write savefile version + int version = SAVE_VERSION; + fwrite(&version, sizeof(int), 1, file); // Inventory Data fwrite(&eManager->nextInv, sizeof(s16), 1, file); // write amount of inventories. for(i = 0; i < eManager->nextInv; ++i) { - fwrite(&eManager->invs[i].lastSlot, sizeof(s16), 1, file); // write amount of items in inventory; - for(j = 0; j < eManager->invs[i].lastSlot; ++j) { - fwrite(&eManager->invs[i].items[j].id, sizeof(s16), 1, file); // write ID of item - fwrite(&eManager->invs[i].items[j].countLevel, sizeof(s16), 1, file); // write count/level of item - fwrite(&eManager->invs[i].items[j].onlyOne, sizeof(bool), 1, file); - if(eManager->invs[i].items[j].id == ITEM_CHEST){ - int invIndex = eManager->invs[i].items[j].chestPtr - eManager->invs; - fwrite(&invIndex, sizeof(int), 1, file); - } - } + saveInventory(&(eManager->invs[i]), eManager, file); } // Entity Data - for(i = 0; i < 5; ++i){ + for(i = 0; i < 5; ++i) { //for every level (except dungeon of course) int amount = calculateImportantEntites(eManager,i); fwrite(&amount, sizeof(s16), 1, file); // read amount of entities in level. for(j = 0; j < eManager->lastSlot[i]; ++j){ if(!entityIsImportant(&eManager->entities[i][j])) continue; + fwrite(&eManager->entities[i][j].type, sizeof(s16), 1, file); // write entity's type ID fwrite(&eManager->entities[i][j].x, sizeof(s16), 1, file); // write entity's x coordinate fwrite(&eManager->entities[i][j].y, sizeof(s16), 1, file); // write entity's y coordinate @@ -104,308 +185,459 @@ void saveCurrentWorld(char * filename, EntityManager * eManager, Entity * player } } + // Day/season Data + fwrite(&worldData->daytime, sizeof(u16), 1, file); + fwrite(&worldData->day, sizeof(int), 1, file); + fwrite(&worldData->season, sizeof(u8), 1, file); + fwrite(&worldData->rain, sizeof(bool), 1, file); + + // Compass Data + fwrite(worldData->compassData, sizeof(u8), 6*3, file); //x,y of choosen stair and count per level + // Map Data //Don't write or load dungeon, so only first 5 levels not 6 - fwrite(map, sizeof(u8), 128*128*5, file); // Map Tile IDs, 128*128*5 bytes = 80KB - fwrite(mapData, sizeof(u8), 128*128*5, file); // Map Tile Data (Damage done to trees/rocks, age of wheat & saplings, etc). 80KB - - fwrite(&daytime, sizeof(u16), 1, file); - - fwrite(minimapData, sizeof(u8), 128*128, file); // Minimap, visibility data 16KB - - //Quest Data - fwrite(&questManager.size, sizeof(int), 1, file); - for(i = 0; i < questManager.size; ++i) { - fwrite(&(questManager.questlines[i].currentQuest), sizeof(int), 1, file); - fwrite(&(questManager.questlines[i].currentQuestDone), sizeof(bool), 1, file); - } - - //Compass Data - for(i=0; i<6; ++i) { - fwrite(&(compassData[i][0]), sizeof(u8), 1, file); //x of choosen stair - fwrite(&(compassData[i][1]), sizeof(u8), 1, file); //y of choosen stair - fwrite(&(compassData[i][2]), sizeof(u8), 1, file); //count - } - - //Day/season Data - fwrite(&day, sizeof(int), 1, file); - fwrite(&season, sizeof(u8), 1, file); - fwrite(&rain, sizeof(bool), 1, file); + fwrite(worldData->map, sizeof(u8), 128*128*5, file); // Map Tile IDs, 128*128*5 bytes = 80KB + fwrite(worldData->data, sizeof(u8), 128*128*5, file); // Map Tile Data (Damage done to trees/rocks, age of wheat & saplings, etc). 80KB fclose(file); } -int loadWorld(char * filename, EntityManager * eManager, Entity * player, u8 * map, u8 * mapData){ - FILE * file; - int i,j; - - if ((file = fopen(filename, "rb"))){ - - fread(&player->p.score, sizeof(int), 1, file); - fread(&player->p.hasWonSaved, sizeof(bool), 1, file); - fread(&player->p.health, sizeof(s16), 1, file); - fread(&player->x, sizeof(s16), 1, file); - fread(&player->y, sizeof(s16), 1, file); - fread(¤tLevel, sizeof(s8), 1, file); - - fread(&eManager->nextInv, sizeof(s16), 1, file); - for(i = 0; i < eManager->nextInv; ++i) { - fread(&eManager->invs[i].lastSlot, sizeof(s16), 1, file); // read amount of items in inventory; - for(j = 0; j < eManager->invs[i].lastSlot; ++j) { - fread(&eManager->invs[i].items[j].id, sizeof(s16), 1, file); // write ID of item - fread(&eManager->invs[i].items[j].countLevel, sizeof(s16), 1, file); // write count/level of item - fread(&eManager->invs[i].items[j].onlyOne, sizeof(bool), 1, file); - eManager->invs[i].items[j].invPtr = (int*)&eManager->invs[i]; // setup Inventory pointer - eManager->invs[i].items[j].slotNum = j; // setup slot number - if(eManager->invs[i].items[j].id == ITEM_CHEST){ // for chest item specifically. - int invIndex; - fread(&invIndex, sizeof(int), 1, file); - eManager->invs[i].items[j].chestPtr = (Inventory*)&eManager->invs[invIndex]; // setup chest inventory pointer. - } - } - } - - for(i = 0; i < 5; ++i){ - fread(&eManager->lastSlot[i], sizeof(s16), 1, file); // read amount of entities in level. - for(j = 0; j < eManager->lastSlot[i]; ++j){ - fread(&eManager->entities[i][j].type, sizeof(s16), 1, file); // read entity's type ID - fread(&eManager->entities[i][j].x, sizeof(s16), 1, file); // read entity's x coordinate - fread(&eManager->entities[i][j].y, sizeof(s16), 1, file); // read entity's y coordinate - eManager->entities[i][j].slotNum = j; - switch(eManager->entities[i][j].type){ - case ENTITY_SMASHPARTICLE: - eManager->entities[i][j].level = i; - eManager->entities[i][j].smashParticle.age = 300; - eManager->entities[i][j].canPass = true; - break; - case ENTITY_TEXTPARTICLE: - eManager->entities[i][j].level = i; - eManager->entities[i][j].canPass = true; - eManager->entities[i][j].textParticle.age = 59; - eManager->entities[i][j].textParticle.text = NULL; - eManager->entities[i][j].textParticle.xx = eManager->entities[i][j].x; - eManager->entities[i][j].textParticle.yy = eManager->entities[i][j].y; - eManager->entities[i][j].textParticle.zz = 2; - eManager->entities[i][j].textParticle.xa = 0; - eManager->entities[i][j].textParticle.ya = 0; - eManager->entities[i][j].textParticle.za = 0; - break; - case ENTITY_SPARK: - eManager->entities[i][j].level = i; - eManager->entities[i][j].spark.age = 300; - break; - case ENTITY_AIRWIZARD: - fread(&eManager->entities[i][j].wizard.health, sizeof(s16), 1, file); - eManager->entities[i][j].level = i; - eManager->entities[i][j].hurtTime = 0; - eManager->entities[i][j].xKnockback = 0; - eManager->entities[i][j].yKnockback = 0; - eManager->entities[i][j].wizard.dir = 0; - eManager->entities[i][j].wizard.attackDelay = 0; - eManager->entities[i][j].wizard.attackTime = 0; - eManager->entities[i][j].wizard.attackType = 0; - eManager->entities[i][j].wizard.xa = 0; - eManager->entities[i][j].wizard.ya = 0; - eManager->entities[i][j].xr = 4; - eManager->entities[i][j].yr = 3; - eManager->entities[i][j].canPass = true; - break; - case ENTITY_SLIME: - fread(&eManager->entities[i][j].slime.health, sizeof(s16), 1, file); - fread(&eManager->entities[i][j].slime.lvl, sizeof(s8), 1, file); - eManager->entities[i][j].level = i; - eManager->entities[i][j].hurtTime = 0; - eManager->entities[i][j].xKnockback = 0; - eManager->entities[i][j].yKnockback = 0; - eManager->entities[i][j].slime.xa = 0; - eManager->entities[i][j].slime.ya = 0; - eManager->entities[i][j].slime.dir = 0; - eManager->entities[i][j].xr = 4; - eManager->entities[i][j].yr = 3; - eManager->entities[i][j].canPass = false; - switch(eManager->entities[i][j].slime.lvl){ - case 2: eManager->entities[i][j].slime.color = 0xFF8282CC; break; - case 3: eManager->entities[i][j].slime.color = 0xFFEFEFEF; break; - case 4: eManager->entities[i][j].slime.color = 0xFFAA6262; break; - default: eManager->entities[i][j].slime.color = 0xFF95DB95; break; - } - break; - case ENTITY_ZOMBIE: - fread(&eManager->entities[i][j].hostile.health, sizeof(s16), 1, file); - fread(&eManager->entities[i][j].hostile.lvl, sizeof(s8), 1, file); - eManager->entities[i][j].level = i; - eManager->entities[i][j].hurtTime = 0; - eManager->entities[i][j].xKnockback = 0; - eManager->entities[i][j].yKnockback = 0; - eManager->entities[i][j].hostile.dir = 0; - eManager->entities[i][j].xr = 4; - eManager->entities[i][j].yr = 3; - eManager->entities[i][j].canPass = false; - switch(eManager->entities[i][j].hostile.lvl){ - case 2: eManager->entities[i][j].hostile.color = 0xFF8282CC; break; - case 3: eManager->entities[i][j].hostile.color = 0xFFEFEFEF; break; - case 4: eManager->entities[i][j].hostile.color = 0xFFAA6262; break; - default: eManager->entities[i][j].hostile.color = 0xFF95DB95; break; - } - break; - case ENTITY_SKELETON: - fread(&eManager->entities[i][j].hostile.health, sizeof(s16), 1, file); - fread(&eManager->entities[i][j].hostile.lvl, sizeof(s8), 1, file); - eManager->entities[i][j].level = i; - eManager->entities[i][j].hurtTime = 0; - eManager->entities[i][j].xKnockback = 0; - eManager->entities[i][j].yKnockback = 0; - eManager->entities[i][j].hostile.dir = 0; - eManager->entities[i][j].hostile.randAttackTime = 0; - eManager->entities[i][j].xr = 4; - eManager->entities[i][j].yr = 3; - eManager->entities[i][j].canPass = false; - switch(eManager->entities[i][j].hostile.lvl){ - case 2: eManager->entities[i][j].hostile.color = 0xFFC4C4C4; break; - case 3: eManager->entities[i][j].hostile.color = 0xFFA0A0A0; break; - case 4: eManager->entities[i][j].hostile.color = 0xFF7A7A7A; break; - default: eManager->entities[i][j].hostile.color = 0xFFFFFFFF; break; - } - break; - case ENTITY_KNIGHT: - fread(&eManager->entities[i][j].hostile.health, sizeof(s16), 1, file); - fread(&eManager->entities[i][j].hostile.lvl, sizeof(s8), 1, file); - eManager->entities[i][j].level = i; - eManager->entities[i][j].hurtTime = 0; - eManager->entities[i][j].xKnockback = 0; - eManager->entities[i][j].yKnockback = 0; - eManager->entities[i][j].hostile.dir = 0; - eManager->entities[i][j].xr = 4; - eManager->entities[i][j].yr = 3; - eManager->entities[i][j].canPass = false; - switch(eManager->entities[i][j].hostile.lvl){ - case 2: eManager->entities[i][j].hostile.color = 0xFF0000C6; break; - case 3: eManager->entities[i][j].hostile.color = 0xFF00A3C6; break; - case 4: eManager->entities[i][j].hostile.color = 0xFF707070; break; - default: eManager->entities[i][j].hostile.color = 0xFFFFFFFF; break; - } - break; - case ENTITY_ITEM: - //eManager->entities[i][j].entityItem.item = newItem(0,0); - fread(&eManager->entities[i][j].entityItem.item.id, sizeof(s16), 1, file); - fread(&eManager->entities[i][j].entityItem.item.countLevel, sizeof(s16), 1, file); - fread(&eManager->entities[i][j].entityItem.age, sizeof(s16), 1, file); - eManager->entities[i][j].level = i; - eManager->entities[i][j].entityItem.age = 0; - eManager->entities[i][j].xr = 3; - eManager->entities[i][j].yr = 3; - eManager->entities[i][j].canPass = false; - eManager->entities[i][j].entityItem.xx = eManager->entities[i][j].x; - eManager->entities[i][j].entityItem.yy = eManager->entities[i][j].y; - eManager->entities[i][j].entityItem.zz = 2; - eManager->entities[i][j].entityItem.xa = 0; - eManager->entities[i][j].entityItem.ya = 0; - eManager->entities[i][j].entityItem.za = 0; - break; - case ENTITY_FURNITURE: - fread(&eManager->entities[i][j].entityFurniture.itemID, sizeof(s16), 1, file); - int invIndex; - fread(&invIndex, sizeof(int), 1, file); - eManager->entities[i][j].entityFurniture.inv = &eManager->invs[invIndex]; - eManager->entities[i][j].xr = 3; - eManager->entities[i][j].yr = 3; - eManager->entities[i][j].canPass = false; - if(eManager->entities[i][j].entityFurniture.itemID == ITEM_LANTERN) eManager->entities[i][j].entityFurniture.r = 8; - break; - case ENTITY_PASSIVE: - fread(&eManager->entities[i][j].passive.health, sizeof(s16), 1, file); - fread(&eManager->entities[i][j].passive.mtype, sizeof(u8), 1, file); - eManager->entities[i][j].level = i; - eManager->entities[i][j].hurtTime = 0; - eManager->entities[i][j].xKnockback = 0; - eManager->entities[i][j].yKnockback = 0; - eManager->entities[i][j].passive.dir = 0; - eManager->entities[i][j].xr = 4; - eManager->entities[i][j].yr = 3; - eManager->entities[i][j].canPass = false; - break; - case ENTITY_GLOWWORM: - eManager->entities[i][j].glowworm.xa = 0; - eManager->entities[i][j].glowworm.ya = 0; - eManager->entities[i][j].glowworm.randWalkTime = 0; - eManager->entities[i][j].glowworm.waitTime = 0; - break; - case ENTITY_DRAGON: - fread(&eManager->entities[i][j].dragon.health, sizeof(s16), 1, file); - eManager->entities[i][j].level = i; - eManager->entities[i][j].hurtTime = 0; - eManager->entities[i][j].xKnockback = 0; - eManager->entities[i][j].yKnockback = 0; - eManager->entities[i][j].dragon.dir = 0; - eManager->entities[i][j].dragon.attackDelay = 0; - eManager->entities[i][j].dragon.attackTime = 0; - eManager->entities[i][j].dragon.attackType = 0; - eManager->entities[i][j].dragon.animTimer = 0; - eManager->entities[i][j].dragon.xa = 0; - eManager->entities[i][j].dragon.ya = 0; - eManager->entities[i][j].xr = 8; - eManager->entities[i][j].yr = 8; - eManager->entities[i][j].canPass = true; - break; - case ENTITY_NPC: - fread(&eManager->entities[i][j].npc.type, sizeof(u8), 1, file); - eManager->entities[i][j].level = i; - eManager->entities[i][j].hurtTime = 0; - eManager->entities[i][j].xKnockback = 0; - eManager->entities[i][j].yKnockback = 0; - eManager->entities[i][j].xr = 4; - eManager->entities[i][j].yr = 3; - eManager->entities[i][j].canPass = false; - break; - } - } - } - //Don't write or load dungeon, so only first 5 levels not 6 - fread(map, sizeof(u8), 128*128*5, file); - fread(mapData, sizeof(u8), 128*128*5, file); - - //set to startvalue incase file is old and doesn't contain saved time - daytime = 6001; - fread(&daytime, sizeof(u16), 1, file); - - fread(minimapData, sizeof(u8), 128*128, file); - - //Quest Data - int qsize = 0; - fread(&qsize, sizeof(int), 1, file); - for(i = 0; i < qsize; ++i) { - fread(&(questManager.questlines[i].currentQuest), sizeof(int), 1, file); - fread(&(questManager.questlines[i].currentQuestDone), sizeof(bool), 1, file); - } - //fill missing questlines with "no progress done" - for(i = qsize; i < questManager.size; ++i) { - questManager.questlines[i].currentQuest = 0; - questManager.questlines[i].currentQuestDone = false; - } - - //Compass Data - //reset incase it is missing in the save - for(i=0; i<6; ++i) { - compassData[i][0] = 0; //x of choosen stair - compassData[i][1] = 0; //y of choosen stair - compassData[i][2] = 0; //count - } - for(i=0; i<6; ++i) { - fread(&(compassData[i][0]), sizeof(u8), 1, file); //x of choosen stair - fread(&(compassData[i][1]), sizeof(u8), 1, file); //y of choosen stair - fread(&(compassData[i][2]), sizeof(u8), 1, file); //count - } - - //Day/season Data - day = 0; - season = 0; - rain = false; - fread(&day, sizeof(int), 1, file); - fread(&season, sizeof(u8), 1, file); - fread(&rain, sizeof(bool), 1, file); - - fclose(file); - return 0; - } - return 1; +void savePlayerInternal(char *filename, PlayerData *player, EntityManager *eManager) { + FILE * file = fopen(filename, "wb"); //TODO: should be checked + + int i; + + //write savefile version + int version = SAVE_VERSION; + fwrite(&version, sizeof(int), 1, file); + + // basic player info + fwrite(&player->score, sizeof(int), 1, file); + fwrite(&player->isSpawned, sizeof(bool), 1, file); + fwrite(&player->entity.p.hasWonSaved, sizeof(bool), 1, file); + fwrite(&player->entity.p.health, sizeof(s16), 1, file); + fwrite(&player->entity.x, sizeof(s16), 1, file); + fwrite(&player->entity.y, sizeof(s16), 1, file); + fwrite(&player->entity.level, sizeof(s8), 1, file); + + saveInventory(&(player->inventory), eManager, file); + + // Sprite info + fwrite(&(player->sprite.choosen), sizeof(bool), 1, file); + fwrite(&(player->sprite.legs), sizeof(u8), 1, file); + fwrite(&(player->sprite.body), sizeof(u8), 1, file); + fwrite(&(player->sprite.arms), sizeof(u8), 1, file); + fwrite(&(player->sprite.head), sizeof(u8), 1, file); + fwrite(&(player->sprite.eyes), sizeof(u8), 1, file); + + // Minimap Data + fwrite(player->minimapData, sizeof(u8), 128*128, file); // Minimap, visibility data 16KB + + // Quest Data + fwrite(&(player->questManager.size), sizeof(int), 1, file); + for(i = 0; i < player->questManager.size; ++i) { + fwrite(&(player->questManager.questlines[i].currentQuest), sizeof(int), 1, file); + fwrite(&(player->questManager.questlines[i].currentQuestDone), sizeof(bool), 1, file); + } + + fclose(file); +} + +//internal load methods +void loadInventory(Inventory *inv, EntityManager *eManager, FILE *file) { + fread(&(inv->lastSlot), sizeof(s16), 1, file); // read amount of items in inventory; + for(int j = 0; j < inv->lastSlot; ++j) { + fread(&(inv->items[j].id), sizeof(s16), 1, file); // write ID of item + fread(&(inv->items[j].countLevel), sizeof(s16), 1, file); // write count/level of item + fread(&(inv->items[j].onlyOne), sizeof(bool), 1, file); + inv->items[j].invPtr = (int*)inv; // setup Inventory pointer + inv->items[j].slotNum = j; // setup slot number + if(inv->items[j].id == ITEM_CHEST){ // for chest item specifically. + int invIndex; + fread(&invIndex, sizeof(int), 1, file); + inv->items[j].chestPtr = (Inventory*)&eManager->invs[invIndex]; // setup chest inventory pointer. + } + } +} + +void loadWorldInternal(char *filename, EntityManager *eManager, WorldData *worldData) { + FILE * file = fopen(filename, "rb"); //TODO: should be checked + + int i, j; + + //read savefile version + int version; + fread(&version, sizeof(int), 1, file); + + // Inventory Data + fread(&eManager->nextInv, sizeof(s16), 1, file); + for(i = 0; i < eManager->nextInv; ++i) { + loadInventory(&(eManager->invs[i]), eManager, file); + } + + // Entity Data + for(i = 0; i < 5; ++i){ + fread(&eManager->lastSlot[i], sizeof(s16), 1, file); // read amount of entities in level. + for(j = 0; j < eManager->lastSlot[i]; ++j){ + fread(&eManager->entities[i][j].type, sizeof(s16), 1, file); // read entity's type ID + fread(&eManager->entities[i][j].x, sizeof(s16), 1, file); // read entity's x coordinate + fread(&eManager->entities[i][j].y, sizeof(s16), 1, file); // read entity's y coordinate + eManager->entities[i][j].slotNum = j; + switch(eManager->entities[i][j].type){ + case ENTITY_SMASHPARTICLE: + eManager->entities[i][j].level = i; + eManager->entities[i][j].smashParticle.age = 300; + eManager->entities[i][j].canPass = true; + break; + case ENTITY_TEXTPARTICLE: + eManager->entities[i][j].level = i; + eManager->entities[i][j].canPass = true; + eManager->entities[i][j].textParticle.age = 59; + eManager->entities[i][j].textParticle.text = NULL; + eManager->entities[i][j].textParticle.xx = eManager->entities[i][j].x; + eManager->entities[i][j].textParticle.yy = eManager->entities[i][j].y; + eManager->entities[i][j].textParticle.zz = 2; + eManager->entities[i][j].textParticle.xa = 0; + eManager->entities[i][j].textParticle.ya = 0; + eManager->entities[i][j].textParticle.za = 0; + break; + case ENTITY_SPARK: + eManager->entities[i][j].level = i; + eManager->entities[i][j].spark.age = 300; + break; + case ENTITY_AIRWIZARD: + fread(&eManager->entities[i][j].wizard.health, sizeof(s16), 1, file); + eManager->entities[i][j].level = i; + eManager->entities[i][j].hurtTime = 0; + eManager->entities[i][j].xKnockback = 0; + eManager->entities[i][j].yKnockback = 0; + eManager->entities[i][j].wizard.dir = 0; + eManager->entities[i][j].wizard.attackDelay = 0; + eManager->entities[i][j].wizard.attackTime = 0; + eManager->entities[i][j].wizard.attackType = 0; + eManager->entities[i][j].wizard.xa = 0; + eManager->entities[i][j].wizard.ya = 0; + eManager->entities[i][j].xr = 4; + eManager->entities[i][j].yr = 3; + eManager->entities[i][j].canPass = true; + break; + case ENTITY_SLIME: + fread(&eManager->entities[i][j].slime.health, sizeof(s16), 1, file); + fread(&eManager->entities[i][j].slime.lvl, sizeof(s8), 1, file); + eManager->entities[i][j].level = i; + eManager->entities[i][j].hurtTime = 0; + eManager->entities[i][j].xKnockback = 0; + eManager->entities[i][j].yKnockback = 0; + eManager->entities[i][j].slime.xa = 0; + eManager->entities[i][j].slime.ya = 0; + eManager->entities[i][j].slime.dir = 0; + eManager->entities[i][j].xr = 4; + eManager->entities[i][j].yr = 3; + eManager->entities[i][j].canPass = false; + switch(eManager->entities[i][j].slime.lvl){ + case 2: eManager->entities[i][j].slime.color = 0xFF8282CC; break; + case 3: eManager->entities[i][j].slime.color = 0xFFEFEFEF; break; + case 4: eManager->entities[i][j].slime.color = 0xFFAA6262; break; + default: eManager->entities[i][j].slime.color = 0xFF95DB95; break; + } + break; + case ENTITY_ZOMBIE: + fread(&eManager->entities[i][j].hostile.health, sizeof(s16), 1, file); + fread(&eManager->entities[i][j].hostile.lvl, sizeof(s8), 1, file); + eManager->entities[i][j].level = i; + eManager->entities[i][j].hurtTime = 0; + eManager->entities[i][j].xKnockback = 0; + eManager->entities[i][j].yKnockback = 0; + eManager->entities[i][j].hostile.dir = 0; + eManager->entities[i][j].xr = 4; + eManager->entities[i][j].yr = 3; + eManager->entities[i][j].canPass = false; + switch(eManager->entities[i][j].hostile.lvl){ + case 2: eManager->entities[i][j].hostile.color = 0xFF8282CC; break; + case 3: eManager->entities[i][j].hostile.color = 0xFFEFEFEF; break; + case 4: eManager->entities[i][j].hostile.color = 0xFFAA6262; break; + default: eManager->entities[i][j].hostile.color = 0xFF95DB95; break; + } + break; + case ENTITY_SKELETON: + fread(&eManager->entities[i][j].hostile.health, sizeof(s16), 1, file); + fread(&eManager->entities[i][j].hostile.lvl, sizeof(s8), 1, file); + eManager->entities[i][j].level = i; + eManager->entities[i][j].hurtTime = 0; + eManager->entities[i][j].xKnockback = 0; + eManager->entities[i][j].yKnockback = 0; + eManager->entities[i][j].hostile.dir = 0; + eManager->entities[i][j].hostile.randAttackTime = 0; + eManager->entities[i][j].xr = 4; + eManager->entities[i][j].yr = 3; + eManager->entities[i][j].canPass = false; + switch(eManager->entities[i][j].hostile.lvl){ + case 2: eManager->entities[i][j].hostile.color = 0xFFC4C4C4; break; + case 3: eManager->entities[i][j].hostile.color = 0xFFA0A0A0; break; + case 4: eManager->entities[i][j].hostile.color = 0xFF7A7A7A; break; + default: eManager->entities[i][j].hostile.color = 0xFFFFFFFF; break; + } + break; + case ENTITY_KNIGHT: + fread(&eManager->entities[i][j].hostile.health, sizeof(s16), 1, file); + fread(&eManager->entities[i][j].hostile.lvl, sizeof(s8), 1, file); + eManager->entities[i][j].level = i; + eManager->entities[i][j].hurtTime = 0; + eManager->entities[i][j].xKnockback = 0; + eManager->entities[i][j].yKnockback = 0; + eManager->entities[i][j].hostile.dir = 0; + eManager->entities[i][j].xr = 4; + eManager->entities[i][j].yr = 3; + eManager->entities[i][j].canPass = false; + switch(eManager->entities[i][j].hostile.lvl){ + case 2: eManager->entities[i][j].hostile.color = 0xFF0000C6; break; + case 3: eManager->entities[i][j].hostile.color = 0xFF00A3C6; break; + case 4: eManager->entities[i][j].hostile.color = 0xFF707070; break; + default: eManager->entities[i][j].hostile.color = 0xFFFFFFFF; break; + } + break; + case ENTITY_ITEM: + //eManager->entities[i][j].entityItem.item = newItem(0,0); + fread(&eManager->entities[i][j].entityItem.item.id, sizeof(s16), 1, file); + fread(&eManager->entities[i][j].entityItem.item.countLevel, sizeof(s16), 1, file); + fread(&eManager->entities[i][j].entityItem.age, sizeof(s16), 1, file); + eManager->entities[i][j].level = i; + eManager->entities[i][j].entityItem.age = 0; + eManager->entities[i][j].xr = 3; + eManager->entities[i][j].yr = 3; + eManager->entities[i][j].canPass = false; + eManager->entities[i][j].entityItem.xx = eManager->entities[i][j].x; + eManager->entities[i][j].entityItem.yy = eManager->entities[i][j].y; + eManager->entities[i][j].entityItem.zz = 2; + eManager->entities[i][j].entityItem.xa = 0; + eManager->entities[i][j].entityItem.ya = 0; + eManager->entities[i][j].entityItem.za = 0; + break; + case ENTITY_FURNITURE: + fread(&eManager->entities[i][j].entityFurniture.itemID, sizeof(s16), 1, file); + int invIndex; + fread(&invIndex, sizeof(int), 1, file); + eManager->entities[i][j].entityFurniture.inv = &eManager->invs[invIndex]; + eManager->entities[i][j].xr = 3; + eManager->entities[i][j].yr = 3; + eManager->entities[i][j].canPass = false; + if(eManager->entities[i][j].entityFurniture.itemID == ITEM_LANTERN) eManager->entities[i][j].entityFurniture.r = 8; + break; + case ENTITY_PASSIVE: + fread(&eManager->entities[i][j].passive.health, sizeof(s16), 1, file); + fread(&eManager->entities[i][j].passive.mtype, sizeof(u8), 1, file); + eManager->entities[i][j].level = i; + eManager->entities[i][j].hurtTime = 0; + eManager->entities[i][j].xKnockback = 0; + eManager->entities[i][j].yKnockback = 0; + eManager->entities[i][j].passive.dir = 0; + eManager->entities[i][j].xr = 4; + eManager->entities[i][j].yr = 3; + eManager->entities[i][j].canPass = false; + break; + case ENTITY_GLOWWORM: + eManager->entities[i][j].glowworm.xa = 0; + eManager->entities[i][j].glowworm.ya = 0; + eManager->entities[i][j].glowworm.randWalkTime = 0; + eManager->entities[i][j].glowworm.waitTime = 0; + break; + case ENTITY_DRAGON: + fread(&eManager->entities[i][j].dragon.health, sizeof(s16), 1, file); + eManager->entities[i][j].level = i; + eManager->entities[i][j].hurtTime = 0; + eManager->entities[i][j].xKnockback = 0; + eManager->entities[i][j].yKnockback = 0; + eManager->entities[i][j].dragon.dir = 0; + eManager->entities[i][j].dragon.attackDelay = 0; + eManager->entities[i][j].dragon.attackTime = 0; + eManager->entities[i][j].dragon.attackType = 0; + eManager->entities[i][j].dragon.animTimer = 0; + eManager->entities[i][j].dragon.xa = 0; + eManager->entities[i][j].dragon.ya = 0; + eManager->entities[i][j].xr = 8; + eManager->entities[i][j].yr = 8; + eManager->entities[i][j].canPass = true; + break; + case ENTITY_NPC: + fread(&eManager->entities[i][j].npc.type, sizeof(u8), 1, file); + eManager->entities[i][j].level = i; + eManager->entities[i][j].hurtTime = 0; + eManager->entities[i][j].xKnockback = 0; + eManager->entities[i][j].yKnockback = 0; + eManager->entities[i][j].xr = 4; + eManager->entities[i][j].yr = 3; + eManager->entities[i][j].canPass = false; + break; + } + } + } + + // Day/season Data + fread(&worldData->daytime, sizeof(u16), 1, file); + fread(&worldData->day, sizeof(int), 1, file); + fread(&worldData->season, sizeof(u8), 1, file); + fread(&worldData->rain, sizeof(bool), 1, file); + + // Compass Data + fread(worldData->compassData, sizeof(u8), 6*3, file); //x,y of choosen stair and count per level + + // Map Data + //Don't write or load dungeon, so only first 5 levels not 6 + fread(worldData->map, sizeof(u8), 128*128*5, file); // Map Tile IDs, 128*128*5 bytes = 80KB + fread(worldData->data, sizeof(u8), 128*128*5, file); // Map Tile Data (Damage done to trees/rocks, age of wheat & saplings, etc). 80KB + + fclose(file); +} + +void loadPlayerInternal(char *filename, PlayerData *player, EntityManager *eManager) { + FILE * file = fopen(filename, "rb"); //TODO: should be checked + + int i; + + //read savefile version + int version; + fread(&version, sizeof(int), 1, file); + + // basic player info + fread(&player->score, sizeof(int), 1, file); + fread(&player->isSpawned, sizeof(bool), 1, file); + fread(&player->entity.p.hasWonSaved, sizeof(bool), 1, file); + fread(&player->entity.p.health, sizeof(s16), 1, file); + fread(&player->entity.x, sizeof(s16), 1, file); + fread(&player->entity.y, sizeof(s16), 1, file); + fread(&player->entity.level, sizeof(s8), 1, file); + + loadInventory(&(player->inventory), eManager, file); + + // Sprite info + fread(&(player->sprite.choosen), sizeof(bool), 1, file); + fread(&(player->sprite.legs), sizeof(u8), 1, file); + fread(&(player->sprite.body), sizeof(u8), 1, file); + fread(&(player->sprite.arms), sizeof(u8), 1, file); + fread(&(player->sprite.head), sizeof(u8), 1, file); + fread(&(player->sprite.eyes), sizeof(u8), 1, file); + + // Minimap Data + fread(player->minimapData, sizeof(u8), 128*128, file); // Minimap, visibility data 16KB + + // Quest Data + fread(&(player->questManager.size), sizeof(int), 1, file); + for(i = 0; i < player->questManager.size; ++i) { + fread(&(player->questManager.questlines[i].currentQuest), sizeof(int), 1, file); + fread(&(player->questManager.questlines[i].currentQuestDone), sizeof(bool), 1, file); + } + + fclose(file); +} + + +bool saveWorld(char *filename, EntityManager *eManager, WorldData *worldData, PlayerData *players, int playerCount) { + //check if old save file exists + bool exists = false; + FILE *testFile = fopen(filename, "rb"); + if(testFile) { + fclose(testFile); + exists = true; + } + + saveTrackFileReset(); + + if(exists) { + //create backup copy + char *filenameBackup = malloc(sizeof(filename)+4+1); + if(filenameBackup==NULL) { + return false; + } + strcpy(filenameBackup, filename); + strcat(filenameBackup, ".bak"); + if(!saveFileCopy(filenameBackup, filename)) { + return false; + } + + //extract files and keep track of references + if(unzipAndLoad(filename, &saveTrackFile, SAVE_COMMENT, ZIPHELPER_KEEP_FILES)!=0) { + saveDeleteTrackedFiles(); + return false; + } + + remove(filename); + } + + //save world data + saveWorldInternal("main.wld", eManager, worldData); + saveTrackFile("main.wld"); + + //save player data of active players + for(int i=0; i #include #include "Entity.h" +#include "Player.h" #include "Globals.h" -void saveCurrentWorld(char * filename, EntityManager * eManager, Entity * player, u8 * map, u8 * mapData); -int loadWorld(char * filename, EntityManager * eManager, Entity * player, u8 * map, u8 * mapData); +#define SAVE_VERSION 1 + +#define SAVE_COMMENT "Minicraft3DSSave" +#define SAVE_COPYBUFFER_SIZE 4096 + +//void saveCurrentWorld(char * filename, EntityManager * eManager, Entity * player, u8 * map, u8 * mapData, WorldData *worldData); +//int loadWorld(char * filename, EntityManager * eManager, Entity * player, u8 * map, u8 * mapData, WorldData *worldData); + +bool saveWorld(char *filename, EntityManager *eManager, WorldData *worldData, PlayerData *players, int playerCount); +bool loadWorld(char *filename, EntityManager *eManager, WorldData *worldData, PlayerData *players, int playerCount); diff --git a/source/Sound.c b/source/Sound.c index 0e188e4..40e2d0f 100644 --- a/source/Sound.c +++ b/source/Sound.c @@ -1,5 +1,9 @@ #include "Sound.h" +u8 soundListenerLevel; +int soundListenerX; +int soundListenerY; + void loadSound(Sound * snd, char * filename){ FILE *file = fopen(filename, "rb"); if(file != NULL){ @@ -16,8 +20,27 @@ void playSound(Sound snd){ csndPlaySound(8, SOUND_FORMAT_16BIT | SOUND_ONE_SHOT, 44100, 1, 0, snd.buffer, snd.buffer, snd.size); } -void playMusic(Sound snd){ - csndPlaySound(10, SOUND_FORMAT_16BIT | SOUND_REPEAT, 44100, 1, 0, snd.buffer, snd.buffer, snd.size); +void playSoundPositioned(Sound snd, s8 level, int x, int y) { + if(level != soundListenerLevel) return; + int xd = soundListenerX - x; + int yd = soundListenerY - y; + if (xd * xd + yd * yd > 80 * 80) return; + + csndPlaySound(8, SOUND_FORMAT_16BIT | SOUND_ONE_SHOT, 44100, 1, 0, snd.buffer, snd.buffer, snd.size); +} + +void setListenerPosition(s8 level, int x, int y) { + soundListenerLevel = level; + soundListenerX = x; + soundListenerY = y; +} + +void playMusic(Sound *snd){ + static Sound *lastSnd; + if(lastSnd==snd) return; + lastSnd = snd; + + csndPlaySound(10, SOUND_FORMAT_16BIT | SOUND_REPEAT, 44100, 1, 0, snd->buffer, snd->buffer, snd->size); } void stopMusic() { @@ -29,19 +52,19 @@ void stopMusic() { void updateMusic(int lvl, int time) { switch(lvl) { case 0: - playMusic(music_floor0); + playMusic(&music_floor0); break; case 1: - if(time>6000 && time<19000) playMusic(music_floor1); - else playMusic(music_floor1_night); + if(time>6000 && time<19000) playMusic(&music_floor1); + else playMusic(&music_floor1_night); break; case 2: case 3: - playMusic(music_floor23); + playMusic(&music_floor23); break; case 4: case 5: //TODO - dungeon needs own music - playMusic(music_floor4); + playMusic(&music_floor4); break; } } diff --git a/source/Sound.h b/source/Sound.h index 42dc00a..3e9b7a2 100644 --- a/source/Sound.h +++ b/source/Sound.h @@ -13,7 +13,10 @@ typedef struct { void loadSound(Sound * snd, char * filename); void playSound(Sound snd); -void playMusic(Sound snd); +void playSoundPositioned(Sound snd, s8 level, int x, int y); +void setListenerPosition(s8 level, int x, int y); + +void playMusic(Sound *snd); void updateMusic(int lvl, int time); void stopMusic(); diff --git a/source/Synchronizer.c b/source/Synchronizer.c new file mode 100644 index 0000000..4896c97 --- /dev/null +++ b/source/Synchronizer.c @@ -0,0 +1,246 @@ +#include "Synchronizer.h" + +#include "Globals.h" +#include "Player.h" +#include "Input.h" +#include "Network.h" +#include "PacketHandler.h" +#include "Ingame.h" + +u32 synchronizerLocalTurn; +unsigned int synchronizerNextSeed; +bool synchronizerRunning; + +int synchronizerCurrentTicks; + +bool synchronizerTurnReady(); +void synchronizerNextTurn(); + +void synchronizerSendLocalInputs(); + +int synchronizerGetTurnIndex(u32 turn); + +void synchronizerInit(int seed, int initPlayerCount, int initPlayerLocalID) { + synchronizerLocalTurn = 0; + synchronizerNextSeed = seed; + playerCount = initPlayerCount; + playerLocalID = initPlayerLocalID; + syncTickCount = 0; + + //reset player turn states (e.g. is turn data recieved), first turn needs to happen with no inputs + for(int i=0; i send to server + sendStartReadyPacket(playerLocalID); +} + +void synchronizerSetPlayerReady(int playerID) { + players[playerID].ready = true; +} + +bool synchronizerAllReady() { + for(int i=0; i1) { + size_t size = writeInputPacket(networkWriteBuffer, &localInputs, playerLocalID, synchronizerLocalTurn); + networkSend(networkWriteBuffer, size); + } +} + +void synchronizerOnInputPacket(u8 playerID, u32 turnNumber, void *data, size_t dataSize) { + if(turnNumber>=synchronizerLocalTurn && turnNumber + +//2-3 seem be optimal (at least for 2 players) +#define SYNCHRONIZER_TICKS_PER_TURN 2 + +void synchronizerInit(int seed, int initPlayerCount, int initPlayerLocalID); + +void synchronizerSendUID(); +void synchronizerSetPlayerUID(int playerID, u32 uid); +void synchronizerSendIfReady(); +void synchronizerSetPlayerReady(int playerID); +bool synchronizerAllReady(); + +void synchronizerStart(); + +void synchronizerTick(void (*gtick)(void)); + +void synchronizerReset(); + +void synchronizerOnInputPacket(u8 playerID, u32 turnNumber, void *data, size_t dataSize); + +// values used ingame +u32 syncTickCount; + +// helpers for random numbers +double gaussrand(bool reset); diff --git a/source/ZipHelper.c b/source/ZipHelper.c new file mode 100644 index 0000000..51b7be2 --- /dev/null +++ b/source/ZipHelper.c @@ -0,0 +1,213 @@ +#include "ZipHelper.h" + +#include +#include +#include +#include "minizip/zip.h" +#include "minizip/unzip.h" + +#define dir_delimter '/' +#define MAX_FILENAME 256 +#define READ_SIZE 9216 + +int unzipAndLoad(char *filename, int (*fileCallback)(char *filename), char *expectedComment, int keepFiles) { + // Open the zip file + unzFile *zipfile = unzOpen(filename); + if (zipfile == NULL) { + return 1; // Error: ZipFile could not be opened. + } + + // Get info about the zip file + unz_global_info global_info; + if (unzGetGlobalInfo(zipfile, &global_info ) != UNZ_OK) { + unzClose(zipfile); + return 2; // Error: Could not read global info + } + + if(expectedComment!=NULL) { + char *buffer = malloc(global_info.size_comment+1); + if(buffer==NULL) { + unzClose(zipfile); + return 3; // Error: Could not read global comment + } + + if (unzGetGlobalComment(zipfile, buffer, global_info.size_comment+1) < 0) { + unzClose(zipfile); + return 3; // Error: Could not read global comment + } + + if (strcmp(expectedComment, buffer)!=0) { + unzClose(zipfile); + return 4; // Error: Global comment did not have expected value + } + + free(buffer); + } + + // Buffer to hold data read from the zip file. + void *read_buffer = malloc(READ_SIZE); + if(read_buffer==NULL) { + // Error: Could not allocate read buffer + return 5; + } + + // Loop to extract all files + uLong i; + for (i = 0; i < global_info.number_entry; ++i) { + // Get info about current file. + unz_file_info file_info; + char filename[MAX_FILENAME]; + if (unzGetCurrentFileInfo(zipfile, &file_info, filename, MAX_FILENAME, NULL, 0, NULL, 0 ) != UNZ_OK) { + free(read_buffer); + unzClose(zipfile); + return 6; // Error: Could not read file info + } + + // Check if this entry is NOT a directory or file. + const size_t filename_length = strlen(filename); + if (filename[ filename_length-1 ] != dir_delimter){ + if (unzOpenCurrentFile( zipfile ) != UNZ_OK) { + free(read_buffer); + unzClose(zipfile); + return 7; + } + + // Open a file to write out the data. + FILE * out = fopen(filename, "wb"); + if (out == NULL) { + free(read_buffer); + unzCloseCurrentFile(zipfile); + unzClose(zipfile); + return 8; + } + + int error = UNZ_OK; + do { + error = unzReadCurrentFile(zipfile, read_buffer, READ_SIZE); + if ( error < 0 ) { + //printf("error %d\n", error); + free(read_buffer); + unzCloseCurrentFile(zipfile); + unzClose(zipfile); + fclose(out); + remove(filename); + return 9; + } + + // Write data to file. + if (error > 0) { + fwrite(read_buffer, error, 1, out); // You should check return of fwrite... + } + } while (error > 0); + + fclose(out); + + //run callback + if((*fileCallback)(filename) != 0) { + free(read_buffer); + unzClose(zipfile); + remove(filename); + return 10; // Error: Callback error + } + + if(keepFiles==ZIPHELPER_CLEANUP_FILES) { + remove(filename); + } + } + + unzCloseCurrentFile(zipfile); + + // Go the the next entry listed in the zip file. + if (( i+1 ) < global_info.number_entry) { + if (unzGoToNextFile( zipfile ) != UNZ_OK) { + free(read_buffer); + unzClose(zipfile); + return 11; + } + } + } + + free(read_buffer); + unzClose(zipfile); + + return 0; +} + +int zipFiles(char *filename, char **files, int fileCount, int mode, char *comment) { + // Set mode + int zipMode = mode==ZIPHELPER_ADD ? APPEND_STATUS_ADDINZIP : APPEND_STATUS_CREATE; + FILE *testFile = fopen(filename, "r"); + if(testFile!=NULL) { + fclose(testFile); + } else { + zipMode = APPEND_STATUS_CREATE; + } + + // Open the zip file + zipFile *zipfile = zipOpen(filename, zipMode); + if (zipfile == NULL) return 1; // Error: ZipFile could not be opened. + + // Buffer to hold data read from the files. + void *read_buffer = malloc(READ_SIZE); + if(read_buffer==NULL) { + // Error: Could not allocate read buffer + return 2; + } + + // Loop all files to add + for(int i = 0; i < fileCount; i++) { + // Open a zipfile to write out the data. + if (zipOpenNewFileInZip(zipfile, files[i], NULL, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION) != ZIP_OK) { + free(read_buffer); + zipClose(zipfile, ""); + return 3; + } + + // Open a file to read the data. + FILE *in = fopen(files[i], "rb"); + if (in == NULL) { + free(read_buffer); + zipCloseFileInZip(zipfile); + zipClose(zipfile, ""); + return 4; + } + + size_t size; + do + { + size = fread(read_buffer, 1, READ_SIZE, in); + + + if(size0) { + //write data to zip + if (zipWriteInFileInZip(zipfile, read_buffer, size) != ZIP_OK) { + //printf("error %d\n", error); + free(read_buffer); + zipCloseFileInZip(zipfile); + zipClose(zipfile, ""); + fclose(in); + return 6; + } + } + } while (size > 0); + + fclose(in); + + zipCloseFileInZip(zipfile); + } + + free(read_buffer); + zipClose(zipfile, comment); + + return 0; +} diff --git a/source/ZipHelper.h b/source/ZipHelper.h new file mode 100644 index 0000000..3d71d01 --- /dev/null +++ b/source/ZipHelper.h @@ -0,0 +1,10 @@ +#pragma once + +#define ZIPHELPER_REPLACE 0 +#define ZIPHELPER_ADD 1 + +#define ZIPHELPER_KEEP_FILES 0 +#define ZIPHELPER_CLEANUP_FILES 1 + +int unzipAndLoad(char *filename, int (*fileCallback)(char *filename), char *expectedComment, int keepFiles); +int zipFiles(char *filename, char **files, int fileCount, int mode, char *comment); diff --git a/source/main.c b/source/main.c index 0e9274d..45f5fcf 100644 --- a/source/main.c +++ b/source/main.c @@ -13,189 +13,72 @@ #include "Menu.h" #include "texturepack.h" #include "Network.h" +#include "SaveLoad.h" +#include "Ingame.h" +#include "Player.h" +#include "Synchronizer.h" +#include "PacketHandler.h" // TODO: Dungeon is way to difficult // -> Skeleton arrows are slower, do a little less damage // -> Or instead of less damage, implement a simple armor system -//TODO: Multiplayer should use normal drawing code -> so remove this first test again -float tmxscr = 400; -float tmyscr = 400; -float tmenuxa = 0.25; -float tmenuya = 0.25; +//TODO: Something still causes desyncs very rarely - -void initMiniMapData() { - int i; - for(i = 0; i < 128 * 128; ++i) { - minimapData[i] = 0; - } -} - -void initMiniMap(bool loadUpWorld) { - int i; - for (i = 0; i < 5; ++i) { - initMinimapLevel(i, loadUpWorld); - } -} - -void initNewMap() { - newSeed(); - createAndValidateSkyMap(128, 128, 0, map[0], data[0]); - createAndValidateTopMap(128, 128, 1, map[1], data[1]); - createAndValidateUndergroundMap(128, 128, 1, 2, map[2], data[2]); - createAndValidateUndergroundMap(128, 128, 2, 3, map[3], data[3]); - createAndValidateUndergroundMap(128, 128, 3, 4, map[4], data[4]); -} - -void setupGame(bool loadUpWorld, bool remote) { - currentLevel = 1; - - // Reset entity manager. - memset(&eManager, 0, sizeof(eManager)); - sf2d_set_clear_color(0xFF6C6D82); //sf2d_set_clear_color(RGBA8(0x82, 0x6D, 0x6C, 0xFF)); - - initMiniMapData(); - - if(!remote) { - if (!loadUpWorld) { - initNewMap(); - initPlayer(); - resetQuests(); - airWizardHealthDisplay = 2000; - int i; - for (i = 0; i < 5; ++i) { - trySpawn(500, i); - } - addEntityToList(newAirWizardEntity(630, 820, 0), &eManager); - daytime = 6000; - day = 0; - season = 0; - rain = false; - } else { - initPlayer(); - resetQuests(); - loadWorld(currentFileName, &eManager, &player, (u8*) map, (u8*) data); - } - - updateMusic(currentLevel, daytime); - - initMiniMap(loadUpWorld); - } else { - //reset level data so no old data can somehow remain - memset(map, 0, 128*128*5 * sizeof(u8)); - memset(data, 0, 128*128*5 * sizeof(u8)); - - currentLevel = 1; - - //TODO: Can Packets get dropped - if yes, should resending be handled by network functions (so I dont need to do it everywhere) - networkPacket packet = { - .requestMapData = { - .type = PACKET_REQUEST_MAPDATA, - .level = 1 - } - }; - networkSend(&packet, sizeof(packetRequestMapData)); - } +void setupGame() { + synchronizerInit(rand(), 1, 0); + synchronizerSetPlayerUID(0, localUID); + synchronizerStart(); - shouldRenderMap = false; - mScrollX = 0; - mScrollY = 0; - zoomLevel = 2; - sprintf(mapText,"x%d",zoomLevel); initGame = 0; } -void setupBGMap(bool loadUpWorld) { +void setupGameServer() { + size_t size; + + networkHostStopConnections(); + + //send start info (seed) + size = writeStartPacket(networkWriteBuffer, rand()); + networkSend(networkWriteBuffer, size); + processPacket(networkWriteBuffer, size); + networkSendWaitFlush(); + + //send save file if loading + FILE *file = fopen(currentFileName, "rb"); + if(file!=NULL) { + sendFile(file, 0, 0); + networkSendWaitFlush(); + fclose(file); + } + + //send start command + size = writeStartRequestPacket(networkWriteBuffer); + networkSend(networkWriteBuffer, size); + processPacket(networkWriteBuffer, size); + + initMPGame = 0; +} + +void setupBGMap() { // Reset entity manager. memset(&eManager, 0, sizeof(eManager)); - sf2d_set_clear_color(0xFF6C6D82); //sf2d_set_clear_color(RGBA8(0x82, 0x6D, 0x6C, 0xFF)); + sf2d_set_clear_color(0xFF6C6D82); - if(!loadUpWorld) { - newSeed(); - createAndValidateTopMap(128, 128, 1, map[1], data[1]); - } else { - loadWorld(currentFileName, &eManager, &player, (u8*) map, (u8*) data); - } + + srand(time(NULL)); + createAndValidateTopMap(128, 128, 1, worldData.map[1], worldData.data[1]); // Reset entity manager. memset(&eManager, 0, sizeof(eManager)); - sf2d_set_clear_color(0xFF6C6D82); //sf2d_set_clear_color(RGBA8(0x82, 0x6D, 0x6C, 0xFF)); + sf2d_set_clear_color(0xFF6C6D82); initBGMap = 0; } -int xscr = 0, yscr = 0; -void tick() { - //TODO: Some stuff DOES need to happen even on client side - if(!isRemote) { - if (player.p.isDead) { - if (player.p.endTimer < 1) { - currentMenu = MENU_LOSE; - } - --player.p.endTimer; - return; - } else if (player.p.hasWon) { - if (player.p.endTimer < 1) { - currentMenu = MENU_WIN; - } - --player.p.endTimer; - return; - } - - tickTouchMap(); - tickTouchQuickSelect(); - ++daytime; - //daytime += 20; - if(daytime>=24000) { - daytime -= 24000; - ++day; - //TODO: maybe make season length not as hardcoded + make the transition better (fade to black and back maybe?) - if(day%7==0) { - ++season; - if(season==4) season = 0; - } - rain = false; - if(season!=3 && rand()%5==0) rain = true; - } - if(daytime==6000 && currentLevel==1) { - playMusic(music_floor1); - } else if(daytime==19000 && currentLevel==1) { - playMusic(music_floor1_night); - } - - int i; - for (i = 0; i < 324; ++i) { - int xx = rand() & 127; - int yy = rand() & 127; - tickTile(xx, yy); - } - tickPlayer(); - xscr = player.x - 100; - yscr = player.y - 56; - if (xscr < 16) - xscr = 16; - else if (xscr > 1832) - xscr = 1832; - if (yscr < 16) - yscr = 16; - else if (yscr > 1912) - yscr = 1912; - - if(eManager.lastSlot[currentLevel]<80 && currentLevel != 5) { - trySpawn(1, currentLevel); - } - - for (i = 0; i < eManager.lastSlot[currentLevel]; ++i) { - Entity * e = &eManager.entities[currentLevel][i]; - if ((e->type != ENTITY_ZOMBIE && e->type != ENTITY_SKELETON && e->type != ENTITY_KNIGHT && e->type != ENTITY_SLIME && e->type != ENTITY_PASSIVE) - || (e->type == ENTITY_GLOWWORM && (daytime>6000 || daytime<18000)) - || (e->x > player.x - 160 && e->y > player.y - 125 && e->x < player.x + 160 && e->y < player.y + 125)) - tickEntity(e); - } - } -} +//for rendering -> move to a better place +extern int xscr, yscr; char debugText[34]; char bossHealthText[34]; @@ -203,7 +86,7 @@ int main() { cfguInit(); CFGU_GetSystemModel(&MODEL_3DS); FILE * file; - shouldRenderDebug = true; + shouldRenderDebug = false; if ((file = fopen("settings.bin", "r"))) { fread(&shouldRenderDebug,sizeof(bool),1,file); fread(&shouldSpeedup,sizeof(bool),1,file); @@ -213,6 +96,21 @@ int main() { sf2d_init(); csndInit(); networkInit(); + + srand(time(NULL)); + + //load or create localUID + if ((file = fopen("m3ds_uid.bin", "rb"))) { + fread(&localUID, sizeof(u32), 1, file); + fclose(file); + } else { + localUID = (((u32) (rand()%256))<<24) | (((u32) (rand()%256))<<16) | (((u32) (rand()%256))<<8) | (((u32) (rand()%256))); + + if ((file = fopen("m3ds_uid.bin", "wb"))) { + fwrite(&localUID, sizeof(u32), 1, file); + fclose(file); + } + } noItem = newItem(ITEM_NULL, 0); @@ -220,13 +118,15 @@ int main() { currentMenu = MENU_TITLE; currentSelection = 0; quitGame = false; + initBGMap = 1; icons = sfil_load_PNG_buffer(icons2_png, SF2D_PLACE_RAM); + playerSprites = sfil_load_PNG_buffer(player_png, SF2D_PLACE_RAM); font = sfil_load_PNG_buffer(Font_png, SF2D_PLACE_RAM); bottombg = sfil_load_PNG_buffer(bottombg_png, SF2D_PLACE_RAM); loadSounds(); - playMusic(music_menu); + playMusic(&music_menu); bakeLights(); @@ -244,33 +144,33 @@ int main() { sf2d_set_clear_color(0xFF); /* Default inputs */ - k_up.input = KEY_DUP | KEY_CPAD_UP | KEY_CSTICK_UP; - k_down.input = KEY_DDOWN | KEY_CPAD_DOWN | KEY_CSTICK_DOWN; - k_left.input = KEY_DLEFT | KEY_CPAD_LEFT | KEY_CSTICK_LEFT; - k_right.input = KEY_DRIGHT | KEY_CPAD_RIGHT | KEY_CSTICK_RIGHT; - k_attack.input = KEY_A | KEY_B | KEY_L | KEY_ZR; - k_menu.input = KEY_X | KEY_Y | KEY_R | KEY_ZL; - k_pause.input = KEY_START; - k_accept.input = KEY_A; - k_decline.input = KEY_B; - k_delete.input = KEY_X; - k_menuNext.input = KEY_R; - k_menuPrev.input = KEY_L; + localInputs.k_up.input = KEY_DUP | KEY_CPAD_UP | KEY_CSTICK_UP; + localInputs.k_down.input = KEY_DDOWN | KEY_CPAD_DOWN | KEY_CSTICK_DOWN; + localInputs.k_left.input = KEY_DLEFT | KEY_CPAD_LEFT | KEY_CSTICK_LEFT; + localInputs.k_right.input = KEY_DRIGHT | KEY_CPAD_RIGHT | KEY_CSTICK_RIGHT; + localInputs.k_attack.input = KEY_A | KEY_B | KEY_L | KEY_ZR; + localInputs.k_menu.input = KEY_X | KEY_Y | KEY_R | KEY_ZL; + localInputs.k_pause.input = KEY_START; + localInputs.k_accept.input = KEY_A; + localInputs.k_decline.input = KEY_B; + localInputs.k_delete.input = KEY_X; + localInputs.k_menuNext.input = KEY_R; + localInputs.k_menuPrev.input = KEY_L; /* If btnSave exists, then use that. */ if ((file = fopen("btnSave.bin", "rb"))) { - fread(&k_up.input, sizeof(int), 1, file); - fread(&k_down.input, sizeof(int), 1, file); - fread(&k_left.input, sizeof(int), 1, file); - fread(&k_right.input, sizeof(int), 1, file); - fread(&k_attack.input, sizeof(int), 1, file); - fread(&k_menu.input, sizeof(int), 1, file); - fread(&k_pause.input, sizeof(int), 1, file); - fread(&k_accept.input, sizeof(int), 1, file); - fread(&k_decline.input, sizeof(int), 1, file); - fread(&k_delete.input, sizeof(int), 1, file); - fread(&k_menuNext.input, sizeof(int), 1, file); - fread(&k_menuPrev.input, sizeof(int), 1, file); + fread(&(localInputs.k_up.input), sizeof(int), 1, file); + fread(&(localInputs.k_down.input), sizeof(int), 1, file); + fread(&(localInputs.k_left.input), sizeof(int), 1, file); + fread(&(localInputs.k_right.input), sizeof(int), 1, file); + fread(&(localInputs.k_attack.input), sizeof(int), 1, file); + fread(&(localInputs.k_menu.input), sizeof(int), 1, file); + fread(&(localInputs.k_pause.input), sizeof(int), 1, file); + fread(&(localInputs.k_accept.input), sizeof(int), 1, file); + fread(&(localInputs.k_decline.input), sizeof(int), 1, file); + fread(&(localInputs.k_delete.input), sizeof(int), 1, file); + fread(&(localInputs.k_menuNext.input), sizeof(int), 1, file); + fread(&(localInputs.k_menuPrev.input), sizeof(int), 1, file); fclose(file); } @@ -281,102 +181,25 @@ int main() { loadTexturePack(fnbuf); fclose(file); } - - tickCount = 0; + + initPlayers(); initRecipes(); - initQuests(); + initTrades(); while (aptMainLoop()) { - ++tickCount; - hidScanInput(); - tickKeys(hidKeysHeld(), hidKeysDown()); - if (quitGame) break; - if (initGame > 0) setupGame(initGame == 1 ? true : false, isRemote); - if (initBGMap > 0) setupBGMap(initBGMap == 1 ? true : false); - - networkRecieve(); + if (initGame > 0 && --initGame==0) setupGame(); + if (initMPGame > 0 && --initMPGame==0) setupGameServer(); + if (initBGMap > 0 && --initBGMap==0) setupBGMap(); - if (currentMenu == 0) { - tick(); - //TODO: Multiplayer should use normal drawing code -> so remove this first test again - if(!isRemote) { - sf2d_start_frame(GFX_TOP, GFX_LEFT); - - offsetX = xscr; - offsetY = yscr; - sf2d_draw_rectangle(0, 0, 400, 240, 0xFF0C0C0C); //RGBA8(12, 12, 12, 255)); //You might think "real" black would be better, but it actually looks better that way - - renderLightsToStencil(false, false, true); - - renderBackground(xscr, yscr); - renderEntities(player.x, player.y, &eManager); - renderPlayer(); - renderWeather(xscr, yscr); - - resetStencilStuff(); - - renderDayNight(); - - offsetX = 0; - offsetY = 0; - - if(shouldRenderDebug){ - sprintf(fpsstr, " FPS: %.0f, X:%d, Y:%d, E:%d", sf2d_get_fps(), player.x, player.y, eManager.lastSlot[currentLevel]); - drawText(fpsstr, 2, 225); - } - - sf2d_end_frame(); - - sf2d_start_frame(GFX_BOTTOM, GFX_LEFT); - if(!shouldRenderMap){ - sf2d_draw_texture(bottombg, 0, 0); - renderGui(); - } else { - renderZoomedMap(); - } - sf2d_end_frame(); - //TODO: Multiplayer should use normal drawing code -> so remove this first test again - } else { - //TODO: Temporary way of getting back to the menu - if (k_pause.clicked){ - sf2d_set_clear_color(0xFF); - currentSelection = 0; - currentMenu = MENU_TITLE; - - networkDisconnect(); - - playMusic(music_menu); - } - - tmxscr += tmenuxa; - tmyscr += tmenuya; - - if (tmxscr < 16) { - tmxscr = 16; - tmenuxa = -tmenuxa; - } else if (tmxscr > 1832) { - tmxscr = 1832; - tmenuxa = -tmenuxa; - } - if (tmyscr < 16) { - tmyscr = 16; - tmenuya = -tmenuya; - } else if (tmyscr > 1792) { - tmyscr = 1792; - tmenuya = -tmenuya; - } - - sf2d_start_frame(GFX_TOP, GFX_LEFT); - offsetX = (int) tmxscr; offsetY = (int) tmyscr; - renderBackground((int) tmxscr, (int) tmyscr); - offsetX = 0; offsetY = 0; - sf2d_end_frame(); - - sf2d_start_frame(GFX_BOTTOM, GFX_LEFT); - sf2d_end_frame(); - } + if (currentMenu == MENU_NONE) { + tickGame(); + renderGame(); } else { + //input scanning ingame is handled by the synchronizer + hidScanInput(); + tickKeys(&localInputs, hidKeysHeld(), hidKeysDown()); + tickMenu(currentMenu); renderMenu(currentMenu, xscr, yscr); } @@ -386,8 +209,9 @@ int main() { stopMusic(); - freeQuests(); + freeTrades(); freeRecipes(); + freePlayers(); freeLightBakes(); sf2d_free_texture(icons); diff --git a/source/minizip/crypt.h b/source/minizip/crypt.h index 2d69da4..622f4bc 100644 --- a/source/minizip/crypt.h +++ b/source/minizip/crypt.h @@ -1,9 +1,9 @@ /* crypt.h -- base code for crypt/uncrypt ZIPfile - Version 1.01h, December 28th, 2009 + Version 1.01e, February 12th, 2005 - Copyright (C) 1998-2009 Gilles Vollant + Copyright (C) 1998-2005 Gilles Vollant This code is a modified version of crypting code in Infozip distribution diff --git a/source/minizip/ioapi.c b/source/minizip/ioapi.c index 51a9058..f1bee23 100644 --- a/source/minizip/ioapi.c +++ b/source/minizip/ioapi.c @@ -1,9 +1,9 @@ /* ioapi.c -- IO base function header for compress/uncompress .zip files using zlib + zip or unzip API - Version 1.01h, December 28th, 2009 + Version 1.01e, February 12th, 2005 - Copyright (C) 1998-2009 Gilles Vollant + Copyright (C) 1998-2005 Gilles Vollant */ #include @@ -141,8 +141,7 @@ long ZCALLBACK fseek_file_func (opaque, stream, offset, origin) default: return -1; } ret = 0; - if (fseek((FILE *)stream, offset, fseek_origin) != 0) - ret = -1; + fseek((FILE *)stream, offset, fseek_origin); return ret; } diff --git a/source/minizip/ioapi.h b/source/minizip/ioapi.h index 1dba776..7d457ba 100644 --- a/source/minizip/ioapi.h +++ b/source/minizip/ioapi.h @@ -1,9 +1,9 @@ /* ioapi.h -- IO base function header for compress/uncompress .zip files using zlib + zip or unzip API - Version 1.01h, December 28th, 2009 + Version 1.01e, February 12th, 2005 - Copyright (C) 1998-2009 Gilles Vollant + Copyright (C) 1998-2005 Gilles Vollant */ #ifndef _ZLIBIOAPI_H diff --git a/source/minizip/unzip.c b/source/minizip/unzip.c index 17d730d..9ad4766 100644 --- a/source/minizip/unzip.c +++ b/source/minizip/unzip.c @@ -1,7 +1,7 @@ /* unzip.c -- IO for uncompress .zip files using zlib - Version 1.01h, December 28th, 2009 + Version 1.01e, February 12th, 2005 - Copyright (C) 1998-2009 Gilles Vollant + Copyright (C) 1998-2005 Gilles Vollant Read unzip.h for more info */ @@ -103,9 +103,6 @@ typedef struct { char *read_buffer; /* internal buffer for compressed data */ z_stream stream; /* zLib stream structure for inflate */ -#ifdef HAVE_BZIP2 - bz_stream bstream; /* bzLib stream structure for bziped */ -#endif uLong pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ uLong stream_initialised; /* flag set if stream structure is initialised*/ @@ -207,7 +204,7 @@ local int unzlocal_getShort (pzlib_filefunc_def,filestream,pX) uLong *pX; { uLong x ; - int i = 0; + int i; int err; err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); @@ -235,7 +232,7 @@ local int unzlocal_getLong (pzlib_filefunc_def,filestream,pX) uLong *pX; { uLong x ; - int i = 0; + int i; int err; err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); @@ -494,11 +491,8 @@ extern unzFile ZEXPORT unzOpen2 (path, pzlib_filefunc_def) s=(unz_s*)ALLOC(sizeof(unz_s)); - if (s!=NULL) - { - *s=us; - unzGoToFirstFile((unzFile)s); - } + *s=us; + unzGoToFirstFile((unzFile)s); return (unzFile)s; } @@ -614,12 +608,10 @@ local int unzlocal_GetCurrentFileInfoInternal (file, /* we check the magic */ if (err==UNZ_OK) - { if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) err=UNZ_ERRNO; else if (uMagic!=0x02014b50) err=UNZ_BADZIPFILE; - } if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK) err=UNZ_ERRNO; @@ -696,13 +688,10 @@ local int unzlocal_GetCurrentFileInfoInternal (file, uSizeRead = extraFieldBufferSize; if (lSeek!=0) - { if (ZSEEK(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) lSeek=0; else err=UNZ_ERRNO; - } - if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) if (ZREAD(s->z_filefunc, s->filestream,extraField,uSizeRead)!=uSizeRead) err=UNZ_ERRNO; @@ -724,13 +713,10 @@ local int unzlocal_GetCurrentFileInfoInternal (file, uSizeRead = commentBufferSize; if (lSeek!=0) - { if (ZSEEK(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) lSeek=0; else err=UNZ_ERRNO; - } - if ((file_info.size_file_comment>0) && (commentBufferSize>0)) if (ZREAD(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead) err=UNZ_ERRNO; @@ -991,12 +977,10 @@ local int unzlocal_CheckCurrentFileCoherencyHeader (s,piSizeVar, if (err==UNZ_OK) - { if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) err=UNZ_ERRNO; else if (uMagic!=0x04034b50) err=UNZ_BADZIPFILE; - } if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) err=UNZ_ERRNO; @@ -1013,9 +997,6 @@ local int unzlocal_CheckCurrentFileCoherencyHeader (s,piSizeVar, err=UNZ_BADZIPFILE; if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && -/* #ifdef HAVE_BZIP2 */ - (s->cur_file_info.compression_method!=Z_BZIP2ED) && -/* #endif */ (s->cur_file_info.compression_method!=Z_DEFLATED)) err=UNZ_BADZIPFILE; @@ -1130,9 +1111,6 @@ extern int ZEXPORT unzOpenCurrentFile3 (file, method, level, raw, password) } if ((s->cur_file_info.compression_method!=0) && -/* #ifdef HAVE_BZIP2 */ - (s->cur_file_info.compression_method!=Z_BZIP2ED) && -/* #endif */ (s->cur_file_info.compression_method!=Z_DEFLATED)) err=UNZ_BADZIPFILE; @@ -1146,34 +1124,6 @@ extern int ZEXPORT unzOpenCurrentFile3 (file, method, level, raw, password) pfile_in_zip_read_info->stream.total_out = 0; - if ((s->cur_file_info.compression_method==Z_BZIP2ED) && - (!raw)) - { -#ifdef HAVE_BZIP2 - pfile_in_zip_read_info->bstream.bzalloc = (void *(*) (void *, int, int))0; - pfile_in_zip_read_info->bstream.bzfree = (free_func)0; - pfile_in_zip_read_info->bstream.opaque = (voidpf)0; - pfile_in_zip_read_info->bstream.state = (voidpf)0; - - pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; - pfile_in_zip_read_info->stream.zfree = (free_func)0; - pfile_in_zip_read_info->stream.opaque = (voidpf)0; - pfile_in_zip_read_info->stream.next_in = (voidpf)0; - pfile_in_zip_read_info->stream.avail_in = 0; - - err=BZ2_bzDecompressInit(&pfile_in_zip_read_info->bstream, 0, 0); - if (err == Z_OK) - pfile_in_zip_read_info->stream_initialised=Z_BZIP2ED; - else - { - TRYFREE(pfile_in_zip_read_info); - return err; - } -#else - pfile_in_zip_read_info->raw=1; -#endif - } - else if ((s->cur_file_info.compression_method==Z_DEFLATED) && (!raw)) { @@ -1185,7 +1135,7 @@ extern int ZEXPORT unzOpenCurrentFile3 (file, method, level, raw, password) err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); if (err == Z_OK) - pfile_in_zip_read_info->stream_initialised=Z_DEFLATED; + pfile_in_zip_read_info->stream_initialised=1; else { TRYFREE(pfile_in_zip_read_info); @@ -1213,8 +1163,6 @@ extern int ZEXPORT unzOpenCurrentFile3 (file, method, level, raw, password) s->pfile_in_zip_read = pfile_in_zip_read_info; - s->encrypted = 0; - # ifndef NOUNCRYPT if (password != NULL) { @@ -1386,53 +1334,6 @@ extern int ZEXPORT unzReadCurrentFile (file, buf, len) iRead += uDoCopy; } else - if (pfile_in_zip_read_info->compression_method==Z_BZIP2ED) - { -#ifdef HAVE_BZIP2 - uLong uTotalOutBefore,uTotalOutAfter; - const Bytef *bufBefore; - uLong uOutThis; - - pfile_in_zip_read_info->bstream.next_in = pfile_in_zip_read_info->stream.next_in; - pfile_in_zip_read_info->bstream.avail_in = pfile_in_zip_read_info->stream.avail_in; - pfile_in_zip_read_info->bstream.total_in_lo32 = pfile_in_zip_read_info->stream.total_in; - pfile_in_zip_read_info->bstream.total_in_hi32 = 0; - pfile_in_zip_read_info->bstream.next_out = pfile_in_zip_read_info->stream.next_out; - pfile_in_zip_read_info->bstream.avail_out = pfile_in_zip_read_info->stream.avail_out; - pfile_in_zip_read_info->bstream.total_out_lo32 = pfile_in_zip_read_info->stream.total_out; - pfile_in_zip_read_info->bstream.total_out_hi32 = 0; - - uTotalOutBefore = pfile_in_zip_read_info->bstream.total_out_lo32; - bufBefore = pfile_in_zip_read_info->bstream.next_out; - - err=BZ2_bzDecompress(&pfile_in_zip_read_info->bstream); - - uTotalOutAfter = pfile_in_zip_read_info->bstream.total_out_lo32; - uOutThis = uTotalOutAfter-uTotalOutBefore; - - pfile_in_zip_read_info->crc32 = - crc32(pfile_in_zip_read_info->crc32,bufBefore, - (uInt)(uOutThis)); - - pfile_in_zip_read_info->rest_read_uncompressed -= - uOutThis; - - iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); - - pfile_in_zip_read_info->stream.next_in = pfile_in_zip_read_info->bstream.next_in; - pfile_in_zip_read_info->stream.avail_in = pfile_in_zip_read_info->bstream.avail_in; - pfile_in_zip_read_info->stream.total_in = pfile_in_zip_read_info->bstream.total_in_lo32; - pfile_in_zip_read_info->stream.next_out = pfile_in_zip_read_info->bstream.next_out; - pfile_in_zip_read_info->stream.avail_out = pfile_in_zip_read_info->bstream.avail_out; - pfile_in_zip_read_info->stream.total_out = pfile_in_zip_read_info->bstream.total_out_lo32; - - if (err==BZ_STREAM_END) - return (iRead==0) ? UNZ_EOF : iRead; - if (err!=BZ_OK) - break; -#endif - } - else { uLong uTotalOutBefore,uTotalOutAfter; const Bytef *bufBefore; @@ -1611,12 +1512,8 @@ extern int ZEXPORT unzCloseCurrentFile (file) TRYFREE(pfile_in_zip_read_info->read_buffer); pfile_in_zip_read_info->read_buffer = NULL; - if (pfile_in_zip_read_info->stream_initialised == Z_DEFLATED) + if (pfile_in_zip_read_info->stream_initialised) inflateEnd(&pfile_in_zip_read_info->stream); -#ifdef HAVE_BZIP2 - else if (pfile_in_zip_read_info->stream_initialised == Z_BZIP2ED) - BZ2_bzDecompressEnd(&pfile_in_zip_read_info->bstream); -#endif pfile_in_zip_read_info->stream_initialised = 0; TRYFREE(pfile_in_zip_read_info); @@ -1637,6 +1534,7 @@ extern int ZEXPORT unzGetGlobalComment (file, szComment, uSizeBuf) char *szComment; uLong uSizeBuf; { + int err=UNZ_OK; unz_s* s; uLong uReadThis ; if (file==NULL) @@ -1669,7 +1567,7 @@ extern uLong ZEXPORT unzGetOffset (file) unz_s* s; if (file==NULL) - return 0; + return UNZ_PARAMERROR; s=(unz_s*)file; if (!s->current_file_ok) return 0; diff --git a/source/minizip/unzip.h b/source/minizip/unzip.h index 94c1b69..b247937 100644 --- a/source/minizip/unzip.h +++ b/source/minizip/unzip.h @@ -1,7 +1,7 @@ /* unzip.h -- IO for uncompress .zip files using zlib - Version 1.01h, December 28th, 2009 + Version 1.01e, February 12th, 2005 - Copyright (C) 1998-2009 Gilles Vollant + Copyright (C) 1998-2005 Gilles Vollant This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g WinZip, InfoZip tools and compatible. @@ -57,12 +57,6 @@ extern "C" { #include "ioapi.h" #endif -#ifdef HAVE_BZIP2 -#include "bzlib.h" -#endif - -#define Z_BZIP2ED 12 - #if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) /* like the STRICT of WIN32, we define a pointer that cannot be converted from (void*) without cast */ diff --git a/source/minizip/zip.c b/source/minizip/zip.c index 1451d61..7fbe002 100644 --- a/source/minizip/zip.c +++ b/source/minizip/zip.c @@ -1,10 +1,10 @@ /* zip.c -- IO on .zip files using zlib - Version 1.01h, December 28th, 2009 + Version 1.01e, February 12th, 2005 27 Dec 2004 Rolf Kalbermatter Modification to zipOpen2 to support globalComment retrieval. - Copyright (C) 1998-2009 Gilles Vollant + Copyright (C) 1998-2005 Gilles Vollant Read zip.h for more info */ @@ -320,9 +320,9 @@ local uLong ziplocal_TmzDateToDosDate(ptm,dosDate) uLong dosDate; { uLong year = (uLong)ptm->tm_year; - if (year>=1980) + if (year>1980) year-=1980; - else if (year>=80) + else if (year>80) year-=80; return (uLong) (((ptm->tm_mday) + (32 * (ptm->tm_mon+1)) + (512 * year)) << 16) | @@ -373,7 +373,7 @@ local int ziplocal_getShort (pzlib_filefunc_def,filestream,pX) uLong *pX; { uLong x ; - int i = 0; + int i; int err; err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); @@ -401,7 +401,7 @@ local int ziplocal_getLong (pzlib_filefunc_def,filestream,pX) uLong *pX; { uLong x ; - int i = 0; + int i; int err; err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); @@ -432,69 +432,67 @@ local int ziplocal_getLong (pzlib_filefunc_def,filestream,pX) /* Locate the Central directory of a zipfile (at the end, just before the global comment) - Fix from Riccardo Cohen */ local uLong ziplocal_SearchCentralDir OF(( const zlib_filefunc_def* pzlib_filefunc_def, voidpf filestream)); local uLong ziplocal_SearchCentralDir(pzlib_filefunc_def,filestream) - const zlib_filefunc_def* pzlib_filefunc_def; - voidpf filestream; + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; { - unsigned char* buf; - uLong uSizeFile; - uLong uBackRead; - uLong uMaxBack=0xffff; /* maximum size of global comment */ - uLong uPosFound=0; + unsigned char* buf; + uLong uSizeFile; + uLong uBackRead; + uLong uMaxBack=0xffff; /* maximum size of global comment */ + uLong uPosFound=0; - if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) - return 0; + if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; - uSizeFile = ZTELL(*pzlib_filefunc_def,filestream); + uSizeFile = ZTELL(*pzlib_filefunc_def,filestream); - if (uMaxBack>uSizeFile) - uMaxBack = uSizeFile; + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; - buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); - if (buf==NULL) - return 0; + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; - uBackRead = 4; - while (uBackReaduMaxBack) - uBackRead = uMaxBack; - else - uBackRead+=BUFREADCOMMENT; - uReadPos = uSizeFile-uBackRead ; + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; - uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? - (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); - if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) - break; + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); + if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; - if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) - break; + if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; - for (i=(int)uReadSize-3; (i--)>0;) - if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && - ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) - { - uPosFound = uReadPos+i; - break; - } + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } - if (uPosFound!=0) - break; - } - TRYFREE(buf); - return uPosFound; + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; } - #endif /* !NO_ADDFILEINEXISTINGZIP*/ /************************************************************/ @@ -523,8 +521,6 @@ extern zipFile ZEXPORT zipOpen2 (pathname, append, globalcomment, pzlib_filefunc if (ziinit.filestream == NULL) return NULL; - if (append == APPEND_STATUS_CREATEAFTER) - ZSEEK(ziinit.z_filefunc,ziinit.filestream,0,SEEK_END); ziinit.begin_pos = ZTELL(ziinit.z_filefunc,ziinit.filestream); ziinit.in_opened_file_inzip = 0; ziinit.ci.stream_initialised = 0; @@ -562,10 +558,9 @@ extern zipFile ZEXPORT zipOpen2 (pathname, append, globalcomment, pzlib_filefunc uLong size_comment; central_pos = ziplocal_SearchCentralDir(&ziinit.z_filefunc,ziinit.filestream); -/* disable to allow appending to empty ZIP archive if (central_pos==0) err=ZIP_ERRNO; -*/ + if (ZSEEK(ziinit.z_filefunc, ziinit.filestream, central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) err=ZIP_ERRNO; @@ -620,7 +615,7 @@ extern zipFile ZEXPORT zipOpen2 (pathname, append, globalcomment, pzlib_filefunc if (size_comment>0) { - ziinit.globalcomment = (char*)ALLOC(size_comment+1); + ziinit.globalcomment = ALLOC(size_comment+1); if (ziinit.globalcomment) { size_comment = ZREAD(ziinit.z_filefunc, ziinit.filestream,ziinit.globalcomment,size_comment); @@ -692,12 +687,12 @@ extern zipFile ZEXPORT zipOpen (pathname, append) return zipOpen2(pathname,append,NULL,NULL); } -extern int ZEXPORT zipOpenNewFileInZip4 (file, filename, zipfi, +extern int ZEXPORT zipOpenNewFileInZip3 (file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, comment, method, level, raw, windowBits, memLevel, strategy, - password, crcForCrypting, versionMadeBy, flagBase) + password, crcForCrypting) zipFile file; const char* filename; const zip_fileinfo* zipfi; @@ -714,8 +709,6 @@ extern int ZEXPORT zipOpenNewFileInZip4 (file, filename, zipfi, int strategy; const char* password; uLong crcForCrypting; - uLong versionMadeBy; - uLong flagBase; { zip_internal* zi; uInt size_filename; @@ -762,7 +755,7 @@ extern int ZEXPORT zipOpenNewFileInZip4 (file, filename, zipfi, else zi->ci.dosDate = ziplocal_TmzDateToDosDate(&zipfi->tmz_date,zipfi->dosDate); } - zi->ci.flag = flagBase; + zi->ci.flag = 0; if ((level==8) || (level==9)) zi->ci.flag |= 2; if ((level==2)) @@ -785,7 +778,7 @@ extern int ZEXPORT zipOpenNewFileInZip4 (file, filename, zipfi, ziplocal_putValue_inmemory(zi->ci.central_header,(uLong)CENTRALHEADERMAGIC,4); /* version info */ - ziplocal_putValue_inmemory(zi->ci.central_header+4,(uLong)versionMadeBy,2); + ziplocal_putValue_inmemory(zi->ci.central_header+4,(uLong)VERSIONMADEBY,2); ziplocal_putValue_inmemory(zi->ci.central_header+6,(uLong)20,2); ziplocal_putValue_inmemory(zi->ci.central_header+8,(uLong)zi->ci.flag,2); ziplocal_putValue_inmemory(zi->ci.central_header+10,(uLong)zi->ci.method,2); @@ -864,7 +857,6 @@ extern int ZEXPORT zipOpenNewFileInZip4 (file, filename, zipfi, zi->ci.stream.next_out = zi->ci.buffered_data; zi->ci.stream.total_in = 0; zi->ci.stream.total_out = 0; - zi->ci.stream.data_type = Z_BINARY; if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) { @@ -920,46 +912,14 @@ extern int ZEXPORT zipOpenNewFileInZip2(file, filename, zipfi, int level; int raw; { - return zipOpenNewFileInZip4 (file, filename, zipfi, + return zipOpenNewFileInZip3 (file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, comment, method, level, raw, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, - NULL, 0, VERSIONMADEBY, 0); + NULL, 0); } -extern int ZEXPORT zipOpenNewFileInZip3 (file, filename, zipfi, - extrafield_local, size_extrafield_local, - extrafield_global, size_extrafield_global, - comment, method, level, raw, - windowBits, memLevel, strategy, - password, crcForCrypting) - zipFile file; - const char* filename; - const zip_fileinfo* zipfi; - const void* extrafield_local; - uInt size_extrafield_local; - const void* extrafield_global; - uInt size_extrafield_global; - const char* comment; - int method; - int level; - int raw; - int windowBits; - int memLevel; - int strategy; - const char* password; - uLong crcForCrypting; -{ - return zipOpenNewFileInZip4 (file, filename, zipfi, - extrafield_local, size_extrafield_local, - extrafield_global, size_extrafield_global, - comment, method, level, raw, - windowBits, memLevel, strategy, - password, crcForCrypting, VERSIONMADEBY, 0); -} - - extern int ZEXPORT zipOpenNewFileInZip (file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, @@ -975,12 +935,10 @@ extern int ZEXPORT zipOpenNewFileInZip (file, filename, zipfi, int method; int level; { - return zipOpenNewFileInZip4 (file, filename, zipfi, + return zipOpenNewFileInZip2 (file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, - comment, method, level, 0, - -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, - NULL, 0, VERSIONMADEBY, 0); + comment, method, level, 0); } local int zipFlushWriteBuffer(zi) @@ -1020,9 +978,9 @@ extern int ZEXPORT zipWriteInFileInZip (file, buf, len) if (zi->in_opened_file_inzip == 0) return ZIP_PARAMERROR; - zi->ci.stream.next_in = (Bytef*)buf; + zi->ci.stream.next_in = (void*)buf; zi->ci.stream.avail_in = len; - zi->ci.crc32 = crc32(zi->ci.crc32,buf,(uInt)len); + zi->ci.crc32 = crc32(zi->ci.crc32,buf,len); while ((err==ZIP_OK) && (zi->ci.stream.avail_in>0)) { @@ -1112,9 +1070,7 @@ extern int ZEXPORT zipCloseFileInZipRaw (file, uncompressed_size, crc32) if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) { - int tmp_err=deflateEnd(&zi->ci.stream); - if (err == ZIP_OK) - err = tmp_err; + err=deflateEnd(&zi->ci.stream); zi->ci.stream_initialised = 0; } @@ -1217,7 +1173,7 @@ extern int ZEXPORT zipClose (file, global_comment) ldi = ldi->next_datablock; } } - free_linkedlist(&(zi->central_dir)); + free_datablock(zi->central_dir.first_block); if (err==ZIP_OK) /* Magic End */ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)ENDHEADERMAGIC,4); diff --git a/source/minizip/zip.h b/source/minizip/zip.h index 39215ca..acacce8 100644 --- a/source/minizip/zip.h +++ b/source/minizip/zip.h @@ -1,7 +1,7 @@ /* zip.h -- IO for compress .zip files using zlib - Version 1.01h, December 28th, 2009 + Version 1.01e, February 12th, 2005 - Copyright (C) 1998-2009 Gilles Vollant + Copyright (C) 1998-2005 Gilles Vollant This unzip package allow creates .ZIP file, compatible with PKZip 2.04g WinZip, InfoZip tools and compatible. @@ -191,7 +191,8 @@ extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file, int memLevel, int strategy, const char* password, - uLong crcForCrypting)); + uLong crcForCtypting)); + /* Same than zipOpenNewFileInZip2, except windowBits,memLevel,,strategy : see parameter strategy in deflateInit2 @@ -199,29 +200,6 @@ extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file, crcForCtypting : crc of file to compress (needed for crypting) */ -extern int ZEXPORT zipOpenNewFileInZip4 OF((zipFile file, - const char* filename, - const zip_fileinfo* zipfi, - const void* extrafield_local, - uInt size_extrafield_local, - const void* extrafield_global, - uInt size_extrafield_global, - const char* comment, - int method, - int level, - int raw, - int windowBits, - int memLevel, - int strategy, - const char* password, - uLong crcForCrypting, - uLong versionMadeBy, - uLong flagBase)); -/* - Same than zipOpenNewFileInZip4, except - versionMadeBy : value for Version made by field - flag : value for flag field (compression level info will be added) - */ extern int ZEXPORT zipWriteInFileInZip OF((zipFile file, const void* buf, diff --git a/source/texturepack.c b/source/texturepack.c index a79f47b..e2a18ff 100644 --- a/source/texturepack.c +++ b/source/texturepack.c @@ -1,9 +1,13 @@ #include "texturepack.h" -#define dir_delimter '/' -#define MAX_FILENAME 256 -#define READ_SIZE 9216 +#include "ZipHelper.h" +#define MAX_FILENAME 256 + +bool texturepackUseDefaultIcons = true; +bool texturepackUseDefaultPlayer = true; +bool texturepackUseDefaultFont = true; +bool texturepackUseDefaultBottom = true; void toLowerString(char * str){ int i; @@ -31,136 +35,66 @@ int getTexturePackComment(char * filename, char * cmmtBuf){ return 0; } -int loadTexturePack(char * filename){ +int loadTexture(char * filename) { + char lowerFilename[MAX_FILENAME]; + strcpy(lowerFilename,filename); + toLowerString(lowerFilename); - bool useDefaultIcons = true; - bool useDefaultFont = true; - bool useDefaultBottom = true; - - // Open the zip file - unzFile *zipfile = unzOpen(filename); - if ( zipfile == NULL ) return 1; // Error: ZipFile could not be opened. - - // Get info about the zip file - unz_global_info global_info; - if (unzGetGlobalInfo(zipfile, &global_info ) != UNZ_OK ) - { - unzClose( zipfile ); - return 2; // Error: Could not read global info - } - - // Buffer to hold data read from the zip file. - char read_buffer[ READ_SIZE ]; - - // Loop to extract all files - uLong i; - for ( i = 0; i < global_info.number_entry; ++i ) - { - // Get info about current file. - unz_file_info file_info; - char filename[ MAX_FILENAME ]; - if (unzGetCurrentFileInfo(zipfile,&file_info,filename,MAX_FILENAME,NULL, 0, NULL, 0 ) != UNZ_OK ){ - unzClose( zipfile ); - return 3; // Error: Could not read file info + if(strcmp(lowerFilename, "icons.png") == 0){ + if(sfil_load_PNG_file(filename, SF2D_PLACE_RAM) == NULL){ + return 0; } - - // Check if this entry is NOT a directory or file. - const size_t filename_length = strlen( filename ); - if ( filename[ filename_length-1 ] != dir_delimter ){ - if ( unzOpenCurrentFile( zipfile ) != UNZ_OK ) - { - unzClose( zipfile ); - return 4; - } - - // Open a file to write out the data. - FILE * out = fopen(filename, "wb" ); - if ( out == NULL ) - { - unzCloseCurrentFile( zipfile ); - unzClose( zipfile ); - return 5; - } - - int error = UNZ_OK; - do - { - error = unzReadCurrentFile( zipfile, read_buffer, READ_SIZE ); - if ( error < 0 ) - { - //printf( "error %d\n", error ); - unzCloseCurrentFile( zipfile ); - unzClose( zipfile ); - return 6; - } - - // Write data to file. - if ( error > 0 ) - { - fwrite( read_buffer, error, 1, out ); // You should check return of fwrite... - } - } while ( error > 0 ); - - fclose(out); - - char lowerFilename[MAX_FILENAME]; - strcpy(lowerFilename,filename); - toLowerString(lowerFilename); - - if(strcmp(lowerFilename,"icons.png") == 0){ - if(sfil_load_PNG_file(filename, SF2D_PLACE_RAM) == NULL){ - unzCloseCurrentFile( zipfile ); - unzClose( zipfile ); - return 7; - } - icons = sfil_load_PNG_file(filename, SF2D_PLACE_RAM); - - reloadColors(); - - useDefaultIcons = false; - - } else if(strcmp(lowerFilename,"font.png") == 0){ - if(sfil_load_PNG_file(filename, SF2D_PLACE_RAM) == NULL){ - unzCloseCurrentFile( zipfile ); - unzClose( zipfile ); - return 7; - } - font = sfil_load_PNG_file(filename, SF2D_PLACE_RAM); - useDefaultFont = false; - } else if(strcmp(lowerFilename,"bottombg.png") == 0){ - if(sfil_load_PNG_file(filename, SF2D_PLACE_RAM) == NULL){ - unzCloseCurrentFile( zipfile ); - unzClose( zipfile ); - return 7; - } - bottombg = sfil_load_PNG_file(filename, SF2D_PLACE_RAM); - useDefaultBottom = false; - } - - remove(filename); + + icons = sfil_load_PNG_file(filename, SF2D_PLACE_RAM); + reloadColors(); + + texturepackUseDefaultIcons = false; + } else if(strcmp(lowerFilename, "player.png") == 0){ + if(sfil_load_PNG_file(filename, SF2D_PLACE_RAM) == NULL){ + return 0; } - - unzCloseCurrentFile( zipfile ); - - // Go the the next entry listed in the zip file. - if ( ( i+1 ) < global_info.number_entry ) - { - if ( unzGoToNextFile( zipfile ) != UNZ_OK ) - { - unzClose( zipfile ); - return 7; - } + + playerSprites = sfil_load_PNG_file(filename, SF2D_PLACE_RAM); + + texturepackUseDefaultPlayer = false; + } else if(strcmp(lowerFilename, "font.png") == 0){ + if(sfil_load_PNG_file(filename, SF2D_PLACE_RAM) == NULL){ + return 0; } + + font = sfil_load_PNG_file(filename, SF2D_PLACE_RAM); + + texturepackUseDefaultFont = false; + } else if(strcmp(lowerFilename, "bottombg.png") == 0){ + if(sfil_load_PNG_file(filename, SF2D_PLACE_RAM) == NULL){ + return 0; + } + + bottombg = sfil_load_PNG_file(filename, SF2D_PLACE_RAM); + + texturepackUseDefaultBottom = false; } - if(useDefaultIcons){ + return 0; +} + +int loadTexturePack(char * filename) { + texturepackUseDefaultIcons = true; + texturepackUseDefaultPlayer = true; + texturepackUseDefaultFont = true; + texturepackUseDefaultBottom = true; + + if(unzipAndLoad(filename, &loadTexture, NULL, ZIPHELPER_CLEANUP_FILES)!=0) { + return 1; + } + + if(texturepackUseDefaultIcons){ icons = sfil_load_PNG_buffer(icons2_png, SF2D_PLACE_RAM); reloadColors(); } - if(useDefaultFont) font = sfil_load_PNG_buffer(Font_png, SF2D_PLACE_RAM); - if(useDefaultBottom) bottombg = sfil_load_PNG_buffer(bottombg_png, SF2D_PLACE_RAM); - - unzClose( zipfile ); + if(texturepackUseDefaultPlayer) playerSprites = sfil_load_PNG_buffer(player_png, SF2D_PLACE_RAM); + if(texturepackUseDefaultFont) font = sfil_load_PNG_buffer(Font_png, SF2D_PLACE_RAM); + if(texturepackUseDefaultBottom) bottombg = sfil_load_PNG_buffer(bottombg_png, SF2D_PLACE_RAM); return 0; } diff --git a/source/texturepack.h b/source/texturepack.h index 565d1cc..b0f61ce 100644 --- a/source/texturepack.h +++ b/source/texturepack.h @@ -1,6 +1,5 @@ #pragma once -#include -#include + #include #include #include