diff --git a/data/icons2.png b/data/icons2.png index 2527908..83c8034 100644 Binary files a/data/icons2.png and b/data/icons2.png differ diff --git a/source/Crafting.c b/source/Crafting.c index 75603cf..70eeedf 100644 --- a/source/Crafting.c +++ b/source/Crafting.c @@ -104,7 +104,7 @@ void initRecipes(){ workbenchRecipes.recipes[20] = defineRecipe(ITEM_WALL_WOOD,1,1,ITEM_WOOD,4); workbenchRecipes.recipes[21] = defineRecipe(ITEM_WALL_STONE,1,1,ITEM_STONE,4); - anvilRecipes.size = 16; + anvilRecipes.size = 17; anvilRecipes.recipes = (Recipe*)malloc(sizeof(Recipe) * (anvilRecipes.size)); anvilRecipes.recipes[0] = defineRecipe(TOOL_SWORD,2,2,ITEM_WOOD,5,ITEM_IRONINGOT,5); anvilRecipes.recipes[1] = defineRecipe(TOOL_AXE,2,2,ITEM_WOOD,5,ITEM_IRONINGOT,5); @@ -122,6 +122,7 @@ void initRecipes(){ anvilRecipes.recipes[13] = defineRecipe(ITEM_ENCHANTER,1,3,ITEM_WOOD,10,ITEM_GOLDINGOT,10,ITEM_GEM,20); anvilRecipes.recipes[14] = defineRecipe(ITEM_WALL_IRON,1,1,ITEM_IRONINGOT,2); anvilRecipes.recipes[15] = defineRecipe(ITEM_WALL_GOLD,1,1,ITEM_GOLDINGOT,2); + anvilRecipes.recipes[16] = defineRecipe(ITEM_COIN,3,1,ITEM_IRONINGOT,1); furnaceRecipes.size = 3; furnaceRecipes.recipes = (Recipe*)malloc(sizeof(Recipe) * (furnaceRecipes.size)); @@ -139,7 +140,7 @@ void initRecipes(){ loomRecipes.recipes = (Recipe*)malloc(sizeof(Recipe) * (loomRecipes.size)); loomRecipes.recipes[0] = defineRecipe(ITEM_STRING,1,1,ITEM_WOOL,1); - enchanterRecipes.size = 8; + enchanterRecipes.size = 7; enchanterRecipes.recipes = (Recipe*)malloc(sizeof(Recipe) * (enchanterRecipes.size)); enchanterRecipes.recipes[0] = defineRecipe(TOOL_SWORD,4,2,ITEM_WOOD,5,ITEM_GEM,50); enchanterRecipes.recipes[1] = defineRecipe(TOOL_AXE,4,2,ITEM_WOOD,5,ITEM_GEM,50); @@ -148,7 +149,6 @@ void initRecipes(){ enchanterRecipes.recipes[4] = defineRecipe(TOOL_SHOVEL,4,2,ITEM_WOOD,5,ITEM_GEM,50); enchanterRecipes.recipes[5] = defineRecipe(ITEM_ARROW_GEM,1,3,ITEM_WOOD,1,ITEM_GEM,3,ITEM_STRING,1); enchanterRecipes.recipes[6] = defineRecipe(ITEM_WALL_GEM,1,1,ITEM_GEM,10); - enchanterRecipes.recipes[7] = defineRecipe(ITEM_WIZARD_SUMMON,1,4,ITEM_CLOUD,100,ITEM_IRONINGOT,10,ITEM_BONE,10,ITEM_LEATHER,10); } diff --git a/source/Crafting.h b/source/Crafting.h index b608d9b..6104c9b 100644 --- a/source/Crafting.h +++ b/source/Crafting.h @@ -29,6 +29,8 @@ RecipeManager anvilRecipes; RecipeManager loomRecipes; RecipeManager enchanterRecipes; +Recipe defineRecipe(int item, int amountOrLevel, int numArgs, ...); + 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 604e4b1..5b0e056 100644 --- a/source/Entity.c +++ b/source/Entity.c @@ -346,6 +346,22 @@ Entity newGlowwormEntity(int x, int y, int level){ return e; } +Entity newNPCEntity(int type, int x, int y, int level){ + Entity e; + e.type = ENTITY_NPC; + e.level = level; + e.x = x; + e.y = y; + e.hurtTime = 0; + e.xKnockback = 0; + e.yKnockback = 0; + e.npc.type = type; + e.xr = 4; + e.yr = 3; + e.canPass = false; + return e; +} + void addEntityToList(Entity e, EntityManager* em){ e.slotNum = em->lastSlot[e.level]; em->entities[e.level][em->lastSlot[e.level]] = e; diff --git a/source/Entity.h b/source/Entity.h index 57df29d..6f06509 100644 --- a/source/Entity.h +++ b/source/Entity.h @@ -23,6 +23,8 @@ #define ENTITY_DRAGONPROJECTILE 16 #define ENTITY_MAGIC_PILLAR 17 +#define ENTITY_NPC 18 + typedef struct Entity Entity; typedef struct { @@ -160,6 +162,10 @@ typedef struct { s8 waitTime; } Glowworm; +typedef struct { + u8 type; +} NPC; + typedef struct { float xa; float ya; @@ -200,6 +206,7 @@ struct Entity { Glowworm glowworm; Dragon dragon; DragonFire dragonFire; + NPC npc; TextParticleEntity textParticle; SmashParticleEntity smashParticle; }; @@ -234,6 +241,7 @@ Entity newTextParticleEntity(char * str, u32 color, int xa, int ya, int level); Entity newSmashParticleEntity(int xa, int ya, int level); Entity newArrowEntity(Entity* parent, int itemID, s8 xa, s8 ya, int level); Entity newGlowwormEntity(int x, int y, int level); +Entity newNPCEntity(int type, int x, int y, int level); void addEntityToList(Entity e, EntityManager* em); void removeEntityFromList(Entity * e,int level,EntityManager* em); diff --git a/source/Globals.c b/source/Globals.c index 4bd6bbd..cd1a606 100644 --- a/source/Globals.c +++ b/source/Globals.c @@ -1,6 +1,6 @@ #include "Globals.h" -char versionText[34] = "Version 1.2.2"; +char versionText[34] = "Version 1.3.0"; char fpsstr[34]; u8 currentMenu = 0; @@ -217,6 +217,7 @@ void tickTouchQuickSelect() { } void hurtEntity(Entity* e, int damage, int dir, u32 hurtColor){ + if (TESTGODMODE && e->type==ENTITY_PLAYER) return; if (e->hurtTime > 0) return; int xd = player.x - e->x; int yd = player.y - e->y; @@ -252,7 +253,7 @@ void hurtEntity(Entity* e, int damage, int dir, u32 hurtColor){ } player.p.score += 50 * (e->hostile.lvl + 1); removeEntityFromList(e,e->level,&eManager); - trySpawn(3, currentLevel); + if(currentLevel != 5) trySpawn(3, currentLevel); return; } break; @@ -262,7 +263,7 @@ void hurtEntity(Entity* e, int damage, int dir, u32 hurtColor){ 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); - trySpawn(3, currentLevel); + if(currentLevel != 5) trySpawn(3, currentLevel); return; } break; @@ -270,7 +271,7 @@ 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_DUNGEON_KEY,1),e->x+8, e->y+8, (rand()%2) + 1); + addItemsToWorld(newItem(ITEM_MAGIC_DUST,1),e->x+8, e->y+8, (rand()%2) + 2); removeEntityFromList(e,e->level,&eManager); playSound(snd_bossdeath); player.p.score += 1000; @@ -295,7 +296,7 @@ void hurtEntity(Entity* e, int damage, int dir, u32 hurtColor){ } player.p.score += 10; removeEntityFromList(e,e->level,&eManager); - trySpawn(3, currentLevel); + if(currentLevel != 5) trySpawn(3, currentLevel); return; } break; @@ -546,16 +547,16 @@ void EntityVsEntity(Entity* e1, Entity* e2){ damage = 1 + (rand()%3); break; case ITEM_ARROW_STONE: - damage = 2 + (rand()%5); + damage = 2 + (rand()%4); break; case ITEM_ARROW_IRON: damage = 8 + (rand()%9); break; case ITEM_ARROW_GOLD: - damage = 16 + (rand()%17); + damage = 16 + (rand()%9); break; case ITEM_ARROW_GEM: - damage = 24 + (rand()%17); + damage = 24 + (rand()%9); break; } @@ -589,6 +590,7 @@ bool EntityBlocksEntity(Entity* e1, Entity* e2){ case ENTITY_PLAYER: case ENTITY_PASSIVE: case ENTITY_MAGIC_PILLAR: + case ENTITY_NPC: return true; break; } @@ -599,9 +601,12 @@ bool EntityBlocksEntity(Entity* e1, Entity* e2){ bool tileIsSolid(int tile, Entity * e){ switch(tile){ - case TILE_TREE: case TILE_ROCK: case TILE_HARDROCK: + case TILE_MAGIC_BARRIER: + case TILE_DUNGEON_WALL: + return true; + case TILE_TREE: case TILE_CACTUS: case TILE_IRONORE: case TILE_GOLDORE: @@ -612,9 +617,10 @@ bool tileIsSolid(int tile, Entity * e){ case TILE_IRON_WALL: case TILE_GOLD_WALL: case TILE_GEM_WALL: - case TILE_DUNGEON_WALL: - case TILE_MAGIC_BARRIER: - return true; + case TILE_BOOKSHELVES: + case TILE_MUSHROOM_BROWN: + case TILE_MUSHROOM_RED: + if(e->type != ENTITY_DRAGON) return true; case TILE_LAVA: case 255: if(e->type != ENTITY_ARROW) return true; @@ -655,6 +661,12 @@ u32 getTileColor(int tile){ case TILE_DUNGEON_WALL: return SWAP_UINT32(dungeonColor[0]); case TILE_DUNGEON_FLOOR: return SWAP_UINT32(dungeonColor[1]); case TILE_MAGIC_BARRIER: return SWAP_UINT32(dungeonColor[0]); + case TILE_BOOKSHELVES: return SWAP_UINT32(woodColor); + case TILE_WOOD_FLOOR: return SWAP_UINT32(woodColor); + case TILE_MYCELIUM: return SWAP_UINT32(myceliumColor); + case TILE_MUSHROOM_BROWN: return SWAP_UINT32(mushroomColor); + case TILE_MUSHROOM_RED: return SWAP_UINT32(mushroomColor); + case TILE_ICE: return SWAP_UINT32(iceColor); default: return 0x111111FF; } @@ -806,6 +818,11 @@ s8 itemTileInteract(int tile, Item* item, int x, int y, int px, int py, int dir) setTile(TILE_GEM_WALL,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; + 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); @@ -847,6 +864,15 @@ s8 itemTileInteract(int tile, Item* item, int x, int y, int px, int py, int dir) setTile(TILE_GEM_WALL,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; + return 1; + } + else if(item->id == ITEM_WOOD) { + setTile(TILE_WOOD_FLOOR,x,y); --item->countLevel; + return 1; + } else if(item->id == ITEM_SAND){ setTile(TILE_SAND,x,y); --item->countLevel; return 1; @@ -942,6 +968,16 @@ s8 itemTileInteract(int tile, Item* item, int x, int y, int px, int py, int dir) 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); + 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); + } break; } return 0; } @@ -952,7 +988,7 @@ void tickTile(int x, int y){ switch(tile){ case TILE_SAPLING_TREE: - setData(++data,x,y); if(data>100){setData(0,x,y); setTile(TILE_TREE,x,y);} + if(season!=3) setData(++data,x,y); if(data>100){setData(0,x,y); setTile(TILE_TREE,x,y);} break; case TILE_TREE: if(eManager.lastSlot[currentLevel]<800 && (daytime>18000 || daytime<5000) && rand()%800==0) { @@ -966,16 +1002,22 @@ void tickTile(int x, int y){ } break; case TILE_SAPLING_CACTUS: - setData(++data,x,y); if(data>100){setData(0,x,y); setTile(TILE_CACTUS,x,y);} + if(season!=3) setData(++data,x,y); if(data>100){setData(0,x,y); setTile(TILE_CACTUS,x,y);} break; case TILE_WHEAT: - if(data<100)setData(++data,x,y); + if(data<100 && season!=3) setData(++data,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); + } break; case TILE_LAVA: if(getTile(x+1,y)==TILE_HOLE) setTile(TILE_LAVA,x+1,y); @@ -1016,7 +1058,13 @@ void tickTile(int x, int y){ } } if(data==0) setTile(TILE_DUNGEON_FLOOR,x,y); + setData(rand()%2,x,y); break; + case TILE_ICE: + if(season!=3) { + setTile(TILE_WATER,x,y); + } + break; } } @@ -1109,7 +1157,7 @@ void tickEntity(Entity* e){ if(e->type == ENTITY_SKELETON) { --(e->hostile.randAttackTime); if(e->hostile.randAttackTime <= 0) { - e->hostile.randAttackTime = 70 - (e->hostile.lvl * 10); + e->hostile.randAttackTime = 80 - (e->hostile.lvl * 5); int aitemID = ITEM_ARROW_WOOD; if(e->hostile.lvl >= 2) aitemID = ITEM_ARROW_STONE; @@ -1617,6 +1665,8 @@ void initPlayer(){ } void playerHurtTile(int tile, int xt, int yt, int damage, int dir){ + if(TESTGODMODE) damage = 99; + char hurtText[11]; switch(tile){ case TILE_TREE: @@ -1798,11 +1848,21 @@ void playerHurtTile(int tile, int xt, int yt, int damage, int dir){ addItemsToWorld(newItem(ITEM_WALL_GEM,1),(xt<<4)+8,(yt<<4)+8,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); + break; } } bool playerUseEnergy(int amount){ + if(TESTGODMODE) return true; if(amount > player.p.stamina) return false; player.p.stamina -= amount; return true; @@ -1974,24 +2034,28 @@ bool useEntity(Entity* e) { 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); @@ -2005,17 +2069,22 @@ bool useEntity(Entity* e) { 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; } + } else if(e->type == ENTITY_NPC) { + openNPCMenu(e->npc.type); + return true; } return false; } @@ -2088,7 +2157,7 @@ void tickPlayer(){ if (swimming && player.p.swimTimer % 60 == 0) { if (player.p.stamina > 0) { - --player.p.stamina; + if(!TESTGODMODE) --player.p.stamina; } else { hurtEntity(&player,1,-1,0xFFAF00FF); } @@ -2101,7 +2170,7 @@ void tickPlayer(){ if(k_attack.clicked){ if (player.p.stamina != 0) { - player.p.stamina--; + if(!TESTGODMODE) player.p.stamina--; player.p.staminaRecharge = 0; playerAttack(); //addEntityToList(newSlimeEntity(1,200,600,1), &eManager); @@ -2148,7 +2217,7 @@ void enterDungeon() { //create map currentLevel = 5; - createDungeonMap(128, 128, map[5], data[5]); + createAndValidateDungeonMap(128, 128, 5, map[5], data[5]); //reset minimap clear state int xd,yd; @@ -2160,11 +2229,11 @@ void enterDungeon() { initMinimapLevel(5, false); newSeed(); + player.x = ((128/2) << 4) + 8; + player.y = ((128/2) << 4) + 8; + //spawn new entities trySpawn(500, 5); - - player.x = ((128/2) << 4) + 8; - player.y = ((128/2) << 4) + 8; updateMusic(currentLevel, daytime); } @@ -2203,6 +2272,9 @@ u32 getMinimapColor(int level, int x, int y) { void initMinimapLevel(int level, bool loadUpWorld) { 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) { @@ -2248,6 +2320,18 @@ void initMinimapLevel(int level, bool loadUpWorld) { } } } + } + 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 */ @@ -2276,6 +2360,8 @@ void reloadColors() { dirtColor[4] = SWAP_UINT32(sf2d_get_pixel(icons, 16, 4)); grassColor = SWAP_UINT32(sf2d_get_pixel(icons, 17, 0)); + myceliumColor = SWAP_UINT32(sf2d_get_pixel(icons, 17, 1)); + mushroomColor = SWAP_UINT32(sf2d_get_pixel(icons, 17, 2)); sandColor = SWAP_UINT32(sf2d_get_pixel(icons, 18, 0)); @@ -2298,4 +2384,7 @@ void reloadColors() { dungeonColor[0] = SWAP_UINT32(sf2d_get_pixel(icons, 24, 0)); dungeonColor[1] = SWAP_UINT32(sf2d_get_pixel(icons, 24, 1)); + + snowColor = SWAP_UINT32(sf2d_get_pixel(icons, 25, 0)); + iceColor = SWAP_UINT32(sf2d_get_pixel(icons, 25, 1)); } \ No newline at end of file diff --git a/source/Globals.h b/source/Globals.h index b5a9be6..ab1b14b 100644 --- a/source/Globals.h +++ b/source/Globals.h @@ -3,6 +3,7 @@ #include "SaveLoad.h" #include "Input.h" #include "MapGen.h" +#include "Quests.h" #include "icons2_png.h" #include "Font_png.h" @@ -26,6 +27,13 @@ #define MENU_SETTINGS_REBIND 12 #define MENU_SETTINGS_TP 13 #define MENU_DUNGEON 14 +#define MENU_NPC 15 + +#define NPC_GIRL 0 +#define NPC_PRIEST 1 +#define NPC_FARMER 2 +#define NPC_LIBRARIAN 3 +#define NPC_DWARF 4 #define TILE_NULL 255 #define TILE_GRASS 0 @@ -60,9 +68,18 @@ #define TILE_DUNGEON_FLOOR 28 #define TILE_DUNGEON_ENTRANCE 29 #define TILE_MAGIC_BARRIER 30 +#define TILE_BOOKSHELVES 31 +#define TILE_WOOD_FLOOR 32 +#define TILE_MYCELIUM 33 +#define TILE_MUSHROOM_BROWN 34 +#define TILE_MUSHROOM_RED 35 +#define TILE_ICE 36 #define SWAP_UINT32(x) (((x) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | ((x) << 24)) +//TODO: Dont forget to change back +#define TESTGODMODE true + bool screenShot; int loadedtp; @@ -86,6 +103,7 @@ sf2d_texture * minimap[6]; u8 map[6][128*128]; u8 data[6][128*128]; u8 minimapData[128*128]; +u8 compassData[6][3]; u32 dirtColor[5]; u32 grassColor; @@ -98,6 +116,10 @@ u32 ironColor; u32 goldColor; u32 gemColor; u32 dungeonColor[2]; +u32 myceliumColor; +u32 mushroomColor; +u32 snowColor; +u32 iceColor; char currentFileName[256]; extern u8 currentMenu; @@ -110,11 +132,15 @@ s16 awX, awY; u32 tickCount; RecipeManager* currentRecipes; Entity* curChestEntity; +char* currentCraftTitle; s16 curInvSel; bool quitGame; s8 currentSelection; u16 daytime; +int day; +u8 season; +bool rain; void tickTile(int x, int y); bool tileIsSolid(int tile, Entity * e); diff --git a/source/Item.c b/source/Item.c index fdfbb1b..a1613ba 100644 --- a/source/Item.c +++ b/source/Item.c @@ -183,6 +183,9 @@ char* getItemName(int itemID, int countLevel){ case ITEM_WIZARD_SUMMON: sprintf(currentName,"%d Wizard Summon", countLevel); return currentName; case ITEM_DRAGON_EGG: sprintf(currentName,"%d Dragon Egg", countLevel); return currentName; case ITEM_DRAGON_SCALE: sprintf(currentName,"%d Dragon Scale", countLevel); return currentName; + case ITEM_BOOKSHELVES: sprintf(currentName,"%d Bookshelves", countLevel); return currentName; + case ITEM_MAGIC_DUST: sprintf(currentName,"%d Magic Dust", countLevel); return currentName; + case ITEM_COIN: sprintf(currentName,"%d Coins", countLevel); return currentName; case TOOL_BUCKET: switch(countLevel){ case 1: return "Water Bucket"; @@ -190,6 +193,7 @@ char* getItemName(int itemID, int countLevel){ default: return "Empty Bucket"; } case TOOL_BOW: return "Bow"; + case TOOL_MAGIC_COMPASS: return "Magic Compass"; default: return ""; // null } } @@ -287,8 +291,11 @@ char* getBasicItemName(int itemID, int countLevel){ case ITEM_BONE: return "Bone"; case ITEM_DUNGEON_KEY: return "Dungeon Key"; case ITEM_WIZARD_SUMMON: return "Wizard Summon"; - case ITEM_DRAGON_EGG: return "%d Dragon Egg"; - case ITEM_DRAGON_SCALE: return "%d Dragon Scale"; + case ITEM_DRAGON_EGG: return "Dragon Egg"; + case ITEM_DRAGON_SCALE: return "Dragon Scale"; + case ITEM_BOOKSHELVES: return "Bookshelves"; + case ITEM_MAGIC_DUST: return "Magic Dust"; + case ITEM_COIN: return "Coin"; case TOOL_BUCKET: switch(countLevel){ case 1: return "Water Bucket"; @@ -296,6 +303,7 @@ char* getBasicItemName(int itemID, int countLevel){ default: return "Empty Bucket"; } case TOOL_BOW: return "Bow"; + case TOOL_MAGIC_COMPASS: return "Magic Compass"; default: return ""; // null } diff --git a/source/Item.h b/source/Item.h index 228b0ec..06b3f37 100644 --- a/source/Item.h +++ b/source/Item.h @@ -68,9 +68,13 @@ #define ITEM_WIZARD_SUMMON 70 #define ITEM_DRAGON_EGG 71 #define ITEM_DRAGON_SCALE 72 +#define ITEM_BOOKSHELVES 73 +#define ITEM_MAGIC_DUST 74 +#define ITEM_COIN 75 #define TOOL_BUCKET 101 #define TOOL_BOW 102 +#define TOOL_MAGIC_COMPASS 103 typedef struct Inventory Inventory; diff --git a/source/MapGen.c b/source/MapGen.c index 559a834..aa8ce3f 100644 --- a/source/MapGen.c +++ b/source/MapGen.c @@ -3,6 +3,9 @@ int w = 0; int h = 0; +u8 randomTile[] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 2}; +int randomTileSize = 10; + float nextFloat(){ return (float)rand()/RAND_MAX; } @@ -64,9 +67,14 @@ void newSeed(){ srand(time(NULL)); } -void createAndValidateTopMap(int w, int h, u8 * map, u8 * data) { +//TODO: Will need to reset entity manager if generation is retried +void createAndValidateTopMap(int w, int h, int level, u8 * map, u8 * data) { do { - createTopMap(w, h, map, data); + //reset Entities + (&eManager)->lastSlot[level] = 0; + (&eManager)->entities[level][0] = nullEntity; + + createTopMap(w, h, level, map, data); int count[256]={[0 ... 255] = 0}; int i; @@ -81,9 +89,13 @@ void createAndValidateTopMap(int w, int h, u8 * map, u8 * data) { } while (true); } -void createAndValidateUndergroundMap(int w, int h,int depthLevel, u8 * map, u8 * data) { +void createAndValidateUndergroundMap(int w, int h, int depthLevel, int level, u8 * map, u8 * data) { do { - createUndergroundMap(w, h, depthLevel, map, data); + //reset Entities + (&eManager)->lastSlot[level] = 0; + (&eManager)->entities[level][0] = nullEntity; + + createUndergroundMap(w, h, depthLevel, level, map, data); int count[256]={[0 ... 255] = 0}; int i = 0; @@ -92,7 +104,7 @@ void createAndValidateUndergroundMap(int w, int h,int depthLevel, u8 * map, u8 * if (count[TILE_DIRT & 0xff] < 100) continue; switch(depthLevel){ case 1: if (count[TILE_IRONORE & 0xff] < 20) continue; break; - case 2: if (count[TILE_GOLDORE & 0xff] < 20) continue; break; + case 2: if (count[TILE_GOLDORE & 0xff] < 20 || count[TILE_MYCELIUM & 0xff] < 40) continue; break; case 3: if (count[TILE_GEMORE & 0xff] < 20) continue; break; } if (depthLevel < 3) if (count[TILE_STAIRS_DOWN & 0xff] < 2) continue; @@ -101,9 +113,13 @@ void createAndValidateUndergroundMap(int w, int h,int depthLevel, u8 * map, u8 * } while (true); } -void createAndValidateDungeonMap(int w, int h, u8 * map, u8 * data) { +void createAndValidateDungeonMap(int w, int h, int level, u8 * map, u8 * data) { do { - createDungeonMap(w, h, map, data); + //reset Entities + (&eManager)->lastSlot[level] = 0; + (&eManager)->entities[level][0] = nullEntity; + + createDungeonMap(w, h, level, map, data); int count[256]={[0 ... 255] = 0}; int i = 0; @@ -115,9 +131,13 @@ void createAndValidateDungeonMap(int w, int h, u8 * map, u8 * data) { } while (true); } -void createAndValidateSkyMap(int w, int h, u8 * map, u8 * data) { +void createAndValidateSkyMap(int w, int h, int level, u8 * map, u8 * data) { do { - createSkyMap(w, h, map, data); + //reset Entities + (&eManager)->lastSlot[level] = 0; + (&eManager)->entities[level][0] = nullEntity; + + createSkyMap(w, h, level, map, data); int count[256]={[0 ... 255] = 0}; int i = 0; @@ -131,7 +151,7 @@ void createAndValidateSkyMap(int w, int h, u8 * map, u8 * data) { -void createTopMap(int w, int h, u8 * map, u8 * data) { +void createTopMap(int w, int h, int level, u8 * map, u8 * data) { double* mnoise1 = Noise(w, h, 16); double* mnoise2 = Noise(w, h, 16); double* mnoise3 = Noise(w, h, 16); @@ -185,6 +205,8 @@ void createTopMap(int w, int h, u8 * map, u8 * data) { } } + createVillage(w, h, level, map, data); + for (i = 0; i < w * h / 400; ++i) { x = rand()%w; y = rand()%h; @@ -254,7 +276,7 @@ void createTopMap(int w, int h, u8 * map, u8 * data) { return; } -void createUndergroundMap(int w, int h,int depthLevel, u8 * map, u8 * data) { +void createUndergroundMap(int w, int h, int depthLevel, int level, u8 * map, u8 * data) { double* mnoise1 = Noise(w, h, 16); double* mnoise2 = Noise(w, h, 16); double* mnoise3 = Noise(w, h, 16); @@ -270,7 +292,8 @@ void createUndergroundMap(int w, int h,int depthLevel, u8 * map, u8 * data) { double* noise1 = Noise(w, h, 32); double* noise2 = Noise(w, h, 32); - int x, y; + int x,y,i,j,k,xx,yy; + for(x = 0; x < w; ++x){ for(y = 0; y < w; ++y){ int i = x + y * w; @@ -307,7 +330,37 @@ void createUndergroundMap(int w, int h,int depthLevel, u8 * map, u8 * data) { } } } - int i,j; + + if(depthLevel==3) { + createDwarfHouse(w, h, level, map, data); + } else if(depthLevel==2) { + for (i = 0; i < w * h / 5400; ++i) { + int xs = rand()%w; + int ys = rand()%h; + for (k = 0; k < 10; ++k) { + x = xs + (rand()%13) - 6; + y = ys + (rand()%13) - 6; + for (j = 0; j < 100; ++j) { + int xo = x + (rand()%5) - (rand()%5); + int yo = y + (rand()%5) - (rand()%5); + for (yy = yo - 1;yy <= yo + 1; ++yy){ + for(xx = xo - 1; xx <= xo + 1; ++xx){ + if (xx >= 0 && yy >= 0 && xx < w && yy < h) { + if (map[xx + yy * w] == TILE_DIRT) { + map[xx + yy * w] = TILE_MYCELIUM; + if(rand()%20==0) { + map[xx + yy * w] = TILE_MUSHROOM_BROWN + rand()%2; //BROWN or RED (=BROWN+1) + data[xx + yy * w] = rand()%2; + } + } + } + } + } + } + } + } + } + for (i = 0; i < w * h / 400; ++i) { int x = rand()%w; int y = rand()%h; @@ -357,145 +410,9 @@ void createUndergroundMap(int w, int h,int depthLevel, u8 * map, u8 * data) { } -void createDungeonRoom(int w, int h, bool dragon, u8 * map, u8 * data) { - int tries; - - for(tries=0; tries<100; ++tries) { - int x = 5+(rand()%(w-10 -10)); - int y = 5+(rand()%(h-10 -10)); - int xr; - int yr; - int wr = 10+(rand()%11); - int hr = 10+(rand()&11); - int xp; - int yp; - int i; - - //create Dragonroom - if(dragon) { - wr = 20; - hr = 20; - x = 5 + (rand()%2)*(w-5*2-wr); - y = 5 + (rand()%2)*(h-5*2-hr); - } - - if(x+wr > w-5) wr = (w-5) - x; - if(y+hr > h-5) hr = (h-5) - y; - - //check instersection - bool allowed = true; - for(xr = x-1; xr < x+wr+1; ++xr) { - for(yr = y-1; yr < y+hr+1; ++yr) { - i = xr + yr * w; - - //255 for paths so rooms can overlap paths - if(map[i]!=TILE_DUNGEON_WALL && map[i]!=255) { - allowed = false; - break; - } - } - if(!allowed) break; - } - if(!allowed) continue; - - //create room - for(xr = x; xr < x+wr; ++xr) { - for(yr = y; yr < y+hr; ++yr) { - i = xr + yr * w; - - map[i] = TILE_DUNGEON_FLOOR; - } - } - - //Create path back to existing stuff - xp = x + wr/2; - yp = y + hr/2; - i = xp + yp * w; - bool checkForFloor = false; - bool xFirst = (rand()%2)==0; - while((checkForFloor && (map[i]!=TILE_DUNGEON_FLOOR && map[i]!=255)) || (!checkForFloor && (map[i]==TILE_DUNGEON_FLOOR || map[i]==255))) { - if(checkForFloor) { - //make connection - map[i] = 255; - } - - //move - if(xFirst) { - if(xp > w/2) --xp; - else if(xp < w/2) ++xp; - else if(yp > h/2) --yp; - else if(yp < h/2) ++yp; - else break; - } else { - if(yp > h/2) --yp; - else if(yp < h/2) ++yp; - else if(xp > w/2) --xp; - else if(xp < w/2) ++xp; - else break; - } - - i = xp + yp * w; - - //search for end of current room - if(!checkForFloor && (map[i]!=TILE_DUNGEON_FLOOR && map[i]!=255)) checkForFloor = true; - } - - //dekorate dragon room - if(dragon) { - for(xr = x; xr < x+wr; ++xr) { - for(yr = y; yr < y+hr; ++yr) { - i = xr + yr * w; - - if((xr==x+1 || xr==x+wr-2 || yr==y+1 || yr==y+hr-2) && (xr!=x && xr!=x+wr-1 && yr!=y && yr!=y+hr-1)) { - map[i] = TILE_MAGIC_BARRIER; - } - } - } - - //add Dragon Entity - addEntityToList(newDragonEntity((x+wr/2) << 4, (y+hr/2) << 4, 5), &eManager); - break; - } - - //dekorate room - bool lava = (rand()%4)==0; - bool pillars = (rand()%4)==0; - for(xr = x; xr < x+wr; ++xr) { - for(yr = y; yr < y+hr; ++yr) { - i = xr + yr * w; - - if(lava && xr > x+1 && xr < x+wr-2 && yr > y+1 && yr < y+hr-2) { - map[i] = TILE_LAVA; - } else if(pillars && xr > x && xr < x+wr-1 && yr > y && yr < y+hr-1 && xr%2 == 0 && yr%2 == 0) { - map[i] = TILE_DUNGEON_WALL; - } else { - //add magic pillars for dragon barrier - if(xr==x+wr/2 && yr==y+hr/2) { - int pcount = 0; - int i = 0; - for (i = 0; i < eManager.lastSlot[5]; ++i) { - Entity * e = &eManager.entities[5][i]; - - if(e->type == ENTITY_MAGIC_PILLAR) { - ++pcount; - } - } - if(pcount<8) { - addEntityToList(newMagicPillarEntity((xr << 4) + 8, (yr << 4) + 8, 5), &eManager); - } - continue; - } - - if(rand()%50==0) map[i] = TILE_IRONORE + (rand()%3); - } - } - } - - break; - } -} - -void createDungeonMap(int w, int h, u8 * map, u8 * data) { +void createDungeonMap(int w, int h, int level, u8 * map, u8 * data) { + hasNPC = false; + int i, x, y; for(x = 0; x < w; ++x){ for(y = 0; y < w; ++y){ @@ -504,18 +421,19 @@ void createDungeonMap(int w, int h, u8 * map, u8 * data) { //Startroom if (x >= (w/2-5) && x <= (w/2+5) && y >= (h/2-5) && y <= (h/2+5) ) { map[i] = TILE_DUNGEON_FLOOR; + data[i] = randomTile[rand()%randomTileSize]; } else { map[i] = TILE_DUNGEON_WALL; + data[i] = 0; } - data[i] = 0; } } //create dragon chamber(only call once and before other rooms) - createDungeonRoom(w, h, true, map, data); + createDungeonRoom(w, h, true, level, map, data); for(i = 0; i < 40; ++i) { - createDungeonRoom(w, h, false, map, data); + createDungeonRoom(w, h, false, level, map, data); } //replace paths with actual dungeon floor @@ -525,6 +443,7 @@ void createDungeonMap(int w, int h, u8 * map, u8 * data) { if (map[i]==255) { map[i] = TILE_DUNGEON_FLOOR; + data[i] = randomTile[rand()%randomTileSize]; } } } @@ -543,7 +462,7 @@ void createDungeonMap(int w, int h, u8 * map, u8 * data) { map[w/2-1 + (h/2-1) * w] = TILE_DUNGEON_WALL; } -void createSkyMap(int w, int h, u8 * map, u8 * data) { +void createSkyMap(int w, int h, int level, u8 * map, u8 * data) { double* noise1 = Noise(w, h, 8); double* noise2 = Noise(w, h, 8); int x, y; @@ -602,4 +521,341 @@ void createSkyMap(int w, int h, u8 * map, u8 * data) { free(noise1); free(noise2); } + +//"Subgenerators" +void findFeatureLocation(int fw, int fh, int * accepted, int aLength, int maxTries, int w, int h, u8 * map, u8 * data) { + int leastNonFitting = fw * fh; + int tries; + + //find the location with the least non fitting tiles out of some randomly tried ones + for(tries=0; triesx) { + map[px + py * w] = TILE_SAND; + --px; + } + hw = 4 + rand()%2; + hh = 4 + rand()%2; + hx = px + 1; + hy = py - hh + 2 + rand()%(hh-2); + ex = px + hw; + ey = py; + createVillageHouse(0, hx, hy, hw, hh, ex, ey, w, h, level, map, data); + + //top + px = cx-1 + rand()%3; + py = cy-1; + while(py>y) { + map[px + py * w] = TILE_SAND; + --py; + } + hw = 5 + rand()%2; + hh = 4 + rand()%2; + hx = px - hw + 2 + rand()%(hw-2); + hy = py + 1; + ex = px; + ey = py+hh; + createVillageHouse(1, hx, hy, hw, hh, ex, ey, w, h, level, map, data); + + //right + px = cx+1; + py = cy-1 + rand()%3; + while(px w-5) wr = (w-5) - x; + if(y+hr > h-5) hr = (h-5) - y; + + //check instersection + bool allowed = true; + for(xr = x-1; xr < x+wr+1; ++xr) { + for(yr = y-1; yr < y+hr+1; ++yr) { + i = xr + yr * w; + + //255 for paths so rooms can overlap paths + if(map[i]!=TILE_DUNGEON_WALL && map[i]!=255) { + allowed = false; + break; + } + } + if(!allowed) break; + } + if(!allowed) continue; + + //create room + for(xr = x; xr < x+wr; ++xr) { + for(yr = y; yr < y+hr; ++yr) { + i = xr + yr * w; + + map[i] = TILE_DUNGEON_FLOOR; + data[i] = randomTile[rand()%randomTileSize]; + } + } + + //Create path back to existing stuff + xp = x + wr/2; + yp = y + hr/2; + i = xp + yp * w; + bool checkForFloor = false; + bool xFirst = (rand()%2)==0; + while((checkForFloor && (map[i]!=TILE_DUNGEON_FLOOR && map[i]!=255)) || (!checkForFloor && (map[i]==TILE_DUNGEON_FLOOR || map[i]==255))) { + if(checkForFloor) { + //make connection + map[i] = 255; + } + + //move + if(xFirst) { + if(xp > w/2) --xp; + else if(xp < w/2) ++xp; + else if(yp > h/2) --yp; + else if(yp < h/2) ++yp; + else break; + } else { + if(yp > h/2) --yp; + else if(yp < h/2) ++yp; + else if(xp > w/2) --xp; + else if(xp < w/2) ++xp; + else break; + } + + i = xp + yp * w; + + //search for end of current room + if(!checkForFloor && (map[i]!=TILE_DUNGEON_FLOOR && map[i]!=255)) checkForFloor = true; + } + + //dekorate dragon room + if(dragon) { + for(xr = x; xr < x+wr; ++xr) { + for(yr = y; yr < y+hr; ++yr) { + i = xr + yr * w; + + if((xr==x+1 || xr==x+wr-2 || yr==y+1 || yr==y+hr-2) && (xr!=x && xr!=x+wr-1 && yr!=y && yr!=y+hr-1)) { + map[i] = TILE_MAGIC_BARRIER; + } + } + } + + //add Dragon Entity + addEntityToList(newDragonEntity((x+wr/2) << 4, (y+hr/2) << 4, level), &eManager); + break; + } + + //dekorate room + bool lava = (rand()%4)==0; + bool pillars = (rand()%4)==0; + bool books = (rand()%4)==0; + for(xr = x; xr < x+wr; ++xr) { + for(yr = y; yr < y+hr; ++yr) { + i = xr + yr * w; + + if(lava && xr > x+1 && xr < x+wr-2 && yr > y+1 && yr < y+hr-2) { + map[i] = TILE_LAVA; + data[i] = 0; + } else if(pillars && xr > x && xr < x+wr-1 && yr > y && yr < y+hr-1 && xr%2 == 0 && yr%2 == 0) { + map[i] = TILE_DUNGEON_WALL; + data[i] = 0; + } else if(books && (xr>x && xry && yrtype == ENTITY_MAGIC_PILLAR) { + ++pcount; + } + } + if(pcount<8) { + addEntityToList(newMagicPillarEntity((xr << 4) + 8, (yr << 4) + 8, level), &eManager); + } + continue; + } + + if(rand()%50==0) map[i] = TILE_IRONORE + (rand()%3); + } + } + } + + break; + } +} + diff --git a/source/MapGen.h b/source/MapGen.h index 61bc7ad..cf98530 100644 --- a/source/MapGen.h +++ b/source/MapGen.h @@ -11,11 +11,21 @@ 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, u8 * map, u8 * data); -void createTopMap(int w, int h, u8 * map, u8 * data); -void createAndValidateUndergroundMap(int w, int h,int depthLevel, u8 * map, u8 * data); -void createUndergroundMap(int w, int h,int depthLevel, u8 * map, u8 * data); -void createAndValidateDungeonMap(int w, int h, u8 * map, u8 * data); -void createDungeonMap(int w, int h, u8 * map, u8 * data); -void createAndValidateSkyMap(int w, int h, u8 * map, u8 * data); -void createSkyMap(int w, int h, u8 * map, u8 * data); +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); +void createUndergroundMap(int w, int h, int depthLevel, int level, u8 * map, u8 * data); +void createAndValidateDungeonMap(int w, int h, int level, u8 * map, u8 * data); +void createDungeonMap(int w, int h, int level, u8 * map, u8 * data); +void createAndValidateSkyMap(int w, int h, int level, u8 * map, u8 * data); +void createSkyMap(int w, int h, int level, u8 * map, u8 * data); + +int featureX; +int featureY; +void findFeatureLocation(int fw, int fh, int * accepted, int aLength, int maxTries, int w, int h, u8 * map, u8 * data); + +void createVillage(int w, int h, int level, u8 * map, u8 * data); +void createDwarfHouse(int w, int h, int level, u8 * map, u8 * data); + +bool hasNPC; +void createDungeonRoom(int w, int h, bool dragon, int level, u8 * map, u8 * data); \ No newline at end of file diff --git a/source/Menu.c b/source/Menu.c index 5aa0b16..b4d73f1 100644 --- a/source/Menu.c +++ b/source/Menu.c @@ -531,7 +531,9 @@ void tickMenu(int menu){ } enterDungeon(); - } + } else if(TESTGODMODE) { + enterDungeon(); + } } else { leaveDungeon(); } @@ -755,6 +757,9 @@ void tickMenu(int menu){ } break; + case MENU_NPC: + tickNPCMenu(); + break; } } @@ -1158,8 +1163,8 @@ void renderMenu(int menu,int xscr,int yscr){ drawTextColor("Cost",248+1,78+1,0xFF000000); drawTextColor("Cost",248,78,0xFF6FE2E2); renderFrame(1,1,14,14,0xFFFF1010); - drawTextColor("Crafting",24+1,14+1,0xFF000000); - drawTextColor("Crafting",24,14,0xFF6FE2E2); + drawTextColor(currentCraftTitle,24+1,14+1,0xFF000000); + drawTextColor(currentCraftTitle,24,14,0xFF6FE2E2); renderRecipes(currentRecipes, 1, 1, 14, 14, curInvSel); Recipe* rec = ¤tRecipes->recipes[curInvSel]; @@ -1420,7 +1425,20 @@ void renderMenu(int menu,int xscr,int yscr){ break; } sf2d_end_frame(); - break; + 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); + } + offsetX = xscr;offsetY = yscr; + renderMenuBackground(xscr,yscr); + offsetX = 0;offsetY = 0; + + renderNPCMenu(xscr, yscr); + sf2d_end_frame(); + break; } } diff --git a/source/Menu.h b/source/Menu.h index 049059c..bd764d7 100644 --- a/source/Menu.h +++ b/source/Menu.h @@ -2,6 +2,7 @@ #include "MenuTutorial.h" #include "texturepack.h" +#include "Quests.h" void initMenus(); diff --git a/source/Quests.c b/source/Quests.c new file mode 100644 index 0000000..5836003 --- /dev/null +++ b/source/Quests.c @@ -0,0 +1,417 @@ +#include "Quests.h" + +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; + +void initQuests() { + questManager.size = 2; + questManager.questlines = (Questline*)malloc(sizeof(Questline) * (questManager.size)); + + priestTrades.size = 5; + priestTrades.recipes = (Recipe*)malloc(sizeof(Recipe) * (priestTrades.size)); + priestTrades.recipes[0] = defineRecipe(ITEM_DUNGEON_KEY,1,1,ITEM_MAGIC_DUST,2); + priestTrades.recipes[1] = defineRecipe(ITEM_WIZARD_SUMMON,1,4,ITEM_CLOUD,100,ITEM_IRONINGOT,10,ITEM_BONE,10,ITEM_LEATHER,10); + priestTrades.recipes[2] = defineRecipe(TOOL_MAGIC_COMPASS,1,2,ITEM_IRONINGOT,10,ITEM_GLASS,5); + priestTrades.recipes[3] = defineRecipe(ITEM_COIN,1,1,ITEM_SLIME,5); + priestTrades.recipes[4] = defineRecipe(ITEM_COIN,1,1,ITEM_FLESH,5); + + farmerTrades.size = 7; + farmerTrades.recipes = (Recipe*)malloc(sizeof(Recipe) * (farmerTrades.size)); + farmerTrades.recipes[0] = defineRecipe(ITEM_WHEAT,5,1,ITEM_COIN,3); + farmerTrades.recipes[1] = defineRecipe(ITEM_BREAD,1,1,ITEM_COIN,3); + farmerTrades.recipes[2] = defineRecipe(ITEM_APPLE,2,1,ITEM_COIN,4); + farmerTrades.recipes[3] = defineRecipe(ITEM_ACORN,3,1,ITEM_COIN,1); + farmerTrades.recipes[4] = defineRecipe(ITEM_SEEDS,4,1,ITEM_COIN,2); + farmerTrades.recipes[5] = defineRecipe(ITEM_COIN,2,1,ITEM_SEEDS,5); + farmerTrades.recipes[6] = defineRecipe(ITEM_COIN,1,1,ITEM_ACORN,5); + + dwarfTrades.size = 2; + dwarfTrades.recipes = (Recipe*)malloc(sizeof(Recipe) * (dwarfTrades.size)); + dwarfTrades.recipes[0] = defineRecipe(ITEM_IRONINGOT,4,1,ITEM_GOLDINGOT,1); + dwarfTrades.recipes[1] = defineRecipe(ITEM_GOLDINGOT,2,1,ITEM_GEM,1); + //TODO: Trade Dragon Scales for something really nice +} + +void resetQuests() { + int i; + for(i=0; i= currentTalkOptions) currentTalkSel=0;} + if (k_down.clicked){ --currentTalkSel; if(currentTalkSel < 0) currentTalkSel=currentTalkOptions-1;} + + if(k_accept.clicked){ + currentTalkDone = true; + } +} + +void tickNPCMenu() { + //TODO: Handle upon currentNPC as well as the fitting quest progress + if(currentNPCMenu==NPC_MENU_TALK) tickTalkMenu(); + + + switch(currentNPC) { + case NPC_GIRL: + + 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; + + currentTalkSel = 0; + currentTalkDone = false; + currentTalkOptions = 1; + 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."; + } + } else if(currentNPCVal==1) { + if(currentTalkSel==0) { + currentNPCVal = 2; + + currentTalkSel = 0; + currentTalkDone = false; + currentTalkOptions = 1; + 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."; + } + } else if(currentNPCVal==2) { + if(currentTalkSel==0) { + currentNPCVal = 3; + + currentTalkSel = 0; + currentTalkDone = false; + currentTalkOptions = 1; + 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 = ""; + } + } else if(currentNPCVal==3) { + if(currentTalkSel==0) { + currentNPCVal = 4; + + currentTalkSel = 0; + currentTalkDone = false; + currentTalkOptions = 2; + currentTalkOption0 = "Let me do it!"; + 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."; + } + } else if(currentNPCVal==4) { + currentMenu = 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); + } + } + } + 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; + + currentTalkSel = 0; + currentTalkDone = false; + currentTalkOptions = 1; + 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; + + currentTalkSel = 0; + currentTalkDone = false; + currentTalkOptions = 2; + currentTalkOption0 = "I need to think about it"; + 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."; + } + } else if(currentNPCVal==1) { + if(currentTalkSel==0) { + currentMenu = MENU_NONE; + } else if(currentTalkSel==1) { + currentNPCVal = 2; + + currentTalkSel = 0; + currentTalkDone = false; + currentTalkOptions = 1; + currentTalkOption0 = ""; + + if(countItemInv(ITEM_GOLDINGOT,0,player.p.inv)>=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->countLevel -= 10; + if(item->countLevel < 1) removeItemFromInventory(item->slotNum, player.p.inv); + + 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."; + + 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 = ""; + + currentTalkOption0 = "Ok"; + } + } + } else if(currentNPCVal==2) { + if(currentTalkSel==0) { + currentMenu = MENU_NONE; + } + } + } + break; + case NPC_DWARF: + if(questManager.questlines[1].currentQuest<=1) { + if(currentNPCMenu==NPC_MENU_TALK && currentTalkDone) { + if(currentNPCVal==0) currentMenu = 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); + } + } + } + break; + } +} + +void renderTalkMenu(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); + + if(currentTalkOptions>=3) drawText(currentTalkOption2, 64, 147); + if(currentTalkOptions>=2) drawText(currentTalkOption1, 64, 171); + if(currentTalkOptions>=1) drawText(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); +} + +void renderNPCMenu(int xscr, int yscr) { + //TODO: Handle upon currentNPC as well as the fitting quest progress + switch(currentNPC) { + case NPC_GIRL: + if(currentNPCMenu==NPC_MENU_TALK) renderTalkMenu("TODO"); + break; + case NPC_PRIEST: + if(currentNPCMenu==NPC_MENU_TALK) renderTalkMenu("Priest Brom"); + break; + case NPC_FARMER: + if(currentNPCMenu==NPC_MENU_TALK) renderTalkMenu("Farmer Garrow"); + break; + case NPC_LIBRARIAN: + if(currentNPCMenu==NPC_MENU_TALK) renderTalkMenu("Librarian Ajihad"); + break; + case NPC_DWARF: + if(currentNPCMenu==NPC_MENU_TALK) renderTalkMenu("Dwarf Orik"); + break; + } +} diff --git a/source/Quests.h b/source/Quests.h new file mode 100644 index 0000000..19b78b9 --- /dev/null +++ b/source/Quests.h @@ -0,0 +1,32 @@ +#pragma once +#include +#include +#include "render.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 openNPCMenu(int npc); + +void renderNPCMenu(int xscr, int yscr); +void tickNPCMenu(); diff --git a/source/Render.c b/source/Render.c index 2e6666a..9e5302b 100644 --- a/source/Render.c +++ b/source/Render.c @@ -472,22 +472,39 @@ void renderTile(int i, int d, int x, int y) { checkSurrTiles4(x >> 4, y >> 4, TILE_FLOWER); checkSurrTiles4(x >> 4, y >> 4, TILE_SAPLING_TREE); - renderConnectedTile4(x, y, 256, 0); + if(currentLevel==1 && season==3) renderConnectedTile4(x, y, 256, 112); + else if(currentLevel==1 && season==2) renderConnectedTile4(x, y, 256, 128); + else renderConnectedTile4(x, y, 256, 0); break; case TILE_TREE: renderTile(TILE_GRASS, 0, x, y); checkSurrTiles8(x >> 4, y >> 4, TILE_TREE); - render(x, y, 256+((tu && tl && tul) ? 16 : 0), 48, 0); - render(x+8, y, 264+((tu && tr && tur) ? 16 : 0), 48, 0); - render(x, y+8, 256+((td && tl && tdl) ? 16 : 0), 56, 0); - render(x+8, y+8, 264+((td && tr && tdr) ? 16 : 0), 56, 0); + if(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) { + 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); + render(x+8, y+8, 360+((td && tr && tdr) ? 16 : 0), 120, 0); + } else { + render(x, y, 256+((tu && tl && tul) ? 16 : 0), 48, 0); + render(x+8, y, 264+((tu && tr && tur) ? 16 : 0), 48, 0); + render(x, y+8, 256+((td && tl && tdl) ? 16 : 0), 56, 0); + render(x+8, y+8, 264+((td && tr && tdr) ? 16 : 0), 56, 0); + } break; case TILE_ROCK: checkSurrTiles8(x >> 4, y >> 4, TILE_ROCK); - renderConnectedTile8(x, y, 336, 64); + if(currentLevel>1) + renderConnectedTile8(x, y, 256, 96); + else + renderConnectedTile8(x, y, 336, 64); break; case TILE_HARDROCK: checkSurrTiles8(x >> 4, y >> 4, TILE_HARDROCK); @@ -504,15 +521,20 @@ void renderTile(int i, int d, int x, int y) { checkSurrTiles4(x >> 4, y >> 4, TILE_CACTUS); checkSurrTiles4(x >> 4, y >> 4, TILE_SAPLING_CACTUS); - renderConnectedTile4(x, y, 320, 0); + if(currentLevel==1 && season==3) { + renderConnectedTile4(x, y, 256, 112); + } else { + renderConnectedTile4(x, y, 320, 0); - if (d > 0) { - render16(x, y, 336, 48, 0); - } + if (d > 0) { + render16(x, y, 336, 48, 0); + } + } 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); renderConnectedTile4(x, y, 384, 0); @@ -541,7 +563,8 @@ void renderTile(int i, int d, int x, int y) { break; case TILE_FLOWER: renderTile(TILE_GRASS, 0, x, y); - render16(x, y, 320, 48, getData(x >> 4, y >> 4)); + 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)); break; case TILE_STAIRS_DOWN: if (currentLevel == 0) @@ -619,14 +642,14 @@ void renderTile(int i, int d, int x, int y) { renderConnectedTile8(x, y, 384, 32); break; case TILE_DUNGEON_FLOOR: - render16(x, y, 464, 32, 0); + render16(x, y, 464 + d*16, 32, 0); break; case TILE_DUNGEON_ENTRANCE: - render16(x, y, 480 + (currentLevel==5 ? 16 : 0), 32, 0); + render16(x, y, 352 + (currentLevel==5 ? 16 : 0), 80, 0); break; case TILE_MAGIC_BARRIER: renderTile(TILE_DUNGEON_FLOOR, 0, x, y); - render16(x, y, 320, 64, 0); + render16(x, y, 320, 64, getData(x >> 4, y >> 4)); //draw remaining pillar count if((player.x - (x+8))*(player.x - (x+8)) + (player.y - (y+8))*(player.y - (y+8)) <= 24*24) { @@ -650,6 +673,38 @@ void renderTile(int i, int d, int x, int y) { drawSizedTextColor(currentCount, x+4, y+4, 2, dungeonColor[0]); } + break; + case TILE_BOOKSHELVES: + checkSurrTiles4(x >> 4, y >> 4, TILE_BOOKSHELVES); + + renderConnectedTile4(x, y, 384, 80 + d*16); + break; + case TILE_WOOD_FLOOR: + 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); + + if(currentLevel==1 && season==3) renderConnectedTile4(x, y, 256, 112); + else renderConnectedTile4(x, y, 448, 80); + break; + case TILE_MUSHROOM_BROWN: + renderTile(TILE_MYCELIUM, 0, x, y); + render16(x, y, 448 + (d&0x1)*16, 96, 0); + break; + case TILE_MUSHROOM_RED: + renderTile(TILE_MYCELIUM, 0, x, y); + render16(x, y, 480 + (d&0x1)*16, 96, 0); + break; + case TILE_ICE: + renderTile(TILE_WATER, 0, x, y); + //checkSurrTiles4(x >> 4, y >> 4, TILE_WATER); + //checkSurrTiles4(x >> 4, y >> 4, TILE_HOLE); + checkSurrTiles4(x >> 4, y >> 4, TILE_ICE); + + renderConnectedTile4(x, y, 448, 112); break; } @@ -858,6 +913,40 @@ void renderMenuBackground(int xScroll, int yScroll) { 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; + int xt; + int yt; + for(xt=0; xt<4; ++xt) { + for(yt=0; yt<3; ++yt) { + sf2d_draw_texture_part_scale(icons, xp + xt*128, yp + yt*128, 192, 0, 64, 64, 2, 2); + sf2d_draw_texture_part_scale(icons, xp2 + xt*128, yp2 + yt*128, 192, 0, 64, 64, 2, 2); + } + } + } + + if(rain) { + int xp = -((xScroll*2)%128); + int yp = -128 + ((tickCount<<2) - yScroll*2)%128; + int xp2 = -((xScroll*2+8)%128); + int yp2 = -128 + ((tickCount<<1)+64 - yScroll*2)%128; + int xt; + int yt; + for(xt=0; xt<4; ++xt) { + for(yt=0; yt<3; ++yt) { + sf2d_draw_texture_part_scale(icons, xp + xt*128, yp + yt*128, 128, 0, 64, 64, 2, 2); + sf2d_draw_texture_part_scale(icons, xp2 + xt*128, yp2 + yt*128, 128, 0, 64, 64, 2, 2); + } + } + } + } +} + void renderDayNight() { if(currentLevel==1 && (daytime<6000 || daytime>18000)) { int color1 = 0x000C0C0C; @@ -1197,6 +1286,8 @@ void renderEntity(Entity* e, int x, int y) { case ENTITY_GLOWWORM: render(x-4, y-4, 224, 112, 0); break; + case ENTITY_NPC: + render16(x - 8, y - 8, (e->npc.type*16) + 0, 64, 0); } } @@ -1244,8 +1335,7 @@ void renderItemList(Inventory * inv, int xo, int yo, int x1, int y1, } } -void renderRecipes(RecipeManager * r, int xo, int yo, int x1, int y1, - int selected) { +void renderRecipes(RecipeManager * r, int xo, int yo, int x1, int y1, int selected) { int size = r->size; if (size < 1) return; @@ -1265,16 +1355,16 @@ void renderRecipes(RecipeManager * r, int xo, int yo, int x1, int y1, int i, col; for (i = 0; i < i1; ++i) { int x = (1 + xo) << 4, y = (i + 1 + yo) << 4; - renderItemIcon(r->recipes[i + io].itemResult, - r->recipes[i + io].itemAmountLevel, x >> 1, y >> 1); + renderItemIcon(r->recipes[i + io].itemResult, r->recipes[i + io].itemAmountLevel, x >> 1, y >> 1); if (r->recipes[i + io].canCraft) col = 0xFFFFFFFF; else col = 0xFF7F7F7F; - drawTextColor( - getBasicItemName(r->recipes[i + io].itemResult, - r->recipes[i + io].itemAmountLevel), x + 18, y + 2, - col); + if(r->recipes[i + io].itemAmountLevel==1) { + drawTextColor(getBasicItemName(r->recipes[i + io].itemResult, r->recipes[i + io].itemAmountLevel), x + 18, y + 2, col); + } else { + drawTextColor(getItemName(r->recipes[i + io].itemResult, r->recipes[i + io].itemAmountLevel), x + 18, y + 2, col); + } } int yy = selected + 1 - io + yo; @@ -1317,6 +1407,8 @@ void renderItemWithTextCentered(Item* item, int width, int y) { } void renderItemIcon(int itemID, int countLevel, int x, int y) { + int xd; + int yd; switch (itemID) { case ITEM_NULL: return; @@ -1491,11 +1583,31 @@ void renderItemIcon(int itemID, int countLevel, int x, int y) { case ITEM_DRAGON_SCALE: render(x, y, 168, 160, 0); break; + case ITEM_BOOKSHELVES: + render(x, y, 232, 144, 0); + break; + case ITEM_MAGIC_DUST: + render(x, y, 200, 152, 0); + break; + case ITEM_COIN: + render(x, y, 208, 152, 0); + break; case TOOL_BUCKET: render(x, y, 200 + countLevel * 8, 144, 0); break; case TOOL_BOW: 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); + if(abs(yd)>abs(xd)) { + if(yd<0) render(x, y, 112, 168, 0); + else render(x, y, 120, 168, 0); + } else { + if(xd<0) render(x, y, 128, 168, 0); + else render(x, y, 136, 168, 0); + } + break; } } diff --git a/source/Render.h b/source/Render.h index 89f2ef7..e7b157c 100644 --- a/source/Render.h +++ b/source/Render.h @@ -31,6 +31,7 @@ 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 renderButtonIcon(u32 icon, int x, int y, float scale); diff --git a/source/SaveLoad.c b/source/SaveLoad.c index da49522..60766d0 100644 --- a/source/SaveLoad.c +++ b/source/SaveLoad.c @@ -1,27 +1,5 @@ #include "SaveLoad.h" -s16 calculateImportantEntites(EntityManager * eManager, int level){ - int i; - s16 count = 0; - for(i = 0; i < eManager->lastSlot[level]; ++i){ - switch(eManager->entities[level][i].type){ - case ENTITY_AIRWIZARD: - case ENTITY_SLIME: - case ENTITY_ZOMBIE: - case ENTITY_SKELETON: - case ENTITY_KNIGHT: - case ENTITY_ITEM: - case ENTITY_FURNITURE: - case ENTITY_PASSIVE: - case ENTITY_GLOWWORM: - case ENTITY_DRAGON: - count++; - break; - } - } - return count; -} - bool entityIsImportant(Entity * e){ switch(e->type){ case ENTITY_AIRWIZARD: @@ -34,12 +12,24 @@ bool entityIsImportant(Entity * e){ case ENTITY_PASSIVE: case ENTITY_GLOWWORM: case ENTITY_DRAGON: + case ENTITY_NPC: return true; default: return false; } } +s16 calculateImportantEntites(EntityManager * eManager, int level){ + int i; + s16 count = 0; + for(i = 0; i < eManager->lastSlot[level]; ++i){ + if(entityIsImportant(&eManager->entities[level][i])){ + count++; + } + } + return count; +} + void saveCurrentWorld(char * filename, EntityManager * eManager, Entity * player, u8 * map, u8 * mapData){ FILE * file = fopen(filename, "wb"); int i,j; @@ -107,6 +97,9 @@ void saveCurrentWorld(char * filename, EntityManager * eManager, Entity * player case ENTITY_DRAGON: fwrite(&eManager->entities[i][j].dragon.health, sizeof(s16), 1, file); break; + case ENTITY_NPC: + fwrite(&eManager->entities[i][j].npc.type, sizeof(u8), 1, file); + break; } } } @@ -120,6 +113,25 @@ void saveCurrentWorld(char * filename, EntityManager * eManager, Entity * player 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); + fclose(file); } @@ -335,6 +347,16 @@ int loadWorld(char * filename, EntityManager * eManager, Entity * player, u8 * m 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; } } } @@ -347,6 +369,40 @@ int loadWorld(char * filename, EntityManager * eManager, Entity * player, u8 * m 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; diff --git a/source/main.c b/source/main.c index ebe5ede..9b21c2f 100644 --- a/source/main.c +++ b/source/main.c @@ -13,6 +13,10 @@ #include "Menu.h" #include "texturepack.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 + void initMiniMapData() { int i; for(i = 0; i < 128 * 128; ++i) { @@ -29,11 +33,11 @@ void initMiniMap(bool loadUpWorld) { void initNewMap() { newSeed(); - createAndValidateSkyMap(128, 128, map[0], data[0]); - createAndValidateTopMap(128, 128, map[1], data[1]); - createAndValidateUndergroundMap(128, 128, 1, map[2], data[2]); - createAndValidateUndergroundMap(128, 128, 2, map[3], data[3]); - createAndValidateUndergroundMap(128, 128, 3, map[4], data[4]); + 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) { @@ -48,6 +52,7 @@ void setupGame(bool loadUpWorld) { if (!loadUpWorld) { initNewMap(); initPlayer(); + resetQuests(); airWizardHealthDisplay = 2000; int i; for (i = 0; i < 5; ++i) { @@ -55,8 +60,12 @@ void setupGame(bool loadUpWorld) { } 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); } @@ -78,7 +87,7 @@ void setupBGMap(bool loadUpWorld) { if(!loadUpWorld) { newSeed(); - createAndValidateTopMap(128, 128, map[1], data[1]); + createAndValidateTopMap(128, 128, 1, map[1], data[1]); } else { loadWorld(currentFileName, &eManager, &player, (u8*) map, (u8*) data); } @@ -113,6 +122,14 @@ void tick() { //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); @@ -138,25 +155,20 @@ void tick() { else if (yscr > 1912) yscr = 1912; - if(eManager.lastSlot[currentLevel]<80) { + 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))) + 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); } } -void clearScreen(int* data, u8 fill, int size) { - int i; - for (i = 0; i < size / 4; ++i) - data[i] = 0xFF000000; -} - char debugText[34]; char bossHealthText[34]; int main() { @@ -243,6 +255,7 @@ int main() { tickCount = 0; initRecipes(); + initQuests(); while (aptMainLoop()) { ++tickCount; hidScanInput(); @@ -266,6 +279,7 @@ int main() { renderBackground(xscr, yscr); renderEntities(player.x, player.y, &eManager); renderPlayer(); + renderWeather(xscr, yscr); resetStencilStuff(); @@ -299,6 +313,7 @@ int main() { stopMusic(); + freeQuests(); freeRecipes(); freeLightBakes();