diff --git a/.gitattributes b/.gitattributes old mode 100644 new mode 100755 diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 index 655af00..ec8a370 --- a/.gitignore +++ b/.gitignore @@ -46,9 +46,15 @@ Temporary Items *.3dsx *.elf *.smdh +*.cia +*.3ds +*.icn +*.bnr build # Eclipse .settings .cproject .project + +# Copyrighted Executables diff --git a/Makefile b/Makefile old mode 100644 new mode 100755 index 4f57573..7b9264e --- a/Makefile +++ b/Makefile @@ -26,15 +26,18 @@ include $(DEVKITARM)/3ds_rules # - icon.png # - /default_icon.png #--------------------------------------------------------------------------------- -TARGET := Minicraft3DS +TARGET := result/Minicraft3DS +ICON_TARGET := icons-banners/icon +RESULT := result BUILD := build SOURCES := source source/minizip DATA := data INCLUDES := include +ROMFS := romfs -APP_TITLE := Test -APP_DESCRIPTION := ??? -APP_AUTHOR := Davideesk +APP_TITLE := Minicraft 3DS +APP_DESCRIPTION := Originally created by Notch. +APP_AUTHOR := Davideesk/andre111/ElijahZAwesome/tognee #--------------------------------------------------------------------------------- # options for code generation @@ -52,7 +55,7 @@ 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 +LIBS := -lsfil -lpng -ljpeg -lz -lsf2d -lcitro3d -lctru -lm #--------------------------------------------------------------------------------- # list of directories containing libraries, this must be the top level containing @@ -106,13 +109,7 @@ 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 + export APP_ICON := $(TOPDIR)/$(ICON_TARGET).png else export APP_ICON := $(TOPDIR)/$(ICON) endif @@ -121,12 +118,17 @@ ifeq ($(strip $(NO_SMDH)),) export _3DSXFLAGS += --smdh=$(CURDIR)/$(TARGET).smdh endif +ifneq ($(ROMFS),) + export _3DSXFLAGS += --romfs=$(CURDIR)/$(ROMFS) +endif + .PHONY: $(BUILD) clean all #--------------------------------------------------------------------------------- all: $(BUILD) $(BUILD): + @test -d $(RESULT) || mkdir $(RESULT) @[ -d $@ ] || mkdir -p $@ @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile @@ -139,12 +141,12 @@ $(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" + @makerom -f cci -rsf resources/$(TARGET).rsf -target d -exefslogo -elf $(TARGET)-strip.elf -o $(TARGET).3ds -desc App:0x1B + @echo "built ... Minicraft3DS.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" + @makerom -f cia -o $(TARGET).cia -elf $(TARGET)-strip.elf -rsf icons-banners/$(TARGET).rsf -exefslogo -target t + @echo "built ... Minicraft3DS.cia" #--------------------------------------------------------------------------------- send: $(BUILD) @3dslink $(TARGET).3dsx diff --git a/README.md b/README.md old mode 100644 new mode 100755 index afde096..8ca113f --- a/README.md +++ b/README.md @@ -1,21 +1,78 @@ # Minicraft3DS -3DS Homebrew port of Notch's ludum dare game "Minicraft" +3DS Homebrew port of Notch's ludum dare game "Minicraft" +Current Version: Version 1.6.1 -Dependencies: +---------- -ctrulib by smea: https://github.com/smealum/ctrulib +**Download:** -sf2dlib by xerpi: https://github.com/xerpi/sf2dlib +If you just want to download the game prebuilt check the releases tab in Github: +//TODO +For building the game yourself look below. -sfillib by xerpi: https://github.com/xerpi/sfillib -zlib: http://www.zlib.net/ +---------- -Current Version: Version 1.1 -You can do anything with the source code (besides sell it) as long as you give proper credit to the right people. +**Dependencies:** + +For building and installing the dependencies look below. + +ctrulib by smea: https://github.com/smealum/ctrulib +citro3d by fincs: https://github.com/fincs/citro3d +sf2dlib by xerpi: https://github.com/xerpi/sf2dlib +sfillib by xerpi: https://github.com/xerpi/sfillib +zlib: http://www.zlib.net/ + + +---------- + + +**Building:** + +**1. Install devkitARM by devkitPro** +- On Windows download https://sourceforge.net/projects/devkitpro/files/Automated%20Installer/ +- And install atleast Minimal System and devkitARM +- This includes make, ctrulib and citro3d + +**2. Install zlib, libjpeg-turbo and libpng** +- Download 3DS-Portlibs: https://github.com/devkitPro/3ds_portlibs +- Run these commands: + +``` + make zlib + make install-zlib + make libjpeg-turbo + make libpng + make install +``` + +**3. Install sf2dlib** +- Download https://github.com/xerpi/sf2dlib +- In the libsf2d directory run these commands: +``` + make + make install +``` +**4. Install sfillib** +- Download https://github.com/xerpi/sfillib +- In the libsfil directory run these commands: +``` + make + make install +``` + +**5. You can now build Minicraft3DS 3dsx, elf, cia, and 3ds files by running the build.bat file.** + + +---------- + + +You can do anything with the source code (besides sell it) as long as you give proper credit to the right people. If you are going to make a mod of this version, be sure to give credit to Markus "Notch" Perrson because he did create the original game after all. +# Misc + This source code is subject to a lot of change for better optimization/cleanliness. -Forum thread: https://gbatemp.net/threads/release-beta-minicraft-3ds-homebrew-edition.399295/ +Forum thread: //TODO diff --git a/build.bat b/build.bat new file mode 100755 index 0000000..c0d9181 --- /dev/null +++ b/build.bat @@ -0,0 +1,17 @@ +@echo off +echo Building 3DSX/ELF/SMDH... +make +echo Creating banner... +bannertool makebanner -i icons-banners/banner.png -a icons-banners/audio.wav -o icons-banners/banner.bnr +echo Creating icon... +bannertool makesmdh -s "Minicraft3DS" -l "3DS Homebrew port of Notch's ludum dare game 'Minicraft', updated." -p "Davideesk/Andre111/ElijahZAwesome" -i icons-banners/icon.png -o icons-banners/icon.icn +echo Creating ROMFS... +3dstool -cvtf romfs icons-banners/romfs.bin --romfs-dir romfs/ +echo Creating CIA... +makerom -f cia -o result/Minicraft3DS.cia -DAPP_ENCRYPTED=false -rsf icons-banners/Minicraft3DS.rsf -target t -exefslogo -elf result/Minicraft3DS.elf -icon icons-banners/icon.icn -banner icons-banners/banner.bnr -romfs icons-banners/romfs.bin +echo Creating 3DS/CCI... +makerom -f cci -o result/Minicraft3DS.3ds -DAPP_ENCRYPTED=true -rsf icons-banners/Minicraft3DS.rsf -target t -exefslogo -elf result/Minicraft3DS.elf -icon icons-banners/icon.icn -banner icons-banners/banner.bnr -romfs icons-banners/romfs.bin +echo Cleaning Up... +del /s result\Minicraft3DS.elf +del /s result\Minicraft3DS.smdh +pause diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..7c7871d --- /dev/null +++ b/build.sh @@ -0,0 +1,12 @@ +#!/bin/bash +cd "`dirname $0`" +echo Building 3DSX/ELF/SMDH... +make +echo Creating banner... +bannertool makebanner -i icons-banners/banner.png -a icons-banners/audio.wav -o icons-banners/banner.bnr +echo Creating icon... +bannertool makesmdh -s "Minicraft3DS" -l "Original game by Notch" -p "Davideesk/Andre111/ElijahZAwesome/tognee" -i icons-banners/icon.png -o icons-banners/icon.icn +echo Creating CIA... +makerom -f cia -o result/Minicraft3DS.cia -DAPP_ENCRYPTED=false -rsf icons-banners/Minicraft3DS.rsf -target t -exefslogo -elf result/Minicraft3DS.elf -icon icons-banners/icon.icn -banner icons-banners/banner.bnr +echo Creating 3DS/CCI... +makerom -f cci -o result/Minicraft3DS.3ds -DAPP_ENCRYPTED=true -rsf icons-banners/Minicraft3DS.rsf -target t -exefslogo -elf result/Minicraft3DS.elf -icon icons-banners/icon.icn -banner icons-banners/banner.bnr diff --git a/data/Font.png b/data/Font.png old mode 100644 new mode 100755 diff --git a/data/bottombg.png b/data/bottombg.png old mode 100644 new mode 100755 diff --git a/data/icons.png b/data/icons.png new file mode 100755 index 0000000..184fbd2 Binary files /dev/null and b/data/icons.png differ diff --git a/data/icons2.png b/data/icons2.png deleted file mode 100644 index 7059007..0000000 Binary files a/data/icons2.png and /dev/null differ diff --git a/data/player.png b/data/player.png new file mode 100644 index 0000000..53e8a3c Binary files /dev/null and b/data/player.png differ diff --git a/icons-banners/Minicraft3DS.rsf b/icons-banners/Minicraft3DS.rsf new file mode 100755 index 0000000..52914d9 --- /dev/null +++ b/icons-banners/Minicraft3DS.rsf @@ -0,0 +1,195 @@ +BasicInfo: + Title : "Cia Builder Example" + CompanyCode : "00" + ProductCode : "CTR-P-PWDR" + ContentType : Application + Logo : Homebrew # Nintendo / Licensed / Distributed / iQue / iQueForSystem + +TitleInfo: + UniqueId : 0xFAA5C # Cia Builder Example Unique ID, you need to set your own one + Category : Application + +CardInfo: + MediaSize : 128MB # 128MB / 256MB / 512MB / 1GB / 2GB / 4GB + MediaType : Card1 # Card1 / Card2 + CardDevice : None # NorFlash(Pick this if you use savedata) / None + + +Option: + UseOnSD : true # true if App is to be installed to SD + FreeProductCode : true # Removes limitations on ProductCode + MediaFootPadding : false # If true CCI files are created with padding + EnableCrypt : false # Enables encryption for NCCH and CIA + EnableCompress : true # Compresses exefs code + +AccessControlInfo: + #UseExtSaveData : true + #ExtSaveDataId: 0xff3ff + #UseExtendedSaveDataAccessControl: true + #AccessibleSaveDataIds: [0x101, 0x202, 0x303, 0x404, 0x505, 0x606] + +SystemControlInfo: + SaveDataSize: 128KB + RemasterVersion: 0 + StackSize: 0x40000 + +# DO NOT EDIT BELOW HERE OR PROGRAMS WILL NOT LAUNCH (most likely) + +AccessControlInfo: + FileSystemAccess: + - Debug + - DirectSdmc + - DirectSdmcWrite + + IdealProcessor : 0 + AffinityMask : 1 + + Priority : 16 + + MaxCpu : 0x9E # Default + DisableDebug : false + EnableForceDebug : false + CanWriteSharedPage : false + CanUsePrivilegedPriority : false + CanUseNonAlphabetAndNumber : false + PermitMainFunctionArgument : false + CanShareDeviceMemory : false + RunnableOnSleep : false + SpecialMemoryArrange : false + CoreVersion : 2 + DescVersion : 2 + + ReleaseKernelMajor : "02" + ReleaseKernelMinor : "33" + MemoryType : Application + HandleTableSize: 512 + + SystemModeExt : Legacy # Legacy(Default)/124MB/178MB Legacy:Use Old3DS SystemMode + CpuSpeed : 804MHz # 268MHz(Default)/804MHz + EnableL2Cache : true # false(default)/true + CanAccessCore2 : true + + IORegisterMapping: + - 1ff50000-1ff57fff + - 1ff70000-1ff77fff + MemoryMapping: + - 1f000000-1f5fffff:r + SystemCallAccess: + ArbitrateAddress: 34 + Break: 60 + CancelTimer: 28 + ClearEvent: 25 + ClearTimer: 29 + CloseHandle: 35 + ConnectToPort: 45 + ControlMemory: 1 + CreateAddressArbiter: 33 + CreateEvent: 23 + CreateMemoryBlock: 30 + CreateMutex: 19 + CreateSemaphore: 21 + CreateThread: 8 + CreateTimer: 26 + DuplicateHandle: 39 + ExitProcess: 3 + ExitThread: 9 + GetCurrentProcessorNumber: 17 + GetHandleInfo: 41 + GetProcessId: 53 + GetProcessIdOfThread: 54 + GetProcessIdealProcessor: 6 + GetProcessInfo: 43 + GetResourceLimit: 56 + GetResourceLimitCurrentValues: 58 + GetResourceLimitLimitValues: 57 + GetSystemInfo: 42 + GetSystemTick: 40 + GetThreadContext: 59 + GetThreadId: 55 + GetThreadIdealProcessor: 15 + GetThreadInfo: 44 + GetThreadPriority: 11 + MapMemoryBlock: 31 + OutputDebugString: 61 + QueryMemory: 2 + ReleaseMutex: 20 + ReleaseSemaphore: 22 + SendSyncRequest1: 46 + SendSyncRequest2: 47 + SendSyncRequest3: 48 + SendSyncRequest4: 49 + SendSyncRequest: 50 + SetThreadPriority: 12 + SetTimer: 27 + SignalEvent: 24 + SleepThread: 10 + UnmapMemoryBlock: 32 + WaitSynchronization1: 36 + WaitSynchronizationN: 37 + InterruptNumbers: + ServiceAccessControl: + - APT:U + - $hioFIO + - $hostio0 + - $hostio1 + - ac:u + - boss:U + - cam:u + - ir:rst + - cfg:u + - dlp:FKCL + - dlp:SRVR + - dsp::DSP + - frd:u + - fs:USER + - gsp::Gpu + - hid:USER + - http:C + - mic:u + - ndm:u + - news:s + - nwm::UDS + - ptm:u + - pxi:dev + - soc:U + - gsp::Lcd + - y2r:u + - ldr:ro + - ir:USER + - ir:u + - csnd:SND + - am:u + - ns:s + +SystemControlInfo: + Dependency: + ac: 0x0004013000002402L + am: 0x0004013000001502L + boss: 0x0004013000003402L + camera: 0x0004013000001602L + cecd: 0x0004013000002602L + cfg: 0x0004013000001702L + codec: 0x0004013000001802L + csnd: 0x0004013000002702L + dlp: 0x0004013000002802L + dsp: 0x0004013000001a02L + friends: 0x0004013000003202L + gpio: 0x0004013000001b02L + gsp: 0x0004013000001c02L + hid: 0x0004013000001d02L + http: 0x0004013000002902L + i2c: 0x0004013000001e02L + ir: 0x0004013000003302L + mcu: 0x0004013000001f02L + mic: 0x0004013000002002L + ndm: 0x0004013000002b02L + news: 0x0004013000003502L + nim: 0x0004013000002c02L + nwm: 0x0004013000002d02L + pdn: 0x0004013000002102L + ps: 0x0004013000003102L + ptm: 0x0004013000002202L + ro: 0x0004013000003702L + socket: 0x0004013000002e02L + spi: 0x0004013000002302L + ssl: 0x0004013000002f02L \ No newline at end of file diff --git a/icons-banners/audio.wav b/icons-banners/audio.wav new file mode 100755 index 0000000..7f3b2f0 Binary files /dev/null and b/icons-banners/audio.wav differ diff --git a/icons-banners/banner.png b/icons-banners/banner.png new file mode 100755 index 0000000..45b47c1 Binary files /dev/null and b/icons-banners/banner.png differ diff --git a/icons-banners/icon.png b/icons-banners/icon.png new file mode 100755 index 0000000..ce37e18 Binary files /dev/null and b/icons-banners/icon.png differ diff --git a/icons-banners/romfs.bin b/icons-banners/romfs.bin new file mode 100644 index 0000000..20e6c8f Binary files /dev/null and b/icons-banners/romfs.bin differ diff --git a/resources/bossdeath.raw b/romfs/resources/bossdeath.raw similarity index 100% rename from resources/bossdeath.raw rename to romfs/resources/bossdeath.raw diff --git a/resources/craft.raw b/romfs/resources/craft.raw similarity index 100% rename from resources/craft.raw rename to romfs/resources/craft.raw diff --git a/resources/death.raw b/romfs/resources/death.raw similarity index 100% rename from resources/death.raw rename to romfs/resources/death.raw diff --git a/resources/monsterhurt.raw b/romfs/resources/monsterhurt.raw similarity index 100% rename from resources/monsterhurt.raw rename to romfs/resources/monsterhurt.raw diff --git a/resources/music/floor0.raw b/romfs/resources/music/floor0.raw similarity index 100% rename from resources/music/floor0.raw rename to romfs/resources/music/floor0.raw diff --git a/resources/music/floor1.raw b/romfs/resources/music/floor1.raw similarity index 100% rename from resources/music/floor1.raw rename to romfs/resources/music/floor1.raw diff --git a/romfs/resources/music/floor1_night.raw b/romfs/resources/music/floor1_night.raw new file mode 100644 index 0000000..1474c1b Binary files /dev/null and b/romfs/resources/music/floor1_night.raw differ diff --git a/resources/music/floor2_3.raw b/romfs/resources/music/floor2_3.raw similarity index 100% rename from resources/music/floor2_3.raw rename to romfs/resources/music/floor2_3.raw diff --git a/resources/music/floor4.raw b/romfs/resources/music/floor4.raw similarity index 100% rename from resources/music/floor4.raw rename to romfs/resources/music/floor4.raw diff --git a/resources/music/menu.raw b/romfs/resources/music/menu.raw similarity index 100% rename from resources/music/menu.raw rename to romfs/resources/music/menu.raw diff --git a/resources/pickup.raw b/romfs/resources/pickup.raw similarity index 100% rename from resources/pickup.raw rename to romfs/resources/pickup.raw diff --git a/resources/playerhurt.raw b/romfs/resources/playerhurt.raw similarity index 100% rename from resources/playerhurt.raw rename to romfs/resources/playerhurt.raw diff --git a/resources/test.raw b/romfs/resources/test.raw similarity index 100% rename from resources/test.raw rename to romfs/resources/test.raw diff --git a/source/Crafting.c b/source/Crafting.c old mode 100644 new mode 100755 index 98d1cbb..c6ef4ff --- a/source/Crafting.c +++ b/source/Crafting.c @@ -1,137 +1,193 @@ #include "Crafting.h" +void cloneRecipeManager(RecipeManager *from, RecipeManager *to) { + //free old manager recipes + free(to->recipes); + + //copy over recipes + to->size = from->size; + to->recipes = (Recipe*)malloc(sizeof(Recipe) * to->size); + memcpy(to->recipes, from->recipes, sizeof(Recipe) * to->size); +} + void checkCanCraftRecipes(RecipeManager * rm, Inventory * inv){ - int i, j; - for(i = 0; i < rm->size; i++){ - 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 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; + Recipe* r1 = (Recipe*)ra; + Recipe* r2 = (Recipe*)rb; if (r1->canCraft && !r2->canCraft) return -1; - 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); + 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); - } + 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; + 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; + 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; +typedef struct _recipecost { + 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. +typedef struct _recipe { + 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; +typedef struct _recipeManager { + int size; + Recipe * recipes; } RecipeManager; - +RecipeManager inHeadRecipes; RecipeManager workbenchRecipes; RecipeManager furnaceRecipes; RecipeManager ovenRecipes; RecipeManager anvilRecipes; +RecipeManager loomRecipes; +RecipeManager enchanterRecipes; +RecipeManager potionMakerRecipes; +Recipe defineRecipe(int item, int amountOrLevel, int numArgs, ...); + +void cloneRecipeManager(RecipeManager *from, RecipeManager *to); void checkCanCraftRecipes(RecipeManager * rm, Inventory * inv); void sortRecipes(RecipeManager * rm); bool craftItem(RecipeManager * rm, Recipe* r, Inventory* inv); diff --git a/source/Entity.c b/source/Entity.c old mode 100644 new mode 100755 index 57c4503..37db8c8 --- a/source/Entity.c +++ b/source/Entity.c @@ -1,204 +1,363 @@ #include "Entity.h" -#define PI 3.141592654 -double gaussrand() -{ - static double U, V; - static int phase = 0; - double Z; - - if(phase == 0) { - U = (rand() + 1.) / (RAND_MAX + 2.); - V = rand() / (RAND_MAX + 1.); - Z = sqrt(-2 * log(U)) * sin(2 * PI * V); - } else - Z = sqrt(-2 * log(U)) * cos(2 * PI * V); - - phase = 1 - phase; - - return Z; -} +#include "Synchronizer.h" Entity newItemEntity(Item item, int x, int y, int level){ - Entity e; - 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; - + 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.xa = gaussrand(false) * 0.1; + e.entityItem.ya = gaussrand(false) * 0.1; e.entityItem.za = ((float)rand() / RAND_MAX) * 0.45 + 1; - - return e; + + return e; } void assignInventory(Entity* e){ - if(eManager.nextInv > 300) return; - e->entityFurniture.inv = &eManager.invs[eManager.nextInv]; - eManager.nextInv++; + 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 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 newPassiveEntity(int type, int x, int y, int level){ + Entity e; + e.type = ENTITY_PASSIVE; + e.level = level; + e.x = x; + e.y = y; + e.hurtTime = 0; + e.xKnockback = 0; + e.yKnockback = 0; + e.passive.mtype = type; + e.passive.health = 20; + e.passive.dir = 0; + e.passive.xa = 0; + e.passive.ya = 0; + e.xr = 4; + e.yr = 3; + e.canPass = false; + 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 = 0xFF8282CC; break; - case 3: e.zombie.color = 0xFFEFEFEF; break; - case 4: e.zombie.color = 0xFFAA6262; break; - default: e.zombie.color = 0xFF95DB95; break; - } - return e; + 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.hostile.lvl = lvl; + e.hostile.xa = 0; + e.hostile.ya = 0; + e.hostile.health = lvl * lvl * 10; + e.hostile.dir = 0; + e.xr = 4; + e.yr = 3; + e.canPass = false; + switch(lvl){ + case 2: e.hostile.color = 0xFF8282CC; break; + case 3: e.hostile.color = 0xFFEFEFEF; break; + case 4: e.hostile.color = 0xFFAA6262; break; + default: e.hostile.color = 0xFF95DB95; break; + } + return e; +} + +Entity newSkeletonEntity(int lvl, int x, int y, int level){ + Entity e; + e.type = ENTITY_SKELETON; + e.level = level; + e.x = x; + e.y = y; + e.hurtTime = 0; + e.xKnockback = 0; + e.yKnockback = 0; + e.hostile.lvl = lvl; + e.hostile.xa = 0; + e.hostile.ya = 0; + e.hostile.health = lvl * lvl * 10; + e.hostile.dir = 0; + e.hostile.randAttackTime = 0; + e.xr = 4; + e.yr = 3; + e.canPass = false; + switch(lvl){ + case 2: e.hostile.color = 0xFFC4C4C4; break; + case 3: e.hostile.color = 0xFFA0A0A0; break; + case 4: e.hostile.color = 0xFF7A7A7A; break; + default: e.hostile.color = 0xFFFFFFFF; break; + } + return e; +} + +Entity newKnightEntity(int lvl, int x, int y, int level){ + Entity e; + e.type = ENTITY_KNIGHT; + e.level = level; + e.x = x; + e.y = y; + e.hurtTime = 0; + e.xKnockback = 0; + e.yKnockback = 0; + e.hostile.lvl = lvl; + e.hostile.xa = 0; + e.hostile.ya = 0; + e.hostile.health = lvl * lvl * 20; + e.hostile.dir = 0; + e.xr = 4; + e.yr = 3; + e.canPass = false; + switch(lvl){ + case 2: e.hostile.color = 0xFF0000C6; break; + case 3: e.hostile.color = 0xFF00A3C6; break; + case 4: e.hostile.color = 0xFF707070; break; + default: e.hostile.color = 0xFFFFFFFF; 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 = 0xFF8282CC; break; - case 3: e.slime.color = 0xFFEFEFEF; break; - case 4: e.slime.color = 0xFFAA6262; break; - default: e.slime.color = 0xFF95DB95; break; - } - return e; + 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 = 0xFF8282CC; break; + case 3: e.slime.color = 0xFFEFEFEF; break; + case 4: e.slime.color = 0xFFAA6262; break; + default: e.slime.color = 0xFF95DB95; 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; + 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; + 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 e; + e.type = ENTITY_SPARK; + e.level = parent->level; + 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 newDragonEntity(int x, int y, int level) { + Entity e; + e.type = ENTITY_DRAGON; + e.level = level; + e.x = x; + e.y = y; + e.hurtTime = 0; + e.xKnockback = 0; + e.yKnockback = 0; + e.dragon.dir = 0; + e.dragon.health = 2000; + e.dragon.attackDelay = 0; + e.dragon.attackTime = 0; + e.dragon.attackType = 0; + e.dragon.animTimer = 0; + e.dragon.xa = 0; + e.dragon.ya = 0; + e.xr = 8; + e.yr = 8; + e.canPass = false; + return e; +} + +Entity newDragonFireEntity(Entity* parent, u8 type, int x, int y, float xa, float ya) { + Entity e; + e.type = ENTITY_DRAGONPROJECTILE; + e.level = parent->level; + e.dragonFire.age = 0; + e.dragonFire.type = type; + e.dragonFire.parent = parent; + e.dragonFire.xa = xa; + e.dragonFire.ya = ya; + e.dragonFire.xx = x; + e.dragonFire.yy = y; + e.x = (int) x; + e.y = (int) y; + e.xr = 3; + e.yr = 3; + e.canPass = true; + return e; +} + +Entity newMagicPillarEntity(int x, int y, int level){ + Entity e; + e.type = ENTITY_MAGIC_PILLAR; + e.level = level; + e.x = x; + e.y = y; + e.xr = 3; + e.yr = 3; + e.canPass = false; + 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; + 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.xa = gaussrand(false) * 0.3; + e.textParticle.ya = gaussrand(false) * 0.2; e.textParticle.za = ((float)rand() / RAND_MAX) * 0.7 + 2; - - return e; + + 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; + Entity e; + e.type = ENTITY_SMASHPARTICLE; + e.level = level; + e.smashParticle.age = 0; + e.x = x; + e.y = y; + e.canPass = true; + playSoundPositioned(snd_monsterHurt, e.level, e.x, e.y); //TODO: This is a wierd location for the effect + return e; +} + +Entity newArrowEntity(Entity* parent, int itemID, s8 xa, s8 ya, int level){ + Entity e; + e.type = ENTITY_ARROW; + e.level = level; + e.arrow.age = 0; + e.arrow.parent = parent; + e.arrow.itemID = itemID; + e.arrow.xa = xa; + e.arrow.ya = ya; + e.x = parent->x; + e.y = parent->y; + e.xr = 2; + e.yr = 2; + e.canPass = false; + e.canSwim = true; + return e; +} + +Entity newGlowwormEntity(int x, int y, int level){ + Entity e; + e.type = ENTITY_GLOWWORM; + e.level = level; + e.glowworm.xa = 0; + e.glowworm.ya = 0; + e.glowworm.randWalkTime = 0; + e.glowworm.waitTime = 0; + e.x = x; + e.y = y; + e.canPass = true; + return e; +} + +Entity newNPCEntity(int type, int x, int y, int level){ + Entity e; + e.type = ENTITY_NPC; + e.level = level; + e.x = x; + e.y = y; + e.hurtTime = 0; + e.xKnockback = 0; + e.yKnockback = 0; + e.npc.type = type; + e.xr = 4; + e.yr = 3; + e.canPass = false; + return e; } void addEntityToList(Entity e, EntityManager* em){ - e.slotNum = em->lastSlot[e.level]; - em->entities[e.level][em->lastSlot[e.level]] = e; - ++em->lastSlot[e.level]; + 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. + 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 old mode 100644 new mode 100755 index c812ea0..c21f5e1 --- a/source/Entity.h +++ b/source/Entity.h @@ -13,80 +13,106 @@ #define ENTITY_TEXTPARTICLE 7 #define ENTITY_SMASHPARTICLE 8 #define ENTITY_PLAYER 9 +#define ENTITY_PASSIVE 10 +#define ENTITY_ARROW 11 +#define ENTITY_SKELETON 12 +#define ENTITY_KNIGHT 13 +#define ENTITY_GLOWWORM 14 + +#define ENTITY_DRAGON 15 +#define ENTITY_DRAGONPROJECTILE 16 +#define ENTITY_MAGIC_PILLAR 17 + +#define ENTITY_NPC 18 typedef struct Entity Entity; +typedef struct _plrd PlayerData; //in order to not include Player.h and cause all sorts of problems + typedef struct { - s8 ax; - s8 ay; - 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; + 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 regenTimer; + int strengthTimer; + int swimBreathTimer; + int speedTimer; int score; - Inventory* inv; - Item* activeItem; + PlayerData *data; } Player; typedef struct { - float xa; - float ya; - float za; - float xx; - float yy; - float zz; - s16 age; - Item item; + 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. + s16 itemID; + bool active; + s8 r; // light radius for lantern. + Inventory* inv; // Points to chest inventory. } EntityFurniture; typedef struct { - s8 xa; - s8 ya; - s16 health; - s8 dir; - s8 lvl; - s8 randWalkTime; - s8 walkDist; - u32 color; -} Zombie; + u8 mtype; + s8 xa; + s8 ya; + s16 health; + s8 dir; + s8 randWalkTime; + s8 walkDist; +} PassiveMob; typedef struct { - s8 xa; - s8 ya; - s16 health; - s8 lvl; - s8 dir; - s8 jumpTime; - u32 color; + s8 xa; + s8 ya; + s16 health; + s8 dir; + s8 lvl; + s8 randWalkTime; + s8 walkDist; + s8 randAttackTime; + u32 color; +} HostileMob; + +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; + s8 xa; + s8 ya; + s16 health; + s8 randWalkTime; + s8 walkDist; + s8 dir; int attackDelay; int attackTime; int attackType; @@ -94,77 +120,129 @@ typedef struct { } AirWizard; typedef struct { - Entity* parent; - s16 age; - float xa; - float ya; - float xx; - float yy; + 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; + Entity* parent; + s16 age; + s16 itemID; + s8 xa; + s8 ya; +} Arrow; + +typedef struct { + s8 xa; + s8 ya; + s16 health; + s8 randWalkTime; + s8 walkDist; + s8 dir; + int attackDelay; + int attackTime; + int attackType; + int animTimer; +} Dragon; + +typedef struct { + Entity* parent; + u8 type; + s16 age; + float xa; + float ya; + float xx; + float yy; +} DragonFire; + +typedef struct { + s8 xa; + s8 ya; + s8 randWalkTime; + s8 waitTime; +} Glowworm; + +typedef struct { + u8 type; +} NPC; + +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; + 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; - }; + 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; + PassiveMob passive; + HostileMob hostile; + Slime slime; + AirWizard wizard; + Spark spark; + Arrow arrow; + Glowworm glowworm; + Dragon dragon; + DragonFire dragonFire; + NPC npc; + 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; + Entity entities[6][1000]; + s16 lastSlot[6]; + Inventory invs[300]; + s16 nextInv; } EntityManager; EntityManager eManager; -s8 currentLevel; +Entity nullEntity; -double gaussrand(); Entity newItemEntity(Item item, int x, int y, int level); -Entity newFurnitureEntity(int itemID,Inventory * invPtr, int x, int y, int level); +Entity newFurnitureEntity(int itemID, Inventory * invPtr, int x, int y, int level); +Entity newPassiveEntity(int type, int x, int y, int level); Entity newZombieEntity(int lvl, int x, int y, int level); +Entity newSkeletonEntity(int lvl, int x, int y, int level); +Entity newKnightEntity(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 newDragonEntity(int x, int y, int level); +Entity newDragonFireEntity(Entity* parent, u8 type, int x, int y, float xa, float ya); +Entity newMagicPillarEntity(int x, int y, int level); Entity newTextParticleEntity(char * str, u32 color, int xa, int ya, int level); Entity newSmashParticleEntity(int xa, int ya, int level); +Entity newArrowEntity(Entity* parent, int itemID, s8 xa, s8 ya, int level); +Entity newGlowwormEntity(int x, int y, int level); +Entity newNPCEntity(int type, int x, int y, int level); void addEntityToList(Entity e, EntityManager* em); void removeEntityFromList(Entity * e,int level,EntityManager* em); - - - diff --git a/source/Globals.c b/source/Globals.c old mode 100644 new mode 100755 index a1aaf28..9e278fe --- a/source/Globals.c +++ b/source/Globals.c @@ -1,40 +1,42 @@ #include "Globals.h" +#include "Menu.h" +#include "Synchronizer.h" -char versionText[34] = "Version 1.1"; +char versionText[34] = "Version 1.6.2"; 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; - } +int getEntities(Entity **result, s8 level, int x0, int y0, int x1, int y1) { + int i, last = 0; + for (i = 0; i < eManager.lastSlot[level]; ++i) { + Entity* e = &eManager.entities[level][i]; + if (intersects(*e, x0, y0, x1, y1)){ + result[last] = e; + ++last; + } } 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; @@ -50,39 +52,41 @@ bool move2(Entity* e,int xa, int ya) { for (yt = yt0; yt <= yt1; ++yt) for (xt = xt0; xt <= xt1; ++xt) { if (xt >= xto0 && xt <= xto1 && yt >= yto0 && yt <= yto1) continue; - entityTileInteract(e, getTile(xt,yt), xt, yt); - if (!e->canPass && tileIsSolid(getTile(xt,yt),e)) { + entityTileInteract(e, getTile(e->level, xt, yt), e->level, xt, yt); + if (!e->canPass && tileIsSolid(getTile(e->level, xt, yt), e)) { blocked = true; return false; } } if (blocked) return false; - Entity * wasInside[eManager.lastSlot[currentLevel]]; - Entity * isInside[eManager.lastSlot[currentLevel]]; - getEntities(wasInside, e->x - e->xr, e->y - e->yr, e->x + e->xr, e->y + e->yr); - int isSize = getEntities(isInside, e->x + xa - e->xr, e->y + ya - e->yr, e->x + xa + e->xr, e->y + ya + e->yr); + Entity * wasInside[eManager.lastSlot[e->level]]; + Entity * isInside[eManager.lastSlot[e->level]]; + int wasSize = getEntities(wasInside, e->level, e->x - e->xr, e->y - e->yr, e->x + e->xr, e->y + e->yr); + int isSize = getEntities(isInside, e->level, e->x + xa - e->xr, e->y + ya - e->yr, e->x + xa + e->xr, e->y + ya + e->yr); int i; - + for (i = 0; i < isSize; ++i) { 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(int i=0; ilevel && intersects(players[i].entity, e->x + xa - e->xr, e->y + ya - e->yr, e->x + xa + e->xr, e->y + ya + e->yr)){ + EntityVsEntity(e, &(players[i].entity)); + } + } + } + removeSimilarElements(wasInside, wasSize, isInside, isSize); + for (i = 0; i < isSize; ++i) { Entity * ent = isInside[i]; if (ent == e || ent == NULL) continue; - if (EntityBlocksEntity(e,ent)) return false; + if (EntityBlocksEntity(e, ent)) return false; } - if(e->x + xa > 0 && e->x + xa < 2048) e->x += xa; + if(e->x + xa > 0 && e->x + xa < 2048) e->x += xa; if(e->y + ya > 0 && e->y + ya < 2048) e->y += ya; return true; } @@ -98,1418 +102,1694 @@ bool move(Entity* e, int xa, int ya) { } bool moveMob(Entity* e, int xa, int ya){ - if (e->xKnockback < 0) { - move2(e,-1, 0); - e->xKnockback++; - } - if (e->xKnockback > 0) { - move2(e,1, 0); - e->xKnockback--; - } - if (e->yKnockback < 0) { - move2(e,0, -1); - e->yKnockback++; - } - if (e->yKnockback > 0) { - move2(e,0, 1); - e->yKnockback--; - } - if (e->hurtTime > 0) return true; - return move(e, xa, ya); -} - -s16 lastTouchX = -1; -s16 lastTouchY = -1; -bool isDraggingMap = false; -bool isChangingSize = false; -void tickTouchMap(){ - if(shouldRenderMap){ - if(k_touch.px > 0 || k_touch.py > 0){ - // Plus/Minus zoom button - if(k_touch.py > 204 && k_touch.py < 232){ - if(k_touch.px > 284 && k_touch.px < 312){ - if(zoomLevel > 4) return; - if(!isChangingSize && !isDraggingMap){ - zoomLevel += 2; - mScrollX -= (50 * (zoomLevel/2)); - mScrollY -= (40 * (zoomLevel/2)); - isChangingSize = true; - sprintf(mapText,"x%d",zoomLevel); - } - if(mScrollX < 320-(128*zoomLevel)) mScrollX = 320-(128*zoomLevel); - else if(mScrollX > 0) mScrollX = 0; - if(mScrollY < 240-(128*zoomLevel)) mScrollY = 240-(128*zoomLevel); - else if(mScrollY > 0) mScrollY = 0; - return; - } else if(k_touch.px > 256 && k_touch.px < 284){ - if(zoomLevel < 4) return; - if(!isChangingSize && !isDraggingMap){ - mScrollX += (50 * (zoomLevel/2)); - mScrollY += (40 * (zoomLevel/2)); - zoomLevel -= 2; - isChangingSize = true; - sprintf(mapText,"x%d",zoomLevel); - } - if(mScrollX < 320-(128*zoomLevel)) mScrollX = 320-(128*zoomLevel); - else if(mScrollX > 0) mScrollX = 0; - if(mScrollY < 240-(128*zoomLevel)) mScrollY = 240-(128*zoomLevel); - else if(mScrollY > 0) mScrollY = 0; - return; - } - } else if(k_touch.py > 8 && k_touch.py < 40 && k_touch.px > 284 && k_touch.px < 312){ - // Exit Button - if(!isChangingSize && !isDraggingMap){ - shouldRenderMap = false; - return; - } - } - - if(!isDraggingMap){ - lastTouchX = k_touch.px; - lastTouchY = k_touch.py; - } - if(zoomLevel > 2){ - int dx = lastTouchX - k_touch.px; - if(dx > 1 || dx < -1){ - mScrollX -= dx; - if(mScrollX < 320-(128*zoomLevel)) mScrollX = 320-(128*zoomLevel); - else if(mScrollX > 0) mScrollX = 0; - } - lastTouchX = k_touch.px; - } - - int dy = lastTouchY - k_touch.py; - if(dy > 1 || dy < -1){ - mScrollY -= dy; - if(mScrollY < 240-(128*zoomLevel)) mScrollY = 240-(128*zoomLevel); - else if(mScrollY > 0) mScrollY = 0; - } - lastTouchY = k_touch.py; - isDraggingMap = true; - } else { - isDraggingMap = false; - isChangingSize = false; - } - } else { - // touch minimap to bring up zoomed map. - if(k_touch.py > 100 && k_touch.py < 228 && k_touch.px > 10 && k_touch.px < 142){ - shouldRenderMap = true; - } - } -} - -void tickTouchQuickSelect() { - if (currentMenu == 0) { - int i = 0; - Inventory * inv = player.p.inv; - - for (i = 0; i < 8; ++i) { - if((inv->lastSlot) > i) { - int xip = i % 4; - int yip = i / 4; - - if(k_touch.py > 72*2+yip*21*2 && k_touch.py < 72*2+yip*21*2+21*2 && k_touch.px > 76*2+xip*21*2 && k_touch.px < 76*2+xip*21*2+21*2) { - playerSetActiveItem(&inv->items[i]); - } - } - } + 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); +void hurtEntity(Entity *e, int damage, int dir, u32 hurtColor, Entity *damager){ + if (shouldRenderDebug && e->type==ENTITY_PLAYER) return; + if (e->hurtTime > 0) return; + playSoundPositioned(snd_monsterHurt, e->level, e->x, e->y); + + char hurtText[11]; + sprintf(hurtText, "%d", damage); + addEntityToList(newTextParticleEntity(hurtText, hurtColor, e->x, e->y, e->level), &eManager); + + int i; + + // In hindsight I should've made a generic Mob struct, but whatever. �\_(-.-)_/� + switch(e->type){ + case ENTITY_PLAYER: + e->p.health -= damage; + if(e->p.health < 1){ + playSoundPositioned(snd_bossdeath, e->level, e->x, e->y); + e->p.endTimer = 60; + e->p.isDead = true; + e->hurtTime = 10; + return; + } + break; + case ENTITY_ZOMBIE: + case ENTITY_SKELETON: + case ENTITY_KNIGHT: + e->hostile.health -= damage; + if(e->hostile.health < 1){ + if(e->type == ENTITY_ZOMBIE) { + addItemsToWorld(newItem(ITEM_FLESH,1), e->level, e->x+8, e->y+8, (rand()%2) + 1); + } else if(e->type == ENTITY_SKELETON) { + addItemsToWorld(newItem(ITEM_BONE,1), e->level, e->x+8, e->y+8, (rand()%2) + 1); + if(rand()%2==0) addItemsToWorld(newItem(ITEM_ARROW_STONE,1), e->level, e->x+8, e->y+8, 1); + } else if(e->type == ENTITY_KNIGHT) { + addItemsToWorld(newItem(ITEM_IRONINGOT,1), e->level, e->x+8, e->y+8, (rand()%2) + 1); + } + if(damager!=NULL && damager->type==ENTITY_PLAYER) damager->p.data->score += 50 * (e->hostile.lvl + 1); + removeEntityFromList(e, e->level, &eManager); + if(e->level != 5) trySpawn(3, e->level); + return; + } + break; + case ENTITY_SLIME: + e->slime.health -= damage; + if(e->slime.health < 1){ + addItemsToWorld(newItem(ITEM_SLIME,1), e->level, e->x+8, e->y+8, (rand()%2) + 1); + if(damager!=NULL && damager->type==ENTITY_PLAYER) damager->p.data->score += 25 * (e->slime.lvl + 1); + removeEntityFromList(e, e->level, &eManager); + if(e->level != 5) trySpawn(3, e->level); + return; + } + break; + case ENTITY_AIRWIZARD: + e->wizard.health -= damage; + airWizardHealthDisplay = e->wizard.health; + if(e->wizard.health < 1){ + addItemsToWorld(newItem(ITEM_MAGIC_DUST,1), e->level, e->x+8, e->y+8, (rand()%2) + 2); + removeEntityFromList(e,e->level,&eManager); + playSound(snd_bossdeath); + + for(i=0; ipassive.health -= damage; + if(e->passive.health < 1){ + if(e->passive.mtype==0) { + addItemsToWorld(newItem(ITEM_WOOL,1), e->level, e->x+8, e->y+8, (rand()%3) + 1); + } else if(e->passive.mtype==1) { + addItemsToWorld(newItem(ITEM_PORK_RAW,1), e->level, e->x+8, e->y+8, (rand()%2) + 1); + } else if(e->passive.mtype==2) { + addItemsToWorld(newItem(ITEM_BEEF_RAW,1), e->level, e->x+8, e->y+8, (rand()%2) + 1); + if((rand()%2)==0) { + addItemsToWorld(newItem(ITEM_LEATHER,1), e->level, e->x+8, e->y+8, 1); + } + } + if(damager!=NULL && damager->type==ENTITY_PLAYER) damager->p.data->score += 10; + removeEntityFromList(e, e->level, &eManager); + if(e->level != 5) trySpawn(3, e->level); + return; + } + break; + case ENTITY_DRAGON: + e->dragon.health -= damage; + if(e->dragon.health < 1){ + addItemsToWorld(newItem(ITEM_DRAGON_EGG,1), e->level, e->x+8, e->y+8, 1); + addItemsToWorld(newItem(ITEM_DRAGON_SCALE,1), e->level, e->x+8, e->y+8, (rand()%11) + 10); + removeEntityFromList(e, e->level, &eManager); + playSound(snd_bossdeath); + for(i=0; ix,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 + 1); - 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 + 1); - 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; - } + 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: + case ENTITY_SKELETON: + case ENTITY_KNIGHT: + switch(e->hostile.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; + case ENTITY_PASSIVE: + switch(e->passive.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 ItemVsEntity(PlayerData *pd, Item *item, Entity *e, int dir) { + //TODO: Too much duplicated code + switch(item->id){ + case ITEM_POWGLOVE: + if(e->type == ENTITY_FURNITURE){ + //Important: close all crafting windows using this furniture (only applies to chest) or else they will write invalid memory + for(int i=0; ientityFurniture.itemID,0); + if(e->entityFurniture.itemID == ITEM_CHEST) nItem.chestPtr = e->entityFurniture.inv; + pushItemToInventoryFront(nItem, &(pd->inventory)); + + removeEntityFromList(e, e->level, &eManager); + pd->activeItem = &(pd->inventory.items[0]); + pd->entity.p.isCarrying = true; + return true; + } break; + case TOOL_AXE: + switch(e->type){ + case ENTITY_PASSIVE: + case ENTITY_ZOMBIE: + case ENTITY_SKELETON: + case ENTITY_KNIGHT: + case ENTITY_SLIME: + case ENTITY_AIRWIZARD: + case ENTITY_DRAGON: + if(playerUseEnergy(pd, 4-item->countLevel)) hurtEntity(e, (item->countLevel + 1) * 2 + (rand()%4), dir, 0xFF0000FF, &(pd->entity)); + else hurtEntity(e, 1+rand()%3, dir, 0xFF0000FF, &(pd->entity)); + return true; + + case ENTITY_MAGIC_PILLAR: + playSoundPositioned(snd_monsterHurt, e->level, e->x, e->y); + + removeEntityFromList(e, e->level, &eManager); + return true; + } break; + case TOOL_SWORD: + switch(e->type){ + case ENTITY_PASSIVE: + case ENTITY_ZOMBIE: + case ENTITY_SKELETON: + case ENTITY_KNIGHT: + case ENTITY_SLIME: + case ENTITY_AIRWIZARD: + case ENTITY_DRAGON: + if(playerUseEnergy(pd, 4-item->countLevel)) hurtEntity(e, (item->countLevel+1)*3+(rand()%(2+item->countLevel*item->countLevel*2)), dir, 0xFF0000FF, &(pd->entity)); + else hurtEntity(e, 1+rand()%3, dir, 0xFF0000FF, &(pd->entity)); + return true; + + case ENTITY_MAGIC_PILLAR: + playSoundPositioned(snd_monsterHurt, e->level, e->x, e->y); + + removeEntityFromList(e, e->level, &eManager); + return true; + } break; + case ITEM_NULL: + switch(e->type){ + case ENTITY_PASSIVE: + case ENTITY_ZOMBIE: + case ENTITY_SKELETON: + case ENTITY_KNIGHT: + case ENTITY_SLIME: + case ENTITY_AIRWIZARD: + case ENTITY_DRAGON: + hurtEntity(e, 1+rand()%3, dir, 0xFF0000FF, &(pd->entity)); + return true; + + case ENTITY_MAGIC_PILLAR: + playSoundPositioned(snd_monsterHurt, e->level, e->x, e->y); + + removeEntityFromList(e, e->level, &eManager); + return true; + } break; } 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, 0xFFAF00FF); - 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, 0xFFAF00FF); - 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, 0xFFAF00FF); - break; - case ENTITY_SPARK: - if(e2 != e1->spark.parent) hurtEntity(e2, 1, -1, 0xFFAF00FF); - break; - } + int damage = 1; + switch(e1->type){ + case ENTITY_PLAYER: playerEntityInteract(e1->p.data, e2); break; + case ENTITY_ZOMBIE: + case ENTITY_SKELETON: + case ENTITY_KNIGHT: + if(e2->type == ENTITY_PLAYER){ + if(e1->type == ENTITY_ZOMBIE) hurtEntity(e2, 2, e1->hostile.dir, 0xFFAF00FF, e1); + else if(e1->type == ENTITY_SKELETON) hurtEntity(e2, 1, e1->hostile.dir, 0xFFAF00FF, e1); + else if(e1->type == ENTITY_KNIGHT) hurtEntity(e2, 3, e1->hostile.dir, 0xFFAF00FF, e1); + switch(e1->hostile.dir){ + case 0: e1->yKnockback = -4; break; + case 1: e1->yKnockback = +4; break; + 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, 0xFFAF00FF, e1); + 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, 0xFFAF00FF, e1); + break; + case ENTITY_SPARK: + if(e2 != e1->spark.parent) hurtEntity(e2, 1, -1, 0xFFAF00FF, e1); + break; + case ENTITY_DRAGON: + if(e2->type == ENTITY_PLAYER) hurtEntity(e2, 3, e1->dragon.dir, 0xFFAF00FF, e1); + break; + case ENTITY_DRAGONPROJECTILE: + if(e2 != e1->dragonFire.parent) hurtEntity(e2, 1, -1, 0xFFAF00FF, e1); + break; + case ENTITY_ARROW: + switch(e1->arrow.itemID) { + case ITEM_ARROW_WOOD: + damage = 1 + (rand()%3); + break; + case ITEM_ARROW_STONE: + damage = 2 + (rand()%4); + break; + case ITEM_ARROW_IRON: + damage = 8 + (rand()%9); + break; + case ITEM_ARROW_GOLD: + damage = 16 + (rand()%9); + break; + case ITEM_ARROW_GEM: + damage = 24 + (rand()%9); + break; + } + + if(e1->arrow.parent->type == ENTITY_PLAYER) { + if(e2->type != ENTITY_PLAYER) { + hurtEntity(e2, damage, -1, 0xFF0000FF, e1); + removeEntityFromList(e1, e1->level, &eManager); + } + } else { + if(e2->type == ENTITY_PLAYER) { + hurtEntity(e2, damage, -1, 0xFFAF00FF, e1); + removeEntityFromList(e1, e1->level, &eManager); + } + } + 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; + switch(e1->type){ + case ENTITY_PLAYER: + case ENTITY_FURNITURE: + switch(e2->type){ + case ENTITY_FURNITURE: + case ENTITY_ZOMBIE: + case ENTITY_SKELETON: + case ENTITY_KNIGHT: + case ENTITY_SLIME: + case ENTITY_AIRWIZARD: + case ENTITY_DRAGON: + case ENTITY_PLAYER: + case ENTITY_PASSIVE: + case ENTITY_MAGIC_PILLAR: + case ENTITY_NPC: + 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 TILE_WOOD_WALL: - case 255: - return true; - case TILE_WATER: - if(e != NULL && !e->canSwim) return true; - case TILE_HOLE: - if(e != NULL && e->type != ENTITY_PLAYER) return true; - } - return false; + switch(tile){ + case TILE_ROCK: + case TILE_HARDROCK: + case TILE_MAGIC_BARRIER: + case TILE_DUNGEON_WALL: + return true; + case TILE_TREE: + case TILE_CACTUS: + case TILE_IRONORE: + case TILE_GOLDORE: + case TILE_GEMORE: + case TILE_CLOUDCACTUS: + case TILE_WOOD_WALL: + case TILE_STONE_WALL: + case TILE_IRON_WALL: + case TILE_GOLD_WALL: + case TILE_GEM_WALL: + case TILE_BOOKSHELVES: + case TILE_MUSHROOM_BROWN: + case TILE_MUSHROOM_RED: + if(e->type != ENTITY_DRAGON) return true; + case TILE_LAVA: + case 255: + if(e->type != ENTITY_ARROW) return true; + case TILE_WATER: + if(e != NULL && !e->canSwim && e->type != ENTITY_ARROW) return true; + case TILE_HOLE: + if(e != NULL && e->type != ENTITY_PLAYER && e->type != ENTITY_ARROW) return true; + } + return false; } /* For minimap */ u32 getTileColor(int tile){ - switch(tile){ - case TILE_WATER: return 0x0000FFFF; - case TILE_LAVA: return 0xFF0000FF; - case TILE_DIRT: return 0x826D6CFF; - case TILE_ROCK: return 0x7F7F7FFF; - case TILE_HARDROCK: return 0x5F5F7FFF; - case TILE_GRASS: return 0x00FF00FF; - case TILE_TREE: return 0x007F00FF; - case TILE_SAND: return 0xFFFF00FF; - case TILE_CACTUS: return 0x009F00FF; - case TILE_FLOWER: return 0x3FFF00FF; - case TILE_IRONORE: return 0xDC9696FF; - case TILE_GOLDORE: return 0xE5E89AFF; - case TILE_GEMORE: return 0xDF98DEFF; - case TILE_CLOUD: return 0xFFFFFFFF; - case TILE_CLOUDCACTUS: return 0xAFAFAFFF; - case TILE_STAIRS_DOWN: return 0x9F9F9FFF; - case TILE_STAIRS_UP: return 0x9F9F9FFF; - case TILE_HOLE: return 0x383838FF; - case TILE_WOOD_WALL: return 0xC1A55EFF; - default: return 0x111111FF; - } + switch(tile){ + case TILE_WATER: return SWAP_UINT32(waterColor[0]); + case TILE_LAVA: return SWAP_UINT32(lavaColor[0]); + case TILE_DIRT: return 0x826D6CFF; + case TILE_ROCK: return SWAP_UINT32(rockColor[1]); + case TILE_HARDROCK: return SWAP_UINT32(rockColor[3]); + case TILE_GRASS: return SWAP_UINT32(grassColor); + case TILE_TREE: return 0x007F00FF; + case TILE_SAND: return SWAP_UINT32(sandColor); + case TILE_CACTUS: return 0x009F00FF; + case TILE_FLOWER: return SWAP_UINT32(grassColor); + case TILE_IRONORE: return SWAP_UINT32(ironColor); + case TILE_GOLDORE: return SWAP_UINT32(goldColor); + case TILE_GEMORE: return SWAP_UINT32(gemColor); + case TILE_CLOUD: return 0xFFFFFFFF; + case TILE_CLOUDCACTUS: return 0xAFAFAFFF; + case TILE_STAIRS_DOWN: return 0x9F9F9FFF; + case TILE_STAIRS_UP: return 0x9F9F9FFF; + case TILE_HOLE: return 0x383838FF; + case TILE_WOOD_WALL: return SWAP_UINT32(woodColor); + case TILE_STONE_WALL: return SWAP_UINT32(rockColor[1]); + case TILE_IRON_WALL: return SWAP_UINT32(ironColor); + case TILE_GOLD_WALL: return SWAP_UINT32(goldColor); + case TILE_GEM_WALL: return SWAP_UINT32(gemColor); + case TILE_DUNGEON_WALL: return SWAP_UINT32(dungeonColor[0]); + case TILE_DUNGEON_FLOOR: return SWAP_UINT32(dungeonColor[1]); + case TILE_MAGIC_BARRIER: return SWAP_UINT32(dungeonColor[0]); + case TILE_BOOKSHELVES: return SWAP_UINT32(woodColor); + case TILE_WOOD_FLOOR: return SWAP_UINT32(woodColor); + case TILE_MYCELIUM: return SWAP_UINT32(myceliumColor); + case TILE_MUSHROOM_BROWN: return SWAP_UINT32(mushroomColor); + case TILE_MUSHROOM_RED: return SWAP_UINT32(mushroomColor); + case TILE_ICE: return SWAP_UINT32(iceColor); + + default: return 0x111111FF; + } } -void healPlayer(int amount){ - player.p.health += amount; - if(player.p.health > 10) player.p.health = 10; - char healText[11]; - sprintf(healText, "%d", amount); - addEntityToList(newTextParticleEntity(healText,0xFF00FF00,player.x,player.y,currentLevel), &eManager); -} -s8 itemTileInteract(int tile, Item* item, int x, int y, int px, int py, int dir){ - - // Furniture items - if(item->id > 27 && item->id < 34){ - 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 == ITEM_WOOD){ - setTile(TILE_WOOD_WALL,x,y); --item->countLevel; - 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 == ITEM_WOOD){ - setTile(TILE_WOOD_WALL,x,y); --item->countLevel; - return 1; - } - else if(item->id == ITEM_SAND){ - setTile(TILE_SAND,x,y); --item->countLevel; - return 1; - } - else if(item->id == TOOL_SHOVEL && playerUseEnergy(4-item->countLevel)){ - addEntityToList(newItemEntity(newItem(ITEM_DIRT,1), (x<<4)+8, (y<<4)+8, currentLevel), &eManager); - setTile(TILE_HOLE,x,y); - return 1; - } break; - case TILE_HOLE: - if(item->id == ITEM_DIRT){ - setTile(TILE_DIRT,x,y); - --item->countLevel; - return 1; + +s8 itemTileInteract(int tile, PlayerData *pd, Item *item, s8 level, int x, int y, int px, int py, int dir){ + + // Furniture items + if(item->id > 27 && item->id < 51){ + if(!tileIsSolid(getTile(level, x, y), NULL)){ + addEntityToList(newFurnitureEntity(item->id, item->chestPtr, (x<<4)+8, (y<<4)+8, level), &eManager); + removeItemFromCurrentInv(item); + pd->activeItem = &noItem; + return 2; + } + return 0; + } + + switch(tile){ + case TILE_TREE: + if(item->id == TOOL_AXE && playerUseEnergy(pd, 4-item->countLevel)){ + playerHurtTile(pd, tile, level, x, y, (rand()%10) + (item->countLevel) * 5 + 10, pd->entity.p.dir); + return 1; + } break; + case TILE_ROCK: + case TILE_HARDROCK: + if(item->id == TOOL_PICKAXE && playerUseEnergy(pd, 4-item->countLevel)){ + playerHurtTile(pd, tile, level, x, y, (rand()%10) + (item->countLevel) * 5 + 10, pd->entity.p.dir); + return 1; + } break; + case TILE_IRONORE: + case TILE_GOLDORE: + case TILE_GEMORE: + case TILE_CLOUDCACTUS: + if(item->id == TOOL_PICKAXE && playerUseEnergy(pd, 4-item->countLevel)){ + playerHurtTile(pd, tile, level, x, y, 1, pd->entity.p.dir); + return 1; + } break; + case TILE_GRASS: + if(item->id == TOOL_HOE && playerUseEnergy(pd, 4-item->countLevel)){ + setTile(TILE_FARM, level, x, y); + return 1; } - else if(item->id == TOOL_BUCKET && item->countLevel == 1 && playerUseEnergy(4)) { - setTile(TILE_WATER,x,y); - item->countLevel = 0; - } - else if(item->id == TOOL_BUCKET && item->countLevel == 2 && playerUseEnergy(4)) { - setTile(TILE_LAVA,x,y); - item->countLevel = 0; - } break; - case TILE_WATER: - if(item->id == ITEM_DIRT){ - setTile(TILE_DIRT,x,y); - --item->countLevel; - return 1; + else if(item->id == ITEM_ACORN){ + setTile(TILE_SAPLING_TREE, level, x, y); --item->countLevel; + return 1; } - else if(item->id == TOOL_BUCKET && item->countLevel == 0 && playerUseEnergy(4)) { - setTile(TILE_HOLE,x,y); + else if(item->id == ITEM_FLOWER){ + setTile(TILE_FLOWER, level, x, y); --item->countLevel; + setData(rand()%4, level, x, y); // determines mirroring. + return 1; + } + else if(item->id == ITEM_WALL_WOOD){ + setTile(TILE_WOOD_WALL, level, x, y); --item->countLevel; + return 1; + } + else if(item->id == ITEM_WALL_STONE){ + setTile(TILE_STONE_WALL, level, x, y); --item->countLevel; + return 1; + } + else if(item->id == ITEM_WALL_IRON){ + setTile(TILE_IRON_WALL, level, x, y); --item->countLevel; + return 1; + } + else if(item->id == ITEM_WALL_GOLD){ + setTile(TILE_GOLD_WALL, level, x, y); --item->countLevel; + return 1; + } + else if(item->id == ITEM_WALL_GEM){ + setTile(TILE_GEM_WALL, level, x, y); --item->countLevel; + return 1; + } + else if(item->id == ITEM_BOOKSHELVES){ + setTile(TILE_BOOKSHELVES, level, x, y); --item->countLevel; + setData(rand()%3, level, x, y); //determines sprite + return 1; + } + else if(item->id == TOOL_SHOVEL && playerUseEnergy(pd, 4-item->countLevel)){ + if(rand()%5==0) addItemsToWorld(newItem(ITEM_SEEDS,1), level, (x<<4)+8, (y<<4)+8, 1); + setTile(TILE_DIRT, level, x, y); + return 1; + } break; + case TILE_SAND: + if(item->id == ITEM_CACTUS){ + setTile(TILE_SAPLING_CACTUS, level, x, y); + --item->countLevel; + return 1; + } + else if(item->id == TOOL_SHOVEL && playerUseEnergy(pd, 4-item->countLevel)){ + addItemsToWorld(newItem(ITEM_SAND,1), level, (x<<4)+8, (y<<4)+8, 1); + setTile(TILE_DIRT, level, x, y); + return 1; + } break; + case TILE_DIRT: + if(item->id == TOOL_HOE && pd->entity.level==1 && playerUseEnergy(pd, 4-item->countLevel)){ + setTile(TILE_FARM, level, x, y); + return 1; + } + else if(item->id == ITEM_WALL_WOOD){ + setTile(TILE_WOOD_WALL, level, x, y); --item->countLevel; + return 1; + } + else if(item->id == ITEM_WALL_STONE){ + setTile(TILE_STONE_WALL, level, x, y); --item->countLevel; + return 1; + } + else if(item->id == ITEM_WALL_IRON){ + setTile(TILE_IRON_WALL, level, x, y); --item->countLevel; + return 1; + } + else if(item->id == ITEM_WALL_GOLD){ + setTile(TILE_GOLD_WALL, level, x, y); --item->countLevel; + return 1; + } + else if(item->id == ITEM_WALL_GEM){ + setTile(TILE_GEM_WALL, level, x, y); --item->countLevel; + return 1; + } + else if(item->id == ITEM_BOOKSHELVES){ + setTile(TILE_BOOKSHELVES, level, x, y); --item->countLevel; + setData(rand()%3, level, x, y); //determines sprite + return 1; + } + else if(item->id == ITEM_WOOD) { + setTile(TILE_WOOD_FLOOR, level, x, y); --item->countLevel; + return 1; + } + else if(item->id == ITEM_SAND){ + setTile(TILE_SAND, level, x, y); --item->countLevel; + return 1; + } + else if(item->id == TOOL_SHOVEL && playerUseEnergy(pd, 4-item->countLevel)){ + addItemsToWorld(newItem(ITEM_DIRT,1), level, (x<<4)+8, (y<<4)+8, 1); + setTile(TILE_HOLE, level, x, y); + return 1; + } break; + case TILE_HOLE: + if(item->id == ITEM_DIRT){ + setTile(TILE_DIRT, level, x, y); + --item->countLevel; + return 1; + } + else if(item->id == TOOL_BUCKET && item->countLevel == 1 && playerUseEnergy(pd, 4)) { + setTile(TILE_WATER, level, x, y); + item->countLevel = 0; + } + else if(item->id == TOOL_BUCKET && item->countLevel == 2 && playerUseEnergy(pd, 4)) { + setTile(TILE_LAVA, level, x, y); + item->countLevel = 0; + } break; + case TILE_WATER: + if(item->id == ITEM_DIRT){ + setTile(TILE_DIRT, level, x, y); + --item->countLevel; + return 1; + } + else if(item->id == TOOL_BUCKET && item->countLevel == 0 && playerUseEnergy(pd, 4)) { + setTile(TILE_HOLE, level, x, y); item->countLevel = 1; - } break; - case TILE_LAVA: - if(item->id == ITEM_DIRT){ - setTile(TILE_DIRT,x,y); - --item->countLevel; - return 1; - } - else if(item->id == TOOL_BUCKET && item->countLevel == 0 && playerUseEnergy(4)) { - setTile(TILE_HOLE,x,y); + } break; + case TILE_LAVA: + if(item->id == ITEM_DIRT){ + setTile(TILE_DIRT, level, x, y); + --item->countLevel; + return 1; + } + else if(item->id == TOOL_BUCKET && item->countLevel == 0 && playerUseEnergy(pd, 4)) { + setTile(TILE_HOLE, level, x, y); item->countLevel = 2; - } break; - case TILE_NULL: - if(item->id == ITEM_CLOUD){ - setTile(TILE_CLOUD,x,y); - --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; + } break; + case TILE_NULL: + if(item->id == ITEM_CLOUD){ + setTile(TILE_CLOUD, level, x, y); + --item->countLevel; + return 1; + } break; + case TILE_FARM: + if(item->id == ITEM_SEEDS){ + setTile(TILE_WHEAT, level, x, y); + setData(0, level, x, y); + --item->countLevel; + return 1; + } break; + case TILE_WHEAT: + if(item->id == TOOL_HOE){ + if(getData(level, x, y) > -1){ + int age = getData(level, x, y); + int count = (rand() % 2); + if(age >= 80) count = (rand()%2) + 1; + addItemsToWorld(newItem(ITEM_SEEDS,1), level, (x<<4)+8, (y<<4)+8, count); + count = 0; + if(age == 100) count = (rand()%3) + 2; + else if(age >= 80) count = (rand()%2) + 1; + addItemsToWorld(newItem(ITEM_WHEAT,1), level, (x<<4)+8, (y<<4)+8, count); + setTile(TILE_DIRT, level, x, y); + } + } break; case TILE_WOOD_WALL: - if(item->id == TOOL_AXE && playerUseEnergy(4-item->countLevel)){ - playerHurtTile(tile, x, y, (rand()%10) + (item->countLevel) * 5 + 10, player.p.dir); - return 1; - } break; - } - return 0; + case TILE_BOOKSHELVES: + if(item->id == TOOL_AXE && playerUseEnergy(pd, 4-item->countLevel)){ + playerHurtTile(pd, tile, level, x, y, (rand()%10) + (item->countLevel) * 5 + 10, pd->entity.p.dir); + return 1; + } break; + case TILE_STONE_WALL: + case TILE_IRON_WALL: + case TILE_GOLD_WALL: + case TILE_GEM_WALL: + if(item->id == TOOL_PICKAXE && playerUseEnergy(pd, 4-item->countLevel)){ + playerHurtTile(pd, tile, level, x, y, (rand()%10) + (item->countLevel) * 5 + 10, pd->entity.p.dir); + return 1; + } break; + case TILE_WOOD_FLOOR: + if(item->id == TOOL_AXE && playerUseEnergy(pd, 4-item->countLevel)){ + addItemsToWorld(newItem(ITEM_WOOD,1), level, (x<<4)+8, (y<<4)+8, 1); + setTile(TILE_DIRT, level, x, y); + } break; + } + return 0; } -void tickTile(int x, int y){ - int tile = getTile(x,y); - int data = getData(x,y); - - 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); - - if(getTile(x+1,y)==TILE_WATER || getTile(x-1,y)==TILE_WATER || getTile(x,y+1)==TILE_WATER || getTile(x,y-1)==TILE_WATER) { - setTile(TILE_ROCK,x,y); - setData(0,x,y); +void tickTile(s8 level, int x, int y){ + int tile = getTile(level, x, y); + int data = getData(level, x, y); + + switch(tile){ + case TILE_SAPLING_TREE: + if(worldData.season!=3) { + setData(++data, level, x, y); + if(data>100){setData(0, level, x, y); setTile(TILE_TREE, level, x, y);} } - break; - case TILE_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; - } - + break; + case TILE_TREE: + if(eManager.lastSlot[level]<800 && (worldData.daytime>18000 || worldData.daytime<5000) && rand()%800==0) { + //check for nearby glowworms + //TODO: This should really use getEntities + int i = 0; + for (i = 0; i < eManager.lastSlot[level]; ++i) { + Entity * e = &eManager.entities[level][i]; + if(e->type==ENTITY_GLOWWORM && ((e->x)-(x<<4))*((e->x)-(x<<4))+((e->y)-(y<<4))*((e->y)-(y<<4)) < (2<<4)*(2<<4)) return; + } + addEntityToList(newGlowwormEntity((x<<4)+8, (y<<4)+8, level), &eManager); + } + break; + case TILE_SAPLING_CACTUS: + if(worldData.season!=3) { + setData(++data, level, x, y); + if(data>100){setData(0, level, x, y); setTile(TILE_CACTUS, level, x, y);} + } + break; + case TILE_WHEAT: + if(data<100 && worldData.season!=3) setData(++data, level, x, y); + break; + case TILE_WATER: + if(getTile(level, x+1, y)==TILE_HOLE) setTile(TILE_WATER, level, x+1, y); + if(getTile(level, x-1, y)==TILE_HOLE) setTile(TILE_WATER, level, x-1, y); + if(getTile(level, x, y+1)==TILE_HOLE) setTile(TILE_WATER, level, x, y+1); + if(getTile(level, x, y-1)==TILE_HOLE) setTile(TILE_WATER, level, x, y-1); + if(level==1 && worldData.season==3 && rand()%12==0) { + if(getTile(level, x+1, y)!=TILE_WATER) setTile(TILE_ICE, level, x, y); + if(getTile(level, x-1, y)!=TILE_WATER) setTile(TILE_ICE, level, x, y); + if(getTile(level, x, y+1)!=TILE_WATER) setTile(TILE_ICE, level, x, y); + if(getTile(level, x, y-1)!=TILE_WATER) setTile(TILE_ICE, level, x, y); + } + break; + case TILE_LAVA: + if(getTile(level, x+1, y)==TILE_HOLE) setTile(TILE_LAVA, level, x+1, y); + if(getTile(level, x-1, y)==TILE_HOLE) setTile(TILE_LAVA, level, x-1, y); + if(getTile(level, x, y+1)==TILE_HOLE) setTile(TILE_LAVA, level, x, y+1); + if(getTile(level, x, y-1)==TILE_HOLE) setTile(TILE_LAVA, level, x, y-1); + + if(getTile(level, x+1, y)==TILE_WATER || getTile(level, x-1, y)==TILE_WATER || getTile(level, x, y+1)==TILE_WATER || getTile(level, x, y-1)==TILE_WATER) { + setTile(TILE_ROCK, level, x, y); + } + break; + case TILE_HOLE: // This makes water flow slightly faster than lava + if(getTile(level, x+1, y)==TILE_WATER) setTile(TILE_WATER, level, x, y); + if(getTile(level, x-1, y)==TILE_WATER) setTile(TILE_WATER, level, x, y); + if(getTile(level, x, y+1)==TILE_WATER) setTile(TILE_WATER, level, x, y); + if(getTile(level, x, y-1)==TILE_WATER) setTile(TILE_WATER, level, x, y); + break; + case TILE_GRASS: + if(getTile(level, x+1, y)==TILE_DIRT) if((rand()%25) == 0) setTile(TILE_GRASS, level, x+1, y); + if(getTile(level, x-1, y)==TILE_DIRT) if((rand()%25) == 0) setTile(TILE_GRASS, level, x-1, y); + if(getTile(level, x, y+1)==TILE_DIRT) if((rand()%25) == 0) setTile(TILE_GRASS, level, x, y+1); + if(getTile(level, x, y-1)==TILE_DIRT) if((rand()%25) == 0) setTile(TILE_GRASS, level, x, y-1); + break; + case TILE_SAND: + if(data > 0) setData(--data, level, x, y); + break; + case TILE_CLOUD: + if((rand()%24000)==0) setTile(TILE_CLOUDCACTUS, level, x, y); + break; + case TILE_MAGIC_BARRIER: + data = 0; + int i = 0; + for (i = 0; i < eManager.lastSlot[level]; ++i) { + Entity * e = &eManager.entities[level][i]; + + if(e->type == ENTITY_MAGIC_PILLAR) { + ++data; + } + } + if(data==0) setTile(TILE_DUNGEON_FLOOR, level, x, y); + setData(rand()%2, level, x, y); + break; + case TILE_ICE: + if(worldData.season!=3) { + setTile(TILE_WATER, level, x, y); + } + 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; + ++e->entityItem.age; + if(e->entityItem.age == 630){ + removeEntityFromList(e, e->level, &eManager); + /* + Programming pro tip: + Remember to put a return statement after you remove the entity, + or else your going to have a very bad time like I did. + */ + return; + } + e->entityItem.xx += e->entityItem.xa; + e->entityItem.yy += e->entityItem.ya; + e->entityItem.zz += e->entityItem.za; + if (e->entityItem.zz < 0) { + e->entityItem.zz = 0; + e->entityItem.za *= -0.5; + e->entityItem.xa *= 0.6; + e->entityItem.ya *= 0.6; + } + e->entityItem.za -= 0.15; + int ox = e->x; + int oy = e->y; + int nx = (int) e->entityItem.xx; + int ny = (int) e->entityItem.yy; + int expectedx = nx - e->x; + int expectedy = ny - e->y; + move(e, nx - e->x, ny - e->y); + int gotx = e->x - ox; + int goty = e->y - oy; + e->entityItem.xx += gotx - expectedx; + e->entityItem.yy += goty - expectedy; } void tickEntityTextParticle(Entity* e){ - ++e->textParticle.age; - if(e->textParticle.age == 60){ - removeEntityFromList(e,e->level,&eManager); - return; - } - e->textParticle.xx += e->textParticle.xa; - e->textParticle.yy += e->textParticle.ya; - e->textParticle.zz += e->textParticle.za; - if (e->textParticle.zz < 0) { - e->textParticle.zz = 0; - e->textParticle.za *= -0.5; - e->textParticle.xa *= 0.6; - e->textParticle.ya *= 0.6; - } - e->textParticle.za -= 0.15; - e->x = (int) e->textParticle.xx; - e->y = (int) e->textParticle.yy; + ++e->textParticle.age; + if(e->textParticle.age == 60){ + removeEntityFromList(e, e->level, &eManager); + return; + } + e->textParticle.xx += e->textParticle.xa; + e->textParticle.yy += e->textParticle.ya; + e->textParticle.zz += e->textParticle.za; + if (e->textParticle.zz < 0) { + e->textParticle.zz = 0; + e->textParticle.za *= -0.5; + e->textParticle.xa *= 0.6; + e->textParticle.ya *= 0.6; + } + e->textParticle.za -= 0.15; + e->x = (int) e->textParticle.xx; + e->y = (int) e->textParticle.yy; } void tickEntity(Entity* e){ - 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++; + PlayerData *nearestPlayer = getNearestPlayer(e->level, e->x, e->y); + switch(e->type){ + case ENTITY_ITEM: tickEntityItem(e); return; + case ENTITY_FURNITURE: return; + case ENTITY_MAGIC_PILLAR: return; + case ENTITY_ZOMBIE: + case ENTITY_SKELETON: + case ENTITY_KNIGHT: + if (e->hurtTime > 0) e->hurtTime--; + if (e->hostile.randWalkTime == 0 && e->type != ENTITY_SKELETON && nearestPlayer!=NULL) { + int xd = nearestPlayer->entity.x - e->x; + int yd = nearestPlayer->entity.y - e->y; + int dist = 50 * 50; + if(e->type == ENTITY_KNIGHT) dist = 80 * 80; - 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++; - awX = e->x; - awY = e->y; - } - - 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; - } + //charge player + if (xd * xd + yd * yd < dist) { + e->hostile.xa = 0; + e->hostile.ya = 0; + if (xd < 0) e->hostile.xa = -1; + if (xd > 0) e->hostile.xa = +1; + if (yd < 0) e->hostile.ya = -1; + if (yd > 0) e->hostile.ya = +1; + } + } + + if(e->hostile.xa < 0) e->hostile.dir = 2; + else if(e->hostile.xa > 0) e->hostile.dir = 3; + if(e->hostile.ya < 0) e->hostile.dir = 1; + else if(e->hostile.ya > 0) e->hostile.dir = 0; + + if(e->type == ENTITY_SKELETON) { + --(e->hostile.randAttackTime); + if(e->hostile.randAttackTime <= 0) { + e->hostile.randAttackTime = 80 - (e->hostile.lvl * 5); + + int aitemID = ITEM_ARROW_WOOD; + if(e->hostile.lvl >= 2) aitemID = ITEM_ARROW_STONE; + + //turn to player when attacking + if(nearestPlayer!=NULL) { + int xd = nearestPlayer->entity.x - e->x; + int yd = nearestPlayer->entity.y - e->y; + if(xd*xd > yd*yd) { + if (xd < 0) e->hostile.dir = 2; + if (xd > 0) e->hostile.dir = 3; + } else { + if (yd < 0) e->hostile.dir = 1; + if (yd > 0) e->hostile.dir = 0; + } + } + + switch(e->hostile.dir) { + case 0: + addEntityToList(newArrowEntity(e, aitemID, 0, 2, e->level), &eManager); + break; + case 1: + addEntityToList(newArrowEntity(e, aitemID, 0, -2, e->level), &eManager); + break; + case 2: + addEntityToList(newArrowEntity(e, aitemID, -2, 0, e->level), &eManager); + break; + case 3: + addEntityToList(newArrowEntity(e, aitemID, 2, 0, e->level), &eManager); + break; + } + } + } + + if(e->hostile.xa != 0 || e->hostile.ya != 0) e->hostile.walkDist++; + + int speed = syncTickCount & 1; + if (!moveMob(e, e->hostile.xa * speed, e->hostile.ya * speed) || (rand()%100) == 0) { + e->hostile.randWalkTime = 60; + e->hostile.xa = ((rand()%3) - 1) * (rand()%2); + e->hostile.ya = ((rand()%3) - 1) * (rand()%2); + } + if (e->hostile.randWalkTime > 0) e->hostile.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); + + if(nearestPlayer!=NULL) { + int xd = nearestPlayer->entity.x - e->x; + int yd = nearestPlayer->entity.y - e->y; + if (xd * xd + yd * yd < 50 * 50) { + if (xd < 0) e->slime.xa = -1; + if (xd > 0) e->slime.xa = +1; + if (yd < 0) e->slime.ya = -1; + if (yd > 0) e->slime.ya = +1; + } + } + + if (e->slime.xa != 0 || e->slime.ya != 0) e->slime.jumpTime = 10; + } + } + + 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 && nearestPlayer!=NULL) { + int xd = nearestPlayer->entity.x - e->x; + int yd = nearestPlayer->entity.y - e->y; + int dist = xd * xd + yd * yd; + if (dist > 80 * 80) { + e->wizard.xa = 0; + 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 = (syncTickCount % 4) == 0 ? 0 : 1; + if (!moveMob(e, e->wizard.xa * wSpeed, e->wizard.ya * wSpeed) || (rand()%100) == 0) { + e->wizard.randWalkTime = 30; + e->wizard.xa = ((rand()%3) - 1) * (rand()%2); + e->wizard.ya = ((rand()%3) - 1) * (rand()%2); + } + + if(e->wizard.xa != 0 || e->wizard.ya != 0){ + e->wizard.walkDist++; + awX = e->x; + awY = e->y; + } + + 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 && nearestPlayer!=NULL) { + int xd = nearestPlayer->entity.x - e->x; + int yd = nearestPlayer->entity.y - e->y; + if (rand()%4 == 0 && xd * xd + yd * yd < 50 * 50) { + if (e->wizard.attackDelay == 0 && e->wizard.attackTime == 0) e->wizard.attackDelay = 120; + } + } + } + + 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(nearestPlayer!=NULL && intersects(nearestPlayer->entity, e->x + e->spark.xa - e->xr, e->y + e->spark.ya - e->yr, e->x + e->spark.xa + e->xr, e->y + e->spark.ya + e->yr)){ + EntityVsEntity(e, &(nearestPlayer->entity)); + removeEntityFromList(e, e->level, &eManager); + } + return; + case ENTITY_DRAGON: + if (e->hurtTime > 0) e->hurtTime--; + + e->dragon.animTimer++; + if(e->dragon.animTimer>=4*4) { + e->dragon.animTimer = 0; + } + + //choose random attack + if (e->dragon.attackDelay > 0) { + e->dragon.attackDelay--; + if (e->dragon.attackDelay <= 0) { + e->dragon.attackType = rand()%2; + e->dragon.attackTime = 121; + } + return; + } + + if (e->dragon.attackTime > 0) { + e->dragon.attackTime--; + + //turn to player when attacking + if(nearestPlayer!=NULL) { + int xd = nearestPlayer->entity.x - e->x; + int yd = nearestPlayer->entity.y - e->y; + if(xd*xd > yd*yd) { + if (xd < 0) e->dragon.dir = 2; + if (xd > 0) e->dragon.dir = 3; + } else { + if (yd < 0) e->dragon.dir = 1; + if (yd > 0) e->dragon.dir = 0; + } + } + + switch(e->dragon.attackType) { + case 0: //Firebreathing + if(e->dragon.attackTime%2 == 0) { + float dfdir = 0; + + if(e->dragon.dir==0) dfdir = 1 * 3.141592 / 2; + else if(e->dragon.dir==1) dfdir = 3 * 3.141592 / 2; + else if(e->dragon.dir==2) dfdir = 2 * 3.141592 / 2; + else if(e->dragon.dir==3) dfdir = 0 * 3.141592 / 2; + + dfdir += 0.03141592 * ((rand()%33) - 16); + + addEntityToList(newDragonFireEntity(e, e->dragon.attackType, e->x + cos(dfdir)*14, e->y + sin(dfdir)*14, cos(dfdir), sin(dfdir)), &eManager); + } + break; + case 1: //Firering + if(e->dragon.attackTime%20 == 0) { + int ai = 0; + for(ai = 0; ai < 16; ai++) { + float ddir = (3.141592 * 2 / 16.0) * ai; + float ddist = (140 - e->dragon.attackTime) / 2; + + addEntityToList(newDragonFireEntity(e, e->dragon.attackType, (e->x) + cos(ddir)*ddist, (e->y) + sin(ddir)*ddist, 0, 0), &eManager); + } + } + break; + } + + return; + } + + //TODO - movement copied from airwizard, adjust to better fit dragon + if (e->dragon.randWalkTime == 0 && nearestPlayer!=NULL) { + int xd = nearestPlayer->entity.x - e->x; + int yd = nearestPlayer->entity.y - e->y; + int dist = xd * xd + yd * yd; + if (dist > 64 * 64) { + e->dragon.xa = 0; + e->dragon.ya = 0; + if (xd < 0) e->dragon.xa = -1; + if (xd > 0) e->dragon.xa = +1; + if (yd < 0) e->dragon.ya = -1; + if (yd > 0) e->dragon.ya = +1; + } else if (dist < 16 * 16) { + e->dragon.xa = 0; + e->dragon.ya = 0; + if (xd < 0) e->dragon.xa = +1; + if (xd > 0) e->dragon.xa = -1; + if (yd < 0) e->dragon.ya = +1; + if (yd > 0) e->dragon.ya = -1; + } + } + + int dSpeed = (syncTickCount % 4) == 0 ? 0 : 1; + if (!moveMob(e, e->dragon.xa * dSpeed, e->dragon.ya * dSpeed) || (rand()%120) == 0) { + e->dragon.randWalkTime = 30; + e->dragon.xa = ((rand()%3) - 1) * (rand()%2); + e->dragon.ya = ((rand()%3) - 1) * (rand()%2); + } + + if(e->dragon.xa != 0 || e->dragon.ya != 0){ + e->dragon.walkDist++; + } + + if(e->dragon.xa < 0) e->dragon.dir = 2; + else if(e->dragon.xa > 0) e->dragon.dir = 3; + if(e->dragon.ya < 0) e->dragon.dir = 1; + else if(e->dragon.ya > 0) e->dragon.dir = 0; + + //if (e->dragon.randWalkTime > 0) { + // e->dragon.randWalkTime--; + // if (e->dragon.randWalkTime == 0) { + if(nearestPlayer!=NULL) { + int xd = nearestPlayer->entity.x - e->x; + int yd = nearestPlayer->entity.y - e->y; + if (rand()%12 == 0 && xd * xd + yd * yd < 50 * 50) { + if (e->dragon.attackDelay == 0 && e->dragon.attackTime == 0) e->dragon.attackDelay = 40; + } + } + // } + //} + + return; + case ENTITY_DRAGONPROJECTILE: + e->dragonFire.age++; + if (e->dragonFire.age >= 30) { + removeEntityFromList(e, e->level, &eManager); + return; + } + e->dragonFire.xx += e->dragonFire.xa; + e->dragonFire.yy += e->dragonFire.ya; + e->x = (int) e->dragonFire.xx; + e->y = (int) e->dragonFire.yy; + + if(nearestPlayer!=NULL && intersects(nearestPlayer->entity, e->x + e->dragonFire.xa - e->xr, e->y + e->dragonFire.ya - e->yr, e->x + e->dragonFire.xa + e->xr, e->y + e->dragonFire.ya + e->yr)){ + EntityVsEntity(e, &(nearestPlayer->entity)); + removeEntityFromList(e, e->level, &eManager); + } + return; + case ENTITY_ARROW: + e->arrow.age++; + if (e->arrow.age >= 260 || !move(e, e->arrow.xa, e->arrow.ya)) { + //only drop arrows shot by player + if(e->arrow.parent->type == ENTITY_PLAYER) addItemsToWorld(newItem(e->arrow.itemID,1), e->level, e->x+4, e->y+4, 1); + removeEntityFromList(e, e->level, &eManager); + return; + } + return; + case ENTITY_PASSIVE: + if (e->hurtTime > 0) e->hurtTime--; + if (e->passive.randWalkTime == 0 && nearestPlayer!=NULL) { + int xd = nearestPlayer->entity.x - e->x; + int yd = nearestPlayer->entity.y - e->y; + //flee from player + if (xd * xd + yd * yd < 40 * 40) { + e->passive.xa = 0; + e->passive.ya = 0; + if (xd < 0) e->passive.xa = +1; + if (xd > 0) e->passive.xa = -1; + if (yd < 0) e->passive.ya = +1; + if (yd > 0) e->passive.ya = -1; + } + } + + if(e->passive.xa < 0) e->passive.dir = 2; + else if(e->passive.xa > 0) e->passive.dir = 3; + if(e->passive.ya < 0) e->passive.dir = 1; + else if(e->passive.ya > 0) e->passive.dir = 0; + + if(e->passive.xa != 0 || e->passive.ya != 0) e->passive.walkDist++; + + int pspeed = syncTickCount & 1; + if (!moveMob(e, e->passive.xa * pspeed, e->passive.ya * pspeed) || (rand()%100) == 0) { + e->passive.randWalkTime = 60; + e->passive.xa = ((rand()%3) - 1) * (rand()%2); + e->passive.ya = ((rand()%3) - 1) * (rand()%2); + } + if (e->passive.randWalkTime > 0) e->passive.randWalkTime--; + return; + case ENTITY_GLOWWORM: + if(worldData.daytime>5000 && worldData.daytime<6000) { + if(rand()%200==0) { + removeEntityFromList(e, e->level, &eManager); + return; + } + } else if(worldData.daytime>6000 && worldData.daytime<18000) { + removeEntityFromList(e, e->level, &eManager); + return; + } + + int gspeed = (((syncTickCount & 0x3) == 3) ? 1 : 0); + if (!moveMob(e, e->glowworm.xa * gspeed, e->glowworm.ya * gspeed) || (e->glowworm.randWalkTime==0) || (rand()%20) == 0) { + if(e->glowworm.randWalkTime != 0) { + e->glowworm.waitTime = 20 + (rand()%60); + } + if(e->glowworm.waitTime == 0 || getTile(e->level, (e->x)>>4, (e->y)>>4)!=TILE_TREE) { + e->glowworm.randWalkTime = 20; + e->glowworm.xa = ((rand()%3) - 1) * (rand()%2); + e->glowworm.ya = ((rand()%3) - 1) * (rand()%2); + } else { + e->glowworm.xa = 0; + e->glowworm.ya = 0; + } + } + if (e->glowworm.randWalkTime > 0) { + e->glowworm.randWalkTime--; + if(e->glowworm.randWalkTime==0 && (e->glowworm.xa != 0 || e->glowworm.xa != 0)) { + e->glowworm.waitTime = 120 + (rand()%60); + } + } else if (e->glowworm.waitTime > 0) { + e->glowworm.waitTime--; + } + 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; + int i, j; for (i = 0; i < count; i++) { - if(eManager.lastSlot[level] > 900) continue; + 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); + if(level > 4) { + minLevel = maxLevel = 4; + } + + int rx = rand()%128; + int ry = rand()%128; + int ex = (rx<<4)+8; + int ey = (ry<<4)+8; + + //do not spawn near players + for(j = 0; j players[j].entity.x-160 && ey > players[j].entity.y-125 && ex < players[j].entity.x+160 && ey < players[j].entity.y+125)) continue; + } + + //spawn if tile is free + if (!tileIsSolid(worldData.map[level][rx+ry*128],&e)) { + if(level==1 && (rand()%2)==0) { //passive entities on overworld + e = newPassiveEntity(rand()%3, ex, ey, level); + } else { + int lvl = (rand()%(maxLevel - minLevel + 1)) + minLevel; + int randMax = 1; + + if(level>1 || level==0) randMax = 2; + if(level>3) randMax = 3; + + switch (rand()%(randMax+1)) { + case 0: + e = newSlimeEntity(lvl, ex, ey, level); + break; + case 1: + e = newZombieEntity(lvl, ex, ey, level); + break; + case 2: + e = newSkeletonEntity(lvl, ex, ey, level); + break; + case 3: + e = newKnightEntity(lvl, ex, ey, level); + break; + } + } 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]; +int getTile(s8 level, int x, int y){ + if(x < 0 || y < 0 || x > 128 || y > 128) return -1; + return worldData.map[level][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 setTile(int id, s8 level, int x, int y){ + if(x < 0 || y < 0 || x > 128 || y > 128) return; + worldData.map[level][x+y*128] = id; + worldData.data[level][x+y*128] = 0; //reset data(set again if needed, hopefully this breaks nothing) + + sf2d_set_pixel(minimap[level], x, y, getMinimapColor(getLocalPlayer(), level, x, y)); } -void setData(int id, int x, int y){ - if(x < 0 || y < 0 || x > 128 || y > 128) return; - data[currentLevel][x+y*128] = id; +int getData(s8 level, int x, int y){ + if(x < 0 || y < 0 || x > 128 || y > 128) return -1; + return worldData.data[level][x+y*128]; } -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 setData(int id, s8 level, int x, int y){ + if(x < 0 || y < 0 || x > 128 || y > 128) return; + worldData.data[level][x+y*128] = id; } -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 addSmashParticles(s8 level, int x, int y, int damage) { + char hurtText[11]; + sprintf(hurtText, "%d", damage); + addEntityToList(newTextParticleEntity(hurtText, 0xFF0000FF, x, y, level), &eManager); + addEntityToList(newSmashParticleEntity(x, y, level), &eManager); } -void 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; - case TILE_WOOD_WALL: - sprintf(hurtText, "%d", damage); - addEntityToList(newTextParticleEntity(hurtText,0xFF0000FF,xt<<4,yt<<4,currentLevel), &eManager); - addEntityToList(newSmashParticleEntity(xt<<4,yt<<4,currentLevel), &eManager); - setData(getData(xt,yt)+damage,xt,yt); - if(getData(xt,yt) > 20){ - setTile(TILE_DIRT,xt,yt); - addItemsToWorld(newItem(ITEM_WOOD,1),(xt<<4)+8,(yt<<4)+8,1); - } - break; - } - -} +void damageAndBreakTile(s8 level, int xt, int yt, int damage, int maxDamage, int replaceTile, int numItems, ...) { + int i; -bool playerUseEnergy(int amount){ - if(amount > player.p.stamina) return false; - player.p.stamina -= amount; - return true; -} + //damage indicator + addSmashParticles(level, xt<<4, yt<<4, damage); -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; - } + //do damage + setData(getData(level, xt, yt)+damage, level, xt, yt); - 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; + //tile has been destroyed + if(getData(level, xt, yt)>maxDamage) { + setTile(replaceTile, level, xt, yt); - 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; + //drop items + va_list al; + numItems<<=1; //each item is the item+count, moved up here to get rid of warning + va_start(al, numItems); + numItems>>=1; + for(i=0; iid == 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 playerHurtTile(PlayerData *pd, int tile, s8 level, int xt, int yt, int damage, int dir){ + if(shouldRenderDebug) damage = 99; + if(UnderStrengthEffect) damage = damage + 4; + + //TODO: Most of this can be combined a lot + switch(tile){ + case TILE_TREE: + if(rand()%120==0)addEntityToList(newItemEntity(newItem(ITEM_APPLE,1), (xt<<4)+8,(yt<<4)+8, level), &eManager); + damageAndBreakTile(level, xt, yt, damage, 20, TILE_GRASS, 2, newItem(ITEM_WOOD,1), rand()%2+1, newItem(ITEM_ACORN,1), rand()%2); + break; + case TILE_CACTUS: + damageAndBreakTile(level, xt, yt, damage, 10, TILE_SAND, 1, newItem(ITEM_CACTUS,1), rand()%2+1); + break; + case TILE_ROCK: + damageAndBreakTile(level, xt, yt, damage, 50, TILE_DIRT, 2, newItem(ITEM_STONE,1), rand()%4+1, newItem(ITEM_COAL,1), rand()%2); + break; + case TILE_HARDROCK: + if((pd->activeItem->id != TOOL_PICKAXE || pd->activeItem->countLevel < 4) && !shouldRenderDebug) damage = 0; + damageAndBreakTile(level, xt, yt, damage, 200, TILE_DIRT, 2, newItem(ITEM_STONE,1), rand()%4+1, newItem(ITEM_COAL,1), rand()%2); + break; + case TILE_IRONORE: + case TILE_GOLDORE: + case TILE_GEMORE: + if(pd->activeItem->id != TOOL_PICKAXE) damage = 0; + addSmashParticles(level, xt<<4, yt<<4, damage); + setData(getData(level, xt, yt)+damage, level, xt, yt); + if(getData(level, xt, yt) > 0){ + int count = rand() & 1; + if (getData(level, xt, yt) >= (rand()%10) + 3) { + if(level!=5) setTile(TILE_DIRT, level, xt, yt); + else setTile(TILE_DUNGEON_FLOOR, level, xt, yt); + count += 2; + } + if(tile==TILE_IRONORE) addItemsToWorld(newItem(ITEM_IRONORE,1), level, (xt<<4)+8, (yt<<4)+8, count); + if(tile==TILE_GOLDORE) addItemsToWorld(newItem(ITEM_GOLDORE,1), level, (xt<<4)+8, (yt<<4)+8, count); + if(tile==TILE_GEMORE) addItemsToWorld(newItem(ITEM_GEM,1), level, (xt<<4)+8, (yt<<4)+8, count); + } break; + case TILE_CLOUDCACTUS: + if(pd->activeItem->id != TOOL_PICKAXE) damage = 0; + addSmashParticles(level, xt<<4, yt<<4, damage); + setData(getData(level, xt, yt)+damage, level, xt, yt); + if(getData(level, xt, yt) > 0){ + int count = rand() % 3; + if (getData(level, xt, yt) >= (rand()%10) + 3) { + setTile(TILE_CLOUD, level, xt, yt); + count += 3; + } + addItemsToWorld(newItem(ITEM_CLOUD,1), level, (xt<<4)+8, (yt<<4)+8, count); + } break; + case TILE_FARM: + setTile(TILE_DIRT, level, xt, yt); + break; + case TILE_SAPLING_TREE: + setTile(TILE_GRASS, level, xt, yt); + break; + case TILE_SAPLING_CACTUS: + setTile(TILE_SAND, level, xt, yt); + break; + case TILE_WHEAT: + if(getData(level, xt, yt) > -1){ + int age = getData(level, xt, yt); + int count = (rand() % 2); + if(age >= 80) count = (rand()%2) + 1; + addItemsToWorld(newItem(ITEM_SEEDS,1), level, (xt<<4)+8, (yt<<4)+8, count); + count = 0; + if(age == 100) count = (rand()%3) + 2; + else if(age >= 80) count = (rand()%2) + 1; + addItemsToWorld(newItem(ITEM_WHEAT,1), level, (xt<<4)+8, (yt<<4)+8, count); + setTile(TILE_DIRT, level, xt, yt); + } break; + case TILE_FLOWER: + setTile(TILE_GRASS, level, xt,yt); + addItemsToWorld(newItem(ITEM_FLOWER,1), level, (xt<<4)+8, (yt<<4)+8, 1); + break; + case TILE_WOOD_WALL: + damageAndBreakTile(level, xt, yt, damage, 20, TILE_DIRT, 1, newItem(ITEM_WALL_WOOD,1), 1); + break; + case TILE_STONE_WALL: + damageAndBreakTile(level, xt, yt, damage, 30, TILE_DIRT, 1, newItem(ITEM_WALL_STONE,1), 1); + break; + case TILE_IRON_WALL: + damageAndBreakTile(level, xt, yt, damage, 40, TILE_DIRT, 1, newItem(ITEM_WALL_IRON,1), 1); + break; + case TILE_GOLD_WALL: + damageAndBreakTile(level, xt, yt, damage, 50, TILE_DIRT, 1, newItem(ITEM_WALL_GOLD,1), 1); + break; + case TILE_GEM_WALL: + damageAndBreakTile(level, xt, yt, damage, 60, TILE_DIRT, 1, newItem(ITEM_WALL_GEM,1), 1); + break; + case TILE_BOOKSHELVES: + addSmashParticles(level, xt<<4, yt<<4, damage); + if(level!=5) setTile(TILE_DIRT, level, xt, yt); + else setTile(TILE_DUNGEON_FLOOR, level, xt, yt); + addItemsToWorld(newItem(ITEM_BOOKSHELVES,1), level, (xt<<4)+8, (yt<<4)+8, 1); + break; + } + +} + + +void switchLevel(PlayerData *pd, s8 change){ + pd->entity.level+=change; + if(pd->entity.level > 4) pd->entity.level = 0; else if(pd->entity.level < 0) pd->entity.level = 4; + + if(pd==getLocalPlayer()) { + if(pd->entity.level == 1) sf2d_set_clear_color(0xFF6C6D82); + else if(pd->entity.level > 1) sf2d_set_clear_color(0xFF666666); + else sf2d_set_clear_color(0xFF007F00); + } + + //for level 0 background + updateLevel1Map(); +} + +void playerEntityInteract(PlayerData *pd, Entity* e){ + switch(e->type){ + case ENTITY_ITEM: + if(e->entityItem.age > 30){ + addItemToInventory(e->entityItem.item, &(pd->inventory)); + removeEntityFromList(e, e->level, &eManager); + playSoundPositioned(snd_pickup, pd->entity.level, pd->entity.x, pd->entity.y); + pd->score++; } - } + break; + case ENTITY_FURNITURE: + switch(pd->entity.p.dir){ + case 0: if(pd->entity.y < e->y) move(e, 0, 2); break; + case 1: if(pd->entity.y > e->y) move(e, 0, -2); break; + case 2: if(pd->entity.x > e->x) move(e, -2, 0); break; + case 3: if(pd->entity.x < e->x) move(e, 2, 0); break; + } + break; + } } - -void switchLevel(s8 change){ - currentLevel+=change; - if(currentLevel > 4) currentLevel = 0; else if(currentLevel < 0) currentLevel = 4; - if(currentLevel == 1) sf2d_set_clear_color(0xFF6C6D82); //sf2d_set_clear_color(RGBA8(0x82, 0x6D, 0x6C, 0xFF)); - else if(currentLevel > 1) sf2d_set_clear_color(0xFF666666); //sf2d_set_clear_color(RGBA8(0x66, 0x66, 0x66, 0xFF)); - else sf2d_set_clear_color(0xFF007F00); //sf2d_set_clear_color(RGBA8(0x00, 0x7F, 0x00, 0xFF)); - - updateMusic(currentLevel); -} - -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,0xFFAF00FF); return; - case TILE_LAVA: if(e->type == ENTITY_PLAYER)hurtEntity(e,1,-1,0xFFAF00FF); 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; - } +void entityTileInteract(Entity*e, int tile, s8 level, int x, int y){ + switch(tile){ + case TILE_STAIRS_DOWN: + if(e->type == ENTITY_PLAYER){ + switchLevel(e->p.data, 1); + e->x = (x << 4) + 8; + e->y = (y << 4) + 8; + } + return; + case TILE_STAIRS_UP: + if(e->type == ENTITY_PLAYER){ + switchLevel(e->p.data, -1); + e->x = (x << 4) + 8; + e->y = (y << 4) + 8; + } + return; + case TILE_CACTUS: if(e->type == ENTITY_PLAYER) hurtEntity(e ,1, -1, 0xFFAF00FF, NULL); return; + case TILE_LAVA: if(e->type == ENTITY_PLAYER) hurtEntity(e, 1, -1, 0xFFAF00FF, NULL); return; + case TILE_WHEAT: + if(e->type == ENTITY_PLAYER || e->type == ENTITY_ZOMBIE){ + if(getData(level, x, y) > -1 && rand()%20 == 0){ + int age = getData(level, x, y); + int count = (rand() % 2); + if(age >= 80) count = (rand()%2) + 1; + addItemsToWorld(newItem(ITEM_SEEDS,1), level, (x<<4)+8, (y<<4)+8, count); + count = 0; + if(age == 100) count = (rand()%3) + 2; + else if(age >= 80) count = (rand()%2) + 1; + addItemsToWorld(newItem(ITEM_WHEAT,1), level, (x<<4)+8, (y<<4)+8, count); + setTile(TILE_DIRT, level, x, y); + } + } + return; + case TILE_FARM: + if(e->type == ENTITY_PLAYER || e->type == ENTITY_ZOMBIE){ + if(rand()%20 == 0) setTile(TILE_DIRT, level, x, y); + } + return; + case TILE_SAND: + if(e->type != ENTITY_ARROW && e->type != ENTITY_ITEM) { + setData(10, level, x, y); + } + return; + case TILE_DUNGEON_ENTRANCE: + if(e->type == ENTITY_PLAYER) { + e->p.data->ingameMenu = MENU_DUNGEON; + } + 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); + 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; +void openCraftingMenu(PlayerData *pd, RecipeManager *rm, char *title) { + pd->currentCraftTitle = title; + pd->ingameMenu = MENU_CRAFTING; + + cloneRecipeManager(rm, &(pd->currentRecipes)); + checkCanCraftRecipes(&(pd->currentRecipes), &(pd->inventory)); + sortRecipes(&(pd->currentRecipes)); } -bool useEntity(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 useEntity(PlayerData *pd, Entity* e) { + if(e->type == ENTITY_FURNITURE){ + switch(e->entityFurniture.itemID){ + case ITEM_WORKBENCH: + openCraftingMenu(pd, &workbenchRecipes, "Crafting"); + return true; + case ITEM_FURNACE: + openCraftingMenu(pd, &furnaceRecipes, "Smelting"); + return true; + case ITEM_OVEN: + openCraftingMenu(pd, &ovenRecipes, "Cooking"); + return true; + case ITEM_ANVIL: + openCraftingMenu(pd, &anvilRecipes, "Smithing"); + return true; + case ITEM_LOOM: + openCraftingMenu(pd, &loomRecipes, "Crafting"); + return true; + case ITEM_ENCHANTER: + openCraftingMenu(pd, &enchanterRecipes, "Enchanting"); + return true; + case ITEM_POTION_MAKER: + openCraftingMenu(pd, &potionMakerRecipes, "Brewing"); + return true; + case ITEM_CHEST: + pd->curChestEntity = e; + pd->ingameMenuInvSel = 0; + pd->ingameMenuInvSelOther = 0; + pd->curChestEntityR = 0; + pd->ingameMenu = MENU_CONTAINER; + return true; + } + } else if(e->type == ENTITY_NPC) { + openNPCMenu(pd, e->npc.type); + return true; + } + return false; } -bool use(int x0, int y0, int x1, int y1) { - Entity * entities[eManager.lastSlot[currentLevel]]; +bool isWater(s8 level, int xt, int yt){ + return getTile(level, xt, yt)==TILE_WATER; +} + +bool dungeonActive() { + //check if dungeon already exists (ie someone is in there) int i; - 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; + for(i = 0; i < playerCount; i++) { + if(players[i].entity.level==5) { + 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; +void enterDungeon(PlayerData *pd) { + //create new one if needed + if(!dungeonActive()) { + //reset Entities + (&eManager)->lastSlot[5] = 0; + (&eManager)->entities[5][0] = nullEntity; + + //create map + createAndValidateDungeonMap(128, 128, 5, worldData.map[5], worldData.data[5]); + + //reset minimap clear state + int xd,yd; + for(xd = 0; xd < 128; ++xd) { + for(yd = 0; yd < 128; ++yd) { + setMinimapVisible(pd, 5, xd, yd, false); + } + } + initMinimapLevel(pd, 5); + + //spawn new entities + trySpawn(500, 5); } - 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,0xFFAF00FF); - } - } - - 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; + pd->entity.level = 5; + pd->entity.x = ((128/2) << 4) + 8; + pd->entity.y = ((128/2) << 4) + 8; } -bool isSwimming(){ - return getTile(player.x>>4,player.y>>4)==TILE_WATER; +void leaveDungeon(PlayerData *pd) { + pd->entity.level = 4; + pd->entity.x = ((128/2) << 4) + 8; + pd->entity.y = ((128/2) << 4) + 8; + + //clear dungeon if empty + if(!dungeonActive()) { + //reset Entities + (&eManager)->lastSlot[5] = 0; + (&eManager)->entities[5][0] = nullEntity; + } } -void playerSetActiveItem(Item * item) { - player.p.activeItem = item; - if(player.p.activeItem->id > 27 && player.p.activeItem->id < 34) player.p.isCarrying = true; - else player.p.isCarrying = false; +void setMinimapVisible(PlayerData *pd, int level, int x, int y, bool visible) { + if(visible) { + pd->minimapData[x + y * 128] = pd->minimapData[x + y * 128] | (1 << level); + } else { + pd->minimapData[x + y * 128] = pd->minimapData[x + y * 128] & (0xFF - (1 << level)); + } + + if(pd==getLocalPlayer()) sf2d_set_pixel(minimap[level], x, y, getMinimapColor(pd, level, x, y)); } +bool getMinimapVisible(PlayerData *pd, int level, int x, int y) { + return (pd->minimapData[x + y * 128] & (1 << level)) > 0; +} + +u32 getMinimapColor(PlayerData *pd, int level, int x, int y) { + if(getMinimapVisible(pd, level, x, y) || (pd->entity.level==0 && level==1)) return getTileColor(worldData.map[level][x + y * 128]); + else return getTileColor(worldData.map[level][x + y * 128]) & 0xFFFFFF00; +} + +void initMinimapLevel(PlayerData *pd, int level) { + int x; + int y; + + if(pd!=getLocalPlayer()) return; + + for (x = 0; x < 128; ++x) { + for (y = 0; y < 128; ++y) { + /* Minimaps */ + sf2d_set_pixel(minimap[level], x, y, getMinimapColor(pd, level, x, y)); + } + } +} + +void updateLevel1Map() { + initMinimapLevel(getLocalPlayer(), 1); +} + + void reloadColors() { - dirtColor[0] = SWAP_UINT32(sf2d_get_pixel(icons, 16, 0)); - dirtColor[1] = SWAP_UINT32(sf2d_get_pixel(icons, 16, 1)); - dirtColor[2] = SWAP_UINT32(sf2d_get_pixel(icons, 16, 2)); - dirtColor[3] = SWAP_UINT32(sf2d_get_pixel(icons, 16, 3)); - dirtColor[4] = SWAP_UINT32(sf2d_get_pixel(icons, 16, 4)); - - grassColor = SWAP_UINT32(sf2d_get_pixel(icons, 17, 0)); + dirtColor[0] = SWAP_UINT32(sf2d_get_pixel(icons, 16, 0)); + dirtColor[1] = SWAP_UINT32(sf2d_get_pixel(icons, 16, 1)); + dirtColor[2] = SWAP_UINT32(sf2d_get_pixel(icons, 16, 2)); + dirtColor[3] = SWAP_UINT32(sf2d_get_pixel(icons, 16, 3)); + dirtColor[4] = SWAP_UINT32(sf2d_get_pixel(icons, 16, 4)); - sandColor = SWAP_UINT32(sf2d_get_pixel(icons, 18, 0)); - - waterColor[0] = SWAP_UINT32(sf2d_get_pixel(icons, 19, 0)); - waterColor[1] = SWAP_UINT32(sf2d_get_pixel(icons, 19, 1)); - - lavaColor[0] = SWAP_UINT32(sf2d_get_pixel(icons, 20, 0)); - lavaColor[1] = SWAP_UINT32(sf2d_get_pixel(icons, 20, 1)); - - rockColor[0] = SWAP_UINT32(sf2d_get_pixel(icons, 21, 0)); - rockColor[1] = SWAP_UINT32(sf2d_get_pixel(icons, 21, 1)); - rockColor[2] = SWAP_UINT32(sf2d_get_pixel(icons, 21, 2)); - rockColor[3] = SWAP_UINT32(sf2d_get_pixel(icons, 21, 3)); - - woodColor = SWAP_UINT32(sf2d_get_pixel(icons, 22, 0)); -} \ No newline at end of file + grassColor = SWAP_UINT32(sf2d_get_pixel(icons, 17, 0)); + myceliumColor = SWAP_UINT32(sf2d_get_pixel(icons, 17, 1)); + mushroomColor = SWAP_UINT32(sf2d_get_pixel(icons, 17, 2)); + + sandColor = SWAP_UINT32(sf2d_get_pixel(icons, 18, 0)); + + waterColor[0] = SWAP_UINT32(sf2d_get_pixel(icons, 19, 0)); + waterColor[1] = SWAP_UINT32(sf2d_get_pixel(icons, 19, 1)); + + lavaColor[0] = SWAP_UINT32(sf2d_get_pixel(icons, 20, 0)); + lavaColor[1] = SWAP_UINT32(sf2d_get_pixel(icons, 20, 1)); + + rockColor[0] = SWAP_UINT32(sf2d_get_pixel(icons, 21, 0)); + rockColor[1] = SWAP_UINT32(sf2d_get_pixel(icons, 21, 1)); + rockColor[2] = SWAP_UINT32(sf2d_get_pixel(icons, 21, 2)); + rockColor[3] = SWAP_UINT32(sf2d_get_pixel(icons, 21, 3)); + + woodColor = SWAP_UINT32(sf2d_get_pixel(icons, 22, 0)); + + ironColor = SWAP_UINT32(sf2d_get_pixel(icons, 23, 0)); + goldColor = SWAP_UINT32(sf2d_get_pixel(icons, 23, 1)); + gemColor = SWAP_UINT32(sf2d_get_pixel(icons, 23, 2)); + + dungeonColor[0] = SWAP_UINT32(sf2d_get_pixel(icons, 24, 0)); + dungeonColor[1] = SWAP_UINT32(sf2d_get_pixel(icons, 24, 1)); + + snowColor = SWAP_UINT32(sf2d_get_pixel(icons, 25, 0)); + iceColor = SWAP_UINT32(sf2d_get_pixel(icons, 25, 1)); +} diff --git a/source/Globals.h b/source/Globals.h old mode 100644 new mode 100755 index ba4a0f8..e8a6170 --- a/source/Globals.h +++ b/source/Globals.h @@ -1,9 +1,14 @@ #pragma once -#include <3ds.h> -#include "SaveLoad.h" -#include "Input.h" -#include "icons2_png.h" +#include <3ds.h> +#include "Entity.h" +#include "Player.h" +#include "Input.h" +#include "MapGen.h" +#include "Quests.h" + +#include "icons_png.h" +#include "player_png.h" #include "Font_png.h" #include "bottombg_png.h" @@ -15,15 +20,29 @@ #define MENU_TUTORIAL 2 #define MENU_ABOUT 3 #define MENU_SETTINGS 4 -#define MENU_INVENTORY 5 -#define MENU_CRAFTING 6 -#define MENU_CONTAINER 7 -#define MENU_WIN 8 -#define MENU_LOSE 9 -#define MENU_PAUSED 10 -#define MENU_LOADGAME 11 -#define MENU_SETTINGS_REBIND 12 -#define MENU_SETTINGS_TP 13 +#define MENU_LOADGAME 5 +#define MENU_SETTINGS_REBIND 6 +#define MENU_SETTINGS_TP 7 +#define MENU_MULTIPLAYER_HOST 8 +#define MENU_MULTIPLAYER_JOIN 9 +#define MENU_MULTIPLAYER_WAIT 10 +#define MENU_LOADING 11 + +#define MENU_PAUSED 100 +#define MENU_INVENTORY 101 +#define MENU_CRAFTING 102 +#define MENU_CONTAINER 103 +#define MENU_WIN 104 +#define MENU_LOSE 105 +#define MENU_DUNGEON 106 +#define MENU_NPC 107 +#define MENU_CHARACTER_CUSTOMIZE 108 + +#define NPC_GIRL 0 +#define NPC_PRIEST 1 +#define NPC_FARMER 2 +#define NPC_LIBRARIAN 3 +#define NPC_DWARF 4 #define TILE_NULL 255 #define TILE_GRASS 0 @@ -48,32 +67,51 @@ #define TILE_HARDROCK 19 #define TILE_CLOUDCACTUS 20 #define TILE_HOLE 21 + #define TILE_WOOD_WALL 22 +#define TILE_STONE_WALL 23 +#define TILE_IRON_WALL 24 +#define TILE_GOLD_WALL 25 +#define TILE_GEM_WALL 26 +#define TILE_DUNGEON_WALL 27 +#define TILE_DUNGEON_FLOOR 28 +#define TILE_DUNGEON_ENTRANCE 29 +#define TILE_MAGIC_BARRIER 30 +#define TILE_BOOKSHELVES 31 +#define TILE_WOOD_FLOOR 32 +#define TILE_MYCELIUM 33 +#define TILE_MUSHROOM_BROWN 34 +#define TILE_MUSHROOM_RED 35 +#define TILE_ICE 36 #define SWAP_UINT32(x) (((x) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | ((x) << 24)) -bool screenShot; +//WARNING: Having this set to different values in different clients will break multiplayer! +#define TESTGODMODE false + +bool paused; + +u32 localUID; + int loadedtp; u8 MODEL_3DS; extern char versionText[34]; -Entity player; - bool shouldRenderDebug; bool shouldSpeedup; bool shouldRenderMap; -u8 zoomLevel; -char mapText[32]; -s16 mScrollX, mScrollY; +bool UnderStrengthEffect; +bool UnderSpeedEffect; +bool regening; +bool UnderSwimBreathEffect; sf2d_texture *icons; +sf2d_texture *playerSprites; sf2d_texture *font; sf2d_texture *bottombg; -sf2d_texture * minimap[5]; -u8 map[5][128*128]; -u8 data[5][128*128]; +sf2d_texture *minimap[6]; u32 dirtColor[5]; u32 grassColor; @@ -82,53 +120,85 @@ u32 waterColor[2]; u32 lavaColor[2]; u32 rockColor[4]; u32 woodColor; +u32 ironColor; +u32 goldColor; +u32 gemColor; +u32 dungeonColor[2]; +u32 myceliumColor; +u32 mushroomColor; +u32 snowColor; +u32 iceColor; char currentFileName[256]; extern u8 currentMenu; extern char fpsstr[]; u8 initGame; +u8 initMPGame; +u8 initBGMap; Item noItem; int airWizardHealthDisplay; s16 awX, awY; -u32 tickCount; -RecipeManager* currentRecipes; -Entity* curChestEntity; -s16 curInvSel; bool quitGame; s8 currentSelection; -void tickTile(int x, int y); +typedef struct _worldData { + u8 map[6][128*128]; + u8 data[6][128*128]; + + u16 daytime; + int day; + u8 season; + bool rain; + + u8 compassData[6][3]; +} WorldData; + +WorldData worldData; + +//TODO: cleanup the order +int getEntities(Entity** result, s8 level, int x0, int y0, int x1, int y1); + +bool moveMob(Entity* e, int xa, int ya); +void hurtEntity(Entity *e, int damage, int dir, u32 hurtColor, Entity *damager); + +void tickTile(s8 level, int x, int y); bool tileIsSolid(int tile, Entity * e); -s8 itemTileInteract(int tile, Item* item, int x, int y, int px, int py, int dir); +s8 itemTileInteract(int tile, PlayerData *pd, Item *item, s8 level, int x, int y, int px, int py, int dir); void tickEntity(Entity* e); -void tickTouchMap(); -void tickTouchQuickSelect(); - void trySpawn(int count, int level); -int getTile(int x, int y); +int getTile(s8 level, int x, int y); +void setTile(int id, s8 level, int x, int y); +int getData(s8 level, int x, int y); +void setData(int id, s8 level, int x, int y); u32 getTileColor(int tile); -void setTile(int id, int x, int y); -int getData(int x, int y); -void setData(int id, int x, int y); bool intersectsEntity(int x, int y, int r, Entity* e); bool EntityBlocksEntity(Entity* e1, Entity* e2); void EntityVsEntity(Entity* e1, Entity* e2); -void entityTileInteract(Entity* e, int tile,int x, int y); +bool ItemVsEntity(PlayerData *pd, Item *item, Entity *e, int dir); +void entityTileInteract(Entity* e, int tile, s8 level, int x, int y); -void initPlayer(); -void tickPlayer(); -void playerAttack(); -bool isSwimming(); -bool playerUseEnergy(int amount); -void playerHurtTile(int tile, int xt, int yt, int damage, int dir); -bool playerIntersectsEntity(Entity* e); -void playerEntityInteract(Entity* e); -void playerSetActiveItem(Item * item); +void openCraftingMenu(PlayerData *pd, RecipeManager *rm, char *title); +bool useEntity(PlayerData *pd, Entity* e); -void reloadColors(); \ No newline at end of file +bool isWater(s8 level, int xt, int yt); + +void playerHurtTile(PlayerData *pd, int tile, s8 level, int xt, int yt, int damage, int dir); +void playerEntityInteract(PlayerData *pd, Entity* e); + +bool dungeonActive(); +void enterDungeon(PlayerData *pd); +void leaveDungeon(PlayerData *pd); + +void setMinimapVisible(PlayerData *pd, int level, int x, int y, bool visible); +bool getMinimapVisible(PlayerData *pd, int level, int x, int y); +u32 getMinimapColor(PlayerData *pd, int level, int x, int y); +void initMinimapLevel(PlayerData *pd, int level); +void updateLevel1Map(); + +void reloadColors(); diff --git a/source/Ingame.c b/source/Ingame.c new file mode 100644 index 0000000..74080b0 --- /dev/null +++ b/source/Ingame.c @@ -0,0 +1,379 @@ +#include "Ingame.h" + +#include "Globals.h" +#include "Player.h" +#include "Entity.h" +#include "IngameMenu.h" +#include "Render.h" +#include "MapGen.h" +#include "Synchronizer.h" +#include "SaveLoad.h" +#include "Network.h" + +#define STALL_TIME 120 + +int stallCounter; +bool stallAreYouSure; + +//generates stairs up and creates compass data +void generatePass2() { + int level, x, y; + + for (level = 0; level < 5; ++level) { + for (x = 0; x < 128; ++x) { + for (y = 0; y < 128; ++y) { + + //generate stairs up matching stairs down + switch (worldData.map[level][x + y * 128]) { + case TILE_STAIRS_DOWN: + if(level < 4) { + worldData.map[level + 1][x + y * 128] = TILE_STAIRS_UP; + if (level == 0) { + worldData.map[level + 1][(x + 1) + y * 128] = TILE_HARDROCK; + worldData.map[level + 1][x + (y + 1) * 128] = TILE_HARDROCK; + worldData.map[level + 1][(x - 1) + y * 128] = TILE_HARDROCK; + worldData.map[level + 1][x + (y - 1) * 128] = TILE_HARDROCK; + worldData.map[level + 1][(x + 1) + (y + 1) * 128] = TILE_HARDROCK; + worldData.map[level + 1][(x - 1) + (y - 1) * 128] = TILE_HARDROCK; + worldData.map[level + 1][(x - 1) + (y + 1) * 128] = TILE_HARDROCK; + worldData.map[level + 1][(x + 1) + (y - 1) * 128] = TILE_HARDROCK; + } else { + worldData.map[level + 1][(x + 1) + y * 128] = TILE_DIRT; + worldData.map[level + 1][x + (y + 1) * 128] = TILE_DIRT; + worldData.map[level + 1][(x - 1) + y * 128] = TILE_DIRT; + worldData.map[level + 1][x + (y - 1) * 128] = TILE_DIRT; + worldData.map[level + 1][(x + 1) + (y + 1) * 128] = TILE_DIRT; + worldData.map[level + 1][(x - 1) + (y - 1) * 128] = TILE_DIRT; + worldData.map[level + 1][(x - 1) + (y + 1) * 128] = TILE_DIRT; + worldData.map[level + 1][(x + 1) + (y - 1) * 128] = TILE_DIRT; + } + } + } + + //calculate compass data + //choose one stair down and store for magic compass + switch (worldData.map[level][x + y * 128]) { + case TILE_STAIRS_DOWN: + case TILE_DUNGEON_ENTRANCE: + worldData.compassData[level][2] = worldData.compassData[level][2] + 1; + if((worldData.compassData[level][2]==1) || (rand()%(worldData.compassData[level][2])==0)) { + worldData.compassData[level][0] = x; + worldData.compassData[level][1] = y; + } + } + } + } + } +} + +void initNewMap() { + createAndValidateSkyMap(128, 128, 0, worldData.map[0], worldData.data[0]); + createAndValidateTopMap(128, 128, 1, worldData.map[1], worldData.data[1]); + createAndValidateUndergroundMap(128, 128, 1, 2, worldData.map[2], worldData.data[2]); + createAndValidateUndergroundMap(128, 128, 2, 3, worldData.map[3], worldData.data[3]); + createAndValidateUndergroundMap(128, 128, 3, 4, worldData.map[4], worldData.data[4]); + generatePass2(); +} + +void initMiniMap(PlayerData *pd) { + int i; + for (i = 0; i < 5; ++i) { + initMinimapLevel(pd, i); + } +} + +void startGame(bool load, char *filename) { + // Reset entity manager. + memset(&eManager, 0, sizeof(eManager)); + + // Reset players + for(int i=0; i 1) { + shouldRenderDebug = false; + } + + if (!load) { + initNewMap(); + airWizardHealthDisplay = 2000; + int i; + for (i = 0; i < 5; ++i) { + trySpawn(500, i); + } + addEntityToList(newAirWizardEntity(630, 820, 0), &eManager); + worldData.daytime = 6000; + worldData.day = 0; + worldData.season = 0; + worldData.rain = false; + } else { + if(!loadWorld(filename, &eManager, &worldData, players, playerCount)) { + //TODO: What do? + networkDisconnect(); + + sf2d_set_clear_color(0xFF); + currentSelection = 0; + currentMenu = MENU_TITLE; + } + } + + // Spawn players + for(int i=0; ientity.level, getLocalPlayer()->entity.x, getLocalPlayer()->entity.y); + + //win/death menus + for(i=0; i=24000) { + worldData.daytime -= 24000; + ++worldData.day; + //TODO: maybe make season length not as hardcoded + make the transition better (fade to black and back maybe?) + if(worldData.day%7==0) { + ++worldData.season; + if(worldData.season==4) worldData.season = 0; + } + worldData.rain = false; + if(worldData.season!=3 && rand()%5==0) worldData.rain = true; + } + + //update music + updateMusic(getLocalPlayer()->entity.level, worldData.daytime); + + //for every active level + s8 level; + for(level = 0; level < 6; level++) { + bool hasPlayer = false; + for(i=0; ix, e->y); + + //should never happen, but just for safety to prevent hard crashes + if(p==NULL) continue; + + if ((e->type != ENTITY_ZOMBIE && e->type != ENTITY_SKELETON && e->type != ENTITY_KNIGHT && e->type != ENTITY_SLIME && e->type != ENTITY_PASSIVE && e->type != ENTITY_GLOWWORM) + || (e->type == ENTITY_GLOWWORM && (worldData.daytime>6000 || worldData.daytime<18000)) + || (e->x > p->entity.x - 160 && e->y > p->entity.y - 125 && e->x < p->entity.x + 160 && e->y < p->entity.y + 125)) { + tickEntity(e); + } + } + } + + stallCounter = 0; +} + +void tickGame() { + synchronizerTick(&syncedTick); + + + if(synchronizerIsRunning()) { + stallCounter++; + //game stalled -> most likely a player disconnected -> present option to exit game + if(stallCounter>=STALL_TIME) { + if(stallCounter==STALL_TIME) stallAreYouSure = false; + + //scan local inputs, because synchronizer only updates them when not stalled + hidScanInput(); + tickKeys(&localInputs, hidKeysHeld(), hidKeysDown()); + + if (localInputs.k_accept.clicked) { + if(stallAreYouSure) { + //create backup save + if(playerLocalID==0) { + char backupSaveFileName[256+32]; + backupSaveFileName[0] = '\0'; + + strncat(backupSaveFileName, currentFileName, strlen(currentFileName)-4); + strcat(backupSaveFileName, ".exit.msv"); + + saveWorld(backupSaveFileName, &eManager, &worldData, players, playerCount); + } + + exitGame(); + } else { + stallAreYouSure = true; + } + } + if (localInputs.k_decline.clicked) { + stallAreYouSure = false; + } + } + } +} + +//for rendering -> move to a better place +int xscr = 0, yscr = 0; + +void renderGame() { + //Important: all code called from this function should never affect game state! + sf2d_start_frame(GFX_TOP, GFX_LEFT); + + int xscr = getLocalPlayer()->entity.x - 100; + int yscr = getLocalPlayer()->entity.y - 56; + if (xscr < 16) + xscr = 16; + else if (xscr > 1832) + xscr = 1832; + if (yscr < 16) + yscr = 16; + else if (yscr > 1912) + yscr = 1912; + + + offsetX = xscr; + offsetY = yscr; + sf2d_draw_rectangle(0, 0, 400, 240, 0xFF0C0C0C); //RGBA8(12, 12, 12, 255)); //You might think "real" black would be better, but it actually looks better that way + + renderLightsToStencil(getLocalPlayer(), false, false, true); + + renderBackground(getLocalPlayer()->entity.level, xscr, yscr); + renderEntities(getLocalPlayer()->entity.level, getLocalPlayer()->entity.x, getLocalPlayer()->entity.y, &eManager); + for(int i=0; ientity.level, xscr, yscr); + + resetStencilStuff(); + + renderDayNight(getLocalPlayer()); + + offsetX = 0; + offsetY = 0; + + if(shouldRenderDebug){ + sprintf(fpsstr, " FPS: %.0f, X:%d, Y:%d, E:%d", sf2d_get_fps(), getLocalPlayer()->entity.x, getLocalPlayer()->entity.y, eManager.lastSlot[getLocalPlayer()->entity.level]); + drawText(fpsstr, 2, 225); + } + + if(getLocalPlayer()->ingameMenu != MENU_NONE) { + ingameMenuRender(getLocalPlayer(), getLocalPlayer()->ingameMenu); + } + + //game stalled -> most likely a player disconnected -> present option to exit game + if(stallCounter>STALL_TIME) { + renderFrame(1,1,24,14,0xFF1010AF); + drawText("Waiting for a long time", (400 - (23 * 12))/2, 32); + + char text[50]; + sprintf(text, "Last response %is ago", stallCounter/60); + drawText(text, (400 - (strlen(text) * 12))/2, 64); + + if(playerLocalID==0) { + drawText("Press to leave the game", (400 - (25 * 12))/2, 160); + renderButtonIcon(localInputs.k_accept.input & -localInputs.k_accept.input, 120, 157, 1); + + drawText("A backup save will be created", (400 - (29 * 12))/2, 192); + } else { + drawText("Press to leave the game", (400 - (25 * 12))/2, 192); + renderButtonIcon(localInputs.k_accept.input & -localInputs.k_accept.input, 120, 189, 1); + } + + if(stallAreYouSure){ + renderFrame(6,5,19,10,0xFF10108F); + + drawText("Are you sure?",122,96); + drawText(" Yes", 164, 117); + renderButtonIcon(localInputs.k_accept.input & -localInputs.k_accept.input, 166, 114, 1); + drawText(" No", 170, 133); + renderButtonIcon(localInputs.k_decline.input & -localInputs.k_decline.input, 166, 130, 1); + } + } + + sf2d_end_frame(); + + sf2d_start_frame(GFX_BOTTOM, GFX_LEFT); + if(!players[playerLocalID].mapShouldRender){ + sf2d_draw_texture(bottombg, 0, 0); + renderGui(getLocalPlayer()); + } else { + renderZoomedMap(getLocalPlayer()); + } + sf2d_end_frame(); +} + +void exitGame() { + networkDisconnect(); + synchronizerReset(); + + sf2d_set_clear_color(0xFF); + currentSelection = 0; + currentMenu = MENU_TITLE; + + playMusic(&music_menu); +} diff --git a/source/Ingame.h b/source/Ingame.h new file mode 100644 index 0000000..f930d2b --- /dev/null +++ b/source/Ingame.h @@ -0,0 +1,9 @@ +#pragma once + +#include <3ds.h> + +void startGame(bool load, char *filename); +void tickGame(); +void renderGame(); + +void exitGame(); diff --git a/source/IngameMenu.c b/source/IngameMenu.c new file mode 100644 index 0000000..6247f54 --- /dev/null +++ b/source/IngameMenu.c @@ -0,0 +1,528 @@ +#include "IngameMenu.h" + +#include "Globals.h" +#include "Menu.h" +#include "Ingame.h" +#include "Player.h" +#include "SaveLoad.h" +#include "Synchronizer.h" + +char pOptions[][24] = {"Return to game", "Save Progress", "Exit to title"}; + +void ingameMenuTick(PlayerData *pd, int menu) { + switch(menu) { + case MENU_PAUSED: + if(!pd->ingameMenuAreYouSure && !pd->ingameMenuAreYouSureSave){ + if (pd->ingameMenuTimer > 0) --pd->ingameMenuTimer; + if (pd->inputs.k_pause.clicked || pd->inputs.k_decline.clicked) pd->ingameMenu = MENU_NONE; + if (pd->inputs.k_up.clicked) { --pd->ingameMenuSelection; if(pd->ingameMenuSelection < 0) pd->ingameMenuSelection=2; } + if (pd->inputs.k_down.clicked) { ++pd->ingameMenuSelection; if(pd->ingameMenuSelection > 2) pd->ingameMenuSelection=0; } + if (pd->inputs.k_accept.clicked){ + switch(pd->ingameMenuSelection){ + case 0: + pd->ingameMenu = MENU_NONE; + break; + case 1: + if(!dungeonActive()) pd->ingameMenuAreYouSureSave = true; + break; + case 2: + pd->ingameMenuAreYouSure = true; + break; + } + } + } else if(pd->ingameMenuAreYouSureSave) { + if (pd->inputs.k_accept.clicked){ + pd->ingameMenuTimer = 60; + + if(playerLocalID==0) { + saveWorld(currentFileName, &eManager, &worldData, players, playerCount); + } + pd->ingameMenuAreYouSureSave = false; + pd->ingameMenuAreYouSure = false; + } else if (pd->inputs.k_decline.clicked){ + pd->ingameMenuAreYouSureSave = false; + pd->ingameMenuAreYouSure = false; + } + } else if(pd->ingameMenuAreYouSure) { + if (pd->inputs.k_accept.clicked){ + pd->ingameMenuAreYouSure = false; + pd->ingameMenuAreYouSureSave = false; + + exitGame(); + } else if (pd->inputs.k_decline.clicked){ + pd->ingameMenuAreYouSure = false; + pd->ingameMenuAreYouSureSave = false; + } + } + break; + case MENU_INVENTORY: + if (pd->inputs.k_menu.clicked || pd->inputs.k_use.clicked || pd->inputs.k_decline.clicked){ + pd->ingameMenu = MENU_NONE; + pd->activeItem = &noItem; + pd->entity.p.isCarrying = false; + } + if (pd->inputs.k_accept.clicked){ // Select item from inventory + if(pd->inventory.lastSlot!=0){ + Item median = pd->inventory.items[pd->ingameMenuInvSel]; // create copy of item. + removeItemFromInventory(pd->ingameMenuInvSel, &(pd->inventory)); // remove original + pushItemToInventoryFront(median, &(pd->inventory)); // add copy to front + playerSetActiveItem(pd, &(pd->inventory.items[0])); // active item = copy. + } + pd->ingameMenu = MENU_NONE; + } + if (pd->inputs.k_up.clicked) { --pd->ingameMenuInvSel; if(pd->ingameMenuInvSel < 0)pd->ingameMenuInvSel=pd->inventory.lastSlot-1; } + if (pd->inputs.k_down.clicked) { ++pd->ingameMenuInvSel; if(pd->ingameMenuInvSel > pd->inventory.lastSlot-1)pd->ingameMenuInvSel=0; } + break; + + case MENU_CRAFTING: + if (pd->inputs.k_menu.clicked || pd->inputs.k_use.clicked || pd->inputs.k_decline.clicked) pd->ingameMenu = MENU_NONE; + if (pd->inputs.k_accept.clicked){ + if(craftItem(&(pd->currentRecipes), &(pd->currentRecipes.recipes[pd->ingameMenuInvSel]), &(pd->inventory))){ + playSoundPositioned(snd_craft, pd->entity.level, pd->entity.x, pd->entity.y); + //reset active item pointer, because it could posibly point to garbage now + pd->activeItem = &noItem; + } + } + if (pd->inputs.k_up.clicked) { --pd->ingameMenuInvSel; if(pd->ingameMenuInvSel < 0)pd->ingameMenuInvSel=pd->currentRecipes.size-1; } + if (pd->inputs.k_down.clicked) { ++pd->ingameMenuInvSel; if(pd->ingameMenuInvSel > pd->currentRecipes.size-1)pd->ingameMenuInvSel=0; } + break; + + case MENU_WIN: + if (pd->inputs.k_accept.clicked){ + pd->ingameMenu = MENU_NONE; + pd->entity.p.hasWon = false; + } + break; + case MENU_LOSE: + if (pd->inputs.k_accept.clicked){ + pd->ingameMenu = MENU_NONE; + pd->entity.p.isDead = false; + pd->entity.p.health = 10; + pd->entity.level = 1; + playerSpawn(pd); + //TODO: This canceled to main menu, but what should I do in multiplayer? + } + pd->entity.hurtTime = 10; + break; + + case MENU_CONTAINER: + if (pd->inputs.k_menu.clicked || pd->inputs.k_decline.clicked) pd->ingameMenu = MENU_NONE; + + if (pd->inputs.k_left.clicked) { + pd->curChestEntityR = 0; + int tmp = pd->ingameMenuInvSel; + pd->ingameMenuInvSel = pd->ingameMenuInvSelOther; + pd->ingameMenuInvSelOther = tmp; + } + if (pd->inputs.k_right.clicked) { + pd->curChestEntityR = 1; + int tmp = pd->ingameMenuInvSel; + pd->ingameMenuInvSel = pd->ingameMenuInvSelOther; + pd->ingameMenuInvSelOther = tmp; + } + + Inventory* i1 = pd->curChestEntityR == 1 ? &(pd->inventory) : pd->curChestEntity->entityFurniture.inv; + Inventory* i2 = pd->curChestEntityR == 0 ? &(pd->inventory) : pd->curChestEntity->entityFurniture.inv; + int len = i1->lastSlot; + if (pd->ingameMenuInvSel < 0) pd->ingameMenuInvSel = 0; + if (pd->ingameMenuInvSel >= len) pd->ingameMenuInvSel = len - 1; + if (pd->inputs.k_up.clicked) --pd->ingameMenuInvSel; + if (pd->inputs.k_down.clicked) ++pd->ingameMenuInvSel; + if (len == 0) pd->ingameMenuInvSel = 0; + if (pd->ingameMenuInvSel < 0) pd->ingameMenuInvSel += len; + if (pd->ingameMenuInvSel >= len) pd->ingameMenuInvSel -= len; + + if(pd->inputs.k_accept.clicked && len > 0){ + Item* pullItem = &i1->items[pd->ingameMenuInvSel]; + Item pushItem = newItem(pullItem->id, pullItem->countLevel); + pushItem.chestPtr = pullItem->chestPtr; + pushItemToInventoryFront(pushItem, i2); + if(i2 == &(pd->inventory)){ + int newslot = pd->activeItem->slotNum + 1; + pd->activeItem = &(pd->inventory.items[newslot]); + } else if(pullItem == pd->activeItem){ + pd->activeItem = &noItem; + } + removeItemFromCurrentInv(pullItem); + if (pd->ingameMenuInvSel >= i1->lastSlot) pd->ingameMenuInvSel = i1->lastSlot - 1; + } + break; + + case MENU_DUNGEON: + if (pd->inputs.k_menu.clicked || pd->inputs.k_decline.clicked) pd->ingameMenu = MENU_NONE; + + if(pd->inputs.k_accept.clicked) { + if(pd->entity.level!=5) { + Item * item = getItemFromInventory(ITEM_DUNGEON_KEY, &(pd->inventory)); + if(item!=NULL) { + --item->countLevel; + if(item->countLevel==0) { + removeItemFromCurrentInv(item); + } + + enterDungeon(pd); + } else if(shouldRenderDebug) { + enterDungeon(pd); + } + } else { + leaveDungeon(pd); + } + + pd->ingameMenu = MENU_NONE; + } + break; + + case MENU_NPC: + tickNPCMenu(pd); + break; + + case MENU_CHARACTER_CUSTOMIZE: + if (pd->inputs.k_up.clicked) { --pd->ingameMenuSelection; if(pd->ingameMenuSelection < 0) pd->ingameMenuSelection=6; } + if (pd->inputs.k_down.clicked) { ++pd->ingameMenuSelection; if(pd->ingameMenuSelection > 6) pd->ingameMenuSelection=0; } + + u8 wrap = 0; + wrap = wrap - 1; + + pd->entity.p.health = 10; + pd->entity.hurtTime = 10; + + //head + if(pd->ingameMenuSelection==0) { + if (pd->inputs.k_left.clicked) { --pd->sprite.head; if(pd->sprite.head == wrap) pd->sprite.head=PLAYER_SPRITE_HEAD_COUNT-1; } + if (pd->inputs.k_right.clicked) { ++pd->sprite.head; if(pd->sprite.head > PLAYER_SPRITE_HEAD_COUNT-1) pd->sprite.head=0; } + //eyes + } else if(pd->ingameMenuSelection==1) { + if (pd->inputs.k_left.clicked) { --pd->sprite.eyes; if(pd->sprite.eyes == wrap) pd->sprite.eyes=PLAYER_SPRITE_EYES_COUNT-1; } + if (pd->inputs.k_right.clicked) { ++pd->sprite.eyes; if(pd->sprite.eyes > PLAYER_SPRITE_EYES_COUNT-1) pd->sprite.eyes=0; } + //body + } else if(pd->ingameMenuSelection==2) { + if (pd->inputs.k_left.clicked) { --pd->sprite.body; if(pd->sprite.body == wrap) pd->sprite.body=PLAYER_SPRITE_BODY_COUNT-1; } + if (pd->inputs.k_right.clicked) { ++pd->sprite.body; if(pd->sprite.body > PLAYER_SPRITE_BODY_COUNT-1) pd->sprite.body=0; } + //arms + } else if(pd->ingameMenuSelection==3) { + if (pd->inputs.k_left.clicked) { --pd->sprite.arms; if(pd->sprite.arms == wrap) pd->sprite.arms=PLAYER_SPRITE_ARMS_COUNT-1; } + if (pd->inputs.k_right.clicked) { ++pd->sprite.arms; if(pd->sprite.arms > PLAYER_SPRITE_ARMS_COUNT-1) pd->sprite.arms=0; } + //legs + } else if(pd->ingameMenuSelection==4) { + if (pd->inputs.k_left.clicked) { --pd->sprite.legs; if(pd->sprite.legs == wrap) pd->sprite.legs=PLAYER_SPRITE_LEGS_COUNT-1; } + if (pd->inputs.k_right.clicked) { ++pd->sprite.legs; if(pd->sprite.legs > PLAYER_SPRITE_LEGS_COUNT-1) pd->sprite.legs=0; } + //rotation + } else if(pd->ingameMenuSelection==5) { + if (pd->inputs.k_left.clicked) { --pd->entity.p.dir; if(pd->entity.p.dir == wrap) pd->entity.p.dir=3; } + if (pd->inputs.k_right.clicked) { ++pd->entity.p.dir; if(pd->entity.p.dir > 3) pd->entity.p.dir=0; } + //done + } else if(pd->ingameMenuSelection==6) { + if(pd->inputs.k_accept.clicked) { + //TODO: are you sure dialog? + pd->ingameMenu = 0; + pd->ingameMenuSelection = 0; + pd->sprite.choosen = true; + } + } + break; + } +} + + +u8 opacity = 255; +bool rev = true; +char scoreText[15]; + +void ingameMenuRender(PlayerData *pd, int menu) { + int i; + + switch(menu) { + case MENU_PAUSED: + renderFrame(1,1,24,14,0xFF1010AF); + drawText("Paused",164,32); + for(i = 3; i >= 0; --i){ + char* msg = pOptions[i]; + u32 color = 0xFF7F7F7F; + if(i == pd->ingameMenuSelection) color = 0xFFFFFFFF; + if((i == 1 && dungeonActive())) { + color = 0xFF7F7FFF; + if(i == pd->ingameMenuSelection) color = 0xFFAFAFFF; + } + drawTextColor(msg,(400 - (strlen(msg) * 12))/2, (i * 24) + 88, color); + } + + if(pd->ingameMenuTimer > 0) drawTextColor("Game Saved!", (400-(11*12))/2, 64,0xFF20FF20); + + if(pd->ingameMenuAreYouSure || pd->ingameMenuAreYouSureSave){ + if(pd->ingameMenuAreYouSure)renderFrame(6,5,19,10,0xFF10108F); + else renderFrame(6,5,19,10,0xFF108F10); + + drawText("Are you sure?",122,96); + drawText(" Yes", 164, 117); + renderButtonIcon(localInputs.k_accept.input & -localInputs.k_accept.input, 166, 114, 1); + drawText(" No", 170, 133); + renderButtonIcon(localInputs.k_decline.input & -localInputs.k_decline.input, 166, 130, 1); + } + break; + case MENU_WIN: + renderFrame(5,3,21,12,0xFFFF1010); + if(!rev){ opacity+=5; if(opacity == 255) rev = true; } + else { opacity-=5; if(opacity == 100) rev = false; } + sprintf(scoreText,"Score: %d", pd->score); + drawTextColor("You Win!",158,76,0x0000AFAF + (opacity << 24)); + drawText(scoreText, 200-((strlen(scoreText)-1)*6), 100); + drawText("Press to continue", 96, 150); + renderButtonIcon(localInputs.k_attack.input & -localInputs.k_attack.input, 166, 148, 1); + + //printf("0x%08X",localInputs.k_attack.input & -localInputs.k_attack.input); + break; + case MENU_LOSE: + renderFrame(5,3,21,12,0xFFFF1010); + if(!rev){ opacity+=5; if(opacity == 255) rev = true; } + else { opacity-=5; if(opacity == 100) rev = false; } + sprintf(scoreText,"Score: %d", pd->score); + drawTextColor("You DIED!",158,76,0x000000AF + (opacity << 24)); + drawText(scoreText, 200-((strlen(scoreText)-1)*6), 100); + drawText("Press to continue", 96, 150); + renderButtonIcon(localInputs.k_attack.input & -localInputs.k_attack.input, 166, 148, 1); + //printf("0x%08X",localInputs.k_attack.input & -localInputs.k_attack.input); + break; + case MENU_INVENTORY: + renderFrame(1,1,24,14,0xFFFF1010); + drawTextColor("Inventory",24+1,14+1,0xFF000000); + drawTextColor("Inventory",24,14,0xFF6FE2E2); + renderItemList(&(pd->inventory), 1,1,24,14, pd->ingameMenuInvSel); + break; + case MENU_CRAFTING: + renderFrame(15,1,24,4,0xFFFF1010); + drawTextColor("Have",248+1,14+1,0xFF000000); + drawTextColor("Have",248,14,0xFF6FE2E2); + renderFrame(15,5,24,14,0xFFFF1010); + drawTextColor("Cost",248+1,78+1,0xFF000000); + drawTextColor("Cost",248,78,0xFF6FE2E2); + renderFrame(1,1,14,14,0xFFFF1010); + drawTextColor(pd->currentCraftTitle,24+1,14+1,0xFF000000); + drawTextColor(pd->currentCraftTitle,24,14,0xFF6FE2E2); + renderRecipes(&(pd->currentRecipes), 1, 1, 14, 14, pd->ingameMenuInvSel); + + Recipe* rec = &(pd->currentRecipes.recipes[pd->ingameMenuInvSel]); + renderItemIcon(rec->itemResult,rec->itemAmountLevel,128,16); + char craftText[12]; + sprintf(craftText, "%d", countItemInv(rec->itemResult, rec->itemAmountLevel, &(pd->inventory))); + drawText(craftText,274,34); + + if(rec->numOfCosts > 0){ + int i; + for(i = 0; i < rec->numOfCosts; i++){ + int amnt = countItemInv(rec->costs[i].costItem,0, &(pd->inventory)); + int ttlCst = rec->costs[i].costAmount; + int col = 0xFFFFFFFF; if(amntcosts[i].costItem,1,128,48+(i*8)); + sprintf(craftText,"%d/%d",ttlCst,amnt); + drawTextColor(craftText,274,96+(i*18),col); + } + } + break; + + case MENU_CONTAINER: + if (pd->curChestEntityR == 1){ offsetX = 48; offsetY = 0;} + else {offsetX = 0; offsetY = 0;} + + renderFrame(1,1,15,14,0xFFFF1010); + drawTextColor("Chest",24+1,14+1,0xFF000000); + drawTextColor("Chest",24,14,0xFF6FE2E2); + renderItemList(pd->curChestEntity->entityFurniture.inv,1,1,15,14, + pd->curChestEntityR == 0 ? pd->ingameMenuInvSel : -pd->ingameMenuInvSelOther - 1); + renderFrame(16,1,30,14,0xFFFF1010); + drawTextColor("Inventory",264+1,14+1,0xFF000000); + drawTextColor("Inventory",264,14,0xFF6FE2E2); + renderItemList(&(pd->inventory),16,1,30,14, + pd->curChestEntityR == 1 ? pd->ingameMenuInvSel : -pd->ingameMenuInvSelOther - 1); + offsetX = 0;offsetY = 0; + break; + + case MENU_DUNGEON: + renderFrame(1,1,24,14,0xFFFF1010); + if(pd->entity.level!=5) { + drawTextColor("Dungeon Entrance",24+1,14+1,0xFF000000); + drawTextColor("Dungeon Entrance",24,14,0xFF6FE2E2); + + drawText("Warning: ", 32, 32); + drawText("You need a Dungeon Key to ", 32, 56); + drawText("enter and cannot save while ", 32, 72); + drawText("being in the Dungeon! ", 32, 88); + drawText("After leaving you will need ", 32, 112); + drawText("a new Dungeon Key for ", 32, 128); + drawText("entering another Dungeon! ", 32, 144); + + drawText(" Enter", 148, 171); + } else { + drawTextColor("Dungeon Exit",24+1,14+1,0xFF000000); + drawTextColor("Dungeon Exit",24,14,0xFF6FE2E2); + + drawText("Warning: ", 32, 32); + drawText("The Dungeon and everything ", 32, 56); + drawText("in it will disappear when ", 32, 72); + drawText("you leave it! ", 32, 88); + drawText("You will need a new Dungeon ", 32, 112); + drawText("Key for entering another ", 32, 128); + drawText("Dungeon again! ", 32, 144); + + drawText(" Leave", 148, 171); + } + + renderButtonIcon(localInputs.k_accept.input & -localInputs.k_accept.input, 150, 168, 1); + drawText(" Stay", 148, 195); + renderButtonIcon(localInputs.k_decline.input & -localInputs.k_decline.input, 150, 192, 1); + break; + + case MENU_NPC: + renderNPCMenu(&(pd->npcMenuData)); + break; + + case MENU_CHARACTER_CUSTOMIZE: + renderFrame(1,1,24,14,0xFFFF1010); + drawTextColor("Character",24+1,14+1,0xFF000000); + drawTextColor("Character",24,14,0xFF6FE2E2); + + drawText("Head: ", 32, 56); + drawText("Eyes: ", 32, 72); + drawText("Body: ", 32, 88); + drawText("Arms: ", 32, 104); + drawText("Legs: ", 32, 120); + drawText("Rot.: ", 32, 144); + + //for the dynamic part + char display[30]; + + sprintf(display, pd->ingameMenuSelection==0 ? "< %02i/%02i >" : " %02i/%02i ", pd->sprite.head+1, PLAYER_SPRITE_HEAD_COUNT); + drawText(display, 96, 56); + sprintf(display, pd->ingameMenuSelection==1 ? "< %02i/%02i >" : " %02i/%02i ", pd->sprite.eyes+1, PLAYER_SPRITE_EYES_COUNT); + drawText(display, 96, 72); + sprintf(display, pd->ingameMenuSelection==2 ? "< %02i/%02i >" : " %02i/%02i ", pd->sprite.body+1, PLAYER_SPRITE_BODY_COUNT); + drawText(display, 96, 88); + sprintf(display, pd->ingameMenuSelection==3 ? "< %02i/%02i >" : " %02i/%02i ", pd->sprite.arms+1, PLAYER_SPRITE_ARMS_COUNT); + drawText(display, 96, 104); + sprintf(display, pd->ingameMenuSelection==4 ? "< %02i/%02i >" : " %02i/%02i ", pd->sprite.legs+1, PLAYER_SPRITE_LEGS_COUNT); + drawText(display, 96, 120); + sprintf(display, pd->ingameMenuSelection==5 ? "< %02i/%02i >" : " %02i/%02i ", pd->entity.p.dir+1, 4); + drawText(display, 96, 144); + sprintf(display, pd->ingameMenuSelection==6 ? "< Done >" : " Done "); + drawText(display, 96, 172); + + int oox = offsetX; + int ooy = offsetY; + int osx = playerScale; + + renderFrame(13,3,22,12,0xFF909090); + //move player sprite to 0/0 + offsetX = pd->entity.x - 8; + offsetY = pd->entity.y - 8; + //move to where I want it + offsetX -= 108; + offsetY -= 28; + playerScale = 8; + renderPlayer(pd); + + offsetX = oox; + offsetY = ooy; + playerScale = osx; + break; + } +} + + +//touch menu +void tickTouchMap(PlayerData *pd){ + if(pd->mapShouldRender){ + if(pd->inputs.k_touch.px > 0 || pd->inputs.k_touch.py > 0){ + // Plus/Minus zoom button + if(pd->inputs.k_touch.py > 204 && pd->inputs.k_touch.py < 232){ + if(pd->inputs.k_touch.px > 284 && pd->inputs.k_touch.px < 312){ + if(pd->mapZoomLevel > 4) return; + if(!pd->touchIsChangingSize && !pd->touchIsDraggingMap){ + pd->mapZoomLevel += 2; + pd->mapScrollX -= (50 * (pd->mapZoomLevel/2)); + pd->mapScrollY -= (40 * (pd->mapZoomLevel/2)); + pd->touchIsChangingSize = true; + sprintf(pd->mapText, "x%d", pd->mapZoomLevel); + } + if(pd->mapScrollX < 320-(128*pd->mapZoomLevel)) pd->mapScrollX = 320-(128*pd->mapZoomLevel); + else if(pd->mapScrollX > 0) pd->mapScrollX = 0; + if(pd->mapScrollY < 240-(128*pd->mapZoomLevel)) pd->mapScrollY = 240-(128*pd->mapZoomLevel); + else if(pd->mapScrollY > 0) pd->mapScrollY = 0; + return; + } else if(pd->inputs.k_touch.px > 256 && pd->inputs.k_touch.px < 284){ + if(pd->mapZoomLevel < 4) return; + if(!pd->touchIsChangingSize && !pd->touchIsDraggingMap){ + pd->mapScrollX += (50 * (pd->mapZoomLevel/2)); + pd->mapScrollY += (40 * (pd->mapZoomLevel/2)); + pd->mapZoomLevel -= 2; + pd->touchIsChangingSize = true; + sprintf(pd->mapText, "x%d", pd->mapZoomLevel); + } + if(pd->mapScrollX < 320-(128*pd->mapZoomLevel)) pd->mapScrollX = 320-(128*pd->mapZoomLevel); + else if(pd->mapScrollX > 0) pd->mapScrollX = 0; + if(pd->mapScrollY < 240-(128*pd->mapZoomLevel)) pd->mapScrollY = 240-(128*pd->mapZoomLevel); + else if(pd->mapScrollY > 0) pd->mapScrollY = 0; + return; + } + } else if(pd->inputs.k_touch.py > 8 && pd->inputs.k_touch.py < 40 && pd->inputs.k_touch.px > 284 && pd->inputs.k_touch.px < 312){ + // Exit Button + if(!pd->touchIsChangingSize && !pd->touchIsDraggingMap){ + pd->mapShouldRender = false; + return; + } + } + + if(!pd->touchIsDraggingMap){ + pd->touchLastX = pd->inputs.k_touch.px; + pd->touchLastY = pd->inputs.k_touch.py; + } + if(pd->mapZoomLevel > 2){ + int dx = pd->touchLastX - pd->inputs.k_touch.px; + if(dx > 1 || dx < -1){ + pd->mapScrollX -= dx; + if(pd->mapScrollX < 320-(128*pd->mapZoomLevel)) pd->mapScrollX = 320-(128*pd->mapZoomLevel); + else if(pd->mapScrollX > 0) pd->mapScrollX = 0; + } + pd->touchLastX = pd->inputs.k_touch.px; + } + + int dy = pd->touchLastY - pd->inputs.k_touch.py; + if(dy > 1 || dy < -1){ + pd->mapScrollY -= dy; + if(pd->mapScrollY < 240-(128*pd->mapZoomLevel)) pd->mapScrollY = 240-(128*pd->mapZoomLevel); + else if(pd->mapScrollY > 0) pd->mapScrollY = 0; + } + pd->touchLastY = pd->inputs.k_touch.py; + pd->touchIsDraggingMap = true; + } else { + pd->touchIsDraggingMap = false; + pd->touchIsChangingSize = false; + } + } else { + // touch minimap to bring up zoomed map. + if(pd->inputs.k_touch.py > 100 && pd->inputs.k_touch.py < 228 && pd->inputs.k_touch.px > 10 && pd->inputs.k_touch.px < 142){ + pd->mapShouldRender = true; + } + } +} + +void tickTouchQuickSelect(PlayerData *pd) { + if (pd->ingameMenu == MENU_NONE && !pd->mapShouldRender) { + int i = 0; + Inventory * inv = &(pd->inventory); + + for (i = 0; i < 8; ++i) { + if((inv->lastSlot) > i) { + int xip = i % 4; + int yip = i / 4; + + if(pd->inputs.k_touch.py > 72*2+yip*21*2 && pd->inputs.k_touch.py < 72*2+yip*21*2+21*2 && pd->inputs.k_touch.px > 76*2+xip*21*2 && pd->inputs.k_touch.px < 76*2+xip*21*2+21*2) { + playerSetActiveItem(pd, &inv->items[i]); + } + } + } + } +} + +void ingameMenuTickTouch(PlayerData *pd) { + tickTouchMap(pd); + tickTouchQuickSelect(pd); +} diff --git a/source/IngameMenu.h b/source/IngameMenu.h new file mode 100644 index 0000000..d3e168f --- /dev/null +++ b/source/IngameMenu.h @@ -0,0 +1,8 @@ +#pragma once + +#include "Player.h" + +void ingameMenuTick(PlayerData *pd, int menu); +void ingameMenuRender(PlayerData *pd, int menu); + +void ingameMenuTickTouch(PlayerData *pd); diff --git a/source/Input.c b/source/Input.c old mode 100644 new mode 100755 index 662cb69..80268b4 --- a/source/Input.c +++ b/source/Input.c @@ -1,23 +1,61 @@ #include "Input.h" void toggleKey(Key* key, bool held, bool down){ - key->down = held; + key->down = held; key->clicked = down; } - -void tickKeys(u32 held, u32 down){ - hidTouchRead(&k_touch); // Update touch position - toggleKey(&k_up, held & k_up.input, down & k_up.input); - toggleKey(&k_down, held & k_down.input, down & k_down.input); - toggleKey(&k_left, held & k_left.input, down & k_left.input); - toggleKey(&k_right, held & k_right.input, down & k_right.input); - toggleKey(&k_pause, held & k_pause.input, down & k_pause.input); - toggleKey(&k_attack, held & k_attack.input, down & k_attack.input); - toggleKey(&k_menu, held & k_menu.input, down & k_menu.input); - toggleKey(&k_accept, held & k_accept.input, down & k_accept.input); - toggleKey(&k_decline, held & k_decline.input, down & k_decline.input); - toggleKey(&k_delete, held & k_delete.input, down & k_delete.input); - toggleKey(&k_menuNext, held & k_menuNext.input, down & k_menuNext.input); - toggleKey(&k_menuPrev, held & k_menuPrev.input, down & k_menuPrev.input); + +void tickKeys(Inputs *inputs, u32 held, u32 down){ + hidTouchRead(&(inputs->k_touch)); // Update touch position + toggleKey(&(inputs->k_up), held & localInputs.k_up.input, down & localInputs.k_up.input); + toggleKey(&(inputs->k_down), held & localInputs.k_down.input, down & localInputs.k_down.input); + toggleKey(&(inputs->k_left), held & localInputs.k_left.input, down & localInputs.k_left.input); + toggleKey(&(inputs->k_right), held & localInputs.k_right.input, down & localInputs.k_right.input); + toggleKey(&(inputs->k_pause), held & localInputs.k_pause.input, down & localInputs.k_pause.input); + toggleKey(&(inputs->k_attack), held & localInputs.k_attack.input, down & localInputs.k_attack.input); + toggleKey(&(inputs->k_pickup), held & localInputs.k_pickup.input, down & localInputs.k_pickup.input); + toggleKey(&(inputs->k_use), held & localInputs.k_use.input, down & localInputs.k_use.input); + toggleKey(&(inputs->k_menu), held & localInputs.k_menu.input, down & localInputs.k_menu.input); + toggleKey(&(inputs->k_accept), held & localInputs.k_accept.input, down & localInputs.k_accept.input); + toggleKey(&(inputs->k_decline), held & localInputs.k_decline.input, down & localInputs.k_decline.input); + toggleKey(&(inputs->k_delete), held & localInputs.k_delete.input, down & localInputs.k_delete.input); + toggleKey(&(inputs->k_menuNext), held & localInputs.k_menuNext.input, down & localInputs.k_menuNext.input); + toggleKey(&(inputs->k_menuPrev), held & localInputs.k_menuPrev.input, down & localInputs.k_menuPrev.input); } +void resetKeys(Inputs *inputs) { + (inputs->k_touch).px = -1; + (inputs->k_touch).py = -1; + + toggleKey(&(inputs->k_up), false, false); + toggleKey(&(inputs->k_down), false, false); + toggleKey(&(inputs->k_left), false, false); + toggleKey(&(inputs->k_right), false, false); + toggleKey(&(inputs->k_pause), false, false); + toggleKey(&(inputs->k_attack), false, false); + toggleKey(&(inputs->k_pickup), false, false); + toggleKey(&(inputs->k_use), false, false); + toggleKey(&(inputs->k_menu), false, false); + toggleKey(&(inputs->k_accept), false, false); + toggleKey(&(inputs->k_decline), false, false); + toggleKey(&(inputs->k_delete), false, false); + toggleKey(&(inputs->k_menuNext), false, false); + toggleKey(&(inputs->k_menuPrev), false, false); +} + +void resetClicked(Inputs *inputs) { + inputs->k_up.clicked = false; + inputs->k_down.clicked = false; + inputs->k_left.clicked = false; + inputs->k_right.clicked = false; + inputs->k_pause.clicked = false; + inputs->k_attack.clicked = false; + inputs->k_pickup.clicked = false; + inputs->k_use.clicked = false; + inputs->k_menu.clicked = false; + inputs->k_accept.clicked = false; + inputs->k_decline.clicked = false; + inputs->k_delete.clicked = false; + inputs->k_menuNext.clicked = false; + inputs->k_menuPrev.clicked = false; +} diff --git a/source/Input.h b/source/Input.h old mode 100644 new mode 100755 index 761a315..d88693c --- a/source/Input.h +++ b/source/Input.h @@ -1,24 +1,35 @@ +#pragma once + #include <3ds.h> +//only down and clicked need to be send, input is for config stuff typedef struct { bool down, clicked; int input; } Key; -Key k_null; -Key k_up; -Key k_down; -Key k_left; -Key k_right; -Key k_attack; -Key k_menu; -Key k_pause; -Key k_accept; -Key k_decline; -Key k_delete; -Key k_menuNext; -Key k_menuPrev; -touchPosition k_touch; +typedef struct { + Key k_null; + Key k_up; + Key k_down; + Key k_left; + Key k_right; + Key k_attack; + Key k_pickup; + Key k_use; + Key k_menu; + Key k_pause; + Key k_accept; + Key k_decline; + Key k_delete; + Key k_menuNext; + Key k_menuPrev; + touchPosition k_touch; +} Inputs; -void tickKeys(u32 held, u32 down); +Inputs localInputs; + +void tickKeys(Inputs *inputs, u32 held, u32 down); +void resetKeys(Inputs *inputs); +void resetClicked(Inputs *inputs); bool clicked(Key key); diff --git a/source/Item.c b/source/Item.c old mode 100644 new mode 100755 index ca47720..284e527 --- a/source/Item.c +++ b/source/Item.c @@ -2,249 +2,349 @@ char currentName[16]; -bool isItemEmpty(Item* item){ - if(item->id < 6 || item->id > 100 || item->onlyOne == true) return false; - if(item->countLevel < 1) return true; - return false; +bool isItemEmpty(Item* item) { + if(item->id < 6 || item->id > 100 || item->onlyOne == true) return false; + if(item->countLevel < 1) return true; + return false; } -void pushItemToInventoryFront(Item item, Inventory * inv){ - 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 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 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); +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. +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 || id > 100) item.onlyOne = true; // Tools + Furniture. - else item.onlyOne = false; - } - item.chestPtr = NULL; - return item; +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 && id < 51) || id > 100) 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; +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; +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; + +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_POTION_MAKER: return "Potion Maker"; + 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_GOLD_APPLE: sprintf(currentName,"%d Golden Apple", countLevel); return currentName; + case ITEM_STRENGTH_POTION: sprintf(currentName,"%d Strength Potion", countLevel); return currentName; + case ITEM_SPEED_POTION: sprintf(currentName,"%d Speed Potion", countLevel); return currentName; + case ITEM_REGEN_POTION: sprintf(currentName,"%d Regen Potion", countLevel); return currentName; + case ITEM_SWIM_BREATH_POTION: sprintf(currentName,"%d Swim Potion", 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; + + case ITEM_LOOM: return "Loom"; + case ITEM_ENCHANTER: return "Enchanter"; + case ITEM_WALL_WOOD: sprintf(currentName,"%d Wood Wall", countLevel); return currentName; + case ITEM_WALL_STONE: sprintf(currentName,"%d Stone Wall", countLevel); return currentName; + case ITEM_WALL_IRON: sprintf(currentName,"%d Iron Wall", countLevel); return currentName; + case ITEM_WALL_GOLD: sprintf(currentName,"%d Gold Wall", countLevel); return currentName; + case ITEM_WALL_GEM: sprintf(currentName,"%d Gem Wall", countLevel); return currentName; + case ITEM_WOOL: sprintf(currentName,"%d Wool", countLevel); return currentName; + case ITEM_STRING: sprintf(currentName,"%d String", countLevel); return currentName; + case ITEM_PORK_RAW: sprintf(currentName,"%d Raw Pork", countLevel); return currentName; + case ITEM_PORK_COOKED: sprintf(currentName,"%d Cooked Pork", countLevel); return currentName; + case ITEM_BEEF_RAW: sprintf(currentName,"%d Raw Beef", countLevel); return currentName; + case ITEM_BEEF_COOKED: sprintf(currentName,"%d Steak", countLevel); return currentName; + case ITEM_LEATHER: sprintf(currentName,"%d Leather", countLevel); return currentName; + case ITEM_ARROW_WOOD: sprintf(currentName,"%d Wood Arrow", countLevel); return currentName; + case ITEM_ARROW_STONE: sprintf(currentName,"%d Rock Arrow", countLevel); return currentName; + case ITEM_ARROW_IRON: sprintf(currentName,"%d Iron Arrow", countLevel); return currentName; + case ITEM_ARROW_GOLD: sprintf(currentName,"%d Gold Arrow", countLevel); return currentName; + case ITEM_ARROW_GEM: sprintf(currentName,"%d Gem Arrow", countLevel); return currentName; + case ITEM_BONE: sprintf(currentName,"%d Bone", countLevel); return currentName; + case ITEM_DUNGEON_KEY: sprintf(currentName,"%d Dungeon Key", countLevel); return currentName; + case ITEM_WIZARD_SUMMON: sprintf(currentName,"%d Wizard Summon", countLevel); return currentName; + case ITEM_DRAGON_EGG: sprintf(currentName,"%d Dragon Egg", countLevel); return currentName; + case ITEM_DRAGON_SCALE: sprintf(currentName,"%d Dragon Scale", countLevel); return currentName; + case ITEM_BOOKSHELVES: sprintf(currentName,"%d Bookshelves", countLevel); return currentName; + case ITEM_MAGIC_DUST: sprintf(currentName,"%d Magic Dust", countLevel); return currentName; + case ITEM_COIN: sprintf(currentName,"%d Coins", countLevel); return currentName; case TOOL_BUCKET: switch(countLevel){ - case 1: return "Water Bucket"; - case 2: return "Lava Bucket"; - default: return "Empty Bucket"; - } - default: return ""; // null - } + case 1: return "Water Bucket"; + case 2: return "Lava Bucket"; + default: return "Empty Bucket"; + } + case TOOL_BOW: return "Bow"; + case TOOL_MAGIC_COMPASS: return "Magic Compass"; + case TOOL_CLAYMORE: + switch(countLevel){ + case 1: return "Rock Claymore"; + case 2: return "Iron Claymore"; + case 3: return "Gold Claymore"; + case 4: return "Gem Claymore"; + default: return "Wood Claymore"; + } + case ITEM_TORCH: return "Torch"; + case ITEM_BED: return "Bed"; + case ITEM_FISHING_ROD: return "Fishing Rod"; + case ITEM_SHEARS: return "Shears"; + case ITEM_FISH_RAW: sprintf(currentName,"%d Fish", countLevel); return currentName; + case ITEM_FISH_COOKED: sprintf(currentName,"%d Cooked Fish", 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"; + +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_POTION_MAKER: return "Potion Maker"; + 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_GOLD_APPLE: return "Gold 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"; + + case ITEM_LOOM: return "Loom"; + case ITEM_ENCHANTER: return "Enchanter"; + case ITEM_WALL_WOOD: return "Wood Wall"; + case ITEM_WALL_STONE: return "Stone Wall"; + case ITEM_WALL_IRON: return "Iron Wall"; + case ITEM_WALL_GOLD: return "Gold Wall"; + case ITEM_WALL_GEM: return "Gem Wall"; + case ITEM_WOOL: return "Wool"; + case ITEM_STRING: return "String"; + case ITEM_PORK_RAW: return "Raw Pork"; + case ITEM_PORK_COOKED: return "Cooked Pork"; + case ITEM_BEEF_RAW: return "Raw Beef"; + case ITEM_BEEF_COOKED: return "Steak"; + case ITEM_LEATHER: return "Leather"; + case ITEM_ARROW_WOOD: return "Wood Arrow"; + case ITEM_ARROW_STONE: return "Rock Arrow"; + case ITEM_ARROW_IRON: return "Iron Arrow"; + case ITEM_ARROW_GOLD: return "Gold Arrow"; + case ITEM_ARROW_GEM: return "Gem Arrow"; + case ITEM_BONE: return "Bone"; + case ITEM_DUNGEON_KEY: return "Dungeon Key"; + case ITEM_WIZARD_SUMMON: return "Wizard Summon"; + case ITEM_DRAGON_EGG: return "Dragon Egg"; + case ITEM_DRAGON_SCALE: return "Dragon Scale"; + case ITEM_BOOKSHELVES: return "Bookshelves"; + case ITEM_MAGIC_DUST: return "Magic Dust"; + case ITEM_COIN: return "Coin"; + case ITEM_STRENGTH_POTION: return "Strength Potion"; + case ITEM_SPEED_POTION: return "Speed Potion"; + case ITEM_REGEN_POTION: return "Regen Potion"; + case ITEM_SWIM_BREATH_POTION: return "Water Potion"; case TOOL_BUCKET: switch(countLevel){ - case 1: return "Water Bucket"; - case 2: return "Lava Bucket"; - default: return "Empty Bucket"; - } - default: return ""; // null - } - + case 1: return "Water Bucket"; + case 2: return "Lava Bucket"; + default: return "Empty Bucket"; + } + case TOOL_BOW: return "Bow"; + case TOOL_MAGIC_COMPASS: return "Magic Compass"; + case TOOL_CLAYMORE: + switch(countLevel){ + case 1: return "Rock Claymore"; + case 2: return "Iron Claymore"; + case 3: return "Gold Claymore"; + case 4: return "Gem Claymore"; + default: return "Wood Claymore"; + } + case ITEM_TORCH: return "Torch"; + case ITEM_BED: return "Bed"; + case ITEM_FISHING_ROD: return "Fishing Rod"; + case ITEM_SHEARS: return "Shears"; + case ITEM_FISH_RAW: return "Fish"; + case ITEM_FISH_COOKED: return "Cooked Fish"; + default: return ""; // null + } + } diff --git a/source/Item.h b/source/Item.h old mode 100644 new mode 100755 index 6e806e7..669c049 --- a/source/Item.h +++ b/source/Item.h @@ -42,22 +42,69 @@ #define ITEM_FURNACE 31 #define ITEM_WORKBENCH 32 #define ITEM_LANTERN 33 + +#define ITEM_LOOM 34 +#define ITEM_ENCHANTER 35 +#define ITEM_POTION_MAKER 36 +#define TOOL_CLAYMORE 37 +#define ITEM_TORCH 38 +#define ITEM_BED 39 + +#define ITEM_WALL_WOOD 51 +#define ITEM_WALL_STONE 52 +#define ITEM_WALL_IRON 53 +#define ITEM_WALL_GOLD 54 +#define ITEM_WALL_GEM 55 +#define ITEM_WOOL 56 +#define ITEM_STRING 57 +#define ITEM_PORK_RAW 58 +#define ITEM_PORK_COOKED 59 +#define ITEM_BEEF_RAW 60 +#define ITEM_BEEF_COOKED 61 +#define ITEM_LEATHER 62 +#define ITEM_ARROW_WOOD 63 +#define ITEM_ARROW_STONE 64 +#define ITEM_ARROW_IRON 65 +#define ITEM_ARROW_GOLD 66 +#define ITEM_ARROW_GEM 67 +#define ITEM_BONE 68 +#define ITEM_DUNGEON_KEY 69 +#define ITEM_WIZARD_SUMMON 70 +#define ITEM_DRAGON_EGG 71 +#define ITEM_DRAGON_SCALE 72 +#define ITEM_BOOKSHELVES 73 +#define ITEM_MAGIC_DUST 74 +#define ITEM_COIN 75 +#define ITEM_GOLD_APPLE 76 +#define ITEM_STRENGTH_POTION 77 +#define ITEM_SPEED_POTION 78 +#define ITEM_REGEN_POTION 79 +#define ITEM_SWIM_BREATH_POTION 80 +#define ITEM_FISH_RAW 81 +#define ITEM_FISH_COOKED 82 + #define TOOL_BUCKET 101 +#define TOOL_BOW 102 +#define TOOL_MAGIC_COMPASS 103 +#define ITEM_FISHING_ROD 104 +#define ITEM_SHEARS 105 + +#define ARMOR_TEST 120 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. + 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. + 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); diff --git a/source/MapGen.c b/source/MapGen.c old mode 100644 new mode 100755 index f354420..49e5438 --- a/source/MapGen.c +++ b/source/MapGen.c @@ -3,8 +3,11 @@ int w = 0; int h = 0; +u8 randomTile[] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 2}; +int randomTileSize = 10; + float nextFloat(){ - return (float)rand()/RAND_MAX; + return (float)rand()/RAND_MAX; } double sample(double * values, int x, int y) { @@ -12,122 +15,150 @@ double sample(double * values, int x, int y) { } double * Noise(int width, int height, int featureSize) { - w = width; - h = height; - double * values = malloc(sizeof(double) * w * h); - int x, y; - for(x = 0; x < w; x+=featureSize){ - for(y = 0; y < w; y+=featureSize){ - values[(x & (w - 1)) + (y & (h - 1)) * w] = nextFloat() * 2 - 1; + w = width; + h = height; + double * values = malloc(sizeof(double) * w * h); + int x, y; + for(x = 0; x < w; x+=featureSize){ + for(y = 0; y < w; y+=featureSize){ + values[(x & (w - 1)) + (y & (h - 1)) * w] = nextFloat() * 2 - 1; + } + } + + int stepSize = featureSize; + double scale = 1.0 / w; + double scaleMod = 1; + do { + int halfStep = stepSize / 2; + for(x = 0; x < w; x+=stepSize){ + for(y = 0; y < w; y+=stepSize){ + double a = sample(values,x, y); + double b = sample(values,x + stepSize, y); + double c = sample(values,x, y + stepSize); + double d = sample(values,x + stepSize, y + stepSize); + + 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; } } - 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)); + stepSize /= 2; + scale *= (scaleMod + 0.8); + scaleMod *= 0.3; + } while (stepSize > 1); + return values; } - -void createAndValidateTopMap(int w, int h, u8 * map, u8 * data) { + +//TODO: Will need to reset entity manager if generation is retried +void createAndValidateTopMap(int w, int h, int level, u8 * map, u8 * data) { do { - createTopMap(w, h, map, data); + //reset Entities + (&eManager)->lastSlot[level] = 0; + (&eManager)->entities[level][0] = nullEntity; + + createTopMap(w, h, level, map, data); int count[256]={[0 ... 255] = 0}; - int i; - for (i = 0; i < w * h; ++i) count[map[i] & 0xff]++; + 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) { +void createAndValidateUndergroundMap(int w, int h, int depthLevel, int level, u8 * map, u8 * data) { do { - createUndergroundMap(w, h, depthLevel, map, data); - + //reset Entities + (&eManager)->lastSlot[level] = 0; + (&eManager)->entities[level][0] = nullEntity; + + createUndergroundMap(w, h, depthLevel, level, map, data); + int count[256]={[0 ... 255] = 0}; - int i = 0; - for (i = 0; i < w * h; ++i)count[map[i] & 0xff]++; + 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; - } + case 1: if (count[TILE_IRONORE & 0xff] < 20) continue; break; + case 2: if (count[TILE_GOLDORE & 0xff] < 20 || count[TILE_MYCELIUM & 0xff] < 40) continue; break; + case 3: if (count[TILE_GEMORE & 0xff] < 20) continue; break; + } if (depthLevel < 3) if (count[TILE_STAIRS_DOWN & 0xff] < 2) continue; - + return; } while (true); } -void createAndValidateSkyMap(int w, int h, u8 * map, u8 * data) { + +void createAndValidateDungeonMap(int w, int h, int level, u8 * map, u8 * data) { do { - createSkyMap(w, h, map, data); - + //reset Entities + (&eManager)->lastSlot[level] = 0; + (&eManager)->entities[level][0] = nullEntity; + + createDungeonMap(w, h, level, map, data); + int count[256]={[0 ... 255] = 0}; - int i = 0; - for (i = 0; i < w * h; ++i)count[map[i] & 0xff]++; + int i = 0; + for (i = 0; i < w * h; ++i)count[map[i] & 0xff]++; + if (count[TILE_DUNGEON_WALL & 0xff] < 100) continue; + if (count[TILE_DUNGEON_FLOOR & 0xff] < 100) continue; + + return; + } while (true); +} + +void createAndValidateSkyMap(int w, int h, int level, u8 * map, u8 * data) { + do { + //reset Entities + (&eManager)->lastSlot[level] = 0; + (&eManager)->entities[level][0] = nullEntity; + + createSkyMap(w, h, level, map, data); + + int count[256]={[0 ... 255] = 0}; + int i = 0; + 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) { + + + +void createTopMap(int w, int h, int level, u8 * map, u8 * data) { double* mnoise1 = Noise(w, h, 16); double* mnoise2 = Noise(w, h, 16); double* mnoise3 = Noise(w, h, 16); 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; + 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]); @@ -141,7 +172,7 @@ void createTopMap(int w, int h, u8 * map, u8 * data) { 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) { @@ -149,10 +180,10 @@ void createTopMap(int w, int h, u8 * map, u8 * data) { } else { map[i] = TILE_GRASS; } - } - } - - for (i = 0; i < w * h / 2800; ++i) { + } + } + + for (i = 0; i < w * h / 2800; ++i) { int xs = rand()%w; int ys = rand()%h; for (k = 0; k < 10; ++k) { @@ -161,16 +192,18 @@ void createTopMap(int w, int h, u8 * map, u8 * data) { 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 (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) { + + createVillage(w, h, level, map, data); + + for (i = 0; i < w * h / 400; ++i) { x = rand()%w; y = rand()%h; for (j = 0; j < 200; ++j) { @@ -183,8 +216,8 @@ void createTopMap(int w, int h, u8 * map, u8 * data) { } } } - - for (i = 0; i < w * h / 800; ++i) { + + for (i = 0; i < w * h / 800; ++i) { x = rand()%w; y = rand()%h; for (j = 0; j < 30;++j) { @@ -196,10 +229,10 @@ void createTopMap(int w, int h, u8 * map, u8 * data) { data[xx + yy * w] = rand()%4; // determines mirroring. } } - + } } - + for (i = 0; i < w * h / 100; ++i) { xx = rand()%w; yy = rand()%h; @@ -209,14 +242,14 @@ void createTopMap(int w, int h, u8 * map, u8 * data) { } } } - - int sCount, attempts = 0; + + 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) - { + 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; @@ -226,39 +259,40 @@ void createTopMap(int w, int h, u8 * map, u8 * data) { 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; + ++sCount; } } if(attempts < (w*h/100)) ++attempts; else break; } - free(mnoise1); - free(mnoise2); - free(mnoise3); - free(noise1); - free(noise2); - return; + free(mnoise1); + free(mnoise2); + free(mnoise3); + free(noise1); + free(noise2); + return; } -void createUndergroundMap(int w, int h,int depthLevel, u8 * map, u8 * data) { +void createUndergroundMap(int w, int h, int depthLevel, int level, u8 * map, u8 * data) { double* mnoise1 = Noise(w, h, 16); double* mnoise2 = Noise(w, h, 16); double* mnoise3 = Noise(w, h, 16); - + 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; + + 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; + for(y = 0; y < w; ++y){ + int i = x + y * w; double val = fabs(noise1[i] - noise2[i]) * 3 - 2; @@ -267,7 +301,7 @@ void createUndergroundMap(int w, int h,int depthLevel, u8 * map, u8 * data) { 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; @@ -279,7 +313,7 @@ void createUndergroundMap(int w, int h,int depthLevel, u8 * map, u8 * data) { 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; @@ -290,10 +324,43 @@ void createUndergroundMap(int w, int h,int depthLevel, u8 * map, u8 * data) { } else { map[i] = TILE_ROCK; } - } - } - int i,j; - for (i = 0; i < w * h / 400; ++i) { + } + } + + //generate dwarf house + if(depthLevel==3) { + createDwarfHouse(w, h, level, map, data); + //generate mushroom patches + } else if(depthLevel==2) { + for (i = 0; i < w * h / 5400; ++i) { + int xs = rand()%w; + int ys = rand()%h; + for (k = 0; k < 10; ++k) { + x = xs + (rand()%13) - 6; + y = ys + (rand()%13) - 6; + for (j = 0; j < 100; ++j) { + int xo = x + (rand()%5) - (rand()%5); + int yo = y + (rand()%5) - (rand()%5); + for (yy = yo - 1;yy <= yo + 1; ++yy){ + for(xx = xo - 1; xx <= xo + 1; ++xx){ + if (xx >= 0 && yy >= 0 && xx < w && yy < h) { + if (map[xx + yy * w] == TILE_DIRT) { + map[xx + yy * w] = TILE_MYCELIUM; + if(rand()%20==0) { + map[xx + yy * w] = TILE_MUSHROOM_BROWN + rand()%2; //BROWN or RED (=BROWN+1) + data[xx + yy * w] = rand()%2; + } + } + } + } + } + } + } + } + } + + //generate ores + for (i = 0; i < w * h / 400; ++i) { int x = rand()%w; int y = rand()%h; for(j = 0; j < 30; ++j) { @@ -306,13 +373,15 @@ void createUndergroundMap(int w, int h,int depthLevel, u8 * map, u8 * data) { } } } - if (depthLevel < 3){ - int sCount, attempts = 0; + + //generate stairs down + 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){ + 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; @@ -323,81 +392,486 @@ void createUndergroundMap(int w, int h,int depthLevel, u8 * map, u8 * data) { 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); - + } + + //generate dungeon entrance + if(depthLevel==3) { + map[w/2+0 + (h/2+0) * w] = TILE_DUNGEON_ENTRANCE; + + map[w/2-1 + (h/2+0) * w] = TILE_DIRT; + map[w/2+1 + (h/2+0) * w] = TILE_DIRT; + map[w/2+0 + (h/2-1) * w] = TILE_DIRT; + map[w/2+1 + (h/2+1) * w] = TILE_DIRT; + + map[w/2-1 + (h/2-1) * w] = TILE_DUNGEON_WALL; + map[w/2-1 + (h/2+1) * w] = TILE_DUNGEON_WALL; + map[w/2+1 + (h/2-1) * w] = TILE_DUNGEON_WALL; + map[w/2+1 + (h/2+1) * w] = TILE_DUNGEON_WALL; + } + + free(mnoise1); + free(mnoise2); + free(mnoise3); + 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; +void createDungeonMap(int w, int h, int level, u8 * map, u8 * data) { + hasNPC = false; - 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 + int i, x, y; + for(x = 0; x < w; ++x){ + for(y = 0; y < w; ++y){ + i = x + y * w; + + //Startroom + if (x >= (w/2-5) && x <= (w/2+5) && y >= (h/2-5) && y <= (h/2+5) ) { + map[i] = TILE_DUNGEON_FLOOR; + data[i] = randomTile[rand()%randomTileSize]; + } else { + map[i] = TILE_DUNGEON_WALL; + data[i] = 0; + } + } + } + + //create dragon chamber(only call once and before other rooms) + createDungeonRoom(w, h, true, level, map, data); + + for(i = 0; i < 40; ++i) { + createDungeonRoom(w, h, false, level, map, data); + } + + //replace paths with actual dungeon floor + for(x = 0; x < w; ++x){ + for(y = 0; y < w; ++y){ + i = x + y * w; + + if (map[i]==255) { + map[i] = TILE_DUNGEON_FLOOR; + data[i] = randomTile[rand()%randomTileSize]; + } + } + } + + //create entrance + map[w/2 + h/2 * w] = TILE_DUNGEON_ENTRANCE; + + map[w/2+1 + h/2 * w] = TILE_DUNGEON_FLOOR; + map[w/2-1 + h/2 * w] = TILE_DUNGEON_FLOOR; + map[w/2 + (h/2+1) * w] = TILE_DUNGEON_FLOOR; + map[w/2 + (h/2-1) * w] = TILE_DUNGEON_FLOOR; + + map[w/2+1 + (h/2+1) * w] = TILE_DUNGEON_WALL; + map[w/2+1 + (h/2-1) * w] = TILE_DUNGEON_WALL; + map[w/2-1 + (h/2+1) * w] = TILE_DUNGEON_WALL; + map[w/2-1 + (h/2-1) * w] = TILE_DUNGEON_WALL; +} + +void createSkyMap(int w, int h, int level, u8 * map, u8 * data) { + double* noise1 = Noise(w, h, 8); + double* noise2 = Noise(w, h, 8); + int x, y; + 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); +} + +//"Subgenerators" +void findFeatureLocation(int fw, int fh, int * accepted, int aLength, int maxTries, int w, int h, u8 * map, u8 * data) { + int leastNonFitting = fw * fh; + int tries; + + //find the location with the least non fitting tiles out of some randomly tried ones + for(tries=0; triesx) { + map[px + py * w] = TILE_SAND; + --px; + } + hw = 4 + rand()%2; + hh = 4 + rand()%2; + hx = px + 1; + hy = py - hh + 2 + rand()%(hh-2); + ex = px + hw; + ey = py; + createVillageHouse(0, hx, hy, hw, hh, ex, ey, w, h, level, map, data); + + //top + px = cx-1 + rand()%3; + py = cy-1; + while(py>y) { + map[px + py * w] = TILE_SAND; + --py; + } + hw = 5 + rand()%2; + hh = 4 + rand()%2; + hx = px - hw + 2 + rand()%(hw-2); + hy = py + 1; + ex = px; + ey = py+hh; + createVillageHouse(1, hx, hy, hw, hh, ex, ey, w, h, level, map, data); + + //right + px = cx+1; + py = cy-1 + rand()%3; + while(px w-5) wr = (w-5) - x; + if(y+hr > h-5) hr = (h-5) - y; + + //check instersection + bool allowed = true; + for(xr = x-1; xr < x+wr+1; ++xr) { + for(yr = y-1; yr < y+hr+1; ++yr) { + i = xr + yr * w; + + //255 for paths so rooms can overlap paths + if(map[i]!=TILE_DUNGEON_WALL && map[i]!=255) { + allowed = false; + break; + } + } + if(!allowed) break; + } + if(!allowed) continue; + + //create room + for(xr = x; xr < x+wr; ++xr) { + for(yr = y; yr < y+hr; ++yr) { + i = xr + yr * w; + + map[i] = TILE_DUNGEON_FLOOR; + data[i] = randomTile[rand()%randomTileSize]; + } + } + + //Create path back to existing stuff + xp = x + wr/2; + yp = y + hr/2; + i = xp + yp * w; + bool checkForFloor = false; + bool xFirst = (rand()%2)==0; + while((checkForFloor && (map[i]!=TILE_DUNGEON_FLOOR && map[i]!=255)) || (!checkForFloor && (map[i]==TILE_DUNGEON_FLOOR || map[i]==255))) { + if(checkForFloor) { + //make connection + map[i] = 255; + } + + //move + if(xFirst) { + if(xp > w/2) --xp; + else if(xp < w/2) ++xp; + else if(yp > h/2) --yp; + else if(yp < h/2) ++yp; + else break; + } else { + if(yp > h/2) --yp; + else if(yp < h/2) ++yp; + else if(xp > w/2) --xp; + else if(xp < w/2) ++xp; + else break; + } + + i = xp + yp * w; + + //search for end of current room + if(!checkForFloor && (map[i]!=TILE_DUNGEON_FLOOR && map[i]!=255)) checkForFloor = true; + } + + //dekorate dragon room + if(dragon) { + for(xr = x; xr < x+wr; ++xr) { + for(yr = y; yr < y+hr; ++yr) { + i = xr + yr * w; + + if((xr==x+1 || xr==x+wr-2 || yr==y+1 || yr==y+hr-2) && (xr!=x && xr!=x+wr-1 && yr!=y && yr!=y+hr-1)) { + map[i] = TILE_MAGIC_BARRIER; + } + } + } + + //add Dragon Entity + addEntityToList(newDragonEntity((x+wr/2) << 4, (y+hr/2) << 4, level), &eManager); + break; + } + + //dekorate room + bool lava = (rand()%4)==0; + bool pillars = (rand()%4)==0; + bool books = (rand()%4)==0; + for(xr = x; xr < x+wr; ++xr) { + for(yr = y; yr < y+hr; ++yr) { + i = xr + yr * w; + + if(lava && xr > x+1 && xr < x+wr-2 && yr > y+1 && yr < y+hr-2) { + map[i] = TILE_LAVA; + data[i] = 0; + } else if(pillars && xr > x && xr < x+wr-1 && yr > y && yr < y+hr-1 && xr%2 == 0 && yr%2 == 0) { + map[i] = TILE_DUNGEON_WALL; + data[i] = 0; + } else if(books && (xr>x && xry && yr= 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; + //add magic pillars for dragon barrier + if(xr==x+wr/2 && yr==y+hr/2) { + int pcount = 0; + int i = 0; + for (i = 0; i < eManager.lastSlot[5]; ++i) { + Entity * e = &eManager.entities[5][i]; + + if(e->type == ENTITY_MAGIC_PILLAR) { + ++pcount; + } + } + if(pcount<8) { + addEntityToList(newMagicPillarEntity((xr << 4) + 8, (yr << 4) + 8, level), &eManager); + } + continue; + } + + if(rand()%50==0) map[i] = TILE_IRONORE + (rand()%3); } } - if(attempts < w*h) ++attempts; else break; } - free(noise1); - free(noise2); - } - + + break; + } +} diff --git a/source/MapGen.h b/source/MapGen.h old mode 100644 new mode 100755 index 8ab4ce7..8e11015 --- a/source/MapGen.h +++ b/source/MapGen.h @@ -2,7 +2,6 @@ #include #include #include -#include #include <3ds.h> #include "Globals.h" @@ -10,10 +9,21 @@ float nextFloat(); double sample(double * values, int x, int y); double * Noise(int w, int h, int featureSize); -void newSeed(); -void createAndValidateTopMap(int w, int h, u8 * map, u8 * data); -void createTopMap(int w, int h, u8 * map, u8 * data); -void createAndValidateUndergroundMap(int w, int h,int depthLevel, u8 * map, u8 * data); -void createUndergroundMap(int w, int h,int depthLevel, u8 * map, u8 * data); -void createAndValidateSkyMap(int w, int h, u8 * map, u8 * data); -void createSkyMap(int w, int h, u8 * map, u8 * data); +void createAndValidateTopMap(int w, int h, int level, u8 * map, u8 * data); +void createTopMap(int w, int h, int level, u8 * map, u8 * data); +void createAndValidateUndergroundMap(int w, int h, int depthLevel, int level, u8 * map, u8 * data); +void createUndergroundMap(int w, int h, int depthLevel, int level, u8 * map, u8 * data); +void createAndValidateDungeonMap(int w, int h, int level, u8 * map, u8 * data); +void createDungeonMap(int w, int h, int level, u8 * map, u8 * data); +void createAndValidateSkyMap(int w, int h, int level, u8 * map, u8 * data); +void createSkyMap(int w, int h, int level, u8 * map, u8 * data); + +int featureX; +int featureY; +void findFeatureLocation(int fw, int fh, int * accepted, int aLength, int maxTries, int w, int h, u8 * map, u8 * data); + +void createVillage(int w, int h, int level, u8 * map, u8 * data); +void createDwarfHouse(int w, int h, int level, u8 * map, u8 * data); + +bool hasNPC; +void createDungeonRoom(int w, int h, bool dragon, int level, u8 * map, u8 * data); diff --git a/source/Menu.c b/source/Menu.c old mode 100644 new mode 100755 index 07300ea..9387798 --- a/source/Menu.c +++ b/source/Menu.c @@ -1,20 +1,22 @@ #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"}; +#include "PacketHandler.h" +#include "SaveLoad.h" + +char options[][12] = {"Start Game", "Host Game", "Join Game", "How To Play", "Settings", "About", "Exit"}; char keybOptions[][24] = {"Exit and Save", "Exit and Don't save","Reset to default"}; -char setOptions[][24] = {"Rebind Buttons", "Texture packs", "Debug Text: ", "N3DS Speedup: ", "Return to title"}; +char setOptions[][24] = {"Rebind Buttons", "Texture packs", "Test Features: ", "N3DS Speedup: ", "Return to title"}; // 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 + 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[12] = {[0 ... 11] = 0}; +int keyProp[14] = {[0 ... 13] = 0}; bool areYouSure = false; bool areYouSureSave = false; bool bindOpt = false; @@ -22,6 +24,7 @@ bool left = false; bool selBut = false; s8 errorBut = -1; s8 curSaveSel = 0; +int menuScanTimer = 0; // Load Game Menu (Start Game) char fileNames[1000][256]; @@ -33,6 +36,7 @@ s8 touchDelay = 0; bool isTouching = false; int touchX = 0, touchY = 0, touchW = 0, touchH = 0; s8 errorFileName = 0; +bool toMultiplayer; // Load Texturepacks Menu char tpFileNames[1000][256]; @@ -40,686 +44,708 @@ char tpFileComment[1000][60]; s16 tpFileCount = 0; s8 isLoadingTP = 0; -s16 pauseSaveDisplayTimer = 0; - void readFiles(){ - memset(&fileNames, 0, sizeof(fileNames)); // 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); - } + memset(&fileNames, 0, sizeof(fileNames)); // reset fileNames + worldFileCount = 0; + DIR * d; + struct dirent * dir; + d = opendir("."); + if (d){ + while ((dir = readdir(d)) != NULL) { + if (strncmp(dir->d_name+strlen(dir->d_name)-4, ".msv", 4) == 0) { // Check if filename contains ".msv" + strncpy(fileNames[worldFileCount], dir->d_name, strlen(dir->d_name)-4); + //TODO: This no longer works, update for new format: + //FILE * file = fopen(dir->d_name, "rb"); + //fread(&fileScore[worldFileCount],sizeof(int), 1, file); + //fread(&fileWin[worldFileCount],sizeof(bool), 1, file); + //fclose(file); + fileScore[worldFileCount] = 0; + fileWin[worldFileCount] = false; + + ++worldFileCount; + } + } + closedir(d); + } } void readTPFiles(){ - memset(&tpFileNames, 0, sizeof(tpFileNames)); // reset tp fileNames - memset(&tpFileComment, 0, sizeof(tpFileComment)); // reset zip comments - tpFileCount = 1; // 0 = default. - DIR * d; - struct dirent * dir; - d = opendir("./texturepacks/"); - if (d){ - while ((dir = readdir(d)) != NULL) { - if (strstr(dir->d_name, ".zip") != NULL) { // Check if filename contains ".zip" - strncpy(tpFileNames[tpFileCount], dir->d_name, strlen(dir->d_name)-4); - - char fullDirName[256]; - sprintf(fullDirName,"./texturepacks/%s",dir->d_name); - //int err = - getTexturePackComment(fullDirName, tpFileComment[tpFileCount]); - /* - if(err > 0){ - char errorText[10]; - sprintf(errorText,"err:%d",err); - strncpy(tpFileComment[tpFileCount], errorText, strlen(errorText)); - }*/ - ++tpFileCount; - } - } - closedir(d); - } + memset(&tpFileNames, 0, sizeof(tpFileNames)); // reset tp fileNames + memset(&tpFileComment, 0, sizeof(tpFileComment)); // reset zip comments + tpFileCount = 1; // 0 = default. + DIR * d; + struct dirent * dir; + d = opendir("./texturepacks/"); + if (d){ + while ((dir = readdir(d)) != NULL) { + if (strstr(dir->d_name, ".zip") != NULL) { // Check if filename contains ".zip" + strncpy(tpFileNames[tpFileCount], dir->d_name, strlen(dir->d_name)-4); + + char fullDirName[256]; + sprintf(fullDirName,"./texturepacks/%s",dir->d_name); + //int err = + getTexturePackComment(fullDirName, tpFileComment[tpFileCount]); + /* + if(err > 0){ + char errorText[10]; + sprintf(errorText,"err:%d",err); + strncpy(tpFileComment[tpFileCount], errorText, strlen(errorText)); + }*/ + ++tpFileCount; + } + } + 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! + 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, ".msv") != NULL) { // Check if filename contains ".msv" + char cmprFile[256]; + strncpy(cmprFile, dir->d_name, strlen(dir->d_name)-4); + if(strncmp(fileNames[worldFileCount],cmprFile,strlen(fileNames[worldFileCount])) == 0) return 3; // Error: Filename cannot already exist. + } + } + closedir(d); + } + + return 0; // No errors found! } void addToFileName(char * c){ - strncat(fileNames[worldFileCount], c, 1); + strncat(fileNames[worldFileCount], c, 1); } /* Keypad */ void doTouchButton(){ - int xVal = k_touch.px, yVal = k_touch.py; - int strLength = strlen(fileNames[worldFileCount]); - if(yVal >= 60 && yVal < 80){ // 0 to 9 - if(xVal >= 4 && xVal < 4+16){ touchX = 4; if(strLength < 24)addToFileName("1");} - 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; + int xVal = localInputs.k_touch.px, yVal = localInputs.k_touch.py; + int strLength = strlen(fileNames[worldFileCount]); + if(yVal >= 60 && yVal < 80){ // 0 to 9 + if(xVal >= 4 && xVal < 4+16){ touchX = 4; if(strLength < 24)addToFileName("1");} + 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; - + int id; + for(id = 0; id < 13; ++id){ + if(id > 8 && id < 12) continue; + if(keyProp[id] & buttonID){ + keyProp[id] ^= buttonID; // Toggle buttonID bit + if(left){ + int id2 = id - 1; + if (id2 == 11) id2 = 8; + if (id2 < 0) return; + keyProp[id2] ^= buttonID; + } else { + int id2 = id+1; + if (id2 == 9) id2 = 12; + if (id2 > 13) return; + keyProp[id2] ^= buttonID; + } + return; + } + } + if(left) keyProp[13] ^= buttonID; + else keyProp[0] ^= buttonID; + } void switchMenuBut(bool left, int buttonID){ - int id; - for(id = 0; id < 12; ++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 > 11) return; - keyProp[id2] ^= buttonID; - } - return; - } - } - if(left) keyProp[11] ^= buttonID; - else keyProp[0] ^= buttonID; + int id; + for(id = 0; id < 13; ++id){ + if(id > 3 && id < 9) continue; + if(keyProp[id] & buttonID){ + keyProp[id] ^= buttonID; // Toggle buttonID bit + if(left){ + int id2 = id - 1; + if (id2 == 8) id2 = 3; + if (id2 < 0) return; + keyProp[id2] ^= buttonID; + } else { + int id2 = id+1; + if (id2 == 4) id2 = 9; + if (id2 > 13) return; + keyProp[id2] ^= buttonID; + } + return; + } + } + if(left) keyProp[13] ^= 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; - if(keyProp[10] == 0) return 10; - if(keyProp[11] == 0) return 11; - return -1; + 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; + if(keyProp[10] == 0) return 10; + if(keyProp[11] == 0) return 11; + if(keyProp[12] == 0) return 12; + if(keyProp[13] == 0) return 13; + return -1; +} + +bool menuHasMapLoaded = false; +float mxscr = 400; +float myscr = 400; +float menuxa = 0; +float menuya = 0; +void initMenus() { + readFiles(); + + if(worldFileCount>0) { + memset(¤tFileName, 0, 255); // reset currentFileName + sprintf(currentFileName,"%s.msv",fileNames[currentSelection]); + + initBGMap = 1; + } else { + initBGMap = 2; + } + + menuHasMapLoaded = true; + while(menuxa==0) menuxa = (rand()%3 - 1) * 0.25; + while(menuya==0) menuya = (rand()%3 - 1) * 0.25; } Item median; void tickMenu(int menu){ - switch(menu){ - case MENU_SETTINGS_REBIND: - if(!bindOpt){ - if(!selBut){ - if (k_up.clicked){ --currentSelection; if(currentSelection < 0)currentSelection=21;} - if (k_down.clicked){ ++currentSelection; if(currentSelection > 21)currentSelection=0;} - if (k_left.clicked){ left = true;} - if (k_right.clicked){ left = false;} - } 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]; - k_menuNext.input = keyProp[10]; - k_menuPrev.input = keyProp[11]; - - FILE *fs=fopen("btnSave.bin","wb"); - fwrite(keyProp,sizeof(int),12,fs); - fclose(fs); - - currentSelection = 0; - currentMenu = MENU_SETTINGS; - errorBut = -1; - } else errorBut = checkPropButtons(); - break; - case 1: // Exit and DON'T save - currentSelection = 0; - currentMenu = MENU_SETTINGS; - 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; - keyProp[10] = KEY_R; - keyProp[11] = KEY_L; - bindOpt = false; - errorBut = -1; - break; - } - } - } - break; - case MENU_PAUSED: - if(!areYouSure && !areYouSureSave){ - if(pauseSaveDisplayTimer > 0) --pauseSaveDisplayTimer; - if (k_pause.clicked || k_decline.clicked) currentMenu = MENU_NONE; - if (k_up.clicked){ --currentSelection; if(currentSelection < 0)currentSelection=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; - - playMusic(music_menu); - } else if (k_decline.clicked){ - areYouSure = false; - areYouSureSave = false; - } - } - break; - case MENU_INVENTORY: - if (k_menu.clicked || k_decline.clicked){ - currentMenu = MENU_NONE; - player.p.activeItem = &noItem; - player.p.isCarrying = false; - } - if (k_accept.clicked){ // Select item from inventory - if(player.p.inv->lastSlot!=0){ - median = player.p.inv->items[curInvSel]; // create copy of item. - removeItemFromInventory(curInvSel, player.p.inv); // remove original - pushItemToInventoryFront(median, player.p.inv); // add copy to front + switch(menu){ + case MENU_SETTINGS_REBIND: + if(!bindOpt){ + if(!selBut){ + if (localInputs.k_up.clicked){ --currentSelection; if(currentSelection < 0)currentSelection=21;} + if (localInputs.k_down.clicked){ ++currentSelection; if(currentSelection > 21)currentSelection=0;} + if (localInputs.k_left.clicked){ left = true;} + if (localInputs.k_right.clicked){ left = false;} + } else { + if (localInputs.k_left.clicked){ + if(left)switchGameBut(true,keys[currentSelection]); + else switchMenuBut(true,keys[currentSelection]); + } else if (localInputs.k_right.clicked) { + if(left)switchGameBut(false,keys[currentSelection]); + else switchMenuBut(false,keys[currentSelection]); + } + } + + if (localInputs.k_accept.clicked) selBut = !selBut; + if (localInputs.k_decline.clicked){ + bindOpt = true; + curSaveSel = 0; + } + } else { + if (localInputs.k_up.clicked){ --curSaveSel; if(curSaveSel < 0)curSaveSel=2;} + if (localInputs.k_down.clicked){ ++curSaveSel; if(curSaveSel > 2)curSaveSel=0;} + if (localInputs.k_decline.clicked){ + bindOpt = false; + errorBut = -1; + } + if (localInputs.k_accept.clicked){ + switch(curSaveSel){ + case 0: // Exit and save + if(checkPropButtons() == -1){ + localInputs.k_up.input = keyProp[0]; + localInputs.k_down.input = keyProp[1]; + localInputs.k_left.input = keyProp[2]; + localInputs.k_right.input = keyProp[3]; + localInputs.k_attack.input = keyProp[4]; + localInputs.k_pickup.input = keyProp[5]; + localInputs.k_use.input = keyProp[6]; + localInputs.k_menu.input = keyProp[7]; + localInputs.k_pause.input = keyProp[8]; + localInputs.k_accept.input = keyProp[9]; + localInputs.k_decline.input = keyProp[10]; + localInputs.k_delete.input = keyProp[11]; + localInputs.k_menuNext.input = keyProp[12]; + localInputs.k_menuPrev.input = keyProp[13]; + + FILE *fs=fopen("btnSave.bin","wb"); + fwrite(keyProp,sizeof(keyProp),1,fs); + fclose(fs); + + currentSelection = 0; + currentMenu = MENU_SETTINGS; + errorBut = -1; + } else errorBut = checkPropButtons(); + break; + case 1: // Exit and DON'T save + currentSelection = 0; + currentMenu = MENU_SETTINGS; + 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; + keyProp[5] = KEY_B; + keyProp[6] = KEY_X; + keyProp[7] = KEY_Y ; + keyProp[8] = KEY_START; + keyProp[9] = KEY_A; + keyProp[10] = KEY_B; + keyProp[11] = KEY_X; + keyProp[12] = KEY_R | KEY_ZR; + keyProp[13] = KEY_L | KEY_ZL; + bindOpt = false; + errorBut = -1; + break; + } + } + } + break; + + case MENU_ABOUT: + if (localInputs.k_decline.clicked) currentMenu = MENU_TITLE; + break; + /* + case MENU_ARMOR: + if (k_delete.clicked || k_decline.clicked){ + currentMenu = MENU_NONE; + player.p.activeItem = &noItem; + player.p.isCarrying = false; + } + if (k_accept.clicked){ // Select item from inventory + if(player.p.inv->lastSlot!=0){ + median = player.p.inv->items[curInvSel]; // create copy of item. + removeItemFromInventory(curInvSel, player.p.inv); // remove original + pushItemToInventoryFront(median, player.p.inv); // add copy to front playerSetActiveItem(&player.p.inv->items[0]); // active item = copy. - } - currentMenu = MENU_NONE; - } - if (k_up.clicked){ --curInvSel; if(curInvSel < 0)curInvSel=player.p.inv->lastSlot-1;} - if (k_down.clicked){ ++curInvSel; if(curInvSel > player.p.inv->lastSlot-1)curInvSel=0;} - break; - - case MENU_CRAFTING: - if (k_menu.clicked || k_decline.clicked) currentMenu = MENU_NONE; - if (k_accept.clicked){ - int newslot = player.p.activeItem->slotNum + 1; - if(craftItem(currentRecipes, ¤tRecipes->recipes[curInvSel], player.p.inv)){ - playSound(snd_craft); - if(player.p.activeItem != &noItem)player.p.activeItem = &player.p.inv->items[newslot]; - } - } - if (k_up.clicked){ --curInvSel; if(curInvSel < 0)curInvSel=currentRecipes->size-1;} - if (k_down.clicked){ ++curInvSel; if(curInvSel > currentRecipes->size-1)curInvSel=0;} - break; - - case MENU_WIN: - if (k_accept.clicked){ - sf2d_set_clear_color(0xFF); - currentSelection = 0; - currentMenu = MENU_TITLE; - saveCurrentWorld(currentFileName, &eManager, &player, (u8*)map, (u8*)data); - - playMusic(music_menu); - } - break; - case MENU_LOSE: - if (k_accept.clicked){ - sf2d_set_clear_color(0xFF); - currentSelection = 0; - currentMenu = MENU_TITLE; - - playMusic(music_menu); - } - break; - case MENU_ABOUT: - if (k_decline.clicked) currentMenu = MENU_TITLE; - 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; - } - } - if((k_touch.px != 0 || k_touch.py != 0) && touchDelay == 0){ - if(!isTouching)doTouchButton(); - } - else if(k_touch.px == 0 || k_touch.py == 0) isTouching = false; - if(touchDelay > 0) --touchDelay; - } - break; - case MENU_SETTINGS_TP: - - if (k_up.clicked){ --currentSelection; if(currentSelection < 0)currentSelection = tpFileCount-1;} - if (k_down.clicked){ ++currentSelection; if(currentSelection > tpFileCount-1)currentSelection=0;} - if (k_decline.clicked){ - if(isLoadingTP < 1){ - currentMenu = MENU_SETTINGS; - currentSelection = 1; - } - } - if (k_accept.clicked){ - - if(currentSelection > 0){ - isLoadingTP = 4; - } else { - icons = sfil_load_PNG_buffer(icons2_png, SF2D_PLACE_RAM); - reloadColors(); - font = sfil_load_PNG_buffer(Font_png, SF2D_PLACE_RAM); - bottombg = sfil_load_PNG_buffer(bottombg_png, SF2D_PLACE_RAM); - currentMenu = MENU_SETTINGS; - currentSelection = 1; - remove("lastTP.bin"); - } - } - break; - - case MENU_SETTINGS: - if (k_up.clicked){ - --currentSelection; - if(currentSelection == 3 && !((MODEL_3DS & 6) != 0)) --currentSelection; - if(currentSelection < 0)currentSelection=4; - } - if (k_down.clicked){ - ++currentSelection; - if(currentSelection == 3 && !((MODEL_3DS & 6) != 0)) ++currentSelection; - if(currentSelection > 4)currentSelection=0; - } - if(k_decline.clicked){ - currentMenu = MENU_TITLE; - currentSelection = 2; - } - if(k_accept.clicked){ - switch(currentSelection){ - case 0: - keyProp[0] = k_up.input; - keyProp[1] = k_down.input; - keyProp[2] = k_left.input; - keyProp[3] = k_right.input; - keyProp[4] = k_attack.input; - keyProp[5] = k_menu.input; - keyProp[6] = k_pause.input; - keyProp[7] = k_accept.input; - keyProp[8] = k_decline.input; - keyProp[9] = k_delete.input; - keyProp[10] = k_menuNext.input; - keyProp[11] = k_menuPrev.input; - left = true; - selBut = false; - bindOpt = false; - currentSelection = 0; - currentMenu = MENU_SETTINGS_REBIND; - break; - case 1: - readTPFiles(); - currentMenu = MENU_SETTINGS_TP; - currentSelection = 0; - break; - case 2: - shouldRenderDebug = !shouldRenderDebug; // toggle option - break; - case 3: - if((MODEL_3DS & 6) != 0){ // detect if user is using a New 3DS - shouldSpeedup = !shouldSpeedup; // toggle option - osSetSpeedupEnable(shouldSpeedup); - } - break; - case 4: - if(true){ - FILE * fset = fopen("settings.bin","wb"); - fwrite(&shouldRenderDebug,sizeof(bool),1,fset); - fwrite(&shouldSpeedup,sizeof(bool),1,fset); - fclose(fset); - } - currentMenu = MENU_TITLE; - currentSelection = 2; - break; - } - } - 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 1: - sprintf(pageText,"Page: %d/%d",pageNum+1,maxPageNum+1); - currentMenu = MENU_TUTORIAL; - break; - case 2: - currentSelection = 0; - currentMenu = MENU_SETTINGS; - break; - case 3: - currentMenu = MENU_ABOUT; - break; - case 4: - quitGame = true; - break; - } - - } - break; - case MENU_TUTORIAL: - if(k_decline.clicked){ - currentSelection = 1; - currentMenu = MENU_TITLE; - } - if(k_menuNext.clicked){ - if(pageNum < maxPageNum){ - ++pageNum; - sprintf(pageText,"Page: %d/%d",pageNum+1,maxPageNum+1); - } - } - if(k_menuPrev.clicked){ - if(pageNum > 0){ - --pageNum; - sprintf(pageText,"Page: %d/%d",pageNum+1,maxPageNum+1); - } - } - - break; - } - + } + 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_LOADGAME: + if(!enteringName && !areYouSure){ // World select + if (localInputs.k_decline.clicked){ + if(toMultiplayer) { + currentMenu = MENU_MULTIPLAYER_HOST; + } else { + currentMenu = MENU_TITLE; + currentSelection = 0; + } + } + if (localInputs.k_up.clicked){ --currentSelection; if(currentSelection < 0)currentSelection = worldFileCount;} + if (localInputs.k_down.clicked){ ++currentSelection; if(currentSelection > worldFileCount)currentSelection=0;} + + if(localInputs.k_delete.clicked){ + if(currentSelection < worldFileCount) areYouSure = true; + } + + if(localInputs.k_accept.clicked){ + if(currentSelection == worldFileCount){ + + enteringName = true; + } else { + memset(¤tFileName, 0, 255); // reset currentFileName + sprintf(currentFileName, "%s.msv", fileNames[currentSelection]); + playSound(snd_test); + if(toMultiplayer) { + initMPGame = 2; + } else { + initGame = 1; + } + currentMenu = MENU_LOADING; + } + } + } else if (areYouSure){ + if (localInputs.k_decline.clicked || localInputs.k_delete.clicked) areYouSure = false; + if (localInputs.k_accept.clicked){ + sprintf(currentFileName,"%s.msv",fileNames[currentSelection]); + remove(currentFileName); + readFiles(); + enteringName = false; + areYouSure = false; + memset(¤tFileName, 0, 255); // reset currentFileName + } + } else { // Enter new world name. + if(localInputs.k_decline.clicked) enteringName = false; + if(localInputs.k_accept.clicked && errorFileName == 0){ + errorFileName = checkFileNameForErrors(); + if(errorFileName == 0){ // If no errors are found with the filename, then start a new game! + memset(¤tFileName, 0, 255); // reset currentFileName + sprintf(currentFileName, "%s.msv", fileNames[worldFileCount]); + currentMenu = MENU_LOADING; + playSound(snd_test); + if(toMultiplayer) { + initMPGame = 2; + } else { + initGame = 1; + } + ++worldFileCount; + currentMenu = MENU_LOADING; + } + } + if((localInputs.k_touch.px != 0 || localInputs.k_touch.py != 0) && touchDelay == 0){ + if(!isTouching)doTouchButton(); + } + else if(localInputs.k_touch.px == 0 || localInputs.k_touch.py == 0) isTouching = false; + if(touchDelay > 0) --touchDelay; + } + break; + case MENU_SETTINGS_TP: + + if (localInputs.k_up.clicked){ --currentSelection; if(currentSelection < 0)currentSelection = tpFileCount-1;} + if (localInputs.k_down.clicked){ ++currentSelection; if(currentSelection > tpFileCount-1)currentSelection=0;} + if (localInputs.k_decline.clicked){ + if(isLoadingTP < 1){ + currentMenu = MENU_SETTINGS; + currentSelection = 1; + } + } + if (localInputs.k_accept.clicked){ + + if(currentSelection > 0){ + isLoadingTP = 4; + } else { + icons = sfil_load_PNG_buffer(icons_png, SF2D_PLACE_RAM); + reloadColors(); + font = sfil_load_PNG_buffer(Font_png, SF2D_PLACE_RAM); + bottombg = sfil_load_PNG_buffer(bottombg_png, SF2D_PLACE_RAM); + currentMenu = MENU_SETTINGS; + currentSelection = 1; + remove("lastTP.bin"); + } + } + break; + + case MENU_SETTINGS: + if (localInputs.k_up.clicked){ + --currentSelection; + if(currentSelection == 3 && !((MODEL_3DS & 6) != 0)) --currentSelection; + if(currentSelection < 0)currentSelection=4; + } + if (localInputs.k_down.clicked){ + ++currentSelection; + if(currentSelection == 3 && !((MODEL_3DS & 6) != 0)) ++currentSelection; + if(currentSelection > 4)currentSelection=0; + } + if(localInputs.k_decline.clicked){ + currentMenu = MENU_TITLE; + currentSelection = 4; + } + if(localInputs.k_accept.clicked){ + switch(currentSelection){ + case 0: + keyProp[0] = localInputs.k_up.input; + keyProp[1] = localInputs.k_down.input; + keyProp[2] = localInputs.k_left.input; + keyProp[3] = localInputs.k_right.input; + keyProp[4] = localInputs.k_attack.input; + keyProp[5] = localInputs.k_pickup.input; + keyProp[6] = localInputs.k_use.input; + keyProp[7] = localInputs.k_menu.input; + keyProp[8] = localInputs.k_pause.input; + keyProp[9] = localInputs.k_accept.input; + keyProp[10] = localInputs.k_decline.input; + keyProp[11] = localInputs.k_delete.input; + keyProp[12] = localInputs.k_menuNext.input; + keyProp[13] = localInputs.k_menuPrev.input; + left = true; + selBut = false; + bindOpt = false; + currentSelection = 0; + currentMenu = MENU_SETTINGS_REBIND; + break; + case 1: + readTPFiles(); + currentMenu = MENU_SETTINGS_TP; + currentSelection = 0; + break; + case 2: + shouldRenderDebug = !shouldRenderDebug; // toggle option + break; + case 3: + if((MODEL_3DS & 6) != 0){ // detect if user is using a New 3DS + shouldSpeedup = !shouldSpeedup; // toggle option + osSetSpeedupEnable(shouldSpeedup); + } + break; + case 4: + if(true){ + FILE * fset = fopen("settings.bin","wb"); + fwrite(&shouldRenderDebug,sizeof(bool),1,fset); + fwrite(&shouldSpeedup,sizeof(bool),1,fset); + fclose(fset); + } + currentMenu = MENU_TITLE; + currentSelection = 2; + break; + } + } + break; + case MENU_TITLE: + //Map BG + if(menuHasMapLoaded) { + mxscr += menuxa; + myscr += menuya; + + if (mxscr < 16) { + mxscr = 16; + menuxa = -menuxa; + } else if (mxscr > 1832) { + mxscr = 1832; + menuxa = -menuxa; + } + if (myscr < 16) { + myscr = 16; + menuya = -menuya; + } else if (myscr > 1792) { + myscr = 1792; + menuya = -menuya; + } + } + + if (localInputs.k_up.clicked){ --currentSelection; if(currentSelection < 0)currentSelection=6;} + if (localInputs.k_down.clicked){ ++currentSelection; if(currentSelection > 6)currentSelection=0;} + + if(localInputs.k_accept.clicked){ + switch(currentSelection){ + case 0: + toMultiplayer = false; + currentMenu = MENU_LOADGAME; + readFiles(); + currentSelection = 0; + enteringName = false; + areYouSure = false; + break; + case 1: + if(networkHost()) { + toMultiplayer = true; + currentMenu = MENU_MULTIPLAYER_HOST; + currentSelection = 0; + } + break; + case 2: + networkScan(); + currentMenu = MENU_MULTIPLAYER_JOIN; + currentSelection = 0; + menuScanTimer = 0; + break; + case 3: + sprintf(pageText,"Page: %d/%d",pageNum+1,maxPageNum+1); + currentMenu = MENU_TUTORIAL; + break; + case 4: + currentSelection = 0; + currentMenu = MENU_SETTINGS; + break; + case 5: + currentMenu = MENU_ABOUT; + break; + case 6: + quitGame = true; + break; + } + + } + break; + case MENU_TUTORIAL: + if(localInputs.k_decline.clicked){ + currentSelection = 3; + currentMenu = MENU_TITLE; + } + if(localInputs.k_menuNext.clicked){ + if(pageNum < maxPageNum){ + ++pageNum; + sprintf(pageText,"Page: %d/%d",pageNum+1,maxPageNum+1); + } + } + if(localInputs.k_menuPrev.clicked){ + if(pageNum > 0){ + --pageNum; + sprintf(pageText,"Page: %d/%d",pageNum+1,maxPageNum+1); + } + } + + break; + case MENU_MULTIPLAYER_HOST: + if (localInputs.k_decline.clicked){ + networkDisconnect(); + + currentMenu = MENU_TITLE; + currentSelection = 1; + } + + if(localInputs.k_accept.clicked){ + if(networkGetNodeCount()>1) { + currentMenu = MENU_LOADGAME; + readFiles(); + currentSelection = 0; + enteringName = false; + areYouSure = false; + } + } + break; + case MENU_MULTIPLAYER_JOIN: + if(menuScanTimer>0) { + menuScanTimer--; + } else { + networkScan(); + menuScanTimer = 30; + } + if(currentSelection >= networkGetScanCount()) currentSelection=networkGetScanCount()-1; + if(currentSelection < 0) currentSelection = 0; + + if (localInputs.k_decline.clicked){ + currentMenu = MENU_TITLE; + currentSelection = 2; + } + if (localInputs.k_up.clicked){ --currentSelection; if(currentSelection < 0) currentSelection = (networkGetScanCount()>0 ? networkGetScanCount()-1 : 0);} + if (localInputs.k_down.clicked){ ++currentSelection; if(currentSelection >= networkGetScanCount()) currentSelection=0;} + + if(localInputs.k_accept.clicked){ + if(networkGetScanCount()!=0) { + for(int t=0; t<10; t++) { //try to connect multiple times, because it will not work the first try every time + if(networkConnect(currentSelection)) { + currentMenu = MENU_MULTIPLAYER_WAIT; + currentSelection = 0; + break; + } + } + } + } + break; + case MENU_MULTIPLAYER_WAIT: + if (localInputs.k_decline.clicked){ + networkDisconnect(); + + currentMenu = MENU_TITLE; + currentSelection = 2; + } + break; + } + } -u8 opacity = 255; -bool rev = true; char scoreText[15]; char * getButtonFunctionGame(int key){ - 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"; + 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 "Pick up"; + if(keyProp[6] & key) return "Use"; + if(keyProp[7] & key) return "Toggle Menu"; + if(keyProp[8] & key) return "Pause"; + if(keyProp[12] & key) return "Next"; + if(keyProp[13] & key) return "Previous"; + 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"; - if(keyProp[10] & key) return "Next"; - if(keyProp[11] & key) return "Previous"; - return "Nothing"; + 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[9] & key) return "Accept"; + if(keyProp[10] & key) return "Decline"; + if(keyProp[11] & key) return "Delete"; + if(keyProp[12] & key) return "Next"; + if(keyProp[13] & key) return "Previous"; + return "Nothing"; } @@ -730,600 +756,562 @@ 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_TUTORIAL: - sf2d_start_frame(GFX_TOP, GFX_LEFT); + int i = 0; + switch(menu){ + case MENU_TUTORIAL: + sf2d_start_frame(GFX_TOP, GFX_LEFT); sf2d_draw_rectangle(0, 0, 400, 240, 0xFF0C0C0C); //You might think "real" black would be better, but it actually looks better that way - - renderTutorialPage(true); - sf2d_end_frame(); - sf2d_start_frame(GFX_BOTTOM, GFX_LEFT); + + renderTutorialPage(true); + sf2d_end_frame(); + sf2d_start_frame(GFX_BOTTOM, GFX_LEFT); sf2d_draw_rectangle(0, 0, 320, 240, 0xFF0C0C0C); //You might think "real" black would be better, but it actually looks better that way - - renderTutorialPage(false); - sf2d_end_frame(); - break; - case MENU_SETTINGS_TP: - offsetX = 0;offsetY = (currentSelection * 40) - 48; - sf2d_start_frame(GFX_TOP, GFX_LEFT); + + renderTutorialPage(false); + sf2d_end_frame(); + break; + case MENU_SETTINGS_TP: + offsetX = 0;offsetY = (currentSelection * 40) - 48; + sf2d_start_frame(GFX_TOP, GFX_LEFT); sf2d_draw_rectangle(0, 0, 400, 240, 0xFF0C0C0C); //You might think "real" black would be better, but it actually looks better that way - - drawText("Texture Packs",122,-16); - for(i = 0; i < tpFileCount; ++i){ - int color = 0xFF923232; - char * text = tpFileNames[i]; - char * cmtText = tpFileComment[i]; - if(i == 0){ - text = "Default"; - cmtText = "Regular look of the game"; - color = 0xFF921060; - } - if(i != currentSelection) color &= 0x7FFFFFFF; // Darken color. - else if(areYouSure)color = 0xFF1010DF; - - renderFrame(1,i*5,24,(i*5)+5,color); - drawText(text,(400-(strlen(text)*12))/2,i*80+16); - - if(strlen(cmtText) > 29){ - char cmtTxt1[30],cmtTxt2[30]; - strncpy(cmtTxt1, cmtText, 29); - strncpy(cmtTxt2, cmtText + 29, strlen(cmtText)-29); - drawTextColor(cmtTxt1,(400-(strlen(cmtTxt1)*12))/2,i*80+36,0xFFAFAFAF); - drawTextColor(cmtTxt2,(400-(strlen(cmtTxt2)*12))/2,i*80+50,0xFFAFAFAF); - } else { - drawTextColor(cmtText,(400-(strlen(cmtText)*12))/2,i*80+43,0xFFAFAFAF); - } - } - offsetX = 0;offsetY = 0; - if(isLoadingTP > 0){ - --isLoadingTP; - renderFrame(1,5,24,9,0xFF666666); - drawTextColor("Loading Texture pack...",(400-(23*12))/2,108,0xFF10FFFF); - if(isLoadingTP == 0){ - char fullDirName[256]; - sprintf(fullDirName,"texturepacks/%s.zip",tpFileNames[currentSelection]); - loadedtp = loadTexturePack(fullDirName); - - FILE * fs=fopen("lastTP.bin","w"); - fprintf(fs,"%s", fullDirName); - fclose(fs); - - currentMenu = MENU_SETTINGS; - currentSelection = 1; - } - } - sf2d_end_frame(); - sf2d_start_frame(GFX_BOTTOM, GFX_LEFT); - sf2d_draw_rectangle(0, 0, 320, 240, 0xFF0C0C0C); //You might think "real" black would be better, but it actually looks better that way - - drawText("Press to select", 58, 100); - renderButtonIcon(k_accept.input & -k_accept.input, 128, 98, 1); - drawText("Press to return", 58, 150); - renderButtonIcon(k_decline.input & -k_decline.input, 128, 148, 1); - sf2d_end_frame(); - break; - case MENU_LOADGAME: - sf2d_start_frame(GFX_TOP, GFX_LEFT); - sf2d_draw_rectangle(0, 0, 400, 240, 0xFF0C0C0C); //You might think "real" black would be better, but it actually looks better that way - - 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 = 0xFF921020; - char * text = fileNames[i]; - if(i == worldFileCount){ - text = "Generate New World"; - color = 0xFF209210; + + drawText("Texture Packs",122,-16); + for(i = 0; i < tpFileCount; ++i){ + int color = 0xFF923232; + char * text = tpFileNames[i]; + char * cmtText = tpFileComment[i]; + if(i == 0){ + text = "Default"; + cmtText = "Regular look of the game"; + color = 0xFF921060; } - if(i != currentSelection) color &= 0xFF7F7F7F; // Darken color. - else { - if(areYouSure)color = 0xFF1010DF; - } - - 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); + if(i != currentSelection) color &= 0x7FFFFFFF; // Darken color. + else if(areYouSure)color = 0xFF1010DF; + + renderFrame(1,i*5,24,(i*5)+5,color); + drawText(text,(400-(strlen(text)*12))/2,i*80+16); + + if(strlen(cmtText) > 29){ + char cmtTxt1[30],cmtTxt2[30]; + strncpy(cmtTxt1, cmtText, 29); + strncpy(cmtTxt2, cmtText + 29, strlen(cmtText)-29); + drawTextColor(cmtTxt1,(400-(strlen(cmtTxt1)*12))/2,i*80+36,0xFFAFAFAF); + drawTextColor(cmtTxt2,(400-(strlen(cmtTxt2)*12))/2,i*80+50,0xFFAFAFAF); } else { - drawText(text,(400-(strlen(text)*12))/2,i*64+24); + drawTextColor(cmtText,(400-(strlen(cmtText)*12))/2,i*80+43,0xFFAFAFAF); } - 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,0xFF1010AF); break; - case 2: drawTextColor("ERROR: You need Letters/Numbers!",(400-32*12)/2,200,0xFF1010AF); break; - case 3: drawTextColor("ERROR: Filename already exists!",(400-31*12)/2,200,0xFF1010AF); break; - } - } - } - sf2d_end_frame(); - sf2d_start_frame(GFX_BOTTOM, GFX_LEFT); - sf2d_draw_rectangle(0, 0, 320, 240, 0xFF0C0C0C); //You might think "real" black would be better, but it actually looks better that way - - if(!enteringName){ // World select - if(!areYouSure){ - drawTextColor("Load World",100,12,0xFF3FFFFF); - drawText("Press or to scroll", 28, 50); - renderButtonIcon(k_up.input & -k_up.input, 98, 48, 1); - renderButtonIcon(k_down.input & -k_down.input, 160, 48, 1); - 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); + offsetX = 0;offsetY = 0; + if(isLoadingTP > 0){ + --isLoadingTP; + renderFrame(1,5,24,9,0xFF666666); + drawTextColor("Loading Texture pack...",(400-(23*12))/2,108,0xFF10FFFF); + if(isLoadingTP == 0){ + char fullDirName[256]; + sprintf(fullDirName,"texturepacks/%s.zip",tpFileNames[currentSelection]); + loadedtp = loadTexturePack(fullDirName); + + FILE * fs=fopen("lastTP.bin","w"); + fprintf(fs,"%s", fullDirName); + fclose(fs); + + currentMenu = MENU_SETTINGS; + currentSelection = 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,0xFF33FFFF); - - sf2d_draw_rectangle(0, 50, 320, 110, 0xFF7F7FBF); - - if(touchDelay > 0){ - sf2d_draw_rectangle(touchX, touchY, touchW, touchH, 0xFF0000AF); - } - - 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); - - 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_REBIND: - sf2d_start_frame(GFX_TOP, GFX_LEFT); - sf2d_draw_rectangle(0, 0, 400, 240, 0xFF0C0C0C); //You might think "real" black would be better, but it actually looks better that way - - drawTextColor("Rebind Buttons",116,12,0xFF00AFAF); - 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 = 0xFF7F7F7F; - - 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 = 0xFF00FF00; - 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, 0xFF7F7F7F); - } else { - drawTextColor(gameButText, 112, (i * 33) + 80, 0xFF7F7F7F); - drawTextColor(menuButText, 280, (i * 33) + 80, ccol); - } - } - if(bindOpt){ - renderFrame(1,1,24,14,0xFFBF1010); - drawTextColor("Save changes?",122,32,0xFF00AFAF); - for(i = 2; i >= 0; --i){ - char* msg = keybOptions[i]; - u32 color = 0xFF4F4F4F; - 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 < 12){ - 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; - case 10: sprintf(errorText, "Error: Missing 'Next'"); break; - case 11: sprintf(errorText, "Error: Missing 'Previous'"); break; - } - drawTextColor(errorText,(400 - (strlen(errorText) * 12))/2,50,0xFF0000FF); - } - - } - sf2d_end_frame(); - sf2d_start_frame(GFX_BOTTOM, GFX_LEFT); + sf2d_end_frame(); + sf2d_start_frame(GFX_BOTTOM, GFX_LEFT); sf2d_draw_rectangle(0, 0, 320, 240, 0xFF0C0C0C); //You might think "real" black would be better, but it actually looks better that way - + + drawText("Press to select", 58, 100); + renderButtonIcon(localInputs.k_accept.input & -localInputs.k_accept.input, 128, 98, 1); + drawText("Press to return", 58, 150); + renderButtonIcon(localInputs.k_decline.input & -localInputs.k_decline.input, 128, 148, 1); + sf2d_end_frame(); + break; + case MENU_LOADGAME: + sf2d_start_frame(GFX_TOP, GFX_LEFT); + sf2d_draw_rectangle(0, 0, 400, 240, 0xFF0C0C0C); //You might think "real" black would be better, but it actually looks better that way + + 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 = 0xFF921020; + char * text = fileNames[i]; + if(i == worldFileCount){ + text = "Generate New World"; + color = 0xFF209210; + } + if(i != currentSelection) color &= 0xFF7F7F7F; // Darken color. + else { + if(areYouSure)color = 0xFF1010DF; + } + + 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,0xFF1010AF); break; + case 2: drawTextColor("ERROR: You need Letters/Numbers!",(400-32*12)/2,200,0xFF1010AF); break; + case 3: drawTextColor("ERROR: Filename already exists!",(400-31*12)/2,200,0xFF1010AF); break; + } + } + } + sf2d_end_frame(); + sf2d_start_frame(GFX_BOTTOM, GFX_LEFT); + sf2d_draw_rectangle(0, 0, 320, 240, 0xFF0C0C0C); //You might think "real" black would be better, but it actually looks better that way + + if(!enteringName){ // World select + if(!areYouSure){ + drawTextColor("Load World",100,12,0xFF3FFFFF); + drawText("Press or to scroll", 28, 50); + renderButtonIcon(localInputs.k_up.input & -localInputs.k_up.input, 98, 48, 1); + renderButtonIcon(localInputs.k_down.input & -localInputs.k_down.input, 160, 48, 1); + drawText("Press to load world", (320-21*12)/2, 100); + renderButtonIcon(localInputs.k_accept.input & -localInputs.k_accept.input, 104, 98, 1); + drawText("Press to return", 58, 150); + renderButtonIcon(localInputs.k_decline.input & -localInputs.k_decline.input, 128, 148, 1); + if(currentSelection != worldFileCount){ + drawText("Press to delete",(320-17*12)/2, 200); + renderButtonIcon(localInputs.k_delete.input & -localInputs.k_delete.input, 128, 198, 1); + } + } else { + drawTextColor("Delete File?",88,12,0xFF3F3FFF); + drawText("Press to confirm", (320-18*12)/2, 100); + renderButtonIcon(localInputs.k_accept.input & -localInputs.k_accept.input, 122, 98, 1); + drawText("Press to return", 58, 150); + renderButtonIcon(localInputs.k_decline.input & -localInputs.k_decline.input, 128, 148, 1); + } + + } else { // Draw the "keyboard" + drawTextColor("Touch the keypad below",(320-22*12)/2,12,0xFF33FFFF); + + sf2d_draw_rectangle(0, 50, 320, 110, 0xFF7F7FBF); + + if(touchDelay > 0){ + sf2d_draw_rectangle(touchX, touchY, touchW, touchH, 0xFF0000AF); + } + + 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); + + drawText("Press to confirm", (320-18*12)/2, 180); + renderButtonIcon(localInputs.k_accept.input & -localInputs.k_accept.input, 122, 178, 1); + drawText("Press to return", 58, 210); + renderButtonIcon(localInputs.k_decline.input & -localInputs.k_decline.input, 128, 208, 1); + } + sf2d_end_frame(); + break; + case MENU_SETTINGS_REBIND: + sf2d_start_frame(GFX_TOP, GFX_LEFT); + sf2d_draw_rectangle(0, 0, 400, 240, 0xFF0C0C0C); //You might think "real" black would be better, but it actually looks better that way + + drawTextColor("Rebind Buttons",116,12,0xFF00AFAF); + 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 = 0xFF7F7F7F; + + 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 = 0xFF00FF00; + 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, 0xFF7F7F7F); + } else { + drawTextColor(gameButText, 112, (i * 33) + 80, 0xFF7F7F7F); + drawTextColor(menuButText, 280, (i * 33) + 80, ccol); + } + } + if(bindOpt){ + renderFrame(1,1,24,14,0xFFBF1010); + drawTextColor("Save changes?",122,32,0xFF00AFAF); + for(i = 2; i >= 0; --i){ + char* msg = keybOptions[i]; + u32 color = 0xFF4F4F4F; + if(i == curSaveSel) color = 0xFFFFFFFF; + drawTextColor(msg,(400 - (strlen(msg) * 12))/2, (i * 24) + 92, color); + } + drawText("Press to return", 98, 190); + renderButtonIcon(localInputs.k_decline.input & -localInputs.k_decline.input, 168, 188, 1); + + if(errorBut >= 0 && errorBut < 14){ + 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 'Pick up'"); break; + case 6: sprintf(errorText, "Error: Missing 'Use'"); break; + case 7: sprintf(errorText, "Error: Missing 'Toggle Menu'"); break; + case 8: sprintf(errorText, "Error: Missing 'Pause'"); break; + case 9: sprintf(errorText, "Error: Missing 'Accept'"); break; + case 10: sprintf(errorText, "Error: Missing 'Decline'"); break; + case 11: sprintf(errorText, "Error: Missing 'Delete'"); break; + case 12: sprintf(errorText, "Error: Missing 'Next'"); break; + case 13: sprintf(errorText, "Error: Missing 'Previous'"); break; + } + drawTextColor(errorText,(400 - (strlen(errorText) * 12))/2,50,0xFF0000FF); + } + + } + sf2d_end_frame(); + sf2d_start_frame(GFX_BOTTOM, GFX_LEFT); + sf2d_draw_rectangle(0, 0, 320, 240, 0xFF0C0C0C); //You might think "real" black would be better, but it actually looks better that way + if(!selBut){ drawText("Press to select", 58, 80); - renderButtonIcon(k_accept.input & -k_accept.input, 128, 78, 1); + renderButtonIcon(localInputs.k_accept.input & -localInputs.k_accept.input, 128, 78, 1); drawText("Press to return", 58, 130); - renderButtonIcon(k_decline.input & -k_decline.input, 128, 128, 1); + renderButtonIcon(localInputs.k_decline.input & -localInputs.k_decline.input, 128, 128, 1); } else { drawText("Press or to scroll", 28, 50); - renderButtonIcon(k_left.input & -k_left.input, 98, 48, 1); - renderButtonIcon(k_right.input & -k_right.input, 160, 48, 1); + renderButtonIcon(localInputs.k_left.input & -localInputs.k_left.input, 98, 48, 1); + renderButtonIcon(localInputs.k_right.input & -localInputs.k_right.input, 160, 48, 1); drawText("Press to unselect", 46, 100); - renderButtonIcon(k_accept.input & -k_accept.input, 118, 98, 1); + renderButtonIcon(localInputs.k_accept.input & -localInputs.k_accept.input, 118, 98, 1); drawText("Press to return", 58, 150); - renderButtonIcon(k_decline.input & -k_decline.input, 128, 148, 1); + renderButtonIcon(localInputs.k_decline.input & -localInputs.k_decline.input, 128, 148, 1); } - sf2d_end_frame(); - break; - - case MENU_PAUSED: - sf2d_start_frame(GFX_TOP, GFX_LEFT); - if(currentLevel == 0){ - sf2d_draw_texture_part_scale(minimap[1],(-xscr/3)-256,(-yscr/3)-32,0,0,128,128,12.5,7.5); - sf2d_draw_rectangle(0,0,400,240, 0xAFDFDFDF); - } - offsetX = xscr;offsetY = yscr; - renderMenuBackground(xscr,yscr); - offsetX = 0;offsetY = 0; - renderFrame(1,1,24,14,0xFF1010AF); - drawText("Paused",164,32); - for(i = 2; i >= 0; --i){ - char* msg = pOptions[i]; - u32 color = 0xFF7F7F7F; - 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,0xFF20FF20); - - if(areYouSure || areYouSureSave){ - if(areYouSure)renderFrame(6,5,19,10,0xFF10108F); - else renderFrame(6,5,19,10,0xFF108F10); - - drawText("Are you sure?",122,96); - drawText(" Yes", 164, 117); - renderButtonIcon(k_accept.input & -k_accept.input, 166, 114, 1); - drawText(" No", 170, 133); - renderButtonIcon(k_decline.input & -k_decline.input, 166, 130, 1); - } - - sf2d_end_frame(); - break; - case MENU_WIN: - sf2d_start_frame(GFX_TOP, GFX_LEFT); - if(currentLevel == 0){ - sf2d_draw_texture_part_scale(minimap[1],(-xscr/3)-256,(-yscr/3)-32,0,0,128,128,12.5,7.5); - sf2d_draw_rectangle(0,0,400,240, 0xAFDFDFDF); - } - offsetX = xscr;offsetY = yscr; - renderMenuBackground(xscr,yscr); - offsetX = 0;offsetY = 0; - renderFrame(5,3,21,12,0xFFFF1010); - if(!rev){ opacity+=5; if(opacity == 255) rev = true; } - else { opacity-=5; if(opacity == 100) rev = false; } - sprintf(scoreText,"Score: %d", player.p.score); - drawTextColor("You Win!",158,76,0x0000AFAF + (opacity << 24)); - drawText(scoreText, 200-((strlen(scoreText)-1)*6), 100); - drawText("Press to continue", 96, 150); - renderButtonIcon(k_attack.input & -k_attack.input, 166, 148, 1); - - //printf("0x%08X",k_attack.input & -k_attack.input); - sf2d_end_frame(); - break; - case MENU_LOSE: - sf2d_start_frame(GFX_TOP, GFX_LEFT); - if(currentLevel == 0){ - sf2d_draw_texture_part_scale(minimap[1],(-xscr/3)-256,(-yscr/3)-32,0,0,128,128,12.5,7.5); - sf2d_draw_rectangle(0,0,400,240, 0xAFDFDFDF); - } - offsetX = xscr;offsetY = yscr; - renderMenuBackground(xscr,yscr); - offsetX = 0;offsetY = 0; - renderFrame(5,3,21,12,0xFFFF1010); - if(!rev){ opacity+=5; if(opacity == 255) rev = true; } - else { opacity-=5; if(opacity == 100) rev = false; } - sprintf(scoreText,"Score: %d", player.p.score); - drawTextColor("You DIED!",158,76,0x000000AF + (opacity << 24)); - drawText(scoreText, 200-((strlen(scoreText)-1)*6), 100); - drawText("Press to continue", 96, 150); - renderButtonIcon(k_attack.input & -k_attack.input, 166, 148, 1); - //printf("0x%08X",k_attack.input & -k_attack.input); - sf2d_end_frame(); - break; - case MENU_INVENTORY: - sf2d_start_frame(GFX_TOP, GFX_LEFT); - if(currentLevel == 0){ - sf2d_draw_texture_part_scale(minimap[1],(-xscr/3)-256,(-yscr/3)-32,0,0,128,128,12.5,7.5); - sf2d_draw_rectangle(0,0,400,240, 0xAFDFDFDF); - } - offsetX = xscr;offsetY = yscr; - renderMenuBackground(xscr,yscr); - offsetX = 0;offsetY = 0; - renderFrame(1,1,24,14,0xFFFF1010); - renderItemList(player.p.inv, 1,1,24,14, curInvSel); - sf2d_end_frame(); - break; - case MENU_CRAFTING: - sf2d_start_frame(GFX_TOP, GFX_LEFT); - if(currentLevel == 0){ - sf2d_draw_texture_part_scale(minimap[1],(-xscr/3)-256,(-yscr/3)-32,0,0,128,128,12.5,7.5); - sf2d_draw_rectangle(0,0,400,240, 0xAFDFDFDF); - } - offsetX = xscr;offsetY = yscr; - renderMenuBackground(xscr,yscr); - offsetX = 0;offsetY = 0; - - renderFrame(15,1,24,4,0xFFFF1010); - renderFrame(15,5,24,14,0xFFFF1010); - renderFrame(1,1,14,14,0xFFFF1010); - renderRecipes(currentRecipes, 1, 1, 14, 14, curInvSel); - - Recipe* rec = ¤tRecipes->recipes[curInvSel]; - renderItemIcon(rec->itemResult,rec->itemAmountLevel,128,16); - char craftText[12]; - sprintf(craftText,"%d",countItemInv(rec->itemResult,rec->itemAmountLevel, player.p.inv)); - drawText(craftText,274,34); - - if(rec->numOfCosts > 0){ - int i; - for(i = 0; i < rec->numOfCosts; i++){ - int amnt = countItemInv(rec->costs[i].costItem,0, player.p.inv); - int ttlCst = rec->costs[i].costAmount; - int col = 0xFFFFFFFF; if(amntcosts[i].costItem,1,128,48+(i*8)); - sprintf(craftText,"%d/%d",amnt,ttlCst); - drawTextColor(craftText,274,96+(i*18),col); - } - } - - sf2d_end_frame(); - break; - - case MENU_CONTAINER: - sf2d_start_frame(GFX_TOP, GFX_LEFT); - if(currentLevel == 0){ - sf2d_draw_texture_part_scale(minimap[1],(-xscr/3)-256,(-yscr/3)-32,0,0,128,128,12.5,7.5); - sf2d_draw_rectangle(0,0,400,240, 0xAFDFDFDF); - } - offsetX = xscr;offsetY = yscr; - renderMenuBackground(xscr,yscr); - if (curChestEntity->entityFurniture.r == 1){ offsetX = 48;offsetY = 0;} - else {offsetX = 0;offsetY = 0;} - - renderFrame(1,1,14,14,0xFFFF1010); - renderItemList(curChestEntity->entityFurniture.inv,1,1,14,14, - curChestEntity->entityFurniture.r == 0 ? curInvSel : -curChestEntity->entityFurniture.oSel - 1); - renderFrame(15,1,28,14,0xFFFF1010); - 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); + sf2d_end_frame(); + break; + + case MENU_ABOUT: + sf2d_start_frame(GFX_TOP, GFX_LEFT); sf2d_draw_rectangle(0, 0, 400, 240, 0xFF0C0C0C); //You might think "real" black would be better, but it actually looks better that way - - 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,0xFF00FF00); - drawSizedTextColor("This port was made by David Benepe (Davideesk)",16,144,1.0,0xFF00FF00); - drawSizedTextColor("just for fun in September/October 2015.",44,156,1.0,0xFF00FF00); - drawSizedTextColor("Updated and modded by Andre Schweiger (andre111)",8,168,1.0,0xFF00FF00); - drawSizedTextColor("TY Notch for creating a fun game to remake!",28,192,1.0,0xFF00FF00); - sf2d_end_frame(); - sf2d_start_frame(GFX_BOTTOM, GFX_LEFT); - sf2d_draw_rectangle(0, 0, 320, 240, 0xFF0C0C0C); //You might think "real" black would be better, but it actually looks better that way - - drawTextColor("Special Thanks to:",52,12,0xFF7F7FFF); - drawTextColor("Smea",136,50,0xFF2020FF); - drawSizedTextColor("for ctrulib",116,70,1.0,0xFF2020FF); - drawTextColor("Xerpi",130,100,0xFFFF2020); - drawSizedTextColor("for sf2dlib",116,120,1.0,0xFFFF2020); - drawTextColor("Music from",100,150,0xFF20FF20); - drawSizedTextColor("opengameart.org/content/",64,170,1.0,0xFF20FF20); - drawSizedTextColor("generic-8-bit-jrpg-soundtrack",48,180,1.0,0xFF20FF20); - - drawText("Press to return", 58, 220); - renderButtonIcon(k_decline.input & -k_decline.input, 128, 218, 1); - sf2d_end_frame(); - break; - case MENU_SETTINGS: - sf2d_start_frame(GFX_TOP, GFX_LEFT); - sf2d_draw_rectangle(0, 0, 400, 240, 0xFF0C0C0C); //You might think "real" black would be better, but it actually looks better that way - - drawText("Settings",(400-(8*12))/2,30); - for(i = 4; i >= 0; --i){ - char* msg = setOptions[i]; - u32 color = 0xFF7F7F7F; - if(i == currentSelection) color = 0xFFFFFFFF; - if(i == 2){ - if(shouldRenderDebug) drawSizedTextColor("On",142, ((8 + i) * 32 - 190) >> 1,2.0, 0xFF00DF00); - else drawSizedTextColor("Off",142, ((8 + i) * 32 - 190) >> 1,2.0, 0xFF0000DF); - } else if(i == 3){ - - if((MODEL_3DS & 6) != 0){ // detect if user is using a New 3DS - if(shouldSpeedup) drawSizedTextColor("On",142, ((8 + i) * 32 - 190) >> 1,2.0, 0xFF00DF00); - else drawSizedTextColor("Off",142, ((8 + i) * 32 - 190) >> 1,2.0, 0xFF0000DF); - } else { - color = 0xFF3F3F3F; - drawSizedTextColor("Off",142, ((8 + i) * 32 - 190) >> 1,2.0, 0xFF3F3F3F); - } - } - drawSizedTextColor(msg,(200 - (strlen(msg) * 8))/2, ((8 + i) * 32 - 190) >> 1,2.0, color); - } - sf2d_end_frame(); - sf2d_start_frame(GFX_BOTTOM, GFX_LEFT); - sf2d_draw_rectangle(0, 0, 320, 240, 0xFF0C0C0C); //You might think "real" black would be better, but it actually looks better that way - - switch(currentSelection){ - case 0: - drawTextColor("Change the controls",(320 - (19 * 12))/2,24,0xFF7FFFFF); - break; - case 1: - drawTextColor("Change the game's art",(320 - (21 * 12))/2,24,0xFF7FFFFF); - break; - case 2: - drawTextColor("Show FPS/Pos/Entities",(320 - (22 * 12))/2,24,0xFF7FFFFF); - break; - case 3: - drawTextColor("Use the N3DS 804mhz mode",(320 - (24 * 12))/2,24,0xFF7FFFFF); - break; - case 4: - drawTextColor("Back to the titlescreen",(320 - (23 * 12))/2,24,0xFF7FFFFF); - break; - } - drawText("Press to select", 58, 100); - renderButtonIcon(k_accept.input & -k_accept.input, 128, 98, 1); - drawText("Press to return", 58, 150); - renderButtonIcon(k_decline.input & -k_decline.input, 128, 148, 1); - sf2d_end_frame(); - break; - case MENU_TITLE: - /* Top Screen */ - sf2d_start_frame(GFX_TOP, GFX_LEFT); - sf2d_draw_rectangle(0, 0, 400, 240, 0xFF0C0C0C); //You might think "real" black would be better, but it actually looks better that way - - renderTitle(76,16); - - for(i = 4; i >= 0; --i){ - char* msg = options[i]; - u32 color = 0xFF7F7F7F; - 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); + + 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,0xFF00FF00); + drawSizedTextColor("This port was made by David Benepe (Davideesk)",16,144,1.0,0xFF00FF00); + drawSizedTextColor("just for fun in September/October 2015.",44,156,1.0,0xFF00FF00); + drawSizedTextColor("Modded by Andre Schweiger (andre111) and ",44,168,1.0,0xFF00FF00); + drawSizedTextColor("Elijah Bansley (ElijahZAwesome)",71,180,1.0,0xFF00FF00); + drawSizedTextColor("TY Notch for creating a fun game to remake!",28,192,1.0,0xFF00FF00); + sf2d_end_frame(); + sf2d_start_frame(GFX_BOTTOM, GFX_LEFT); sf2d_draw_rectangle(0, 0, 320, 240, 0xFF0C0C0C); //You might think "real" black would be better, but it actually looks better that way - + + drawTextColor("Special Thanks to:",52,12,0xFF7F7FFF); + drawTextColor("Smea",136,50,0xFF2020FF); + drawSizedTextColor("for ctrulib",116,70,1.0,0xFF2020FF); + drawTextColor("Xerpi",130,100,0xFFFF2020); + drawSizedTextColor("for sf2dlib",116,120,1.0,0xFFFF2020); + drawTextColor("Music from",100,150,0xFF20FF20); + drawSizedTextColor("opengameart.org/content/",64,170,1.0,0xFF20FF20); + drawSizedTextColor("generic-8-bit-jrpg-soundtrack",48,180,1.0,0xFF20FF20); + + drawText("Press to return", 58, 220); + renderButtonIcon(localInputs.k_decline.input & -localInputs.k_decline.input, 128, 218, 1); + sf2d_end_frame(); + break; + case MENU_SETTINGS: + sf2d_start_frame(GFX_TOP, GFX_LEFT); + sf2d_draw_rectangle(0, 0, 400, 240, 0xFF0C0C0C); //You might think "real" black would be better, but it actually looks better that way + + drawText("Settings",(400-(8*12))/2,30); + for(i = 4; i >= 0; --i){ + char* msg = setOptions[i]; + u32 color = 0xFF7F7F7F; + if(i == currentSelection) color = 0xFFFFFFFF; + if(i == 2){ + if(shouldRenderDebug) drawSizedTextColor("On",142, ((8 + i) * 32 - 190) >> 1,2.0, 0xFF00DF00); + else drawSizedTextColor("Off",142, ((8 + i) * 32 - 190) >> 1,2.0, 0xFF0000DF); + } else if(i == 3){ + + if((MODEL_3DS & 6) != 0){ // detect if user is using a New 3DS + if(shouldSpeedup) drawSizedTextColor("On",142, ((8 + i) * 32 - 190) >> 1,2.0, 0xFF00DF00); + else drawSizedTextColor("Off",142, ((8 + i) * 32 - 190) >> 1,2.0, 0xFF0000DF); + } else { + color = 0xFF3F3F3F; + drawSizedTextColor("Off",142, ((8 + i) * 32 - 190) >> 1,2.0, 0xFF3F3F3F); + } + } + drawSizedTextColor(msg,(200 - (strlen(msg) * 8))/2, ((8 + i) * 32 - 190) >> 1,2.0, color); + } + sf2d_end_frame(); + sf2d_start_frame(GFX_BOTTOM, GFX_LEFT); + sf2d_draw_rectangle(0, 0, 320, 240, 0xFF0C0C0C); //You might think "real" black would be better, but it actually looks better that way + + switch(currentSelection){ + case 0: + drawTextColor("Change the controls",(320 - (19 * 12))/2,24,0xFF7FFFFF); + break; + case 1: + drawTextColor("Change the game's art",(320 - (21 * 12))/2,24,0xFF7FFFFF); + break; + case 2: + drawTextColor("Enable Testing Features.",(320 - (22 * 12))/2,24,0xFF7FFFFF); + break; + case 3: + drawTextColor("Use the N3DS 804mhz mode",(320 - (24 * 12))/2,24,0xFF7FFFFF); + break; + case 4: + drawTextColor("Back to the titlescreen",(320 - (23 * 12))/2,24,0xFF7FFFFF); + break; + } + drawText("Press to select", 58, 100); + renderButtonIcon(localInputs.k_accept.input & -localInputs.k_accept.input, 128, 98, 1); + drawText("Press to return", 58, 150); + renderButtonIcon(localInputs.k_decline.input & -localInputs.k_decline.input, 128, 148, 1); + sf2d_end_frame(); + break; + case MENU_TITLE: + /* Top Screen */ + sf2d_start_frame(GFX_TOP, GFX_LEFT); + sf2d_draw_rectangle(0, 0, 400, 240, 0xFF0C0C0C); //You might think "real" black would be better, but it actually looks better that way + //map BG + if(menuHasMapLoaded) { + offsetX = (int) mxscr; offsetY = (int) myscr; + renderBackground(1, (int) mxscr, (int) myscr); + offsetX = 0; offsetY = 0; + + sf2d_draw_rectangle(0, 0, 400, 240, 0xAA0C0C0C); //You might think "real" black would be better, but it actually looks better that way + } + + renderTitle(76,8); + + for(i = 6; i >= 0; --i){ + char* msg = options[i]; + u32 color = 0xFF7F7F7F; + if(i == currentSelection) color = 0xFFFFFFFF; + drawSizedTextColor(msg,((200 - (strlen(msg) * 8))/2) + 1, (((8 + i) * 20 - 74) >> 1) + 1,2.0, 0xFF000000); + drawSizedTextColor(msg,(200 - (strlen(msg) * 8))/2, ((8 + i) * 20 - 74) >> 1,2.0, color); + } + + drawText(versionText,2,225); + sf2d_end_frame(); + + /* Bottom Screen */ + sf2d_start_frame(GFX_BOTTOM, GFX_LEFT); + sf2d_draw_rectangle(0, 0, 320, 240, 0xFF0C0C0C); //You might think "real" black would be better, but it actually looks better that way + //map BG + if(menuHasMapLoaded) { + offsetX = (int) mxscr + 20; offsetY = (int) myscr + 120; + renderBackground(1, (int) mxscr + 20, (int) myscr + 120); + offsetX = 0; offsetY = 0; + + sf2d_draw_rectangle(0, 0, 320, 240, 0xAA0C0C0C); //You might think "real" black would be better, but it actually looks better that way + } + 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; - menuRenderTilePit(startX,startY,176,16,waterColor[0]);// water pit - 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,0xFFAEC6DC);//Iron ore - startX = 40;startY = 90; - render16b (startX,startY,128,112,0,0xFFADFFAD);//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; - menuRenderTilePit(startX,startY,112,16,grassColor);// grass pit - 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,0xFF7FFFFF); - 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,0xFF7FFFFF); - break; - case 2: // "Settings" - drawTextColor("Modify the game's feel",(320 - (22 * 12))/2,24,0xFF7FFFFF); - renderc(48,48,0,112,64,32,0); - break; - case 3: // "About" - drawTextColor("Who made this game?",(320 - (19 * 12))/2,24,0xFF7FFFFF); - - // Secret code ;) - //drawSizedText("497420776173206e6f746368",(320 - (24 * 8))/2,80,1); - //drawSizedText("506f727465642062792044617669646565736b",(320 - (38 * 8))/2,88,1); - //drawSizedText("576879207265616420746869733f",(320 - (28 * 8))/2,96,1); - //drawSizedText("596f75207761737465642074696d65",(320 - (30 * 8))/2,104,1); - //drawSizedText("4861204861204861204861204861",(320 - (28 * 8))/2,112,1); - //drawSizedText("5468652063616b652069732061206c6965",(320 - (34 * 8))/2,120,1); - //drawSizedText("4044617669646565736b2074776974746572",(320 - (36 * 8))/2,128,1); - //drawSizedText("3533363536333732363537343231",(320 - (28 * 8))/2,136,1); - //drawSizedText("4c69617220746578742062656c6f77",(320 - (30 * 8))/2,144,1); - //drawSizedText("(Totally not a secret code or anything)",4,160,1); - - break; - case 4: // "Exit" - drawTextColor("Exit to the homebrew menu",(320 - (25 * 12))/2,24,0xFF7FFFFF); - drawTextColor("(bye-bye)",(320 - (9 * 12))/2,100,0xFF7FFFFF); - break; - } - sf2d_end_frame(); - break; - } + switch(currentSelection){ + case 0: // "Start Game" + break; + case 1: // "Host Game" + drawTextColor("Host local multiplayer",(320 - (22 * 12))/2,24,0xFF7FFFFF); + break; + case 2: // "Join Game" + drawTextColor("Join local multiplayer",(320 - (22 * 12))/2,24,0xFF7FFFFF); + break; + case 3: // "How To Play" + startX = 72;startY = 54; + render16(startX,startY,96,208,0);//C-PAD + startX = 72;startY = 37; + 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,0xFF7FFFFF); + break; + case 4: // "Settings" + drawTextColor("Modify the game's feel",(320 - (22 * 12))/2,24,0xFF7FFFFF); + renderc(48,48,0,112,64,32,0); + break; + case 5: // "About" + drawTextColor("Who made this game?",(320 - (19 * 12))/2,24,0xFF7FFFFF); + + // Secret code ;) + //drawSizedText("497420776173206e6f746368",(320 - (24 * 8))/2,80,1); + //drawSizedText("506f727465642062792044617669646565736b",(320 - (38 * 8))/2,88,1); + //drawSizedText("576879207265616420746869733f",(320 - (28 * 8))/2,96,1); + //drawSizedText("596f75207761737465642074696d65",(320 - (30 * 8))/2,104,1); + //drawSizedText("4861204861204861204861204861",(320 - (28 * 8))/2,112,1); + //drawSizedText("5468652063616b652069732061206c6965",(320 - (34 * 8))/2,120,1); + //drawSizedText("4044617669646565736b2074776974746572",(320 - (36 * 8))/2,128,1); + //drawSizedText("3533363536333732363537343231",(320 - (28 * 8))/2,136,1); + //drawSizedText("4c69617220746578742062656c6f77",(320 - (30 * 8))/2,144,1); + //drawSizedText("(Totally not a secret code or anything)",4,160,1); + + break; + case 6: // "Exit" + drawTextColor("Exit to the home menu",(320 - (21 * 12))/2,24,0xFF7FFFFF); + drawTextColor("(bye-bye)",(320 - (9 * 12))/2,100,0xFF7FFFFF); + break; + } + sf2d_end_frame(); + break; + case MENU_MULTIPLAYER_HOST: + sf2d_start_frame(GFX_TOP, GFX_LEFT); + sf2d_draw_rectangle(0, 0, 400, 240, 0xFF0C0C0C); //You might think "real" black would be better, but it actually looks better that way + + networkUpdateStatus(); + drawText("Connected Players",98,8); + int j = 0; + int lastj = 0; + for(i = 0; i", 64, 110); - renderButtonIcon(biasedCirclePad(k_up.input), 44, 92, 1); - renderButtonIcon(k_accept.input & -k_accept.input, 44, 108, 1); - renderButtonIcon(biasedCirclePad(k_down.input), 44, 125, 1); - break; - case 3: // Furniture - sf2d_draw_rectangle(64, 48, 192, 32, grassColor); - renderc(32,24,64,128,96,16,0);//Furniture entities - - renderFurniture(ITEM_WORKBENCH, 50,60); - render16(50,46,0,112,0);//Player-down - renderc(50,58,16,160,16,8,2);//Slash - render(54,58,56,152,0);//Power glove - - render16(92,56,0,128,0);//Player(Carrying) - render16(92,44,128,128,0);//Workbench - break; - case 4: // Crafting - renderFrame(11,3,19,6,0xFFFF1010); - renderFrame(11,7,19,12,0xFFFF1010); - renderFrame(1,3,10,12,0xFFFF1010); - renderItemStuffWithText(TOOL_AXE,0,true,28,64); - renderItemIcon(TOOL_AXE, 0, 94, 32); - drawText("0", 206, 66); - renderItemIcon(ITEM_WOOD, 0, 94, 64); - drawText("16/5", 206, 130); - break; - case 5: // Farming (Bottom screen) - renderc(24,16,144,0,112,16,0); // Wheat Stages - - render16(20,40,144,0,0); // Farm Tile - render16(36,40,240,0,0); // Wheat Tile - render16(52,40,240,0,0); // Wheat Tile - render16(20,54,16,112,0); // Player (Up) - renderc(20,50,16,160,16,8,0); // Slash (Up) - render(19,45,40,152,0); // Seeds - render(26,39,48,152,0); // Wheat1 - render(29,44,48,152,0); // Wheat2 - - renderc(72,40,144,0,32,16,0); // Farm Tile + Seeded Wheat Tile - render16(72,54,16,112,0); // Player (Up) - renderc(72,50,16,160,16,8,0); // Slash (Up) - render(76,48,40,152,0); // Seeds - - sf2d_draw_rectangle(216, 80, 32, 32, dirtColor[1]); // Dirt color for grass - render16b(108, 40, 112, 16, 0, grassColor); // Grass - render16(124, 40,144,0,0); // Farm Tile - render16(108,54,16,112,0); // Player (Up) - renderc(108,50,16,160,16,8,0); // Slash (Up) - render(112,48,72,144,0); // Gem Hoe - - sf2d_draw_rectangle(112, 156, 32, 32, dirtColor[1]); // Dirt color for grass - render16b(56, 78, 112, 16, 0, grassColor); // Grass - sf2d_draw_rectangle(80, 156, 32, 32, dirtColor[1]); // Dirt color - render16b(40, 78, 0, 0, 0, 0xFF8F8FA8); // Dirt Dots - render(44, 82, 40,152,0); // Seeds - - render16(24,78,48,112,0); // Player (Right) - renderc(36,78,40,160,8,16,0); // Slash (Right) - render(38,82,32,144,0); // Gem Shovel - - render(82,78,48,152,0); // Wheat - render(90,78,48,152,0); // Wheat - render(82,86,48,152,0); // Wheat - render(90,86,48,152,0); // Wheat - drawText(">",203,164); - render16(108,76,96,128,0); // Oven - drawText(">",246,164); - render(132,82,72,152,0); // Bread - break; - case 6: //Mining - render16b(23,32,80,0,0,0xFFC8C8DF); // iron ore - render16b(23,52,80,0,0,0xFFB9E8E5); // gold ore - render16b(23,72,80,0,0,0xFFDE98DF); // gem ore - renderb(41,38,88,152,0,0xFFC8C8DF); // Iron ore item - renderb(41,58,88,152,0,0xFFB9E8E5); // Gold ore item - render(41,78,112,152,0); // Gem item - drawText(">",104,74); - drawText(">",104,114); - drawText(">",104,154); - render16(60,32,112,128,0); // Furnace - render16(60,52,112,128,0); // Furnace - render16(60,72,64,128,0); // Anvil - drawText(">",160,74); - drawText(">",160,114); - drawText(">",160,154); - renderb(88,36,96,152,0,0xFFC8C8DF); // Iron ingot item - renderb(88,56,96,152,0,0xFFB9E8E5); // Gold ingot item - renderb(88,76,152,144,0,0xFFB9E8E5); // Gem Pickaxe - drawText(">",200,74); - drawText(">",200,114); - render16(106,32,64,128,0); // Anvil - render16(106,52,64,128,0); // Anvil - drawText(">",244,74); - drawText(">",244,114); - render(130,36,136,144,0); // Iron Pickaxe - render(130,56,144,144,0); // Gold Pickaxe - break; - } - - drawText(pageText,(320-(strlen(pageText))*12)/2,12); - if(pageNum > 0){ - drawText("<",2,16); - renderButtonIcon(k_menuPrev.input & -k_menuPrev.input, 8, 2, 2); - } - if(pageNum < maxPageNum){ - drawText(">",306,16); - renderButtonIcon(k_menuNext.input & -k_menuNext.input, 136, 2, 2); - } - drawText("Press to exit",(320-(15*12))/2,218); - renderButtonIcon(k_decline.input & -k_decline.input, 140, 216, 1); - } + if(topScreen){ + drawTextColor("How to Play",(400-11*12)/2,12,0xFF00AFAF); + switch(pageNum){ + case 0: // Moving the character + drawTextColor("Movement",(400-8*12)/2,40,0xFF007FBF); + drawText("Press to move up",92,90); + renderButtonIcon(biasedCirclePad(localInputs.k_up.input), 164, 88, 1); + drawText("Press to move down",80,120); + renderButtonIcon(biasedCirclePad(localInputs.k_down.input), 152, 118, 1); + drawText("Press to move left",80,150); + renderButtonIcon(biasedCirclePad(localInputs.k_left.input), 152, 148, 1); + drawText("Press to move right",74,180); + renderButtonIcon(biasedCirclePad(localInputs.k_right.input), 146, 178, 1); + break; + case 1: // Attacking + drawTextColor("Attacking",(400-9*12)/2,40,0xFF007FBF); + drawText("Press to Attack",98,80); + renderButtonIcon(localInputs.k_attack.input & -localInputs.k_attack.input, 168, 78, 1); + drawText("Attack with an item to use it",26,120); + drawText("Use the axe to cut down trees",26,140); + drawText("Use the sword to attack enemies",14,160); + drawText("Use the shovel to dig ground",32,180); + drawText("Use the pickaxe to mine rock/ore",8,200); + break; + case 2: // Inventory + drawTextColor("Inventory",(400-9*12)/2,40,0xFF007FBF); + drawText("Press to open the menu",56,80); + renderButtonIcon(biasedMenuXY(localInputs.k_menu.input), 126, 78, 1); + drawText("Press to scroll up",80,110); + renderButtonIcon(biasedCirclePad(localInputs.k_up.input), 152, 108, 1); + drawText("Press to scroll down",68,140); + renderButtonIcon(biasedCirclePad(localInputs.k_down.input), 140, 138, 1); + drawText("Press to select an item",50,170); + renderButtonIcon(localInputs.k_accept.input & -localInputs.k_accept.input, 120, 168, 1); + drawText("Press to close the menu",50,200); + renderButtonIcon(localInputs.k_decline.input & -localInputs.k_decline.input, 120, 198, 1); + break; + case 3: // Furniture + drawTextColor("Furniture",(400-9*12)/2,40,0xFF007FBF); + drawText("Use furniture for item crafting",(400-31*12)/2,74); + drawText("Press to open the menu",56,100); + renderButtonIcon(biasedMenuXY(localInputs.k_menu.input), 126, 98, 1); + drawText("while infront of the furniture",(400-30*12)/2,116); + drawText("Use the lantern item to light",(400-29*12)/2,144); + drawText("up underground areas",(400-20*12)/2,160); + drawText("Use the power glove item to",(400-27*12)/2,184); + drawText("pick up furniture",(400-17*12)/2,200); + break; + case 4: // Crafting + drawTextColor("Crafting",(400-8*12)/2,40,0xFF007FBF); + drawText("Create new items and tools",(400-26*12)/2,74); + drawText("Go up to a furniture item and",(400-29*12)/2,104); + drawText("Press to open the menu",56,120); + renderButtonIcon(biasedMenuXY(localInputs.k_menu.input), 126, 118, 1); + drawText("Gather up the required materials",(400-32*12)/2,150); + drawText("and then press to craft it",(400-28*12)/2,166); + renderButtonIcon(localInputs.k_accept.input & -localInputs.k_accept.input, 210, 164, 1); + break; + case 5: // Farming + drawTextColor("Farming",(400-7*12)/2,40,0xFF007FBF); + drawText("Grow wheat to make bread",(400-24*12)/2,74); + drawText("Dig up grass to gather seeds",(400-28*12)/2,94); + drawText("Use the hoe to till ground",(400-26*12)/2,114); + drawText("Harvest wheat when it is yellow",(400-31*12)/2,134); + drawText("Use the oven to bake bread",(400-26*12)/2,154); + drawText("It takes 4 wheat to craft bread",(400-31*12)/2,174); + break; + case 6: // Mining + drawTextColor("Mining",(400-6*12)/2,40,0xFF007FBF); + drawText("Use a pickaxe tool for mining",(400-29*12)/2,74); + drawText("Mine rocks for stone",(400-20*12)/2,94); + drawText("Mine iron ore for iron",(400-22*12)/2,114); + drawText("Mine gold ore for gold",(400-22*12)/2,134); + drawText("Mine gem ore to get gems",(400-24*12)/2,154); + drawText("It takes 4 ore and 1 coal to",(400-28*12)/2,190); + drawText("make an ingot inside a furnace",(400-30*12)/2,210); + break; + case 7: // Potion Brewing + drawTextColor("Brewing",(400-6*12)/2,40,0xFF007FBF); + drawText("Create potions.",(400-13*12)/2,74); + drawText("The potions give you abilities",(400-29*12)/2,94); + drawText("Like speed and strength",(400-22*12)/2,114); + drawText("They are hard to obtain",(400-22*12)/2,134); + break; + } + } else { + switch(pageNum){ + case 0: // Moving the character + render16(30,56,16,112,0);//Player up + renderButtonIcon(biasedCirclePad(localInputs.k_up.input), 30,40, 2); + render16(60,56,0,112,0);//Player down + renderButtonIcon(biasedCirclePad(localInputs.k_down.input), 60,40, 2); + render16(90,56,48,112,1);//Player left + renderButtonIcon(biasedCirclePad(localInputs.k_left.input), 90,40, 2); + render16(120,56,48,112,0);//Player right + renderButtonIcon(biasedCirclePad(localInputs.k_right.input), 120,40, 2); + break; + case 1: // Attacking + render16(60,56,0,112,0);//Player-down + renderButtonIcon(localInputs.k_attack.input & -localInputs.k_attack.input, 80, 56, 2); + renderc(60,68,16,160,16,8,2);//Slash + + menuRenderTilePit(12,20,256,0);// grass pit + render16(12+8,20+4,256,48,0);//Tree + renderc(12+9,20+14,16,160,16,8,0);//Slash + render(12+9+4,20+14,192,144,0);//Axe + render16(12+9,20+18,16,112,0);//Player-up + + menuRenderTilePit(122,62,320,0);// sand pit + render16(130,70,256,16,0);// hole + render16(116,70,48,112,0);//Player-right + renderb(136,76,16,152,0,sandColor);// Sand item + renderc(128,70,40,160,8,16,0);//Slash + render(130,74,0,144,0);//Shovel + break; + case 2: // Inventory + renderFrame(4,4,17,11,0xFFFF1010); + renderItemStuffWithText(ITEM_APPLE,5,false,80,78); + renderItemStuffWithText(ITEM_SLIME,11,false,80,94); + renderItemStuffWithText(TOOL_SWORD,4,true,80,110); + renderItemStuffWithText(ITEM_IRONORE,3,false,80,126); + renderItemStuffWithText(ITEM_IRONINGOT,11,false,80,142); + sf2d_draw_rectangle(64, 110, 12, 12, 0xFF); + drawText(">", 64, 110); + renderButtonIcon(biasedCirclePad(localInputs.k_up.input), 44, 92, 1); + renderButtonIcon(localInputs.k_accept.input & -localInputs.k_accept.input, 44, 108, 1); + renderButtonIcon(biasedCirclePad(localInputs.k_down.input), 44, 125, 1); + break; + case 3: // Furniture + sf2d_draw_rectangle(64, 48, 192, 32, grassColor); + renderc(32,24,64,128,96,16,0);//Furniture entities + + renderFurniture(ITEM_WORKBENCH, 50,60); + render16(50,46,0,112,0);//Player-down + renderc(50,58,16,160,16,8,2);//Slash + render(54,58,56,152,0);//Power glove + + render16(92,56,0,128,0);//Player(Carrying) + render16(92,44,128,128,0);//Workbench + break; + case 4: // Crafting + renderFrame(11,3,19,6,0xFFFF1010); + renderFrame(11,7,19,12,0xFFFF1010); + renderFrame(1,3,10,12,0xFFFF1010); + renderItemStuffWithText(TOOL_AXE,0,true,28,64); + renderItemIcon(TOOL_AXE, 0, 94, 32); + drawText("0", 206, 66); + renderItemIcon(ITEM_WOOD, 0, 94, 64); + drawText("16/5", 206, 130); + break; + case 5: // Farming (Bottom screen) + renderc(24,16,352,48,112,16,0); // Wheat Stages + + render16(20,40,352,48,0); // Farm Tile + render16(36,40,448,48,0); // Wheat Tile + render16(52,40,448,48,0); // Wheat Tile + render16(20,54,16,112,0); // Player (Up) + renderc(20,50,16,160,16,8,0); // Slash (Up) + render(19,45,40,152,0); // Seeds + render(26,39,48,152,0); // Wheat1 + render(29,44,48,152,0); // Wheat2 + + renderc(72,40,352,48,32,16,0); // Farm Tile + Seeded Wheat Tile + render16(72,54,16,112,0); // Player (Up) + renderc(72,50,16,160,16,8,0); // Slash (Up) + render(76,48,40,152,0); // Seeds + + sf2d_draw_rectangle(216, 80, 32, 32, dirtColor[1]); // Dirt color for grass + render16(108, 40, 256, 0, 0); // Grass + render16(124, 40,352,48,0); // Farm Tile + render16(108,54,16,112,0); // Player (Up) + renderc(108,50,16,160,16,8,0); // Slash (Up) + render(112,48,72,144,0); // Gem Hoe + + sf2d_draw_rectangle(112, 156, 32, 32, dirtColor[1]); // Dirt color for grass + render16(56, 78, 256, 0, 0); // Grass + sf2d_draw_rectangle(80, 156, 32, 32, dirtColor[1]); // Dirt color + render16(40, 78, 336, 80, 0); // Dirt Dots + render(44, 82, 40,152,0); // Seeds + + render16(24,78,48,112,0); // Player (Right) + renderc(36,78,40,160,8,16,0); // Slash (Right) + render(38,82,32,144,0); // Gem Shovel + + render(82,78,48,152,0); // Wheat + render(90,78,48,152,0); // Wheat + render(82,86,48,152,0); // Wheat + render(90,86,48,152,0); // Wheat + drawText(">",203,164); + render16(108,76,96,128,0); // Oven + drawText(">",246,164); + render(132,82,72,152,0); // Bread + break; + case 6: //Mining + render16(23,32,464,48,0); // iron ore + render16(23,52,480,48,0); // gold ore + render16(23,72,496,48,0); // gem ore + renderb(41,38,88,152,0,ironColor); // Iron ore item + renderb(41,58,88,152,0,goldColor); // Gold ore item + render(41,78,112,152,0); // Gem item + drawText(">",104,74); + drawText(">",104,114); + drawText(">",104,154); + render16(60,32,112,128,0); // Furnace + render16(60,52,112,128,0); // Furnace + render16(60,72,240,128,0); // Enchanter + drawText(">",160,74); + drawText(">",160,114); + drawText(">",160,154); + renderb(88,36,96,152,0,ironColor); // Iron ingot item + renderb(88,56,96,152,0,goldColor); // Gold ingot item + renderb(88,76,152,144,0,goldColor); // Gem Pickaxe + drawText(">",200,74); + drawText(">",200,114); + render16(106,32,64,128,0); // Anvil + render16(106,52,64,128,0); // Anvil + drawText(">",244,74); + drawText(">",244,114); + render(130,36,136,144,0); // Iron Pickaxe + render(130,56,144,144,0); // Gold Pickaxe + break; + case 7: // Brewing + render16(65, 56, 240, 96, 0); + break; + } + + drawText(pageText,(320-(strlen(pageText))*12)/2,12); + if(pageNum > 0){ + drawText("<",2,16); + renderButtonIcon(localInputs.k_menuPrev.input & -localInputs.k_menuPrev.input, 8, 2, 2); + } + if(pageNum < maxPageNum){ + drawText(">",306,16); + renderButtonIcon(localInputs.k_menuNext.input & -localInputs.k_menuNext.input, 136, 2, 2); + } + drawText("Press to exit",(320-(15*12))/2,218); + renderButtonIcon(localInputs.k_decline.input & -localInputs.k_decline.input, 140, 216, 1); + } } diff --git a/source/MenuTutorial.h b/source/MenuTutorial.h old mode 100644 new mode 100755 index 682d7c7..5ce7f4e --- a/source/MenuTutorial.h +++ b/source/MenuTutorial.h @@ -1,7 +1,7 @@ #pragma once #include <3ds.h> -#include +#include #include #include #include diff --git a/source/Network.c b/source/Network.c new file mode 100755 index 0000000..1489bbe --- /dev/null +++ b/source/Network.c @@ -0,0 +1,620 @@ +#include "Network.h" + +#include "PacketHandler.h" +#include +#include +#include + +bool udsRunning; + +size_t scannedNetworksCount; +udsNetworkScanInfo *scannedNetworks; + +bool isConnected; +bool isServer; + +size_t networkBufferSize; +void *networkBuffer; + +udsNetworkStruct networkStruct; +udsBindContext networkBindCtx; + +udsConnectionStatus networkStatus; + +u16 networkConnectedMask; + +//new code +//structure in buffer is u16(seqID),u16(size),size(data), ... +void *networkSendBuffer; +size_t networkSendBufferStartPos; +size_t networkSendBufferEndPos; +size_t networkSendBufferWrapPos; + +u16 networkSeqSendNext; +u16 networkSeqSendConf[UDS_MAXNODES+1]; +u16 networkSeqRecvLast[UDS_MAXNODES+1]; +void *networkAckBuffer; + +//async internal send/recieve handling +Thread networkThread; +volatile bool networkRunThread; +LightLock sendBufferLock; + +void networkHandleSend(); +void networkHandleRecieve(); +void clearSendAckedBuffer(); +bool sendAck(u16 target, u16 ack); + +void networkThreadMain(void *arg) { + while(networkRunThread) { + if(udsRunning && isConnected) { + networkUpdateStatus(); + networkHandleRecieve(); + networkHandleSend(); + } + + //TODO: Set meaningfull value, WARNING: Setting this near 1ms (1000*1000) will make everything super laggy, higher values actually work better! + svcSleepThread(10000 * 1000); + } +} + +void networkUpdateStatus() { + /*for(int i=0; i<10; i++) { + Result ret = udsGetConnectionStatus(&networkStatus); + if(!R_FAILED(ret)) { + return; + } + }*/ + if(udsWaitConnectionStatusEvent(false, false)) { + udsGetConnectionStatus(&networkStatus); + } +} + +void networkHandleRecieve() { + bool recieved = false; + do { + recieved = false; + + size_t actualSize = 0; + u16 sourceNetworkNodeID; + u32 ackToSend = 0; + + memset(networkBuffer, 0, networkBufferSize); + + Result ret = udsPullPacket(&networkBindCtx, networkBuffer, networkBufferSize, &actualSize, &sourceNetworkNodeID); + if(R_FAILED(ret)) { + //TODO: what do? + + //actualSize will be 0 if no packet is available + } else if(actualSize) { + void *readPointer = networkBuffer; + + //ack frame + if(actualSize==sizeof(u16)) { + networkSeqSendConf[sourceNetworkNodeID] = *((u16*) readPointer); + clearSendAckedBuffer(); + //normal frame + } else { + while(actualSize>0) { + //read seqID and size + u16 seqID = *((u16*) readPointer); + readPointer += sizeof(u16); + actualSize -= sizeof(u16); + + u16 size = *((u16*) readPointer); + readPointer += sizeof(u16); + actualSize -= sizeof(u16); + + //if the seq id was expected handle the packet + u16 nextID = networkGetExpectedSeqFrom(sourceNetworkNodeID); + if(seqID==nextID) { + networkSeqRecvLast[sourceNetworkNodeID] = seqID; + ackToSend = seqID; + + //handle data - TODO: WARNING: Do not send sizeof(u16) packets or else they will get confused with this one + if(size==sizeof(u16)) { + networkConnectedMask = *((u16*) readPointer); + } else { + processPacket(readPointer, size); + } + } else if(networkSeqIsLowerThan(seqID, nextID)) { + ackToSend = seqID; + } + readPointer += size; + actualSize -= size; + } + + if(ackToSend!=0) { + if(sendAck(sourceNetworkNodeID, ackToSend)) { + } + } + } + + recieved = true; + } + } while(recieved); +} + +void networkHandleSend() { + if(networkSendBufferStartPos!=networkSendBufferEndPos) { + LightLock_Lock(&sendBufferLock); + + //determine send size + size_t currentSize = 0; + while(networkSendBufferStartPos+currentSize0) { + //TODO: Once we have our own custom mask, no longer broadcast, but send directly because bcast doesn't always reach everyone? + if(networkConnectedMask==0) { + //send frame + Result ret = udsSendTo(UDS_BROADCAST_NETWORKNODEID, NETWORK_CHANNEL, UDS_SENDFLAG_Default, networkSendBuffer+networkSendBufferStartPos, currentSize); + if(UDS_CHECK_SENDTO_FATALERROR(ret)) { + //TODO: what do? + } else if(R_FAILED(ret)) { + //TODO: what do? + } + } else { + for(int i=1; i<=UDS_MAXNODES; i++) { + if(i!=networkGetLocalNodeID()/* && networkIsNodeConnected(i)*/ && networkConnectedMask & (1 << (i-1))) { + //send frame + Result ret = udsSendTo(i, NETWORK_CHANNEL, UDS_SENDFLAG_Default, networkSendBuffer+networkSendBufferStartPos, currentSize); + if(UDS_CHECK_SENDTO_FATALERROR(ret)) { + //TODO: what do? + } else if(R_FAILED(ret)) { + //TODO: what do? + } + } + } + } + } + + LightLock_Unlock(&sendBufferLock); + } +} + +void clearSendAckedBuffer() { + //find last ack recieved from all com partners + u16 ackID = 0; + for(int i=1; i<=UDS_MAXNODES; i++) { + if(i!=networkGetLocalNodeID()/* && networkIsNodeConnected(i)*/ && networkConnectedMask & (1 << (i-1))) { + if(networkSeqSendConf[i]==0) { + ackID = 0; + return; + } + + if(ackID==0) { + ackID = networkSeqSendConf[i]; + } else if(networkSeqSendConf[i]<100) { + if(ackID > networkSeqSendConf[i] && ackID<65535-100) ackID = networkSeqSendConf[i]; + } else if(networkSeqSendConf[i]>65535-100) { + if(ackID > networkSeqSendConf[i] || ackID<100) ackID = networkSeqSendConf[i]; + } else { + if(ackID > networkSeqSendConf[i]) ackID = networkSeqSendConf[i]; + } + } + } + if(ackID==0) return; + + LightLock_Lock(&sendBufferLock); + + //clear buffer of acknowledgt packets + while(networkSendBufferStartPos!=networkSendBufferEndPos) { + //find current seqid and size + u16 seqID = *((u16*) (networkSendBuffer+networkSendBufferStartPos)); + u16 size = *((u16*) (networkSendBuffer+networkSendBufferStartPos+sizeof(u16))); + + if(seqID<=ackID || (ackID<100 && seqID>65535-100)) { + + size_t currentSize = sizeof(u16)*2 + size; + + //adjust buffer "pointers" + networkSendBufferStartPos += currentSize; + if(networkSendBufferStartPos==networkSendBufferEndPos) { + networkSendBufferStartPos = 0; + networkSendBufferEndPos = 0; + networkSendBufferWrapPos = 0; + } + + //wrap + if(networkSendBufferStartPos==networkSendBufferWrapPos) { + networkSendBufferStartPos = 0; + networkSendBufferWrapPos = networkSendBufferEndPos; + } + } else { + break; + } + } + LightLock_Unlock(&sendBufferLock); +} + +bool sendAck(u16 target, u16 ack) { + Result ret = udsSendTo(target, NETWORK_CHANNEL, UDS_SENDFLAG_Default, &ack, sizeof(u16)); + if(UDS_CHECK_SENDTO_FATALERROR(ret)) { + //TODO: what do? + return false; + } else if(R_FAILED(ret)) { + //TODO: what do? + return false; + } else { + return true; + } +} + +void networkInit() { + Result ret = udsInit(0x3000, NULL); + if(R_FAILED(ret)) { + udsRunning = false; + } else { + udsRunning = true; + + scannedNetworksCount = 0; + scannedNetworks = NULL; + isConnected = false; + isServer = false; + networkConnectedMask = 0; + + networkWriteBuffer = malloc(NETWORK_MAXDATASIZE); + if(networkWriteBuffer==NULL) { + networkExit(); + return; + } + + networkBufferSize = 0x4000; + networkBuffer = malloc(networkBufferSize); + if(networkBuffer==NULL) { + networkExit(); + return; + } + + networkSendBufferStartPos = 0; + networkSendBufferEndPos = 0; + networkSendBufferWrapPos = 0; + networkSendBuffer = malloc(NETWORK_SENDBUFFERSIZE); + if(networkSendBuffer==NULL) { + networkExit(); + return; + } + + networkSeqSendNext = 1; + for(int i=0; i=scannedNetworksCount) return false; + + Result ret = udsGetNodeInfoUsername(&(scannedNetworks[pos].nodes[0]), name); + if(R_FAILED(ret)) { + //TODO: what do? + return false; + } + return true; + } + return false; +} + +bool networkConnect(int pos) { + if(udsRunning && !isConnected) { + if(pos<0 || pos>=scannedNetworksCount) return false; + + Result ret = udsConnectNetwork(&scannedNetworks[pos].network, NETWORK_PASSPHRASE, strlen(NETWORK_PASSPHRASE)+1, &networkBindCtx, UDS_BROADCAST_NETWORKNODEID, UDSCONTYPE_Client, NETWORK_CHANNEL, NETWORK_RECVBUFSIZE); + if(R_FAILED(ret)) { + return false; + } else { + if(udsWaitConnectionStatusEvent(false, false)) {} + udsGetConnectionStatus(&networkStatus); + isConnected = true; + isServer = false; + networkConnectedMask = 0; + return true; + } + } + return false; +} + +void networkDisconnect() { + //For clients this just means disconnect, for the server it means destroy the network + if(udsRunning && isConnected) { + isConnected = false; + + LightLock_Lock(&sendBufferLock); + //reset send buffer + networkSendBufferStartPos = 0; + networkSendBufferEndPos = 0; + networkSendBufferWrapPos = 0; + + //reset ack status + networkSeqSendNext = 1; + for(int i=0; i20*networkthreadsleep) (wait unti no more stuff gets send) + + //TODO + if(isServer) { + //TODO: Clients need to cleanup too, how can I tell they got disconnected + udsDestroyNetwork(); + } else { + udsDisconnectNetwork(); + } + udsUnbind(&networkBindCtx); + + isServer = false; + } +} + + +void networkStart() { + //TODO: This sends the node_bitmask from server to everyone else, because it is uncorrect on some clients? + if(udsRunning && isConnected && isServer) { + void *buffer = networkWriteBuffer; + + *((u16*) buffer) = networkStatus.node_bitmask; + networkConnectedMask = networkStatus.node_bitmask; + + networkSend(networkWriteBuffer, sizeof(u16)); + networkSendWaitFlush(); + } +} + + +bool networkConnected() { + return isConnected; +} + +int networkGetNodeCount() { + if(udsRunning && isConnected) { + return networkStatus.total_nodes; + } else { + return 0; + } +} + +u16 networkGetLocalNodeID() { + if(udsRunning && isConnected) { + return networkStatus.cur_NetworkNodeID; + } else { + return 0; + } +} + +bool networkIsNodeConnected(u16 id) { + if(udsRunning && isConnected) { + return networkStatus.node_bitmask & (1 << (id-1)); + } else { + return false; + } +} + +bool networkGetNodeName(u16 id, char *name) { + if(udsRunning && isConnected && networkIsNodeConnected(id)) { + udsNodeInfo nodeInfo; + udsGetNodeInformation(id, &nodeInfo); + + Result ret = udsGetNodeInfoUsername(&nodeInfo, name); + if(R_FAILED(ret)) { + //TODO: what do? + return false; + } + return true; + } + return false; +} + +u16 networkGetExpectedSeqFrom(u16 id) { + u16 nextID = networkSeqRecvLast[id]; + nextID += 1; + if(nextID==0) { + nextID = 1; + } + return nextID; +} + +bool networkSeqIsLowerThan(u16 firstID, u16 secondID) { + if (secondID < 100) { + return (firstID < secondID) || (firstID > 65536-100); + } else if (secondID > 65536-100) { + return (firstID < secondID) && (firstID > 100); + } else { + return (firstID < secondID); + } +} + +int fitInSendBuffer(size_t size) { + //add "header" length + size += sizeof(u16)*2; + + //we have no wrap currently + if(networkSendBufferStartPos<=networkSendBufferEndPos) { + //and can fit without wrap + if(networkSendBufferEndPos+sizesize) { + networkSendBufferEndPos += size; + + return networkSendBufferEndPos-size; + } + } + + return -1; +} + +void networkSend(void *packet, size_t size) { + //search for fit in buffer (and BLOCK until free space is found) + LightLock_Lock(&sendBufferLock); + int pos = fitInSendBuffer(size); + while(pos==-1) { + LightLock_Unlock(&sendBufferLock); + svcSleepThread(4500 * 1000); //TODO: Set meaningfull value + LightLock_Lock(&sendBufferLock); + + pos = fitInSendBuffer(size); + } + + //fit found -> space is allready "reserved" -> write packet to buffer + void *writePointer = networkSendBuffer + pos; + + //write seq number + *((u16*) writePointer) = networkSeqSendNext; + networkSeqSendNext++; + if(networkSeqSendNext==0) { + networkSeqSendNext = 1; + } + writePointer += sizeof(u16); + + //write size + *((u16*) writePointer) = (u16) size; + writePointer += sizeof(u16); + + //write data + memcpy(writePointer, packet, size); + writePointer += size; + + LightLock_Unlock(&sendBufferLock); +} + +void networkSendWaitFlush() { + while(networkSendBufferStartPos!=networkSendBufferEndPos) { + svcSleepThread(4500 * 1000); + } +} diff --git a/source/Network.h b/source/Network.h new file mode 100755 index 0000000..92c1e32 --- /dev/null +++ b/source/Network.h @@ -0,0 +1,47 @@ +#pragma once + +#include <3ds.h> + +#define NETWORK_WLANCOMMID 0x11441850 +#define NETWORK_PASSPHRASE "minicraft3dsLP" +#define NETWORK_CHANNEL 1 + +#define NETWORK_RECVBUFSIZE UDS_DEFAULT_RECVBUFSIZE + +#define NETWORK_MAXDATASIZE 1024 +#define NETWORK_SENDBUFFERSIZE ((NETWORK_MAXDATASIZE+256)*10) + +#define NETWORK_STACKSIZE (8*1024) + +#define NETWORK_MAXPLAYERS 8 + +void *networkWriteBuffer; + +void networkInit(); +void networkExit(); + +bool networkAvailable(); + +bool networkHost(); +void networkHostStopConnections(); +void networkScan(); +int networkGetScanCount(); +bool networkGetScanName(char *name, int pos); +bool networkConnect(int pos); +void networkDisconnect(); + +void networkStart(); + +void networkUpdateStatus(); +bool networkConnected(); + +int networkGetNodeCount(); +u16 networkGetLocalNodeID(); +bool networkIsNodeConnected(u16 id); +bool networkGetNodeName(u16 id, char *name); + +u16 networkGetExpectedSeqFrom(u16 id); +bool networkSeqIsLowerThan(u16 firstID, u16 secondID); + +void networkSend(void *packet, size_t size); +void networkSendWaitFlush(); diff --git a/source/PacketHandler.c b/source/PacketHandler.c new file mode 100755 index 0000000..5322a31 --- /dev/null +++ b/source/PacketHandler.c @@ -0,0 +1,357 @@ +#include "PacketHandler.h" +#include +#include "Synchronizer.h" + +FILE *recvFile; +size_t recvFileSize; + +void * writeBool(void *buffer, size_t *size, bool value) { + *((bool*) buffer) = value; + *(size) += sizeof(bool); + return buffer + sizeof(bool); +} + +void * writeU8(void *buffer, size_t *size, u8 value) { + *((u8*) buffer) = value; + *(size) += sizeof(u8); + return buffer + sizeof(u8); +} + +void * writeU16(void *buffer, size_t *size, u16 value) { + *((u16*) buffer) = value; + *(size) += sizeof(u16); + return buffer + sizeof(u16); +} + +void * writeU32(void *buffer, size_t *size, u32 value) { + *((u32*) buffer) = value; + *(size) += sizeof(u32); + return buffer + sizeof(u32); +} + +void * writeSizeT(void *buffer, size_t *size, size_t value) { + *((size_t*) buffer) = value; + *(size) += sizeof(size_t); + return buffer + sizeof(size_t); +} + +void * readBool(void *buffer, size_t *size, bool *value) { + *value = *((bool*) buffer); + *(size) -= sizeof(bool); + return buffer + sizeof(bool); +} + +void * readU8(void *buffer, size_t *size, u8 *value) { + *value = *((u8*) buffer); + *(size) -= sizeof(u8); + return buffer + sizeof(u8); +} + +void * readU16(void *buffer, size_t *size, u16 *value) { + *value = *((u16*) buffer); + *(size) -= sizeof(u16); + return buffer + sizeof(u16); +} + +void * readU32(void *buffer, size_t *size, u32 *value) { + *value = *((u32*) buffer); + *(size) -= sizeof(u32); + return buffer + sizeof(u32); +} + +void * readSizeT(void *buffer, size_t *size, size_t *value) { + *value = *((size_t*) buffer); + *(size) -= sizeof(size_t); + return buffer + sizeof(size_t); +} + +void processPacket(void *packet, size_t size) { + //Differenciate the packets and process them + switch(packetGetID(packet)) { + case PACKET_START: { + void *buffer = packetGetDataStart(packet); + size = packetGetDataSize(size); + + //find player index based on network node id + //and set player uuid in synchronizer + u32 seed; + u32 playerCount = 1; + int playerIndex = 0; + + buffer = readU32(buffer, &size, &seed); + buffer = readU32(buffer, &size, &playerCount); + for(int i=0; ik_touch.px); + buffer = writeU16(buffer, &size, inputs->k_touch.py); + + buffer = writeBool(buffer, &size, inputs->k_up.down); buffer = writeBool(buffer, &size, inputs->k_up.clicked); + buffer = writeBool(buffer, &size, inputs->k_down.down); buffer = writeBool(buffer, &size, inputs->k_down.clicked); + buffer = writeBool(buffer, &size, inputs->k_left.down); buffer = writeBool(buffer, &size, inputs->k_left.clicked); + buffer = writeBool(buffer, &size, inputs->k_right.down); buffer = writeBool(buffer, &size, inputs->k_right.clicked); + buffer = writeBool(buffer, &size, inputs->k_attack.down); buffer = writeBool(buffer, &size, inputs->k_attack.clicked); + buffer = writeBool(buffer, &size, inputs->k_pickup.down); buffer = writeBool(buffer, &size, inputs->k_pickup.clicked); + buffer = writeBool(buffer, &size, inputs->k_use.down); buffer = writeBool(buffer, &size, inputs->k_use.clicked); + buffer = writeBool(buffer, &size, inputs->k_menu.down); buffer = writeBool(buffer, &size, inputs->k_menu.clicked); + buffer = writeBool(buffer, &size, inputs->k_pause.down); buffer = writeBool(buffer, &size, inputs->k_pause.clicked); + buffer = writeBool(buffer, &size, inputs->k_accept.down); buffer = writeBool(buffer, &size, inputs->k_accept.clicked); + buffer = writeBool(buffer, &size, inputs->k_decline.down); buffer = writeBool(buffer, &size, inputs->k_decline.clicked); + buffer = writeBool(buffer, &size, inputs->k_delete.down); buffer = writeBool(buffer, &size, inputs->k_delete.clicked); + buffer = writeBool(buffer, &size, inputs->k_menuNext.down); buffer = writeBool(buffer, &size, inputs->k_menuNext.clicked); + buffer = writeBool(buffer, &size, inputs->k_menuPrev.down); buffer = writeBool(buffer, &size, inputs->k_menuPrev.clicked); + + return size; +} + +bool readInputPacketData(void *buffer, size_t size, Inputs *inputs) { + buffer = readU16(buffer, &size, &(inputs->k_touch.px)); + if(size<=0) return false; + buffer = readU16(buffer, &size, &(inputs->k_touch.py)); + if(size<=0) return false; + + buffer = readBool(buffer, &size, &(inputs->k_up.down)); buffer = readBool(buffer, &size, &(inputs->k_up.clicked)); + if(size<=0) return false; + buffer = readBool(buffer, &size, &(inputs->k_down.down)); buffer = readBool(buffer, &size, &(inputs->k_down.clicked)); + if(size<=0) return false; + buffer = readBool(buffer, &size, &(inputs->k_left.down)); buffer = readBool(buffer, &size, &(inputs->k_left.clicked)); + if(size<=0) return false; + buffer = readBool(buffer, &size, &(inputs->k_right.down)); buffer = readBool(buffer, &size, &(inputs->k_right.clicked)); + if(size<=0) return false; + buffer = readBool(buffer, &size, &(inputs->k_attack.down)); buffer = readBool(buffer, &size, &(inputs->k_attack.clicked)); + if(size<=0) return false; + buffer = readBool(buffer, &size, &(inputs->k_pickup.down)); buffer = readBool(buffer, &size, &(inputs->k_pickup.clicked)); + if(size<=0) return false; + buffer = readBool(buffer, &size, &(inputs->k_use.down)); buffer = readBool(buffer, &size, &(inputs->k_use.clicked)); + if(size<=0) return false; + buffer = readBool(buffer, &size, &(inputs->k_menu.down)); buffer = readBool(buffer, &size, &(inputs->k_menu.clicked)); + if(size<=0) return false; + buffer = readBool(buffer, &size, &(inputs->k_pause.down)); buffer = readBool(buffer, &size, &(inputs->k_pause.clicked)); + if(size<=0) return false; + buffer = readBool(buffer, &size, &(inputs->k_accept.down)); buffer = readBool(buffer, &size, &(inputs->k_accept.clicked)); + if(size<=0) return false; + buffer = readBool(buffer, &size, &(inputs->k_decline.down)); buffer = readBool(buffer, &size, &(inputs->k_decline.clicked)); + if(size<=0) return false; + buffer = readBool(buffer, &size, &(inputs->k_delete.down)); buffer = readBool(buffer, &size, &(inputs->k_delete.clicked)); + if(size<=0) return false; + buffer = readBool(buffer, &size, &(inputs->k_menuNext.down)); buffer = readBool(buffer, &size, &(inputs->k_menuNext.clicked)); + if(size<=0) return false; + buffer = readBool(buffer, &size, &(inputs->k_menuPrev.down)); buffer = readBool(buffer, &size, &(inputs->k_menuPrev.clicked)); + + return size==0; +} + +void sendFile(FILE *file, u8 fileType, u8 id) { + fseek(file, 0, SEEK_END); // seek to end of file + size_t fsize = ftell(file); // get current file pointer + fseek(file, 0, SEEK_SET); // seek back to beginning of file + + //send file header + void *buffer = networkWriteBuffer; + size_t size = 0; + buffer = writeU8(buffer, &size, PACKET_START_FILEHEADER); + buffer = writeU8(buffer, &size, 0); + buffer = writeU32(buffer, &size, 0); + buffer = writeU8(buffer, &size, fileType); + buffer = writeU8(buffer, &size, id); + buffer = writeSizeT(buffer, &size, fsize); + networkSend(networkWriteBuffer, size); + + //send file data + while(fsize>0) { + buffer = networkWriteBuffer; + size = 0; + buffer = writeU8(buffer, &size, PACKET_START_FILEDATA); + buffer = writeU8(buffer, &size, 0); + buffer = writeU32(buffer, &size, 0); + + //read file data + size_t towrite = NETWORK_MAXDATASIZE - size; + if(towrite>fsize) towrite = fsize; + + fread(buffer, 1, towrite, file); + + size += towrite; + fsize -= towrite; + + //send file data + networkSend(networkWriteBuffer, size); + } +} + +void sendIDPacket(u8 playerID, u32 uid) { + void *buffer = networkWriteBuffer; + size_t size = 0; + + buffer = writeU8(buffer, &size, PACKET_START_ID); + buffer = writeU8(buffer, &size, playerID); + buffer = writeU32(buffer, &size, 0); + + buffer = writeU32(buffer, &size, uid); + + networkSend(networkWriteBuffer, size); +} + +void sendStartReadyPacket(u8 playerID) { + void *buffer = networkWriteBuffer; + size_t size = 0; + + buffer = writeU8(buffer, &size, PACKET_START_READY); + buffer = writeU8(buffer, &size, playerID); + buffer = writeU32(buffer, &size, 0); + + networkSend(networkWriteBuffer, size); +} + +void sendStartSyncPacket() { + void *buffer = networkWriteBuffer; + size_t size = 0; + + buffer = writeU8(buffer, &size, PACKET_TURN_START); + buffer = writeU8(buffer, &size, 0); + buffer = writeU32(buffer, &size, 0); + + networkSend(networkWriteBuffer, size); +} diff --git a/source/PacketHandler.h b/source/PacketHandler.h new file mode 100755 index 0000000..0001e79 --- /dev/null +++ b/source/PacketHandler.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include "Network.h" +#include "Player.h" + +#define PACKET_START 0 +#define PACKET_START_FILEHEADER 1 +#define PACKET_START_FILEDATA 2 +#define PACKET_START_REQUEST_IDS 3 +#define PACKET_START_ID 4 +#define PACKET_START_READY 5 + +#define PACKET_TURN_START 10 +#define PACKET_TURN_INPUT 11 + +void processPacket(void *packet, size_t size); + +u8 packetGetID(void *packet); +u8 packetGetSender(void *packet); +u32 packetGetTurn(void *packet); +void * packetGetDataStart(void *packet); +size_t packetGetDataSize(size_t size); + +size_t writeStartPacket(void *buffer, u32 seed); +size_t writeStartRequestPacket(void *buffer); + +size_t writeInputPacket(void *buffer, Inputs *inputs, u8 playerID, u32 turnNumber); +bool readInputPacketData(void *buffer, size_t size, Inputs *inputs); + +void sendFile(FILE *file, u8 fileType, u8 id); +void sendIDPacket(u8 playerID, u32 uid); +void sendStartReadyPacket(u8 playerID); +void sendStartSyncPacket(); diff --git a/source/Player.c b/source/Player.c new file mode 100644 index 0000000..f757b42 --- /dev/null +++ b/source/Player.c @@ -0,0 +1,657 @@ +#include "Player.h" + +#include +#include "Globals.h" + +void potionEffect(int type) { + if(type == 1) { + UnderStrengthEffect = true; + } + if(type == 2) { + UnderSpeedEffect = true; + } + if (type == 3) { + regening = true; + } + if (type == 4) { + UnderSwimBreathEffect = true; + } +} + +void initPlayers() { + for(int i=0; ientity.type = ENTITY_PLAYER; + pd->entity.level = 1; + pd->entity.xr = 4; + pd->entity.yr = 3; + pd->entity.canSwim = true; + pd->entity.p.ax = 0; + pd->entity.p.ay = 0; + pd->entity.p.health = 10; + pd->entity.p.stamina = 10; + pd->entity.p.walkDist = 0; + pd->entity.p.attackTimer = 0; + pd->entity.p.dir = 0; + pd->entity.p.isDead = false; + pd->entity.p.hasWon = false; + + pd->entity.p.data = pd; +} + +void playerInitInventory(PlayerData *pd) { + //reset inventory + pd->inventory.lastSlot = 0; + pd->activeItem = &noItem; + + if(shouldRenderDebug && playerCount < 2) { + addItemToInventory(newItem(ITEM_POWGLOVE,0), &(pd->inventory)); + addItemToInventory(newItem(ITEM_WORKBENCH,0), &(pd->inventory)); + addItemToInventory(newItem(ITEM_GOLD_APPLE,1), &(pd->inventory)); + addItemToInventory(newItem(ITEM_STRENGTH_POTION,1), &(pd->inventory)); + addItemToInventory(newItem(ITEM_REGEN_POTION,1), &(pd->inventory)); + addItemToInventory(newItem(ITEM_SWIM_BREATH_POTION,1), &(pd->inventory)); + addItemToInventory(newItem(ITEM_SPEED_POTION,1), &(pd->inventory)); + addItemToInventory(newItem(ITEM_POTION_MAKER,0), &(pd->inventory)); + addItemToInventory(newItem(TOOL_SHOVEL,1), &(pd->inventory)); + addItemToInventory(newItem(TOOL_HOE,4), &(pd->inventory)); + addItemToInventory(newItem(TOOL_SWORD,4), &(pd->inventory)); + addItemToInventory(newItem(TOOL_PICKAXE,4), &(pd->inventory)); + addItemToInventory(newItem(TOOL_AXE,4), &(pd->inventory)); + + addItemToInventory(newItem(ITEM_ANVIL,0), &(pd->inventory)); + addItemToInventory(newItem(ITEM_CHEST,0), &(pd->inventory)); + addItemToInventory(newItem(ITEM_OVEN,0), &(pd->inventory)); + addItemToInventory(newItem(ITEM_FURNACE,0), &(pd->inventory)); + addItemToInventory(newItem(ITEM_LANTERN,0), &(pd->inventory)); + + addItemToInventory(newItem(TOOL_MAGIC_COMPASS,1), &(pd->inventory)); + + int i; + for (i = 7;i < 28;++i) addItemToInventory(newItem(i,50), &(pd->inventory)); + } +} + +void playerInitSprite(PlayerData *pd) { + pd->sprite.choosen = false; + + pd->sprite.legs = 0; + pd->sprite.body = 0; + pd->sprite.arms = 0; + pd->sprite.head = 0; + pd->sprite.eyes = 0; +} + +void playerInitMenus(PlayerData *pd) { + pd->ingameMenu = MENU_NONE; + pd->ingameMenuSelection = 0; + pd->ingameMenuInvSel = 0; + pd->ingameMenuInvSelOther = 0; + pd->ingameMenuAreYouSure = false; + pd->ingameMenuAreYouSureSave = false; + pd->ingameMenuTimer = 0; + + resetNPCMenuData(&(pd->npcMenuData)); + + pd->mapShouldRender = false; + pd->mapScrollX = 0; + pd->mapScrollY = 0; + pd->mapZoomLevel = 2; + sprintf(pd->mapText,"x%d", pd->mapZoomLevel); + + pd->touchLastX = -1; + pd->touchLastY = -1; + pd->touchIsDraggingMap = false; + pd->touchIsChangingSize = false; +} + +void initPlayer(PlayerData *pd) { + pd->isSpawned = false; + + playerInitMiniMapData(pd->minimapData); + playerInitEntity(pd); + playerInitInventory(pd); + + playerInitSprite(pd); + + initQuests(&(pd->questManager)); + resetQuests(&(pd->questManager)); + + playerInitMenus(pd); + + pd->score = 0; +} + +void freePlayer(PlayerData *pd) { + freeQuests(&(pd->questManager)); +} + +PlayerData* getNearestPlayer(s8 level, s16 x, s16 y) { + int nearest = -1; + unsigned int nearestDist = UINT_MAX; + + for(int i=0; iactiveItem->id){ + //shooting arrows + case TOOL_BOW: + item = getItemFromInventory(ITEM_ARROW_WOOD, &(pd->inventory)); + if(item!=NULL) { + aitemID = ITEM_ARROW_WOOD; + aitem = item; + } + item = getItemFromInventory(ITEM_ARROW_STONE, &(pd->inventory)); + if(item!=NULL) { + aitemID = ITEM_ARROW_STONE; + aitem = item; + } + item = getItemFromInventory(ITEM_ARROW_IRON, &(pd->inventory)); + if(item!=NULL) { + aitemID = ITEM_ARROW_IRON; + aitem = item; + } + item = getItemFromInventory(ITEM_ARROW_GOLD, &(pd->inventory)); + if(item!=NULL) { + aitemID = ITEM_ARROW_GOLD; + aitem = item; + } + item = getItemFromInventory(ITEM_ARROW_GEM, &(pd->inventory)); + if(item!=NULL) { + aitemID = ITEM_ARROW_GEM; + aitem = item; + } + + if(aitemID!=0) { + --aitem->countLevel; + if (isItemEmpty(aitem)) { + removeItemFromInventory(aitem->slotNum, &(pd->inventory)); + } + + switch(pd->entity.p.dir) { + case 0: + addEntityToList(newArrowEntity(&(pd->entity), aitemID, 0, 2, pd->entity.level), &eManager); + break; + case 1: + addEntityToList(newArrowEntity(&(pd->entity), aitemID, 0, -2, pd->entity.level), &eManager); + break; + case 2: + addEntityToList(newArrowEntity(&(pd->entity), aitemID, -2, 0, pd->entity.level), &eManager); + break; + case 3: + addEntityToList(newArrowEntity(&(pd->entity), aitemID, 2, 0, pd->entity.level), &eManager); + break; + } + return true; + } + break; + + // Health items + case ITEM_APPLE: + if(pd->entity.p.health < 10 && playerUseEnergy(pd, 2)){ + playerHeal(pd, 1); + --(pd->activeItem->countLevel); + } + break; + case ITEM_STRENGTH_POTION: + if(pd->entity.p.health < 20 && pd->entity.p.strengthTimer == 0){ + potionEffect(1); + --(pd->activeItem->countLevel); + } + return 0; + case ITEM_SPEED_POTION: + if(pd->entity.p.health < 20 && pd->entity.p.speedTimer == 0){ + potionEffect(2); + --(pd->activeItem->countLevel); + } + return 0; + case ITEM_REGEN_POTION: + if(pd->entity.p.health < 20 && pd->entity.p.regenTimer == 0){ + potionEffect(3); + --(pd->activeItem->countLevel); + } + return 0; + case ITEM_SWIM_BREATH_POTION: + if(pd->entity.p.health < 20 && pd->entity.p.swimBreathTimer == 0){ + potionEffect(4); + --(pd->activeItem->countLevel); + } + return 0; + case ITEM_GOLD_APPLE: + if(pd->entity.p.health < 10 && playerUseEnergy(pd, 1)){ + playerHeal(pd, 8); + playerUseEnergy(pd, -10); + --(pd->activeItem->countLevel); + } + return 0; + case ITEM_FLESH: + if(pd->entity.p.health < 10 && playerUseEnergy(pd, 4+(rand()%4))){ + playerHeal(pd, 1); + --(pd->activeItem->countLevel); + } + break; + case ITEM_BREAD: + if(pd->entity.p.health < 10 && playerUseEnergy(pd, 3)){ + playerHeal(pd, 2); + --(pd->activeItem->countLevel); + } + break; + case ITEM_PORK_RAW: + if(pd->entity.p.health < 10 && playerUseEnergy(pd, 4+(rand()%4))){ + playerHeal(pd, 1); + --(pd->activeItem->countLevel); + } + break; + case ITEM_PORK_COOKED: + if(pd->entity.p.health < 10 && playerUseEnergy(pd, 3)){ + playerHeal(pd, 3); + --(pd->activeItem->countLevel); + } + break; + case ITEM_BEEF_RAW: + if(pd->entity.p.health < 10 && playerUseEnergy(pd, 4+(rand()%4))){ + playerHeal(pd, 1); + --(pd->activeItem->countLevel); + } + break; + case ITEM_BEEF_COOKED: + if(pd->entity.p.health < 10 && playerUseEnergy(pd, 3)){ + playerHeal(pd, 4); + --(pd->activeItem->countLevel); + } + break; + //special item + case ITEM_WIZARD_SUMMON: + if(pd->entity.level==0) { + --(pd->activeItem->countLevel); + + airWizardHealthDisplay = 2000; + addEntityToList(newAirWizardEntity(630, 820, 0), &eManager); + } + break; + } + + if (isItemEmpty(pd->activeItem)) { + removeItemFromInventory(pd->activeItem->slotNum, &(pd->inventory)); + pd->activeItem = &noItem; + } + + return false; +} + +bool playerInteract(PlayerData *pd, int x0, int y0, int x1, int y1) { + Entity * es[eManager.lastSlot[pd->entity.level]]; + int eSize = getEntities(es, pd->entity.level, x0, y0, x1, y1); + int i; + for (i = 0; i < eSize; ++i) { + Entity * ent = es[i]; + if (ent != &(pd->entity)){ + if (ItemVsEntity(pd, pd->activeItem, ent, pd->entity.p.dir)) return true; + } + } + return false; +} + +void playerAttack(PlayerData *pd) { + bool done = false; + pd->entity.p.attackTimer = 5; + int yo = -2; + int range = 12; + + //directly using an item + if(playerUseItem(pd)) return; + + //interacting with entities + switch(pd->entity.p.dir){ + case 0: if(playerInteract(pd, pd->entity.x - 8, pd->entity.y + 4 + yo, pd->entity.x + 8, pd->entity.y + range + yo)) return; break; + case 1: if(playerInteract(pd, pd->entity.x - 8, pd->entity.y - range + yo, pd->entity.x + 8, pd->entity.y - 4 + yo)) return; break; + case 2: if(playerInteract(pd, pd->entity.x - range, pd->entity.y - 8 + yo, pd->entity.x - 4, pd->entity.y + 8 + yo)) return; break; + case 3: if(playerInteract(pd, pd->entity.x + 4, pd->entity.y - 8 + yo, pd->entity.x + range, pd->entity.y + 8 + yo)) return; break; + } + + int xt = pd->entity.x >> 4; + int yt = (pd->entity.y + yo) >> 4; + int r = 12; + if (pd->entity.p.dir == 0) yt = (pd->entity.y + r + yo) >> 4; + if (pd->entity.p.dir == 1) yt = (pd->entity.y - r + yo) >> 4; + if (pd->entity.p.dir == 2) xt = (pd->entity.x - r) >> 4; + if (pd->entity.p.dir == 3) xt = (pd->entity.x + r) >> 4; + + //interacting with tiles + if (xt >= 0 && yt >= 0 && xt < 128 && yt < 128) { + s8 itract = itemTileInteract(getTile(pd->entity.level, xt, yt), pd, pd->activeItem, pd->entity.level, xt, yt, pd->entity.x, pd->entity.y, pd->entity.p.dir); + if(itract > 0){ + if(itract==2) pd->entity.p.isCarrying = false; + done = true; + } + + if (pd->activeItem != &noItem && isItemEmpty(pd->activeItem)) { + removeItemFromInventory(pd->activeItem->slotNum, &(pd->inventory)); + pd->activeItem = &noItem; + } + } + + if(done) return; + + //breaking tiles + if (pd->activeItem == &noItem || pd->activeItem->id == TOOL_SWORD || pd->activeItem->id == TOOL_AXE) { + if (xt >= 0 && yt >= 0 && xt < 128 && 128) { + playerHurtTile(pd, getTile(pd->entity.level, xt, yt), pd->entity.level, xt, yt, (rand()%3) + 1, pd->entity.p.dir); + } + } +} + +bool playerUseArea(PlayerData *pd, int x0, int y0, int x1, int y1) { + Entity * entities[eManager.lastSlot[pd->entity.level]]; + int i; + int ae = getEntities(entities, pd->entity.level, x0, y0, x1, y1); + for(i = 0; i < ae; ++i){ + if(useEntity(pd, entities[i])) return true; + } + return false; +} + +bool playerUse(PlayerData *pd) { + int yo = -2; + if (pd->entity.p.dir == 0 && playerUseArea(pd, pd->entity.x - 8, pd->entity.y + 4 + yo, pd->entity.x + 8, pd->entity.y + 12 + yo)) return true; + if (pd->entity.p.dir == 1 && playerUseArea(pd, pd->entity.x - 8, pd->entity.y - 12 + yo, pd->entity.x + 8, pd->entity.y - 4 + yo)) return true; + if (pd->entity.p.dir == 3 && playerUseArea(pd, pd->entity.x + 4, pd->entity.y - 8 + yo, pd->entity.x + 12, pd->entity.y + 8 + yo)) return true; + if (pd->entity.p.dir == 2 && playerUseArea(pd, pd->entity.x - 12, pd->entity.y - 8 + yo, pd->entity.x - 4, pd->entity.y + 8 + yo)) return true; + return false; +} + +void tickPlayer(PlayerData *pd, bool inmenu) { + if (pd->entity.p.isDead) return; + + //invincibility time + if (pd->entity.hurtTime > 0) pd->entity.hurtTime--; + + //stamina recharging + bool swimming = isWater(pd->entity.level, pd->entity.x>>4, pd->entity.y>>4); + if (pd->entity.p.stamina <= 0 && pd->entity.p.staminaRechargeDelay == 0 && pd->entity.p.staminaRecharge == 0) { + pd->entity.p.staminaRechargeDelay = 40; + } + + if (pd->entity.p.staminaRechargeDelay > 0) { + --pd->entity.p.staminaRechargeDelay; + } + + if (pd->entity.p.staminaRechargeDelay == 0) { + ++pd->entity.p.staminaRecharge; + if (swimming) pd->entity.p.staminaRecharge = 0; + + while (pd->entity.p.staminaRecharge > 10) { + pd->entity.p.staminaRecharge -= 10; + if (pd->entity.p.stamina < 10) ++pd->entity.p.stamina; + } + } + + if(!inmenu) { + if(!pd->sprite.choosen) { + pd->ingameMenu = MENU_CHARACTER_CUSTOMIZE; + pd->ingameMenuSelection = 0; + return; + } + + //movement + pd->entity.p.ax = 0; + pd->entity.p.ay = 0; + + if (pd->inputs.k_left.down){ + if(UnderSpeedEffect) { + pd->entity.p.ax -= 2; + pd->entity.p.dir = 2; + ++pd->entity.p.walkDist; + ++pd->entity.p.walkDist; + } else { + pd->entity.p.ax -= 1; + pd->entity.p.dir = 2; + ++pd->entity.p.walkDist; + } + } + if (pd->inputs.k_right.down){ + if(UnderSpeedEffect) { + pd->entity.p.ax += 2; + pd->entity.p.dir = 3; + ++pd->entity.p.walkDist; + ++pd->entity.p.walkDist; + } else { + pd->entity.p.ax += 1; + pd->entity.p.dir = 3; + ++pd->entity.p.walkDist; + } + } + if (pd->inputs.k_up.down){ + if(UnderSpeedEffect) { + pd->entity.p.ay -= 2; + pd->entity.p.dir = 1; + ++pd->entity.p.walkDist; + ++pd->entity.p.walkDist; + } else { + pd->entity.p.ay -= 1; + pd->entity.p.dir = 1; + ++pd->entity.p.walkDist; + } + } + if (pd->inputs.k_down.down){ + if(UnderSpeedEffect) { + pd->entity.p.ay += 2; + pd->entity.p.dir = 0; + ++pd->entity.p.walkDist; + ++pd->entity.p.walkDist; + } else { + pd->entity.p.ay += 1; + pd->entity.p.dir = 0; + ++pd->entity.p.walkDist; + } + } + if (pd->entity.p.staminaRechargeDelay % 2 == 0) moveMob(&(pd->entity), pd->entity.p.ax, pd->entity.p.ay); + + //"pausing", TODO: since multiplayer this will no longer pause + if (pd->inputs.k_pause.clicked){ + pd->ingameMenuSelection = 0; + pd->ingameMenu = MENU_PAUSED; + } + + //attacking + if(pd->inputs.k_attack.clicked){ + if (pd->entity.p.stamina != 0) { + if(!shouldRenderDebug) pd->entity.p.stamina--; + pd->entity.p.staminaRecharge = 0; + + playerAttack(pd); + } + } + + //picking up furniture + if(pd->inputs.k_pickup.clicked){ + if (pd->entity.p.isCarrying){ + if (pd->entity.p.stamina != 0) { + if(!shouldRenderDebug) pd->entity.p.stamina--; + pd->entity.p.staminaRecharge = 0; + playerAttack(pd); + } + }else{ + int yo = -2; + int range = 12; + // see if entity near player + int x0, y0, x1, y1 = 0; + switch(pd->entity.p.dir){ + case 0: + x0 = pd->entity.x - 8; + y0 = pd->entity.y + 4 + yo; + x1 = pd->entity.x + 8; + y1 = pd->entity.y + range + yo; + break; + case 1: + x0 = pd->entity.x - 8; + y0 = pd->entity.y - range + yo; + x1 = pd->entity.x + 8; + y1 = pd->entity.y - 4 + yo; + break; + case 2: + x0 = pd->entity.x - range; + y0 = pd->entity.y - 8 + yo; + x1 = pd->entity.x - 4; + y1 = pd->entity.y + 8 + yo; + break; + case 3: + x0 = pd->entity.x + 4; + y0 = pd->entity.y - 8 + yo; + x1 = pd->entity.x + range; + y1 = pd->entity.y + 8 + yo; + break; + } + Entity * es[eManager.lastSlot[pd->entity.level]]; + int eSize = getEntities(es, pd->entity.level, x0, y0, x1, y1); + int i; + for (i = 0; i < eSize; ++i) { + Entity * ent = es[i]; + if (ent != &(pd->entity)){ + if(ent->type == ENTITY_FURNITURE){ + //Important: close all crafting windows using this furniture (only applies to chest) or else they will write invalid memory + for(int i=0; ientityFurniture.itemID,0); + if(ent->entityFurniture.itemID == ITEM_CHEST) nItem.chestPtr = ent->entityFurniture.inv; + pushItemToInventoryFront(nItem, &(pd->inventory)); + + removeEntityFromList(ent, ent->level, &eManager); + pd->activeItem = &(pd->inventory.items[0]); + pd->entity.p.isCarrying = true; + } + } + } + } + } + + // in head crafting + if(pd->inputs.k_use.clicked){ + pd->ingameMenuInvSel = 0; + if(!playerUse(pd)){ + pd->currentCraftTitle = "Crafting"; + openCraftingMenu(pd, &inHeadRecipes, "Crafting"); + } + } + + if (pd->inputs.k_menu.clicked){ + pd->ingameMenuInvSel = 0; + if(!playerUse(pd)) pd->ingameMenu = MENU_INVENTORY; + } + } + + //swimming stamina and drowning + if (swimming && pd->entity.p.swimTimer % 60 == 0 && UnderSwimBreathEffect != true) { + if (pd->entity.p.stamina > 0) { + if(!shouldRenderDebug) --pd->entity.p.stamina; + } else { + hurtEntity(&(pd->entity), 1, -1, 0xFFAF00FF, NULL); + } + } + + //Regen healing + if (regening && pd->entity.p.regenTimer % 75 == 0) { + playerHeal(pd, 1); + } + + if(isWater(pd->entity.level, pd->entity.x>>4, pd->entity.y>>4)) ++pd->entity.p.swimTimer; + if(regening) ++pd->entity.p.regenTimer; + if(UnderSpeedEffect) ++pd->entity.p.speedTimer; + if(UnderStrengthEffect) ++pd->entity.p.strengthTimer; + if(UnderSwimBreathEffect) ++pd->entity.p.swimBreathTimer; + if(pd->entity.p.attackTimer > 0) --pd->entity.p.attackTimer; + + //TODO - maybe move to own function + //Update Minimap + int xp; + int yp; + for(xp = (pd->entity.x>>4)-5; xp<(pd->entity.x>>4)+5; ++xp) { + for(yp = (pd->entity.y>>4)-5; yp<(pd->entity.y>>4)+5; ++yp) { + if(xp>=0 && xp<128 && yp>=0 && yp<128) { + if(!getMinimapVisible(pd, pd->entity.level, xp, yp)) { + setMinimapVisible(pd, pd->entity.level, xp, yp, true); + } + } + } + } +} + +void playerSetActiveItem(PlayerData *pd, Item *item) { + pd->activeItem = item; + if(pd->activeItem->id > 27 && pd->activeItem->id < 51) pd->entity.p.isCarrying = true; + else pd->entity.p.isCarrying = false; +} + +bool playerUseEnergy(PlayerData *pd, int amount) { + if(shouldRenderDebug) return true; + if(amount > pd->entity.p.stamina) return false; + pd->entity.p.stamina -= amount; + return true; +} + +void playerHeal(PlayerData *pd, int amount) { + pd->entity.p.health += amount; + if(pd->entity.p.health > 10) pd->entity.p.health = 10; + char healText[11]; + sprintf(healText, "%d", amount); + addEntityToList(newTextParticleEntity(healText,0xFF00FF00, pd->entity.x, pd->entity.y, pd->entity.level), &eManager); +} + +void playerSpawn(PlayerData *pd) { + while(true){ + int rx = rand()%128; + int ry = rand()%128; + if(getTile(pd->entity.level, rx, ry) == TILE_GRASS){ + pd->entity.x = (rx << 4) + 8; + pd->entity.y = (ry << 4) + 8; + pd->isSpawned = true; + break; + } + } +} diff --git a/source/Player.h b/source/Player.h new file mode 100644 index 0000000..b3d28bc --- /dev/null +++ b/source/Player.h @@ -0,0 +1,96 @@ +#pragma once + +#include "Input.h" +#include "Entity.h" +#include "QuestsData.h" +#include "Crafting.h" + +#define MAX_PLAYERS 8 +#define MAX_INPUT_BUFFER 3 + +#define PLAYER_SPRITE_HEAD_COUNT 4 +#define PLAYER_SPRITE_EYES_COUNT 5 +#define PLAYER_SPRITE_BODY_COUNT 6 +#define PLAYER_SPRITE_ARMS_COUNT 6 +#define PLAYER_SPRITE_LEGS_COUNT 5 + + +typedef struct _plrsp { + bool choosen; + + u8 legs; + u8 body; + u8 arms; + u8 head; + u8 eyes; +} PlayerSprite; + +typedef struct _plrd { + //for identification in save data and sync game start + u32 id; + bool idSet; + bool ready; + + //input/multiplayer/synchronization + Inputs inputs; + Inputs nextInputs[MAX_INPUT_BUFFER]; + bool nextTurnReady[MAX_INPUT_BUFFER]; + + // + bool isSpawned; + u8 minimapData[128*128]; + + int score; + QuestlineManager questManager; + + Entity entity; + Inventory inventory; + Item *activeItem; + + PlayerSprite sprite; + + //menu data + u8 ingameMenu; + s8 ingameMenuSelection; + s16 ingameMenuInvSel; + s16 ingameMenuInvSelOther; + bool ingameMenuAreYouSure; + bool ingameMenuAreYouSureSave; + s16 ingameMenuTimer; + NPC_MenuData npcMenuData; + + RecipeManager currentRecipes; + char *currentCraftTitle; + Entity *curChestEntity; + s8 curChestEntityR; + + bool mapShouldRender; + u8 mapZoomLevel; + s16 mapScrollX; + s16 mapScrollY; + char mapText[32]; + + s16 touchLastX; + s16 touchLastY; + bool touchIsDraggingMap; + bool touchIsChangingSize; +} PlayerData; + +PlayerData players[MAX_PLAYERS]; +int playerCount; +int playerLocalID; + +void initPlayers(); +void freePlayers(); + +void initPlayer(PlayerData *pd); +void freePlayer(PlayerData *pd); + +PlayerData* getNearestPlayer(s8 level, s16 x, s16 y); +PlayerData* getLocalPlayer(); + +void tickPlayer(PlayerData *pd, bool inmenu); +void playerSetActiveItem(PlayerData *pd, Item * item); +bool playerUseEnergy(PlayerData * pd, int amount); +void playerHeal(PlayerData *pd, int amount); +void playerSpawn(PlayerData *pd); diff --git a/source/Quests.c b/source/Quests.c new file mode 100755 index 0000000..9f10b43 --- /dev/null +++ b/source/Quests.c @@ -0,0 +1,437 @@ +#include "Quests.h" + +#include "Globals.h" +#include "Render.h" + +void initTrades() { + priestTrades.size = 5; + priestTrades.recipes = (Recipe*)malloc(sizeof(Recipe) * (priestTrades.size)); + priestTrades.recipes[0] = defineRecipe(ITEM_DUNGEON_KEY,1,1,ITEM_MAGIC_DUST,2); + priestTrades.recipes[1] = defineRecipe(ITEM_WIZARD_SUMMON,1,4,ITEM_CLOUD,100,ITEM_IRONINGOT,10,ITEM_BONE,10,ITEM_LEATHER,10); + priestTrades.recipes[2] = defineRecipe(TOOL_MAGIC_COMPASS,1,2,ITEM_IRONINGOT,10,ITEM_GLASS,5); + priestTrades.recipes[3] = defineRecipe(ITEM_COIN,1,1,ITEM_SLIME,5); + priestTrades.recipes[4] = defineRecipe(ITEM_COIN,1,1,ITEM_FLESH,5); + + farmerTrades.size = 7; + farmerTrades.recipes = (Recipe*)malloc(sizeof(Recipe) * (farmerTrades.size)); + farmerTrades.recipes[0] = defineRecipe(ITEM_WHEAT,5,1,ITEM_COIN,3); + farmerTrades.recipes[1] = defineRecipe(ITEM_BREAD,1,1,ITEM_COIN,3); + farmerTrades.recipes[2] = defineRecipe(ITEM_APPLE,2,1,ITEM_COIN,4); + farmerTrades.recipes[3] = defineRecipe(ITEM_ACORN,3,1,ITEM_COIN,1); + farmerTrades.recipes[4] = defineRecipe(ITEM_SEEDS,4,1,ITEM_COIN,2); + farmerTrades.recipes[5] = defineRecipe(ITEM_COIN,2,1,ITEM_SEEDS,5); + farmerTrades.recipes[6] = defineRecipe(ITEM_COIN,1,1,ITEM_ACORN,5); + + dwarfTrades.size = 2; + dwarfTrades.recipes = (Recipe*)malloc(sizeof(Recipe) * (dwarfTrades.size)); + dwarfTrades.recipes[0] = defineRecipe(ITEM_IRONINGOT,4,1,ITEM_GOLDINGOT,1); + dwarfTrades.recipes[1] = defineRecipe(ITEM_GOLDINGOT,2,1,ITEM_GEM,1); + //TODO: Trade Dragon Scales for something really nice +} + +void freeTrades() { + free(priestTrades.recipes); + free(farmerTrades.recipes); + free(dwarfTrades.recipes); +} + +void initQuests(QuestlineManager *questManager) { + if(questManager->questlines!=NULL) { + freeQuests(questManager); + } + + questManager->size = 2; + questManager->questlines = (Questline*)malloc(sizeof(Questline) * (questManager->size)); +} + +void resetQuests(QuestlineManager *questManager) { + int i; + for(i=0; isize; ++i) { + questManager->questlines[i].currentQuest = 0; + questManager->questlines[i].currentQuestDone = false; + } +} + +void freeQuests(QuestlineManager *questManager) { + free(questManager->questlines); + questManager->questlines = NULL; +} + +void resetNPCMenuData(NPC_MenuData *data) { + data->currentNPC = 0; + data->currentNPCMenu = 0; + data->currentNPCVal = 0; + + data->currentTalkSel = 0; + data->currentTalkDone = false; + data->currentTalkOptions = 0; + + data->currentTalkOption0 = ""; + data->currentTalkOption1 = ""; + data->currentTalkOption2 = ""; + data->currentTalk0 = ""; + data->currentTalk1 = ""; + data->currentTalk2 = ""; + data->currentTalk3 = ""; + data->currentTalk4 = ""; + data->currentTalk5 = ""; +} + +void openNPCMenu(PlayerData *pd, int npc) { + pd->ingameMenu = MENU_NPC; + + NPC_MenuData *data = &(pd->npcMenuData); + QuestlineManager *questManager = &(pd->questManager); + + data->currentNPC = npc; + data->currentNPCVal = 0; + data->currentNPCMenu = NPC_MENU_TALK; + + data->currentTalkSel = 0; + data->currentTalkDone = false; + data->currentTalkOptions = 1; + data->currentTalkOption0 = "Bye"; + data->currentTalkOption1 = ""; + data->currentTalkOption2 = ""; + data->currentTalk0 = ""; + data->currentTalk1 = ""; + data->currentTalk2 = ""; + data->currentTalk3 = ""; + data->currentTalk4 = ""; + data->currentTalk5 = ""; + + //TODO: Handle upon currentNPC as well as the fitting quest progress + switch(data->currentNPC) { + case NPC_GIRL: + data->currentTalkOptions = 1; + data->currentTalkOption0 = "..."; + + data->currentTalk0 = "Hello?"; + data->currentTalk1 = "I have a feeling of having"; + data->currentTalk2 = "forgotten something very"; + data->currentTalk3 = "important."; + data->currentTalk4 = "Hopefully I will remember"; + data->currentTalk5 = "it soon..."; + break; + case NPC_PRIEST: + data->currentTalkOptions = 3; + data->currentTalkOption1 = "Trade"; + data->currentTalkOption2 = "Why are you so few?"; + + data->currentTalk0 = "Welcome to our small village"; + data->currentTalk1 = "I am the leader of our group."; + data->currentTalk2 = "If you have anything usefull"; + data->currentTalk3 = "for us I might be able to"; + data->currentTalk4 = "provide something nice in"; + data->currentTalk5 = "exchange."; + break; + case NPC_FARMER: + data->currentTalkOptions = 2; + data->currentTalkOption0 = "Maybe some other time"; + data->currentTalkOption1 = "Trade"; + + data->currentTalk0 = "Hello friend!"; + data->currentTalk1 = "Nice seeing somebody else"; + data->currentTalk2 = "visit my little farm."; + data->currentTalk3 = "Interested in buying some"; + data->currentTalk4 = "fresh farm goods?"; + data->currentTalk5 = ""; + break; + case NPC_LIBRARIAN: + data->currentTalkOptions = 2; + data->currentTalkOption0 = "Nothing"; + data->currentTalkOption1 = "What are you doing here?"; + if(questManager->questlines[1].currentQuest==1) { + data->currentTalkOptions = 3; + data->currentTalkOption2 = "Dwarvish language"; + } + + data->currentTalk0 = "Oh hello?"; + data->currentTalk1 = "You must be quite brave"; + data->currentTalk2 = "or stupid to be walking"; + data->currentTalk3 = "around in this dungeon."; + data->currentTalk4 = ""; + data->currentTalk5 = "How can I help you?"; + break; + case NPC_DWARF: + if(questManager->questlines[1].currentQuest<=1) { + questManager->questlines[1].currentQuest = 1; + + data->currentTalkOptions = 1; + data->currentTalkOption0 = "?"; + + data->currentTalk0 = "Dwo neal bet reck da lo"; + data->currentTalk1 = "dhum don lir lugn at el"; + data->currentTalk2 = "nur tor erno ur yo trad"; + data->currentTalk3 = "thra so tir kho ukk tin"; + data->currentTalk4 = "hel dro ic"; + data->currentTalk5 = ""; + //TODO: set to 2 once translation book has been bought from librarian(can only be done once it is 1, so the dwarf has been found once) + } else if(questManager->questlines[1].currentQuest==2) { + data->currentTalkOptions = 2; + data->currentTalkOption0 = "Not really"; + data->currentTalkOption1 = "Trade"; + + data->currentTalk0 = "How are ya?"; + data->currentTalk1 = "Pretty unusal meeting a"; + data->currentTalk2 = "human down here."; + data->currentTalk3 = ""; + data->currentTalk4 = "have something valuable"; + data->currentTalk5 = "to trade?"; + } + break; + } +} + +void tickTalkMenu(PlayerData *pd, NPC_MenuData *data) { + if (pd->inputs.k_menu.clicked || pd->inputs.k_decline.clicked) pd->ingameMenu = MENU_NONE; + + if (pd->inputs.k_up.clicked){ ++data->currentTalkSel; if(data->currentTalkSel >= data->currentTalkOptions) data->currentTalkSel=0;} + if (pd->inputs.k_down.clicked){ --data->currentTalkSel; if(data->currentTalkSel < 0) data->currentTalkSel=data->currentTalkOptions-1;} + + if(pd->inputs.k_accept.clicked){ + data->currentTalkDone = true; + } +} + +void tickNPCMenu(PlayerData *pd) { + NPC_MenuData *data = &(pd->npcMenuData); + QuestlineManager *questManager = &(pd->questManager); + + //TODO: Handle upon currentNPC as well as the fitting quest progress + if(data->currentNPCMenu==NPC_MENU_TALK) tickTalkMenu(pd, data); + + + switch(data->currentNPC) { + case NPC_GIRL: + if(data->currentNPCMenu==NPC_MENU_TALK && data->currentTalkDone) { + if(data->currentNPCVal==0) pd->ingameMenu = MENU_NONE; + } + break; + case NPC_PRIEST: + if(data->currentNPCMenu==NPC_MENU_TALK && data->currentTalkDone) { + if(data->currentNPCVal==0) { + if(data->currentTalkSel==0) { + pd->ingameMenu = MENU_NONE; + } else if(data->currentTalkSel==1) { + openCraftingMenu(pd, &priestTrades, "Trading"); + } else if(data->currentTalkSel==2) { + data->currentNPCVal = 1; + + data->currentTalkSel = 0; + data->currentTalkDone = false; + data->currentTalkOptions = 1; + data->currentTalkOption0 = "..."; + + data->currentTalk0 = "For quite some time now this"; + data->currentTalk1 = "village has been tyrannized"; + data->currentTalk2 = "by a powerfull Air Wizard."; + data->currentTalk3 = "We are the only ones who"; + data->currentTalk4 = "still have not given up"; + data->currentTalk5 = "our old homes."; + } + } else if(data->currentNPCVal==1) { + if(data->currentTalkSel==0) { + data->currentNPCVal = 2; + + data->currentTalkSel = 0; + data->currentTalkDone = false; + data->currentTalkOptions = 1; + data->currentTalkOption0 = "..."; + + data->currentTalk0 = "Most of the time the wizard"; + data->currentTalk1 = "hides somewhere in the"; + data->currentTalk2 = "cloudes. They can only be"; + data->currentTalk3 = "reached by a stairwell"; + data->currentTalk4 = "protected by an almost"; + data->currentTalk5 = "undestroyable stone barrier."; + } + } else if(data->currentNPCVal==2) { + if(data->currentTalkSel==0) { + data->currentNPCVal = 3; + + data->currentTalkSel = 0; + data->currentTalkDone = false; + data->currentTalkOptions = 1; + data->currentTalkOption0 = "..."; + + data->currentTalk0 = "I am guessing you would "; + data->currentTalk1 = "need tools atleast as"; + data->currentTalk2 = "strong as diamonds to be"; + data->currentTalk3 = "able to destroy it."; + data->currentTalk4 = ""; + data->currentTalk5 = ""; + } + } else if(data->currentNPCVal==3) { + if(data->currentTalkSel==0) { + data->currentNPCVal = 4; + + data->currentTalkSel = 0; + data->currentTalkDone = false; + data->currentTalkOptions = 2; + data->currentTalkOption0 = "Let me do it!"; + data->currentTalkOption1 = "I am not sure"; + + data->currentTalk0 = "I am willing to give an"; + data->currentTalk1 = "ancient artifact passed"; + data->currentTalk2 = "down over generations to"; + data->currentTalk3 = "anybody who manages to"; + data->currentTalk4 = "chase the wizard away and"; + data->currentTalk5 = "come back with proof."; + } + } else if(data->currentNPCVal==4) { + pd->ingameMenu = MENU_NONE; + } + } + break; + case NPC_FARMER: + if(data->currentNPCMenu==NPC_MENU_TALK && data->currentTalkDone) { + if(data->currentNPCVal==0) { + if(data->currentTalkSel==0) { + pd->ingameMenu = MENU_NONE; + } else if(data->currentTalkSel==1) { + openCraftingMenu(pd, &farmerTrades, "Trading"); + } + } + } + break; + case NPC_LIBRARIAN: + if(data->currentNPCMenu==NPC_MENU_TALK && data->currentTalkDone) { + if(data->currentNPCVal==0) { + if(data->currentTalkSel==0) { + pd->ingameMenu = MENU_NONE; + } else if(data->currentTalkSel==1) { + data->currentNPCVal = 2; + + data->currentTalkSel = 0; + data->currentTalkDone = false; + data->currentTalkOptions = 1; + data->currentTalkOption0 = "Ok"; + + data->currentTalk0 = "The books in this dungeon"; + data->currentTalk1 = "house secrets that cannot be"; + data->currentTalk2 = "found anywhere else in the"; + data->currentTalk3 = "world. So I came to study"; + data->currentTalk4 = "them. Most are written in"; + data->currentTalk5 = "an ancient language."; + } else if(data->currentTalkSel==2) { + data->currentNPCVal = 1; + + data->currentTalkSel = 0; + data->currentTalkDone = false; + data->currentTalkOptions = 2; + data->currentTalkOption0 = "I need to think about it"; + data->currentTalkOption1 = "Here they are"; + + data->currentTalk0 = "So you have met a dwarf but"; + data->currentTalk1 = "had a little communication"; + data->currentTalk2 = "problem? I do have a dwarvish"; + data->currentTalk3 = "translation book but I havent"; + data->currentTalk4 = "read it yet. For 10 Gold bars"; + data->currentTalk5 = "I will give it to you anyway."; + } + } else if(data->currentNPCVal==1) { + if(data->currentTalkSel==0) { + pd->ingameMenu = MENU_NONE; + } else if(data->currentTalkSel==1) { + data->currentNPCVal = 2; + + data->currentTalkSel = 0; + data->currentTalkDone = false; + data->currentTalkOptions = 1; + data->currentTalkOption0 = ""; + + if(countItemInv(ITEM_GOLDINGOT, 0, &(pd->inventory))>=10) { + //remove gold from player inventory + //TODO: Maybe I should make a generic substract items method sometime + Item* item = getItemFromInventory(ITEM_GOLDINGOT, &(pd->inventory)); + item->countLevel -= 10; + if(item->countLevel < 1) removeItemFromInventory(item->slotNum, &(pd->inventory)); + + questManager->questlines[1].currentQuest = 2; + + data->currentTalk0 = "Thank you these will be"; + data->currentTalk1 = "really helpfull."; + data->currentTalk2 = "Here take this book with"; + data->currentTalk3 = "it you should be able to"; + data->currentTalk4 = "easily understand anything"; + data->currentTalk5 = "a dwarf can say."; + + data->currentTalkOption0 = "Thanks"; + } else { + data->currentTalk0 = "You do not seem to have"; + data->currentTalk1 = "enough Gold Bars with you."; + data->currentTalk2 = ""; + data->currentTalk3 = "Ask again when you have"; + data->currentTalk4 = "collected the 10 Bars."; + data->currentTalk5 = ""; + + data->currentTalkOption0 = "Ok"; + } + } + } else if(data->currentNPCVal==2) { + if(data->currentTalkSel==0) { + pd->ingameMenu = MENU_NONE; + } + } + } + break; + case NPC_DWARF: + if(questManager->questlines[1].currentQuest<=1) { + if(data->currentNPCMenu==NPC_MENU_TALK && data->currentTalkDone) { + if(data->currentNPCVal==0) pd->ingameMenu = MENU_NONE; + } + } else if(questManager->questlines[1].currentQuest==2) { + if(data->currentNPCMenu==NPC_MENU_TALK && data->currentTalkDone) { + if(data->currentTalkSel==0) { + pd->ingameMenu = MENU_NONE; + } else if(data->currentTalkSel==1) { + openCraftingMenu(pd, &dwarfTrades, "Trading"); + } + } + } + break; + } +} + +void renderTalkMenu(NPC_MenuData *data, char * name) { + renderFrame(1,1,24,14,0xFFFF1010); + drawTextColor(name,24+1,14+1,0xFF000000); + drawTextColor(name,24,14,0xFF6FE2E2); + + drawText(data->currentTalk0, 32, 32); + drawText(data->currentTalk1, 32, 48); + drawText(data->currentTalk2, 32, 64); + drawText(data->currentTalk3, 32, 80); + drawText(data->currentTalk4, 32, 96); + drawText(data->currentTalk5, 32, 112); + + if(data->currentTalkOptions>=3) drawText(data->currentTalkOption2, 64, 147); + if(data->currentTalkOptions>=2) drawText(data->currentTalkOption1, 64, 171); + if(data->currentTalkOptions>=1) drawText(data->currentTalkOption0, 64, 195); + + if(data->currentTalkOptions>=3 && data->currentTalkSel==2) drawText(">", 48, 147); + if(data->currentTalkOptions>=2 && data->currentTalkSel==1) drawText(">", 48, 171); + if(data->currentTalkOptions>=1 && data->currentTalkSel==0) drawText(">", 48, 195); +} + +void renderNPCMenu(NPC_MenuData *data) { + //TODO: Handle upon currentNPC as well as the fitting quest progress + switch(data->currentNPC) { + case NPC_GIRL: + if(data->currentNPCMenu==NPC_MENU_TALK) renderTalkMenu(data, "Maria"); + break; + case NPC_PRIEST: + if(data->currentNPCMenu==NPC_MENU_TALK) renderTalkMenu(data, "Priest Brom"); + break; + case NPC_FARMER: + if(data->currentNPCMenu==NPC_MENU_TALK) renderTalkMenu(data, "Farmer Garrow"); + break; + case NPC_LIBRARIAN: + if(data->currentNPCMenu==NPC_MENU_TALK) renderTalkMenu(data, "Librarian Ajihad"); + break; + case NPC_DWARF: + if(data->currentNPCMenu==NPC_MENU_TALK) renderTalkMenu(data, "Dwarf Orik"); + break; + } +} diff --git a/source/Quests.h b/source/Quests.h new file mode 100755 index 0000000..05a581e --- /dev/null +++ b/source/Quests.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include "QuestsData.h" +#include "Player.h" +#include "Crafting.h" + +#define NPC_MENU_TALK 0 + +RecipeManager priestTrades; +RecipeManager farmerTrades; +RecipeManager dwarfTrades; + +void initTrades(); +void freeTrades(); + +void initQuests(QuestlineManager *questManager); +void resetQuests(QuestlineManager *questManager); +void freeQuests(QuestlineManager *questManager); + +void resetNPCMenuData(NPC_MenuData *data); +void openNPCMenu(PlayerData *pd, int npc); +void tickNPCMenu(PlayerData *pd); +void renderNPCMenu(NPC_MenuData *data); diff --git a/source/QuestsData.h b/source/QuestsData.h new file mode 100644 index 0000000..91a770e --- /dev/null +++ b/source/QuestsData.h @@ -0,0 +1,35 @@ +#pragma once + +#include <3ds.h> + +typedef struct _questline { + int currentQuest; + bool currentQuestDone; +} Questline; + +typedef struct _questlineManager { + int size; + Questline * questlines; +} QuestlineManager; + +typedef struct _npcMenuData { + u8 currentNPC; + + int currentNPCMenu; + int currentNPCVal; + + int currentTalkSel; + bool currentTalkDone; + int currentTalkOptions; + char * currentTalkOption0; + char * currentTalkOption1; + char * currentTalkOption2; + char * currentTalk0; + char * currentTalk1; + char * currentTalk2; + char * currentTalk3; + char * currentTalk4; + char * currentTalk5; +} NPC_MenuData; + +//TODO: Actually move the data here diff --git a/source/Render.c b/source/Render.c old mode 100644 new mode 100755 index 6f200db..3496c0a --- a/source/Render.c +++ b/source/Render.c @@ -1,5 +1,7 @@ #include "Render.h" +extern u32 syncTickCount; + void render(s32 xp, s32 yp, u32 xTile, u32 yTile, u8 bits) { xp -= offsetX; yp -= offsetY; @@ -142,10 +144,34 @@ void render16s(s32 xp, s32 yp, u32 tile, u8 bits, u32 color) { 16, 16, scaleX, scaleY, color); } +void render32(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 += 32; + } + if ((bits & 2) > 0) { + scaleY = -2; + yp += 32; + } + sf2d_draw_texture_part_scale(icons, xp << 1, yp << 1, xTile, yTile, 32, 32, + scaleX, scaleY); +} + +int playerScale = 2; +void renderp(s32 xp, s32 yp, u32 xTile, u32 yTile) { + xp -= offsetX; + yp -= offsetY; + int scaleX = playerScale, scaleY = playerScale; + sf2d_draw_texture_part_scale(playerSprites, xp << 1, yp << 1, xTile, yTile, 16, 16, + scaleX, scaleY); +} + void renderTitle(int x, int y) { - sf2d_draw_texture_part_scale(icons, (x - 26) << 1, y << 1, 0, 240, 104, 16, - 2.0, 2.0); // MINICRAFT - sf2d_draw_texture_part(icons, x + 48, y + 52, 104, 240, 152, 16); // 3DS HOMEBREW EDITION + sf2d_draw_texture_part_scale(icons, (x - 26) << 1, y << 1, 0, 240, 104, 16, 2.0, 2.0); // MINICRAFT + sf2d_draw_texture_part(icons, x + 48, y + 44, 104, 240, 152, 16); // 3DS HOMEBREW EDITION } void renderButtonIcon(u32 keyIcon, int x, int y, float scale) { @@ -258,61 +284,81 @@ void renderFrame(int x1, int y1, int x2, int y2, u32 bgColor) { void bakeLights() { playerLightBake = sf2d_create_texture(64, 64, TEXFMT_RGBA8, SF2D_PLACE_RAM); - lanternLightBake = sf2d_create_texture(128, 128, TEXFMT_RGBA8, - SF2D_PLACE_RAM); + lanternLightBake = sf2d_create_texture(128, 128, TEXFMT_RGBA8, SF2D_PLACE_RAM); + + glowwormLightBake = sf2d_create_texture(32, 32, TEXFMT_RGBA8, SF2D_PLACE_RAM); + glowwormBigLightBake = sf2d_create_texture(64, 64, TEXFMT_RGBA8, SF2D_PLACE_RAM); bakeLight(playerLightBake, 32, 32, 32); bakeLight(lanternLightBake, 64, 64, 64); + + bakeLight(glowwormLightBake, 8, 8, 8); + bakeLight(glowwormBigLightBake, 12, 12, 12); } void freeLightBakes() { sf2d_free_texture(playerLightBake); sf2d_free_texture(lanternLightBake); + + sf2d_free_texture(glowwormLightBake); + sf2d_free_texture(glowwormBigLightBake); } -void renderLightsToStencil() { - if (currentLevel > 1) { - GPU_SetDepthTestAndWriteMask(true, GPU_NEVER, 0); - GPU_SetStencilTest(true, GPU_NEVER, 1, 0xFF, 0xFF); - GPU_SetStencilOp(GPU_STENCIL_REPLACE, GPU_STENCIL_KEEP, - GPU_STENCIL_KEEP); - GPU_SetAlphaTest(true, GPU_GREATER, 0); +void renderLightsToStencil(PlayerData *pd, bool force, bool invert, bool rplayer) { + if (force || (pd->entity.level > 1 && pd->entity.level != 5)) { + C3D_DepthTest(true, GPU_NEVER, 0); + C3D_StencilTest(true, GPU_NEVER, 1, 0xFF, 0xFF); + C3D_StencilOp(GPU_STENCIL_REPLACE, GPU_STENCIL_KEEP, GPU_STENCIL_KEEP); + C3D_AlphaTest(true, GPU_GREATER, 0); + + if(pd->activeItem->id == ITEM_LANTERN) renderLight(pd->entity.x, pd->entity.y, lanternLightBake); + else if(rplayer) renderLight(pd->entity.x, pd->entity.y, playerLightBake); - if(player.p.activeItem->id == ITEM_LANTERN) renderLight(player.x, player.y, lanternLightBake); - else renderLight(player.x, player.y, playerLightBake); - int i; - for (i = 0; i < eManager.lastSlot[currentLevel]; ++i) { - Entity e = eManager.entities[currentLevel][i]; - if (e.type != ENTITY_FURNITURE)continue; - if (e.entityFurniture.itemID == ITEM_LANTERN && e.x > player.x - 160 - && e.y > player.y - 125 && e.x < player.x + 160 && e.y < player.y + 125) - renderLight(e.x, e.y, lanternLightBake); + for (i = 0; i < eManager.lastSlot[pd->entity.level]; ++i) { + Entity e = eManager.entities[pd->entity.level][i]; + if (e.type == ENTITY_FURNITURE) { + if (e.entityFurniture.itemID == ITEM_LANTERN && e.x > pd->entity.x - 160 && e.y > pd->entity.y - 125 && e.x < pd->entity.x + 160 && e.y < pd->entity.y + 125) + renderLight(e.x, e.y, lanternLightBake); + } else if(e.type == ENTITY_GLOWWORM && e.x > pd->entity.x - 160 && e.y > pd->entity.y - 125 && e.x < pd->entity.x + 160 && e.y < pd->entity.y + 125) { //TODO could be made smaller becuase of smaller light radius + if(rand()%10==0) continue; + else if(rand()%100==0) renderLight(e.x+20, e.y-20, glowwormBigLightBake); + else renderLight(e.x+8, e.y-8, glowwormLightBake); + } } - + int xo = offsetX >> 4; int yo = offsetY >> 4; int x, y; //added offset to render lights from lava which is offscreen + //TODO: Even this is not performant enough for old 3DS, when there is a lot of lava on screen for (x = xo-2; x <= 13 + xo+2; ++x) { - for (y = yo-2; y <= 8 + yo+2; ++y) - if(getTile(x, y) == TILE_LAVA) renderLight(x << 4, y << 4, playerLightBake); + for (y = yo-2; y <= 8 + yo+2; ++y) { + if(getTile(pd->entity.level, x, y) == TILE_LAVA) { + //experimental "speedhack" + if(getTile(pd->entity.level, x+1,y)==TILE_LAVA && getTile(pd->entity.level, x-1,y)==TILE_LAVA && getTile(pd->entity.level, x,y+1)==TILE_LAVA && getTile(pd->entity.level, x,y-1)==TILE_LAVA) { + if((x+y)%2 == 0) continue; + } + renderLight((x << 4) + 8, (y << 4) + 8, playerLightBake); + } + } } - - - GPU_SetDepthTestAndWriteMask(true, GPU_GEQUAL, GPU_WRITE_ALL); - GPU_SetStencilTest(true, GPU_EQUAL, 1, 0xFF, 0x0); - GPU_SetAlphaTest(false, GPU_ALWAYS, 0x00); - GPU_SetStencilOp(GPU_STENCIL_KEEP, GPU_STENCIL_KEEP, - GPU_STENCIL_REPLACE); + + + C3D_DepthTest(true, GPU_GEQUAL, GPU_WRITE_ALL); + if(invert) { + C3D_StencilTest(true, GPU_EQUAL, 0, 0xFF, 0x0); + } else { + C3D_StencilTest(true, GPU_EQUAL, 1, 0xFF, 0x0); + } + C3D_AlphaTest(false, GPU_ALWAYS, 0x00); + C3D_StencilOp(GPU_STENCIL_KEEP, GPU_STENCIL_KEEP, GPU_STENCIL_REPLACE); } } void resetStencilStuff() { - if (currentLevel > 1) { - GPU_SetStencilTest(false, GPU_ALWAYS, 0x00, 0xFF, 0x00); - GPU_SetStencilOp(GPU_STENCIL_KEEP, GPU_STENCIL_KEEP, GPU_STENCIL_KEEP); - } + C3D_StencilTest(false, GPU_ALWAYS, 0x00, 0xFF, 0x00); + C3D_StencilOp(GPU_STENCIL_KEEP, GPU_STENCIL_KEEP, GPU_STENCIL_KEEP); } void renderLight(int x, int y, sf2d_texture* texture) { @@ -369,11 +415,17 @@ bool tur = false; bool tdl = false; bool tdr = false; -void renderDotsWithColor(int x, int y, u8 bits1, u8 bits2, u8 bits3, u8 bits4, u32 color) { - if(tu && tl) renderb(x, y, 0, 0, bits1, color); - if(tu && tr) renderb(x + 8, y, 8, 0, bits2, color); - if(td && tl) renderb(x, y + 8, 0, 8, bits3, color); - if(td && tr) renderb(x + 8, y + 8, 8, 8, bits4, color); +void renderDots(int x, int y, u8 bits1, u8 bits2, u8 bits3, u8 bits4, u32 xTile, u32 yTile) { + //another speedhack for o3DS + if(tu && tl && tr && td) { + render16(x, y, xTile, yTile, bits1); + return; + } + + if(tu && tl) render(x, y, xTile, yTile, bits1); + if(tu && tr) render(x + 8, y, xTile+8, yTile, bits2); + if(td && tl) render(x, y + 8, xTile, yTile+8, bits3); + if(td && tr) render(x + 8, y + 8, xTile+8, yTile+8, bits4); } void resetSurrTiles() { @@ -388,377 +440,578 @@ void resetSurrTiles() { tdr = false; } -void checkSurrTiles8(int xt, int yt, int id) { - if (getTile(xt, yt - 1) == id) +void checkSurrTiles8(u8 level, int xt, int yt, int id) { + if (getTile(level, xt, yt - 1) == id) tu = true; - if (getTile(xt - 1, yt) == id) + if (getTile(level, xt - 1, yt) == id) tl = true; - if (getTile(xt + 1, yt) == id) + if (getTile(level, xt + 1, yt) == id) tr = true; - if (getTile(xt, yt + 1) == id) + if (getTile(level, xt, yt + 1) == id) td = true; - if (getTile(xt - 1, yt - 1) == id) + if (getTile(level, xt - 1, yt - 1) == id) tul = true; - if (getTile(xt + 1, yt - 1) == id) + if (getTile(level, xt + 1, yt - 1) == id) tur = true; - if (getTile(xt - 1, yt + 1) == id) + if (getTile(level, xt - 1, yt + 1) == id) tdl = true; - if (getTile(xt + 1, yt + 1) == id) + if (getTile(level, xt + 1, yt + 1) == id) tdr = true; } -void checkSurrTiles4(int xt, int yt, int id) { - if (getTile(xt, yt - 1) == id) +void checkSurrTiles4(u8 level, int xt, int yt, int id) { + if (getTile(level, xt, yt - 1) == id) tu = true; - if (getTile(xt - 1, yt) == id) + if (getTile(level, xt - 1, yt) == id) tl = true; - if (getTile(xt + 1, yt) == id) + if (getTile(level, xt + 1, yt) == id) tr = true; - if (getTile(xt, yt + 1) == id) + if (getTile(level, xt, yt + 1) == id) td = true; } u8 tData = 0; -void renderTile(int i, int x, int y) { +void renderTile(int i, int d, u8 level, int x, int y) { int age = 0; switch (i) { case TILE_GRASS: - checkSurrTiles4(x >> 4, y >> 4, TILE_GRASS); - checkSurrTiles4(x >> 4, y >> 4, TILE_TREE); - checkSurrTiles4(x >> 4, y >> 4, TILE_FLOWER); - checkSurrTiles4(x >> 4, y >> 4, TILE_SAPLING_TREE); - - renderConnectedTile4(x, y, 112, 16, grassColor); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_GRASS); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_TREE); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_FLOWER); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_SAPLING_TREE); + + if(level==1 && worldData.season==3) renderConnectedTile4(x, y, 256, 112); + else if(level==1 && worldData.season==2) renderConnectedTile4(x, y, 256, 128); + else renderConnectedTile4(x, y, 256, 0); break; case TILE_TREE: - renderTile(TILE_GRASS, x, y); - - checkSurrTiles8(x >> 4, y >> 4, TILE_TREE); - - render(x, y, 0+((tu && tl && tul) ? 16 : 0), 16, 0); - render(x+8, y, 8+((tu && tr && tur) ? 16 : 0), 16, 0); - render(x, y+8, 0+((td && tl && tdl) ? 16 : 0), 24, 0); - render(x+8, y+8, 8+((td && tr && tdr) ? 16 : 0), 24, 0); - + renderTile(TILE_GRASS, 0, level, x, y); + + checkSurrTiles8(level, x >> 4, y >> 4, TILE_TREE); + + if(worldData.season==2) { + render(x, y, 352+((tu && tl && tul) ? 16 : 0), 96, 0); + render(x+8, y, 360+((tu && tr && tur) ? 16 : 0), 96, 0); + render(x, y+8, 352+((td && tl && tdl) ? 16 : 0), 104, 0); + render(x+8, y+8, 360+((td && tr && tdr) ? 16 : 0), 104, 0); + } else if(worldData.season==3) { + render(x, y, 352+((tu && tl && tul) ? 16 : 0), 112, 0); + render(x+8, y, 360+((tu && tr && tur) ? 16 : 0), 112, 0); + render(x, y+8, 352+((td && tl && tdl) ? 16 : 0), 120, 0); + render(x+8, y+8, 360+((td && tr && tdr) ? 16 : 0), 120, 0); + } else { + render(x, y, 256+((tu && tl && tul) ? 16 : 0), 48, 0); + render(x+8, y, 264+((tu && tr && tur) ? 16 : 0), 48, 0); + render(x, y+8, 256+((td && tl && tdl) ? 16 : 0), 56, 0); + render(x+8, y+8, 264+((td && tr && tdr) ? 16 : 0), 56, 0); + } + break; case TILE_ROCK: - checkSurrTiles8(x >> 4, y >> 4, TILE_ROCK); - renderConnectedTile8(x, y, 32, 16, rockColor[0]); + checkSurrTiles8(level, x >> 4, y >> 4, TILE_ROCK); + if(level>1) + renderConnectedTile8(x, y, 256, 96); + else + renderConnectedTile8(x, y, 336, 64); break; case TILE_HARDROCK: - checkSurrTiles8(x >> 4, y >> 4, TILE_HARDROCK); - renderConnectedTile8(x, y, 32, 16, rockColor[2]); + checkSurrTiles8(level, x >> 4, y >> 4, TILE_HARDROCK); + renderConnectedTile8(x, y, 416, 64); break; case TILE_DIRT: // render dots. - if (currentLevel > 1) - render16b(x, y, 0, 0, 0, 0xFF383838); + if (level > 1) + render16(x, y, 320, 80, 0); else - render16b(x, y, 0, 0, 0, 0xFF8F8FA8); + render16(x, y, 336, 80, 0); break; case TILE_SAND: - checkSurrTiles4(x >> 4, y >> 4, TILE_SAND); - checkSurrTiles4(x >> 4, y >> 4, TILE_CACTUS); - checkSurrTiles4(x >> 4, y >> 4, TILE_SAPLING_CACTUS); - - renderConnectedTile4(x, y, 112, 16, sandColor); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_SAND); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_CACTUS); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_SAPLING_CACTUS); + + if(level==1 && worldData.season==3) { + renderConnectedTile4(x, y, 256, 112); + } else { + renderConnectedTile4(x, y, 320, 0); + + if (d > 0) { + render16(x, y, 336, 48, 0); + } + } break; case TILE_WATER: - checkSurrTiles4(x >> 4, y >> 4, TILE_WATER); - checkSurrTiles4(x >> 4, y >> 4, TILE_HOLE); - - renderConnectedTile4(x, y, 176, 16, waterColor[0]); - - srand((tickCount + (x / 2 - y) * 4311) / 10); - renderDotsWithColor(x, y, rand() & 3, rand() & 3, rand() & 3, rand() & 3, waterColor[1]); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_WATER); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_HOLE); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_ICE); + + renderConnectedTile4(x, y, 384, 0); + + srand((syncTickCount + (x / 2 - y) * 4311) / 10); + renderDots(x, y, rand() & 3, rand() & 3, rand() & 3, rand() & 3, 288, 64); break; case TILE_LAVA: - checkSurrTiles4(x >> 4, y >> 4, TILE_LAVA); - checkSurrTiles4(x >> 4, y >> 4, TILE_HOLE); - - renderConnectedTile4(x, y, 176, 16, lavaColor[0]); - - srand((tickCount + (x / 2 - y) * 4311) / 10); - renderDotsWithColor(x, y, rand() & 3, rand() & 3, rand() & 3, rand() & 3, lavaColor[1]); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_LAVA); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_HOLE); + + renderConnectedTile4(x, y, 448, 0); + + srand((syncTickCount + (x / 2 - y) * 4311) / 10); + renderDots(x, y, rand() & 3, rand() & 3, rand() & 3, rand() & 3, 304, 64); break; case TILE_HOLE: - checkSurrTiles4(x >> 4, y >> 4, TILE_HOLE); - checkSurrTiles4(x >> 4, y >> 4, TILE_WATER); - checkSurrTiles4(x >> 4, y >> 4, TILE_LAVA); - - renderConnectedTile4(x, y, 176, 16, 0xFF383838); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_HOLE); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_WATER); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_LAVA); + + renderConnectedTile4(x, y, 256, 16); break; case TILE_CACTUS: - renderTile(TILE_SAND, x, y); - render16(x, y, 48, 0, 0); + renderTile(TILE_SAND, 0, level, x, y); + render16(x, y, 304, 48, 0); break; case TILE_FLOWER: - renderTile(TILE_GRASS, x, y); - render16(x, y, 64, 0, getData(x >> 4, y >> 4)); + renderTile(TILE_GRASS, 0, level, x, y); + if(level==1 && worldData.season==3) render16(x, y, 320, 112, d); + else render16(x, y, 320, 48, d); break; case TILE_STAIRS_DOWN: - if (currentLevel == 0) - renderTile(TILE_CLOUD, x, y); - render16(x, y, 96, 0, 0); + if (level == 0) + renderTile(TILE_CLOUD, 0, level, x, y); + render16(x, y, 256, 64, 0); break; case TILE_STAIRS_UP: - render16(x, y, 112, 0, 0); + render16(x, y, 272, 64, 0); break; case TILE_IRONORE: - render16b(x, y, 80, 0, 0, 0xFFC8C8DF); + render16(x, y, 464, 48, 0); break; case TILE_GOLDORE: - render16b(x, y, 80, 0, 0, 0xFFB9E8E5); + render16(x, y, 480, 48, 0); break; case TILE_GEMORE: - render16b(x, y, 80, 0, 0, 0xFFDE98DF); + render16(x, y, 496, 48, 0); break; case TILE_CLOUD: - checkSurrTiles4(x >> 4, y >> 4, TILE_CLOUD); - checkSurrTiles4(x >> 4, y >> 4, TILE_STAIRS_DOWN); - checkSurrTiles4(x >> 4, y >> 4, TILE_CLOUDCACTUS); - - renderConnectedTile4(x, y, 64, 32, 0xFFFFFFFF); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_CLOUD); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_STAIRS_DOWN); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_CLOUDCACTUS); + + renderConnectedTile4(x, y, 320, 16); break; case TILE_CLOUDCACTUS: - renderTile(TILE_CLOUD, x, y); - render16(x, y, 80, 0, 0); + renderTile(TILE_CLOUD, 0, level, x, y); + render16(x, y, 496, 64, 0); break; case TILE_SAPLING_TREE: - renderTile(TILE_GRASS, x, y); - render16(x, y, 32, 0, 0); + renderTile(TILE_GRASS, 0, level, x, y); + render16(x, y, 288, 48, 0); break; case TILE_SAPLING_CACTUS: - renderTile(TILE_SAND, x, y); - render16(x, y, 32, 0, 0); + renderTile(TILE_SAND, 0, level, x, y); + render16(x, y, 288, 48, 0); break; case TILE_FARM: - render16(x, y, 144, 0, 0); + render16(x, y, 352, 48, 0); break; case TILE_WHEAT: - age = getData(x >> 4, y >> 4) / 20; + age = d / 20; if (age > 5) age = 5; - render16(x, y, 160 + (age << 4), 0, 0); + render16(x, y, 368 + (age << 4), 48, 0); break; case TILE_WOOD_WALL: - checkSurrTiles4(x >> 4, y >> 4, TILE_WOOD_WALL); - - renderConnectedTile4(x, y, 0, 32, woodColor); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_WOOD_WALL); + + renderConnectedTile4(x, y, 384, 16); + break; + case TILE_STONE_WALL: + checkSurrTiles4(level, x >> 4, y >> 4, TILE_STONE_WALL); + + renderConnectedTile4(x, y, 256, 80); + break; + case TILE_IRON_WALL: + checkSurrTiles4(level, x >> 4, y >> 4, TILE_IRON_WALL); + + renderConnectedTile4(x, y, 448, 16); + break; + case TILE_GOLD_WALL: + checkSurrTiles4(level, x >> 4, y >> 4, TILE_GOLD_WALL); + + renderConnectedTile4(x, y, 256, 32); + break; + case TILE_GEM_WALL: + checkSurrTiles4(level, x >> 4, y >> 4, TILE_GEM_WALL); + + renderConnectedTile4(x, y, 320, 32); + break; + case TILE_DUNGEON_WALL: + checkSurrTiles8(level, x >> 4, y >> 4, TILE_DUNGEON_WALL); + + renderConnectedTile8(x, y, 384, 32); + break; + case TILE_DUNGEON_FLOOR: + render16(x, y, 464 + d*16, 32, 0); + break; + case TILE_DUNGEON_ENTRANCE: + render16(x, y, 352 + (level==5 ? 16 : 0), 80, 0); + break; + case TILE_MAGIC_BARRIER: + renderTile(TILE_DUNGEON_FLOOR, 0, level, x, y); + render16(x, y, 320, 64, d); + + //draw remaining pillar count + PlayerData *lp = getLocalPlayer(); + if((lp->entity.x - (x+8))*(lp->entity.x - (x+8)) + (lp->entity.y - (y+8))*(lp->entity.y - (y+8)) <= 24*24) { + x -= offsetX; + y -= offsetY; + + int data = 0; + int i = 0; + for (i = 0; i < eManager.lastSlot[level]; ++i) { + Entity * e = &eManager.entities[level][i]; + + if(e->type == ENTITY_MAGIC_PILLAR) { + ++data; + } + } + + char currentCount[3]; + sprintf(currentCount, "%d", data); + + drawSizedTextColor(currentCount, x+4 + 1, y+4 + 1, 2, dungeonColor[1]); + drawSizedTextColor(currentCount, x+4, y+4, 2, dungeonColor[0]); + } + + break; + case TILE_BOOKSHELVES: + checkSurrTiles4(level, x >> 4, y >> 4, TILE_BOOKSHELVES); + + renderConnectedTile4(x, y, 384, 80 + d*16); + break; + case TILE_WOOD_FLOOR: + render16(x, y, 336, 96, 0); + break; + case TILE_MYCELIUM: + checkSurrTiles4(level, x >> 4, y >> 4, TILE_MYCELIUM); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_MUSHROOM_BROWN); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_MUSHROOM_RED); + + if(level==1 && worldData.season==3) renderConnectedTile4(x, y, 256, 112); + else renderConnectedTile4(x, y, 448, 80); + break; + case TILE_MUSHROOM_BROWN: + renderTile(TILE_MYCELIUM, 0, level, x, y); + render16(x, y, 448 + (d&0x1)*16, 96, 0); + break; + case TILE_MUSHROOM_RED: + renderTile(TILE_MYCELIUM, 0, level, x, y); + render16(x, y, 480 + (d&0x1)*16, 96, 0); + break; + case TILE_ICE: + renderTile(TILE_WATER, 0, level, x, y); + //checkSurrTiles4(x >> 4, y >> 4, TILE_WATER); + //checkSurrTiles4(x >> 4, y >> 4, TILE_HOLE); + checkSurrTiles4(level, x >> 4, y >> 4, TILE_ICE); + + renderConnectedTile4(x, y, 448, 112); break; } resetSurrTiles(); } -void renderConnectedTile4(int x, int y, u32 xTile, u32 yTile, u32 color) { +void renderConnectedTile4(int x, int y, u32 xTile, u32 yTile) { //render complete tile in one piece to reduce strain(added for o3DS) if (tl && tr && tu && td) { - render16b(x, y, xTile+48, yTile, 0, color); + render16(x, y, xTile+48, yTile, 0); return; } - + int l = (tl ? 16 : 0); int r = (tr ? 16 : 0); int u = (tu ? 32 : 0); int d = (td ? 32 : 0); - - renderb(x, y, xTile +l+u, yTile, 0, color); - renderb(x+8, y, xTile+8+r+u, yTile, 0, color); - renderb(x, y+8, xTile +l+d, yTile+8, 0, color); - renderb(x+8, y+8, xTile+8+r+d, yTile+8, 0, color); + + render(x, y, xTile +l+u, yTile, 0); + render(x+8, y, xTile+8+r+u, yTile, 0); + render(x, y+8, xTile +l+d, yTile+8, 0); + render(x+8, y+8, xTile+8+r+d, yTile+8, 0); } -void renderConnectedTile8(int x, int y, u32 xTile, u32 yTile, u32 color) { +void renderConnectedTile8(int x, int y, u32 xTile, u32 yTile) { //render complete tile in one piece to reduce strain(added for o3DS) if (tl && tr && tu && td && tul && tur && tdl && tdr) { - render16b(x, y, xTile+64, yTile, 0, color); + render16(x, y, xTile+64, yTile, 0); return; } - + int l = (tl ? 16 : 0); int r = (tr ? 16 : 0); int u = (tu ? 32 : 0); int d = (td ? 32 : 0); - - renderb(x, y, xTile +l+u+((tl && tu && tul) ? 16 : 0), yTile, 0, color); - renderb(x+8, y, xTile+8+r+u+((tr && tu && tur) ? 16 : 0), yTile, 0, color); - renderb(x, y+8, xTile +l+d+((tl && td && tdl) ? 16 : 0), yTile+8, 0, color); - renderb(x+8, y+8, xTile+8+r+d+((tr && td && tdr) ? 16 : 0), yTile+8, 0, color); + + render(x, y, xTile +l+u+((tl && tu && tul) ? 16 : 0), yTile, 0); + render(x+8, y, xTile+8+r+u+((tr && tu && tur) ? 16 : 0), yTile, 0); + render(x, y+8, xTile +l+d+((tl && td && tdl) ? 16 : 0), yTile+8, 0); + render(x+8, y+8, xTile+8+r+d+((tr && td && tdr) ? 16 : 0), yTile+8, 0); } -void renderZoomedMap() { - int mx = mScrollX; - int my = mScrollY; - if(zoomLevel == 2) mx = 32; - sf2d_draw_texture_scale(minimap[currentLevel], mx, my, zoomLevel, zoomLevel); // zoomed map +void renderZoomedMap(PlayerData *pd) { + sf2d_draw_rectangle(0, 0, 320, 240, 0xFF0C0C0C); //You might think "real" black would be better, but it actually looks better that way + + int mx = pd->mapScrollX; + int my = pd->mapScrollY; + if(pd->mapZoomLevel == 2) mx = 32; + sf2d_draw_texture_scale(minimap[pd->entity.level], mx, my, pd->mapZoomLevel, pd->mapZoomLevel); // zoomed map + // Airwizard on zoomed map - if(currentLevel == 0){ + if(pd->entity.level == 0){ if(awX != 0 && awY != 0){ render16c( - (mx+((awX/16)*zoomLevel)-16)/2, - (my+((awY/16)*zoomLevel)-16)/2, - 160, 112, - ((player.p.walkDist >> 6) & 1) == 0 ? 0 : 1, + (mx+((awX/16)*pd->mapZoomLevel)-16)/2, + (my+((awY/16)*pd->mapZoomLevel)-16)/2, + 160, 112, + ((pd->entity.p.walkDist >> 6) & 1) == 0 ? 0 : 1, 2, 2 ); } - } + } // Player on zoomed map - render16c( - (mx+((player.x/16)*zoomLevel)-16)/2, - (my+((player.y/16)*zoomLevel)-16)/2, - 0, 112, - ((player.p.walkDist >> 6) & 1) == 0 ? 0 : 1, - 2, 2 - ); - - drawText(mapText,224, 214); // "x2"/"x4"/"x6" - render16(142, 2, 72, 208, 0); // Exit button - renderc(126, 102, 40, 208, 32, 16, 0); // Plus/Minus zoom buttons - if(zoomLevel < 3) sf2d_draw_rectangle(258, 210, 26, 20, 0x7F4F4F4F); // gray out minus button - else if(zoomLevel > 5) sf2d_draw_rectangle(284, 210, 26, 20, 0x7F4F4F4F); // gray out minus button + //TODO: Maybe also render other players? + render16c( + (mx+((pd->entity.x/16)*pd->mapZoomLevel)-16)/2, + (my+((pd->entity.y/16)*pd->mapZoomLevel)-16)/2, + 0, 112, + ((pd->entity.p.walkDist >> 6) & 1) == 0 ? 0 : 1, + 2, 2 + ); + + drawText(pd->mapText,224, 214); // "x2"/"x4"/"x6" + render16(142, 2, 72, 208, 0); // Exit button + renderc(126, 102, 40, 208, 32, 16, 0); // Plus/Minus zoom buttons + if(pd->mapZoomLevel < 3) sf2d_draw_rectangle(258, 210, 26, 20, 0x7F4F4F4F); // gray out minus button + else if(pd->mapZoomLevel > 5) sf2d_draw_rectangle(284, 210, 26, 20, 0x7F4F4F4F); // gray out minus button } char scoreT[32]; -void renderGui() { +void renderGui(PlayerData *pd) { int i; + //health and stamina for (i = 0; i < 10; ++i) { - if (i < player.p.health) + if (i < pd->entity.p.health) render(i * 8 + 6, 5, 168, 152, 0); else render(i * 8 + 6, 5, 176, 152, 0); - if (i < player.p.stamina) + if (i < pd->entity.p.stamina) render(i * 8 + 6, 14, 184, 152, 0); else render(i * 8 + 6, 14, 191, 152, 0); } - sf2d_draw_texture(minimap[currentLevel], 10, 102); - renderItemWithTextCentered(player.p.activeItem, 320, 66); - itoa(player.p.score, scoreT, 10); // integer to base10 string + + //minimap + sf2d_draw_texture(minimap[pd->entity.level], 10, 102); + + //active item + renderItemWithTextCentered(pd->activeItem, 320, 66); + itoa(pd->score, scoreT, 10); // integer to base10 string drawText("Score:",214,12); drawText(scoreT,(140-(strlen(scoreT)*12))/2 + 180,29); - if(currentLevel == 0){ + if(pd->entity.level == 0){ if(awX != 0 && awY != 0){ renderc(1 + (awX/32), 47 + (awY/32), 88, 216, 8, 8, 0); // Mini-AWizard head. } - } - renderc(1 + (player.x/32), 47 + (player.y/32), 88, 208, 8, 8, 0); // Mini-Player head. - + } + //TODO: Maybe also render other players? + renderc(1 + (pd->entity.x/32), 47 + (pd->entity.y/32), 88, 208, 8, 8, 0); // Mini-Player head. + //quick select drawText("Quickselect:",164,118); - Inventory * inv = player.p.inv; + Inventory * inv = &(pd->inventory); Item * item; for (i = 0; i < 8; ++i) { if((inv->lastSlot) > i) { int xip = i % 4; int yip = i / 4; - + item = &inv->items[i]; renderItemIcon(item->id, item->countLevel, 81+xip*21, 77+yip*21); } } } -void renderPlayer() { - if (player.p.isDead) +void renderPlayer(PlayerData *pd) { + if (pd->entity.level!=getLocalPlayer()->entity.level) { return; - int xo = player.x - 8; - int yo = player.y - 8; + } + if (pd->entity.p.isDead) { + return; + } + int xo = pd->entity.x - 8; + int yo = pd->entity.y - 8; - if (player.p.attackTimer > 0 && player.p.dir == 1) { + //attack animation upwards + if (pd->entity.p.attackTimer > 0 && pd->entity.p.dir == 1) { renderc(xo, yo - 4, 16, 160, 16, 8, 0); - renderItemIcon(player.p.activeItem->id, player.p.activeItem->countLevel, - xo + 4, yo - 4); + renderItemIcon(pd->activeItem->id, pd->activeItem->countLevel, xo + 4, yo - 4); } - u8 walk = (player.p.walkDist >> 4) & 1; - bool swimming = isSwimming(); - switch (player.p.dir) { - case 0: // down - if (swimming) - renderc(xo, yo + 4, 48, - 160 + (((player.p.swimTimer >> 4) & 1) << 3), 16, 8, 0); - else - renderc(xo, yo + 8, 0, 120 + (player.p.isCarrying ? 16 : 0), 16, 8, - walk == 0 ? 0 : 1); - renderc(xo, yo, 0, 112 + (player.p.isCarrying ? 16 : 0), 16, 8, - walk == 0 ? 0 : 1); + + //find basic indices + int aIndexBig = 0; + int aIndexSmall = 0; + switch(pd->entity.p.dir) { + case 0: //down + aIndexBig = 0; + aIndexSmall = 0; break; - case 1: // up - 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); + case 1: //up + aIndexBig = 2; + aIndexSmall = 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); + case 2: //left + aIndexBig = 7; + aIndexSmall = 3; 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); + case 3: //right + aIndexBig = 4; + aIndexSmall = 2; break; } - if (player.p.isCarrying) { - renderFurniture(player.p.activeItem->id, xo, yo - 12); + //find index offset based on walk state + u8 walkingOffset = (pd->entity.p.walkDist >> 4) % 2; + if(pd->entity.p.dir==2 || pd->entity.p.dir==3) { + walkingOffset = (pd->entity.p.walkDist >> 4) % 4; + if(walkingOffset==2) walkingOffset = 0; + if(walkingOffset==3) walkingOffset = 2; } - if (player.p.attackTimer > 0) { - switch (player.p.dir) { + bool swimming = isWater(pd->entity.level, pd->entity.x>>4, pd->entity.y>>4); + + //render water anim when swimming + if (swimming) { + renderc(xo, yo + 5, 48, 160 + (((pd->entity.p.swimTimer >> 4) & 1) << 3), 16, 8, 0); + } + + + //render the different parts + //legs + if(!swimming) { + renderp(xo, yo, (0+aIndexBig+walkingOffset)*16, pd->sprite.legs*16); + } + //body + renderp(xo, yo, (10+aIndexBig+walkingOffset)*16, pd->sprite.body*16); + //arms (normal) + if(!(pd->entity.p.isCarrying)) { + renderp(xo, yo, (20+aIndexBig+walkingOffset)*16, pd->sprite.arms*16); + } + //head + renderp(xo, yo, (30+aIndexSmall)*16, pd->sprite.head*16); + //eyes + renderp(xo, yo, (34+aIndexSmall)*16, pd->sprite.eyes*16); + //arms (carrying) + if(pd->entity.p.isCarrying) { + renderp(xo, yo, (38+aIndexSmall)*16, pd->sprite.arms*16); + } + + + //furniture + if (pd->entity.p.isCarrying) { + renderFurniture(pd->activeItem->id, xo, yo - 12); + } + + //attack animation (other directios) + if (pd->entity.p.attackTimer > 0) { + switch (pd->entity.p.dir) { case 0: - renderc(xo - player.p.ax, yo - player.p.ay + 12, 16, 168, 16, 8, 0); - renderItemIcon(player.p.activeItem->id, - player.p.activeItem->countLevel, xo + 4, yo + 12); + renderc(xo - pd->entity.p.ax, yo - pd->entity.p.ay + 12, 16, 168, 16, 8, 0); + renderItemIcon(pd->activeItem->id, pd->activeItem->countLevel, xo + 4, yo + 12); break; case 2: - renderc(xo - player.p.ax - 4, yo - player.p.ay, 32, 160, 8, 16, 0); - renderItemIcon(player.p.activeItem->id, - player.p.activeItem->countLevel, xo - 4, yo + 4); + renderc(xo - pd->entity.p.ax - 4, yo - pd->entity.p.ay, 32, 160, 8, 16, 0); + renderItemIcon(pd->activeItem->id, pd->activeItem->countLevel, xo - 4, yo + 4); break; case 3: - renderc(xo - player.p.ax + 12, yo - player.p.ay, 40, 160, 8, 16, 0); - renderItemIcon(player.p.activeItem->id, - player.p.activeItem->countLevel, xo + 12, yo + 4); + renderc(xo - pd->entity.p.ax + 12, yo - pd->entity.p.ay, 40, 160, 8, 16, 0); + renderItemIcon(pd->activeItem->id, pd->activeItem->countLevel, xo + 12, yo + 4); break; } } } -void renderMenuBackground(int xScroll, int yScroll) { - sf2d_draw_rectangle(0, 0, 400, 240, 0xFF0C0C0C); //You might think "real" black would be better, but it actually looks better that way - renderLightsToStencil(); - renderBackground(xScroll, yScroll); - resetStencilStuff(); +void renderWeather(u8 level, int xScroll, int yScroll) { + if(level==1) { + if(worldData.season==3) { + int xp = -128 + ((syncTickCount>>2) - xScroll*2)%128; + int yp = -128 + ((syncTickCount>>1) - yScroll*2)%128; + int xp2 = 0 - ((syncTickCount>>2) + xScroll*2)%128; + int yp2 = -128 + ((syncTickCount>>1)+64 - yScroll*2)%128; + int xt; + int yt; + for(xt=0; xt<4; ++xt) { + for(yt=0; yt<3; ++yt) { + sf2d_draw_texture_part_scale(icons, xp + xt*128, yp + yt*128, 192, 0, 64, 64, 2, 2); + sf2d_draw_texture_part_scale(icons, xp2 + xt*128, yp2 + yt*128, 192, 0, 64, 64, 2, 2); + } + } + } + + if(worldData.rain) { + int xp = -((xScroll*2)%128); + int yp = -128 + ((syncTickCount<<2) - yScroll*2)%128; + int xp2 = -((xScroll*2+8)%128); + int yp2 = -128 + ((syncTickCount<<1)+64 - yScroll*2)%128; + int xt; + int yt; + for(xt=0; xt<4; ++xt) { + for(yt=0; yt<3; ++yt) { + sf2d_draw_texture_part_scale(icons, xp + xt*128, yp + yt*128, 128, 0, 64, 64, 2, 2); + sf2d_draw_texture_part_scale(icons, xp2 + xt*128, yp2 + yt*128, 128, 0, 64, 64, 2, 2); + } + } + } + } } -void renderBackground(int xScroll, int yScroll) { - if(currentLevel > 0) sf2d_draw_rectangle(0, 0, 400, 240, dirtColor[currentLevel]); // dirt color - else { +void renderDayNight(PlayerData *pd) { + if(pd->entity.level==1 && (worldData.daytime<6000 || worldData.daytime>18000)) { + int color1 = 0x000C0C0C; + int color2 = 0x00100C0C; + int alpha1 = 0x88; + int alpha2 = 0xDD; + + if(worldData.daytime>5000 && worldData.daytime<6000) { + alpha2 = (alpha2 * (1000-(worldData.daytime-5000)))/1000; + alpha1 = (alpha1 * (1000-(worldData.daytime-5000)))/1000; + } else if(worldData.daytime>18000 && worldData.daytime<19000) { + alpha1 = (alpha1 * (worldData.daytime-18000))/1000; + alpha2 = (alpha2 * (worldData.daytime-18000))/1000; + } + + color1 = color1 | (alpha1 << 24); + color2 = color2 | (alpha2 << 24); + + sf2d_draw_rectangle(0, 0, 400, 240, color1); //You might think "real" black would be better, but it actually looks better that way + renderLightsToStencil(pd, true, true, false); + sf2d_draw_rectangle(0, 0, 400, 240, color2); //You might think "real" black would be better, but it actually looks better that way + resetStencilStuff(); + } +} + +void renderBackground(s8 level, int xScroll, int yScroll) { + if(level == 0) { sf2d_draw_texture_part_scale(minimap[1], (-xScroll / 3) - 256, (-yScroll / 3) - 32, 0, 0, 128, 128, 12.5, 7.5); sf2d_draw_rectangle(0, 0, 400, 240, 0xAFDFDFDF); + } else if(level == 5) { + sf2d_draw_rectangle(0, 0, 400, 240, dungeonColor[1]); + } else { + sf2d_draw_rectangle(0, 0, 400, 240, dirtColor[level]); // dirt color } int xo = xScroll >> 4; int yo = yScroll >> 4; int x, y; for (x = xo; x <= 13 + xo; ++x) { for (y = yo; y <= 8 + yo; ++y) - renderTile(getTile(x, y), x << 4, y << 4); + renderTile(getTile(level, x, y), getData(level, x, y), level, x << 4, y << 4); } } @@ -858,6 +1111,15 @@ void renderFurniture(int itemID, int x, int y) { case ITEM_LANTERN: render16(x, y, 144, 128, 0); break; + case ITEM_LOOM: + render16(x, y, 224, 128, 0); + break; + case ITEM_ENCHANTER: + render16(x, y, 240, 128, 0); + break; + case ITEM_POTION_MAKER: + render16(x, y, 192, 168, 0); + break; } } @@ -875,24 +1137,50 @@ void renderEntity(Entity* e, int x, int y) { renderFurniture(e->entityFurniture.itemID, x - 8, y - 8); break; case ENTITY_ZOMBIE: - switch (e->zombie.dir) { + switch (e->hostile.dir) { case 0: // down - render16b(x - 8, y - 8, 64, 112, - ((e->zombie.walkDist >> 4) & 1) == 0 ? 0 : 1, - e->zombie.color); + render16b(x - 8, y - 8, 64, 112, ((e->hostile.walkDist >> 4) & 1) == 0 ? 0 : 1, e->hostile.color); break; case 1: // up - render16b(x - 8, y - 8, 80, 112, - ((e->zombie.walkDist >> 4) & 1) == 0 ? 0 : 1, - e->zombie.color); + render16b(x - 8, y - 8, 80, 112, ((e->hostile.walkDist >> 4) & 1) == 0 ? 0 : 1, e->hostile.color); break; case 2: // left - render16b(x - 8, y - 8, 96 + (((e->zombie.walkDist >> 4) & 1) << 4), - 112, 1, e->zombie.color); + render16b(x - 8, y - 8, 96 + (((e->hostile.walkDist >> 4) & 1) << 4), 112, 1, e->hostile.color); break; case 3: // right - render16b(x - 8, y - 8, 96 + (((e->zombie.walkDist >> 4) & 1) << 4), - 112, 0, e->zombie.color); + render16b(x - 8, y - 8, 96 + (((e->hostile.walkDist >> 4) & 1) << 4), 112, 0, e->hostile.color); + break; + } + break; + case ENTITY_SKELETON: + switch (e->hostile.dir) { + case 0: // down + render16b(x - 8, y - 8, 0, 80, ((e->hostile.walkDist >> 4) & 1) == 0 ? 0 : 1, e->hostile.color); + break; + case 1: // up + render16b(x - 8, y - 8, 16, 80, ((e->hostile.walkDist >> 4) & 1) == 0 ? 0 : 1, e->hostile.color); + break; + case 2: // left + render16b(x - 8, y - 8, 32 + (((e->hostile.walkDist >> 4) & 1) << 4), 80, 1, e->hostile.color); + break; + case 3: // right + render16b(x - 8, y - 8, 32 + (((e->hostile.walkDist >> 4) & 1) << 4), 80, 0, e->hostile.color); + break; + } + break; + case ENTITY_KNIGHT: + switch (e->hostile.dir) { + case 0: // down + render16b(x - 8, y - 8, 64, 80, ((e->hostile.walkDist >> 4) & 1) == 0 ? 0 : 1, e->hostile.color); + break; + case 1: // up + render16b(x - 8, y - 8, 80, 80, ((e->hostile.walkDist >> 4) & 1) == 0 ? 0 : 1, e->hostile.color); + break; + case 2: // left + render16b(x - 8, y - 8, 96 + (((e->hostile.walkDist >> 4) & 1) << 4), 80, 1, e->hostile.color); + break; + case 3: // right + render16b(x - 8, y - 8, 96 + (((e->hostile.walkDist >> 4) & 1) << 4), 80, 0, e->hostile.color); break; } break; @@ -903,10 +1191,10 @@ void renderEntity(Entity* e, int x, int y) { case ENTITY_AIRWIZARD: e->wizard.spriteAdjust = 0; if (e->wizard.health < 200) { - if (tickCount / 4 % 3 < 2) + if (syncTickCount / 4 % 3 < 2) e->wizard.spriteAdjust = 16; } else if (e->wizard.health < 1000) { - if (tickCount / 5 % 4 < 2) + if (syncTickCount / 5 % 4 < 2) e->wizard.spriteAdjust = 16; } switch (e->wizard.dir) { @@ -928,6 +1216,22 @@ void renderEntity(Entity* e, int x, int y) { break; } break; + case ENTITY_PASSIVE: + switch (e->passive.dir) { + case 0: // down + render16(x - 8, y - 8, (e->passive.mtype*64) + 0, 96, ((e->passive.walkDist >> 4) & 1) == 0 ? 0 : 1); + break; + case 1: // up + render16(x - 8, y - 8, (e->passive.mtype*64) + 16, 96, ((e->passive.walkDist >> 4) & 1) == 0 ? 0 : 1); + break; + case 2: // left + render16(x - 8, y - 8, (e->passive.mtype*64) + 32 + (((e->passive.walkDist >> 4) & 1) << 4), 96, 1); + break; + case 3: // right + render16(x - 8, y - 8, (e->passive.mtype*64) + 32 + (((e->passive.walkDist >> 4) & 1) << 4), 96, 0); + break; + } + break; case ENTITY_TEXTPARTICLE: x -= offsetX; y -= offsetY; @@ -945,13 +1249,80 @@ void renderEntity(Entity* e, int x, int y) { return; renderr(x, y, 200, 152, 0, e->spark.age * 0.0349); break; + case ENTITY_DRAGON: + switch (e->dragon.dir) { + case 0: // down + render32(x - 16, y - 16, 0+(e->dragon.animTimer/4 * 32), 256, 2); + break; + case 1: // up + render32(x - 16, y - 16, 0+(e->dragon.animTimer/4 * 32), 256, 0); + break; + case 2: // left + render32(x - 16, y - 16, 0+(e->dragon.animTimer/4 * 32), 288, 1); + break; + case 3: // right + render32(x - 16, y - 16, 0+(e->dragon.animTimer/4 * 32), 288, 0); + break; + } + break; + case ENTITY_DRAGONPROJECTILE: + if(e->dragonFire.type==0) { + renderr(x, y, 0, 320, 0, e->dragonFire.age * 0.349); + } else { + render(x, y, 8, 320 + (e->dragonFire.age/10)*8, 0); + } + break; + case ENTITY_MAGIC_PILLAR: + render16(x - 8, y - 8, 16, 320, 0); + break; + case ENTITY_ARROW: + if (e->arrow.age >= 200) + if (e->arrow.age / 6 % 2 == 0) + return; + + int abits = 0; + int ayp = 168; + if(e->arrow.xa<0) { + abits += 1; + } + if(e->arrow.ya<0) { + ayp += 8; + } + if(e->arrow.ya>0) { + ayp += 8; + abits += 2; + } + + switch (e->arrow.itemID) { + case ITEM_ARROW_WOOD: + render(x-2, y-2, 72, ayp, abits); + break; + case ITEM_ARROW_STONE: + render(x-2, y-2, 80, ayp, abits); + break; + case ITEM_ARROW_IRON: + render(x-2, y-2, 88, ayp, abits); + break; + case ITEM_ARROW_GOLD: + render(x-2, y-2, 96, ayp, abits); + break; + case ITEM_ARROW_GEM: + render(x-2, y-2, 104, ayp, abits); + break; + } + break; + case ENTITY_GLOWWORM: + render(x-4, y-4, 224, 112, 0); + break; + case ENTITY_NPC: + render16(x - 8, y - 8, (e->npc.type*16) + 0, 64, 0); } } -void renderEntities(int x, int y, EntityManager* em) { +void renderEntities(u8 level, int x, int y, EntityManager* em) { int i; - for (i = 0; i < em->lastSlot[currentLevel]; ++i) { - Entity e = em->entities[currentLevel][i]; + for (i = 0; i < em->lastSlot[level]; ++i) { + Entity e = em->entities[level][i]; if (e.x > x - 200 && e.y > y - 125 && e.x < x + 200 && e.y < y + 125) renderEntity(&e, e.x, e.y); } @@ -992,8 +1363,50 @@ void renderItemList(Inventory * inv, int xo, int yo, int x1, int y1, } } -void renderRecipes(RecipeManager * r, int xo, int yo, int x1, int y1, - int selected) { +void renderArmorList(Inventory * inv, int xo, int yo, int x1, int y1, + int selected) { + // If lastSlot is 0, then there are no items are in the inventory. + bool drawCursor = true; + int w = x1 - xo; + int h = y1 - yo - 2; + int i1 = inv->lastSlot; + if (i1 > h) + i1 = h; + int i; + int io = selected - h / 2; + int armorcount; + for (i = 0; i < i1; ++i) { + if(inv->items[i + io].id > 119 && inv->items[i + io].id < 141) { + armorcount = armorcount + 1; + } + } + if (selected < 0) { + drawCursor = false; + selected = 0; + } + if (io > inv->lastSlot - h) + io = armorcount; + if (io < 0) + io = 1; + for (i = 0; i < i1; ++i) { + if(inv->items[i + io].id > 119 && inv->items[i + io].id < 141) { + 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; @@ -1013,16 +1426,16 @@ void renderRecipes(RecipeManager * r, int xo, int yo, int x1, int y1, int i, col; for (i = 0; i < i1; ++i) { int x = (1 + xo) << 4, y = (i + 1 + yo) << 4; - renderItemIcon(r->recipes[i + io].itemResult, - r->recipes[i + io].itemAmountLevel, x >> 1, y >> 1); + renderItemIcon(r->recipes[i + io].itemResult, r->recipes[i + io].itemAmountLevel, x >> 1, y >> 1); if (r->recipes[i + io].canCraft) col = 0xFFFFFFFF; else col = 0xFF7F7F7F; - drawTextColor( - getBasicItemName(r->recipes[i + io].itemResult, - r->recipes[i + io].itemAmountLevel), x + 18, y + 2, - col); + if(r->recipes[i + io].itemAmountLevel==1) { + drawTextColor(getBasicItemName(r->recipes[i + io].itemResult, r->recipes[i + io].itemAmountLevel), x + 18, y + 2, col); + } else { + drawTextColor(getItemName(r->recipes[i + io].itemResult, r->recipes[i + io].itemAmountLevel), x + 18, y + 2, col); + } } int yy = selected + 1 - io + yo; @@ -1050,13 +1463,13 @@ void renderItemStuffWithText(int itemID, int itemCL, bool onlyOne, int x, int y) y + 2, 0xFFD2D2D2, 0xFFFFFFFF); } -/* For bottom screen */ +/* For bottom screen */ void renderItemWithTextCentered(Item* item, int width, int y) { char * tn = getItemName(item->id, item->countLevel); - int x = (width - ((strlen(tn) + 2) * 12))/2; - + int x = (width - ((strlen(tn) + 2) * 12))/2; + renderItemIcon(item->id, item->countLevel, x >> 1, y >> 1); - + if (item->onlyOne) drawText(getItemName(item->id, item->countLevel), x + 18, y + 2); else @@ -1065,6 +1478,8 @@ void renderItemWithTextCentered(Item* item, int width, int y) { } void renderItemIcon(int itemID, int countLevel, int x, int y) { + int xd; + int yd; switch (itemID) { case ITEM_NULL: return; @@ -1108,7 +1523,7 @@ void renderItemIcon(int itemID, int countLevel, int x, int y) { render(x, y, 0, 152, 0); break; case ITEM_WOOD: - render(x, y, 8, 152, 0); + render(x, y, 160, 168, 0); break; case ITEM_STONE: renderb(x, y, 16, 152, 0, rockColor[1]); @@ -1143,6 +1558,21 @@ void renderItemIcon(int itemID, int countLevel, int x, int y) { case ITEM_APPLE: render(x, y, 80, 152, 0); break; + case ITEM_GOLD_APPLE: + render(x, y, 144, 168, 0); + break; + case ITEM_STRENGTH_POTION: + render(x, y, 176, 160, 0); + break; + case ITEM_SPEED_POTION: + render(x, y, 184, 160, 0); + break; + case ITEM_REGEN_POTION: + render(x, y, 192, 160, 0); + break; + case ITEM_SWIM_BREATH_POTION: + render(x, y, 200, 160, 0); + break; case ITEM_SLIME: renderb(x, y, 88, 152, 0, 0xFF4DC04D); break; @@ -1150,16 +1580,16 @@ void renderItemIcon(int itemID, int countLevel, int x, int y) { renderb(x, y, 88, 152, 0, 0xFF383838); break; case ITEM_IRONORE: - renderb(x, y, 88, 152, 0, 0xFF9999BC); + renderb(x, y, 88, 152, 0, ironColor); break; case ITEM_GOLDORE: - renderb(x, y, 88, 152, 0, 0xFF77CECE); + renderb(x, y, 88, 152, 0, goldColor); break; case ITEM_IRONINGOT: - renderb(x, y, 96, 152, 0, 0xFFC8C8DF); + renderb(x, y, 96, 152, 0, ironColor); break; case ITEM_GOLDINGOT: - renderb(x, y, 96, 152, 0, 0xFFBCEAEA); + renderb(x, y, 96, 152, 0, goldColor); break; case ITEM_GLASS: render(x, y, 104, 152, 0); @@ -1167,8 +1597,127 @@ void renderItemIcon(int itemID, int countLevel, int x, int y) { case ITEM_GEM: render(x, y, 112, 152, 0); break; + case ITEM_LOOM: + render(x, y, 120, 160, 0); + break; + case ITEM_ENCHANTER: + render(x, y, 144, 160, 0); + break; + case ITEM_POTION_MAKER: + render(x, y, 192, 168, 0); + break; + case ITEM_WALL_WOOD: + renderb(x, y, 224, 144, 0, woodColor); + break; + case ITEM_WALL_STONE: + renderb(x, y, 224, 144, 0, rockColor[1]); + break; + case ITEM_WALL_IRON: + renderb(x, y, 224, 144, 0, ironColor); + break; + case ITEM_WALL_GOLD: + renderb(x, y, 224, 144, 0, goldColor); + break; + case ITEM_WALL_GEM: + renderb(x, y, 224, 144, 0, gemColor); + break; + case ITEM_WOOL: + render(x, y, 64, 160, 0); + break; + case ITEM_STRING: + render(x, y, 72, 160, 0); + break; + case ITEM_PORK_RAW: + render(x, y, 80, 160, 0); + break; + case ITEM_PORK_COOKED: + render(x, y, 88, 160, 0); + break; + case ITEM_BEEF_RAW: + render(x, y, 96, 160, 0); + break; + case ITEM_BEEF_COOKED: + render(x, y, 104, 160, 0); + break; + case ITEM_LEATHER: + render(x, y, 112, 160, 0); + break; + case ITEM_ARROW_WOOD: + render(x, y, 72, 168, 0); + break; + case ITEM_ARROW_STONE: + render(x, y, 80, 168, 0); + break; + case ITEM_ARROW_IRON: + render(x, y, 88, 168, 0); + break; + case ITEM_ARROW_GOLD: + render(x, y, 96, 168, 0); + break; + case ITEM_ARROW_GEM: + render(x, y, 104, 168, 0); + break; + case ITEM_BONE: + render(x, y, 128, 160, 0); + break; + case ITEM_DUNGEON_KEY: + render(x, y, 136, 160, 0); + break; + case ITEM_WIZARD_SUMMON: + render(x, y, 152, 160, 0); + break; + case ITEM_DRAGON_EGG: + render(x, y, 160, 160, 0); + break; + case ITEM_DRAGON_SCALE: + render(x, y, 168, 160, 0); + break; + case ITEM_BOOKSHELVES: + render(x, y, 232, 144, 0); + break; + case ITEM_MAGIC_DUST: + render(x, y, 200, 152, 0); + break; + case ITEM_COIN: + render(x, y, 208, 152, 0); + break; case TOOL_BUCKET: render(x, y, 200 + countLevel * 8, 144, 0); break; + case TOOL_BOW: + render(x, y, 64, 168, 0); + break; + case TOOL_MAGIC_COMPASS: + xd = worldData.compassData[getLocalPlayer()->entity.level][0] - (getLocalPlayer()->entity.x>>4); + yd = worldData.compassData[getLocalPlayer()->entity.level][1] - (getLocalPlayer()->entity.y>>4); + if(abs(yd)>abs(xd)) { + if(yd<0) render(x, y, 112, 168, 0); + else render(x, y, 120, 168, 0); + } else { + if(xd<0) render(x, y, 128, 168, 0); + else render(x, y, 136, 168, 0); + } + break; + case TOOL_CLAYMORE: + render(x, y, countLevel * 8, 176, 0); + break; + case ITEM_TORCH: + render(x, y, 168, 168, 0); + break; + case ITEM_BED: + render(x, y, 176, 168, 0); + break; + case ITEM_SHEARS: + render(x, y, 40, 176, 0); + break; + case ITEM_FISHING_ROD: + render(x, y, 48, 176, 0); + break; + case ITEM_FISH_RAW: + render(x, y, 56, 176, 0); + break; + case ITEM_FISH_COOKED: + render(x, y, 64, 176, 0); + break; } } diff --git a/source/Render.h b/source/Render.h old mode 100644 new mode 100755 index 237139e..e967538 --- a/source/Render.h +++ b/source/Render.h @@ -1,7 +1,7 @@ #pragma once #include <3ds.h> #include -#include +#include #include #include #include "Globals.h" @@ -9,40 +9,42 @@ sf2d_texture *playerLightBake; sf2d_texture *lanternLightBake; +sf2d_texture *glowwormLightBake; +sf2d_texture *glowwormBigLightBake; int offsetX, offsetY; +int playerScale; void render(s32 xp, s32 yp, u32 xTile, u32 yTile, u8 bits); void renderb(s32 xp, s32 yp, u32 xTile, u32 yTile, u8 bits, u32 color); 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 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 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 render32(s32 xp, s32 yp, u32 xTile, u32 yTile, u8 bits); void renderTitle(int x, int y); void renderFrame(int x1, int y1, int x2, int y2, u32 bgColor); -void renderTile(int i, int x, int y); -void renderConnectedTile4(int x, int y, u32 xTile, u32 yTile, u32 color); -void renderConnectedTile8(int x, int y, u32 xTile, u32 yTile, u32 color); -void renderBackground(int xScroll, int yScroll); -void renderMenuBackground(int xScroll, int yScroll); //Renders the darkness +void renderTile(int i, int d, u8 level, int x, int y); +void renderConnectedTile4(int x, int y, u32 xTile, u32 yTile); +void renderConnectedTile8(int x, int y, u32 xTile, u32 yTile); +void renderBackground(s8 level, int xScroll, int yScroll); +void renderWeather(u8 level, int xScroll, int yScroll); +void renderDayNight(PlayerData *pd); void renderButtonIcon(u32 icon, int x, int y, float scale); void bakeLights(); void freeLightBakes(); -void renderLightsToStencil(); +void renderLightsToStencil(PlayerData *pd, bool force, bool invert, bool rplayer); void resetStencilStuff(); void bakeLight(sf2d_texture* texture, int x, int y, int r); void renderLight(int x, int y, sf2d_texture* texture); -void renderGui(); -void renderZoomedMap(); -void renderPlayer(); +void renderGui(PlayerData *pd); +void renderZoomedMap(PlayerData *pd); +void renderPlayer(PlayerData *pd); void drawText(char * msg, u32 x, u32 y); void drawSizedText(char * msg, u32 x, u32 y, float size); @@ -52,12 +54,11 @@ void drawSizedTextColor(char * msg, int x, int y, float size, u32 color); void renderFurniture(int itemID, int x, int y); void renderEntity(Entity* e, int x, int y); -void renderEntities(int x, int y, EntityManager* em); +void renderEntities(u8 level, int x, int y, EntityManager* em); -void renderRecipes(RecipeManager * r, int xo, int yo, int x1, int y1, - int selected); -void renderItemList(Inventory * inv, int xo, int yo, int x1, int y1, - int selected); +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 renderArmorList(Inventory * inv, int xo, int yo, int x1, int y1, int selected); void renderItemWithText(Item* item, int x, int y); void renderItemStuffWithText(int itemID, int itemCL, bool onlyOne, int x, int y); void renderItemWithTextCentered(Item* item, int width, int y); diff --git a/source/SaveLoad.c b/source/SaveLoad.c old mode 100644 new mode 100755 index 37e5536..ecd1067 --- a/source/SaveLoad.c +++ b/source/SaveLoad.c @@ -1,251 +1,663 @@ #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; -} +#include "ZipHelper.h" 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; - } + switch(e->type){ + case ENTITY_AIRWIZARD: + case ENTITY_SLIME: + case ENTITY_ZOMBIE: + case ENTITY_SKELETON: + case ENTITY_KNIGHT: + case ENTITY_ITEM: + case ENTITY_FURNITURE: + case ENTITY_PASSIVE: + case ENTITY_GLOWWORM: + case ENTITY_DRAGON: + case ENTITY_NPC: + 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); +s16 calculateImportantEntites(EntityManager * eManager, int level){ + int i; + s16 count = 0; + for(i = 0; i < eManager->lastSlot[level]; ++i){ + if(entityIsImportant(&eManager->entities[level][i])){ + count++; + } + } + return count; } -int loadWorld(char * filename, EntityManager * eManager, Entity * player, u8 * map, u8 * mapData){ - FILE * file; - int i,j; - - if ((file = fopen(filename, "rb"))){ - - fread(&player->p.score, sizeof(int), 1, file); - fread(&player->p.hasWonSaved, sizeof(bool), 1, file); - fread(&player->p.health, sizeof(s16), 1, file); - fread(&player->x, sizeof(s16), 1, file); - fread(&player->y, sizeof(s16), 1, file); - fread(¤tLevel, sizeof(s8), 1, file); - - fread(&eManager->nextInv, sizeof(s16), 1, file); - for(i = 0; i < eManager->nextInv; ++i) { - fread(&eManager->invs[i].lastSlot, sizeof(s16), 1, file); // read amount of items in inventory; - for(j = 0; j < eManager->invs[i].lastSlot; ++j) { - fread(&eManager->invs[i].items[j].id, sizeof(s16), 1, file); // write ID of item - fread(&eManager->invs[i].items[j].countLevel, sizeof(s16), 1, file); // write count/level of item - fread(&eManager->invs[i].items[j].onlyOne, sizeof(bool), 1, file); - eManager->invs[i].items[j].invPtr = (int*)&eManager->invs[i]; // setup Inventory pointer - eManager->invs[i].items[j].slotNum = j; // setup slot number - if(eManager->invs[i].items[j].id == ITEM_CHEST){ // for chest item specifically. - int invIndex; - fread(&invIndex, sizeof(int), 1, file); - eManager->invs[i].items[j].chestPtr = (Inventory*)&eManager->invs[invIndex]; // setup chest inventory pointer. - } - } - } - - for(i = 0; i < 5; ++i){ - fread(&eManager->lastSlot[i], sizeof(s16), 1, file); // read amount of entities in level. - for(j = 0; j < eManager->lastSlot[i]; ++j){ - fread(&eManager->entities[i][j].type, sizeof(s16), 1, file); // read entity's type ID - fread(&eManager->entities[i][j].x, sizeof(s16), 1, file); // read entity's x coordinate - fread(&eManager->entities[i][j].y, sizeof(s16), 1, file); // read entity's y coordinate - eManager->entities[i][j].slotNum = j; - switch(eManager->entities[i][j].type){ - case ENTITY_SMASHPARTICLE: - eManager->entities[i][j].level = i; - eManager->entities[i][j].smashParticle.age = 300; - eManager->entities[i][j].canPass = true; - break; - case ENTITY_TEXTPARTICLE: - eManager->entities[i][j].level = i; - eManager->entities[i][j].canPass = true; - eManager->entities[i][j].textParticle.age = 59; - eManager->entities[i][j].textParticle.text = NULL; - eManager->entities[i][j].textParticle.xx = eManager->entities[i][j].x; - eManager->entities[i][j].textParticle.yy = eManager->entities[i][j].y; - eManager->entities[i][j].textParticle.zz = 2; - eManager->entities[i][j].textParticle.xa = 0; - eManager->entities[i][j].textParticle.ya = 0; - eManager->entities[i][j].textParticle.za = 0; - break; - case ENTITY_SPARK: - eManager->entities[i][j].level = i; - eManager->entities[i][j].spark.age = 300; - break; - case ENTITY_AIRWIZARD: - fread(&eManager->entities[i][j].wizard.health, sizeof(s16), 1, file); - eManager->entities[i][j].level = i; - eManager->entities[i][j].hurtTime = 0; - eManager->entities[i][j].xKnockback = 0; - eManager->entities[i][j].yKnockback = 0; - eManager->entities[i][j].wizard.dir = 0; - eManager->entities[i][j].wizard.attackDelay = 0; - eManager->entities[i][j].wizard.attackTime = 0; - eManager->entities[i][j].wizard.attackType = 0; - eManager->entities[i][j].wizard.xa = 0; - eManager->entities[i][j].wizard.ya = 0; - eManager->entities[i][j].xr = 4; - eManager->entities[i][j].yr = 3; - eManager->entities[i][j].canPass = true; - break; - case ENTITY_SLIME: - fread(&eManager->entities[i][j].slime.health, sizeof(s16), 1, file); - fread(&eManager->entities[i][j].slime.lvl, sizeof(s8), 1, file); - eManager->entities[i][j].level = i; - eManager->entities[i][j].hurtTime = 0; - eManager->entities[i][j].xKnockback = 0; - eManager->entities[i][j].yKnockback = 0; - eManager->entities[i][j].slime.xa = 0; - eManager->entities[i][j].slime.ya = 0; - eManager->entities[i][j].slime.dir = 0; - eManager->entities[i][j].xr = 4; - eManager->entities[i][j].yr = 3; - eManager->entities[i][j].canPass = false; - switch(eManager->entities[i][j].slime.lvl){ - case 2: eManager->entities[i][j].slime.color = 0xFF8282CC; break; - case 3: eManager->entities[i][j].slime.color = 0xFFEFEFEF; break; - case 4: eManager->entities[i][j].slime.color = 0xFFAA6262; break; - default: eManager->entities[i][j].slime.color = 0xFF95DB95; break; - } - break; - case ENTITY_ZOMBIE: - fread(&eManager->entities[i][j].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 = 0xFF8282CC; break; - case 3: eManager->entities[i][j].zombie.color = 0xFFEFEFEF; break; - case 4: eManager->entities[i][j].zombie.color = 0xFFAA6262; break; - default: eManager->entities[i][j].zombie.color = 0xFF95DB95; 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; +//helper methods +char **files; +int fileCount; + +void saveTrackFileReset() { + if(files!=NULL) { + for(int i=0; i0) { + fwrite(buffer, 1, size, out); + } + } while(size>0); + + fclose(in); + fclose(out); + + return true; +} + +//internal save methods +void saveInventory(Inventory *inv, EntityManager *eManager, FILE *file) { + fwrite(&inv->lastSlot, sizeof(s16), 1, file); // write amount of items in inventory; + for(int j = 0; j < inv->lastSlot; ++j) { + fwrite(&(inv->items[j].id), sizeof(s16), 1, file); // write ID of item + fwrite(&(inv->items[j].countLevel), sizeof(s16), 1, file); // write count/level of item + fwrite(&(inv->items[j].onlyOne), sizeof(bool), 1, file); + if(inv->items[j].id == ITEM_CHEST){ + int invIndex = inv->items[j].chestPtr - eManager->invs; + fwrite(&invIndex, sizeof(int), 1, file); + } + } +} + +void saveWorldInternal(char *filename, EntityManager *eManager, WorldData *worldData) { + FILE * file = fopen(filename, "wb"); //TODO: should be checked + + int i, j; + + //write savefile version + int version = SAVE_VERSION; + fwrite(&version, sizeof(int), 1, file); + + // Inventory Data + fwrite(&eManager->nextInv, sizeof(s16), 1, file); // write amount of inventories. + for(i = 0; i < eManager->nextInv; ++i) { + saveInventory(&(eManager->invs[i]), eManager, file); + } + + // Entity Data + for(i = 0; i < 5; ++i) { //for every level (except dungeon of course) + int amount = calculateImportantEntites(eManager,i); + fwrite(&amount, sizeof(s16), 1, file); // read amount of entities in level. + for(j = 0; j < eManager->lastSlot[i]; ++j){ + if(!entityIsImportant(&eManager->entities[i][j])) continue; + + fwrite(&eManager->entities[i][j].type, sizeof(s16), 1, file); // write entity's type ID + fwrite(&eManager->entities[i][j].x, sizeof(s16), 1, file); // write entity's x coordinate + fwrite(&eManager->entities[i][j].y, sizeof(s16), 1, file); // write entity's y coordinate + 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: + case ENTITY_SKELETON: + case ENTITY_KNIGHT: + fwrite(&eManager->entities[i][j].hostile.health, sizeof(s16), 1, file); + fwrite(&eManager->entities[i][j].hostile.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; + case ENTITY_PASSIVE: + fwrite(&eManager->entities[i][j].passive.health, sizeof(s16), 1, file); + fwrite(&eManager->entities[i][j].passive.mtype, sizeof(u8), 1, file); + break; + case ENTITY_DRAGON: + fwrite(&eManager->entities[i][j].dragon.health, sizeof(s16), 1, file); + break; + case ENTITY_NPC: + fwrite(&eManager->entities[i][j].npc.type, sizeof(u8), 1, file); + break; + } + } + } + + // Day/season Data + fwrite(&worldData->daytime, sizeof(u16), 1, file); + fwrite(&worldData->day, sizeof(int), 1, file); + fwrite(&worldData->season, sizeof(u8), 1, file); + fwrite(&worldData->rain, sizeof(bool), 1, file); + + // Compass Data + fwrite(worldData->compassData, sizeof(u8), 6*3, file); //x,y of choosen stair and count per level + + // Map Data + //Don't write or load dungeon, so only first 5 levels not 6 + fwrite(worldData->map, sizeof(u8), 128*128*5, file); // Map Tile IDs, 128*128*5 bytes = 80KB + fwrite(worldData->data, sizeof(u8), 128*128*5, file); // Map Tile Data (Damage done to trees/rocks, age of wheat & saplings, etc). 80KB + + fclose(file); +} + +void savePlayerInternal(char *filename, PlayerData *player, EntityManager *eManager) { + FILE * file = fopen(filename, "wb"); //TODO: should be checked + + int i; + + //write savefile version + int version = SAVE_VERSION; + fwrite(&version, sizeof(int), 1, file); + + // basic player info + fwrite(&player->score, sizeof(int), 1, file); + fwrite(&player->isSpawned, sizeof(bool), 1, file); + fwrite(&player->entity.p.hasWonSaved, sizeof(bool), 1, file); + fwrite(&player->entity.p.health, sizeof(s16), 1, file); + fwrite(&player->entity.x, sizeof(s16), 1, file); + fwrite(&player->entity.y, sizeof(s16), 1, file); + fwrite(&player->entity.level, sizeof(s8), 1, file); + + saveInventory(&(player->inventory), eManager, file); + + // Sprite info + fwrite(&(player->sprite.choosen), sizeof(bool), 1, file); + fwrite(&(player->sprite.legs), sizeof(u8), 1, file); + fwrite(&(player->sprite.body), sizeof(u8), 1, file); + fwrite(&(player->sprite.arms), sizeof(u8), 1, file); + fwrite(&(player->sprite.head), sizeof(u8), 1, file); + fwrite(&(player->sprite.eyes), sizeof(u8), 1, file); + + // Minimap Data + fwrite(player->minimapData, sizeof(u8), 128*128, file); // Minimap, visibility data 16KB + + // Quest Data + fwrite(&(player->questManager.size), sizeof(int), 1, file); + for(i = 0; i < player->questManager.size; ++i) { + fwrite(&(player->questManager.questlines[i].currentQuest), sizeof(int), 1, file); + fwrite(&(player->questManager.questlines[i].currentQuestDone), sizeof(bool), 1, file); + } + + //Potion Data + fwrite(&UnderStrengthEffect, sizeof(bool), 1, file); + fwrite(&UnderSpeedEffect, sizeof(bool), 1, file); + fwrite(®ening, sizeof(bool), 1, file); + fwrite(&UnderSwimBreathEffect, sizeof(bool), 1, file); + fwrite(&player->entity.p.strengthTimer, sizeof(int), 1, file); + fwrite(&player->entity.p.speedTimer, sizeof(int), 1, file); + fwrite(&player->entity.p.swimBreathTimer, sizeof(int), 1, file); + fwrite(&player->entity.p.regenTimer, sizeof(int), 1, file); + + fclose(file); +} + +//internal load methods +void loadInventory(Inventory *inv, EntityManager *eManager, FILE *file) { + fread(&(inv->lastSlot), sizeof(s16), 1, file); // read amount of items in inventory; + for(int j = 0; j < inv->lastSlot; ++j) { + fread(&(inv->items[j].id), sizeof(s16), 1, file); // write ID of item + fread(&(inv->items[j].countLevel), sizeof(s16), 1, file); // write count/level of item + fread(&(inv->items[j].onlyOne), sizeof(bool), 1, file); + inv->items[j].invPtr = (int*)inv; // setup Inventory pointer + inv->items[j].slotNum = j; // setup slot number + if(inv->items[j].id == ITEM_CHEST){ // for chest item specifically. + int invIndex; + fread(&invIndex, sizeof(int), 1, file); + inv->items[j].chestPtr = (Inventory*)&eManager->invs[invIndex]; // setup chest inventory pointer. + } + } +} + +void loadWorldInternal(char *filename, EntityManager *eManager, WorldData *worldData) { + FILE * file = fopen(filename, "rb"); //TODO: should be checked + + int i, j; + + //read savefile version + int version; + fread(&version, sizeof(int), 1, file); + + // Inventory Data + fread(&eManager->nextInv, sizeof(s16), 1, file); + for(i = 0; i < eManager->nextInv; ++i) { + loadInventory(&(eManager->invs[i]), eManager, file); + } + + // Entity Data + for(i = 0; i < 5; ++i){ + fread(&eManager->lastSlot[i], sizeof(s16), 1, file); // read amount of entities in level. + for(j = 0; j < eManager->lastSlot[i]; ++j){ + fread(&eManager->entities[i][j].type, sizeof(s16), 1, file); // read entity's type ID + fread(&eManager->entities[i][j].x, sizeof(s16), 1, file); // read entity's x coordinate + fread(&eManager->entities[i][j].y, sizeof(s16), 1, file); // read entity's y coordinate + eManager->entities[i][j].slotNum = j; + switch(eManager->entities[i][j].type){ + case ENTITY_SMASHPARTICLE: + eManager->entities[i][j].level = i; + eManager->entities[i][j].smashParticle.age = 300; + eManager->entities[i][j].canPass = true; + break; + case ENTITY_TEXTPARTICLE: + eManager->entities[i][j].level = i; + eManager->entities[i][j].canPass = true; + eManager->entities[i][j].textParticle.age = 59; + eManager->entities[i][j].textParticle.text = NULL; + eManager->entities[i][j].textParticle.xx = eManager->entities[i][j].x; + eManager->entities[i][j].textParticle.yy = eManager->entities[i][j].y; + eManager->entities[i][j].textParticle.zz = 2; + eManager->entities[i][j].textParticle.xa = 0; + eManager->entities[i][j].textParticle.ya = 0; + eManager->entities[i][j].textParticle.za = 0; + break; + case ENTITY_SPARK: + eManager->entities[i][j].level = i; + eManager->entities[i][j].spark.age = 300; + break; + case ENTITY_AIRWIZARD: + fread(&eManager->entities[i][j].wizard.health, sizeof(s16), 1, file); + eManager->entities[i][j].level = i; + eManager->entities[i][j].hurtTime = 0; + eManager->entities[i][j].xKnockback = 0; + eManager->entities[i][j].yKnockback = 0; + eManager->entities[i][j].wizard.dir = 0; + eManager->entities[i][j].wizard.attackDelay = 0; + eManager->entities[i][j].wizard.attackTime = 0; + eManager->entities[i][j].wizard.attackType = 0; + eManager->entities[i][j].wizard.xa = 0; + eManager->entities[i][j].wizard.ya = 0; + eManager->entities[i][j].xr = 4; + eManager->entities[i][j].yr = 3; + eManager->entities[i][j].canPass = true; + break; + case ENTITY_SLIME: + fread(&eManager->entities[i][j].slime.health, sizeof(s16), 1, file); + fread(&eManager->entities[i][j].slime.lvl, sizeof(s8), 1, file); + eManager->entities[i][j].level = i; + eManager->entities[i][j].hurtTime = 0; + eManager->entities[i][j].xKnockback = 0; + eManager->entities[i][j].yKnockback = 0; + eManager->entities[i][j].slime.xa = 0; + eManager->entities[i][j].slime.ya = 0; + eManager->entities[i][j].slime.dir = 0; + eManager->entities[i][j].xr = 4; + eManager->entities[i][j].yr = 3; + eManager->entities[i][j].canPass = false; + switch(eManager->entities[i][j].slime.lvl){ + case 2: eManager->entities[i][j].slime.color = 0xFF8282CC; break; + case 3: eManager->entities[i][j].slime.color = 0xFFEFEFEF; break; + case 4: eManager->entities[i][j].slime.color = 0xFFAA6262; break; + default: eManager->entities[i][j].slime.color = 0xFF95DB95; break; + } + break; + case ENTITY_ZOMBIE: + fread(&eManager->entities[i][j].hostile.health, sizeof(s16), 1, file); + fread(&eManager->entities[i][j].hostile.lvl, sizeof(s8), 1, file); + eManager->entities[i][j].level = i; + eManager->entities[i][j].hurtTime = 0; + eManager->entities[i][j].xKnockback = 0; + eManager->entities[i][j].yKnockback = 0; + eManager->entities[i][j].hostile.dir = 0; + eManager->entities[i][j].xr = 4; + eManager->entities[i][j].yr = 3; + eManager->entities[i][j].canPass = false; + switch(eManager->entities[i][j].hostile.lvl){ + case 2: eManager->entities[i][j].hostile.color = 0xFF8282CC; break; + case 3: eManager->entities[i][j].hostile.color = 0xFFEFEFEF; break; + case 4: eManager->entities[i][j].hostile.color = 0xFFAA6262; break; + default: eManager->entities[i][j].hostile.color = 0xFF95DB95; break; + } + break; + case ENTITY_SKELETON: + fread(&eManager->entities[i][j].hostile.health, sizeof(s16), 1, file); + fread(&eManager->entities[i][j].hostile.lvl, sizeof(s8), 1, file); + eManager->entities[i][j].level = i; + eManager->entities[i][j].hurtTime = 0; + eManager->entities[i][j].xKnockback = 0; + eManager->entities[i][j].yKnockback = 0; + eManager->entities[i][j].hostile.dir = 0; + eManager->entities[i][j].hostile.randAttackTime = 0; + eManager->entities[i][j].xr = 4; + eManager->entities[i][j].yr = 3; + eManager->entities[i][j].canPass = false; + switch(eManager->entities[i][j].hostile.lvl){ + case 2: eManager->entities[i][j].hostile.color = 0xFFC4C4C4; break; + case 3: eManager->entities[i][j].hostile.color = 0xFFA0A0A0; break; + case 4: eManager->entities[i][j].hostile.color = 0xFF7A7A7A; break; + default: eManager->entities[i][j].hostile.color = 0xFFFFFFFF; break; + } + break; + case ENTITY_KNIGHT: + fread(&eManager->entities[i][j].hostile.health, sizeof(s16), 1, file); + fread(&eManager->entities[i][j].hostile.lvl, sizeof(s8), 1, file); + eManager->entities[i][j].level = i; + eManager->entities[i][j].hurtTime = 0; + eManager->entities[i][j].xKnockback = 0; + eManager->entities[i][j].yKnockback = 0; + eManager->entities[i][j].hostile.dir = 0; + eManager->entities[i][j].xr = 4; + eManager->entities[i][j].yr = 3; + eManager->entities[i][j].canPass = false; + switch(eManager->entities[i][j].hostile.lvl){ + case 2: eManager->entities[i][j].hostile.color = 0xFF0000C6; break; + case 3: eManager->entities[i][j].hostile.color = 0xFF00A3C6; break; + case 4: eManager->entities[i][j].hostile.color = 0xFF707070; break; + default: eManager->entities[i][j].hostile.color = 0xFFFFFFFF; break; + } + break; + case ENTITY_ITEM: + //eManager->entities[i][j].entityItem.item = newItem(0,0); + fread(&eManager->entities[i][j].entityItem.item.id, sizeof(s16), 1, file); + fread(&eManager->entities[i][j].entityItem.item.countLevel, sizeof(s16), 1, file); + fread(&eManager->entities[i][j].entityItem.age, sizeof(s16), 1, file); + eManager->entities[i][j].level = i; + eManager->entities[i][j].entityItem.age = 0; + eManager->entities[i][j].xr = 3; + eManager->entities[i][j].yr = 3; + eManager->entities[i][j].canPass = false; + eManager->entities[i][j].entityItem.xx = eManager->entities[i][j].x; + eManager->entities[i][j].entityItem.yy = eManager->entities[i][j].y; + eManager->entities[i][j].entityItem.zz = 2; + eManager->entities[i][j].entityItem.xa = 0; + eManager->entities[i][j].entityItem.ya = 0; + eManager->entities[i][j].entityItem.za = 0; + break; + case ENTITY_FURNITURE: + fread(&eManager->entities[i][j].entityFurniture.itemID, sizeof(s16), 1, file); + int invIndex; + fread(&invIndex, sizeof(int), 1, file); + eManager->entities[i][j].entityFurniture.inv = &eManager->invs[invIndex]; + eManager->entities[i][j].xr = 3; + eManager->entities[i][j].yr = 3; + eManager->entities[i][j].canPass = false; + if(eManager->entities[i][j].entityFurniture.itemID == ITEM_LANTERN) eManager->entities[i][j].entityFurniture.r = 8; + break; + case ENTITY_PASSIVE: + fread(&eManager->entities[i][j].passive.health, sizeof(s16), 1, file); + fread(&eManager->entities[i][j].passive.mtype, sizeof(u8), 1, file); + eManager->entities[i][j].level = i; + eManager->entities[i][j].hurtTime = 0; + eManager->entities[i][j].xKnockback = 0; + eManager->entities[i][j].yKnockback = 0; + eManager->entities[i][j].passive.dir = 0; + eManager->entities[i][j].xr = 4; + eManager->entities[i][j].yr = 3; + eManager->entities[i][j].canPass = false; + break; + case ENTITY_GLOWWORM: + eManager->entities[i][j].glowworm.xa = 0; + eManager->entities[i][j].glowworm.ya = 0; + eManager->entities[i][j].glowworm.randWalkTime = 0; + eManager->entities[i][j].glowworm.waitTime = 0; + break; + case ENTITY_DRAGON: + fread(&eManager->entities[i][j].dragon.health, sizeof(s16), 1, file); + eManager->entities[i][j].level = i; + eManager->entities[i][j].hurtTime = 0; + eManager->entities[i][j].xKnockback = 0; + eManager->entities[i][j].yKnockback = 0; + eManager->entities[i][j].dragon.dir = 0; + eManager->entities[i][j].dragon.attackDelay = 0; + eManager->entities[i][j].dragon.attackTime = 0; + eManager->entities[i][j].dragon.attackType = 0; + eManager->entities[i][j].dragon.animTimer = 0; + eManager->entities[i][j].dragon.xa = 0; + eManager->entities[i][j].dragon.ya = 0; + eManager->entities[i][j].xr = 8; + eManager->entities[i][j].yr = 8; + eManager->entities[i][j].canPass = true; + break; + case ENTITY_NPC: + fread(&eManager->entities[i][j].npc.type, sizeof(u8), 1, file); + eManager->entities[i][j].level = i; + eManager->entities[i][j].hurtTime = 0; + eManager->entities[i][j].xKnockback = 0; + eManager->entities[i][j].yKnockback = 0; + eManager->entities[i][j].xr = 4; + eManager->entities[i][j].yr = 3; + eManager->entities[i][j].canPass = false; + break; + } + } + } + + // Day/season Data + fread(&worldData->daytime, sizeof(u16), 1, file); + fread(&worldData->day, sizeof(int), 1, file); + fread(&worldData->season, sizeof(u8), 1, file); + fread(&worldData->rain, sizeof(bool), 1, file); + + // Compass Data + fread(worldData->compassData, sizeof(u8), 6*3, file); //x,y of choosen stair and count per level + + // Map Data + //Don't write or load dungeon, so only first 5 levels not 6 + fread(worldData->map, sizeof(u8), 128*128*5, file); // Map Tile IDs, 128*128*5 bytes = 80KB + fread(worldData->data, sizeof(u8), 128*128*5, file); // Map Tile Data (Damage done to trees/rocks, age of wheat & saplings, etc). 80KB + + fclose(file); +} + +void loadPlayerInternal(char *filename, PlayerData *player, EntityManager *eManager) { + FILE * file = fopen(filename, "rb"); //TODO: should be checked + + int i; + + //read savefile version + int version; + fread(&version, sizeof(int), 1, file); + + // basic player info + fread(&player->score, sizeof(int), 1, file); + fread(&player->isSpawned, sizeof(bool), 1, file); + fread(&player->entity.p.hasWonSaved, sizeof(bool), 1, file); + fread(&player->entity.p.health, sizeof(s16), 1, file); + fread(&player->entity.x, sizeof(s16), 1, file); + fread(&player->entity.y, sizeof(s16), 1, file); + fread(&player->entity.level, sizeof(s8), 1, file); + + loadInventory(&(player->inventory), eManager, file); + + // Sprite info + fread(&(player->sprite.choosen), sizeof(bool), 1, file); + fread(&(player->sprite.legs), sizeof(u8), 1, file); + fread(&(player->sprite.body), sizeof(u8), 1, file); + fread(&(player->sprite.arms), sizeof(u8), 1, file); + fread(&(player->sprite.head), sizeof(u8), 1, file); + fread(&(player->sprite.eyes), sizeof(u8), 1, file); + + // Minimap Data + fread(player->minimapData, sizeof(u8), 128*128, file); // Minimap, visibility data 16KB + + // Quest Data + fread(&(player->questManager.size), sizeof(int), 1, file); + for(i = 0; i < player->questManager.size; ++i) { + fread(&(player->questManager.questlines[i].currentQuest), sizeof(int), 1, file); + fread(&(player->questManager.questlines[i].currentQuestDone), sizeof(bool), 1, file); + } + + //Potion Data + fread(&UnderStrengthEffect, sizeof(bool), 1, file); + fread(&UnderSpeedEffect, sizeof(bool), 1, file); + fread(®ening, sizeof(bool), 1, file); + fread(&UnderSwimBreathEffect, sizeof(bool), 1, file); + fread(&player->entity.p.strengthTimer, sizeof(int), 1, file); + fread(&player->entity.p.speedTimer, sizeof(int), 1, file); + fread(&player->entity.p.swimBreathTimer, sizeof(int), 1, file); + fread(&player->entity.p.regenTimer, sizeof(int), 1, file); + + fclose(file); +} + + +bool saveWorld(char *filename, EntityManager *eManager, WorldData *worldData, PlayerData *players, int playerCount) { + //check if old save file exists + bool exists = false; + FILE *testFile = fopen(filename, "rb"); + if(testFile) { + fclose(testFile); + exists = true; + } + + saveTrackFileReset(); + + if(exists) { + //create backup copy + char *filenameBackup = malloc(sizeof(filename)+4+1); + if(filenameBackup==NULL) { + return false; + } + strcpy(filenameBackup, filename); + strcat(filenameBackup, ".bak"); + if(!saveFileCopy(filenameBackup, filename)) { + return false; + } + + //extract files and keep track of references + if(unzipAndLoad(filename, &saveTrackFile, SAVE_COMMENT, ZIPHELPER_KEEP_FILES)!=0) { + saveDeleteTrackedFiles(); + return false; + } + + remove(filename); + } + + //save world data + saveWorldInternal("main.wld", eManager, worldData); + saveTrackFile("main.wld"); + + //save player data of active players + for(int i=0; i #include #include "Entity.h" +#include "Player.h" +#include "Globals.h" -void saveCurrentWorld(char * filename, EntityManager * eManager, Entity * player, u8 * map, u8 * mapData); -int loadWorld(char * filename, EntityManager * eManager, Entity * player, u8 * map, u8 * mapData); +#define SAVE_VERSION 1 + +#define SAVE_COMMENT "Minicraft3DSSave" +#define SAVE_COPYBUFFER_SIZE 4096 + +//void saveCurrentWorld(char * filename, EntityManager * eManager, Entity * player, u8 * map, u8 * mapData, WorldData *worldData); +//int loadWorld(char * filename, EntityManager * eManager, Entity * player, u8 * map, u8 * mapData, WorldData *worldData); + +bool saveWorld(char *filename, EntityManager *eManager, WorldData *worldData, PlayerData *players, int playerCount); +bool loadWorld(char *filename, EntityManager *eManager, WorldData *worldData, PlayerData *players, int playerCount); diff --git a/source/Sound.c b/source/Sound.c old mode 100644 new mode 100755 index 70f7d64..3529237 --- a/source/Sound.c +++ b/source/Sound.c @@ -1,71 +1,104 @@ #include "Sound.h" +u8 soundListenerLevel; +int soundListenerX; +int soundListenerY; + 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); + FILE *file = fopen(filename, "rb"); + if(file != NULL){ + fseek(file, 0, SEEK_END); + snd->size = ftell(file)/2; + fseek(file, 0, SEEK_SET); + snd->buffer = linearAlloc(snd->size*sizeof(u16)); + 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); + csndPlaySound(8, SOUND_FORMAT_16BIT | SOUND_ONE_SHOT, 44100, 1, 0, snd.buffer, snd.buffer, snd.size); } -void playMusic(Sound snd){ - csndPlaySound(10, SOUND_FORMAT_16BIT | SOUND_REPEAT, 44100, 1, 0, snd.buffer, snd.buffer, snd.size); +void playSoundPositioned(Sound snd, s8 level, int x, int y) { + if(level != soundListenerLevel) return; + int xd = soundListenerX - x; + int yd = soundListenerY - y; + if (xd * xd + yd * yd > 80 * 80) return; + + csndPlaySound(8, SOUND_FORMAT_16BIT | SOUND_ONE_SHOT, 44100, 1, 0, snd.buffer, snd.buffer, snd.size); } -void updateMusic(int lvl) { +void setListenerPosition(s8 level, int x, int y) { + soundListenerLevel = level; + soundListenerX = x; + soundListenerY = y; +} + +void playMusic(Sound *snd){ + static Sound *lastSnd; + if(lastSnd==snd) return; + lastSnd = snd; + + csndPlaySound(10, SOUND_FORMAT_16BIT | SOUND_REPEAT, 44100, 1, 0, snd->buffer, snd->buffer, snd->size); +} + +void stopMusic() { + CSND_SetPlayState(8, 0); + CSND_SetPlayState(10, 0); + csndExecCmds(true); +} + +void updateMusic(int lvl, int time) { switch(lvl) { case 0: - playMusic(music_floor0); + playMusic(&music_floor0); break; case 1: - playMusic(music_floor1); + if(time>6000 && time<19000) playMusic(&music_floor1); + else playMusic(&music_floor1_night); break; case 2: case 3: - playMusic(music_floor23); + playMusic(&music_floor23); break; case 4: - playMusic(music_floor4); + case 5: //TODO - dungeon needs own music + playMusic(&music_floor4); break; } } void loadSounds() { - loadSound(&snd_playerHurt, "resources/playerhurt.raw"); - loadSound(&snd_playerDeath, "resources/playerdeath.raw"); - loadSound(&snd_monsterHurt, "resources/monsterhurt.raw"); - loadSound(&snd_test, "resources/test.raw"); - loadSound(&snd_pickup, "resources/pickup.raw"); - loadSound(&snd_bossdeath, "resources/bossdeath.raw"); - loadSound(&snd_craft, "resources/craft.raw"); - - loadSound(&music_menu, "resources/music/menu.raw"); - loadSound(&music_floor0, "resources/music/floor0.raw"); - loadSound(&music_floor1, "resources/music/floor1.raw"); - loadSound(&music_floor23, "resources/music/floor2_3.raw"); - loadSound(&music_floor4, "resources/music/floor4.raw"); + loadSound(&snd_playerHurt, "romfs:/resources/playerhurt.raw"); + loadSound(&snd_playerDeath, "romfs:/resources/death.raw"); + loadSound(&snd_monsterHurt, "romfs:/resources/monsterhurt.raw"); + loadSound(&snd_test, "romfs:/resources/test.raw"); + loadSound(&snd_pickup, "romfs:/resources/pickup.raw"); + loadSound(&snd_bossdeath, "romfs:/resources/bossdeath.raw"); + loadSound(&snd_craft, "romfs:/resources/craft.raw"); + + loadSound(&music_menu, "romfs:/resources/music/menu.raw"); + loadSound(&music_floor0, "romfs:/resources/music/floor0.raw"); + loadSound(&music_floor1, "romfs:/resources/music/floor1.raw"); + loadSound(&music_floor1_night, "romfs:/resources/music/floor1_night.raw"); + loadSound(&music_floor23, "romfs:/resources/music/floor2_3.raw"); + loadSound(&music_floor4, "romfs:/resources/music/floor4.raw"); } 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); - + 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); + linearFree(music_menu.buffer); linearFree(music_floor0.buffer); linearFree(music_floor1.buffer); + linearFree(music_floor1_night.buffer); linearFree(music_floor23.buffer); linearFree(music_floor4.buffer); } diff --git a/source/Sound.h b/source/Sound.h old mode 100644 new mode 100755 index 2e36791..b85193b --- a/source/Sound.h +++ b/source/Sound.h @@ -6,15 +6,19 @@ typedef struct { - u32 size; - u8 * buffer; + u32 size; + u8 * buffer; } Sound; void loadSound(Sound * snd, char * filename); void playSound(Sound snd); -void playMusic(Sound snd); -void updateMusic(int lvl); +void playSoundPositioned(Sound snd, s8 level, int x, int y); +void setListenerPosition(s8 level, int x, int y); + +void playMusic(Sound *snd); +void updateMusic(int lvl, int time); +void stopMusic(); void loadSounds(); void freeSounds(); @@ -30,5 +34,6 @@ Sound snd_craft; Sound music_menu; Sound music_floor0; Sound music_floor1; +Sound music_floor1_night; Sound music_floor23; -Sound music_floor4; \ No newline at end of file +Sound music_floor4; diff --git a/source/Synchronizer.c b/source/Synchronizer.c new file mode 100644 index 0000000..546a4d2 --- /dev/null +++ b/source/Synchronizer.c @@ -0,0 +1,250 @@ +#include "Synchronizer.h" + +#include "Globals.h" +#include "Player.h" +#include "Input.h" +#include "Network.h" +#include "PacketHandler.h" +#include "Ingame.h" + +u32 synchronizerLocalTurn; +unsigned int synchronizerNextSeed; +bool synchronizerRunning; + +int synchronizerCurrentTicks; + +bool synchronizerTurnReady(); +void synchronizerNextTurn(); + +void synchronizerSendLocalInputs(); + +int synchronizerGetTurnIndex(u32 turn); + +void synchronizerInit(int seed, int initPlayerCount, int initPlayerLocalID) { + synchronizerLocalTurn = 0; + synchronizerNextSeed = seed; + playerCount = initPlayerCount; + playerLocalID = initPlayerLocalID; + syncTickCount = 0; + + //reset player turn states (e.g. is turn data recieved), first turn needs to happen with no inputs + for(int i=0; i send to server + sendStartReadyPacket(playerLocalID); +} + +void synchronizerSetPlayerReady(int playerID) { + players[playerID].ready = true; +} + +bool synchronizerAllReady() { + for(int i=0; i1) { + size_t size = writeInputPacket(networkWriteBuffer, &localInputs, playerLocalID, synchronizerLocalTurn); + networkSend(networkWriteBuffer, size); + } +} + +void synchronizerOnInputPacket(u8 playerID, u32 turnNumber, void *data, size_t dataSize) { + if(turnNumber>=synchronizerLocalTurn && turnNumber + +//2-3 seem be optimal (at least for 2 players) +#define SYNCHRONIZER_TICKS_PER_TURN 2 + +void synchronizerInit(int seed, int initPlayerCount, int initPlayerLocalID); + +void synchronizerSendUID(); +void synchronizerSetPlayerUID(int playerID, u32 uid); +void synchronizerSendIfReady(); +void synchronizerSetPlayerReady(int playerID); +bool synchronizerAllReady(); +bool synchronizerIsRunning(); + +void synchronizerStart(); + +void synchronizerTick(void (*gtick)(void)); + +void synchronizerReset(); + +void synchronizerOnInputPacket(u8 playerID, u32 turnNumber, void *data, size_t dataSize); + +// values used ingame +u32 syncTickCount; + +// helpers for random numbers +double gaussrand(bool reset); diff --git a/source/ZipHelper.c b/source/ZipHelper.c new file mode 100644 index 0000000..2868587 --- /dev/null +++ b/source/ZipHelper.c @@ -0,0 +1,213 @@ +#include "ZipHelper.h" + +#include +#include +#include +#include "minizip/zip.h" +#include "minizip/unzip.h" + +#define dir_delimter '/' +#define MAX_FILENAME 256 +#define READ_SIZE 9216 + +int unzipAndLoad(char *filename, int (*fileCallback)(char *filename), char *expectedComment, int keepFiles) { + // Open the zip file + unzFile *zipfile = unzOpen(filename); + if (zipfile == NULL) { + return 1; // Error: ZipFile could not be opened. + } + + // Get info about the zip file + unz_global_info global_info; + if (unzGetGlobalInfo(zipfile, &global_info ) != UNZ_OK) { + unzClose(zipfile); + return 2; // Error: Could not read global info + } + + if(expectedComment!=NULL) { + char *buffer = malloc(global_info.size_comment+1); + if(buffer==NULL) { + unzClose(zipfile); + return 3; // Error: Could not read global comment + } + + if (unzGetGlobalComment(zipfile, buffer, global_info.size_comment+1) < 0) { + unzClose(zipfile); + return 3; // Error: Could not read global comment + } + + if (strcmp(expectedComment, buffer)!=0) { + unzClose(zipfile); + return 4; // Error: Global comment did not have expected value + } + + free(buffer); + } + + // Buffer to hold data read from the zip file. + void *read_buffer = malloc(READ_SIZE); + if(read_buffer==NULL) { + // Error: Could not allocate read buffer + return 5; + } + + // Loop to extract all files + uLong i; + for (i = 0; i < global_info.number_entry; ++i) { + // Get info about current file. + unz_file_info file_info; + char filename[MAX_FILENAME]; + if (unzGetCurrentFileInfo(zipfile, &file_info, filename, MAX_FILENAME, NULL, 0, NULL, 0 ) != UNZ_OK) { + free(read_buffer); + unzClose(zipfile); + return 6; // Error: Could not read file info + } + + // Check if this entry is NOT a directory or file. + const size_t filename_length = strlen(filename); + if (filename[ filename_length-1 ] != dir_delimter){ + if (unzOpenCurrentFile( zipfile ) != UNZ_OK) { + free(read_buffer); + unzClose(zipfile); + return 7; + } + + // Open a file to write out the data. + FILE * out = fopen(filename, "wb"); + if (out == NULL) { + free(read_buffer); + unzCloseCurrentFile(zipfile); + unzClose(zipfile); + return 8; + } + + int error = UNZ_OK; + do { + error = unzReadCurrentFile(zipfile, read_buffer, READ_SIZE); + if ( error < 0 ) { + //printf("error %d\n", error); + free(read_buffer); + unzCloseCurrentFile(zipfile); + unzClose(zipfile); + fclose(out); + remove(filename); + return 9; + } + + // Write data to file. + if (error > 0) { + fwrite(read_buffer, error, 1, out); // You should check return of fwrite... + } + } while (error > 0); + + fclose(out); + + //run callback + if((*fileCallback)(filename) != 0) { + free(read_buffer); + unzClose(zipfile); + remove(filename); + return 10; // Error: Callback error + } + + if(keepFiles==ZIPHELPER_CLEANUP_FILES) { + remove(filename); + } + } + + unzCloseCurrentFile(zipfile); + + // Go the the next entry listed in the zip file. + if (( i+1 ) < global_info.number_entry) { + if (unzGoToNextFile( zipfile ) != UNZ_OK) { + free(read_buffer); + unzClose(zipfile); + return 11; + } + } + } + + free(read_buffer); + unzClose(zipfile); + + return 0; +} + +int zipFiles(char *filename, char **files, int fileCount, int mode, char *comment) { + // Set mode + int zipMode = mode==ZIPHELPER_ADD ? APPEND_STATUS_ADDINZIP : APPEND_STATUS_CREATE; + FILE *testFile = fopen(filename, "r"); + if(testFile!=NULL) { + fclose(testFile); + } else { + zipMode = APPEND_STATUS_CREATE; + } + + // Open the zip file + zipFile *zipfile = zipOpen(filename, zipMode); + if (zipfile == NULL) return 1; // Error: ZipFile could not be opened. + + // Buffer to hold data read from the files. + void *read_buffer = malloc(READ_SIZE); + if(read_buffer==NULL) { + // Error: Could not allocate read buffer + return 2; + } + + // Loop all files to add + for(int i = 0; i < fileCount; i++) { + // Open a zipfile to write out the data. + if (zipOpenNewFileInZip(zipfile, files[i], NULL, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION) != ZIP_OK) { + free(read_buffer); + zipClose(zipfile, ""); + return 3; + } + + // Open a file to read the data. + FILE *in = fopen(files[i], "rb"); + if (in == NULL) { + free(read_buffer); + zipCloseFileInZip(zipfile); + zipClose(zipfile, ""); + return 4; + } + + size_t size; + do + { + size = fread(read_buffer, 1, READ_SIZE, in); + + + if(size0) { + //write data to zip + if (zipWriteInFileInZip(zipfile, read_buffer, size) != ZIP_OK) { + //printf("error %d\n", error); + free(read_buffer); + zipCloseFileInZip(zipfile); + zipClose(zipfile, ""); + fclose(in); + return 6; + } + } + } while (size > 0); + + fclose(in); + + zipCloseFileInZip(zipfile); + } + + free(read_buffer); + zipClose(zipfile, comment); + + return 0; +} diff --git a/source/ZipHelper.h b/source/ZipHelper.h new file mode 100644 index 0000000..3d71d01 --- /dev/null +++ b/source/ZipHelper.h @@ -0,0 +1,10 @@ +#pragma once + +#define ZIPHELPER_REPLACE 0 +#define ZIPHELPER_ADD 1 + +#define ZIPHELPER_KEEP_FILES 0 +#define ZIPHELPER_CLEANUP_FILES 1 + +int unzipAndLoad(char *filename, int (*fileCallback)(char *filename), char *expectedComment, int keepFiles); +int zipFiles(char *filename, char **files, int fileCount, int mode, char *comment); diff --git a/source/main.c b/source/main.c old mode 100644 new mode 100755 index fcf2853..a253153 --- a/source/main.c +++ b/source/main.c @@ -1,7 +1,7 @@ #include <3ds.h> #include #include -#include +#include #include #include #include @@ -12,268 +12,215 @@ #include "MapGen.h" #include "Menu.h" #include "texturepack.h" +#include "Network.h" +#include "SaveLoad.h" +#include "Ingame.h" +#include "Player.h" +#include "Synchronizer.h" +#include "PacketHandler.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) { +// TODO: Dungeon is way to difficult +// -> Skeleton arrows are slower, do a little less damage +// -> Or instead of less damage, implement a simple armor system - 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; - } - } - } +//TODO: Something still causes desyncs very rarely - /* Minimaps */ - sf2d_set_pixel(minimap[i], x, y, getTileColor(map[i][x + y * 128])); - } - } - } -} +static aptHookCookie cookie; -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() { + synchronizerInit(rand(), 1, 0); + synchronizerSetPlayerUID(0, localUID); + synchronizerStart(); -void setupGame(bool loadUpWorld) { - currentLevel = 1; - - // Reset entity manager. - memset(&eManager, 0, sizeof(eManager)); - sf2d_set_clear_color(0xFF6C6D82); //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); - } - - updateMusic(currentLevel); - - initMiniMap(loadUpWorld); - shouldRenderMap = false; - mScrollX = 0; - mScrollY = 0; - zoomLevel = 2; - sprintf(mapText,"x%d",zoomLevel); 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; - } - - tickTouchMap(); - tickTouchQuickSelect(); +void setupGameServer() { + size_t size; - 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; + networkHostStopConnections(); - 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->x < player.x + 160 && e->y < player.y + 125)) - tickEntity(e); - } + networkStart(); + //send start info (seed) + size = writeStartPacket(networkWriteBuffer, rand()); + networkSend(networkWriteBuffer, size); + processPacket(networkWriteBuffer, size); + networkSendWaitFlush(); + + //send save file if loading + FILE *file = fopen(currentFileName, "rb"); + if(file!=NULL) { + sendFile(file, 0, 0); + networkSendWaitFlush(); + fclose(file); + } + + //send start command + size = writeStartRequestPacket(networkWriteBuffer); + networkSend(networkWriteBuffer, size); + processPacket(networkWriteBuffer, size); + + initMPGame = 0; } -void clearScreen(int* data, u8 fill, int size) { - int i; - for (i = 0; i < size / 4; ++i) - data[i] = 0xFF000000; +void setupBGMap() { + // Reset entity manager. + memset(&eManager, 0, sizeof(eManager)); + sf2d_set_clear_color(0xFF6C6D82); + + + srand(time(NULL)); + createAndValidateTopMap(128, 128, 1, worldData.map[1], worldData.data[1]); + + // Reset entity manager. + memset(&eManager, 0, sizeof(eManager)); + sf2d_set_clear_color(0xFF6C6D82); + + initBGMap = 0; } +static void task_apt_hook(APT_HookType hook, void* param) { + switch(hook) { + case APTHOOK_ONSUSPEND: + paused = true; + stopMusic(); + break; + default: + break; + } +} + + +//for rendering -> move to a better place +extern int xscr, yscr; char debugText[34]; char bossHealthText[34]; int main() { + romfsInit(); cfguInit(); CFGU_GetSystemModel(&MODEL_3DS); FILE * file; - shouldRenderDebug = true; + shouldRenderDebug = false; if ((file = fopen("settings.bin", "r"))) { fread(&shouldRenderDebug,sizeof(bool),1,file); fread(&shouldSpeedup,sizeof(bool),1,file); osSetSpeedupEnable(shouldSpeedup); fclose(file); } - - sf2d_init(); + sf2d_init(); csndInit(); + networkInit(); + + srand(time(NULL)); + + //load or create localUID + if ((file = fopen("m3ds_uid.bin", "rb"))) { + fread(&localUID, sizeof(u32), 1, file); + fclose(file); + } else { + localUID = (((u32) (rand()%256))<<24) | (((u32) (rand()%256))<<16) | (((u32) (rand()%256))<<8) | (((u32) (rand()%256))); + + if ((file = fopen("m3ds_uid.bin", "wb"))) { + fwrite(&localUID, sizeof(u32), 1, file); + fclose(file); + } + } + noItem = newItem(ITEM_NULL, 0); - + + initMenus(); currentMenu = MENU_TITLE; currentSelection = 0; quitGame = false; + initBGMap = 1; - icons = sfil_load_PNG_buffer(icons2_png, SF2D_PLACE_RAM); + icons = sfil_load_PNG_buffer(icons_png, SF2D_PLACE_RAM); + playerSprites = sfil_load_PNG_buffer(player_png, SF2D_PLACE_RAM); font = sfil_load_PNG_buffer(Font_png, SF2D_PLACE_RAM); bottombg = sfil_load_PNG_buffer(bottombg_png, SF2D_PLACE_RAM); loadSounds(); - playMusic(music_menu); - + playMusic(&music_menu); + bakeLights(); - + int i; - for (i = 0; i < 5; ++i) { - minimap[i] = sf2d_create_texture(128, 128, TEXFMT_RGBA8, - SF2D_PLACE_RAM); + for (i = 0; i < 6; ++i) { + minimap[i] = sf2d_create_texture(128, 128, TEXFMT_RGBA8, SF2D_PLACE_RAM); sf2d_texture_tile32(minimap[i]); } - + reloadColors(); - + sf2d_set_vblank_wait(true); sf2d_set_clear_color(0xFF); /* Default inputs */ - k_up.input = KEY_DUP | KEY_CPAD_UP | KEY_CSTICK_UP; - k_down.input = KEY_DDOWN | KEY_CPAD_DOWN | KEY_CSTICK_DOWN; - k_left.input = KEY_DLEFT | KEY_CPAD_LEFT | KEY_CSTICK_LEFT; - k_right.input = KEY_DRIGHT | KEY_CPAD_RIGHT | KEY_CSTICK_RIGHT; - k_attack.input = KEY_A | KEY_B | KEY_L | KEY_ZR; - k_menu.input = KEY_X | KEY_Y | KEY_R | KEY_ZL; - k_pause.input = KEY_START; - k_accept.input = KEY_A; - k_decline.input = KEY_B; - k_delete.input = KEY_X; - k_menuNext.input = KEY_R; - k_menuPrev.input = KEY_L; + localInputs.k_up.input = KEY_DUP | KEY_CPAD_UP | KEY_CSTICK_UP; + localInputs.k_down.input = KEY_DDOWN | KEY_CPAD_DOWN | KEY_CSTICK_DOWN; + localInputs.k_left.input = KEY_DLEFT | KEY_CPAD_LEFT | KEY_CSTICK_LEFT; + localInputs.k_right.input = KEY_DRIGHT | KEY_CPAD_RIGHT | KEY_CSTICK_RIGHT; + localInputs.k_attack.input = KEY_A; + localInputs.k_pickup.input = KEY_B; + localInputs.k_use.input = KEY_X; + localInputs.k_menu.input = KEY_Y ; + localInputs.k_pause.input = KEY_START; + localInputs.k_accept.input = KEY_A; + localInputs.k_decline.input = KEY_B; + localInputs.k_delete.input = KEY_X; + localInputs.k_menuNext.input = KEY_R | KEY_ZR; + localInputs.k_menuPrev.input = KEY_L | KEY_ZL; /* If btnSave exists, then use that. */ if ((file = fopen("btnSave.bin", "rb"))) { - fread(&k_up.input, sizeof(int), 1, file); - fread(&k_down.input, sizeof(int), 1, file); - fread(&k_left.input, sizeof(int), 1, file); - fread(&k_right.input, sizeof(int), 1, file); - fread(&k_attack.input, sizeof(int), 1, file); - fread(&k_menu.input, sizeof(int), 1, file); - fread(&k_pause.input, sizeof(int), 1, file); - fread(&k_accept.input, sizeof(int), 1, file); - fread(&k_decline.input, sizeof(int), 1, file); - fread(&k_delete.input, sizeof(int), 1, file); - fread(&k_menuNext.input, sizeof(int), 1, file); - fread(&k_menuPrev.input, sizeof(int), 1, file); + fread(&(localInputs.k_up.input), sizeof(int), 1, file); + fread(&(localInputs.k_down.input), sizeof(int), 1, file); + fread(&(localInputs.k_left.input), sizeof(int), 1, file); + fread(&(localInputs.k_right.input), sizeof(int), 1, file); + fread(&(localInputs.k_attack.input), sizeof(int), 1, file); + fread(&(localInputs.k_pickup.input), sizeof(int), 1, file); + fread(&(localInputs.k_use.input), sizeof(int), 1, file); + fread(&(localInputs.k_menu.input), sizeof(int), 1, file); + fread(&(localInputs.k_pause.input), sizeof(int), 1, file); + fread(&(localInputs.k_accept.input), sizeof(int), 1, file); + fread(&(localInputs.k_decline.input), sizeof(int), 1, file); + fread(&(localInputs.k_delete.input), sizeof(int), 1, file); + fread(&(localInputs.k_menuNext.input), sizeof(int), 1, file); + fread(&(localInputs.k_menuPrev.input), sizeof(int), 1, file); fclose(file); } - + /* If lastTP exists, then use that. */ if ((file = fopen("lastTP.bin", "r"))) { char fnbuf[256]; fgets(fnbuf, 256, file); // get directory to texturepack - loadTexturePack(fnbuf); + loadTexturePack(fnbuf); fclose(file); } - tickCount = 0; + initPlayers(); initRecipes(); + initTrades(); + aptHook(&cookie, task_apt_hook, NULL); while (aptMainLoop()) { - ++tickCount; - hidScanInput(); - tickKeys(hidKeysHeld(), hidKeysDown()); - + if (paused == true) playMusic(&music_menu); if (quitGame) break; - if (initGame > 0) setupGame(initGame == 1 ? true : false); + if (initGame > 0 && --initGame==0) setupGame(); + if (initMPGame > 0 && --initMPGame==0) setupGameServer(); + if (initBGMap > 0 && --initBGMap==0) setupBGMap(); - if (currentMenu == 0) { - tick(); - sf2d_start_frame(GFX_TOP, GFX_LEFT); - - offsetX = xscr; - offsetY = yscr; - sf2d_draw_rectangle(0, 0, 400, 240, 0xFF0C0C0C); //RGBA8(12, 12, 12, 255)); //You might think "real" black would be better, but it actually looks better that way - renderLightsToStencil(); - - renderBackground(xscr, yscr); - renderEntities(player.x, player.y, &eManager); - renderPlayer(); - - resetStencilStuff(); - offsetX = 0; - offsetY = 0; - - if(shouldRenderDebug){ - sprintf(fpsstr, " FPS: %.0f, X:%d, Y:%d, E:%d", sf2d_get_fps(), player.x, player.y, eManager.lastSlot[currentLevel]); - drawText(fpsstr, 2, 225); - } - - sf2d_end_frame(); - - sf2d_start_frame(GFX_BOTTOM, GFX_LEFT); - if(!shouldRenderMap){ - sf2d_draw_texture(bottombg, 0, 0); - renderGui(); - } else { - renderZoomedMap(); - } - sf2d_end_frame(); + if (currentMenu == MENU_NONE) { + tickGame(); + renderGame(); } else { + //input scanning ingame is handled by the synchronizer + hidScanInput(); + tickKeys(&localInputs, hidKeysHeld(), hidKeysDown()); + tickMenu(currentMenu); renderMenu(currentMenu, xscr, yscr); } @@ -281,7 +228,11 @@ int main() { sf2d_swapbuffers(); } + stopMusic(); + + freeTrades(); freeRecipes(); + freePlayers(); freeLightBakes(); sf2d_free_texture(icons); @@ -290,9 +241,12 @@ int main() { sf2d_free_texture(minimap[2]); sf2d_free_texture(minimap[3]); sf2d_free_texture(minimap[4]); + sf2d_free_texture(minimap[5]); freeSounds(); + networkExit(); csndExit(); cfguExit(); + romfsExit(); sf2d_fini(); return 0; } diff --git a/source/minizip/crypt.h b/source/minizip/crypt.h old mode 100644 new mode 100755 index 2d69da4..622f4bc --- a/source/minizip/crypt.h +++ b/source/minizip/crypt.h @@ -1,9 +1,9 @@ /* crypt.h -- base code for crypt/uncrypt ZIPfile - Version 1.01h, December 28th, 2009 + Version 1.01e, February 12th, 2005 - Copyright (C) 1998-2009 Gilles Vollant + Copyright (C) 1998-2005 Gilles Vollant This code is a modified version of crypting code in Infozip distribution diff --git a/source/minizip/ioapi.c b/source/minizip/ioapi.c old mode 100644 new mode 100755 index 51a9058..f1bee23 --- a/source/minizip/ioapi.c +++ b/source/minizip/ioapi.c @@ -1,9 +1,9 @@ /* ioapi.c -- IO base function header for compress/uncompress .zip files using zlib + zip or unzip API - Version 1.01h, December 28th, 2009 + Version 1.01e, February 12th, 2005 - Copyright (C) 1998-2009 Gilles Vollant + Copyright (C) 1998-2005 Gilles Vollant */ #include @@ -141,8 +141,7 @@ long ZCALLBACK fseek_file_func (opaque, stream, offset, origin) default: return -1; } ret = 0; - if (fseek((FILE *)stream, offset, fseek_origin) != 0) - ret = -1; + fseek((FILE *)stream, offset, fseek_origin); return ret; } diff --git a/source/minizip/ioapi.h b/source/minizip/ioapi.h old mode 100644 new mode 100755 index 1dba776..7d457ba --- a/source/minizip/ioapi.h +++ b/source/minizip/ioapi.h @@ -1,9 +1,9 @@ /* ioapi.h -- IO base function header for compress/uncompress .zip files using zlib + zip or unzip API - Version 1.01h, December 28th, 2009 + Version 1.01e, February 12th, 2005 - Copyright (C) 1998-2009 Gilles Vollant + Copyright (C) 1998-2005 Gilles Vollant */ #ifndef _ZLIBIOAPI_H diff --git a/source/minizip/unzip.c b/source/minizip/unzip.c old mode 100644 new mode 100755 index 17d730d..9ad4766 --- a/source/minizip/unzip.c +++ b/source/minizip/unzip.c @@ -1,7 +1,7 @@ /* unzip.c -- IO for uncompress .zip files using zlib - Version 1.01h, December 28th, 2009 + Version 1.01e, February 12th, 2005 - Copyright (C) 1998-2009 Gilles Vollant + Copyright (C) 1998-2005 Gilles Vollant Read unzip.h for more info */ @@ -103,9 +103,6 @@ typedef struct { char *read_buffer; /* internal buffer for compressed data */ z_stream stream; /* zLib stream structure for inflate */ -#ifdef HAVE_BZIP2 - bz_stream bstream; /* bzLib stream structure for bziped */ -#endif uLong pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ uLong stream_initialised; /* flag set if stream structure is initialised*/ @@ -207,7 +204,7 @@ local int unzlocal_getShort (pzlib_filefunc_def,filestream,pX) uLong *pX; { uLong x ; - int i = 0; + int i; int err; err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); @@ -235,7 +232,7 @@ local int unzlocal_getLong (pzlib_filefunc_def,filestream,pX) uLong *pX; { uLong x ; - int i = 0; + int i; int err; err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); @@ -494,11 +491,8 @@ extern unzFile ZEXPORT unzOpen2 (path, pzlib_filefunc_def) s=(unz_s*)ALLOC(sizeof(unz_s)); - if (s!=NULL) - { - *s=us; - unzGoToFirstFile((unzFile)s); - } + *s=us; + unzGoToFirstFile((unzFile)s); return (unzFile)s; } @@ -614,12 +608,10 @@ local int unzlocal_GetCurrentFileInfoInternal (file, /* we check the magic */ if (err==UNZ_OK) - { if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) err=UNZ_ERRNO; else if (uMagic!=0x02014b50) err=UNZ_BADZIPFILE; - } if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK) err=UNZ_ERRNO; @@ -696,13 +688,10 @@ local int unzlocal_GetCurrentFileInfoInternal (file, uSizeRead = extraFieldBufferSize; if (lSeek!=0) - { if (ZSEEK(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) lSeek=0; else err=UNZ_ERRNO; - } - if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) if (ZREAD(s->z_filefunc, s->filestream,extraField,uSizeRead)!=uSizeRead) err=UNZ_ERRNO; @@ -724,13 +713,10 @@ local int unzlocal_GetCurrentFileInfoInternal (file, uSizeRead = commentBufferSize; if (lSeek!=0) - { if (ZSEEK(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) lSeek=0; else err=UNZ_ERRNO; - } - if ((file_info.size_file_comment>0) && (commentBufferSize>0)) if (ZREAD(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead) err=UNZ_ERRNO; @@ -991,12 +977,10 @@ local int unzlocal_CheckCurrentFileCoherencyHeader (s,piSizeVar, if (err==UNZ_OK) - { if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) err=UNZ_ERRNO; else if (uMagic!=0x04034b50) err=UNZ_BADZIPFILE; - } if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) err=UNZ_ERRNO; @@ -1013,9 +997,6 @@ local int unzlocal_CheckCurrentFileCoherencyHeader (s,piSizeVar, err=UNZ_BADZIPFILE; if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && -/* #ifdef HAVE_BZIP2 */ - (s->cur_file_info.compression_method!=Z_BZIP2ED) && -/* #endif */ (s->cur_file_info.compression_method!=Z_DEFLATED)) err=UNZ_BADZIPFILE; @@ -1130,9 +1111,6 @@ extern int ZEXPORT unzOpenCurrentFile3 (file, method, level, raw, password) } if ((s->cur_file_info.compression_method!=0) && -/* #ifdef HAVE_BZIP2 */ - (s->cur_file_info.compression_method!=Z_BZIP2ED) && -/* #endif */ (s->cur_file_info.compression_method!=Z_DEFLATED)) err=UNZ_BADZIPFILE; @@ -1146,34 +1124,6 @@ extern int ZEXPORT unzOpenCurrentFile3 (file, method, level, raw, password) pfile_in_zip_read_info->stream.total_out = 0; - if ((s->cur_file_info.compression_method==Z_BZIP2ED) && - (!raw)) - { -#ifdef HAVE_BZIP2 - pfile_in_zip_read_info->bstream.bzalloc = (void *(*) (void *, int, int))0; - pfile_in_zip_read_info->bstream.bzfree = (free_func)0; - pfile_in_zip_read_info->bstream.opaque = (voidpf)0; - pfile_in_zip_read_info->bstream.state = (voidpf)0; - - pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; - pfile_in_zip_read_info->stream.zfree = (free_func)0; - pfile_in_zip_read_info->stream.opaque = (voidpf)0; - pfile_in_zip_read_info->stream.next_in = (voidpf)0; - pfile_in_zip_read_info->stream.avail_in = 0; - - err=BZ2_bzDecompressInit(&pfile_in_zip_read_info->bstream, 0, 0); - if (err == Z_OK) - pfile_in_zip_read_info->stream_initialised=Z_BZIP2ED; - else - { - TRYFREE(pfile_in_zip_read_info); - return err; - } -#else - pfile_in_zip_read_info->raw=1; -#endif - } - else if ((s->cur_file_info.compression_method==Z_DEFLATED) && (!raw)) { @@ -1185,7 +1135,7 @@ extern int ZEXPORT unzOpenCurrentFile3 (file, method, level, raw, password) err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); if (err == Z_OK) - pfile_in_zip_read_info->stream_initialised=Z_DEFLATED; + pfile_in_zip_read_info->stream_initialised=1; else { TRYFREE(pfile_in_zip_read_info); @@ -1213,8 +1163,6 @@ extern int ZEXPORT unzOpenCurrentFile3 (file, method, level, raw, password) s->pfile_in_zip_read = pfile_in_zip_read_info; - s->encrypted = 0; - # ifndef NOUNCRYPT if (password != NULL) { @@ -1386,53 +1334,6 @@ extern int ZEXPORT unzReadCurrentFile (file, buf, len) iRead += uDoCopy; } else - if (pfile_in_zip_read_info->compression_method==Z_BZIP2ED) - { -#ifdef HAVE_BZIP2 - uLong uTotalOutBefore,uTotalOutAfter; - const Bytef *bufBefore; - uLong uOutThis; - - pfile_in_zip_read_info->bstream.next_in = pfile_in_zip_read_info->stream.next_in; - pfile_in_zip_read_info->bstream.avail_in = pfile_in_zip_read_info->stream.avail_in; - pfile_in_zip_read_info->bstream.total_in_lo32 = pfile_in_zip_read_info->stream.total_in; - pfile_in_zip_read_info->bstream.total_in_hi32 = 0; - pfile_in_zip_read_info->bstream.next_out = pfile_in_zip_read_info->stream.next_out; - pfile_in_zip_read_info->bstream.avail_out = pfile_in_zip_read_info->stream.avail_out; - pfile_in_zip_read_info->bstream.total_out_lo32 = pfile_in_zip_read_info->stream.total_out; - pfile_in_zip_read_info->bstream.total_out_hi32 = 0; - - uTotalOutBefore = pfile_in_zip_read_info->bstream.total_out_lo32; - bufBefore = pfile_in_zip_read_info->bstream.next_out; - - err=BZ2_bzDecompress(&pfile_in_zip_read_info->bstream); - - uTotalOutAfter = pfile_in_zip_read_info->bstream.total_out_lo32; - uOutThis = uTotalOutAfter-uTotalOutBefore; - - pfile_in_zip_read_info->crc32 = - crc32(pfile_in_zip_read_info->crc32,bufBefore, - (uInt)(uOutThis)); - - pfile_in_zip_read_info->rest_read_uncompressed -= - uOutThis; - - iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); - - pfile_in_zip_read_info->stream.next_in = pfile_in_zip_read_info->bstream.next_in; - pfile_in_zip_read_info->stream.avail_in = pfile_in_zip_read_info->bstream.avail_in; - pfile_in_zip_read_info->stream.total_in = pfile_in_zip_read_info->bstream.total_in_lo32; - pfile_in_zip_read_info->stream.next_out = pfile_in_zip_read_info->bstream.next_out; - pfile_in_zip_read_info->stream.avail_out = pfile_in_zip_read_info->bstream.avail_out; - pfile_in_zip_read_info->stream.total_out = pfile_in_zip_read_info->bstream.total_out_lo32; - - if (err==BZ_STREAM_END) - return (iRead==0) ? UNZ_EOF : iRead; - if (err!=BZ_OK) - break; -#endif - } - else { uLong uTotalOutBefore,uTotalOutAfter; const Bytef *bufBefore; @@ -1611,12 +1512,8 @@ extern int ZEXPORT unzCloseCurrentFile (file) TRYFREE(pfile_in_zip_read_info->read_buffer); pfile_in_zip_read_info->read_buffer = NULL; - if (pfile_in_zip_read_info->stream_initialised == Z_DEFLATED) + if (pfile_in_zip_read_info->stream_initialised) inflateEnd(&pfile_in_zip_read_info->stream); -#ifdef HAVE_BZIP2 - else if (pfile_in_zip_read_info->stream_initialised == Z_BZIP2ED) - BZ2_bzDecompressEnd(&pfile_in_zip_read_info->bstream); -#endif pfile_in_zip_read_info->stream_initialised = 0; TRYFREE(pfile_in_zip_read_info); @@ -1637,6 +1534,7 @@ extern int ZEXPORT unzGetGlobalComment (file, szComment, uSizeBuf) char *szComment; uLong uSizeBuf; { + int err=UNZ_OK; unz_s* s; uLong uReadThis ; if (file==NULL) @@ -1669,7 +1567,7 @@ extern uLong ZEXPORT unzGetOffset (file) unz_s* s; if (file==NULL) - return 0; + return UNZ_PARAMERROR; s=(unz_s*)file; if (!s->current_file_ok) return 0; diff --git a/source/minizip/unzip.h b/source/minizip/unzip.h old mode 100644 new mode 100755 index 94c1b69..b247937 --- a/source/minizip/unzip.h +++ b/source/minizip/unzip.h @@ -1,7 +1,7 @@ /* unzip.h -- IO for uncompress .zip files using zlib - Version 1.01h, December 28th, 2009 + Version 1.01e, February 12th, 2005 - Copyright (C) 1998-2009 Gilles Vollant + Copyright (C) 1998-2005 Gilles Vollant This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g WinZip, InfoZip tools and compatible. @@ -57,12 +57,6 @@ extern "C" { #include "ioapi.h" #endif -#ifdef HAVE_BZIP2 -#include "bzlib.h" -#endif - -#define Z_BZIP2ED 12 - #if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) /* like the STRICT of WIN32, we define a pointer that cannot be converted from (void*) without cast */ diff --git a/source/minizip/zip.c b/source/minizip/zip.c old mode 100644 new mode 100755 index 1451d61..7fbe002 --- a/source/minizip/zip.c +++ b/source/minizip/zip.c @@ -1,10 +1,10 @@ /* zip.c -- IO on .zip files using zlib - Version 1.01h, December 28th, 2009 + Version 1.01e, February 12th, 2005 27 Dec 2004 Rolf Kalbermatter Modification to zipOpen2 to support globalComment retrieval. - Copyright (C) 1998-2009 Gilles Vollant + Copyright (C) 1998-2005 Gilles Vollant Read zip.h for more info */ @@ -320,9 +320,9 @@ local uLong ziplocal_TmzDateToDosDate(ptm,dosDate) uLong dosDate; { uLong year = (uLong)ptm->tm_year; - if (year>=1980) + if (year>1980) year-=1980; - else if (year>=80) + else if (year>80) year-=80; return (uLong) (((ptm->tm_mday) + (32 * (ptm->tm_mon+1)) + (512 * year)) << 16) | @@ -373,7 +373,7 @@ local int ziplocal_getShort (pzlib_filefunc_def,filestream,pX) uLong *pX; { uLong x ; - int i = 0; + int i; int err; err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); @@ -401,7 +401,7 @@ local int ziplocal_getLong (pzlib_filefunc_def,filestream,pX) uLong *pX; { uLong x ; - int i = 0; + int i; int err; err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); @@ -432,69 +432,67 @@ local int ziplocal_getLong (pzlib_filefunc_def,filestream,pX) /* Locate the Central directory of a zipfile (at the end, just before the global comment) - Fix from Riccardo Cohen */ local uLong ziplocal_SearchCentralDir OF(( const zlib_filefunc_def* pzlib_filefunc_def, voidpf filestream)); local uLong ziplocal_SearchCentralDir(pzlib_filefunc_def,filestream) - const zlib_filefunc_def* pzlib_filefunc_def; - voidpf filestream; + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; { - unsigned char* buf; - uLong uSizeFile; - uLong uBackRead; - uLong uMaxBack=0xffff; /* maximum size of global comment */ - uLong uPosFound=0; + unsigned char* buf; + uLong uSizeFile; + uLong uBackRead; + uLong uMaxBack=0xffff; /* maximum size of global comment */ + uLong uPosFound=0; - if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) - return 0; + if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; - uSizeFile = ZTELL(*pzlib_filefunc_def,filestream); + uSizeFile = ZTELL(*pzlib_filefunc_def,filestream); - if (uMaxBack>uSizeFile) - uMaxBack = uSizeFile; + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; - buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); - if (buf==NULL) - return 0; + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; - uBackRead = 4; - while (uBackReaduMaxBack) - uBackRead = uMaxBack; - else - uBackRead+=BUFREADCOMMENT; - uReadPos = uSizeFile-uBackRead ; + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; - uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? - (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); - if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) - break; + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); + if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; - if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) - break; + if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; - for (i=(int)uReadSize-3; (i--)>0;) - if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && - ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) - { - uPosFound = uReadPos+i; - break; - } + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } - if (uPosFound!=0) - break; - } - TRYFREE(buf); - return uPosFound; + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; } - #endif /* !NO_ADDFILEINEXISTINGZIP*/ /************************************************************/ @@ -523,8 +521,6 @@ extern zipFile ZEXPORT zipOpen2 (pathname, append, globalcomment, pzlib_filefunc if (ziinit.filestream == NULL) return NULL; - if (append == APPEND_STATUS_CREATEAFTER) - ZSEEK(ziinit.z_filefunc,ziinit.filestream,0,SEEK_END); ziinit.begin_pos = ZTELL(ziinit.z_filefunc,ziinit.filestream); ziinit.in_opened_file_inzip = 0; ziinit.ci.stream_initialised = 0; @@ -562,10 +558,9 @@ extern zipFile ZEXPORT zipOpen2 (pathname, append, globalcomment, pzlib_filefunc uLong size_comment; central_pos = ziplocal_SearchCentralDir(&ziinit.z_filefunc,ziinit.filestream); -/* disable to allow appending to empty ZIP archive if (central_pos==0) err=ZIP_ERRNO; -*/ + if (ZSEEK(ziinit.z_filefunc, ziinit.filestream, central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) err=ZIP_ERRNO; @@ -620,7 +615,7 @@ extern zipFile ZEXPORT zipOpen2 (pathname, append, globalcomment, pzlib_filefunc if (size_comment>0) { - ziinit.globalcomment = (char*)ALLOC(size_comment+1); + ziinit.globalcomment = ALLOC(size_comment+1); if (ziinit.globalcomment) { size_comment = ZREAD(ziinit.z_filefunc, ziinit.filestream,ziinit.globalcomment,size_comment); @@ -692,12 +687,12 @@ extern zipFile ZEXPORT zipOpen (pathname, append) return zipOpen2(pathname,append,NULL,NULL); } -extern int ZEXPORT zipOpenNewFileInZip4 (file, filename, zipfi, +extern int ZEXPORT zipOpenNewFileInZip3 (file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, comment, method, level, raw, windowBits, memLevel, strategy, - password, crcForCrypting, versionMadeBy, flagBase) + password, crcForCrypting) zipFile file; const char* filename; const zip_fileinfo* zipfi; @@ -714,8 +709,6 @@ extern int ZEXPORT zipOpenNewFileInZip4 (file, filename, zipfi, int strategy; const char* password; uLong crcForCrypting; - uLong versionMadeBy; - uLong flagBase; { zip_internal* zi; uInt size_filename; @@ -762,7 +755,7 @@ extern int ZEXPORT zipOpenNewFileInZip4 (file, filename, zipfi, else zi->ci.dosDate = ziplocal_TmzDateToDosDate(&zipfi->tmz_date,zipfi->dosDate); } - zi->ci.flag = flagBase; + zi->ci.flag = 0; if ((level==8) || (level==9)) zi->ci.flag |= 2; if ((level==2)) @@ -785,7 +778,7 @@ extern int ZEXPORT zipOpenNewFileInZip4 (file, filename, zipfi, ziplocal_putValue_inmemory(zi->ci.central_header,(uLong)CENTRALHEADERMAGIC,4); /* version info */ - ziplocal_putValue_inmemory(zi->ci.central_header+4,(uLong)versionMadeBy,2); + ziplocal_putValue_inmemory(zi->ci.central_header+4,(uLong)VERSIONMADEBY,2); ziplocal_putValue_inmemory(zi->ci.central_header+6,(uLong)20,2); ziplocal_putValue_inmemory(zi->ci.central_header+8,(uLong)zi->ci.flag,2); ziplocal_putValue_inmemory(zi->ci.central_header+10,(uLong)zi->ci.method,2); @@ -864,7 +857,6 @@ extern int ZEXPORT zipOpenNewFileInZip4 (file, filename, zipfi, zi->ci.stream.next_out = zi->ci.buffered_data; zi->ci.stream.total_in = 0; zi->ci.stream.total_out = 0; - zi->ci.stream.data_type = Z_BINARY; if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) { @@ -920,46 +912,14 @@ extern int ZEXPORT zipOpenNewFileInZip2(file, filename, zipfi, int level; int raw; { - return zipOpenNewFileInZip4 (file, filename, zipfi, + return zipOpenNewFileInZip3 (file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, comment, method, level, raw, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, - NULL, 0, VERSIONMADEBY, 0); + NULL, 0); } -extern int ZEXPORT zipOpenNewFileInZip3 (file, filename, zipfi, - extrafield_local, size_extrafield_local, - extrafield_global, size_extrafield_global, - comment, method, level, raw, - windowBits, memLevel, strategy, - password, crcForCrypting) - zipFile file; - const char* filename; - const zip_fileinfo* zipfi; - const void* extrafield_local; - uInt size_extrafield_local; - const void* extrafield_global; - uInt size_extrafield_global; - const char* comment; - int method; - int level; - int raw; - int windowBits; - int memLevel; - int strategy; - const char* password; - uLong crcForCrypting; -{ - return zipOpenNewFileInZip4 (file, filename, zipfi, - extrafield_local, size_extrafield_local, - extrafield_global, size_extrafield_global, - comment, method, level, raw, - windowBits, memLevel, strategy, - password, crcForCrypting, VERSIONMADEBY, 0); -} - - extern int ZEXPORT zipOpenNewFileInZip (file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, @@ -975,12 +935,10 @@ extern int ZEXPORT zipOpenNewFileInZip (file, filename, zipfi, int method; int level; { - return zipOpenNewFileInZip4 (file, filename, zipfi, + return zipOpenNewFileInZip2 (file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, - comment, method, level, 0, - -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, - NULL, 0, VERSIONMADEBY, 0); + comment, method, level, 0); } local int zipFlushWriteBuffer(zi) @@ -1020,9 +978,9 @@ extern int ZEXPORT zipWriteInFileInZip (file, buf, len) if (zi->in_opened_file_inzip == 0) return ZIP_PARAMERROR; - zi->ci.stream.next_in = (Bytef*)buf; + zi->ci.stream.next_in = (void*)buf; zi->ci.stream.avail_in = len; - zi->ci.crc32 = crc32(zi->ci.crc32,buf,(uInt)len); + zi->ci.crc32 = crc32(zi->ci.crc32,buf,len); while ((err==ZIP_OK) && (zi->ci.stream.avail_in>0)) { @@ -1112,9 +1070,7 @@ extern int ZEXPORT zipCloseFileInZipRaw (file, uncompressed_size, crc32) if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) { - int tmp_err=deflateEnd(&zi->ci.stream); - if (err == ZIP_OK) - err = tmp_err; + err=deflateEnd(&zi->ci.stream); zi->ci.stream_initialised = 0; } @@ -1217,7 +1173,7 @@ extern int ZEXPORT zipClose (file, global_comment) ldi = ldi->next_datablock; } } - free_linkedlist(&(zi->central_dir)); + free_datablock(zi->central_dir.first_block); if (err==ZIP_OK) /* Magic End */ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)ENDHEADERMAGIC,4); diff --git a/source/minizip/zip.h b/source/minizip/zip.h old mode 100644 new mode 100755 index 39215ca..acacce8 --- a/source/minizip/zip.h +++ b/source/minizip/zip.h @@ -1,7 +1,7 @@ /* zip.h -- IO for compress .zip files using zlib - Version 1.01h, December 28th, 2009 + Version 1.01e, February 12th, 2005 - Copyright (C) 1998-2009 Gilles Vollant + Copyright (C) 1998-2005 Gilles Vollant This unzip package allow creates .ZIP file, compatible with PKZip 2.04g WinZip, InfoZip tools and compatible. @@ -191,7 +191,8 @@ extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file, int memLevel, int strategy, const char* password, - uLong crcForCrypting)); + uLong crcForCtypting)); + /* Same than zipOpenNewFileInZip2, except windowBits,memLevel,,strategy : see parameter strategy in deflateInit2 @@ -199,29 +200,6 @@ extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file, crcForCtypting : crc of file to compress (needed for crypting) */ -extern int ZEXPORT zipOpenNewFileInZip4 OF((zipFile file, - const char* filename, - const zip_fileinfo* zipfi, - const void* extrafield_local, - uInt size_extrafield_local, - const void* extrafield_global, - uInt size_extrafield_global, - const char* comment, - int method, - int level, - int raw, - int windowBits, - int memLevel, - int strategy, - const char* password, - uLong crcForCrypting, - uLong versionMadeBy, - uLong flagBase)); -/* - Same than zipOpenNewFileInZip4, except - versionMadeBy : value for Version made by field - flag : value for flag field (compression level info will be added) - */ extern int ZEXPORT zipWriteInFileInZip OF((zipFile file, const void* buf, diff --git a/source/texturepack.c b/source/texturepack.c old mode 100644 new mode 100755 index a79f47b..9398c10 --- a/source/texturepack.c +++ b/source/texturepack.c @@ -1,166 +1,100 @@ #include "texturepack.h" -#define dir_delimter '/' -#define MAX_FILENAME 256 -#define READ_SIZE 9216 +#include "ZipHelper.h" +#define MAX_FILENAME 256 + +bool texturepackUseDefaultIcons = true; +bool texturepackUseDefaultPlayer = true; +bool texturepackUseDefaultFont = true; +bool texturepackUseDefaultBottom = true; void toLowerString(char * str){ - int i; - for (i = 0; str[i] != '\0'; i++)str[i] = (char)tolower((unsigned char)str[i]); + int i; + for (i = 0; str[i] != '\0'; i++)str[i] = (char)tolower((unsigned char)str[i]); } int getTexturePackComment(char * filename, char * cmmtBuf){ - // Open the zip file - unzFile *zipfile = unzOpen(filename); - if ( zipfile == NULL ) return 1; // Error: ZipFile could not be opened. - - // Get info about the zip file - unz_global_info global_info; - if (unzGetGlobalInfo(zipfile, &global_info ) != UNZ_OK ) - { - unzClose( zipfile ); - return 2; // Error: Could not read global info - } - - unzGetGlobalComment(zipfile, cmmtBuf, 58); + // Open the zip file + unzFile *zipfile = unzOpen(filename); + if ( zipfile == NULL ) return 1; // Error: ZipFile could not be opened. - unzClose( zipfile ); + // Get info about the zip file + unz_global_info global_info; + if (unzGetGlobalInfo(zipfile, &global_info ) != UNZ_OK ) + { + unzClose( zipfile ); + return 2; // Error: Could not read global info + } - return 0; + unzGetGlobalComment(zipfile, cmmtBuf, 58); + + unzClose( zipfile ); + + return 0; } -int loadTexturePack(char * filename){ - - bool useDefaultIcons = true; - bool useDefaultFont = true; - bool useDefaultBottom = true; +int loadTexture(char * filename) { + char lowerFilename[MAX_FILENAME]; + strcpy(lowerFilename,filename); + toLowerString(lowerFilename); - // Open the zip file - unzFile *zipfile = unzOpen(filename); - if ( zipfile == NULL ) return 1; // Error: ZipFile could not be opened. - - // Get info about the zip file - unz_global_info global_info; - if (unzGetGlobalInfo(zipfile, &global_info ) != UNZ_OK ) - { - unzClose( zipfile ); - return 2; // Error: Could not read global info - } + if(strcmp(lowerFilename, "icons.png") == 0){ + if(sfil_load_PNG_file(filename, SF2D_PLACE_RAM) == NULL){ + return 0; + } - // Buffer to hold data read from the zip file. - char read_buffer[ READ_SIZE ]; + icons = sfil_load_PNG_file(filename, SF2D_PLACE_RAM); + reloadColors(); - // Loop to extract all files - uLong i; - for ( i = 0; i < global_info.number_entry; ++i ) - { - // Get info about current file. - unz_file_info file_info; - char filename[ MAX_FILENAME ]; - if (unzGetCurrentFileInfo(zipfile,&file_info,filename,MAX_FILENAME,NULL, 0, NULL, 0 ) != UNZ_OK ){ - unzClose( zipfile ); - return 3; // Error: Could not read file info - } + texturepackUseDefaultIcons = false; + } else if(strcmp(lowerFilename, "player.png") == 0){ + if(sfil_load_PNG_file(filename, SF2D_PLACE_RAM) == NULL){ + return 0; + } - // Check if this entry is NOT a directory or file. - const size_t filename_length = strlen( filename ); - if ( filename[ filename_length-1 ] != dir_delimter ){ - if ( unzOpenCurrentFile( zipfile ) != UNZ_OK ) - { - unzClose( zipfile ); - return 4; - } + playerSprites = sfil_load_PNG_file(filename, SF2D_PLACE_RAM); - // Open a file to write out the data. - FILE * out = fopen(filename, "wb" ); - if ( out == NULL ) - { - unzCloseCurrentFile( zipfile ); - unzClose( zipfile ); - return 5; - } + texturepackUseDefaultPlayer = false; + } else if(strcmp(lowerFilename, "font.png") == 0){ + if(sfil_load_PNG_file(filename, SF2D_PLACE_RAM) == NULL){ + return 0; + } - int error = UNZ_OK; - do - { - error = unzReadCurrentFile( zipfile, read_buffer, READ_SIZE ); - if ( error < 0 ) - { - //printf( "error %d\n", error ); - unzCloseCurrentFile( zipfile ); - unzClose( zipfile ); - return 6; - } + font = sfil_load_PNG_file(filename, SF2D_PLACE_RAM); - // Write data to file. - if ( error > 0 ) - { - fwrite( read_buffer, error, 1, out ); // You should check return of fwrite... - } - } while ( error > 0 ); + texturepackUseDefaultFont = false; + } else if(strcmp(lowerFilename, "bottombg.png") == 0){ + if(sfil_load_PNG_file(filename, SF2D_PLACE_RAM) == NULL){ + return 0; + } - fclose(out); - - char lowerFilename[MAX_FILENAME]; - strcpy(lowerFilename,filename); - toLowerString(lowerFilename); - - if(strcmp(lowerFilename,"icons.png") == 0){ - if(sfil_load_PNG_file(filename, SF2D_PLACE_RAM) == NULL){ - unzCloseCurrentFile( zipfile ); - unzClose( zipfile ); - return 7; - } - icons = sfil_load_PNG_file(filename, SF2D_PLACE_RAM); - - reloadColors(); - - useDefaultIcons = false; + bottombg = sfil_load_PNG_file(filename, SF2D_PLACE_RAM); - } else if(strcmp(lowerFilename,"font.png") == 0){ - if(sfil_load_PNG_file(filename, SF2D_PLACE_RAM) == NULL){ - unzCloseCurrentFile( zipfile ); - unzClose( zipfile ); - return 7; - } - font = sfil_load_PNG_file(filename, SF2D_PLACE_RAM); - useDefaultFont = false; - } else if(strcmp(lowerFilename,"bottombg.png") == 0){ - if(sfil_load_PNG_file(filename, SF2D_PLACE_RAM) == NULL){ - unzCloseCurrentFile( zipfile ); - unzClose( zipfile ); - return 7; - } - bottombg = sfil_load_PNG_file(filename, SF2D_PLACE_RAM); - useDefaultBottom = false; - } - - remove(filename); - } + texturepackUseDefaultBottom = false; + } - unzCloseCurrentFile( zipfile ); - - // Go the the next entry listed in the zip file. - if ( ( i+1 ) < global_info.number_entry ) - { - if ( unzGoToNextFile( zipfile ) != UNZ_OK ) - { - unzClose( zipfile ); - return 7; - } - } - } - - if(useDefaultIcons){ - icons = sfil_load_PNG_buffer(icons2_png, SF2D_PLACE_RAM); - reloadColors(); - } - if(useDefaultFont) font = sfil_load_PNG_buffer(Font_png, SF2D_PLACE_RAM); - if(useDefaultBottom) bottombg = sfil_load_PNG_buffer(bottombg_png, SF2D_PLACE_RAM); - - unzClose( zipfile ); - - return 0; + return 0; +} + +int loadTexturePack(char * filename) { + texturepackUseDefaultIcons = true; + texturepackUseDefaultPlayer = true; + texturepackUseDefaultFont = true; + texturepackUseDefaultBottom = true; + + if(unzipAndLoad(filename, &loadTexture, NULL, ZIPHELPER_CLEANUP_FILES)!=0) { + return 1; + } + + if(texturepackUseDefaultIcons){ + icons = sfil_load_PNG_buffer(icons_png, SF2D_PLACE_RAM); + reloadColors(); + } + if(texturepackUseDefaultPlayer) playerSprites = sfil_load_PNG_buffer(player_png, SF2D_PLACE_RAM); + if(texturepackUseDefaultFont) font = sfil_load_PNG_buffer(Font_png, SF2D_PLACE_RAM); + if(texturepackUseDefaultBottom) bottombg = sfil_load_PNG_buffer(bottombg_png, SF2D_PLACE_RAM); + + return 0; } diff --git a/source/texturepack.h b/source/texturepack.h old mode 100644 new mode 100755 index 565d1cc..b0f61ce --- a/source/texturepack.h +++ b/source/texturepack.h @@ -1,6 +1,5 @@ #pragma once -#include -#include + #include #include #include