From 3699414dcdf4cfce4539bae1e414235b36e046ae Mon Sep 17 00:00:00 2001 From: Andre Schweiger Date: Sat, 7 Jan 2017 21:54:28 +0100 Subject: [PATCH] Started work on new update Added some small new map generation features Added "NPCs" and "Quests" (atleast a first experiment for them) Added magic compass to make search for stairs leass annoying Added mostly visual season and weather effects --- data/icons2.png | Bin 26995 -> 33115 bytes source/Crafting.c | 6 +- source/Crafting.h | 2 + source/Entity.c | 16 ++ source/Entity.h | 8 + source/Globals.c | 133 +++++++++-- source/Globals.h | 26 +++ source/Item.c | 12 +- source/Item.h | 4 + source/MapGen.c | 568 +++++++++++++++++++++++++++++++++------------- source/MapGen.h | 26 ++- source/Menu.c | 26 ++- source/Menu.h | 1 + source/Quests.c | 417 ++++++++++++++++++++++++++++++++++ source/Quests.h | 32 +++ source/Render.c | 156 +++++++++++-- source/Render.h | 1 + source/SaveLoad.c | 100 ++++++-- source/main.c | 43 ++-- 19 files changed, 1324 insertions(+), 253 deletions(-) create mode 100644 source/Quests.c create mode 100644 source/Quests.h diff --git a/data/icons2.png b/data/icons2.png index 2527908eca10db6039a76d1d26623365f529d365..83c8034c99d24c14858d88bdb0f527f04b6825c5 100644 GIT binary patch literal 33115 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

R4!zmF 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 literal 26995 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 Lb!YMRkcz5K9 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();