diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6503924 --- /dev/null +++ b/Makefile @@ -0,0 +1,207 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +TOPDIR ?= $(CURDIR) +include $(DEVKITARM)/3ds_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +# +# NO_SMDH: if set to anything, no SMDH file is generated. +# APP_TITLE is the name of the app stored in the SMDH file (Optional) +# APP_DESCRIPTION is the description of the app stored in the SMDH file (Optional) +# APP_AUTHOR is the author of the app stored in the SMDH file (Optional) +# ICON is the filename of the icon (.png), relative to the project folder. +# If not set, it attempts to use one of the following (in this order): +# - .png +# - icon.png +# - /default_icon.png +#--------------------------------------------------------------------------------- +TARGET := Minicraft3DS +BUILD := build +SOURCES := source +DATA := data +INCLUDES := include + +APP_TITLE := Test +APP_DESCRIPTION := ??? +APP_AUTHOR := Davideesk + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard + +CFLAGS := -g -Wall -O2 -mword-relocations \ + -fomit-frame-pointer -ffast-math \ + $(ARCH) + +CFLAGS += $(INCLUDE) -DARM11 -D_3DS + +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 + +ASFLAGS := -g $(ARCH) +LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) + +LIBS := -lsfil -lpng -ljpeg -lz -lsf2d -lctru -lm + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(CTRULIB) $(PORTLIBS) + + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +ifeq ($(strip $(ICON)),) + icons := $(wildcard *.png) + ifneq (,$(findstring $(TARGET).png,$(icons))) + export APP_ICON := $(TOPDIR)/$(TARGET).png + else + ifneq (,$(findstring icon.png,$(icons))) + export APP_ICON := $(TOPDIR)/icon.png + endif + endif +else + export APP_ICON := $(TOPDIR)/$(ICON) +endif + +ifeq ($(strip $(NO_SMDH)),) + export _3DSXFLAGS += --smdh=$(CURDIR)/$(TARGET).smdh +endif + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).3dsx $(OUTPUT).smdh $(TARGET).elf $(TARGET)-strip.elf $(TARGET).cia $(TARGET).3ds +#--------------------------------------------------------------------------------- +$(TARGET)-strip.elf: $(BUILD) + @$(STRIP) $(TARGET).elf -o $(TARGET)-strip.elf +#--------------------------------------------------------------------------------- +cci: $(TARGET)-strip.elf + @makerom -f cci -rsf resources/$(TARGET).rsf -target d -exefslogo -elf $(TARGET)-strip.elf -o $(TARGET).3ds + @echo "built ... sfil_sample.3ds" +#--------------------------------------------------------------------------------- +cia: $(TARGET)-strip.elf + @makerom -f cia -o $(TARGET).cia -elf $(TARGET)-strip.elf -rsf resources/$(TARGET).rsf -exefslogo -target t + @echo "built ... sfil_sample.cia" +#--------------------------------------------------------------------------------- +send: $(BUILD) + @3dslink $(TARGET).3dsx +#--------------------------------------------------------------------------------- +run: $(BUILD) + @citra $(TARGET).3dsx + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +ifeq ($(strip $(NO_SMDH)),) +$(OUTPUT).3dsx : $(OUTPUT).elf $(OUTPUT).smdh +else +$(OUTPUT).3dsx : $(OUTPUT).elf +endif + +$(OUTPUT).elf : $(OFILES) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +%.jpeg.o: %.jpeg +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +%.png.o : %.png +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +# WARNING: This is not the right way to do this! TODO: Do it right! +#--------------------------------------------------------------------------------- +%.vsh.o : %.vsh +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @python $(AEMSTRO)/aemstro_as.py $< ../$(notdir $<).shbin + @bin2s ../$(notdir $<).shbin | $(PREFIX)as -o $@ + @echo "extern const u8" `(echo $(notdir $<).shbin | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(notdir $<).shbin | tr . _)`.h + @echo "extern const u8" `(echo $(notdir $<).shbin | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(notdir $<).shbin | tr . _)`.h + @echo "extern const u32" `(echo $(notdir $<).shbin | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(notdir $<).shbin | tr . _)`.h + @rm ../$(notdir $<).shbin + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/data/Font.png b/data/Font.png new file mode 100644 index 0000000..fdae3fb Binary files /dev/null and b/data/Font.png differ diff --git a/data/icons2.png b/data/icons2.png new file mode 100644 index 0000000..19caa7a Binary files /dev/null and b/data/icons2.png differ diff --git a/resources/bossdeath.raw b/resources/bossdeath.raw new file mode 100644 index 0000000..2ac37dc Binary files /dev/null and b/resources/bossdeath.raw differ diff --git a/resources/craft.raw b/resources/craft.raw new file mode 100644 index 0000000..4a0261b Binary files /dev/null and b/resources/craft.raw differ diff --git a/resources/death.raw b/resources/death.raw new file mode 100644 index 0000000..6a91b12 Binary files /dev/null and b/resources/death.raw differ diff --git a/resources/monsterhurt.raw b/resources/monsterhurt.raw new file mode 100644 index 0000000..71cd557 Binary files /dev/null and b/resources/monsterhurt.raw differ diff --git a/resources/pickup.raw b/resources/pickup.raw new file mode 100644 index 0000000..410c05b Binary files /dev/null and b/resources/pickup.raw differ diff --git a/resources/playerhurt.raw b/resources/playerhurt.raw new file mode 100644 index 0000000..b88b43f Binary files /dev/null and b/resources/playerhurt.raw differ diff --git a/resources/test.raw b/resources/test.raw new file mode 100644 index 0000000..4291187 Binary files /dev/null and b/resources/test.raw differ diff --git a/source/Crafting.c b/source/Crafting.c new file mode 100644 index 0000000..670b906 --- /dev/null +++ b/source/Crafting.c @@ -0,0 +1,136 @@ +#include "Crafting.h" + +void checkCanCraftRecipes(RecipeManager * rm, Inventory * inv){ + int i, j; + for(i = 0; i < rm->size; i++){ + rm->recipes[i].canCraft = true; + for(j = 0; j < rm->recipes[i].numOfCosts; j++){ + if(countItemInv(rm->recipes[i].costs[j].costItem,0,inv) < rm->recipes[i].costs[j].costAmount){ + rm->recipes[i].canCraft = false; + } + } + } +} + +int compareCanCraft(const void * ra, const void * rb) { + Recipe* r1 = (Recipe*)ra; + Recipe* r2 = (Recipe*)rb; + if (r1->canCraft && !r2->canCraft) return -1; + if (!r1->canCraft && r2->canCraft) return 1; + return r1->order - r2->order; // Needed for stable sorting. +} + +void sortRecipes(RecipeManager * rm){ + qsort(rm->recipes,rm->size,sizeof(Recipe),compareCanCraft); +} + +void deductCost(Cost c, Inventory * inv){ + Item* item = getItemFromInventory(c.costItem, inv); + if(!item->onlyOne){ + item->countLevel -= c.costAmount; + if(item->countLevel < 1) removeItemFromInventory(item->slotNum, inv); + } else { + removeItemFromInventory(item->slotNum, inv); + } +} + +bool craftItem(RecipeManager * rm, Recipe* r, Inventory* inv){ + if(r->canCraft){ + int i; + for(i=0;inumOfCosts;++i) deductCost(r->costs[i], inv); + Item item = newItem(r->itemResult,r->itemAmountLevel); + + if(!item.onlyOne && countItemInv(item.id,item.countLevel,inv) > 0){ + getItemFromInventory(item.id, inv)->countLevel += r->itemAmountLevel; + } else{ + pushItemToInventoryFront(item, inv); + } + checkCanCraftRecipes(rm, inv); + sortRecipes(rm); + return true; + } + return false; +} + +Cost newCost(int i, int c){ + Cost nc; + nc.costItem = i; + nc.costAmount = c; + return nc; +} + +u8 curPlace = 0; +Recipe defineRecipe(int item, int amountOrLevel, int numArgs, ...){ + Recipe r; + r.itemResult = item; + r.itemAmountLevel = amountOrLevel; + r.numOfCosts = numArgs; + int i; + va_list al; + numArgs <<= 1; // Did this to get rid of a warning. + va_start(al,numArgs); + for(i=0;i +#include "Item.h" + +typedef struct { + int costItem; + int costAmount; +} Cost; + +typedef struct { + bool canCraft; + int itemResult; + int itemAmountLevel; + s8 numOfCosts; + Cost costs[6]; // Up to 6 items for costs + u8 order; // Used for stable sorting. +} Recipe; + +typedef struct { + int size; + Recipe * recipes; +} RecipeManager; + + +RecipeManager workbenchRecipes; +RecipeManager furnaceRecipes; +RecipeManager ovenRecipes; +RecipeManager anvilRecipes; + +void checkCanCraftRecipes(RecipeManager * rm, Inventory * inv); +void sortRecipes(RecipeManager * rm); +bool craftItem(RecipeManager * rm, Recipe* r, Inventory* inv); + +void initRecipes(); +void freeRecipes(); diff --git a/source/Entity.c b/source/Entity.c new file mode 100644 index 0000000..3d48e77 --- /dev/null +++ b/source/Entity.c @@ -0,0 +1,204 @@ +#include "Entity.h" + +#define PI 3.141592654 +double gaussrand() +{ + static double U, V; + static int phase = 0; + double Z; + + if(phase == 0) { + U = (rand() + 1.) / (RAND_MAX + 2.); + V = rand() / (RAND_MAX + 1.); + Z = sqrt(-2 * log(U)) * sin(2 * PI * V); + } else + Z = sqrt(-2 * log(U)) * cos(2 * PI * V); + + phase = 1 - phase; + + return Z; +} + +Entity newItemEntity(Item item, int x, int y, int level){ + Entity e; + e.type = ENTITY_ITEM; + e.level = level; + e.entityItem.age = 0; + e.entityItem.item = item; + e.x = x; + e.y = y; + e.xr = 3; + e.yr = 3; + e.canPass = false; + + e.entityItem.xx = x; + e.entityItem.yy = y; + e.entityItem.zz = 2; + e.entityItem.xa = gaussrand() * 0.1; + e.entityItem.ya = gaussrand() * 0.1; + e.entityItem.za = ((float)rand() / RAND_MAX) * 0.45 + 1; + + return e; +} + +void assignInventory(Entity* e){ + if(eManager.nextInv > 300) return; + e->entityFurniture.inv = &eManager.invs[eManager.nextInv]; + eManager.nextInv++; +} + +Entity newFurnitureEntity(int itemID,Inventory * invPtr, int x, int y, int level){ + Entity e; + e.type = ENTITY_FURNITURE; + e.level = level; + e.x = x; + e.y = y; + e.xr = 3; + e.yr = 3; + e.entityFurniture.itemID = itemID; + e.canPass = false; + if(itemID == ITEM_LANTERN) e.entityFurniture.r = 8; + else if(itemID == ITEM_CHEST){ + if(invPtr == NULL)assignInventory(&e); + else e.entityFurniture.inv = invPtr; + } + return e; +} + + +Entity newZombieEntity(int lvl, int x, int y, int level){ + Entity e; + e.type = ENTITY_ZOMBIE; + e.level = level; + e.x = x; + e.y = y; + e.hurtTime = 0; + e.xKnockback = 0; + e.yKnockback = 0; + e.zombie.lvl = lvl; + e.zombie.health = lvl * lvl * 10; + e.zombie.dir = 0; + e.xr = 4; + e.yr = 3; + e.canPass = false; + switch(lvl){ + case 2: e.zombie.color = 0xCC8282FF; break; + case 3: e.zombie.color = 0xEFEFEFFF; break; + case 4: e.zombie.color = 0x6262AAFF; break; + default: e.zombie.color = 0x95DB95FF; break; + } + return e; +} + +Entity newSlimeEntity(int lvl, int x, int y, int level){ + Entity e; + e.type = ENTITY_SLIME; + e.level = level; + e.x = x; + e.y = y; + e.hurtTime = 0; + e.xKnockback = 0; + e.yKnockback = 0; + e.slime.lvl = lvl; + e.slime.xa = 0; + e.slime.ya = 0; + e.slime.dir = 0; + e.slime.health = lvl * lvl * 5; + e.xr = 4; + e.yr = 3; + e.canPass = false; + switch(lvl){ + case 2: e.slime.color = 0xCC8282FF; break; + case 3: e.slime.color = 0xEFEFEFFF; break; + case 4: e.slime.color = 0x6262AAFF; break; + default: e.slime.color = 0x95DB95FF; break; + } + return e; +} + +Entity newAirWizardEntity(int x, int y, int level){ + Entity e; + e.type = ENTITY_AIRWIZARD; + e.level = level; + e.x = x; + e.y = y; + e.hurtTime = 0; + e.xKnockback = 0; + e.yKnockback = 0; + e.wizard.dir = 0; + e.wizard.health = 2000; + e.wizard.attackDelay = 0; + e.wizard.attackTime = 0; + e.wizard.attackType = 0; + e.wizard.xa = 0; + e.wizard.ya = 0; + e.xr = 4; + e.yr = 3; + e.canPass = true; + return e; +} + +Entity newSparkEntity(Entity* parent, float xa, float ya){ + Entity e; + e.type = ENTITY_SPARK; + e.spark.age = 0; + e.spark.parent = parent; + e.spark.xa = xa; + e.spark.ya = ya; + e.spark.xx = parent->x; + e.spark.yy = parent->y; + e.xr = 3; + e.yr = 3; + e.canPass = true; + return e; +} + +Entity newTextParticleEntity(char * str, u32 color, int x, int y, int level){ + Entity e; + e.type = ENTITY_TEXTPARTICLE; + e.level = level; + e.textParticle.color = color; + e.textParticle.age = 0; + e.textParticle.text = (char*)calloc(strlen(str),sizeof(char)); + strncpy(e.textParticle.text,str,strlen(str)); + e.x = x; + e.y = y; + e.canPass = true; + e.textParticle.xx = x; + e.textParticle.yy = y; + e.textParticle.zz = 2; + e.textParticle.xa = gaussrand() * 0.3; + e.textParticle.ya = gaussrand() * 0.2; + e.textParticle.za = ((float)rand() / RAND_MAX) * 0.7 + 2; + + return e; +} +Entity newSmashParticleEntity(int x, int y, int level){ + Entity e; + e.type = ENTITY_SMASHPARTICLE; + e.level = level; + e.smashParticle.age = 0; + e.x = x; + e.y = y; + e.canPass = true; + playSound(snd_monsterHurt); + return e; +} + +void addEntityToList(Entity e, EntityManager* em){ + e.slotNum = em->lastSlot[e.level]; + em->entities[e.level][em->lastSlot[e.level]] = e; + ++em->lastSlot[e.level]; +} + +Entity nullEntity; +void removeEntityFromList(Entity * e,int level,EntityManager* em){ + int i; + if(em->entities[level][e->slotNum].type == ENTITY_TEXTPARTICLE) free(em->entities[level][e->slotNum].textParticle.text); + for(i = e->slotNum; i < em->lastSlot[level];++i){ + em->entities[level][i] = em->entities[level][i + 1]; // Move the items down. + em->entities[level][i].slotNum = i; + } + em->lastSlot[level]--; + em->entities[level][em->lastSlot[level]] = nullEntity; // Make the last slot null. +} diff --git a/source/Entity.h b/source/Entity.h new file mode 100644 index 0000000..c812ea0 --- /dev/null +++ b/source/Entity.h @@ -0,0 +1,170 @@ +#pragma once +#include "Crafting.h" +#include + +// Entity types +#define ENTITY_NULL 0 +#define ENTITY_ITEM 1 +#define ENTITY_FURNITURE 2 +#define ENTITY_ZOMBIE 3 +#define ENTITY_SLIME 4 +#define ENTITY_AIRWIZARD 5 +#define ENTITY_SPARK 6 +#define ENTITY_TEXTPARTICLE 7 +#define ENTITY_SMASHPARTICLE 8 +#define ENTITY_PLAYER 9 + +typedef struct Entity Entity; + +typedef struct { + s8 ax; + s8 ay; + u8 dir; + s8 health; + s8 stamina; + s8 staminaRecharge; + s8 staminaRechargeDelay; + s8 attackTimer; + u8 spawnTrigger; + bool isDead; + bool hasWon; + bool hasWonSaved; + s8 endTimer; + s16 walkDist; + bool isCarrying; + bool isSwimming; + int swimTimer; + int score; + Inventory* inv; + Item* activeItem; +} Player; + + +typedef struct { + float xa; + float ya; + float za; + float xx; + float yy; + float zz; + s16 age; + Item item; +} EntityItem; + +typedef struct { + s16 itemID; + bool active; + s8 r; // light radius for lantern. window select for chests. + Inventory* inv; // Points to chest inventory. + s16 oSel; // other selection inside the chest inv. +} EntityFurniture; + +typedef struct { + s8 xa; + s8 ya; + s16 health; + s8 dir; + s8 lvl; + s8 randWalkTime; + s8 walkDist; + u32 color; +} Zombie; + +typedef struct { + s8 xa; + s8 ya; + s16 health; + s8 lvl; + s8 dir; + s8 jumpTime; + u32 color; +} Slime; + +typedef struct { + s8 xa; + s8 ya; + s16 health; + s8 randWalkTime; + s8 walkDist; + s8 dir; + int attackDelay; + int attackTime; + int attackType; + s8 spriteAdjust; +} AirWizard; + +typedef struct { + Entity* parent; + s16 age; + float xa; + float ya; + float xx; + float yy; +} Spark; + +typedef struct { + float xa; + float ya; + float za; + float xx; + float yy; + float zz; + s16 age; + char* text; + int color; +} TextParticleEntity; + +typedef struct { + s16 age; +} SmashParticleEntity; + +struct Entity { + s16 x; + s16 y; + s8 xKnockback,yKnockback; + u8 xr,yr; + u8 type; + u8 level; + s8 hurtTime; + s16 slotNum; // Read-only. Do not mess with this. + bool canPass; + bool canSwim; + union { + Player p; + EntityItem entityItem; + EntityFurniture entityFurniture; + Zombie zombie; + Slime slime; + AirWizard wizard; + Spark spark; + TextParticleEntity textParticle; + SmashParticleEntity smashParticle; + }; +}; + +typedef struct { + Entity entities[5][1000]; + Entity wizardSparks[120]; + s16 lastSlot[5]; + Inventory invs[301];//1 for the player, 300 for chests. + s16 nextInv; +} EntityManager; + +EntityManager eManager; +s8 currentLevel; + + +double gaussrand(); +Entity newItemEntity(Item item, int x, int y, int level); +Entity newFurnitureEntity(int itemID,Inventory * invPtr, int x, int y, int level); +Entity newZombieEntity(int lvl, int x, int y, int level); +Entity newSlimeEntity(int lvl, int x, int y, int level); +Entity newAirWizardEntity(int x, int y, int level); +Entity newSparkEntity(Entity* parent, float xa, float ya); +Entity newTextParticleEntity(char * str, u32 color, int xa, int ya, int level); +Entity newSmashParticleEntity(int xa, int ya, 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 new file mode 100644 index 0000000..3387ff1 --- /dev/null +++ b/source/Globals.c @@ -0,0 +1,1319 @@ +#include "Globals.h" + +char versionText[34] = "BETA BUILD 3"; +char fpsstr[34]; +u8 currentMenu = 0; + +void addItemsToWorld(Item item,int x, int y, int count){ + int i; + for(i = 0;i x1 || e.y - e.yr > y1); +} + +int getEntities(Entity** result,int x0, int y0, int x1, int y1) { + int i, last = 0; + for (i = 0; i < eManager.lastSlot[currentLevel]; ++i) { + Entity* e = &eManager.entities[currentLevel][i]; + if (intersects(*e,x0, y0, x1, y1)){ + result[last] = e; + ++last; + } + } + return last; +} + +void removeSimilarElements(Entity * arr1[], Entity * arr2[]){ + int i,j; + for(i=0;ix) - e->xr) >> 4; + int yto0 = ((e->y) - e->yr) >> 4; + int xto1 = ((e->x) + e->xr) >> 4; + int yto1 = ((e->y) + e->yr) >> 4; + + int xt0 = ((e->x + xa) - e->xr) >> 4; + int yt0 = ((e->y + ya) - e->yr) >> 4; + int xt1 = ((e->x + xa) + e->xr) >> 4; + int yt1 = ((e->y + ya) + e->yr) >> 4; + bool blocked = false; + int xt,yt; + for (yt = yt0; yt <= yt1; ++yt) + for (xt = xt0; xt <= xt1; ++xt) { + if (xt >= xto0 && xt <= xto1 && yt >= yto0 && yt <= yto1) continue; + entityTileInteract(e, getTile(xt,yt), xt, yt); + if (!e->canPass && tileIsSolid(getTile(xt,yt),e)) { + blocked = true; + return false; + } + } + if (blocked) return false; + + Entity * wasInside[eManager.lastSlot[currentLevel]]; + Entity * isInside[eManager.lastSlot[currentLevel]]; + getEntities(wasInside, e->x - e->xr, e->y - e->yr, e->x + e->xr, e->y + e->yr); + int isSize = getEntities(isInside, e->x + xa - e->xr, e->y + ya - e->yr, e->x + xa + e->xr, e->y + ya + e->yr); + int i; + + for (i = 0; i < isSize; ++i) { + Entity * ent = isInside[i]; + if (ent == e || ent == NULL) continue; + EntityVsEntity(e, ent); + } + if(e->type != ENTITY_PLAYER){ + if(intersects(player, e->x + xa - e->xr, e->y + ya - e->yr, e->x + xa + e->xr, e->y + ya + e->yr)){ + EntityVsEntity(e, &player); + } + } + removeSimilarElements(wasInside, isInside); + + for (i = 0; i < isSize; ++i) { + Entity * ent = isInside[i]; + if (ent == e || ent == NULL) continue; + if (EntityBlocksEntity(e,ent)) return false; + } + + if(e->x + xa > 0 && e->x + xa < 2048) e->x += xa; + if(e->y + ya > 0 && e->y + ya < 2048) e->y += ya; + return true; +} + +bool move(Entity* e, int xa, int ya) { + if (xa != 0 || ya != 0) { + bool stopped = true; + if (xa != 0 && move2(e, xa, 0)) stopped = false; + if (ya != 0 && move2(e, 0, ya)) stopped = false; + return !stopped; + } + return true; +} + +bool moveMob(Entity* e, int xa, int ya){ + if (e->xKnockback < 0) { + move2(e,-1, 0); + e->xKnockback++; + } + if (e->xKnockback > 0) { + move2(e,1, 0); + e->xKnockback--; + } + if (e->yKnockback < 0) { + move2(e,0, -1); + e->yKnockback++; + } + if (e->yKnockback > 0) { + move2(e,0, 1); + e->yKnockback--; + } + if (e->hurtTime > 0) return true; + return move(e, xa, ya); +} + +void hurtEntity(Entity* e, int damage, int dir, u32 hurtColor){ + if (e->hurtTime > 0) return; + int xd = player.x - e->x; + int yd = player.y - e->y; + if (xd * xd + yd * yd < 80 * 80) playSound(snd_monsterHurt); + + char hurtText[11]; + sprintf(hurtText, "%d", damage); + addEntityToList(newTextParticleEntity(hurtText,hurtColor,e->x,e->y,currentLevel), &eManager); + + // In hindsight I should've made a generic Mob struct, but whatever. ¯\_(-.-)_/¯ + switch(e->type){ + case ENTITY_PLAYER: + e->p.health -= damage; + if(e->p.health < 1){ + playSound(snd_bossdeath); + e->p.endTimer = 60; + e->p.isDead = true; + return; + } + break; + case ENTITY_ZOMBIE: + e->zombie.health -= damage; + if(e->zombie.health < 1){ + addItemsToWorld(newItem(ITEM_FLESH,1),e->x+8, e->y+8, (rand()%2) + 1); + player.p.score += 50 * e->zombie.lvl; + removeEntityFromList(e,e->level,&eManager); + trySpawn(3, currentLevel); + return; + } + break; + case ENTITY_SLIME: + e->slime.health -= damage; + if(e->slime.health < 1){ + addItemsToWorld(newItem(ITEM_SLIME,1),e->x+8, e->y+8, (rand()%2) + 1); + player.p.score += 25 * e->slime.lvl; + removeEntityFromList(e,e->level,&eManager); + trySpawn(3, currentLevel); + return; + } + break; + case ENTITY_AIRWIZARD: + e->wizard.health -= damage; + airWizardHealthDisplay = e->wizard.health; + if(e->wizard.health < 1){ + removeEntityFromList(e,e->level,&eManager); + playSound(snd_bossdeath); + player.p.score += 1000; + player.p.endTimer = 60; + player.p.hasWon = true; + player.p.hasWonSaved = true; + return; + } + break; + } + + switch(dir){ + case -1: + switch(e->type){ + case ENTITY_PLAYER: + switch(e->p.dir){ + case 0: e->yKnockback = -10; break; + case 1: e->yKnockback = +10; break; + case 2: e->xKnockback = +10; break; + case 3: e->xKnockback = -10; break; + } + break; + case ENTITY_ZOMBIE: + switch(e->zombie.dir){ + case 0: e->yKnockback = -10; break; + case 1: e->yKnockback = +10; break; + case 2: e->xKnockback = +10; break; + case 3: e->xKnockback = -10; break; + } + break; + case ENTITY_SLIME: + switch(e->slime.dir){ + case 0: e->yKnockback = -10; break; + case 1: e->yKnockback = +10; break; + case 2: e->xKnockback = +10; break; + case 3: e->xKnockback = -10; break; + } + break; + } + break; + case 0: e->yKnockback = +10; break; + case 1: e->yKnockback = -10; break; + case 2: e->xKnockback = -10; break; + case 3: e->xKnockback = +10; break; + } + e->hurtTime = 10; +} + +bool ItemVsEntity(Item* item, Entity* e, int dir){ + switch(item->id){ + case ITEM_POWGLOVE: + if(e->type == ENTITY_FURNITURE){ + Item nItem = newItem(e->entityFurniture.itemID,0); + if(e->entityFurniture.itemID == ITEM_CHEST) nItem.chestPtr = e->entityFurniture.inv; + pushItemToInventoryFront(nItem, player.p.inv); + removeEntityFromList(e,currentLevel,&eManager); + player.p.activeItem = &player.p.inv->items[0]; + player.p.isCarrying = true; + return true; + } break; + case TOOL_AXE: + switch(e->type){ + case ENTITY_ZOMBIE: + case ENTITY_SLIME: + case ENTITY_AIRWIZARD: + if(playerUseEnergy(4-item->countLevel)) hurtEntity(e,(item->countLevel + 1) * 2 + (rand()%4),dir,0xFF0000FF); + else hurtEntity(e,1+rand()%3,dir,0xFF0000FF); + return true; + } break; + case TOOL_SWORD: + switch(e->type){ + case ENTITY_ZOMBIE: + case ENTITY_SLIME: + case ENTITY_AIRWIZARD: + if(playerUseEnergy(4-item->countLevel)) hurtEntity(e,(item->countLevel+1)*3+(rand()%(2+item->countLevel*item->countLevel*2)),dir,0xFF0000FF); + else hurtEntity(e,1+rand()%3,dir,0xFF0000FF); + return true; + } break; + case ITEM_NULL: + switch(e->type){ + case ENTITY_ZOMBIE: + case ENTITY_SLIME: + case ENTITY_AIRWIZARD: + hurtEntity(e,1+rand()%3,dir,0xFF0000FF); + return true; + } break; + } + return false; +} + +bool interact(int x0, int y0, int x1, int y1) { + Entity * es[eManager.lastSlot[currentLevel]]; + int eSize = getEntities(es, x0, y0, x1, y1); + int i; + for (i = 0; i < eSize; ++i) { + Entity * ent = es[i]; + if (ent != &player){ + if (ItemVsEntity(player.p.activeItem, ent, player.p.dir)) return true; + } + } + return false; +} + +void EntityVsEntity(Entity* e1, Entity* e2){ + switch(e1->type){ + case ENTITY_PLAYER: playerEntityInteract(e2); break; + case ENTITY_ZOMBIE: + if(e2->type == ENTITY_PLAYER){ + hurtEntity(e2, 2, e1->zombie.dir, 0xFF00AFFF); + switch(e1->zombie.dir){ + case 0: e1->yKnockback = -4; break; + case 1: e1->yKnockback = +4; break; + case 2: e1->xKnockback = +4; break; + case 3: e1->xKnockback = -4; break; + } + } + break; + case ENTITY_SLIME: + if(e2->type == ENTITY_PLAYER){ + hurtEntity(e2, 1, e1->slime.dir, 0xFF00AFFF); + switch(e1->slime.dir){ + case 0: e1->yKnockback = -4; break; + case 1: e1->yKnockback = +4; break; + case 2: e1->xKnockback = +4; break; + case 3: e1->xKnockback = -4; break; + } + } + break; + case ENTITY_AIRWIZARD: + if(e2->type == ENTITY_PLAYER) hurtEntity(e2, 3, e1->wizard.dir, 0xFF00AFFF); + break; + case ENTITY_SPARK: + if(e2 != e1->spark.parent) hurtEntity(e2, 1, -1, 0xFF00AFFF); + break; + } +} + +bool EntityBlocksEntity(Entity* e1, Entity* e2){ + switch(e1->type){ + case ENTITY_PLAYER: + case ENTITY_FURNITURE: + switch(e2->type){ + case ENTITY_FURNITURE: + case ENTITY_ZOMBIE: + case ENTITY_SLIME: + case ENTITY_AIRWIZARD: + case ENTITY_PLAYER: + return true; + break; + } + break; + } + return false; +} + +bool tileIsSolid(int tile, Entity * e){ + switch(tile){ + case TILE_TREE: + case TILE_ROCK: + case TILE_HARDROCK: + case TILE_CACTUS: + case TILE_IRONORE: + case TILE_GOLDORE: + case TILE_GEMORE: + case TILE_CLOUDCACTUS: + case TILE_LAVA: + case 255: + return true; + case TILE_WATER: + if(e != NULL && !e->canSwim) return true; + } + return false; +} + +/* For minimap */ +u32 getTileColor(int tile){ + switch(tile){ + case TILE_WATER: return 0xFFFF0000; + case TILE_LAVA: return 0xFF0000FF; + case TILE_DIRT: return 0xFF6C6D82; + case TILE_ROCK: return 0xFF7F7F7F; + case TILE_HARDROCK: return 0xFF7F5F5F; + case TILE_GRASS: return 0xFF00FF00; + case TILE_TREE: return 0xFF007F00; + case TILE_SAND: return 0xFF00FFFF; + case TILE_CACTUS: return 0xFF009F00; + case TILE_FLOWER: return 0xFF00FF3F; + case TILE_IRONORE: return 0xFF9696DC; + case TILE_GOLDORE: return 0xFF9AE8E5; + case TILE_GEMORE: return 0xFFDE98DF; + case TILE_CLOUD: return 0xFFFFFFFF; + case TILE_CLOUDCACTUS: return 0xFFAFAFAF; + case TILE_STAIRS_DOWN: return 0xFF9F9F9F; + case TILE_STAIRS_UP: return 0xFF9F9F9F; + case TILE_HOLE: return 0xFF383838; + default: return 0xFF111111; + } +} + +void healPlayer(int amount){ + player.p.health += amount; + if(player.p.health > 10) player.p.health = 10; + char healText[11]; + sprintf(healText, "%d", amount); + addEntityToList(newTextParticleEntity(healText,0x00FF00FF,player.x,player.y,currentLevel), &eManager); +} + +s8 itemTileInteract(int tile, Item* item, int x, int y, int px, int py, int dir){ + + // Furniture items + if(item->id > 27){ + if(!tileIsSolid(getTile(x,y), NULL)){ + addEntityToList(newFurnitureEntity(item->id,item->chestPtr, (x<<4)+8, (y<<4)+8, currentLevel), &eManager); + removeItemFromCurrentInv(item); + player.p.activeItem = &noItem; + return 2; + } + return 0; + } + + // Health items + switch(item->id){ + case ITEM_APPLE: + if(player.p.health < 10 && playerUseEnergy(2)){ + healPlayer(1); + --item->countLevel; + if(item->countLevel < 1){ + removeItemFromCurrentInv(item); + player.p.activeItem = &noItem; + } + } + return 0; + case ITEM_FLESH: + if(player.p.health < 10 && playerUseEnergy(4+(rand()%4))){ + healPlayer(1); + --item->countLevel; + if(item->countLevel < 1){ + removeItemFromCurrentInv(item); + player.p.activeItem = &noItem; + } + } + return 0; + case ITEM_BREAD: + if(player.p.health < 10 && playerUseEnergy(3)){ + healPlayer(2); + --item->countLevel; + if(item->countLevel < 1){ + removeItemFromCurrentInv(item); + player.p.activeItem = &noItem; + } + } + return 0; + } + + switch(tile){ + case TILE_TREE: + if(item->id == TOOL_AXE && playerUseEnergy(4-item->countLevel)){ + playerHurtTile(tile, x, y, (rand()%10) + (item->countLevel) * 5 + 10, player.p.dir); + return 1; + } break; + case TILE_ROCK: + if(item->id == TOOL_PICKAXE && playerUseEnergy(4-item->countLevel)){ + playerHurtTile(tile, x, y, (rand()%10) + (item->countLevel) * 5 + 10, player.p.dir); + return 1; + } break; + case TILE_HARDROCK: + if(item->id == TOOL_PICKAXE && playerUseEnergy(4-item->countLevel)){ + playerHurtTile(tile, x, y, (rand()%10) + (item->countLevel) * 5 + 10, player.p.dir); + return 1; + } break; + case TILE_IRONORE: + if(item->id == TOOL_PICKAXE && playerUseEnergy(4-item->countLevel)){ + playerHurtTile(tile, x, y, 1, player.p.dir); + return 1; + } break; + case TILE_GOLDORE: + if(item->id == TOOL_PICKAXE && playerUseEnergy(4-item->countLevel)){ + playerHurtTile(tile, x, y, 1, player.p.dir); + return 1; + } break; + case TILE_GEMORE: + if(item->id == TOOL_PICKAXE && playerUseEnergy(4-item->countLevel)){ + playerHurtTile(tile, x, y, 1, player.p.dir); + return 1; + } break; + case TILE_CLOUDCACTUS: + if(item->id == TOOL_PICKAXE && playerUseEnergy(4-item->countLevel)){ + playerHurtTile(tile, x, y, 1, player.p.dir); + return 1; + } break; + case TILE_GRASS: + if(item->id == TOOL_HOE && playerUseEnergy(4-item->countLevel)){ + setTile(TILE_FARM,x,y); + return 1; + } + else if(item->id == ITEM_ACORN){ + setTile(TILE_SAPLING_TREE,x,y); --item->countLevel; + return 1; + } + else if(item->id == ITEM_FLOWER){ + setTile(TILE_FLOWER,x,y); --item->countLevel; + setData(rand()%4,x,y); // determines mirroring. + 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); + return 1; + } break; + case TILE_SAND: + if(item->id == ITEM_CACTUS){ + setTile(TILE_SAPLING_CACTUS,x,y); + --item->countLevel; + return 1; + } + else if(item->id == TOOL_SHOVEL && playerUseEnergy(4-item->countLevel)){ + addEntityToList(newItemEntity(newItem(ITEM_SAND,1), (x<<4)+8, (y<<4)+8, currentLevel), &eManager); + setTile(TILE_DIRT,x,y); + return 1; + } break; + case TILE_DIRT: + if(item->id == TOOL_HOE && playerUseEnergy(4-item->countLevel)){ + setTile(TILE_FARM,x,y); + return 1; + } + else if(item->id == TOOL_SHOVEL && playerUseEnergy(4-item->countLevel)){ + addEntityToList(newItemEntity(newItem(ITEM_DIRT,1), (x<<4)+8, (y<<4)+8, currentLevel), &eManager); + setTile(TILE_HOLE,x,y); + return 1; + } break; + case TILE_HOLE: + case TILE_WATER: + case TILE_LAVA: + if(item->id == ITEM_DIRT){ + setTile(TILE_DIRT,x,y); + --item->countLevel; + return 1; + } break; + case TILE_NULL: + if(item->id == ITEM_CLOUD){ + setTile(TILE_CLOUD,x,y); + --item->countLevel; + return 1; + } break; + case TILE_FARM: + if(item->id == ITEM_SEEDS){ + setTile(TILE_WHEAT,x,y); + setData(0,x,y); + --item->countLevel; + return 1; + } break; + case TILE_WHEAT: + if(item->id == TOOL_HOE){ + if(getData(x,y) > -1){ + int age = getData(x,y); + int count = (rand() % 2); + if(age >= 80) count = (rand()%2) + 1; + addItemsToWorld(newItem(ITEM_SEEDS,1),(x<<4)+8,(y<<4)+8,count); + count = 0; + if(age == 100)count = (rand()%3) + 2; + else if(age >= 80)count = (rand()%2) + 1; + addItemsToWorld(newItem(ITEM_WHEAT,1),(x<<4)+8,(y<<4)+8,count); + setTile(TILE_DIRT,x,y); + } + } break; + } + return 0; +} + +void tickTile(int x, int y){ + int tile = getTile(x,y); + int data = getData(x,y); + + switch(tile){ + case TILE_SAPLING_TREE: + setData(++data,x,y); if(data>100){setData(0,x,y); setTile(TILE_TREE,x,y);} + break; + case TILE_SAPLING_CACTUS: + 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); + 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); + break; + case TILE_LAVA: + if(getTile(x+1,y)==TILE_HOLE) setTile(TILE_LAVA,x+1,y); + if(getTile(x-1,y)==TILE_HOLE) setTile(TILE_LAVA,x-1,y); + if(getTile(x,y+1)==TILE_HOLE) setTile(TILE_LAVA,x,y+1); + if(getTile(x,y-1)==TILE_HOLE) setTile(TILE_LAVA,x,y-1); + break; + case TILE_HOLE: // This makes water flow slightly faster than lava + if(getTile(x+1,y)==TILE_WATER) setTile(TILE_WATER,x,y); + if(getTile(x-1,y)==TILE_WATER) setTile(TILE_WATER,x,y); + if(getTile(x,y+1)==TILE_WATER) setTile(TILE_WATER,x,y); + if(getTile(x,y-1)==TILE_WATER) setTile(TILE_WATER,x,y); + break; + case TILE_GRASS: + if(getTile(x+1,y)==TILE_DIRT) if((rand()%25) == 0) setTile(TILE_GRASS,x+1,y); + if(getTile(x-1,y)==TILE_DIRT) if((rand()%25) == 0) setTile(TILE_GRASS,x-1,y); + if(getTile(x,y+1)==TILE_DIRT) if((rand()%25) == 0) setTile(TILE_GRASS,x,y+1); + if(getTile(x,y-1)==TILE_DIRT) if((rand()%25) == 0) setTile(TILE_GRASS,x,y-1); + break; + } + +} + +void tickEntityItem(Entity* e){ + ++e->entityItem.age; + if(e->entityItem.age == 630){ + removeEntityFromList(e,e->level,&eManager); + /* + Programming pro tip: + Remember to put a return statement after you remove the entity, + or else your going to have a very bad time like I did. + */ + return; + } + e->entityItem.xx += e->entityItem.xa; + e->entityItem.yy += e->entityItem.ya; + e->entityItem.zz += e->entityItem.za; + if (e->entityItem.zz < 0) { + e->entityItem.zz = 0; + e->entityItem.za *= -0.5; + e->entityItem.xa *= 0.6; + e->entityItem.ya *= 0.6; + } + e->entityItem.za -= 0.15; + int ox = e->x; + int oy = e->y; + int nx = (int) e->entityItem.xx; + int ny = (int) e->entityItem.yy; + int expectedx = nx - e->x; + int expectedy = ny - e->y; + move(e, nx - e->x, ny - e->y); + int gotx = e->x - ox; + int goty = e->y - oy; + e->entityItem.xx += gotx - expectedx; + e->entityItem.yy += goty - expectedy; +} + +void tickEntityTextParticle(Entity* e){ + ++e->textParticle.age; + if(e->textParticle.age == 60){ + removeEntityFromList(e,e->level,&eManager); + return; + } + e->textParticle.xx += e->textParticle.xa; + e->textParticle.yy += e->textParticle.ya; + e->textParticle.zz += e->textParticle.za; + if (e->textParticle.zz < 0) { + e->textParticle.zz = 0; + e->textParticle.za *= -0.5; + e->textParticle.xa *= 0.6; + e->textParticle.ya *= 0.6; + } + e->textParticle.za -= 0.15; + e->x = (int) e->textParticle.xx; + e->y = (int) e->textParticle.yy; +} + +void tickEntity(Entity* e){ + switch(e->type){ + case ENTITY_ITEM: tickEntityItem(e); return; + case ENTITY_FURNITURE: return; + case ENTITY_ZOMBIE: + if (e->hurtTime > 0) e->hurtTime--; + if (e->zombie.randWalkTime == 0) { + int xd = player.x - e->x; + int yd = player.y - e->y; + if (xd * xd + yd * yd < 50 * 50) { + e->zombie.xa = 0; + e->zombie.ya = 0; + if (xd < 0) e->zombie.xa = -1; + if (xd > 0) e->zombie.xa = +1; + if (yd < 0) e->zombie.ya = -1; + if (yd > 0) e->zombie.ya = +1; + } + } + + if(e->zombie.xa < 0) e->zombie.dir = 2; + else if(e->zombie.xa > 0) e->zombie.dir = 3; + if(e->zombie.ya < 0) e->zombie.dir = 1; + else if(e->zombie.ya > 0) e->zombie.dir = 0; + + if(e->zombie.xa != 0 || e->zombie.ya != 0) e->zombie.walkDist++; + + int speed = tickCount & 1; + if (!moveMob(e, e->zombie.xa * speed, e->zombie.ya * speed) || (rand()%100) == 0) { + e->zombie.randWalkTime = 60; + e->zombie.xa = ((rand()%3) - 1) * (rand()%2); + e->zombie.ya = ((rand()%3) - 1) * (rand()%2); + } + if (e->zombie.randWalkTime > 0) e->zombie.randWalkTime--; + return; + case ENTITY_SLIME: + if (e->hurtTime > 0) e->hurtTime--; + + if (!moveMob(e, e->slime.xa, e->slime.ya) || (rand()%10) == 0) { + if (e->slime.jumpTime <= -10) { + e->slime.xa = ((rand()%3) - 1); + e->slime.ya = ((rand()%3) - 1); + + int xd = player.x - e->x; + int yd = player.y - e->y; + if (xd * xd + yd * yd < 50 * 50) { + if (xd < 0) e->slime.xa = -1; + if (xd > 0) e->slime.xa = +1; + if (yd < 0) e->slime.ya = -1; + if (yd > 0) e->slime.ya = +1; + } + + if (e->slime.xa != 0 || e->slime.ya != 0) e->slime.jumpTime = 10; + } + } + + if(e->slime.xa < 0) e->slime.dir = 2; + else if(e->slime.xa > 0) e->slime.dir = 3; + if(e->slime.ya < 0) e->slime.dir = 1; + else if(e->slime.ya > 0) e->slime.dir = 0; + + if (e->slime.jumpTime > -10) e->slime.jumpTime--; + if(e->slime.jumpTime == 0){ + e->slime.xa = 0; + e->slime.ya = 0; + } + return; + case ENTITY_AIRWIZARD: + if (e->hurtTime > 0) e->hurtTime--; + + if (e->wizard.attackDelay > 0) { + e->wizard.dir = (e->wizard.attackDelay - 45) / 4 % 4; + e->wizard.dir = (e->wizard.dir * 2 % 4) + (e->wizard.dir / 2); + if (e->wizard.attackDelay < 45) e->wizard.dir = 0; + e->wizard.attackDelay--; + if (e->wizard.attackDelay <= 0) { + e->wizard.attackType = 0; + if (e->wizard.health < 1000) e->wizard.attackType = 1; + if (e->wizard.health < 200) e->wizard.attackType = 2; + e->wizard.attackTime = 120; + } + return; + } + + if (e->wizard.attackTime > 0) { + e->wizard.attackTime--; + float dir = e->wizard.attackTime * 0.25 * (e->wizard.attackTime % 2 * 2 - 1); + float speed = (0.7) + (e->wizard.attackType+1) * 0.2; + addEntityToList(newSparkEntity(e, cos(dir) * speed, sin(dir) * speed), &eManager); + return; + } + + if (e->wizard.randWalkTime == 0) { + int xd = player.x - e->x; + int yd = player.y - e->y; + int dist = xd * xd + yd * yd; + if (dist > 80 * 80) { + e->wizard.xa = 0; + e->wizard.ya = 0; + if (xd < 0) e->wizard.xa = -1; + if (xd > 0) e->wizard.xa = +1; + if (yd < 0) e->wizard.ya = -1; + if (yd > 0) e->wizard.ya = +1; + } else if (dist < 24 * 24) { + e->wizard.xa = 0; + e->wizard.ya = 0; + if (xd < 0) e->wizard.xa = +1; + if (xd > 0) e->wizard.xa = -1; + if (yd < 0) e->wizard.ya = +1; + if (yd > 0) e->wizard.ya = -1; + } + } + + int wSpeed = (tickCount % 4) == 0 ? 0 : 1; + if (!moveMob(e, e->wizard.xa * wSpeed, e->wizard.ya * wSpeed) || (rand()%100) == 0) { + e->wizard.randWalkTime = 30; + e->wizard.xa = ((rand()%3) - 1) * (rand()%2); + e->wizard.ya = ((rand()%3) - 1) * (rand()%2); + } + + if(e->wizard.xa != 0 || e->wizard.ya != 0) e->wizard.walkDist++; + + if(e->wizard.xa < 0) e->wizard.dir = 2; + else if(e->wizard.xa > 0) e->wizard.dir = 3; + if(e->wizard.ya < 0) e->wizard.dir = 1; + else if(e->wizard.ya > 0) e->wizard.dir = 0; + + if (e->wizard.randWalkTime > 0) { + e->wizard.randWalkTime--; + if (e->wizard.randWalkTime == 0) { + int xd = player.x - e->x; + int yd = player.y - e->y; + if (rand()%4 == 0 && xd * xd + yd * yd < 50 * 50) { + if (e->wizard.attackDelay == 0 && e->wizard.attackTime == 0) e->wizard.attackDelay = 120; + } + } + } + + return; + case ENTITY_SPARK: + e->spark.age++; + if (e->spark.age >= 260) { + removeEntityFromList(e,e->level,&eManager); + return; + } + e->spark.xx += e->spark.xa; + e->spark.yy += e->spark.ya; + e->x = (int) e->spark.xx; + e->y = (int) e->spark.yy; + + if(intersects(player, e->x + e->spark.xa - e->xr, e->y + e->spark.ya - e->yr, e->x + e->spark.xa + e->xr, e->y + e->spark.ya + e->yr)){ + EntityVsEntity(e, &player); + removeEntityFromList(e,e->level,&eManager); + } + return; + case ENTITY_TEXTPARTICLE: tickEntityTextParticle(e); return; + case ENTITY_SMASHPARTICLE: + ++e->smashParticle.age; + if(e->smashParticle.age > 10) removeEntityFromList(e,e->level,&eManager); + return; + } +} + +void trySpawn(int count, int level) { + int i; + for (i = 0; i < count; i++) { + if(eManager.lastSlot[level] > 900) continue; + Entity e; + + int minLevel = 1; + int maxLevel = 1; + if (level > 0) maxLevel = level; + else if (level == 0) { + minLevel = maxLevel = 4; + } + + int rx = rand()%128; + int ry = rand()%128; + int ex = (rx<<4)+8; + int ey = (ry<<4)+8; + + if(level == currentLevel && (ex > player.x-160 && ey > player.y-125 && ex < player.x+160 && ey < player.y+125)) continue; + + if (!tileIsSolid(map[level][rx+ry*128],&e)) { + int lvl = (rand()%(maxLevel - minLevel + 1)) + minLevel; + if ((rand()%2) == 0) e = newSlimeEntity(lvl, ex, ey, level); + else e = newZombieEntity(lvl, ex, ey, level); + addEntityToList(e, &eManager); + } + } +} + +int getTile(int x, int y){ + if(x < 0 || y < 0 || x > 128 || y > 128) return -1; + return map[currentLevel][x+y*128]; +} + +void setTile(int id, int x, int y){ + if(x < 0 || y < 0 || x > 128 || y > 128) return; + map[currentLevel][x+y*128] = id; + sf2d_set_pixel(minimap[currentLevel], x, y, getTileColor(id)); +} +int getData(int x, int y){ + if(x < 0 || y < 0 || x > 128 || y > 128) return -1; + return data[currentLevel][x+y*128]; +} + +void setData(int id, int x, int y){ + if(x < 0 || y < 0 || x > 128 || y > 128) return; + data[currentLevel][x+y*128] = id; +} + +void spawnPlayer(){ + while(true){ + int rx = rand()%128; + int ry = rand()%128; + if(getTile(rx,ry) == TILE_GRASS){ + player.x = (rx << 4) + 8; + player.y = (ry << 4) + 8; + break; + } + } + +} + +void initPlayer(){ + player.type = ENTITY_PLAYER; + spawnPlayer(); + player.xr = 4; + player.yr = 3; + player.canSwim = true; + player.p.ax = 0; + player.p.ay = 0; + player.p.health = 10; + player.p.stamina = 10; + player.p.score = 0; + player.p.walkDist = 0; + player.p.attackTimer = 0; + player.p.dir = 0; + player.p.inv = &eManager.invs[0]; + eManager.nextInv++; + player.p.inv->lastSlot = 0; + player.p.activeItem = &noItem; + player.p.isDead = false; + player.p.hasWon = false; + + addItemToInventory(newItem(ITEM_WORKBENCH,0), player.p.inv); + addItemToInventory(newItem(ITEM_POWGLOVE,0), player.p.inv); + + /* + addItemToInventory(newItem(TOOL_SHOVEL,4), player.p.inv); + addItemToInventory(newItem(TOOL_HOE,4), player.p.inv); + addItemToInventory(newItem(TOOL_SWORD,4), player.p.inv); + addItemToInventory(newItem(TOOL_PICKAXE,4), player.p.inv); + addItemToInventory(newItem(TOOL_AXE,4), player.p.inv); + + addItemToInventory(newItem(ITEM_ANVIL,0), player.p.inv); + addItemToInventory(newItem(ITEM_CHEST,0), player.p.inv); + addItemToInventory(newItem(ITEM_OVEN,0), player.p.inv); + addItemToInventory(newItem(ITEM_FURNACE,0), player.p.inv); + addItemToInventory(newItem(ITEM_LANTERN,0), player.p.inv); + + int i; + for (i = 7;i < 28;++i) addItemToInventory(newItem(i,50), player.p.inv); + //*/ +} + +void playerHurtTile(int tile, int xt, int yt, int damage, int dir){ + char hurtText[11]; + switch(tile){ + case TILE_TREE: + if(rand()%120==0)addEntityToList(newItemEntity(newItem(ITEM_APPLE,1), (xt<<4)+8,(yt<<4)+8, currentLevel), &eManager); + sprintf(hurtText, "%d", damage); + addEntityToList(newTextParticleEntity(hurtText,0xFF0000FF,xt<<4,yt<<4,currentLevel), &eManager); + addEntityToList(newSmashParticleEntity(xt<<4,yt<<4,currentLevel), &eManager); + setData(getData(xt,yt)+damage,xt,yt); + if(getData(xt,yt) > 20){ + setTile(TILE_GRASS,xt,yt); + addItemsToWorld(newItem(ITEM_WOOD,1),(xt<<4)+8,(yt<<4)+8,rand()%2+1); + addItemsToWorld(newItem(ITEM_ACORN,1),(xt<<4)+8,(yt<<4)+8,rand()%2); + } + break; + case TILE_CACTUS: + sprintf(hurtText, "%d", damage); + addEntityToList(newTextParticleEntity(hurtText,0xFF0000FF,xt<<4,yt<<4,currentLevel), &eManager); + addEntityToList(newSmashParticleEntity(xt<<4,yt<<4,currentLevel), &eManager); + setData(getData(xt,yt)+damage,xt,yt); + if(getData(xt,yt) > 10){ + setTile(TILE_SAND,xt,yt); + addItemsToWorld(newItem(ITEM_CACTUS,1),(xt<<4)+8,(yt<<4)+8,rand()%2+1); + } + break; + case TILE_ROCK: + sprintf(hurtText, "%d", damage); + addEntityToList(newTextParticleEntity(hurtText,0xFF0000FF,xt<<4,yt<<4,currentLevel), &eManager); + addEntityToList(newSmashParticleEntity(xt<<4,yt<<4,currentLevel), &eManager); + setData(getData(xt,yt)+damage,xt,yt); + if(getData(xt,yt) > 50){ + setTile(TILE_DIRT,xt,yt); + addItemsToWorld(newItem(ITEM_STONE,1),(xt<<4)+8,(yt<<4)+8,rand()%4+1); + addItemsToWorld(newItem(ITEM_COAL,1),(xt<<4)+8,(yt<<4)+8,rand()%2); + } + break; + case TILE_HARDROCK: + if(player.p.activeItem->id != TOOL_PICKAXE || player.p.activeItem->countLevel < 4) damage = 0; + sprintf(hurtText, "%d", damage); + addEntityToList(newTextParticleEntity(hurtText,0xFF0000FF,xt<<4,yt<<4,currentLevel), &eManager); + addEntityToList(newSmashParticleEntity(xt<<4,yt<<4,currentLevel), &eManager); + setData(getData(xt,yt)+damage,xt,yt); + if(getData(xt,yt) > 200){ + setTile(TILE_DIRT,xt,yt); + addItemsToWorld(newItem(ITEM_STONE,1),(xt<<4)+8,(yt<<4)+8,rand()%4+1); + addItemsToWorld(newItem(ITEM_COAL,1),(xt<<4)+8,(yt<<4)+8,rand()%2); + } + break; + case TILE_IRONORE: + if(player.p.activeItem->id != TOOL_PICKAXE) damage = 0; + sprintf(hurtText, "%d", damage); + addEntityToList(newTextParticleEntity(hurtText,0xFF0000FF,xt<<4,yt<<4,currentLevel), &eManager); + addEntityToList(newSmashParticleEntity(xt<<4,yt<<4,currentLevel), &eManager); + setData(getData(xt,yt)+damage,xt,yt); + if(getData(xt,yt) > 0){ + int count = rand() & 1; + if (getData(xt,yt) >= (rand()%10) + 3) { + setTile(TILE_DIRT,xt,yt); + count += 2; + } + addItemsToWorld(newItem(ITEM_IRONORE,1),(xt<<4)+8,(yt<<4)+8,count); + } break; + case TILE_GOLDORE: + if(player.p.activeItem->id != TOOL_PICKAXE) damage = 0; + sprintf(hurtText, "%d", damage); + addEntityToList(newTextParticleEntity(hurtText,0xFF0000FF,xt<<4,yt<<4,currentLevel), &eManager); + addEntityToList(newSmashParticleEntity(xt<<4,yt<<4,currentLevel), &eManager); + setData(getData(xt,yt)+damage,xt,yt); + if(getData(xt,yt) > 0){ + int count = rand() & 1; + if (getData(xt,yt) >= (rand()%10) + 3) { + setTile(TILE_DIRT,xt,yt); + count += 2; + } + addItemsToWorld(newItem(ITEM_GOLDORE,1),(xt<<4)+8,(yt<<4)+8,count); + } break; + case TILE_GEMORE: + if(player.p.activeItem->id != TOOL_PICKAXE) damage = 0; + sprintf(hurtText, "%d", damage); + addEntityToList(newTextParticleEntity(hurtText,0xFF0000FF,xt<<4,yt<<4,currentLevel), &eManager); + addEntityToList(newSmashParticleEntity(xt<<4,yt<<4,currentLevel), &eManager); + setData(getData(xt,yt)+damage,xt,yt); + if(getData(xt,yt) > 0){ + int count = rand() & 1; + if (getData(xt,yt) >= (rand()%10) + 3) { + setTile(TILE_DIRT,xt,yt); + count += 2; + } + addItemsToWorld(newItem(ITEM_GEM,1),(xt<<4)+8,(yt<<4)+8,count); + } break; + case TILE_CLOUDCACTUS: + if(player.p.activeItem->id != TOOL_PICKAXE) damage = 0; + sprintf(hurtText, "%d", damage); + addEntityToList(newTextParticleEntity(hurtText,0xFF0000FF,xt<<4,yt<<4,currentLevel), &eManager); + addEntityToList(newSmashParticleEntity(xt<<4,yt<<4,currentLevel), &eManager); + setData(getData(xt,yt)+damage,xt,yt); + if(getData(xt,yt) > 0){ + int count = rand() % 3; + if (getData(xt,yt) >= (rand()%10) + 3) { + setTile(TILE_CLOUD,xt,yt); + count += 3; + } + addItemsToWorld(newItem(ITEM_CLOUD,1),(xt<<4)+8,(yt<<4)+8,count); + } break; + case TILE_FARM: + setTile(TILE_DIRT,xt,yt); + break; + case TILE_SAPLING_TREE: + setTile(TILE_GRASS,xt,yt); + break; + case TILE_SAPLING_CACTUS: + setTile(TILE_SAND,xt,yt); + break; + case TILE_WHEAT: + if(getData(xt,yt) > -1){ + int age = getData(xt,yt); + int count = (rand() % 2); + if(age >= 80) count = (rand()%2) + 1; + addItemsToWorld(newItem(ITEM_SEEDS,1),(xt<<4)+8,(yt<<4)+8,count); + count = 0; + if(age == 100)count = (rand()%3) + 2; + else if(age >= 80)count = (rand()%2) + 1; + addItemsToWorld(newItem(ITEM_WHEAT,1),(xt<<4)+8,(yt<<4)+8,count); + setTile(TILE_DIRT,xt,yt); + } break; + case TILE_FLOWER: + setTile(TILE_GRASS,xt,yt); + addEntityToList(newItemEntity(newItem(ITEM_FLOWER,1), (xt<<4)+8,(yt<<4)+8, currentLevel), &eManager); + break; + } + +} + +bool playerUseEnergy(int amount){ + if(amount > player.p.stamina) return false; + player.p.stamina -= amount; + return true; +} + +void playerAttack(){ + bool done = false; + player.p.attackTimer = 5; + int yo = -2; + int range = 12; + + switch(player.p.dir){ + case 0: if(interact(player.x - 8, player.y + 4 + yo, player.x + 8, player.y + range + yo)) return; break; + case 1: if(interact(player.x - 8, player.y - range + yo, player.x + 8, player.y - 4 + yo)) return; break; + case 2: if(interact(player.x - range, player.y - 8 + yo, player.x - 4, player.y + 8 + yo)) return; break; + case 3: if(interact(player.x + 4, player.y - 8 + yo, player.x + range, player.y + 8 + yo)) return; break; + } + + int xt = player.x >> 4; + int yt = (player.y + yo) >> 4; + int r = 12; + if (player.p.dir == 0) yt = (player.y + r + yo) >> 4; + if (player.p.dir == 1) yt = (player.y - r + yo) >> 4; + if (player.p.dir == 2) xt = (player.x - r) >> 4; + if (player.p.dir == 3) xt = (player.x + r) >> 4; + + if (xt >= 0 && yt >= 0 && xt < 128 && yt < 128) { + s8 itract = itemTileInteract(getTile(xt,yt),player.p.activeItem,xt,yt,player.x,player.y,player.p.dir); + if(itract > 0){ + if(itract==2)player.p.isCarrying = false; + done = true; + } + + if (isItemEmpty(player.p.activeItem)) { + removeItemFromInventory(player.p.activeItem->slotNum, player.p.inv); + player.p.activeItem = &noItem; + } + } + + if(done) return; + + if (player.p.activeItem == &noItem || player.p.activeItem->id == TOOL_SWORD || player.p.activeItem->id == TOOL_AXE) { + xt = player.x >> 4; + yt = (player.y + yo) >> 4; + r = 12; + if (player.p.dir == 0) yt = (player.y + r + yo) >> 4; + if (player.p.dir == 1) yt = (player.y - r + yo) >> 4; + if (player.p.dir == 2) xt = (player.x - r) >> 4; + if (player.p.dir == 3) xt = (player.x + r) >> 4; + + if (xt >= 0 && yt >= 0 && xt < 128 && 128) { + playerHurtTile(getTile(xt,yt), xt, yt, (rand()%3) + 1, player.p.dir); + } + } +} + + +void switchLevel(s8 change){ + currentLevel+=change; + if(currentLevel > 4) currentLevel = 0; else if(currentLevel < 0) currentLevel = 4; + if(currentLevel == 1) sf2d_set_clear_color(RGBA8(0x82, 0x6D, 0x6C, 0xFF)); + else if(currentLevel > 1) sf2d_set_clear_color(RGBA8(0x66, 0x66, 0x66, 0xFF)); + else sf2d_set_clear_color(RGBA8(0x00, 0x1D, 0xC1, 0xFF)); +} + +bool playerIntersectsEntity(Entity* e){ + return (player.x < e->x + e->xr && player.x + 4 > e->x && player.y < e->y + e->yr && player.y + 4 > e->y); +} + +void playerEntityInteract(Entity* e){ + switch(e->type){ + case ENTITY_ITEM: + if(e->entityItem.age > 30){//30 + addItemToInventory(e->entityItem.item, player.p.inv); + removeEntityFromList(e,currentLevel,&eManager); + playSound(snd_pickup); + player.p.score++; + } + break; + case ENTITY_FURNITURE: + switch(player.p.dir){ + case 0: if(player.y < e->y) move(e,0,2); break; + case 1: if(player.y > e->y) move(e,0,-2); break; + case 2: if(player.x > e->x) move(e,-2,0); break; + case 3: if(player.x < e->x) move(e,2,0); break; + } + break; + + } + +} + +void entityTileInteract(Entity*e, int tile, int x, int y){ + switch(tile){ + case TILE_STAIRS_DOWN: + if(e->type == ENTITY_PLAYER){ + switchLevel(1); + player.x = (x << 4) + 8; + player.y = (y << 4) + 8; + } + return; + case TILE_STAIRS_UP: + if(e->type == ENTITY_PLAYER){ + switchLevel(-1); + player.x = (x << 4) + 8; + player.y = (y << 4) + 8; + } + return; + case TILE_CACTUS: if(e->type == ENTITY_PLAYER)hurtEntity(e,1,-1,0xFF00AFFF); return; + case TILE_LAVA: if(e->type == ENTITY_PLAYER)hurtEntity(e,1,-1,0xFF00AFFF); return; + case TILE_WHEAT: + if(e->type == ENTITY_PLAYER || e->type == ENTITY_ZOMBIE){ + if(getData(x,y) > -1 && rand()%20 == 0){ + int age = getData(x,y); + int count = (rand() % 2); + if(age >= 80) count = (rand()%2) + 1; + addItemsToWorld(newItem(ITEM_SEEDS,1),(x<<4)+8,(y<<4)+8,count); + count = 0; + if(age == 100)count = (rand()%3) + 2; + else if(age >= 80)count = (rand()%2) + 1; + addItemsToWorld(newItem(ITEM_WHEAT,1),(x<<4)+8,(y<<4)+8,count); + setTile(TILE_DIRT,x,y); + } + } + return; + case TILE_FARM: + if(e->type == ENTITY_PLAYER || e->type == ENTITY_ZOMBIE){ + if(rand()%20 == 0)setTile(TILE_DIRT,x,y); + } + return; + } +} + +bool intersectsEntity(int x, int y, int r, Entity* e){ + return (x < e->x + e->xr && x + r > e->x && y < e->y + e->yr && y + r > e->y); +} + +bool isPlayerInsideEntity(int x, int y){ + int i; + for(i = 0; i < eManager.lastSlot[currentLevel];++i){ + Entity e = eManager.entities[currentLevel][i]; + if(!e.canPass && intersectsEntity(x-16,y-16,16,&e)){ + playerEntityInteract(&eManager.entities[currentLevel][i]); + return true; + } + } + return false; +} + +bool useEntity(Entity* e) { + if(e->type == ENTITY_FURNITURE){ + switch(e->entityFurniture.itemID){ + case ITEM_WORKBENCH: + currentRecipes = &workbenchRecipes; + currentMenu = MENU_CRAFTING; + checkCanCraftRecipes(currentRecipes, player.p.inv); + sortRecipes(currentRecipes); + return true; + case ITEM_FURNACE: + currentRecipes = &furnaceRecipes; + currentMenu = MENU_CRAFTING; + checkCanCraftRecipes(currentRecipes, player.p.inv); + sortRecipes(currentRecipes); + return true; + case ITEM_OVEN: + currentRecipes = &ovenRecipes; + currentMenu = MENU_CRAFTING; + checkCanCraftRecipes(currentRecipes, player.p.inv); + sortRecipes(currentRecipes); + return true; + case ITEM_ANVIL: + currentRecipes = &anvilRecipes; + currentMenu = MENU_CRAFTING; + checkCanCraftRecipes(currentRecipes, player.p.inv); + sortRecipes(currentRecipes); + return true; + case ITEM_CHEST: + curChestEntity = e; + curInvSel = 0; + curChestEntity->entityFurniture.r = 0; + curChestEntity->entityFurniture.oSel = 0; + currentMenu = MENU_CONTAINER; + return true; + } + } + return false; +} + +bool use(int x0, int y0, int x1, int y1) { + Entity * entities[eManager.lastSlot[currentLevel]]; + int i; + int ae = getEntities(entities, x0, y0, x1, y1); + for(i = 0; i < ae; ++i){ + if(useEntity(entities[i])) return true; + } + return false; +} + +bool playerUse() { + int yo = -2; + if (player.p.dir == 0 && use(player.x - 8, player.y + 4 + yo, player.x + 8, player.y + 12 + yo)) return true; + if (player.p.dir == 1 && use(player.x - 8, player.y - 12 + yo, player.x + 8, player.y - 4 + yo)) return true; + if (player.p.dir == 3 && use(player.x + 4, player.y - 8 + yo, player.x + 12, player.y + 8 + yo)) return true; + if (player.p.dir == 2 && use(player.x - 12, player.y - 8 + yo, player.x - 4, player.y + 8 + yo)) return true; + return false; +} + +void tickPlayer(){ + if (player.hurtTime > 0) player.hurtTime--; + bool swimming = isSwimming(); + if (player.p.stamina <= 0 && player.p.staminaRechargeDelay == 0 && player.p.staminaRecharge == 0) { + player.p.staminaRechargeDelay = 40; + } + + if (player.p.staminaRechargeDelay > 0) { + --player.p.staminaRechargeDelay; + } + + if (player.p.staminaRechargeDelay == 0) { + ++player.p.staminaRecharge; + if (swimming) player.p.staminaRecharge = 0; + + while (player.p.staminaRecharge > 10) { + player.p.staminaRecharge -= 10; + if (player.p.stamina < 10) ++player.p.stamina; + } + } + + player.p.ax = 0; + player.p.ay = 0; + + if (k_left.down){ + player.p.ax -= 1; + player.p.dir = 2; + ++player.p.walkDist; + } + if (k_right.down){ + player.p.ax += 1; + player.p.dir = 3; + ++player.p.walkDist; + } + if (k_up.down){ + player.p.ay -= 1; + player.p.dir = 1; + ++player.p.walkDist; + } + if (k_down.down){ + player.p.ay += 1; + player.p.dir = 0; + ++player.p.walkDist; + } + if (player.p.staminaRechargeDelay % 2 == 0) moveMob(&player, player.p.ax, player.p.ay); + + + if (swimming && player.p.swimTimer % 60 == 0) { + if (player.p.stamina > 0) { + --player.p.stamina; + } else { + hurtEntity(&player,1,-1,0xFF00AFFF); + } + } + + if (k_pause.clicked){ + currentSelection = 0; + currentMenu = MENU_PAUSED; + } + + if(k_attack.clicked){ + if (player.p.stamina != 0) { + player.p.stamina--; + player.p.staminaRecharge = 0; + playerAttack(); + //addEntityToList(newSlimeEntity(1,200,600,1), &eManager); + } + } + + if (k_menu.clicked){ + curInvSel = 0; + if(!playerUse()) currentMenu = MENU_INVENTORY; + } + + if(isSwimming()) ++player.p.swimTimer; + if(player.p.attackTimer > 0) --player.p.attackTimer; +} + +bool isSwimming(){ + return getTile(player.x>>4,player.y>>4)==TILE_WATER; +} + diff --git a/source/Globals.h b/source/Globals.h new file mode 100644 index 0000000..36a6cc6 --- /dev/null +++ b/source/Globals.h @@ -0,0 +1,100 @@ +#pragma once +#include <3ds.h> +#include "SaveLoad.h" +#include "Input.h" + +#define CIRCLEPAD 0xF0000000 +#define CSTICK 0x0F000000 + +#define MENU_NONE 0 +#define MENU_TITLE 1 +#define MENU_TUTORIAL 2 +#define MENU_ABOUT 3 +#define MENU_SETTINGS 4 +#define MENU_INVENTORY 5 +#define MENU_CRAFTING 6 +#define MENU_CONTAINER 7 +#define MENU_WIN 8 +#define MENU_LOSE 9 +#define MENU_PAUSED 10 +#define MENU_LOADGAME 11 + +#define TILE_NULL 255 +#define TILE_GRASS 0 +#define TILE_TREE 1 +#define TILE_ROCK 2 +#define TILE_DIRT 3 +#define TILE_SAND 4 +#define TILE_WATER 5 +#define TILE_LAVA 6 +#define TILE_CACTUS 7 +#define TILE_FLOWER 8 +#define TILE_IRONORE 9 +#define TILE_GOLDORE 10 +#define TILE_GEMORE 11 +#define TILE_FARM 12 +#define TILE_WHEAT 13 +#define TILE_SAPLING_TREE 14 +#define TILE_SAPLING_CACTUS 15 +#define TILE_STAIRS_DOWN 16 +#define TILE_STAIRS_UP 17 +#define TILE_CLOUD 18 +#define TILE_HARDROCK 19 +#define TILE_CLOUDCACTUS 20 +#define TILE_HOLE 21 + +bool screenShot; + +extern char versionText[34]; + +Entity player; +sf2d_texture * lightScreen; + +sf2d_texture * minimap[5]; +u8 map[5][128*128]; +u8 data[5][128*128]; +u8 treeTable[256]; +u16 rockTable[256]; +u16 grassTable[16]; +char currentFileName[256]; +extern u8 currentMenu; +extern char fpsstr[]; +u8 initGame; +Item noItem; +int airWizardHealthDisplay; +u32 tickCount; +RecipeManager* currentRecipes; +Entity* curChestEntity; +s16 curInvSel; +bool quitGame; +s8 currentSelection; + +void tickTile(int x, int y); +bool tileIsSolid(int tile, Entity * e); + +s8 itemTileInteract(int tile, Item* item, int x, int y, int px, int py, int dir); + +void tickEntity(Entity* e); + +void trySpawn(int count, int level); + +int getTile(int x, int y); +u32 getTileColor(int tile); +void setTile(int id, int x, int y); +int getData(int x, int y); +void setData(int id, int x, int y); + +bool intersectsEntity(int x, int y, int r, Entity* e); + +bool EntityBlocksEntity(Entity* e1, Entity* e2); +void EntityVsEntity(Entity* e1, Entity* e2); +void entityTileInteract(Entity* e, int tile,int x, int y); + +void initPlayer(); +void tickPlayer(); +void playerAttack(); +bool isSwimming(); +bool playerUseEnergy(int amount); +void playerHurtTile(int tile, int xt, int yt, int damage, int dir); +bool playerIntersectsEntity(Entity* e); +void playerEntityInteract(Entity* e); diff --git a/source/Input.c b/source/Input.c new file mode 100644 index 0000000..0b4387f --- /dev/null +++ b/source/Input.c @@ -0,0 +1,20 @@ +#include "Input.h" + +void toggleKey(Key* key, bool held, bool down){ + key->down = held; + key->clicked = down; +} + +void tickKeys(u32 held, u32 down){ + toggleKey(&k_up, held & k_up.input, down & k_up.input); + toggleKey(&k_down, held & k_down.input, down & k_down.input); + toggleKey(&k_left, held & k_left.input, down & k_left.input); + toggleKey(&k_right, held & k_right.input, down & k_right.input); + toggleKey(&k_pause, held & k_pause.input, down & k_pause.input); + toggleKey(&k_attack, held & k_attack.input, down & k_attack.input); + toggleKey(&k_menu, held & k_menu.input, down & k_menu.input); + toggleKey(&k_accept, held & k_accept.input, down & k_accept.input); + toggleKey(&k_decline, held & k_decline.input, down & k_decline.input); + toggleKey(&k_delete, held & k_delete.input, down & k_delete.input); +} + diff --git a/source/Input.h b/source/Input.h new file mode 100644 index 0000000..3bd6270 --- /dev/null +++ b/source/Input.h @@ -0,0 +1,21 @@ +#include <3ds.h> + +typedef struct { + bool down, clicked; + int input; +} Key; + +Key k_null; +Key k_up; +Key k_down; +Key k_left; +Key k_right; +Key k_attack; +Key k_menu; +Key k_pause; +Key k_accept; +Key k_decline; +Key k_delete; + +void tickKeys(u32 held,u32 down); +bool clicked(Key key); diff --git a/source/Item.c b/source/Item.c new file mode 100644 index 0000000..a4dc71e --- /dev/null +++ b/source/Item.c @@ -0,0 +1,238 @@ +#include "Item.h" + +char currentName[16]; + +bool isItemEmpty(Item* item){ + if(item->id < 6 || item->onlyOne == true) return false; + if(item->countLevel < 1) return true; + return false; +} + +void pushItemToInventoryFront(Item item, Inventory * inv){ + if(inv->lastSlot < 300) ++inv->lastSlot; + int i; + for(i = inv->lastSlot;i > 0;--i){ + inv->items[i] = inv->items[i-1]; // Move the items up the list. + inv->items[i].slotNum = i; + } + item.invPtr = (int*)inv; + inv->items[0] = item; + inv->items[0].slotNum = 0; + +} + +void addItemToInventory(Item item, Inventory * inv){ + if(!item.onlyOne){ + int i; + for(i = 0;i < inv->lastSlot;++i){ //Search inventory if item already exists. + if(inv->items[i].id == item.id){ + inv->items[i].countLevel+=item.countLevel; + return; + } + } + } + + item.slotNum = inv->lastSlot; + item.invPtr = (int*)inv; + inv->items[inv->lastSlot] = item; + ++inv->lastSlot; +} + +void removeItemFromCurrentInv(Item* item){ + removeItemFromInventory(item->slotNum, (Inventory*)item->invPtr); +} + +Item nullItem; +void removeItemFromInventory(int slot, Inventory * inv){ + int i; + for(i = slot;i < inv->lastSlot - 1;++i){ + inv->items[i] = inv->items[i + 1]; // Move the items down. + inv->items[i].slotNum--; + } + --inv->lastSlot; + inv->items[inv->lastSlot] = nullItem; // Make the last slot null. +} + +Item newItem(int id, int cLevel){ + Item item; + item.id = id; + if(id != ITEM_NULL){ + if(cLevel > 999) cLevel = 999; + item.countLevel = cLevel; + if(id < 7 || id > 27) item.onlyOne = true; // Tools + Furniture. + else item.onlyOne = false; + } + item.chestPtr = NULL; + return item; +} + +Item* getItemFromInventory(int itemID, Inventory * inv){ + int i; + for(i = 0;i < inv->lastSlot;++i){ + if(inv->items[i].id == itemID){ + return &inv->items[i]; + } + } + return (Item*)NULL; +} + +int countItemInv(int itemID, int level, Inventory* inv){ + int i, count = 0; + for(i = 0;i < inv->lastSlot;++i){ + if(inv->items[i].id == itemID){ + if(inv->items[i].onlyOne){ + if(level == inv->items[i].countLevel) count++; + } else count += inv->items[i].countLevel; + } + } + return count; +} + +char* getItemName(int itemID, int countLevel){ + switch(itemID){ + case TOOL_SHOVEL: + switch(countLevel){ + case 1: return "Rock Shovel"; + case 2: return "Iron Shovel"; + case 3: return "Gold Shovel"; + case 4: return "Gem Shovel"; + default: return "Wood Shovel"; + } + case TOOL_HOE: + switch(countLevel){ + case 1: return "Rock Hoe"; + case 2: return "Iron Hoe"; + case 3: return "Gold Hoe"; + case 4: return "Gem Hoe"; + default: return "Wood Hoe"; + } + case TOOL_SWORD: + switch(countLevel){ + case 1: return "Rock Sword"; + case 2: return "Iron Sword"; + case 3: return "Gold Sword"; + case 4: return "Gem Sword"; + default: return "Wood Sword"; + } + case TOOL_PICKAXE: + switch(countLevel){ + case 1: return "Rock Pickaxe"; + case 2: return "Iron Pickaxe"; + case 3: return "Gold Pickaxe"; + case 4: return "Gem Pickaxe"; + default: return "Wood Pickaxe"; + } + case TOOL_AXE: + switch(countLevel){ + case 1: return "Rock Axe"; + case 2: return "Iron Axe"; + case 3: return "Gold Axe"; + case 4: return "Gem Axe"; + default: return "Wood Axe"; + } + case ITEM_ANVIL: return "Anvil"; + case ITEM_CHEST: return "Chest"; + case ITEM_OVEN: return "Oven"; + case ITEM_FURNACE: return "Furnace"; + case ITEM_WORKBENCH: return "Workbench"; + case ITEM_LANTERN: return "Lantern"; + case ITEM_POWGLOVE: return "Power Glove"; + case ITEM_FLOWER: sprintf(currentName,"%d Flower", countLevel); return currentName; + case ITEM_WOOD: sprintf(currentName,"%d Wood", countLevel); return currentName; + case ITEM_STONE: sprintf(currentName,"%d Stone", countLevel); return currentName; + case ITEM_SAND: sprintf(currentName,"%d Sand", countLevel); return currentName; + case ITEM_DIRT: sprintf(currentName,"%d Dirt", countLevel); return currentName; + case ITEM_CLOUD: sprintf(currentName,"%d Cloud", countLevel); return currentName; + case ITEM_ACORN: sprintf(currentName,"%d Acorn", countLevel); return currentName; + case ITEM_CACTUS: sprintf(currentName,"%d Cactus", countLevel); return currentName; + case ITEM_SEEDS: sprintf(currentName,"%d Seeds", countLevel); return currentName; + case ITEM_WHEAT: sprintf(currentName,"%d Wheat", countLevel); return currentName; + case ITEM_FLESH: sprintf(currentName,"%d Flesh", countLevel); return currentName; + case ITEM_BREAD: sprintf(currentName,"%d Bread", countLevel); return currentName; + case ITEM_APPLE: sprintf(currentName,"%d Apple", countLevel); return currentName; + case ITEM_COAL: sprintf(currentName,"%d Coal", countLevel); return currentName; + case ITEM_IRONORE: sprintf(currentName,"%d Iron ore", countLevel); return currentName; + case ITEM_GOLDORE: sprintf(currentName,"%d Gold ore", countLevel); return currentName; + case ITEM_IRONINGOT: sprintf(currentName,"%d Iron ingot", countLevel); return currentName; + case ITEM_GOLDINGOT: sprintf(currentName,"%d Gold ingot", countLevel); return currentName; + case ITEM_GLASS: sprintf(currentName,"%d Glass", countLevel); return currentName; + case ITEM_GEM: sprintf(currentName,"%d Gem", countLevel); return currentName; + case ITEM_SLIME: sprintf(currentName,"%d Slime", countLevel); return currentName; + default: return ""; // null + } +} + +char* getBasicItemName(int itemID, int countLevel){ + switch(itemID){ + case TOOL_SHOVEL: + switch(countLevel){ + case 1: return "Rock Shovel"; + case 2: return "Iron Shovel"; + case 3: return "Gold Shovel"; + case 4: return "Gem Shovel"; + default: return "Wood Shovel"; + } + case TOOL_HOE: + switch(countLevel){ + case 1: return "Rock Hoe"; + case 2: return "Iron Hoe"; + case 3: return "Gold Hoe"; + case 4: return "Gem Hoe"; + default: return "Wood Hoe"; + } + case TOOL_SWORD: + switch(countLevel){ + case 1: return "Rock Sword"; + case 2: return "Iron Sword"; + case 3: return "Gold Sword"; + case 4: return "Gem Sword"; + default: return "Wood Sword"; + } + case TOOL_PICKAXE: + switch(countLevel){ + case 1: return "Rock Pickaxe"; + case 2: return "Iron Pickaxe"; + case 3: return "Gold Pickaxe"; + case 4: return "Gem Pickaxe"; + default: return "Wood Pickaxe"; + } + case TOOL_AXE: + switch(countLevel){ + case 1: return "Rock Axe"; + case 2: return "Iron Axe"; + case 3: return "Gold Axe"; + case 4: return "Gem Axe"; + default: return "Wood Axe"; + } + case ITEM_ANVIL: return "Anvil"; + case ITEM_CHEST: return "Chest"; + case ITEM_OVEN: return "Oven"; + case ITEM_FURNACE: return "Furnace"; + case ITEM_WORKBENCH: return "Workbench"; + case ITEM_LANTERN: return "Lantern"; + case ITEM_POWGLOVE: return "Power Glove"; + case ITEM_FLOWER: return "Flower"; + case ITEM_WOOD: return "Wood"; + case ITEM_STONE: return "Stone"; + case ITEM_SAND: return "Sand"; + case ITEM_DIRT: return "Dirt"; + case ITEM_CLOUD: return "Cloud"; + case ITEM_ACORN: return "Acorn"; + case ITEM_CACTUS: return "Cactus"; + case ITEM_SEEDS: return "Seeds"; + case ITEM_WHEAT: return "Wheat"; + case ITEM_FLESH: return "Flesh"; + case ITEM_BREAD: return "Bread"; + case ITEM_APPLE: return "Apple"; + case ITEM_COAL: return "Coal"; + case ITEM_IRONORE: return "Iron ore"; + case ITEM_GOLDORE: return "Gold ore"; + case ITEM_IRONINGOT: return "Iron ingot"; + case ITEM_GOLDINGOT: return "Gold ingot"; + case ITEM_GLASS: return "Glass"; + case ITEM_GEM: return "Gem"; + case ITEM_SLIME: return "Slime"; + default: return ""; // null + } + +} diff --git a/source/Item.h b/source/Item.h new file mode 100644 index 0000000..428522b --- /dev/null +++ b/source/Item.h @@ -0,0 +1,70 @@ +#pragma once +#include <3ds.h> +#include +#include +#include +#include +#include +#include "Sound.h" + +#define ITEM_NULL 0 +#define TOOL_SHOVEL 1 +#define TOOL_HOE 2 +#define TOOL_SWORD 3 +#define TOOL_PICKAXE 4 +#define TOOL_AXE 5 +#define ITEM_POWGLOVE 6 +#define ITEM_FLOWER 7 +#define ITEM_WOOD 8 +#define ITEM_STONE 9 +#define ITEM_SAND 10 +#define ITEM_DIRT 11 +#define ITEM_CLOUD 12 +#define ITEM_ACORN 13 +#define ITEM_CACTUS 14 +#define ITEM_SEEDS 15 +#define ITEM_WHEAT 16 +#define ITEM_FLESH 17 +#define ITEM_BREAD 18 +#define ITEM_APPLE 19 +#define ITEM_SLIME 20 +#define ITEM_COAL 21 +#define ITEM_IRONORE 22 +#define ITEM_GOLDORE 23 +#define ITEM_IRONINGOT 24 +#define ITEM_GOLDINGOT 25 +#define ITEM_GLASS 26 +#define ITEM_GEM 27 +#define ITEM_ANVIL 28 +#define ITEM_CHEST 29 +#define ITEM_OVEN 30 +#define ITEM_FURNACE 31 +#define ITEM_WORKBENCH 32 +#define ITEM_LANTERN 33 + +typedef struct Inventory Inventory; + +typedef struct { + s16 id; + s16 countLevel; // Count for items, Level for tools. + s16 slotNum; // Read-only. Do not mess with this. + bool onlyOne; + int* invPtr; // pointer to current inventory. + Inventory * chestPtr; // pointer to chest inventory for chest item. +} Item; + +struct Inventory { + Item items[300]; // Maximum of 300 slots in every inventory. + s16 lastSlot; // lastSlot can also be used to read the size of the inventory. +}; + +bool isItemEmpty(Item* item); +Item newItem(int id, int cLevel); +char* getItemName(int itemID, int countLevel); +char* getBasicItemName(int itemID, int countLevel); +void pushItemToInventoryFront(Item item, Inventory * inv); +void addItemToInventory(Item item, Inventory * inv); +void removeItemFromCurrentInv(Item* item); +void removeItemFromInventory(int slot, Inventory * inv); +Item* getItemFromInventory(int itemID, Inventory * inv); +int countItemInv(int itemID,int level, Inventory* inv); diff --git a/source/MapGen.c b/source/MapGen.c new file mode 100644 index 0000000..f354420 --- /dev/null +++ b/source/MapGen.c @@ -0,0 +1,403 @@ +#include "MapGen.h" + +int w = 0; +int h = 0; + +float nextFloat(){ + return (float)rand()/RAND_MAX; +} + +double sample(double * values, int x, int y) { + return values[(x & (w - 1)) + (y & (h - 1)) * w]; +} + +double * Noise(int width, int height, int featureSize) { + w = width; + h = height; + double * values = malloc(sizeof(double) * w * h); + int x, y; + for(x = 0; x < w; x+=featureSize){ + for(y = 0; y < w; y+=featureSize){ + values[(x & (w - 1)) + (y & (h - 1)) * w] = nextFloat() * 2 - 1; + } + } + + int stepSize = featureSize; + double scale = 1.0 / w; + double scaleMod = 1; + do { + int halfStep = stepSize / 2; + for(x = 0; x < w; x+=stepSize){ + for(y = 0; y < w; y+=stepSize){ + double a = sample(values,x, y); + double b = sample(values,x + stepSize, y); + double c = sample(values,x, y + stepSize); + double d = sample(values,x + stepSize, y + stepSize); + + double e = (a + b + c + d) / 4.0 + (nextFloat() * 2 - 1) * stepSize * scale; + values[((x + halfStep) & (w - 1)) + ((y + halfStep) & (h - 1)) * w] = e; + } + } + for(x = 0; x < w; x+=stepSize){ + for(y = 0; y < w; y+=stepSize){ + double a = sample(values,x, y); + double b = sample(values,x + stepSize, y); + double c = sample(values,x, y + stepSize); + double d = sample(values,x + halfStep, y + halfStep); + double e = sample(values,x + halfStep, y - halfStep); + double f = sample(values,x - halfStep, y + halfStep); + double H = (a + b + d + e) / 4.0 + (nextFloat() * 2 - 1) * stepSize * scale * 0.5; + double g = (a + c + d + f) / 4.0 + (nextFloat() * 2 - 1) * stepSize * scale * 0.5; + values[((x + halfStep) & (w - 1)) + (y & (h - 1)) * w] = H; + values[(x & (w - 1)) + ((y + halfStep) & (h - 1)) * w] = g; + } + } + + stepSize /= 2; + scale *= (scaleMod + 0.8); + scaleMod *= 0.3; + } while (stepSize > 1); + return values; + } + +void newSeed(){ + srand(time(NULL)); +} + +void createAndValidateTopMap(int w, int h, u8 * map, u8 * data) { + do { + createTopMap(w, h, map, data); + + int count[256]={[0 ... 255] = 0}; + int i; + for (i = 0; i < w * h; ++i) count[map[i] & 0xff]++; + if (count[TILE_ROCK & 0xff] < 100) continue; + if (count[TILE_SAND & 0xff] < 100) continue; + if (count[TILE_GRASS & 0xff] < 100) continue; + if (count[TILE_TREE & 0xff] < 100) continue; + if (count[TILE_STAIRS_DOWN & 0xff] < 2) continue; + + return; + } while (true); +} + +void createAndValidateUndergroundMap(int w, int h,int depthLevel, u8 * map, u8 * data) { + do { + createUndergroundMap(w, h, depthLevel, map, data); + + int count[256]={[0 ... 255] = 0}; + int i = 0; + for (i = 0; i < w * h; ++i)count[map[i] & 0xff]++; + if (count[TILE_ROCK & 0xff] < 100) continue; + 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 3: if (count[TILE_GEMORE & 0xff] < 20) continue; break; + } + if (depthLevel < 3) if (count[TILE_STAIRS_DOWN & 0xff] < 2) continue; + + return; + } while (true); +} +void createAndValidateSkyMap(int w, int h, u8 * map, u8 * data) { + do { + createSkyMap(w, h, map, data); + + int count[256]={[0 ... 255] = 0}; + int i = 0; + for (i = 0; i < w * h; ++i)count[map[i] & 0xff]++; + if (count[TILE_CLOUD & 0xff] < 1600) continue; + if (count[TILE_STAIRS_DOWN & 0xff] < 2) continue; + + return; + } while (true); +} + + + +void createTopMap(int w, int h, u8 * map, u8 * data) { + double* mnoise1 = Noise(w, h, 16); + double* mnoise2 = Noise(w, h, 16); + double* mnoise3 = Noise(w, h, 16); + double* noise1 = Noise(w, h, 32); + double* noise2 = Noise(w, h, 32); + + 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; + + double val = fabs(noise1[i] - noise2[i]) * 3 - 2; + double mval = fabs(mnoise1[i] - mnoise2[i]); + mval = fabs(mval - mnoise3[i]) * 3 - 2; + + double xd = x / (w - 1.0) * 2 - 1; + double yd = y / (h - 1.0) * 2 - 1; + if (xd < 0) xd = -xd; + if (yd < 0) yd = -yd; + double dist = xd >= yd ? xd : yd; + dist = dist * dist * dist * dist; + dist = dist * dist * dist * dist; + val = val + 1 - dist * 20; //-2 before to -21.0 after + + if (val < -0.5) { + map[i] = TILE_WATER; + } else if (val > 0.5 && mval < -1) { + map[i] = TILE_ROCK; + } else { + map[i] = TILE_GRASS; + } + } + } + + for (i = 0; i < w * h / 2800; ++i) { + int xs = rand()%w; + int ys = rand()%h; + for (k = 0; k < 10; ++k) { + x = xs + (rand()%21) - 10; + y = ys + (rand()%21) - 10; + 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_GRASS) map[xx + yy * w] = TILE_SAND; + } + } + } + } + } + + for (i = 0; i < w * h / 400; ++i) { + x = rand()%w; + y = rand()%h; + for (j = 0; j < 200; ++j) { + xx = x + (rand()%15) - (rand()%15); + yy = y + (rand()%15) - (rand()%15); + if (xx >= 0 && yy >= 0 && xx < w && yy < h) { + if (map[xx + yy * w] == TILE_GRASS) { + map[xx + yy * w] = TILE_TREE; + } + } + } + } + + for (i = 0; i < w * h / 800; ++i) { + x = rand()%w; + y = rand()%h; + for (j = 0; j < 30;++j) { + xx = x + (rand()%5) - (rand()%5); + yy = y + (rand()%5) - (rand()%5); + if (xx >= 0 && yy >= 0 && xx < w && yy < h) { + if (map[xx + yy * w] == TILE_GRASS) { + map[xx + yy * w] = TILE_FLOWER; + data[xx + yy * w] = rand()%4; // determines mirroring. + } + } + + } + } + + for (i = 0; i < w * h / 100; ++i) { + xx = rand()%w; + yy = rand()%h; + if (xx >= 0 && yy >= 0 && xx < w && yy < h) { + if (map[xx + yy * w] == TILE_SAND) { + map[xx + yy * w] = TILE_CACTUS; + } + } + } + + int sCount, attempts = 0; + for (sCount = 0; sCount < 4;) { + xx = rand()%w; + yy = rand()%h; + if (xx >= 0 && yy >= 0 && xx < w && yy < h) { + if (map[xx + yy * w] == TILE_ROCK) + { + map[xx + yy * w] = TILE_STAIRS_DOWN; + map[xx + (yy+1) * w] = TILE_ROCK; + map[(xx+1) + yy * w] = TILE_ROCK; + map[xx + (yy-1) * w] = TILE_ROCK; + map[(xx-1) + yy * w] = TILE_ROCK; + map[(xx-1) + (yy-1) * w] = TILE_ROCK; + map[(xx+1) + (yy+1) * w] = TILE_ROCK; + map[(xx+1) + (yy-1) * w] = TILE_ROCK; + map[(xx-1) + (yy+1) * w] = TILE_ROCK; + ++sCount; + } + } + if(attempts < (w*h/100)) ++attempts; else break; + } + free(mnoise1); + free(mnoise2); + free(mnoise3); + free(noise1); + free(noise2); + return; +} + +void createUndergroundMap(int w, int h,int depthLevel, u8 * map, u8 * data) { + double* mnoise1 = Noise(w, h, 16); + double* mnoise2 = Noise(w, h, 16); + double* mnoise3 = Noise(w, h, 16); + + double* nnoise1 = Noise(w, h, 16); + double* nnoise2 = Noise(w, h, 16); + double* nnoise3 = Noise(w, h, 16); + + double* wnoise1 = Noise(w, h, 16); + double* wnoise2 = Noise(w, h, 16); + double* wnoise3 = Noise(w, h, 16); + + double* noise1 = Noise(w, h, 32); + double* noise2 = Noise(w, h, 32); + + int x, y; + for(x = 0; x < w; ++x){ + for(y = 0; y < w; ++y){ + int i = x + y * w; + + double val = fabs(noise1[i] - noise2[i]) * 3 - 2; + + double mval = fabs(mnoise1[i] - mnoise2[i]); + mval = fabs(mval - mnoise3[i]) * 3 - 2; + + double nval = fabs(nnoise1[i] - nnoise2[i]); + nval = fabs(nval - nnoise3[i]) * 3 - 2; + + double wval = fabs(wnoise1[i] - wnoise2[i]); + wval = fabs(nval - wnoise3[i]) * 3 - 2; + + double xd = x / (w - 1.0) * 2 - 1; + double yd = y / (h - 1.0) * 2 - 1; + if (xd < 0) xd = -xd; + if (yd < 0) yd = -yd; + double dist = xd >= yd ? xd : yd; + dist = dist * dist * dist * dist; + dist = dist * dist * dist * dist; + val = val + 1 - dist * 20; + + if (val > -2 && wval < -2.0 + (depthLevel) / 2 * 3) { + if (depthLevel > 2) + map[i] = TILE_LAVA; + else + map[i] = TILE_WATER; + } else if (val > -2 && (mval < -1.7 || nval < -1.4)) { + map[i] = TILE_DIRT; + } else { + map[i] = TILE_ROCK; + } + } + } + int i,j; + for (i = 0; i < w * h / 400; ++i) { + int x = rand()%w; + int y = rand()%h; + for(j = 0; j < 30; ++j) { + int xx = x + (rand()%5) - (rand()%5); + int yy = y + (rand()%5) - (rand()%5); + if (xx >= 2 && yy >= 2 && xx < w-2 && yy < h-2) { + if (map[xx + yy * w] == TILE_ROCK) { + map[xx + yy * w] = TILE_IRONORE+depthLevel-1; + } + } + } + } + if (depthLevel < 3){ + int sCount, attempts = 0; + for (sCount = 0; sCount < 4;) { + int xx = rand()%w; + int yy = rand()%h; + if (xx >= 0 && yy >= 0 && xx < w && yy < h) { + if (map[xx + yy * w] == TILE_ROCK){ + map[xx + yy * w] = TILE_STAIRS_DOWN; + map[xx + (yy+1) * w] = TILE_ROCK; + map[(xx+1) + yy * w] = TILE_ROCK; + map[xx + (yy-1) * w] = TILE_ROCK; + map[(xx-1) + yy * w] = TILE_ROCK; + map[(xx-1) + (yy-1) * w] = TILE_ROCK; + map[(xx+1) + (yy+1) * w] = TILE_ROCK; + map[(xx+1) + (yy-1) * w] = TILE_ROCK; + map[(xx-1) + (yy+1) * w] = TILE_ROCK; + ++sCount; + } + } + if(attempts < (w*h/100)) ++attempts; else break; + } + } + free(mnoise1); + free(mnoise2); + free(mnoise3); + free(nnoise1); + free(nnoise2); + free(nnoise3); + free(wnoise1); + free(wnoise2); + free(wnoise3); + free(noise1); + free(noise2); + +} + void createSkyMap(int w, int h, u8 * map, u8 * data) { + double* noise1 = Noise(w, h, 8); + double* noise2 = Noise(w, h, 8); + int x, y; + for(x = 0; x < w; ++x){ + for(y = 0; y < w; ++y){ + int i = x + y * w; + + double val = fabs(noise1[i] - noise2[i]) * 3 - 2; + + double xd = x / (w - 1.0) * 2 - 1; + double yd = y / (h - 1.0) * 2 - 1; + if (xd < 0) xd = -xd; + if (yd < 0) yd = -yd; + double dist = xd >= yd ? xd : yd; + dist = dist * dist * dist * dist; + dist = dist * dist * dist * dist; + val = -val * 1 - 2.2; + val = val + 1 - dist * 20; + + if (val < -0.25) { + map[i] = -1; // render nothing + } else { + map[i] = TILE_CLOUD; + } + } + } + int i; + for (i = 0; i < w * h / 50; ++i) { + int xx = rand()%w; + int yy = rand()%h; + if (xx >= 0 && yy >= 0 && xx < w && yy < h) { + if (map[xx + yy * w] == TILE_CLOUD) map[xx + yy * w] = TILE_CLOUDCACTUS; + } + } + int sCount, attempts = 0; + for (sCount = 0; sCount < 2;) { + int xx = rand()%w; + int yy = rand()%h; + if (xx >= 0 && yy >= 0 && xx < w && yy < h) { + if (map[xx + yy * w] == TILE_CLOUD) + { + map[xx + yy * w] = TILE_STAIRS_DOWN; + map[xx + (yy+1) * w] = TILE_CLOUD; + map[(xx+1) + yy * w] = TILE_CLOUD; + map[xx + (yy-1) * w] = TILE_CLOUD; + map[(xx-1) + yy * w] = TILE_CLOUD; + map[(xx-1) + (yy-1) * w] = TILE_CLOUD; + map[(xx+1) + (yy+1) * w] = TILE_CLOUD; + map[(xx+1) + (yy-1) * w] = TILE_CLOUD; + map[(xx-1) + (yy+1) * w] = TILE_CLOUD; + ++sCount; + } + } + if(attempts < w*h) ++attempts; else break; + } + free(noise1); + free(noise2); + } + diff --git a/source/MapGen.h b/source/MapGen.h new file mode 100644 index 0000000..8ab4ce7 --- /dev/null +++ b/source/MapGen.h @@ -0,0 +1,19 @@ +#pragma once +#include +#include +#include +#include +#include <3ds.h> + +#include "Globals.h" + +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 createAndValidateSkyMap(int w, int h, u8 * map, u8 * data); +void createSkyMap(int w, int h, u8 * map, u8 * data); diff --git a/source/Menu.c b/source/Menu.c new file mode 100644 index 0000000..dafc8bf --- /dev/null +++ b/source/Menu.c @@ -0,0 +1,1006 @@ +#include "Menu.h" + +char options[][12] = {"Start Game", "How To Play","Settings", "About", "Exit"}; +char pOptions[][24] = {"Return to game", "Save Progress", "Exit to title"}; +char keybOptions[][24] = {"Exit and Save", "Exit and Don't save","Reset to default"}; + +// Rebind buttons menu (Settings) +int keys[] = { + KEY_A,KEY_B,KEY_X,KEY_Y, + KEY_CPAD_UP,KEY_CPAD_DOWN,KEY_CPAD_LEFT,KEY_CPAD_RIGHT, + KEY_DUP, KEY_DDOWN, KEY_DLEFT, KEY_DRIGHT, + KEY_CSTICK_UP,KEY_CSTICK_DOWN,KEY_CSTICK_LEFT,KEY_CSTICK_RIGHT, + KEY_L,KEY_R,KEY_ZL,KEY_ZR, + KEY_START,KEY_SELECT +}; +int keyProp[10] = {[0 ... 9] = 0}; +bool areYouSure = false; +bool areYouSureSave = false; +bool bindOpt = false; +bool left = false; +bool selBut = false; +s8 errorBut = -1; +s8 curSaveSel = 0; + +// Load Game Menu (Start Game) +char fileNames[1000][256]; +int fileScore[1000]; +bool fileWin[1000]; +s16 worldFileCount = 0; +bool enteringName = false; +s8 touchDelay = 0; +bool isTouching = false; +int touchX = 0, touchY = 0, touchW = 0, touchH = 0; +s8 errorFileName = 0; + +s16 pauseSaveDisplayTimer = 0; + +void readFiles(){ + memset(&fileNames, 0, 256000); // reset fileNames + worldFileCount = 0; + DIR * d; + struct dirent * dir; + d = opendir("."); + if (d){ + while ((dir = readdir(d)) != NULL) { + if (strstr(dir->d_name, ".wld") != NULL) { // Check if filename contains ".wld" + strncpy(fileNames[worldFileCount], dir->d_name, strlen(dir->d_name)-4); + FILE * file = fopen(dir->d_name, "rb"); + fread(&fileScore[worldFileCount],sizeof(int), 1, file); + fread(&fileWin[worldFileCount],sizeof(bool), 1, file); + fclose(file); + ++worldFileCount; + } + } + closedir(d); + } +} + +s8 checkFileNameForErrors(){ + int length = strlen(fileNames[worldFileCount]); + if(length < 1)return 1; // Error: Length cannot be 0. + int i; + bool isGood = false; + for(i=0; i < length; ++i){ if(isalnum((int)fileNames[worldFileCount][i])) isGood = true; } + if(!isGood) return 2; // Error: Filename must contain atleast one letter or number. + + DIR * d; + struct dirent * dir; + d = opendir("."); + if (d){ + while ((dir = readdir(d)) != NULL) { + if (strstr(dir->d_name, ".wld") != NULL) { // Check if filename contains ".wld" + char cmprFile[256]; + strncpy(cmprFile, dir->d_name, strlen(dir->d_name)-4); + if(strncmp(fileNames[worldFileCount],cmprFile,strlen(fileNames[worldFileCount])) == 0) return 3; // Error: Filename cannot already exist. + } + } + closedir(d); + } + + return 0; // No errors found! +} + +void addToFileName(char * c){ + strncat(fileNames[worldFileCount], c, 1); +} + +/* Keypad */ +void doTouchButton(touchPosition touch){ + int xVal = touch.px, yVal = touch.py; + int strLength = strlen(fileNames[worldFileCount]); + if(yVal >= 60 && yVal < 80){ // 0 to 9 + if(xVal >= 4 && xVal < 4+16){ touchX = 4; if(strLength < 24)addToFileName("1");} + else if(xVal >= 36 && xVal < 4+32+16){ touchX = 36; if(strLength < 24)addToFileName("2");} + else if(xVal >= 68 && xVal < 4+64+16){ touchX = 68; if(strLength < 24)addToFileName("3");} + else if(xVal >= 100 && xVal < 4+96+16){ touchX = 100; if(strLength < 24)addToFileName("4");} + else if(xVal >= 132 && xVal < 4+128+16){ touchX = 132; if(strLength < 24)addToFileName("5");} + else if(xVal >= 164 && xVal < 4+160+16){ touchX = 164; if(strLength < 24)addToFileName("6");} + else if(xVal >= 196 && xVal < 4+192+16){ touchX = 196; if(strLength < 24)addToFileName("7");} + else if(xVal >= 228 && xVal < 4+224+16){ touchX = 228; if(strLength < 24)addToFileName("8");} + else if(xVal >= 260 && xVal < 4+256+16){ touchX = 260; if(strLength < 24)addToFileName("9");} + else if(xVal >= 292 && xVal < 4+288+16){ touchX = 292; if(strLength < 24)addToFileName("0");} + else return; + touchY = 56; + touchW = 22; + touchH = 22; + touchDelay = 6; + isTouching = true; + } else if(yVal >= 80 && yVal < 100){ // Q to P + if(xVal >= 4 && xVal < 4+16){ touchX = 4; if(strLength < 24)addToFileName("Q");} + else if(xVal >= 36 && xVal < 4+32+16){ touchX = 36; if(strLength < 24)addToFileName("W");} + else if(xVal >= 68 && xVal < 4+64+16){ touchX = 68; if(strLength < 24)addToFileName("E");} + else if(xVal >= 100 && xVal < 4+96+16){ touchX = 100; if(strLength < 24)addToFileName("R");} + else if(xVal >= 132 && xVal < 4+128+16){ touchX = 132; if(strLength < 24)addToFileName("T");} + else if(xVal >= 164 && xVal < 4+160+16){ touchX = 164; if(strLength < 24)addToFileName("Y");} + else if(xVal >= 196 && xVal < 4+192+16){ touchX = 196; if(strLength < 24)addToFileName("U");} + else if(xVal >= 228 && xVal < 4+224+16){ touchX = 228; if(strLength < 24)addToFileName("I");} + else if(xVal >= 260 && xVal < 4+256+16){ touchX = 260; if(strLength < 24)addToFileName("O");} + else if(xVal >= 292 && xVal < 4+288+16){ touchX = 292; if(strLength < 24)addToFileName("P");} + else return; + touchY = 76; + touchW = 22; + touchH = 22; + touchDelay = 6; + isTouching = true; + } else if(yVal >= 100 && yVal < 120){ // A to L + if(xVal >= 20 && xVal < 36){ touchX = 20; if(strLength < 24)addToFileName("A");} + else if(xVal >= 52 && xVal < 68){ touchX = 52; if(strLength < 24)addToFileName("S");} + else if(xVal >= 84 && xVal < 100){ touchX = 84; if(strLength < 24)addToFileName("D");} + else if(xVal >= 116 && xVal < 132){ touchX = 116; if(strLength < 24)addToFileName("F");} + else if(xVal >= 148 && xVal < 164){ touchX = 148; if(strLength < 24)addToFileName("G");} + else if(xVal >= 180 && xVal < 196){ touchX = 180; if(strLength < 24)addToFileName("H");} + else if(xVal >= 212 && xVal < 230){ touchX = 212; if(strLength < 24)addToFileName("J");} + else if(xVal >= 244 && xVal < 262){ touchX = 244; if(strLength < 24)addToFileName("K");} + else if(xVal >= 276 && xVal < 294){ touchX = 276; if(strLength < 24)addToFileName("L");} + else return; + touchY = 96; + touchW = 22; + touchH = 22; + touchDelay = 6; + isTouching = true; + } else if(yVal >= 120 && yVal < 140){ // Z to M + if(xVal >= 52 && xVal < 68){ touchX = 52; if(strLength < 24)addToFileName("Z");} + else if(xVal >= 84 && xVal < 100){ touchX = 84; if(strLength < 24)addToFileName("X");} + else if(xVal >= 116 && xVal < 132){ touchX = 116; if(strLength < 24)addToFileName("C");} + else if(xVal >= 148 && xVal < 164){ touchX = 148; if(strLength < 24)addToFileName("V");} + else if(xVal >= 180 && xVal < 196){ touchX = 180; if(strLength < 24)addToFileName("B");} + else if(xVal >= 212 && xVal < 230){ touchX = 212; if(strLength < 24)addToFileName("N");} + else if(xVal >= 244 && xVal < 262){ touchX = 244; if(strLength < 24)addToFileName("M");} + else return; + touchY = 116; + touchW = 22; + touchH = 22; + touchDelay = 6; + isTouching = true; + } else if(yVal >= 140 && yVal < 160){ // SPACE and BACKSPACE + if(xVal >= 32 && xVal < 112){ + touchX = 32; + touchW = 16*5+14; + if(strLength < 24)addToFileName("_"); // Underscores don't appear in the game. + } + else if(xVal >= 148 && xVal < 262){ + if(strLength <= 0) return; + touchX = 148; + touchW = 16*9+8; + fileNames[worldFileCount][strLength-1] = '\0'; + } + else return; + touchY = 136; + touchH = 22; + touchDelay = 6; + isTouching = true; + } + errorFileName = 0; +} + +void switchGameBut(bool left, int buttonID){ + int id; + for(id = 0; id < 7; ++id){ + if(keyProp[id] & buttonID){ + keyProp[id] ^= buttonID; // Toggle buttonID bit + if(left){ + int id2 = id -1; + if (id2 < 0) return; + keyProp[id2] ^= buttonID; + } else { + int id2 = id+1; + if (id2 > 6) return; + keyProp[id2] ^= buttonID; + } + return; + } + } + if(left) keyProp[6] ^= buttonID; + else keyProp[0] ^= buttonID; + +} +void switchMenuBut(bool left, int buttonID){ + int id; + for(id = 0; id < 10; ++id){ + if(id > 3 && id < 7) continue; + if(keyProp[id] & buttonID){ + keyProp[id] ^= buttonID; // Toggle buttonID bit + if(left){ + int id2 = id - 1; + if (id2 == 6) id2 = 3; + if (id2 < 0) return; + keyProp[id2] ^= buttonID; + } else { + int id2 = id+1; + if (id2 == 4) id2 = 7; + if (id2 > 9) return; + keyProp[id2] ^= buttonID; + } + return; + } + } + if(left) keyProp[9] ^= buttonID; + else keyProp[0] ^= buttonID; +} + +s8 checkPropButtons(){ + if(keyProp[0] == 0) return 0; + if(keyProp[1] == 0) return 1; + if(keyProp[2] == 0) return 2; + if(keyProp[3] == 0) return 3; + if(keyProp[4] == 0) return 4; + if(keyProp[5] == 0) return 5; + if(keyProp[6] == 0) return 6; + if(keyProp[7] == 0) return 7; + if(keyProp[8] == 0) return 8; + if(keyProp[9] == 0) return 9; + return -1; +} + +Item median; +void tickMenu(int menu){ + switch(menu){ + case MENU_SETTINGS: + if(!bindOpt){ + if(!selBut){ + if (k_up.clicked){ --currentSelection; if(currentSelection < 0)currentSelection=21;} + if (k_down.clicked){ ++currentSelection; if(currentSelection > 21)currentSelection=0;} + if (k_left.clicked){ left = true;} + if (k_right.clicked){ left = false;} + } else { + if (k_left.clicked){ + if(left)switchGameBut(true,keys[currentSelection]); + else switchMenuBut(true,keys[currentSelection]); + } else if (k_right.clicked) { + if(left)switchGameBut(false,keys[currentSelection]); + else switchMenuBut(false,keys[currentSelection]); + } + } + + if (k_accept.clicked) selBut = !selBut; + if (k_decline.clicked){ + bindOpt = true; + curSaveSel = 0; + } + } else { + if (k_up.clicked){ --curSaveSel; if(curSaveSel < 0)curSaveSel=2;} + if (k_down.clicked){ ++curSaveSel; if(curSaveSel > 2)curSaveSel=0;} + if (k_decline.clicked){ + bindOpt = false; + errorBut = -1; + } + if (k_accept.clicked){ + switch(curSaveSel){ + case 0: // Exit and save + if(checkPropButtons() == -1){ + k_up.input = keyProp[0]; + k_down.input = keyProp[1]; + k_left.input = keyProp[2]; + k_right.input = keyProp[3]; + k_attack.input = keyProp[4]; + k_menu.input = keyProp[5]; + k_pause.input = keyProp[6]; + k_accept.input = keyProp[7]; + k_decline.input = keyProp[8]; + k_delete.input = keyProp[9]; + + FILE *fs=fopen("btnSave.bin","wb"); + fwrite(keyProp,sizeof(int),9,fs); + fclose(fs); + + currentSelection = 2; + currentMenu = MENU_TITLE; + errorBut = -1; + } else errorBut = checkPropButtons(); + break; + case 1: // Exit and DON'T save + currentSelection = 2; + currentMenu = MENU_TITLE; + errorBut = -1; + break; + case 2: // reset defaults + keyProp[0] = KEY_DUP | KEY_CPAD_UP | KEY_CSTICK_UP; + keyProp[1] = KEY_DDOWN | KEY_CPAD_DOWN | KEY_CSTICK_DOWN; + keyProp[2] = KEY_DLEFT | KEY_CPAD_LEFT | KEY_CSTICK_LEFT; + keyProp[3] = KEY_DRIGHT | KEY_CPAD_RIGHT | KEY_CSTICK_RIGHT; + keyProp[4] = KEY_A | KEY_B | KEY_L | KEY_ZR; + keyProp[5] = KEY_X | KEY_Y | KEY_R | KEY_ZL; + keyProp[6] = KEY_START; + keyProp[7] = KEY_A; + keyProp[8] = KEY_B; + keyProp[9] = KEY_X; + bindOpt = false; + errorBut = -1; + break; + } + } + } + break; + case MENU_PAUSED: + if(!areYouSure && !areYouSureSave){ + if(pauseSaveDisplayTimer > 0) --pauseSaveDisplayTimer; + if (k_pause.clicked) currentMenu = MENU_NONE; + if (k_up.clicked){ --currentSelection; if(currentSelection < 0)currentSelection=2;} + if (k_down.clicked){ ++currentSelection; if(currentSelection > 2)currentSelection=0;} + if (k_accept.clicked){ + switch(currentSelection){ + case 0: + currentMenu = MENU_NONE; + break; + case 1: + areYouSureSave = true; + break; + case 2: + areYouSure = true; + break; + } + } + } else if(areYouSureSave) { + if (k_accept.clicked){ + pauseSaveDisplayTimer = 60; + saveCurrentWorld(currentFileName, &eManager, &player, (u8*)map, (u8*)data); + areYouSureSave = false; + areYouSure = false; + } else if (k_decline.clicked){ + areYouSureSave = false; + areYouSure = false; + } + } else { + if (k_accept.clicked){ + areYouSure = false; + areYouSureSave = false; + sf2d_set_clear_color(0xFF); + currentSelection = 0; + currentMenu = MENU_TITLE; + } else if (k_decline.clicked){ + areYouSure = false; + areYouSureSave = false; + } + } + break; + case MENU_INVENTORY: + if (k_menu.clicked || k_decline.clicked){ + currentMenu = MENU_NONE; + player.p.activeItem = &noItem; + player.p.isCarrying = false; + } + if (k_accept.clicked){ // Select item from inventory + if(player.p.inv->lastSlot!=0){ + median = player.p.inv->items[curInvSel]; // create copy of item. + removeItemFromInventory(curInvSel, player.p.inv); // remove original + pushItemToInventoryFront(median, player.p.inv); // add copy to front + player.p.activeItem = &player.p.inv->items[0]; // active item = copy. + if(player.p.activeItem->id > 27) player.p.isCarrying = true; + else player.p.isCarrying = false; + } + currentMenu = MENU_NONE; + } + if (k_up.clicked){ --curInvSel; if(curInvSel < 0)curInvSel=player.p.inv->lastSlot-1;} + if (k_down.clicked){ ++curInvSel; if(curInvSel > player.p.inv->lastSlot-1)curInvSel=0;} + break; + + case MENU_CRAFTING: + if (k_menu.clicked || k_decline.clicked) currentMenu = MENU_NONE; + if (k_accept.clicked){ + int newslot = player.p.activeItem->slotNum + 1; + if(craftItem(currentRecipes, ¤tRecipes->recipes[curInvSel], player.p.inv)){ + playSound(snd_craft); + if(player.p.activeItem != &noItem)player.p.activeItem = &player.p.inv->items[newslot]; + } + } + if (k_up.clicked){ --curInvSel; if(curInvSel < 0)curInvSel=currentRecipes->size-1;} + if (k_down.clicked){ ++curInvSel; if(curInvSel > currentRecipes->size-1)curInvSel=0;} + break; + + case MENU_WIN: + if (k_accept.clicked){ + sf2d_set_clear_color(0xFF); + currentMenu = MENU_TITLE; + saveCurrentWorld(currentFileName, &eManager, &player, (u8*)map, (u8*)data); + } + break; + case MENU_LOSE: + if (k_accept.clicked){ + sf2d_set_clear_color(0xFF); + currentMenu = MENU_TITLE; + } + break; + case MENU_ABOUT: + if (k_decline.clicked) currentMenu = MENU_TITLE; + break; + + case MENU_CONTAINER: + if (k_menu.clicked || k_decline.clicked)currentMenu = MENU_NONE; + + if (k_left.clicked) { + curChestEntity->entityFurniture.r = 0; + int tmp = curInvSel; + curInvSel = curChestEntity->entityFurniture.oSel; + curChestEntity->entityFurniture.oSel = tmp; + } + if (k_right.clicked) { + curChestEntity->entityFurniture.r = 1; + int tmp = curInvSel; + curInvSel = curChestEntity->entityFurniture.oSel; + curChestEntity->entityFurniture.oSel = tmp; + } + + Inventory* i1 = curChestEntity->entityFurniture.r == 1 ? player.p.inv : curChestEntity->entityFurniture.inv; + Inventory* i2 = curChestEntity->entityFurniture.r == 0 ? player.p.inv : curChestEntity->entityFurniture.inv; + int len = i1->lastSlot; + if (curInvSel < 0) curInvSel = 0; + if (curInvSel >= len) curInvSel = len - 1; + if (k_up.clicked) --curInvSel; + if (k_down.clicked) ++curInvSel; + if (len == 0) curInvSel = 0; + if (curInvSel < 0) curInvSel += len; + if (curInvSel >= len) curInvSel -= len; + + if(k_accept.clicked && len > 0){ + Item* pullItem = &i1->items[curInvSel]; + Item pushItem = newItem(pullItem->id,pullItem->countLevel); + pushItem.chestPtr = pullItem->chestPtr; + pushItemToInventoryFront(pushItem, i2); + if(i2 == player.p.inv){ + int newslot = player.p.activeItem->slotNum + 1; + player.p.activeItem = &player.p.inv->items[newslot]; + } else if(pullItem == player.p.activeItem){ + player.p.activeItem = &noItem; + } + removeItemFromCurrentInv(pullItem); + if (curInvSel >= i1->lastSlot) curInvSel = i1->lastSlot - 1; + } + + break; + + case MENU_LOADGAME: + if(!enteringName && !areYouSure){ // World select + if (k_decline.clicked){ + currentMenu = MENU_TITLE; + currentSelection = 0; + } + if (k_up.clicked){ --currentSelection; if(currentSelection < 0)currentSelection = worldFileCount;} + if (k_down.clicked){ ++currentSelection; if(currentSelection > worldFileCount)currentSelection=0;} + + if(k_delete.clicked){ + if(currentSelection < worldFileCount) areYouSure = true; + } + + if(k_accept.clicked){ + if(currentSelection == worldFileCount){ + + enteringName = true; + } else { + memset(¤tFileName, 0, 255); // reset currentFileName + sprintf(currentFileName,"%s.wld",fileNames[currentSelection]); + playSound(snd_test); + initGame = 1; + currentMenu = MENU_NONE; + } + } + } else if (areYouSure){ + if (k_decline.clicked || k_delete.clicked) areYouSure = false; + if (k_accept.clicked){ + sprintf(currentFileName,"%s.wld",fileNames[currentSelection]); + remove(currentFileName); + readFiles(); + enteringName = false; + areYouSure = false; + memset(¤tFileName, 0, 255); // reset currentFileName + } + }else { // Enter new world name. + if(k_decline.clicked) enteringName = false; + if(k_accept.clicked && errorFileName == 0){ + errorFileName = checkFileNameForErrors(); + if(errorFileName == 0){ // If no errors are found with the filename, then start a new game! + memset(¤tFileName, 0, 255); // reset currentFileName + sprintf(currentFileName,"%s.wld",fileNames[worldFileCount]); + currentMenu = MENU_NONE; + playSound(snd_test); + initGame = 2; + ++worldFileCount; + } + } + touchPosition touch; + hidTouchRead(&touch); + if((touch.px != 0 || touch.py != 0) && touchDelay == 0){ + if(!isTouching)doTouchButton(touch); + } + else if(touch.px == 0 || touch.py == 0) isTouching = false; + if(touchDelay > 0) --touchDelay; + } + break; + + case MENU_TITLE: + if (k_up.clicked){ --currentSelection; if(currentSelection < 0)currentSelection=4;} + if (k_down.clicked){ ++currentSelection; if(currentSelection > 4)currentSelection=0;} + + if(k_accept.clicked){ + switch(currentSelection){ + case 0: + currentMenu = MENU_LOADGAME; + readFiles(); + currentSelection = 0; + enteringName = false; + areYouSure = false; + break; + case 2: + keyProp[0] = k_up.input; + keyProp[1] = k_down.input; + keyProp[2] = k_left.input; + keyProp[3] = k_right.input; + keyProp[4] = k_attack.input; + keyProp[5] = k_menu.input; + keyProp[6] = k_pause.input; + keyProp[7] = k_accept.input; + keyProp[8] = k_decline.input; + keyProp[9] = k_delete.input; + left = true; + selBut = false; + bindOpt = false; + currentSelection = 0; + currentMenu = MENU_SETTINGS; + break; + case 3: + currentMenu = MENU_ABOUT; + break; + case 4: + quitGame = true; + break; + } + + } + break; + } + +} + +u8 opacity = 255; +bool rev = true; +char scoreText[15]; + +char * getButtonFunctionGame(int key){ + if(keyProp[0] & key) return "Move up"; + if(keyProp[1] & key) return "Move down"; + if(keyProp[2] & key) return "Move left"; + if(keyProp[3] & key) return "Move right"; + if(keyProp[4] & key) return "Attack"; + if(keyProp[5] & key) return "Toggle Menu"; + if(keyProp[6] & key) return "Pause"; + return "Nothing"; +} +char * getButtonFunctionMenu(int key){ + if(keyProp[0] & key) return "Up"; + if(keyProp[1] & key) return "Down"; + if(keyProp[2] & key) return "Left"; + if(keyProp[3] & key) return "Right"; + if(keyProp[7] & key) return "Accept"; + if(keyProp[8] & key) return "Decline"; + if(keyProp[9] & key) return "Delete"; + return "Nothing"; +} + + +char guiText0[] = "1 2 3 4 5 6 7 8 9 0"; +char guiText1[] = "Q W E R T Y U I O P"; +char guiText2[] = "A S D F G H J K L"; +char guiText3[] = "Z X C V B N M"; +char guiText4[] = " SPACE BACKSPACE"; + +void renderMenu(int menu,int xscr,int yscr){ + int i = 0; + switch(menu){ + case MENU_LOADGAME: + sf2d_start_frame(GFX_TOP, GFX_LEFT); + if(!enteringName){ // World select + offsetX = 0;offsetY = (currentSelection * 32) - 48; + drawText("Select a file",122,-16); + for(i = 0; i < worldFileCount + 1; ++i){ + int color = 0x201092FF; + char * text = fileNames[i]; + if(i == worldFileCount){ + text = "Generate New World"; + color = 0x109220FF; + } + if(i != currentSelection) color &= 0x7F7F7FFF; // Darken color. + else { + if(areYouSure)color = 0xDF1010FF; + } + + char scoreText[24]; + sprintf(scoreText,"Score: %d",fileScore[i]); + + renderFrame(1,i*4,24,(i*4)+4,color); + if(i != worldFileCount){ + drawText(text,(400-(strlen(text)*12))/2,i*64+12); + drawText(scoreText,(400-(strlen(scoreText)*12))/2,i*64+32); + } else { + drawText(text,(400-(strlen(text)*12))/2,i*64+24); + } + if(fileWin[i] && i != worldFileCount) render16(18,i*32+8,24,208,0); // Render crown + } + offsetX = 0;offsetY = 0; + } else { // Enter new world name. + drawText("Enter a name",128,16); + drawText(fileNames[worldFileCount],(400-(strlen(fileNames[worldFileCount])*12))/2, 48); + + if(errorFileName > 0){ + switch(errorFileName){// Error: Filename cannot already exist. + case 1: drawTextColor("ERROR: Length cannot be 0!",(400-26*12)/2,200,0xAF1010FF); break; + case 2: drawTextColor("ERROR: You need Letters/Numbers!",(400-32*12)/2,200,0xAF1010FF); break; + case 3: drawTextColor("ERROR: Filename already exists!",(400-31*12)/2,200,0xAF1010FF); break; + } + } + } + sf2d_end_frame(); + sf2d_start_frame(GFX_BOTTOM, GFX_LEFT); + if(!enteringName){ // World select + if(!areYouSure){ + drawTextColor("Load World",100,12,0xFFFF3FFF); + drawText("Press or to scroll", 28, 50); + renderButtonIcon(k_up.input & -k_up.input, 98, 48, 1); + renderButtonIcon(k_down.input & -k_down.input, 160, 48, 1); + drawText("Press to load world", (320-21*12)/2, 100); + renderButtonIcon(k_accept.input & -k_accept.input, 104, 98, 1); + drawText("Press to return", 58, 150); + renderButtonIcon(k_decline.input & -k_decline.input, 128, 148, 1); + if(currentSelection != worldFileCount){ + drawText("Press to delete",(320-17*12)/2, 200); + renderButtonIcon(k_delete.input & -k_delete.input, 128, 198, 1); + } + } else { + drawTextColor("Delete File?",88,12,0xFF3F3FFF); + drawText("Press to confirm", (320-18*12)/2, 100); + renderButtonIcon(k_accept.input & -k_accept.input, 122, 98, 1); + drawText("Press to return", 58, 150); + renderButtonIcon(k_decline.input & -k_decline.input, 128, 148, 1); + } + + } else { // Draw the "keyboard" + drawTextColor("Touch the keypad below",(320-22*12)/2,12,0xFFFF33FF); + + sf2d_draw_rectangle(0, 50, 320, 110, 0xBF7F7FFF); + drawSizedText(guiText0,4, 60, 2); + drawSizedText(guiText1,4, 80, 2); + drawSizedText(guiText2,12, 100, 2); + drawSizedText(guiText3,28, 120, 2); + drawSizedText(guiText4,12, 140, 2); + + if(touchDelay > 0){ + sf2d_draw_rectangle(touchX, touchY, touchW, touchH, 0xAF); + } + + drawText("Press to confirm", (320-18*12)/2, 180); + renderButtonIcon(k_accept.input & -k_accept.input, 122, 178, 1); + drawText("Press to return", 58, 210); + renderButtonIcon(k_decline.input & -k_decline.input, 128, 208, 1); + } + + sf2d_end_frame(); + break; + case MENU_SETTINGS: + sf2d_start_frame(GFX_TOP, GFX_LEFT); + drawTextColor("Rebind Buttons",116,12,0xAFAF00FF); + drawText("Button",16,32); + drawText("Game",140,32); + drawText("Menus",280,32); + + char gameButText[34]; + char menuButText[34]; + + for(i = 0; i < 5; ++i){ + if((currentSelection-2) + i > 21 || (currentSelection-2) + i < 0) continue; + renderButtonIcon(keys[(currentSelection-2) + i], 16, (i * 18) + 30, 2); + int ccol = 0x7F7F7FFF; + + sprintf(gameButText,"%s",getButtonFunctionGame(keys[(currentSelection-2) + i])); + sprintf(menuButText,"%s",getButtonFunctionMenu(keys[(currentSelection-2) + i])); + + if(i == 2){ + if(!selBut)ccol = 0xFFFFFFFF; + else{ + ccol = 0x00FF00FF; + if(left)sprintf(gameButText,"<%s>",getButtonFunctionGame(keys[(currentSelection-2) + i])); + else sprintf(menuButText,"<%s>",getButtonFunctionMenu(keys[(currentSelection-2) + i])); + } + } + if(left){ + drawTextColor(gameButText, 112, (i * 33) + 80, ccol); + drawTextColor(menuButText, 280, (i * 33) + 80, 0x7F7F7FFF); + } else { + drawTextColor(gameButText, 112, (i * 33) + 80, 0x7F7F7FFF); + drawTextColor(menuButText, 280, (i * 33) + 80, ccol); + } + } + if(bindOpt){ + renderFrame(1,1,24,14,0x1010BFFF); + drawTextColor("Save changes?",122,32,0xAFAF00FF); + for(i = 2; i >= 0; --i){ + char* msg = keybOptions[i]; + u32 color = 0x4F4F4FFF; + if(i == curSaveSel) color = 0xFFFFFFFF; + drawTextColor(msg,(400 - (strlen(msg) * 12))/2, (i * 24) + 92, color); + } + drawText("Press to return", 98, 190); + renderButtonIcon(k_decline.input & -k_decline.input, 168, 188, 1); + + if(errorBut >= 0 && errorBut < 9){ + char errorText[30]; + switch(errorBut){ + case 0: sprintf(errorText, "Error: Missing 'Move up'"); break; + case 1: sprintf(errorText, "Error: Missing 'Move down'"); break; + case 2: sprintf(errorText, "Error: Missing 'Move right'"); break; + case 3: sprintf(errorText, "Error: Missing 'Move left'"); break; + case 4: sprintf(errorText, "Error: Missing 'Attack'"); break; + case 5: sprintf(errorText, "Error: Missing 'Toggle Menu'"); break; + case 6: sprintf(errorText, "Error: Missing 'Pause'"); break; + case 7: sprintf(errorText, "Error: Missing 'Accept'"); break; + case 8: sprintf(errorText, "Error: Missing 'Decline'"); break; + case 9: sprintf(errorText, "Error: Missing 'Delete'"); break; + } + drawTextColor(errorText,(400 - (strlen(errorText) * 12))/2,50,0xFF0000FF); + } + + } + sf2d_end_frame(); + sf2d_start_frame(GFX_BOTTOM, GFX_LEFT); + if(!selBut){ + drawText("Press to select", 58, 80); + renderButtonIcon(k_accept.input & -k_accept.input, 128, 78, 1); + drawText("Press to return", 58, 130); + renderButtonIcon(k_decline.input & -k_decline.input, 128, 128, 1); + } else { + drawText("Press or to scroll", 28, 50); + renderButtonIcon(k_left.input & -k_left.input, 98, 48, 1); + renderButtonIcon(k_right.input & -k_right.input, 160, 48, 1); + drawText("Press to unselect", 46, 100); + renderButtonIcon(k_accept.input & -k_accept.input, 118, 98, 1); + drawText("Press to return", 58, 150); + renderButtonIcon(k_decline.input & -k_decline.input, 128, 148, 1); + } + sf2d_end_frame(); + break; + + case MENU_PAUSED: + sf2d_start_frame(GFX_TOP, GFX_LEFT); + if(currentLevel == 0){ + sf2d_draw_texture_part_scale(minimap[1],(-xscr/3)-256,(-yscr/3)-32,0,0,128,128,12.5,7.5); + sf2d_draw_rectangle(0,0,400,240, 0xDFDFDFAF); + } + offsetX = xscr;offsetY = yscr; + renderBackground(xscr,yscr); + offsetX = 0;offsetY = 0; + renderFrame(1,1,24,14,0xAF1010FF); + drawText("Paused",164,32); + for(i = 2; i >= 0; --i){ + char* msg = pOptions[i]; + u32 color = 0x7F7F7FFF; + if(i == currentSelection) color = 0xFFFFFFFF; + drawTextColor(msg,(400 - (strlen(msg) * 12))/2, (i * 24) + 100, color); + } + + if(pauseSaveDisplayTimer > 0) drawTextColor("Game Saved!", (400-(11*12))/2, 64,0x20FF20FF); + + if(areYouSure || areYouSureSave){ + if(areYouSure)renderFrame(6,5,19,10,0x8F1010FF); + else renderFrame(6,5,19,10,0x108F10FF); + + drawText("Are you sure?",122,96); + drawText(" Yes", 164, 117); + renderButtonIcon(k_accept.input & -k_accept.input, 166, 114, 1); + drawText(" No", 170, 133); + renderButtonIcon(k_decline.input & -k_decline.input, 166, 130, 1); + } + + sf2d_end_frame(); + break; + case MENU_WIN: + sf2d_start_frame(GFX_TOP, GFX_LEFT); + if(currentLevel == 0){ + sf2d_draw_texture_part_scale(minimap[1],(-xscr/3)-256,(-yscr/3)-32,0,0,128,128,12.5,7.5); + sf2d_draw_rectangle(0,0,400,240, 0xDFDFDFAF); + } + offsetX = xscr;offsetY = yscr; + renderBackground(xscr,yscr); + offsetX = 0;offsetY = 0; + renderFrame(5,3,21,12,0x1010FFFF); + if(!rev){ opacity+=5; if(opacity == 255) rev = true; } + else { opacity-=5; if(opacity == 100) rev = false; } + sprintf(scoreText,"Score: %d", player.p.score); + drawTextColor("You Win!",158,76,0xAFAF0000 + opacity); + drawText(scoreText, 200-((strlen(scoreText)-1)*6), 100); + drawText("Press to continue", 96, 150); + renderButtonIcon(k_attack.input & -k_attack.input, 166, 148, 1); + + //printf("0x%08X",k_attack.input & -k_attack.input); + sf2d_end_frame(); + break; + case MENU_LOSE: + sf2d_start_frame(GFX_TOP, GFX_LEFT); + if(currentLevel == 0){ + sf2d_draw_texture_part_scale(minimap[1],(-xscr/3)-256,(-yscr/3)-32,0,0,128,128,12.5,7.5); + sf2d_draw_rectangle(0,0,400,240, 0xDFDFDFAF); + } + offsetX = xscr;offsetY = yscr; + renderBackground(xscr,yscr); + offsetX = 0;offsetY = 0; + renderFrame(5,3,21,12,0x1010FFFF); + if(!rev){ opacity+=5; if(opacity == 255) rev = true; } + else { opacity-=5; if(opacity == 100) rev = false; } + sprintf(scoreText,"Score: %d", player.p.score); + drawTextColor("You DIED!",158,76,0xAF000000 + opacity); + drawText(scoreText, 200-((strlen(scoreText)-1)*6), 100); + drawText("Press to continue", 96, 150); + renderButtonIcon(k_attack.input & -k_attack.input, 166, 148, 1); + //printf("0x%08X",k_attack.input & -k_attack.input); + sf2d_end_frame(); + break; + case MENU_INVENTORY: + sf2d_start_frame(GFX_TOP, GFX_LEFT); + if(currentLevel == 0){ + sf2d_draw_texture_part_scale(minimap[1],(-xscr/3)-256,(-yscr/3)-32,0,0,128,128,12.5,7.5); + sf2d_draw_rectangle(0,0,400,240, 0xDFDFDFAF); + } + offsetX = xscr;offsetY = yscr; + renderBackground(xscr,yscr); + offsetX = 0;offsetY = 0; + renderFrame(1,1,24,14,0x1010FFFF); + renderItemList(player.p.inv, 1,1,24,14, curInvSel); + sf2d_end_frame(); + break; + case MENU_CRAFTING: + sf2d_start_frame(GFX_TOP, GFX_LEFT); + if(currentLevel == 0){ + sf2d_draw_texture_part_scale(minimap[1],(-xscr/3)-256,(-yscr/3)-32,0,0,128,128,12.5,7.5); + sf2d_draw_rectangle(0,0,400,240, 0xDFDFDFAF); + } + offsetX = xscr;offsetY = yscr; + renderBackground(xscr,yscr); + offsetX = 0;offsetY = 0; + + renderFrame(15,1,24,4,0x1010FFFF); + renderFrame(15,5,24,14,0x1010FFFF); + renderFrame(1,1,14,14,0x1010FFFF); + renderRecipes(currentRecipes, 1, 1, 14, 14, curInvSel); + + Recipe* rec = ¤tRecipes->recipes[curInvSel]; + renderItemIcon(rec->itemResult,rec->itemAmountLevel,128,16); + char craftText[12]; + sprintf(craftText,"%d",countItemInv(rec->itemResult,rec->itemAmountLevel, player.p.inv)); + drawText(craftText,274,34); + + if(rec->numOfCosts > 0){ + int i; + for(i = 0; i < rec->numOfCosts; i++){ + int amnt = countItemInv(rec->costs[i].costItem,0, player.p.inv); + int ttlCst = rec->costs[i].costAmount; + int col = 0xFFFFFFFF; if(amntcosts[i].costItem,1,128,48+(i*8)); + sprintf(craftText,"%d/%d",amnt,ttlCst); + drawTextColor(craftText,274,96+(i*18),col); + } + } + + sf2d_end_frame(); + break; + + case MENU_CONTAINER: + sf2d_start_frame(GFX_TOP, GFX_LEFT); + if(currentLevel == 0){ + sf2d_draw_texture_part_scale(minimap[1],(-xscr/3)-256,(-yscr/3)-32,0,0,128,128,12.5,7.5); + sf2d_draw_rectangle(0,0,400,240, 0xDFDFDFAF); + } + offsetX = xscr;offsetY = yscr; + renderBackground(xscr,yscr); + if (curChestEntity->entityFurniture.r == 1){ offsetX = 48;offsetY = 0;} + else {offsetX = 0;offsetY = 0;} + + renderFrame(1,1,14,14,0x1010FFFF); + renderItemList(curChestEntity->entityFurniture.inv,1,1,14,14, + curChestEntity->entityFurniture.r == 0 ? curInvSel : -curChestEntity->entityFurniture.oSel - 1); + renderFrame(15,1,28,14,0x1010FFFF); + renderItemList(player.p.inv,15,1,28,14, + curChestEntity->entityFurniture.r == 1 ? curInvSel : -curChestEntity->entityFurniture.oSel - 1); + offsetX = 0;offsetY = 0; + sf2d_end_frame(); + break; + case MENU_ABOUT: + sf2d_start_frame(GFX_TOP, GFX_LEFT); + drawText("About Minicraft",110,12); + drawSizedText("Minicraft was made by Markus Persson for the",24,36,1.0); + drawSizedText("22'nd ludum dare competition in december 2011.",16,48,1.0); + drawSizedText("it is dedicated to my father. <3",72,60,1.0); + drawSizedText("- Markus \"Notch\" Persson",104,76,1.0); + + drawTextColor("3DS Homebrew Edition",74,120,0x00FF00FF); + drawSizedTextColor("This port was made by David Benepe (Davideesk)",16,144,1.0,0x00FF00FF); + drawSizedTextColor("just for fun in September/October 2015.",44,156,1.0,0x00FF00FF); + drawSizedTextColor("TY Notch for creating a fun game to remake!",28,180,1.0,0x00FF00FF); + sf2d_end_frame(); + sf2d_start_frame(GFX_BOTTOM, GFX_LEFT); + drawTextColor("Special Thanks to:",52,12,0xFF7F7FFF); + drawTextColor("Smea",136,60,0xFF2020FF); + drawSizedTextColor("for ctrulib",116,80,1.0,0xFF2020FF); + drawTextColor("Xerpi",130,120,0x2020FFFF); + drawSizedTextColor("for sf2dlib",116,140,1.0,0x2020FFFF); + drawText("Press to return", 58, 220); + renderButtonIcon(k_decline.input & -k_decline.input, 128, 218, 1); + sf2d_end_frame(); + break; + case MENU_TITLE: + /* Top Screen */ + sf2d_start_frame(GFX_TOP, GFX_LEFT); + renderTitle(76,16); + + for(i = 4; i >= 0; --i){ + char* msg = options[i]; + u32 color = 0x7F7F7FFF; + if(i == currentSelection) color = 0xFFFFFFFF; + drawSizedTextColor(msg,(200 - (strlen(msg) * 8))/2, ((8 + i) * 20 - 50) >> 1,2.0, color); + } + + drawText(versionText,2,225); + sf2d_end_frame(); + + /* Bottom Screen */ + sf2d_start_frame(GFX_BOTTOM, GFX_LEFT); + int startX = 0, startY = 0;// relative coordinates ftw + switch(currentSelection){ + case 0: // "Start Game" + startX = 20;startY = 50; + render16(startX,startY+12,0,128,0);//Player(Carrying) + render16(startX,startY,128,128,0);//Workbench + startX = 120;startY = 20; + render16b(startX,startY,16,96,0,0x001DC1FF);// water pit + render16b(startX+16,startY,32,96,0,0x001DC1FF); + render16b(startX,startY+16,48,96,0,0x001DC1FF); + render16b(startX+16,startY+16,64,96,0,0x001DC1FF); + renderc (startX+8,startY+12,48,160,16,8,0);//Waves + renderc (startX+8,startY+8,0,112,16,8,0);//Player (Top-Half) + startX = 110;startY = 76; + render16 (startX,startY,48,112,0);//Player + renderc (startX+12,startY,40,160,8,16,0);//Slash + render (startX+14,startY+4,152,144,0);//Pickaxe + render16b(startX+18,startY,80,0,0,0xDCC6AEFF);//Iron ore + startX = 40;startY = 90; + render16b (startX,startY,128,112,0,0xADFFADFF);//Slime + render16 (startX+18,startY,48,112,1);//Player (Mirrored) + renderc (startX+14,startY,32,160,8,16,0);//Slash + render (startX+12,startY+4,104,144,1);//Sword + startX = 64;startY = 40; + render16b(startX,startY,16,80,0,0x69B569FF);// grass pit + render16b(startX+16,startY,32,80,0,0x69B569FF); + render16b(startX,startY+16,48,80,0,0x69B569FF); + render16b(startX+16,startY+16,64,80,0,0x69B569FF); + render16 (startX+8,startY+4,0,16,0);//Tree + render (startX+1,startY+14,80,152,0);// Apple + render16 (startX+9,startY+18,16,112,0);//Player + renderc (startX+9,startY+14,16,160,16,8,0);//Slash + drawTextColor("Play minicraft",24,24,0xFFFF7FFF); + break; + case 1: // "How To Play" + startX = 72;startY = 54; + render16(startX,startY,96,208,0);//C-PAD + startX = 72;startY = 37; + render16(startX,startY-16,16,112,0);//Player + render16(startX,startY,112,208,0);//C-PAD up + startX = 72;startY = 71; + render16(startX,startY+16,0,112,0);//Player + render16(startX,startY,144,208,0);//C-PAD down + startX = 39;startY = 54; + render16(startX,startY,48,112,1);//Player + render16(startX+16,startY,128,208,0);//C-PAD left + startX = 89;startY = 54; + render16(startX+16,startY,48,112,0);//Player + render16(startX,startY,160,208,0);//C-PAD right + + drawTextColor("Learn the basics",64,24,0xFFFF7FFF); + break; + case 2: // "Settings" + break; + case 3: // "About" + break; + } + sf2d_end_frame(); + break; + + + } + +} diff --git a/source/Menu.h b/source/Menu.h new file mode 100644 index 0000000..15a5b54 --- /dev/null +++ b/source/Menu.h @@ -0,0 +1,12 @@ +#pragma once + +#include <3ds.h> +#include +#include +#include +#include + +#include "Render.h" + +void renderMenu(int menu,int xscr,int yscr); +void tickMenu(int menu); diff --git a/source/Render.c b/source/Render.c new file mode 100644 index 0000000..9f22301 --- /dev/null +++ b/source/Render.c @@ -0,0 +1,826 @@ +#include "Render.h" + +void render(s32 xp, s32 yp, u32 xTile, u32 yTile, u8 bits){ + xp-=offsetX; + yp-=offsetY; + int scaleX = 2, scaleY = 2; + if((bits & 1) > 0){scaleX=-2;xp+=8;} + if((bits & 2) > 0){scaleY=-2;yp+=8;} + sf2d_draw_texture_part_scale(icons,xp<<1,yp<<1,xTile,yTile,8,8,scaleX,scaleY); +} +void renderb(s32 xp, s32 yp, u32 xTile, u32 yTile, u8 bits, u32 color){ + xp-=offsetX; + yp-=offsetY; + int scaleX = 2, scaleY = 2; + if((bits & 1) > 0){scaleX=-2;xp+=8;} + if((bits & 2) > 0){scaleY=-2;yp+=8;} + sf2d_draw_texture_part_scale_blend(icons,xp<<1,yp<<1,xTile,yTile,8,8,scaleX,scaleY,color); +} +void renderr(s32 xp, s32 yp, u32 xTile, u32 yTile, u8 bits, float rotate){ + xp-=offsetX; + yp-=offsetY; + int scaleX = 2, scaleY = 2; + if((bits & 1) > 0){scaleX=-2;xp+=8;} + if((bits & 2) > 0){scaleY=-2;yp+=8;} + sf2d_draw_texture_part_rotate_scale(icons,xp<<1,yp<<1,rotate,xTile,yTile,8,8,scaleX,scaleY); +} +void renderc(s32 xp, s32 yp, u32 xTile, u32 yTile,int sizeX, int sizeY, u8 bits){ + xp-=offsetX; + yp-=offsetY; + int scaleX = 2, scaleY = 2; + if((bits & 1) > 0){scaleX=-2;xp+=sizeX;} + if((bits & 2) > 0){scaleY=-2;yp+=sizeY;} + sf2d_draw_texture_part_scale(icons,xp<<1,yp<<1,xTile,yTile,sizeX,sizeY,scaleX,scaleY); +} +void renderbc(s32 xp, s32 yp, u32 xTile, u32 yTile,int sizeX, int sizeY, u8 bits, u32 color){ + xp-=offsetX; + yp-=offsetY; + int scaleX = 2, scaleY = 2; + if((bits & 1) > 0){scaleX=-2;xp+=sizeX;} + if((bits & 2) > 0){scaleY=-2;yp+=sizeY;} + sf2d_draw_texture_part_scale_blend(icons,xp<<1,yp<<1,xTile,yTile,sizeX,sizeY,scaleX,scaleY,color); +} +void render16(s32 xp, s32 yp, u32 xTile, u32 yTile, u8 bits){ + xp-=offsetX; + yp-=offsetY; + int scaleX = 2, scaleY = 2; + if((bits & 1) > 0){scaleX=-2;xp+=16;} + if((bits & 2) > 0){scaleY=-2;yp+=16;} + sf2d_draw_texture_part_scale(icons,xp<<1,yp<<1,xTile,yTile,16,16,scaleX,scaleY); +} + + +void render16c(s32 xp, s32 yp, u32 xTile, u32 yTile, u8 bits,float scaleX, float scaleY){ + xp-=offsetX; + yp-=offsetY; + if((bits & 1) > 0){scaleX=-scaleX;xp+=16;} + if((bits & 2) > 0){scaleY=-scaleY;yp+=16;} + sf2d_draw_texture_part_scale(icons,xp*scaleX,yp*scaleY,xTile,yTile,16,16,scaleX,scaleY); +} + +void render16b(s32 xp, s32 yp, u32 xTile, u32 yTile, u8 bits,u32 color){ + xp-=offsetX; + yp-=offsetY; + int scaleX = 2, scaleY = 2; + if((bits & 1) > 0){scaleX=-2;xp+=16;} + if((bits & 2) > 0){scaleY=-2;yp+=16;} + sf2d_draw_texture_part_scale_blend(icons,xp<<1,yp<<1,xTile,yTile,16,16,scaleX,scaleY,color); +} +void render16s(s32 xp, s32 yp, u32 tile, u8 bits,u32 color){ + xp-=offsetX; + yp-=offsetY; + int xTile = tile & 255; + int yTile = tile >> 8; + int scaleX = 2, scaleY = 2; + if((bits & 1) > 0){scaleX=-2;xp+=16;} + if((bits & 2) > 0){scaleY=-2;yp+=16;} + sf2d_draw_texture_part_scale_blend(icons,xp<<1,yp<<1,xTile,yTile,16,16,scaleX,scaleY,color); +} + +void renderTitle(int x, int y){ + sf2d_draw_texture_part_scale(icons,(x-26)<<1,y<<1,0,240,104,16,2.0,2.0); // MINICRAFT + sf2d_draw_texture_part(icons,x+48,y+52,104,240,152,16); // 3DS HOMEBREW EDITION +} + +void renderButtonIcon(u32 keyIcon, int x, int y, float scale){ + switch(keyIcon){ + case CIRCLEPAD: render16c(x,y,96,208,0,scale,scale); break; + case KEY_CPAD_UP: render16c(x,y,112,208,0,scale,scale); break; + case KEY_CPAD_LEFT: render16c(x,y,128,208,0,scale,scale); break; + case KEY_CPAD_DOWN: render16c(x,y,144,208,0,scale,scale); break; + case KEY_CPAD_RIGHT: render16c(x,y,160,208,0,scale,scale); break; + + /* New 3DS only */ + case CSTICK: render16c(x,y,176,208,0,scale,scale); break; + case KEY_CSTICK_UP: render16c(x,y,192,208,0,scale,scale); break; + case KEY_CSTICK_LEFT: render16c(x,y,208,208,0,scale,scale); break; + case KEY_CSTICK_DOWN: render16c(x,y,224,208,0,scale,scale); break; + case KEY_CSTICK_RIGHT: render16c(x,y,240,208,0,scale,scale); break; + + case KEY_A: render16c(x,y,0,224,0,scale,scale); break; + case KEY_B: render16c(x,y,16,224,0,scale,scale); break; + case KEY_X: render16c(x,y,32,224,0,scale,scale); break; + case KEY_Y: render16c(x,y,48,224,0,scale,scale); break; + case KEY_DUP: render16c(x,y,64,224,0,scale,scale); break; + case KEY_DLEFT: render16c(x,y,80,224,0,scale,scale); break; + case KEY_DDOWN: render16c(x,y,96,224,0,scale,scale); break; + case KEY_DRIGHT: render16c(x,y,112,224,0,scale,scale); break; + case KEY_START: + render16c(x-8,y,128,224,0,scale,scale); + render16c(x+8,y,144,224,0,scale,scale); + break; + case KEY_SELECT: + render16c(x-8,y,160,224,0,scale,scale); + render16c(x+8,y,176,224,0,scale,scale); + break; + case KEY_L: render16c(x,y,192,224,0,scale,scale); break; + case KEY_R: render16c(x,y,208,224,0,scale,scale); break; + + /* New 3DS only */ + case KEY_ZL: render16c(x,y,224,224,0,scale,scale); break; + case KEY_ZR: render16c(x,y,240,224,0,scale,scale); break; + } +} + +int getFrame(int a, int b, int s){ + return (a == s) ? 0 : ((a < b-1) ? 8 : 16); +} + +void renderFrame(int x1, int y1, int x2, int y2, u32 bgColor){ + int startX = x1; + int startY = y1; + sf2d_draw_rectangle((x1<<4)+4-(offsetX<<1),(y1<<4)+4-(offsetY<<1),((x2-x1)<<4)-8,((y2-y1)<<4)-8, bgColor); + while(x1 < x2){ + y1 = startY; + while(y1 < y2){ + int xp=(x1<<4)-(offsetX<<1); + int yp=(y1<<4)-(offsetY<<1); + sf2d_draw_texture_part_scale(icons,xp,yp,getFrame(x1,x2,startX),200+getFrame(y1,y2,startY),8,8,2.0,2.0); + ++y1; + } + ++x1; + } +} + +void renderDotsWithColor(int val, int x, int y,u8 bits, u32 color){ + switch(val){ + case 3: renderb(x,y,0,0,bits,color); return; + case 5: renderb(x+8,y,8,0,bits,color); return; + case 7: renderbc(x,y,0,0,16,8,bits,color); return; + case 10: renderb(x,y+8,0,8,bits,color); return; + case 11: renderbc(x,y,0,0,8,16,bits,color); return; + case 12: renderb(x+8,y+8,8,8,bits,color); return; + case 13: renderbc(x+8,y,8,0,8,16,bits,color); return; + case 14: renderbc(x,y+8,0,8,16,8,bits,color); return; + case 15: render16b(x,y,0,0,bits,color); return; + } +} + +void renderRockDotsWithColor(int val, int x, int y, u32 color){ + switch(val){ + case 208: render16b(x,y,0,0,0,color); return; + case 16: renderb(x+8,y+8,8,8,0,color); return; + case 32: renderb(x,y+8,0,8,0,color); return; + case 48: renderb(x,y+8,0,8,0,color); return; + case 64: renderb(x,y,0,0,0,color); return; + case 80: renderbc(x,y,0,0,8,16,0,color);return; + case 96: renderbc(x+8,y,8,0,8,16,0,color);return; + case 112: renderbc(x,y+8,0,8,16,8,0,color);return; + case 128: renderbc(x,y,0,0,16,8,0,color);return; + case 144: renderb(x,y+8,0,8,0,color);renderbc(x+8,y,8,0,8,16,0,color); return; + case 160: renderb(x,y,0,0,0,color);renderbc(x+8,y,8,0,8,16,0,color); return; + case 176: renderb(x+8,y,8,0,0,color);renderbc(x,y,0,0,8,16,0,color); return; + case 192: renderb(x+8,y+8,8,8,0,color);renderbc(x,y,0,0,8,16,0,color); return; + case 4112: renderbc(x,y,0,0,8,16,0,color);return; + case 4128: renderbc(x,y+8,0,8,8,16,0,color);return; + case 4192: renderb(x,y,0,0,0,color); return; + case 8192: renderb(x,y+8,0,8,0,color); return; + case 8208: renderb(x+8,y+8,8,8,0,color); return; + case 8224: renderb(x+8,y+8,8,8,0,color); return; + case 8240: renderb(x+8,y,8,0,0,color); return; + case 8256: renderb(x,y+8,0,8,0,color); return; + case 8272: renderb(x+8,y,8,0,0,color); return; + case 8288: renderb(x,y,0,0,0,color); return; + case 8304: renderb(x+8,y,8,0,0,color);renderb(x,y+8,0,8,0,color); return; + case 8320: renderbc(x+8,y,8,0,8,16,0,color);return; + case 8336: renderbc(x,y,0,0,8,16,0,color);return; + case 8352: renderb(x,y,0,0,0,color); renderb(x+8,y+8,8,8,0,color); return; + case 8368: renderb(x+8,y,8,0,0,color); return; + case 8384: renderb(x,y+8,0,8,0,color); return; + case 8400: renderb(x,y,0,0,0,color); return; + case 8416: renderb(x+8,y+8,8,8,0,color); return; + } +} + +void renderLights(){ + renderLight(player.x,player.y,4*8); + int i; + for(i = 0; i < eManager.lastSlot[currentLevel]; ++i){ + Entity e = eManager.entities[currentLevel][i]; + if(e.type != ENTITY_FURNITURE) continue; + if(e.x > player.x-160 && e.y > player.y-125 && e.x 200) x1 = 200; + if (y1 > 120) y1 = 120; + + int xx, yy; + for (yy = y0; yy < y1; yy++) { + int yd = yy - y; + yd = yd * yd; + for (xx = x0; xx < x1; xx++) { + int xd = xx - x; + int dist = xd * xd + yd; + if (dist <= r * r) sf2d_set_pixel(lightScreen, xx, yy, 0); // set transparent pixel + } + } +} + +void renderLightScreen(){ + sf2d_draw_texture_scale(lightScreen,0,0,2.0,2.0); +} + +u8 checkSurrTiles8(int xt, int yt, int id){ + u8 vt = 0; + if(getTile(xt,yt-1) == id) vt |= 1; + if(getTile(xt-1,yt) == id) vt |= 2; + if(getTile(xt+1,yt) == id) vt |= 4; + if(getTile(xt,yt+1) == id) vt |= 8; + if(getTile(xt-1,yt-1) == id) vt |= 16; + if(getTile(xt+1,yt-1) == id) vt |= 32; + if(getTile(xt-1,yt+1) == id) vt |= 64; + if(getTile(xt+1,yt+1) == id) vt |= 128; + return vt; +} +u8 checkSurrTiles4(int xt, int yt, int id){ + u8 vt = 0; + if(getTile(xt,yt-1) == id) vt |= 1; + if(getTile(xt-1,yt) == id) vt |= 2; + if(getTile(xt+1,yt) == id) vt |= 4; + if(getTile(xt,yt+1) == id) vt |= 8; + return vt; +} + +u8 v = 0; +u8 tData = 0; +void renderTile(int i, int x, int y){ + int age=0; + switch(i){ + case TILE_GRASS: + v = checkSurrTiles4(x >> 4,y >> 4, TILE_GRASS) + | checkSurrTiles4(x >> 4,y >> 4, TILE_TREE) + | checkSurrTiles4(x >> 4,y >> 4, TILE_FLOWER) + | checkSurrTiles4(x >> 4,y >> 4, TILE_SAPLING_TREE); + render16b(x,y,grassTable[v],80,0,0x69B569FF); + renderDotsWithColor(v, x, y, 0, 0x8ED38EFF); + break; + case TILE_TREE: + render16(x,y,treeTable[checkSurrTiles8(x >> 4,y >> 4, TILE_TREE)],16,0); + break; + case TILE_ROCK: + v = checkSurrTiles8(x >> 4,y >> 4, TILE_ROCK); + render16s(x,y,rockTable[v]+8192,0,0xFFFFFFFF); + renderRockDotsWithColor(rockTable[v], x, y, 0x949494FF); + break; + case TILE_HARDROCK: + v = checkSurrTiles8(x >> 4,y >> 4, TILE_HARDROCK); + render16s(x,y,rockTable[v]+8192,0,0xCFCFFFFF); + renderRockDotsWithColor(rockTable[v], x, y, 0x9494FFFF); + break; + case TILE_DIRT: + if(currentLevel>1)render16b(x,y,0,0,0,0x383838FF); + else render16b(x,y,0,0,0,0xA88F8FFF); + break; + case TILE_SAND: + v = checkSurrTiles4(x >> 4,y >> 4, TILE_SAND) + | checkSurrTiles4(x >> 4,y >> 4, TILE_CACTUS) + | checkSurrTiles4(x >> 4,y >> 4, TILE_SAPLING_CACTUS); + render16b(x,y,grassTable[v],80,0,0xF7F77BFF); + renderDotsWithColor(v, x, y, 0, 0xB7B75BFF); + break; + case TILE_WATER: + v = checkSurrTiles4(x >> 4,y >> 4, TILE_WATER)|checkSurrTiles4(x >> 4,y >> 4, TILE_HOLE); + render16b(x,y,grassTable[v],96,0,0x001DC1FF); + srand((tickCount + (x / 2 - y) * 4311)/10); + renderDotsWithColor(v, x, y, rand()&3, 0x6B6BFFFF); + break; + case TILE_LAVA: + v = checkSurrTiles4(x >> 4,y >> 4, TILE_LAVA)|checkSurrTiles4(x >> 4,y >> 4, TILE_HOLE); + render16b(x,y,grassTable[v],96,0,0xC11D00FF); + srand((tickCount + (x / 2 - y) * 4311)/10); + renderDotsWithColor(v, x, y, rand()&3, 0xFF6B6BFF); + break; + case TILE_HOLE: + render16b(x,y,grassTable[ + checkSurrTiles4(x >> 4,y >> 4, TILE_HOLE)| + checkSurrTiles4(x >> 4,y >> 4, TILE_WATER)| + checkSurrTiles4(x >> 4,y >> 4, TILE_LAVA) + ],96,0,0x383838FF); + break; + case TILE_CACTUS: + render16(x,y,48,0,0); + break; + case TILE_FLOWER: + render16(x,y,64,0,getData(x>>4,y>>4)); + break; + case TILE_STAIRS_DOWN: + if(currentLevel == 0) renderTile(TILE_CLOUD,x,y); + render16(x,y,96,0,0); + break; + case TILE_STAIRS_UP: + render16(x,y,112,0,0); + break; + case TILE_IRONORE: + render16b(x,y,80,0,0,0xDFC8C8FF); + break; + case TILE_GOLDORE: + render16b(x,y,80,0,0,0xE5E8B9FF); + break; + case TILE_GEMORE: + render16b(x,y,80,0,0,0xDF98DEFF); + break; + case TILE_CLOUD: + render16b(x,y,grassTable[checkSurrTiles4(x >> 4,y >> 4, TILE_CLOUD) + | checkSurrTiles4(x >> 4,y >> 4, TILE_STAIRS_DOWN) + | checkSurrTiles4(x >> 4,y >> 4, TILE_CLOUDCACTUS)],80,0,0xFFFFFFFF); + break; + case TILE_CLOUDCACTUS: + renderTile(TILE_CLOUD,x,y); + render16(x,y,80,0,0); + break; + case TILE_SAPLING_TREE: + renderTile(TILE_GRASS,x,y); + render16(x,y,32,0,0); + break; + case TILE_SAPLING_CACTUS: + renderTile(TILE_SAND,x,y); + render16(x,y,32,0,0); + break; + case TILE_FARM: + render16(x,y,144,0,0); + break; + case TILE_WHEAT: + age = getData(x>>4,y>>4)/20; + if(age > 5) age = 5; + render16(x,y,160+(age<<4),0,0); + break; + } + +} + +void renderGui(){ + int i; + for(i=0;i<10;++i){ + if(i < player.p.health) render(i*8+1,1,168,152,0); else render(i*8+1,1,176,152,0); + if(i < player.p.stamina) render(i*8+1,10,184,152,0); else render(i*8+1,10,191,152,0); + } +} + +void renderPlayer(){ + if(player.p.isDead) return; + int xo = player.x - 8; + int yo = player.y - 8; + + if(player.p.attackTimer > 0 && player.p.dir == 1){ + renderc(xo,yo-4,16,160,16,8,0); + renderItemIcon(player.p.activeItem->id,player.p.activeItem->countLevel,xo+4,yo-4); + } + u8 walk = (player.p.walkDist >> 4) & 1; + bool swimming = isSwimming(); + switch(player.p.dir){ + case 0: // down + if(swimming)renderc(xo,yo+4,48,160+(((player.p.swimTimer>>4)&1)<<3),16,8,0); + else renderc(xo,yo+8,0,120+(player.p.isCarrying?16:0),16,8,walk==0?0:1); + renderc(xo,yo,0,112+(player.p.isCarrying?16:0),16,8,walk==0?0:1); + break; + case 1: // up + if(swimming)renderc(xo,yo+4,48,160+(((player.p.swimTimer>>4)&1)<<3),16,8,0); + else renderc(xo,yo+8,16,120+(player.p.isCarrying?16:0),16,8,walk==0?0:1); + renderc(xo,yo,16,112+(player.p.isCarrying?16:0),16,8,walk==0?0:1); + break; + case 2: // left + if(swimming)renderc(xo,yo+4,48,160+(((player.p.swimTimer>>4)&1)<<3),16,8,0); + else renderc(xo,yo+8,32+(walk<<4),120+(player.p.isCarrying?16:0),16,8,1); + renderc(xo,yo,32+(walk<<4),112+(player.p.isCarrying?16:0),16,8,1); + break; + case 3: // right + if(swimming)renderc(xo,yo+4,48,160+(((player.p.swimTimer>>4)&1)<<3),16,8,0); + else renderc(xo,yo+8,32+(walk<<4),120+(player.p.isCarrying?16:0),16,8,0); + renderc(xo,yo,32+(walk<<4),112+(player.p.isCarrying?16:0),16,8,0); + break; + } + + if(player.p.isCarrying){ + renderFurniture(player.p.activeItem->id, xo, yo-12); + } + + if(player.p.attackTimer > 0){ + switch(player.p.dir){ + case 0: + renderc(xo-player.p.ax,yo-player.p.ay+12,16,168,16,8,0); + renderItemIcon(player.p.activeItem->id,player.p.activeItem->countLevel, xo+4,yo+12); + break; + case 2: + renderc(xo-player.p.ax-4,yo-player.p.ay,32,160,8,16,0); + renderItemIcon(player.p.activeItem->id,player.p.activeItem->countLevel, xo-4,yo+4); + break; + case 3: + renderc(xo-player.p.ax+12,yo-player.p.ay,40,160,8,16,0); + renderItemIcon(player.p.activeItem->id,player.p.activeItem->countLevel, xo+12,yo+4); + break; + } + } +} + +void renderBackground(int xScroll, int yScroll){ + int xo = xScroll >> 4; + int yo = yScroll >> 4; + int x,y; + for (x = xo;x <= 13 + xo; ++x) { + for (y = yo;y <= 8 + yo; ++y)renderTile(getTile(x,y),x << 4,y << 4); + } +} + +char * fontChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789.,!?'\"-+=/\\%()<>:; "; + +void drawText(char * msg, u32 x, u32 y){ + int i = 0; + x-=offsetX<<1; + y-=offsetY<<1; + for(i = 0; i> 5; + if (ix >= 0) { + sf2d_draw_texture_part(font,x + i * 12,y,(ix&31)*12,16+(iy*12),12,12); + } + } +} + +void drawSizedText(char * msg, u32 x, u32 y, float size){ + int i = 0; + for(i = 0; i> 5; + if (ix >= 0) { + sf2d_draw_texture_part_scale(font,(x + i * 8) * size,y,(ix&31)<<3,iy<<3,8,8,size,size); + } + } +} + +void drawTextColor(char * msg, u32 x, u32 y, u32 color){ + int i = 0; + x-=offsetX<<1; + y-=offsetY<<1; + for(i = 0; i> 5; + if (ix >= 0) { + sf2d_draw_texture_part_blend(font,x + i * 12,y,(ix&31)*12,16+(iy*12),12,12, color); + } + } +} + +// Changes text color after the first space. +void drawTextColorSpecial(char * msg, u32 x, u32 y, u32 color, u32 color2){ + int i = 0; + x-=offsetX<<1; + y-=offsetY<<1; + bool sOver = false; + for(i = 0; i> 5; + if (ix >= 0) { + if(sOver)sf2d_draw_texture_part_blend(font,x + i * 12,y,(ix&31)*12,16+(iy*12),12,12, color2); + else sf2d_draw_texture_part_blend(font,x + i * 12,y,(ix&31)*12,16+(iy*12),12,12, color); + } + } +} + +void drawSizedTextColor(char * msg, int x, int y, float size, u32 color){ + int i; + for(i = 0; i> 5; + if (ix >= 0) sf2d_draw_texture_part_scale_blend(font,(x + i * 8) * size,y*size,(ix&31)<<3,iy<<3,8,8,size,size,color); + } +} + +void renderFurniture(int itemID, int x, int y){ + switch(itemID){ + case ITEM_ANVIL: render16(x,y,64,128,0); break; + case ITEM_CHEST: render16(x,y,80,128,0); break; + case ITEM_OVEN: render16(x,y,96,128,0); break; + case ITEM_FURNACE: render16(x,y,112,128,0); break; + case ITEM_WORKBENCH: render16(x,y,128,128,0); break; + case ITEM_LANTERN: render16(x,y,144,128,0); break; + } +} + +char ertxt[20]; +void renderEntity(Entity* e, int x, int y){ + switch(e->type){ + case ENTITY_ITEM: + if (e->entityItem.age >= 520) if (e->entityItem.age / 6 % 2 == 0) return; + renderItemIcon2(e->entityItem.item.id,e->entityItem.item.countLevel, x-4, y-4, (int)e->entityItem.zz); + break; + case ENTITY_FURNITURE: renderFurniture(e->entityFurniture.itemID, x-8, y-8); break; + case ENTITY_ZOMBIE: + switch(e->zombie.dir){ + case 0: // down + render16b(x-8,y-8,64,112,((e->zombie.walkDist>>4)&1)==0?0:1,e->zombie.color); + break; + case 1: // up + render16b(x-8,y-8,80,112,((e->zombie.walkDist>>4)&1)==0?0:1,e->zombie.color); + break; + case 2: // left + render16b(x-8,y-8,96+(((e->zombie.walkDist>>4)&1)<<4),112,1,e->zombie.color); + break; + case 3: // right + render16b(x-8,y-8,96+(((e->zombie.walkDist>>4)&1)<<4),112,0,e->zombie.color); + break; + } + break; + case ENTITY_SLIME: + render16b(x-8,y-8-(e->slime.jumpTime>0?4:0),128+(e->slime.jumpTime>0?16:0),112,0,e->slime.color); + break; + case ENTITY_AIRWIZARD: + e->wizard.spriteAdjust = 0; + if(e->wizard.health < 200){ if (tickCount / 4 % 3 < 2) e->wizard.spriteAdjust = 16; } + else if(e->wizard.health < 1000){ if (tickCount / 5 % 4 < 2) e->wizard.spriteAdjust = 16; } + switch(e->wizard.dir){ + case 0: // down + render16(x-8,y-8,160,112+e->wizard.spriteAdjust,((e->wizard.walkDist>>4)&1)==0?0:1); + break; + case 1: // up + render16(x-8,y-8,176,112+e->wizard.spriteAdjust,((e->wizard.walkDist>>4)&1)==0?0:1); + break; + case 2: // left + render16(x-8,y-8,192+(((e->wizard.walkDist>>4)&1)<<4),112+e->wizard.spriteAdjust,1); + break; + case 3: // right + render16(x-8,y-8,192+(((e->wizard.walkDist>>4)&1)<<4),112+e->wizard.spriteAdjust,0); + break; + } + break; + case ENTITY_TEXTPARTICLE: + x-=offsetX; + y-=offsetY; + drawSizedTextColor(e->textParticle.text,x+1,y-(int)e->textParticle.zz+1,2,0xFF); + drawSizedTextColor(e->textParticle.text,x,y-(int)e->textParticle.zz,2,e->textParticle.color); + break; + case ENTITY_SMASHPARTICLE: render16(x,y,0,160,0); break; + case ENTITY_SPARK: + if (e->spark.age >= 200) if (e->spark.age / 6 % 2 == 0) return; + renderr(x,y,200,152,0,e->spark.age*0.0349); + break; + } +} + +void renderEntities(int x, int y, EntityManager* em){ + int i; + for(i = 0; i < em->lastSlot[currentLevel]; ++i){ + Entity e = em->entities[currentLevel][i]; + if(e.x > x-200 && e.y > y-125 && e.xlastSlot; + if (i1 > h) i1 = h; + int io = selected - h / 2; + if (io > inv->lastSlot - h) io = inv->lastSlot - h; + if (io < 0) io = 0; + + int i; + for(i = 0; i < i1; ++i) renderItemWithText(&inv->items[i+io], (1 + xo) << 4, (i + 1 + yo) << 4); + + if(drawCursor){ + int yy = selected + 1 - io + yo; + sf2d_draw_rectangle((xo<<4)-(offsetX<<1),(yy<<4)-(offsetY<<1),12,12,0xFF); + drawText(">", (xo << 4), yy << 4); + sf2d_draw_rectangle(((xo+w)<<4)-12-(offsetX<<1),(yy<<4)-(offsetY<<1),12,12,0xFF); + drawText("<", ((xo+w)<<4)-12, yy<<4); + } +} + +void renderRecipes(RecipeManager * r,int xo, int yo, int x1, int y1, int selected){ + int size = r->size; + if(size < 1) return; + if(selected < 0) selected = 0; + int w = x1 - xo; + int h = y1 - yo - 2; + int i1 = size; + if (i1 > h) i1 = h; + int io = selected - h / 2; + if (io > size - h) io = size - h; + if (io < 0) io = 0; + + 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); + if(r->recipes[i+io].canCraft) col = 0xFFFFFFFF; + else col = 0x7F7F7FFF; + drawTextColor(getBasicItemName(r->recipes[i+io].itemResult,r->recipes[i+io].itemAmountLevel),x+18,y+2,col); + } + + int yy = selected + 1 - io + yo; + sf2d_draw_rectangle(xo<<4,yy<<4,12,12,0xFF); + drawText(">", xo << 4, yy << 4); + sf2d_draw_rectangle(((xo+w)<<4)-12,yy<<4,12,12,0xFF); + drawText("<", ((xo+w)<<4)-12, yy<<4); +} + +void renderItemWithText(Item* item, int x, int y){ + renderItemIcon(item->id,item->countLevel,x>>1,y>>1); + if(item->onlyOne) drawText(getItemName(item->id,item->countLevel),x+18,y+2); + else drawTextColorSpecial(getItemName(item->id,item->countLevel),x+18,y+2,0xD2D2D2FF,0xFFFFFFFF); +} + +void renderItemIcon2(int itemID, int countLevel, int x, int y, int z){ + switch(itemID){ + case ITEM_NULL: return; + case TOOL_SHOVEL: renderb(x,y,countLevel*8,144,0,0xFF); break; + case TOOL_HOE: renderb(x,y,40+countLevel*8,144,0,0xFF); break; + case TOOL_SWORD: renderb(x,y,80+countLevel*8,144,0,0xFF); break; + case TOOL_PICKAXE: renderb(x,y,120+countLevel*8,144,0,0xFF); break; + case TOOL_AXE: renderb(x,y,160+countLevel*8,144,0,0xFF); break; + case ITEM_ANVIL: renderb(x,y,120,152,0,0xFF); break; + case ITEM_CHEST: renderb(x,y,128,152,0,0xFF); break; + case ITEM_OVEN: renderb(x,y,136,152,0,0xFF); break; + case ITEM_FURNACE: renderb(x,y,144,152,0,0xFF); break; + case ITEM_WORKBENCH: renderb(x,y,152,152,0,0xFF); break; + case ITEM_LANTERN: renderb(x,y,160,152,0,0xFF); break; + case ITEM_POWGLOVE: renderb(x,y,56,152,0,0xFF); break; + case ITEM_FLOWER: renderb(x,y,0,152,0,0xFF); break; + case ITEM_WOOD: renderb(x,y,8,152,0,0xFF); break; + case ITEM_STONE: renderb(x,y,16,152,0,0xFF); break; + case ITEM_SAND: renderb(x,y,16,152,0,0xFF); break; + case ITEM_DIRT: renderb(x,y,16,152,0,0xFF); break; + case ITEM_CLOUD: renderb(x,y,16,152,0,0xFF); break; + case ITEM_ACORN: renderb(x,y,24,152,0,0xFF); break; + case ITEM_CACTUS: renderb(x,y,32,152,0,0xFF); break; + case ITEM_SEEDS: renderb(x,y,40,152,0,0xFF); break; + case ITEM_WHEAT: renderb(x,y,48,152,0,0xFF); break; + case ITEM_FLESH: renderb(x,y,64,152,0,0xFF); break; + case ITEM_BREAD: renderb(x,y,72,152,0,0xFF); break; + case ITEM_APPLE: renderb(x,y,80,152,0,0xFF); break; + case ITEM_SLIME: renderb(x,y,88,152,0,0xFF); break; + case ITEM_COAL: renderb(x,y,88,152,0,0xFF); break; + case ITEM_IRONORE: renderb(x,y,88,152,0,0xFF); break; + case ITEM_GOLDORE: renderb(x,y,88,152,0,0xFF); break; + case ITEM_IRONINGOT: renderb(x,y,96,152,0,0xFF); break; + case ITEM_GOLDINGOT: renderb(x,y,96,152,0,0xFF); break; + case ITEM_GLASS: renderb(x,y,104,152,0,0xFF); break; + case ITEM_GEM: renderb(x,y,112,152,0,0xFF); break; + } + y-=z; + renderItemIcon(itemID, countLevel, x, y); +} + +void renderItemIcon(int itemID,int countLevel, int x, int y){ + switch(itemID){ + case ITEM_NULL: return; + case TOOL_SHOVEL: render(x,y,countLevel*8,144,0); break; + case TOOL_HOE: render(x,y,40+countLevel*8,144,0); break; + case TOOL_SWORD: render(x,y,80+countLevel*8,144,0); break; + case TOOL_PICKAXE: render(x,y,120+countLevel*8,144,0); break; + case TOOL_AXE: render(x,y,160+countLevel*8,144,0); break; + case ITEM_ANVIL: render(x,y,120,152,0); break; + case ITEM_CHEST: render(x,y,128,152,0); break; + case ITEM_OVEN: render(x,y,136,152,0); break; + case ITEM_FURNACE: render(x,y,144,152,0); break; + case ITEM_WORKBENCH: render(x,y,152,152,0); break; + case ITEM_LANTERN: render(x,y,160,152,0); break; + case ITEM_POWGLOVE: render(x,y,56,152,0); break; + case ITEM_FLOWER: render(x,y,0,152,0); break; + case ITEM_WOOD: render(x,y,8,152,0); break; + case ITEM_STONE: renderb(x,y,16,152,0,0xCFCFCFFF); break; + case ITEM_SAND: renderb(x,y,16,152,0,0xF7F77BFF); break; + case ITEM_DIRT: renderb(x,y,16,152,0,0xAF9781FF); break; + case ITEM_CLOUD: renderb(x,y,16,152,0,0xFFFFFFFF); break; + case ITEM_ACORN: render(x,y,24,152,0); break; + case ITEM_CACTUS: render(x,y,32,152,0); break; + case ITEM_SEEDS: render(x,y,40,152,0); break; + case ITEM_WHEAT: render(x,y,48,152,0); break; + case ITEM_FLESH: render(x,y,64,152,0); break; + case ITEM_BREAD: render(x,y,72,152,0); break; + case ITEM_APPLE: render(x,y,80,152,0); break; + case ITEM_SLIME: renderb(x,y,88,152,0,0x4DC04DFF); break; + case ITEM_COAL: renderb(x,y,88,152,0,0x383838FF); break; + case ITEM_IRONORE: renderb(x,y,88,152,0,0xBC9999FF); break; + case ITEM_GOLDORE: renderb(x,y,88,152,0,0xCECE77FF); break; + case ITEM_IRONINGOT: renderb(x,y,96,152,0,0xDFC8C8FF); break; + case ITEM_GOLDINGOT: renderb(x,y,96,152,0,0xEAEABCFF); break; + case ITEM_GLASS: render(x,y,104,152,0); break; + case ITEM_GEM: render(x,y,112,152,0); break; + } +} + +void defineTables(){ + + int i = 0; + for(i = 256;i > 0;--i){ + // Creates the lookup table for the tree tile. + if((i&255)==255) treeTable[i] = 208; + else if((i&239)==239) treeTable[i] = 144; + else if((i&191)==191) treeTable[i] = 160; + else if((i&127)==127) treeTable[i] = 176; + else if((i&223)==223) treeTable[i] = 192; + else if((i&206)==206) treeTable[i] = 112; + else if((i&55)==55) treeTable[i] = 128; + else if((i&173)==173) treeTable[i] = 96; + else if((i&91)==91) treeTable[i] = 80; + + else if((i&159)==159) treeTable[i] = 224; + else if((i&111)==111) treeTable[i] = 240; + + else if((i&19)==19) treeTable[i] = 64; + else if((i&37)==37) treeTable[i] = 48; + else if((i&74)==74) treeTable[i] = 32; + else if((i&140)==140) treeTable[i] = 16; + } + + /* + boolean up = i & 1 + boolean left = i & 2 + boolean right = i & 4 + boolean down = i & 8 + boolean up-left = i & 16 + boolean up-right = i & 32 + boolean down-left = i & 64 + boolean down-right = i & 128 + */ + + for(i = 256;i > 0;--i){ + // Creates the lookup table for the rock tile. + if((i&255)==255) rockTable[i] = 208; + else if((i&239)==239) rockTable[i] = 144; + else if((i&191)==191) rockTable[i] = 160; + else if((i&127)==127) rockTable[i] = 176; + else if((i&223)==223) rockTable[i] = 192; + else if((i&207)==207) rockTable[i] = 256*16+32; + else if((i&63)==63) rockTable[i] = 256*16+16; + else if((i&206)==206) rockTable[i] = 112; + else if((i&95)==95) rockTable[i] = 256*32+144; + else if((i&159)==159) rockTable[i] = 256*32+160; + else if((i&31)==31) rockTable[i] = 256*32+208; + else if((i&55)==55) rockTable[i] = 128; + else if((i&175)==175) rockTable[i] = 256*32+128; + else if((i&143)==143) rockTable[i] = 256*32+224; + else if((i&173)==173) rockTable[i] = 96; + else if((i&111)==111) rockTable[i] = 256*32+112; + else if((i&47)==47) rockTable[i] = 256*32+48; + else if((i&45)==45) rockTable[i] = 256*32+176; + else if((i&79)==79) rockTable[i] = 256*32+192; + else if((i&23)==23) rockTable[i] = 256*32+96; + else if((i&91)==91) rockTable[i] = 80; + else if((i&27)==27) rockTable[i] = 256*16+96; + else if((i&19)==19) rockTable[i] = 64; + else if((i&75)==75) rockTable[i] = 256*32; + else if((i&141)==141) rockTable[i] = 256*32+16; + else if((i&142)==142) rockTable[i] = 256*32+32; + else if((i&78)==78) rockTable[i] = 256*32+64; + else if((i&39)==39) rockTable[i] = 256*32+80; + else if((i&37)==37) rockTable[i] = 48; + else if((i&74)==74) rockTable[i] = 32; + else if((i&140)==140) rockTable[i] = 16; + else if((i&15)==15) rockTable[i] = 256*16+112; + else if((i&11)==11) rockTable[i] = 256*16+192; + else if((i&13)==13) rockTable[i] = 256*16+208; + else if((i&14)==14) rockTable[i] = 256*16+224; + else if((i&7)==7) rockTable[i] = 256*16+240; + else if((i&12)==12) rockTable[i] = 256*16+128; + else if((i&10)==10) rockTable[i] = 256*16+144; + else if((i&5)==5) rockTable[i] = 256*16+160; + else if((i&3)==3) rockTable[i] = 256*16+176; + else if((i&9)==9) rockTable[i] = 256*16; + else if((i&6)==6) rockTable[i] = 256*16+48; + else if((i&8)==8) rockTable[i] = 224; + else if((i&4)==4) rockTable[i] = 256*16+64; + else if((i&2)==2) rockTable[i] = 256*16+80; + else if((i&1)==1) rockTable[i] = 240; + + } + + // Lookup table for the grass/sand tile. + grassTable[1] = 192; + grassTable[2] = 160; + grassTable[3] = 64; + grassTable[4] = 144; + grassTable[5] = 48; + grassTable[6] = 224; + grassTable[7] = 128; + grassTable[8] = 176; + grassTable[9] = 240; + grassTable[10] = 32; + grassTable[11] = 80; + grassTable[12] = 16; + grassTable[13] = 96; + grassTable[14] = 112; + grassTable[15] = 208; + +} diff --git a/source/Render.h b/source/Render.h new file mode 100644 index 0000000..e4a5f78 --- /dev/null +++ b/source/Render.h @@ -0,0 +1,54 @@ +#pragma once +#include <3ds.h> +#include +#include +#include +#include +#include "Globals.h" +#include "MapGen.h" + +sf2d_texture *icons; +sf2d_texture *font; +sf2d_texture *lightScreen; +int offsetX, offsetY; + +void render(s32 xp, s32 yp, u32 xTile, u32 yTile, u8 bits); +void renderb(s32 xp, s32 yp, u32 xTile, u32 yTile, u8 bits, u32 color); +void renderr(s32 xp, s32 yp, u32 xTile, u32 yTile, u8 bits,float rotate); +void renderc(s32 xp, s32 yp, u32 xTile, u32 yTile,int sizeX, int sizeY, u8 bits); +void renderbc(s32 xp, s32 yp, u32 xTile, u32 yTile,int sizeX, int sizeY, u8 bits, u32 color); +void render16(s32 xp, s32 yp, u32 xTile, u32 yTile, u8 bits); +void render16c(s32 xp, s32 yp, u32 xTile, u32 yTile, u8 bits,float scaleX,float scaleY); +void render16b(s32 xp, s32 yp, u32 xTile, u32 yTile, u8 bits,u32 color); +void render16s(s32 xp, s32 yp, u32 tile, u8 bits,u32 color); + +void renderTitle(int x, int y); +void renderFrame(int x1, int y1, int x2, int y2, u32 bgColor); +void renderTile(int i, int x, int y); +void renderBackground(int xScroll, int yScroll); +void renderButtonIcon(u32 icon, int x, int y, float scale); + +void renderLights(); +void renderLight(int x, int y, int r); +void renderLightScreen(); + +void renderGui(); +void renderPlayer(); + +void drawText(char * msg, u32 x, u32 y); +void drawSizedText(char * msg, u32 x, u32 y, float size); +void drawTextColor(char * msg, u32 x, u32 y, u32 color); +void drawTextColorSpecial(char * msg, u32 x, u32 y, u32 color, u32 color2); +void drawSizedTextColor(char * msg, int x, int y, float size, u32 color); + +void renderFurniture(int itemID, int x, int y); +void renderEntity(Entity* e, int x, int y); +void renderEntities(int x, int y, EntityManager* em); + +void renderRecipes(RecipeManager * r,int xo, int yo, int x1, int y1, int selected); +void renderItemList(Inventory * inv,int xo, int yo, int x1, int y1, int selected); +void renderItemWithText(Item* item, int x, int y); +void renderItemIcon(int itemID, int countLevel, int x, int y); +void renderItemIcon2(int itemID, int countLevel, int x, int y, int z); + +void defineTables(); diff --git a/source/SaveLoad.c b/source/SaveLoad.c new file mode 100644 index 0000000..a8664f6 --- /dev/null +++ b/source/SaveLoad.c @@ -0,0 +1,251 @@ +#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_ITEM: + case ENTITY_FURNITURE: + count++; + break; + } + } + return count; +} + +bool entityIsImportant(Entity * e){ + switch(e->type){ + case ENTITY_AIRWIZARD: + case ENTITY_SLIME: + case ENTITY_ZOMBIE: + case ENTITY_ITEM: + case ENTITY_FURNITURE: + return true; + default: + return false; + } +} + +void saveCurrentWorld(char * filename, EntityManager * eManager, Entity * player, u8 * map, u8 * mapData){ + FILE * file = fopen(filename, "wb"); + int i,j; + + // Player Data + fwrite(&player->p.score, sizeof(int), 1, file); + fwrite(&player->p.hasWonSaved, sizeof(bool), 1, file); + fwrite(&player->p.health, sizeof(s16), 1, file); + fwrite(&player->x, sizeof(s16), 1, file); + fwrite(&player->y, sizeof(s16), 1, file); + fwrite(¤tLevel, sizeof(s8), 1, file); + + // Inventory Data + fwrite(&eManager->nextInv, sizeof(s16), 1, file); // write amount of inventories. + for(i = 0; i < eManager->nextInv; ++i) { + fwrite(&eManager->invs[i].lastSlot, sizeof(s16), 1, file); // write amount of items in inventory; + for(j = 0; j < eManager->invs[i].lastSlot; ++j) { + fwrite(&eManager->invs[i].items[j].id, sizeof(s16), 1, file); // write ID of item + fwrite(&eManager->invs[i].items[j].countLevel, sizeof(s16), 1, file); // write count/level of item + fwrite(&eManager->invs[i].items[j].onlyOne, sizeof(bool), 1, file); + if(eManager->invs[i].items[j].id == ITEM_CHEST){ + int invIndex = eManager->invs[i].items[j].chestPtr - eManager->invs; + fwrite(&invIndex, sizeof(int), 1, file); + } + } + } + + // Entity Data + for(i = 0; i < 5; ++i){ + int amount = calculateImportantEntites(eManager,i); + fwrite(&amount, sizeof(s16), 1, file); // read amount of entities in level. + for(j = 0; j < eManager->lastSlot[i]; ++j){ + if(!entityIsImportant(&eManager->entities[i][j])) continue; + fwrite(&eManager->entities[i][j].type, sizeof(s16), 1, file); // write entity's type ID + fwrite(&eManager->entities[i][j].x, sizeof(s16), 1, file); // write entity's x coordinate + fwrite(&eManager->entities[i][j].y, sizeof(s16), 1, file); // write entity's y coordinate + switch(eManager->entities[i][j].type){ + case ENTITY_AIRWIZARD: + fwrite(&eManager->entities[i][j].wizard.health, sizeof(s16), 1, file); + break; + case ENTITY_SLIME: + fwrite(&eManager->entities[i][j].slime.health, sizeof(s16), 1, file); + fwrite(&eManager->entities[i][j].slime.lvl, sizeof(s8), 1, file); + break; + case ENTITY_ZOMBIE: + fwrite(&eManager->entities[i][j].zombie.health, sizeof(s16), 1, file); + fwrite(&eManager->entities[i][j].zombie.lvl, sizeof(s8), 1, file); + break; + case ENTITY_ITEM: + fwrite(&eManager->entities[i][j].entityItem.item.id, sizeof(s16), 1, file); + fwrite(&eManager->entities[i][j].entityItem.item.countLevel, sizeof(s16), 1, file); + fwrite(&eManager->entities[i][j].entityItem.age, sizeof(s16), 1, file); + break; + case ENTITY_FURNITURE: + fwrite(&eManager->entities[i][j].entityFurniture.itemID, sizeof(s16), 1, file); + int invIndex = eManager->entities[i][j].entityFurniture.inv - eManager->invs; + fwrite(&invIndex, sizeof(int), 1, file); + break; + } + } + } + + // Map Data + fwrite(map, sizeof(u8), 128*128*5, file); // Map Tile IDs, 128*128*5 bytes = 80KB + fwrite(mapData, sizeof(u8), 128*128*5, file); // Map Tile Data (Damage done to trees/rocks, age of wheat & saplings, etc). 80KB + + fclose(file); +} + +int loadWorld(char * filename, EntityManager * eManager, Entity * player, u8 * map, u8 * mapData){ + FILE * file; + int i,j; + + if ((file = fopen(filename, "rb"))){ + + fread(&player->p.score, sizeof(int), 1, file); + fread(&player->p.hasWonSaved, sizeof(bool), 1, file); + fread(&player->p.health, sizeof(s16), 1, file); + fread(&player->x, sizeof(s16), 1, file); + fread(&player->y, sizeof(s16), 1, file); + fread(¤tLevel, sizeof(s8), 1, file); + + fread(&eManager->nextInv, sizeof(s16), 1, file); + for(i = 0; i < eManager->nextInv; ++i) { + fread(&eManager->invs[i].lastSlot, sizeof(s16), 1, file); // read amount of items in inventory; + for(j = 0; j < eManager->invs[i].lastSlot; ++j) { + fread(&eManager->invs[i].items[j].id, sizeof(s16), 1, file); // write ID of item + fread(&eManager->invs[i].items[j].countLevel, sizeof(s16), 1, file); // write count/level of item + fread(&eManager->invs[i].items[j].onlyOne, sizeof(bool), 1, file); + eManager->invs[i].items[j].invPtr = (int*)&eManager->invs[i]; // setup Inventory pointer + eManager->invs[i].items[j].slotNum = j; // setup slot number + if(eManager->invs[i].items[j].id == ITEM_CHEST){ // for chest item specifically. + int invIndex; + fread(&invIndex, sizeof(int), 1, file); + eManager->invs[i].items[j].chestPtr = (Inventory*)&eManager->invs[invIndex]; // setup chest inventory pointer. + } + } + } + + for(i = 0; i < 5; ++i){ + fread(&eManager->lastSlot[i], sizeof(s16), 1, file); // read amount of entities in level. + for(j = 0; j < eManager->lastSlot[i]; ++j){ + fread(&eManager->entities[i][j].type, sizeof(s16), 1, file); // read entity's type ID + fread(&eManager->entities[i][j].x, sizeof(s16), 1, file); // read entity's x coordinate + fread(&eManager->entities[i][j].y, sizeof(s16), 1, file); // read entity's y coordinate + eManager->entities[i][j].slotNum = j; + switch(eManager->entities[i][j].type){ + case ENTITY_SMASHPARTICLE: + eManager->entities[i][j].level = i; + eManager->entities[i][j].smashParticle.age = 300; + eManager->entities[i][j].canPass = true; + break; + case ENTITY_TEXTPARTICLE: + eManager->entities[i][j].level = i; + eManager->entities[i][j].canPass = true; + eManager->entities[i][j].textParticle.age = 59; + eManager->entities[i][j].textParticle.text = NULL; + eManager->entities[i][j].textParticle.xx = eManager->entities[i][j].x; + eManager->entities[i][j].textParticle.yy = eManager->entities[i][j].y; + eManager->entities[i][j].textParticle.zz = 2; + eManager->entities[i][j].textParticle.xa = 0; + eManager->entities[i][j].textParticle.ya = 0; + eManager->entities[i][j].textParticle.za = 0; + break; + case ENTITY_SPARK: + eManager->entities[i][j].level = i; + eManager->entities[i][j].spark.age = 300; + break; + case ENTITY_AIRWIZARD: + fread(&eManager->entities[i][j].wizard.health, sizeof(s16), 1, file); + eManager->entities[i][j].level = i; + eManager->entities[i][j].hurtTime = 0; + eManager->entities[i][j].xKnockback = 0; + eManager->entities[i][j].yKnockback = 0; + eManager->entities[i][j].wizard.dir = 0; + eManager->entities[i][j].wizard.attackDelay = 0; + eManager->entities[i][j].wizard.attackTime = 0; + eManager->entities[i][j].wizard.attackType = 0; + eManager->entities[i][j].wizard.xa = 0; + eManager->entities[i][j].wizard.ya = 0; + eManager->entities[i][j].xr = 4; + eManager->entities[i][j].yr = 3; + eManager->entities[i][j].canPass = true; + break; + case ENTITY_SLIME: + fread(&eManager->entities[i][j].slime.health, sizeof(s16), 1, file); + fread(&eManager->entities[i][j].slime.lvl, sizeof(s8), 1, file); + eManager->entities[i][j].level = i; + eManager->entities[i][j].hurtTime = 0; + eManager->entities[i][j].xKnockback = 0; + eManager->entities[i][j].yKnockback = 0; + eManager->entities[i][j].slime.xa = 0; + eManager->entities[i][j].slime.ya = 0; + eManager->entities[i][j].slime.dir = 0; + eManager->entities[i][j].xr = 4; + eManager->entities[i][j].yr = 3; + eManager->entities[i][j].canPass = false; + switch(eManager->entities[i][j].slime.lvl){ + case 2: eManager->entities[i][j].slime.color = 0xCC8282FF; break; + case 3: eManager->entities[i][j].slime.color = 0xEFEFEFFF; break; + case 4: eManager->entities[i][j].slime.color = 0x6262AAFF; break; + default: eManager->entities[i][j].slime.color = 0x95DB95FF; break; + } + break; + case ENTITY_ZOMBIE: + fread(&eManager->entities[i][j].zombie.health, sizeof(s16), 1, file); + fread(&eManager->entities[i][j].zombie.lvl, sizeof(s8), 1, file); + eManager->entities[i][j].level = i; + eManager->entities[i][j].hurtTime = 0; + eManager->entities[i][j].xKnockback = 0; + eManager->entities[i][j].yKnockback = 0; + eManager->entities[i][j].zombie.dir = 0; + eManager->entities[i][j].xr = 4; + eManager->entities[i][j].yr = 3; + eManager->entities[i][j].canPass = false; + switch(eManager->entities[i][j].zombie.lvl){ + case 2: eManager->entities[i][j].zombie.color = 0xCC8282FF; break; + case 3: eManager->entities[i][j].zombie.color = 0xEFEFEFFF; break; + case 4: eManager->entities[i][j].zombie.color = 0x6262AAFF; break; + default: eManager->entities[i][j].zombie.color = 0x95DB95FF; break; + } + break; + case ENTITY_ITEM: + //eManager->entities[i][j].entityItem.item = newItem(0,0); + fread(&eManager->entities[i][j].entityItem.item.id, sizeof(s16), 1, file); + fread(&eManager->entities[i][j].entityItem.item.countLevel, sizeof(s16), 1, file); + fread(&eManager->entities[i][j].entityItem.age, sizeof(s16), 1, file); + eManager->entities[i][j].level = i; + eManager->entities[i][j].entityItem.age = 0; + eManager->entities[i][j].xr = 3; + eManager->entities[i][j].yr = 3; + eManager->entities[i][j].canPass = false; + eManager->entities[i][j].entityItem.xx = eManager->entities[i][j].x; + eManager->entities[i][j].entityItem.yy = eManager->entities[i][j].y; + eManager->entities[i][j].entityItem.zz = 2; + eManager->entities[i][j].entityItem.xa = 0; + eManager->entities[i][j].entityItem.ya = 0; + eManager->entities[i][j].entityItem.za = 0; + break; + case ENTITY_FURNITURE: + fread(&eManager->entities[i][j].entityFurniture.itemID, sizeof(s16), 1, file); + int invIndex; + fread(&invIndex, sizeof(int), 1, file); + eManager->entities[i][j].entityFurniture.inv = &eManager->invs[invIndex]; + eManager->entities[i][j].xr = 3; + eManager->entities[i][j].yr = 3; + eManager->entities[i][j].canPass = false; + if(eManager->entities[i][j].entityFurniture.itemID == ITEM_LANTERN) eManager->entities[i][j].entityFurniture.r = 8; + break; + } + } + } + fread(map, sizeof(u8), 128*128*5, file); + fread(mapData, sizeof(u8), 128*128*5, file); + fclose(file); + return 0; + } + return 1; +} diff --git a/source/SaveLoad.h b/source/SaveLoad.h new file mode 100644 index 0000000..0468bf5 --- /dev/null +++ b/source/SaveLoad.h @@ -0,0 +1,7 @@ +#pragma once +#include +#include +#include "Entity.h" + +void saveCurrentWorld(char * filename, EntityManager * eManager, Entity * player, u8 * map, u8 * mapData); +int loadWorld(char * filename, EntityManager * eManager, Entity * player, u8 * map, u8 * mapData); diff --git a/source/Sound.c b/source/Sound.c new file mode 100644 index 0000000..f81da06 --- /dev/null +++ b/source/Sound.c @@ -0,0 +1,27 @@ +#include "Sound.h" + +void loadSound(Sound * snd, char * filename){ + FILE *file = fopen(filename, "rb"); + if(file != NULL){ + fseek(file, 0, SEEK_END); + snd->size = ftell(file); + fseek(file, 0, SEEK_SET); + snd->buffer = linearAlloc(snd->size); + fread(snd->buffer, 1, snd->size, file); + } + fclose(file); +} + +void playSound(Sound snd){ + csndPlaySound(8, SOUND_FORMAT_16BIT | SOUND_ONE_SHOT, 44100, 1, 0, snd.buffer, snd.buffer, snd.size); +} + +void freeSounds(){ + linearFree(snd_playerHurt.buffer); + linearFree(snd_playerDeath.buffer); + linearFree(snd_monsterHurt.buffer); + linearFree(snd_test.buffer); + linearFree(snd_pickup.buffer); + linearFree(snd_bossdeath.buffer); + linearFree(snd_craft.buffer); +} diff --git a/source/Sound.h b/source/Sound.h new file mode 100644 index 0000000..14fedae --- /dev/null +++ b/source/Sound.h @@ -0,0 +1,23 @@ +#pragma once +#include +#include +#include +#include <3ds.h> + + +typedef struct { + u32 size; + u8 * buffer; +} Sound; + +void loadSound(Sound * snd, char * filename); +void playSound(Sound snd); +void freeSounds(); + +Sound snd_playerHurt; +Sound snd_playerDeath; +Sound snd_monsterHurt; +Sound snd_test; +Sound snd_pickup; +Sound snd_bossdeath; +Sound snd_craft; diff --git a/source/main.c b/source/main.c new file mode 100644 index 0000000..f25251f --- /dev/null +++ b/source/main.c @@ -0,0 +1,279 @@ +#include <3ds.h> +#include +#include +#include +#include +#include +#include +#include + +#include "icons2_png.h" +#include "Font_png.h" + +#include "Globals.h" +#include "Render.h" +#include "MapGen.h" +#include "Menu.h" + + +void initMiniMap(bool loadUpWorld){ + int i,x,y; + for(i=0;i<5;++i){ + for(x=0;x < 128;++x){ + for(y=0;y < 128;++y){ + + if(!loadUpWorld){ // generate stairs up when making a new world. + switch(map[i][x+y*128]){ + case TILE_STAIRS_DOWN: + map[i+1][x+y*128] = TILE_STAIRS_UP; + if(i == 0){ + map[i+1][(x+1)+y*128] = TILE_HARDROCK; + map[i+1][x+(y+1)*128] = TILE_HARDROCK; + map[i+1][(x-1)+y*128] = TILE_HARDROCK; + map[i+1][x+(y-1)*128] = TILE_HARDROCK; + map[i+1][(x+1)+(y+1)*128] = TILE_HARDROCK; + map[i+1][(x-1)+(y-1)*128] = TILE_HARDROCK; + map[i+1][(x-1)+(y+1)*128] = TILE_HARDROCK; + map[i+1][(x+1)+(y-1)*128] = TILE_HARDROCK; + } else { + map[i+1][(x+1)+y*128] = TILE_DIRT; + map[i+1][x+(y+1)*128] = TILE_DIRT; + map[i+1][(x-1)+y*128] = TILE_DIRT; + map[i+1][x+(y-1)*128] = TILE_DIRT; + map[i+1][(x+1)+(y+1)*128] = TILE_DIRT; + map[i+1][(x-1)+(y-1)*128] = TILE_DIRT; + map[i+1][(x-1)+(y+1)*128] = TILE_DIRT; + map[i+1][(x+1)+(y-1)*128] = TILE_DIRT; + } + } + } + + /* Minimaps */ + switch(map[i][x+y*128]){ + case TILE_WATER: sf2d_set_pixel (minimap[i], x, y, 0xFFFF0000); break; + case TILE_LAVA: sf2d_set_pixel (minimap[i], x, y, 0xFF0000FF); break; + case TILE_DIRT: sf2d_set_pixel (minimap[i], x, y, 0xFF6C6D82); break; + case TILE_ROCK: sf2d_set_pixel (minimap[i], x, y, 0xFF7F7F7F); break; + case TILE_HARDROCK: sf2d_set_pixel (minimap[i], x, y, 0xFF7F5F5F); break; + case TILE_GRASS: sf2d_set_pixel (minimap[i], x, y, 0xFF00FF00); break; + case TILE_TREE: sf2d_set_pixel (minimap[i], x, y, 0xFF007F00); break; + case TILE_SAND: sf2d_set_pixel (minimap[i], x, y, 0xFF00FFFF); break; + case TILE_CACTUS: sf2d_set_pixel (minimap[i], x, y, 0xFF009F00); break; + case TILE_FLOWER: sf2d_set_pixel (minimap[i], x, y, 0xFF00FF3F); break; + case TILE_IRONORE: sf2d_set_pixel (minimap[i], x, y, 0xFF9696DC); break; + case TILE_GOLDORE: sf2d_set_pixel (minimap[i], x, y, 0xFF9AE8E5); break; + case TILE_GEMORE: sf2d_set_pixel (minimap[i], x, y, 0xFFDE98DF); break; + case TILE_CLOUD: sf2d_set_pixel (minimap[i], x, y, 0xFFFFFFFF); break; + case TILE_CLOUDCACTUS: sf2d_set_pixel (minimap[i], x, y, 0xFFAFAFAF); break; + case TILE_STAIRS_DOWN: sf2d_set_pixel (minimap[i], x, y, 0xFF9F9F9F); break; + case TILE_STAIRS_UP: sf2d_set_pixel (minimap[i], x, y, 0xFF9F9F9F); break; + default: sf2d_set_pixel (minimap[i], x, y, 0xFF111111); break; + } + } + } + } +} + +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]); +} + +void setupGame(bool loadUpWorld){ + currentLevel = 1; + + // Reset entity manager. + memset(&eManager, 0, sizeof(eManager)); + sf2d_set_clear_color(RGBA8(0x82, 0x6D, 0x6C, 0xFF)); + + if(!loadUpWorld){ + initNewMap(); + initPlayer(); + airWizardHealthDisplay = 2000; + int i; + for(i=0;i<5;++i){ + trySpawn(500, i); + } + addEntityToList(newAirWizardEntity(630,820,0), &eManager); + } else { + initPlayer(); + loadWorld(currentFileName, &eManager, &player, (u8*)map, (u8*)data); + } + + initMiniMap(loadUpWorld); + initGame = 0; +} + + +int xscr=0, yscr=0; +void tick(){ + if(player.p.isDead){ + if(player.p.endTimer < 1){ + currentMenu = MENU_LOSE; + } + --player.p.endTimer; + return; + } else if(player.p.hasWon){ + if(player.p.endTimer < 1){ + currentMenu = MENU_WIN; + } + --player.p.endTimer; + return; + } + + int i; + for(i = 0;i < 324;++i){ + int xx = rand() & 127; + int yy = rand() & 127; + tickTile(xx,yy); + } + tickPlayer(); + xscr = player.x - 100; + yscr = player.y - 56; + if (xscr < 16) xscr = 16; else if (xscr > 1832) xscr = 1832; + if (yscr < 16) yscr = 16; else if (yscr > 1912) yscr = 1912; + + for(i = 0; i < eManager.lastSlot[currentLevel]; ++i){ + Entity * e = &eManager.entities[currentLevel][i]; + if((e->type != ENTITY_ZOMBIE && e->type != ENTITY_SLIME) || (e->x > player.x-160 && e->y > player.y-125 && e->xy 0) setupGame(initGame == 1 ? true : false); + + if(currentMenu == 0){ + tick(); + sprintf(fpsstr, " FPS: %.0f, X:%d, Y:%d, E:%d", sf2d_get_fps(),player.x, player.y,eManager.lastSlot[currentLevel]); + 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, 0xDFDFDFAF); + } + offsetX = xscr;offsetY = yscr; + renderBackground(xscr,yscr); + renderEntities(player.x, player.y, &eManager); + renderPlayer(); + offsetX = 0;offsetY = 0; + renderItemWithText(player.p.activeItem, 10, 205); + // drawText(debugText,2,208); + drawText(fpsstr,2,225); + sf2d_end_frame(); + + sf2d_start_frame(GFX_BOTTOM, GFX_LEFT); + if(currentLevel == 0 && airWizardHealthDisplay > 0){ + sprintf(bossHealthText, "BOSS: %.0f%%", ((float)airWizardHealthDisplay/2000.0)*100); + drawText(bossHealthText,2,225); + } + renderGui(); + sf2d_draw_texture(minimap[currentLevel], 192, 112);//y:56 + sf2d_end_frame(); + } else{ + tickMenu(currentMenu); + renderMenu(currentMenu,xscr,yscr); + } + + sf2d_swapbuffers(); + } + + freeRecipes(); + sf2d_free_texture(icons); + sf2d_free_texture(minimap[0]); + sf2d_free_texture(minimap[1]); + sf2d_free_texture(minimap[2]); + sf2d_free_texture(minimap[3]); + sf2d_free_texture(minimap[4]); + freeSounds(); + csndExit(); + sf2d_fini(); + return 0; +}