Compare commits

..

56 commits

Author SHA1 Message Date
tognee
43ce7184e1 fixed build script 2019-09-18 01:28:50 +02:00
tognee
7b42d7dc1d Removed powerglove from spawn as now it works with button 2019-09-18 00:52:28 +02:00
tognee
873c7bc06e Added items, functionality next 2019-09-18 00:52:06 +02:00
tognee
80c95de053 Updated README.md 2019-09-18 00:41:11 +02:00
tognee
7bc9c274de Added in head crafting and pickup button 2019-09-18 00:39:14 +02:00
tognee
24460ee48c Added controlls pickup and use 2019-09-18 00:25:42 +02:00
tognee
52ab48ba55 Updated Makefile 2019-09-18 00:12:11 +02:00
tognee
a5ea06a928 Cleaning up stuff 2019-09-18 00:09:46 +02:00
ElijahZAwesome
5423be67ca swapping pcs so ill just commit this~ 2018-02-11 15:32:32 -06:00
ElijahZAwesome
4f0e3c4dd4 i did a stupid 2018-02-09 21:59:10 -06:00
ElijahZAwesome
7402ac40fb improved sound encoding, thanks nop90 2018-02-09 16:18:49 -06:00
ElijahZAwesome
06b18f3c64 Fixed player sprites 2018-02-08 17:16:11 -06:00
ElijahZAwesome
cd4681780c Yay 'optimization' 2018-02-08 14:06:04 -06:00
ElijahZAwesome
2870e5c55a unrip 3ds build, im just an idiot. 2018-02-08 13:35:17 -06:00
ElijahZAwesome
0cd0c75a90 RIP 3ds build, addeeed all dependencies.
dont ask me why 3ds doesnt work, it just wont build. idfk
2018-02-08 13:31:50 -06:00
ElijahZAwesome
a2ea96cef8 hecking--- 2018-02-08 09:52:30 -06:00
ElijahZAwesome
6b4a849e5c holy shit lol romfs sucks 2018-02-08 08:24:36 -06:00
ElijahZAwesome
1ed878494f still no sound yet, but progress 2018-02-07 21:29:02 -06:00
ElijahZAwesome
4120d4b8ec reimplemented all the potions, having another swing at romfs tommorow 2018-02-06 23:12:49 -06:00
Elijah Bansley
f748519d3e
Update _config.yml 2018-02-06 22:27:37 -06:00
Elijah Bansley
af94b53d34 Set theme jekyll-theme-cayman 2018-02-06 22:23:26 -06:00
ElijahZAwesome
cb3ae7ff5f possibly gh-pages site 2018-02-06 22:22:48 -06:00
ElijahZAwesome
bf50acf31d ughhhhhhhhhh 2018-02-06 22:16:29 -06:00
Elijah
0ea343ce70 Merge branch 'master' of https://github.com/ElijahZAwesome/Minicraft3DS 2018-02-06 21:48:30 -06:00
Elijah
cf479ffe57 uhm 2018-02-06 21:48:27 -06:00
ElijahZAwesome
e98ed24890 added strength back 2018-02-06 21:46:59 -06:00
Elijah Bansley
71363476cf
Update README.md 2018-02-06 21:39:12 -06:00
Elijah Bansley
b37a6ca781
Update README.md 2018-02-06 21:38:42 -06:00
ElijahZAwesome
d55122b707 fixed some more shit, not ready yet 2018-02-06 18:13:56 -06:00
ElijahZAwesome
edecea1055 fixed remaining merge errors, maybe still dont build this yet 2018-02-06 17:22:10 -06:00
ElijahZAwesome
1e3fb61d76 Merged 1.5 update from Andre, maybe. 2018-02-06 14:08:27 -06:00
André Schweiger
75ec2ee884 Fix Player Sprite 2018-02-06 14:31:45 +01:00
André Schweiger
1e1ddcf994 Change Join Menu to only display host player name 2018-02-05 19:53:03 +01:00
André Schweiger
b7e5af60bf Fix README 2018-02-05 19:36:32 +01:00
André Schweiger
2d1f4831bc Update README 2018-02-05 19:29:51 +01:00
André Schweiger
265a322535 Allow to exit Game when a connection drops 2018-02-05 19:06:48 +01:00
Andre Schweiger
4a4f1ecb0e Small Fixes 2018-02-04 22:34:00 +01:00
Andre Schweiger
64f54d3397 Update Version to 1.5.1 2018-02-04 21:04:01 +01:00
Andre Schweiger
6513c0d1fa Fix Multiplayer with more than 2 people
At least on Citra and quite hacky
Join Menu still shows wrong player count, but this affects nothing
2018-02-04 21:02:16 +01:00
Andre Schweiger
d2f7796b79 Version 1.5.0
Big Refactor
2018-02-04 18:05:43 +01:00
ElijahZAwesome
e54122951d lil' more armor shit, attempting to put music into ROMFS 2018-01-28 08:27:51 -06:00
ElijahZAwesome
22fdd7acc7 lil' more work on armor, nothing shouuld be broke and stuff should build, but armor isnt ready yet. 2018-01-26 15:52:38 -06:00
Elijah
ddbb771e89 added potion saving for reals this time
dont build this commit cause workin on something
2018-01-26 08:31:09 -06:00
Elijah
2a368a81c0 friggin makefile 2018-01-26 07:28:04 -06:00
ElijahZAwesome
f03c4bc60d Merge branch 'master' of https://github.com/ElijahZAwesome/Minicraft3DS 2018-01-26 07:23:31 -06:00
ElijahZAwesome
a20f960f35 Added potion saving and stuff. forgot about that 2018-01-26 07:23:27 -06:00
Elijah
1d240a9dad added mac build support 2018-01-25 00:29:05 -06:00
Elijah Bansley
06408ea957
Update README.md 2018-01-24 22:41:54 -06:00
ElijahZAwesome
6370ea9014 Sound update stuff 2018-01-23 17:58:08 -06:00
Elijah Bansley
a2aa5c428d
Update README.md 2018-01-22 20:28:03 -06:00
Elijah Bansley
8407e92e8a
Update README.md 2018-01-22 20:14:05 -06:00
Elijah Bansley
718e2b876d
Update README.md 2018-01-22 18:37:56 -06:00
ElijahZAwesome
24567827c2 Regen potion and stuff, other little things 2018-01-22 18:37:15 -06:00
ElijahZAwesome
5e06ba05c7 changed makefile 2018-01-22 17:09:17 -06:00
ElijahZAwesome
960982e4ab Finished sped potion, began work on regen potion and brewing tutorial 2018-01-22 17:07:55 -06:00
ElijahZAwesome
f34ebde15b Potions Update 2018-01-22 02:32:12 -06:00
78 changed files with 9264 additions and 6938 deletions

0
.gitattributes vendored Normal file → Executable file
View file

1
.gitignore vendored Normal file → Executable file
View file

@ -58,4 +58,3 @@ build
.project .project
# Copyrighted Executables # Copyrighted Executables
*.exe

22
Makefile Normal file → Executable file
View file

@ -27,14 +27,17 @@ include $(DEVKITARM)/3ds_rules
# - <libctru folder>/default_icon.png # - <libctru folder>/default_icon.png
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
TARGET := result/Minicraft3DS TARGET := result/Minicraft3DS
ICON_TARGET := icons-banners/icon
RESULT := result
BUILD := build BUILD := build
SOURCES := source source/minizip SOURCES := source source/minizip
DATA := data DATA := data
INCLUDES := include INCLUDES := include
ROMFS := romfs
APP_TITLE := Minicraft 3DS APP_TITLE := Minicraft 3DS
APP_DESCRIPTION := Minicraft was originally created by Markus "Notch" Perrson. Ported to the 3DS by Davideesk. APP_DESCRIPTION := Originally created by Notch.
APP_AUTHOR := Davideesk/andre111/ElijahZAwesome APP_AUTHOR := Davideesk/andre111/ElijahZAwesome/tognee
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
# options for code generation # options for code generation
@ -106,13 +109,7 @@ export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
ifeq ($(strip $(ICON)),) ifeq ($(strip $(ICON)),)
icons := $(wildcard *.png) icons := $(wildcard *.png)
ifneq (,$(findstring $(TARGET).png,$(icons))) export APP_ICON := $(TOPDIR)/$(ICON_TARGET).png
export APP_ICON := $(TOPDIR)/$(TARGET).png
else
ifneq (,$(findstring icons-banners/icon.png,$(icons)))
export APP_ICON := $(TOPDIR)/icons-banners/icon.png
endif
endif
else else
export APP_ICON := $(TOPDIR)/$(ICON) export APP_ICON := $(TOPDIR)/$(ICON)
endif endif
@ -121,12 +118,17 @@ ifeq ($(strip $(NO_SMDH)),)
export _3DSXFLAGS += --smdh=$(CURDIR)/$(TARGET).smdh export _3DSXFLAGS += --smdh=$(CURDIR)/$(TARGET).smdh
endif endif
ifneq ($(ROMFS),)
export _3DSXFLAGS += --romfs=$(CURDIR)/$(ROMFS)
endif
.PHONY: $(BUILD) clean all .PHONY: $(BUILD) clean all
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
all: $(BUILD) all: $(BUILD)
$(BUILD): $(BUILD):
@test -d $(RESULT) || mkdir $(RESULT)
@[ -d $@ ] || mkdir -p $@ @[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
@ -143,7 +145,7 @@ cci: $(TARGET)-strip.elf
@echo "built ... Minicraft3DS.3ds" @echo "built ... Minicraft3DS.3ds"
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
cia: $(TARGET)-strip.elf cia: $(TARGET)-strip.elf
@makerom -f cia -o $(TARGET).cia -elf $(TARGET)-strip.elf -rsf resources/$(TARGET).rsf -exefslogo -target t @makerom -f cia -o $(TARGET).cia -elf $(TARGET)-strip.elf -rsf icons-banners/$(TARGET).rsf -exefslogo -target t
@echo "built ... Minicraft3DS.cia" @echo "built ... Minicraft3DS.cia"
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
send: $(BUILD) send: $(BUILD)

73
README.md Normal file → Executable file
View file

@ -1,37 +1,78 @@
# Minicraft3DS # 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: ----------
make by GNUWin32: http://gnuwin32.sourceforge.net/packages/make.htm (Should probably work, you may have to add it to PATH) **Download:**
ctrulib by smea: https://github.com/smealum/ctrulib If you just want to download the game prebuilt check the releases tab in Github:
//TODO
For building the game yourself look below.
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/ (Pst, pro tip: get this from the link below instead! I'ts much easier!) **Dependencies:**
Then make sure to have libpng and libjpeg installed, I recommend installing them with this: https://github.com/devkitPro/3ds_portlibs For building and installing the dependencies look below.
# Building 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/
As well as the dependencies, put bannertool, and makerom either in your path or in the directory. Then simply run build.bat and everything will be there.
# Version ----------
Current Version: Version 1.3
# License **Building:**
You can do anything with the source code (besides sell it) as long as you give proper credit to the right people. **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. 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 # Misc
This source code is subject to a lot of change for better optimization/cleanliness. 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

21
build.bat Normal file → Executable file
View file

@ -1,6 +1,17 @@
@echo off
echo Building 3DSX/ELF/SMDH...
make make
bannertool.exe makebanner -i icons-banners/banner.png -a icons-banners/audio.wav -o icons-banners/banner.bnr echo Creating banner...
bannertool.exe 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 bannertool makebanner -i icons-banners/banner.png -a icons-banners/audio.wav -o icons-banners/banner.bnr
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 icon...
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 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
pause 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

12
build.sh Executable file
View file

@ -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

0
data/Font.png Normal file → Executable file
View file

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

0
data/bottombg.png Normal file → Executable file
View file

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

BIN
data/icons.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

BIN
data/player.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

0
icons-banners/Minicraft3DS.rsf Normal file → Executable file
View file

0
icons-banners/audio.wav Normal file → Executable file
View file

0
icons-banners/banner.png Normal file → Executable file
View file

Before

Width:  |  Height:  |  Size: 6 KiB

After

Width:  |  Height:  |  Size: 6 KiB

0
icons-banners/icon.png Normal file → Executable file
View file

Before

Width:  |  Height:  |  Size: 415 B

After

Width:  |  Height:  |  Size: 415 B

BIN
icons-banners/romfs.bin Normal file

Binary file not shown.

247
source/Crafting.c Normal file → Executable file
View file

@ -1,164 +1,193 @@
#include "Crafting.h" #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){ void checkCanCraftRecipes(RecipeManager * rm, Inventory * inv){
int i, j; int i, j;
for(i = 0; i < rm->size; i++){ for(i = 0; i < rm->size; i++){
rm->recipes[i].canCraft = true; rm->recipes[i].canCraft = true;
for(j = 0; j < rm->recipes[i].numOfCosts; j++){ 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){ if(countItemInv(rm->recipes[i].costs[j].costItem,0,inv) < rm->recipes[i].costs[j].costAmount){
rm->recipes[i].canCraft = false; rm->recipes[i].canCraft = false;
} }
} }
} }
} }
int compareCanCraft(const void * ra, const void * rb) { int compareCanCraft(const void * ra, const void * rb) {
Recipe* r1 = (Recipe*)ra; Recipe* r1 = (Recipe*)ra;
Recipe* r2 = (Recipe*)rb; 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; if (!r1->canCraft && r2->canCraft) return 1;
return r1->order - r2->order; // Needed for stable sorting. return r1->order - r2->order; // Needed for stable sorting.
} }
void sortRecipes(RecipeManager * rm){ 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){ void deductCost(Cost c, Inventory * inv){
Item* item = getItemFromInventory(c.costItem, inv); Item* item = getItemFromInventory(c.costItem, inv);
if(!item->onlyOne){ if(!item->onlyOne){
item->countLevel -= c.costAmount; item->countLevel -= c.costAmount;
if(item->countLevel < 1) removeItemFromInventory(item->slotNum, inv); if(item->countLevel < 1) removeItemFromInventory(item->slotNum, inv);
} else { } else {
removeItemFromInventory(item->slotNum, inv); removeItemFromInventory(item->slotNum, inv);
} }
} }
bool craftItem(RecipeManager * rm, Recipe* r, Inventory* inv){ bool craftItem(RecipeManager * rm, Recipe* r, Inventory* inv){
if(r->canCraft){ if(r->canCraft){
int i; int i;
for(i=0;i<r->numOfCosts;++i) deductCost(r->costs[i], inv); for(i=0;i<r->numOfCosts;++i) deductCost(r->costs[i], inv);
Item item = newItem(r->itemResult,r->itemAmountLevel); Item item = newItem(r->itemResult,r->itemAmountLevel);
if(!item.onlyOne && countItemInv(item.id,item.countLevel,inv) > 0){ if(!item.onlyOne && countItemInv(item.id,item.countLevel,inv) > 0){
getItemFromInventory(item.id, inv)->countLevel += r->itemAmountLevel; getItemFromInventory(item.id, inv)->countLevel += r->itemAmountLevel;
} else{ } else{
pushItemToInventoryFront(item, inv); pushItemToInventoryFront(item, inv);
} }
checkCanCraftRecipes(rm, inv); checkCanCraftRecipes(rm, inv);
sortRecipes(rm); sortRecipes(rm);
return true; return true;
} }
return false; return false;
} }
Cost newCost(int i, int c){ Cost newCost(int i, int c){
Cost nc; Cost nc;
nc.costItem = i; nc.costItem = i;
nc.costAmount = c; nc.costAmount = c;
return nc; return nc;
} }
u8 curPlace = 0; u8 curPlace = 0;
Recipe defineRecipe(int item, int amountOrLevel, int numArgs, ...){ Recipe defineRecipe(int item, int amountOrLevel, int numArgs, ...){
Recipe r; Recipe r;
r.itemResult = item; r.itemResult = item;
r.itemAmountLevel = amountOrLevel; r.itemAmountLevel = amountOrLevel;
r.numOfCosts = numArgs; r.numOfCosts = numArgs;
int i; int i;
va_list al; va_list al;
numArgs <<= 1; // Did this to get rid of a warning. numArgs <<= 1; // Did this to get rid of a warning.
va_start(al,numArgs); va_start(al,numArgs);
for(i=0;i<r.numOfCosts;++i) r.costs[i] = newCost(va_arg(al,int), va_arg(al,int)); for(i=0;i<r.numOfCosts;++i) r.costs[i] = newCost(va_arg(al,int), va_arg(al,int));
va_end(al); va_end(al);
r.order = curPlace; r.order = curPlace;
curPlace++; curPlace++;
return r; return r;
} }
void initRecipes(){ void initRecipes(){
curPlace = 0;
curPlace = 0;
workbenchRecipes.size = 22; inHeadRecipes.size = 2;
workbenchRecipes.recipes = (Recipe*)malloc(sizeof(Recipe) * (workbenchRecipes.size)); inHeadRecipes.recipes = (Recipe*)malloc(sizeof(Recipe) * (inHeadRecipes.size));
workbenchRecipes.recipes[0] = defineRecipe(ITEM_WORKBENCH,1,1,ITEM_WOOD,20); inHeadRecipes.recipes[0] = defineRecipe(ITEM_WORKBENCH,1,1,ITEM_WOOD,10);
workbenchRecipes.recipes[1] = defineRecipe(ITEM_FURNACE,1,1,ITEM_STONE,20); inHeadRecipes.recipes[1] = defineRecipe(ITEM_TORCH,1,2,ITEM_WOOD,1,ITEM_COAL,1);
workbenchRecipes.recipes[2] = defineRecipe(ITEM_OVEN,1,1,ITEM_STONE,20);
workbenchRecipes.recipes[3] = defineRecipe(ITEM_CHEST,1,1,ITEM_WOOD,20); workbenchRecipes.size = 24;
workbenchRecipes.recipes[4] = defineRecipe(ITEM_ANVIL,1,1,ITEM_IRONINGOT,5); workbenchRecipes.recipes = (Recipe*)malloc(sizeof(Recipe) * (workbenchRecipes.size));
workbenchRecipes.recipes[5] = defineRecipe(ITEM_LANTERN,1,3,ITEM_WOOD,5,ITEM_SLIME,10,ITEM_GLASS,4); workbenchRecipes.recipes[0] = defineRecipe(ITEM_WORKBENCH,1,1,ITEM_WOOD,10);
workbenchRecipes.recipes[1] = defineRecipe(ITEM_FURNACE,1,1,ITEM_STONE,20);
workbenchRecipes.recipes[2] = defineRecipe(ITEM_OVEN,1,1,ITEM_STONE,20);
workbenchRecipes.recipes[3] = defineRecipe(ITEM_CHEST,1,1,ITEM_WOOD,20);
workbenchRecipes.recipes[4] = defineRecipe(ITEM_ANVIL,1,1,ITEM_IRONINGOT,5);
workbenchRecipes.recipes[5] = defineRecipe(ITEM_LANTERN,1,3,ITEM_WOOD,5,ITEM_SLIME,10,ITEM_GLASS,4);
workbenchRecipes.recipes[6] = defineRecipe(ITEM_LOOM,1,2,ITEM_WOOD,10,ITEM_WOOL,5); workbenchRecipes.recipes[6] = defineRecipe(ITEM_LOOM,1,2,ITEM_WOOD,10,ITEM_WOOL,5);
workbenchRecipes.recipes[7] = defineRecipe(TOOL_SWORD,0,1,ITEM_WOOD,5); workbenchRecipes.recipes[7] = defineRecipe(TOOL_SWORD,0,1,ITEM_WOOD,5);
workbenchRecipes.recipes[8] = defineRecipe(TOOL_AXE,0,1,ITEM_WOOD,5); workbenchRecipes.recipes[8] = defineRecipe(TOOL_AXE,0,1,ITEM_WOOD,5);
workbenchRecipes.recipes[9] = defineRecipe(TOOL_HOE,0,1,ITEM_WOOD,5); workbenchRecipes.recipes[9] = defineRecipe(TOOL_HOE,0,1,ITEM_WOOD,5);
workbenchRecipes.recipes[10] = defineRecipe(TOOL_PICKAXE,0,1,ITEM_WOOD,5); workbenchRecipes.recipes[10] = defineRecipe(TOOL_PICKAXE,0,1,ITEM_WOOD,5);
workbenchRecipes.recipes[11] = defineRecipe(TOOL_SHOVEL,0,1,ITEM_WOOD,5); workbenchRecipes.recipes[11] = defineRecipe(TOOL_SHOVEL,0,1,ITEM_WOOD,5);
workbenchRecipes.recipes[12] = defineRecipe(TOOL_BOW,0,2,ITEM_WOOD,10,ITEM_STRING,1); workbenchRecipes.recipes[12] = defineRecipe(TOOL_BOW,0,2,ITEM_WOOD,10,ITEM_STRING,1);
workbenchRecipes.recipes[13] = defineRecipe(ITEM_ARROW_WOOD,1,2,ITEM_WOOD,2,ITEM_STRING,1); workbenchRecipes.recipes[13] = defineRecipe(ITEM_ARROW_WOOD,1,2,ITEM_WOOD,2,ITEM_STRING,1);
workbenchRecipes.recipes[14] = defineRecipe(TOOL_SWORD,1,2,ITEM_WOOD,5,ITEM_STONE,5); workbenchRecipes.recipes[14] = defineRecipe(TOOL_SWORD,1,2,ITEM_WOOD,5,ITEM_STONE,5);
workbenchRecipes.recipes[15] = defineRecipe(TOOL_AXE,1,2,ITEM_WOOD,5,ITEM_STONE,5); workbenchRecipes.recipes[15] = defineRecipe(TOOL_AXE,1,2,ITEM_WOOD,5,ITEM_STONE,5);
workbenchRecipes.recipes[16] = defineRecipe(TOOL_HOE,1,2,ITEM_WOOD,5,ITEM_STONE,5); workbenchRecipes.recipes[16] = defineRecipe(TOOL_HOE,1,2,ITEM_WOOD,5,ITEM_STONE,5);
workbenchRecipes.recipes[17] = defineRecipe(TOOL_PICKAXE,1,2,ITEM_WOOD,5,ITEM_STONE,5); workbenchRecipes.recipes[17] = defineRecipe(TOOL_PICKAXE,1,2,ITEM_WOOD,5,ITEM_STONE,5);
workbenchRecipes.recipes[18] = defineRecipe(TOOL_SHOVEL,1,2,ITEM_WOOD,5,ITEM_STONE,5); workbenchRecipes.recipes[18] = defineRecipe(TOOL_SHOVEL,1,2,ITEM_WOOD,5,ITEM_STONE,5);
workbenchRecipes.recipes[19] = defineRecipe(ITEM_ARROW_STONE,1,3,ITEM_WOOD,1,ITEM_STONE,1,ITEM_STRING,1); workbenchRecipes.recipes[19] = defineRecipe(ITEM_ARROW_STONE,1,3,ITEM_WOOD,1,ITEM_STONE,1,ITEM_STRING,1);
workbenchRecipes.recipes[20] = defineRecipe(ITEM_WALL_WOOD,1,1,ITEM_WOOD,4); workbenchRecipes.recipes[20] = defineRecipe(ITEM_WALL_WOOD,1,1,ITEM_WOOD,4);
workbenchRecipes.recipes[21] = defineRecipe(ITEM_WALL_STONE,1,1,ITEM_STONE,4); workbenchRecipes.recipes[21] = defineRecipe(ITEM_WALL_STONE,1,1,ITEM_STONE,4);
workbenchRecipes.recipes[22] = defineRecipe(ITEM_FISHING_ROD,1,2,ITEM_WOOD,10,ITEM_STRING,3);
workbenchRecipes.recipes[23] = defineRecipe(ITEM_TORCH,1,2,ITEM_WOOD,1,ITEM_COAL,1);
anvilRecipes.size = 17; anvilRecipes.size = 19;
anvilRecipes.recipes = (Recipe*)malloc(sizeof(Recipe) * (anvilRecipes.size)); anvilRecipes.recipes = (Recipe*)malloc(sizeof(Recipe) * (anvilRecipes.size));
anvilRecipes.recipes[0] = defineRecipe(TOOL_SWORD,2,2,ITEM_WOOD,5,ITEM_IRONINGOT,5); anvilRecipes.recipes[0] = defineRecipe(TOOL_SWORD,2,2,ITEM_WOOD,5,ITEM_IRONINGOT,5);
anvilRecipes.recipes[1] = defineRecipe(TOOL_AXE,2,2,ITEM_WOOD,5,ITEM_IRONINGOT,5); anvilRecipes.recipes[1] = defineRecipe(TOOL_AXE,2,2,ITEM_WOOD,5,ITEM_IRONINGOT,5);
anvilRecipes.recipes[2] = defineRecipe(TOOL_HOE,2,2,ITEM_WOOD,5,ITEM_IRONINGOT,5); anvilRecipes.recipes[2] = defineRecipe(TOOL_HOE,2,2,ITEM_WOOD,5,ITEM_IRONINGOT,5);
anvilRecipes.recipes[3] = defineRecipe(TOOL_PICKAXE,2,2,ITEM_WOOD,5,ITEM_IRONINGOT,5); anvilRecipes.recipes[3] = defineRecipe(TOOL_PICKAXE,2,2,ITEM_WOOD,5,ITEM_IRONINGOT,5);
anvilRecipes.recipes[4] = defineRecipe(TOOL_SHOVEL,2,2,ITEM_WOOD,5,ITEM_IRONINGOT,5); anvilRecipes.recipes[4] = defineRecipe(TOOL_SHOVEL,2,2,ITEM_WOOD,5,ITEM_IRONINGOT,5);
anvilRecipes.recipes[5] = defineRecipe(ITEM_ARROW_IRON,1,3,ITEM_WOOD,1,ITEM_IRONINGOT,1,ITEM_STRING,1); anvilRecipes.recipes[5] = defineRecipe(ITEM_ARROW_IRON,1,3,ITEM_WOOD,1,ITEM_IRONINGOT,1,ITEM_STRING,1);
anvilRecipes.recipes[6] = defineRecipe(TOOL_SWORD,3,2,ITEM_WOOD,5,ITEM_GOLDINGOT,5); anvilRecipes.recipes[6] = defineRecipe(TOOL_SWORD,3,2,ITEM_WOOD,5,ITEM_GOLDINGOT,5);
anvilRecipes.recipes[7] = defineRecipe(TOOL_AXE,3,2,ITEM_WOOD,5,ITEM_GOLDINGOT,5); anvilRecipes.recipes[7] = defineRecipe(TOOL_AXE,3,2,ITEM_WOOD,5,ITEM_GOLDINGOT,5);
anvilRecipes.recipes[8] = defineRecipe(TOOL_HOE,3,2,ITEM_WOOD,5,ITEM_GOLDINGOT,5); anvilRecipes.recipes[8] = defineRecipe(TOOL_HOE,3,2,ITEM_WOOD,5,ITEM_GOLDINGOT,5);
anvilRecipes.recipes[9] = defineRecipe(TOOL_PICKAXE,3,2,ITEM_WOOD,5,ITEM_GOLDINGOT,5); anvilRecipes.recipes[9] = defineRecipe(TOOL_PICKAXE,3,2,ITEM_WOOD,5,ITEM_GOLDINGOT,5);
anvilRecipes.recipes[10] = defineRecipe(TOOL_SHOVEL,3,2,ITEM_WOOD,5,ITEM_GOLDINGOT,5); anvilRecipes.recipes[10] = defineRecipe(TOOL_SHOVEL,3,2,ITEM_WOOD,5,ITEM_GOLDINGOT,5);
anvilRecipes.recipes[11] = defineRecipe(ITEM_ARROW_GOLD,1,3,ITEM_WOOD,1,ITEM_GOLDINGOT,1,ITEM_STRING,1); anvilRecipes.recipes[11] = defineRecipe(ITEM_ARROW_GOLD,1,3,ITEM_WOOD,1,ITEM_GOLDINGOT,1,ITEM_STRING,1);
anvilRecipes.recipes[12] = defineRecipe(TOOL_BUCKET,0,1,ITEM_IRONINGOT,10); anvilRecipes.recipes[12] = defineRecipe(TOOL_BUCKET,0,1,ITEM_IRONINGOT,10);
anvilRecipes.recipes[13] = defineRecipe(ITEM_ENCHANTER,1,3,ITEM_WOOD,10,ITEM_GOLDINGOT,10,ITEM_GEM,20); anvilRecipes.recipes[13] = defineRecipe(ITEM_ENCHANTER,1,3,ITEM_WOOD,10,ITEM_GOLDINGOT,10,ITEM_GEM,20);
anvilRecipes.recipes[14] = defineRecipe(ITEM_WALL_IRON,1,1,ITEM_IRONINGOT,2); anvilRecipes.recipes[14] = defineRecipe(ITEM_WALL_IRON,1,1,ITEM_IRONINGOT,2);
anvilRecipes.recipes[15] = defineRecipe(ITEM_WALL_GOLD,1,1,ITEM_GOLDINGOT,2); anvilRecipes.recipes[15] = defineRecipe(ITEM_WALL_GOLD,1,1,ITEM_GOLDINGOT,2);
anvilRecipes.recipes[16] = defineRecipe(ITEM_COIN,3,1,ITEM_IRONINGOT,1); anvilRecipes.recipes[16] = defineRecipe(ITEM_COIN,3,1,ITEM_IRONINGOT,1);
anvilRecipes.recipes[17] = defineRecipe(ITEM_POTION_MAKER,1,3,ITEM_IRONINGOT,5,ITEM_GOLDINGOT,25,ITEM_GEM,5);
furnaceRecipes.size = 3; anvilRecipes.recipes[18] = defineRecipe(ITEM_SHEARS,1,1,ITEM_IRONINGOT,2);
furnaceRecipes.recipes = (Recipe*)malloc(sizeof(Recipe) * (furnaceRecipes.size));
furnaceRecipes.recipes[0] = defineRecipe(ITEM_IRONINGOT,1,2,ITEM_IRONORE,4,ITEM_COAL,1); furnaceRecipes.size = 3;
furnaceRecipes.recipes[1] = defineRecipe(ITEM_GOLDINGOT,1,2,ITEM_GOLDORE,4,ITEM_COAL,1); furnaceRecipes.recipes = (Recipe*)malloc(sizeof(Recipe) * (furnaceRecipes.size));
furnaceRecipes.recipes[2] = defineRecipe(ITEM_GLASS,1,2,ITEM_SAND,4,ITEM_COAL,1); furnaceRecipes.recipes[0] = defineRecipe(ITEM_IRONINGOT,1,2,ITEM_IRONORE,4,ITEM_COAL,1);
furnaceRecipes.recipes[1] = defineRecipe(ITEM_GOLDINGOT,1,2,ITEM_GOLDORE,4,ITEM_COAL,1);
ovenRecipes.size = 3; furnaceRecipes.recipes[2] = defineRecipe(ITEM_GLASS,1,2,ITEM_SAND,4,ITEM_COAL,1);
ovenRecipes.recipes = (Recipe*)malloc(sizeof(Recipe) * (ovenRecipes.size));
ovenRecipes.recipes[0] = defineRecipe(ITEM_BREAD,1,1,ITEM_WHEAT,4); ovenRecipes.size = 4;
ovenRecipes.recipes = (Recipe*)malloc(sizeof(Recipe) * (ovenRecipes.size));
ovenRecipes.recipes[0] = defineRecipe(ITEM_BREAD,1,1,ITEM_WHEAT,4);
ovenRecipes.recipes[1] = defineRecipe(ITEM_PORK_COOKED,1,2,ITEM_PORK_RAW,1,ITEM_COAL,1); ovenRecipes.recipes[1] = defineRecipe(ITEM_PORK_COOKED,1,2,ITEM_PORK_RAW,1,ITEM_COAL,1);
ovenRecipes.recipes[2] = defineRecipe(ITEM_BEEF_COOKED,1,2,ITEM_BEEF_RAW,1,ITEM_COAL,1); ovenRecipes.recipes[2] = defineRecipe(ITEM_BEEF_COOKED,1,2,ITEM_BEEF_RAW,1,ITEM_COAL,1);
ovenRecipes.recipes[3] = defineRecipe(ITEM_FISH_COOKED,1,2,ITEM_FISH_RAW,1,ITEM_COAL,1);
loomRecipes.size = 1;
loomRecipes.recipes = (Recipe*)malloc(sizeof(Recipe) * (loomRecipes.size)); loomRecipes.size = 2;
loomRecipes.recipes[0] = defineRecipe(ITEM_STRING,1,1,ITEM_WOOL,1); loomRecipes.recipes = (Recipe*)malloc(sizeof(Recipe) * (loomRecipes.size));
loomRecipes.recipes[0] = defineRecipe(ITEM_STRING,1,1,ITEM_WOOL,1);
loomRecipes.recipes[1] = defineRecipe(ITEM_BED,1,2,ITEM_WOOD,5,ITEM_WOOL,3);
enchanterRecipes.size = 8; enchanterRecipes.size = 8;
enchanterRecipes.recipes = (Recipe*)malloc(sizeof(Recipe) * (enchanterRecipes.size)); enchanterRecipes.recipes = (Recipe*)malloc(sizeof(Recipe) * (enchanterRecipes.size));
enchanterRecipes.recipes[0] = defineRecipe(TOOL_SWORD,4,2,ITEM_WOOD,5,ITEM_GEM,50); enchanterRecipes.recipes[0] = defineRecipe(TOOL_SWORD,4,2,ITEM_WOOD,5,ITEM_GEM,50);
enchanterRecipes.recipes[1] = defineRecipe(TOOL_AXE,4,2,ITEM_WOOD,5,ITEM_GEM,50); enchanterRecipes.recipes[1] = defineRecipe(TOOL_AXE,4,2,ITEM_WOOD,5,ITEM_GEM,50);
enchanterRecipes.recipes[2] = defineRecipe(TOOL_HOE,4,2,ITEM_WOOD,5,ITEM_GEM,50); enchanterRecipes.recipes[2] = defineRecipe(TOOL_HOE,4,2,ITEM_WOOD,5,ITEM_GEM,50);
enchanterRecipes.recipes[3] = defineRecipe(TOOL_PICKAXE,4,2,ITEM_WOOD,5,ITEM_GEM,50); enchanterRecipes.recipes[3] = defineRecipe(TOOL_PICKAXE,4,2,ITEM_WOOD,5,ITEM_GEM,50);
enchanterRecipes.recipes[4] = defineRecipe(TOOL_SHOVEL,4,2,ITEM_WOOD,5,ITEM_GEM,50); enchanterRecipes.recipes[4] = defineRecipe(TOOL_SHOVEL,4,2,ITEM_WOOD,5,ITEM_GEM,50);
enchanterRecipes.recipes[5] = defineRecipe(ITEM_ARROW_GEM,1,3,ITEM_WOOD,1,ITEM_GEM,3,ITEM_STRING,1); enchanterRecipes.recipes[5] = defineRecipe(ITEM_ARROW_GEM,1,3,ITEM_WOOD,1,ITEM_GEM,3,ITEM_STRING,1);
enchanterRecipes.recipes[6] = defineRecipe(ITEM_WALL_GEM,1,1,ITEM_GEM,10); enchanterRecipes.recipes[6] = defineRecipe(ITEM_WALL_GEM,1,1,ITEM_GEM,10);
enchanterRecipes.recipes[7] = defineRecipe(ITEM_GOLD_APPLE,1,2,ITEM_APPLE,1,ITEM_GOLDINGOT,15); enchanterRecipes.recipes[7] = defineRecipe(ITEM_GOLD_APPLE,1,2,ITEM_APPLE,1,ITEM_GOLDINGOT,15);
potionMakerRecipes.size = 4;
potionMakerRecipes.recipes = (Recipe*)malloc(sizeof(Recipe) * (potionMakerRecipes.size));
potionMakerRecipes.recipes[0] = defineRecipe(ITEM_STRENGTH_POTION,1,3,ITEM_GOLD_APPLE,1,ITEM_GLASS,10,ITEM_IRONINGOT,10);
potionMakerRecipes.recipes[1] = defineRecipe(ITEM_SPEED_POTION,1,4,ITEM_GEM,2,ITEM_GLASS,10,ITEM_IRONINGOT,10, ITEM_GOLDINGOT,15);
potionMakerRecipes.recipes[2] = defineRecipe(ITEM_REGEN_POTION,1,3,ITEM_GOLD_APPLE,2,ITEM_GLASS,10,ITEM_GEM,10);
potionMakerRecipes.recipes[3] = defineRecipe(ITEM_SWIM_BREATH_POTION,1,4,ITEM_GOLD_APPLE,2,ITEM_GLASS,10,ITEM_GEM,10, ITEM_STRING,15);
} }
/* Free up allocated memory */ /* Free up allocated memory */
void freeRecipes(){ void freeRecipes(){
free(workbenchRecipes.recipes); free(inHeadRecipes.recipes);
free(ovenRecipes.recipes); free(workbenchRecipes.recipes);
free(furnaceRecipes.recipes); free(ovenRecipes.recipes);
free(anvilRecipes.recipes); free(furnaceRecipes.recipes);
free(anvilRecipes.recipes);
free(loomRecipes.recipes); free(loomRecipes.recipes);
free(enchanterRecipes.recipes); free(enchanterRecipes.recipes);
free(potionMakerRecipes.recipes);
} }

30
source/Crafting.h Normal file → Executable file
View file

@ -2,35 +2,37 @@
#include <stdarg.h> #include <stdarg.h>
#include "Item.h" #include "Item.h"
typedef struct { typedef struct _recipecost {
int costItem; int costItem;
int costAmount; int costAmount;
} Cost; } Cost;
typedef struct { typedef struct _recipe {
bool canCraft; bool canCraft;
int itemResult; int itemResult;
int itemAmountLevel; int itemAmountLevel;
s8 numOfCosts; s8 numOfCosts;
Cost costs[6]; // Up to 6 items for costs Cost costs[6]; // Up to 6 items for costs
u8 order; // Used for stable sorting. u8 order; // Used for stable sorting.
} Recipe; } Recipe;
typedef struct { typedef struct _recipeManager {
int size; int size;
Recipe * recipes; Recipe * recipes;
} RecipeManager; } RecipeManager;
RecipeManager inHeadRecipes;
RecipeManager workbenchRecipes; RecipeManager workbenchRecipes;
RecipeManager furnaceRecipes; RecipeManager furnaceRecipes;
RecipeManager ovenRecipes; RecipeManager ovenRecipes;
RecipeManager anvilRecipes; RecipeManager anvilRecipes;
RecipeManager loomRecipes; RecipeManager loomRecipes;
RecipeManager enchanterRecipes; RecipeManager enchanterRecipes;
RecipeManager potionMakerRecipes;
Recipe defineRecipe(int item, int amountOrLevel, int numArgs, ...); Recipe defineRecipe(int item, int amountOrLevel, int numArgs, ...);
void cloneRecipeManager(RecipeManager *from, RecipeManager *to);
void checkCanCraftRecipes(RecipeManager * rm, Inventory * inv); void checkCanCraftRecipes(RecipeManager * rm, Inventory * inv);
void sortRecipes(RecipeManager * rm); void sortRecipes(RecipeManager * rm);
bool craftItem(RecipeManager * rm, Recipe* r, Inventory* inv); bool craftItem(RecipeManager * rm, Recipe* r, Inventory* inv);

525
source/Entity.c Normal file → Executable file
View file

@ -1,380 +1,363 @@
#include "Entity.h" #include "Entity.h"
#define PI 3.141592654 #include "Synchronizer.h"
double gaussrand()
{
static double U, V;
static int phase = 0;
double Z;
if(phase == 0) {
U = (rand() + 1.) / (RAND_MAX + 2.);
V = rand() / (RAND_MAX + 1.);
Z = sqrt(-2 * log(U)) * sin(2 * PI * V);
} else
Z = sqrt(-2 * log(U)) * cos(2 * PI * V);
phase = 1 - phase;
return Z;
}
Entity newItemEntity(Item item, int x, int y, int level){ Entity newItemEntity(Item item, int x, int y, int level){
Entity e; Entity e;
e.type = ENTITY_ITEM; e.type = ENTITY_ITEM;
e.level = level; e.level = level;
e.entityItem.age = 0; e.entityItem.age = 0;
e.entityItem.item = item; e.entityItem.item = item;
e.x = x; e.x = x;
e.y = y; e.y = y;
e.xr = 3; e.xr = 3;
e.yr = 3; e.yr = 3;
e.canPass = false; e.canPass = false;
e.entityItem.xx = x; e.entityItem.xx = x;
e.entityItem.yy = y; e.entityItem.yy = y;
e.entityItem.zz = 2; e.entityItem.zz = 2;
e.entityItem.xa = gaussrand() * 0.1; e.entityItem.xa = gaussrand(false) * 0.1;
e.entityItem.ya = gaussrand() * 0.1; e.entityItem.ya = gaussrand(false) * 0.1;
e.entityItem.za = ((float)rand() / RAND_MAX) * 0.45 + 1; e.entityItem.za = ((float)rand() / RAND_MAX) * 0.45 + 1;
return e; return e;
} }
void assignInventory(Entity* e){ void assignInventory(Entity* e){
if(eManager.nextInv > 300) return; if(eManager.nextInv > 300) return;
e->entityFurniture.inv = &eManager.invs[eManager.nextInv]; e->entityFurniture.inv = &eManager.invs[eManager.nextInv];
eManager.nextInv++; eManager.nextInv++;
} }
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 e; Entity e;
e.type = ENTITY_FURNITURE; e.type = ENTITY_FURNITURE;
e.level = level; e.level = level;
e.x = x; e.x = x;
e.y = y; e.y = y;
e.xr = 3; e.xr = 3;
e.yr = 3; e.yr = 3;
e.entityFurniture.itemID = itemID; e.entityFurniture.itemID = itemID;
e.canPass = false; e.canPass = false;
if(itemID == ITEM_LANTERN) e.entityFurniture.r = 8; if(itemID == ITEM_LANTERN) e.entityFurniture.r = 8;
else if(itemID == ITEM_CHEST){ else if(itemID == ITEM_CHEST){
if(invPtr == NULL)assignInventory(&e); if(invPtr == NULL)assignInventory(&e);
else e.entityFurniture.inv = invPtr; else e.entityFurniture.inv = invPtr;
} }
return e; return e;
} }
Entity newPassiveEntity(int type, int x, int y, int level){ Entity newPassiveEntity(int type, int x, int y, int level){
Entity e; Entity e;
e.type = ENTITY_PASSIVE; e.type = ENTITY_PASSIVE;
e.level = level; e.level = level;
e.x = x; e.x = x;
e.y = y; e.y = y;
e.hurtTime = 0; e.hurtTime = 0;
e.xKnockback = 0; e.xKnockback = 0;
e.yKnockback = 0; e.yKnockback = 0;
e.passive.mtype = type; e.passive.mtype = type;
e.passive.health = 20; e.passive.health = 20;
e.passive.dir = 0; e.passive.dir = 0;
e.passive.xa = 0; e.passive.xa = 0;
e.passive.ya = 0; e.passive.ya = 0;
e.xr = 4; e.xr = 4;
e.yr = 3; e.yr = 3;
e.canPass = false; e.canPass = false;
return e; return e;
} }
Entity newZombieEntity(int lvl, int x, int y, int level){ Entity newZombieEntity(int lvl, int x, int y, int level){
Entity e; Entity e;
e.type = ENTITY_ZOMBIE; e.type = ENTITY_ZOMBIE;
e.level = level; e.level = level;
e.x = x; e.x = x;
e.y = y; e.y = y;
e.hurtTime = 0; e.hurtTime = 0;
e.xKnockback = 0; e.xKnockback = 0;
e.yKnockback = 0; e.yKnockback = 0;
e.hostile.lvl = lvl; e.hostile.lvl = lvl;
e.hostile.xa = 0; e.hostile.xa = 0;
e.hostile.ya = 0; e.hostile.ya = 0;
e.hostile.health = lvl * lvl * 10; e.hostile.health = lvl * lvl * 10;
e.hostile.dir = 0; e.hostile.dir = 0;
e.xr = 4; e.xr = 4;
e.yr = 3; e.yr = 3;
e.canPass = false; e.canPass = false;
switch(lvl){ switch(lvl){
case 2: e.hostile.color = 0xFF8282CC; break; case 2: e.hostile.color = 0xFF8282CC; break;
case 3: e.hostile.color = 0xFFEFEFEF; break; case 3: e.hostile.color = 0xFFEFEFEF; break;
case 4: e.hostile.color = 0xFFAA6262; break; case 4: e.hostile.color = 0xFFAA6262; break;
default: e.hostile.color = 0xFF95DB95; break; default: e.hostile.color = 0xFF95DB95; break;
} }
return e; return e;
} }
Entity newSkeletonEntity(int lvl, int x, int y, int level){ Entity newSkeletonEntity(int lvl, int x, int y, int level){
Entity e; Entity e;
e.type = ENTITY_SKELETON; e.type = ENTITY_SKELETON;
e.level = level; e.level = level;
e.x = x; e.x = x;
e.y = y; e.y = y;
e.hurtTime = 0; e.hurtTime = 0;
e.xKnockback = 0; e.xKnockback = 0;
e.yKnockback = 0; e.yKnockback = 0;
e.hostile.lvl = lvl; e.hostile.lvl = lvl;
e.hostile.xa = 0; e.hostile.xa = 0;
e.hostile.ya = 0; e.hostile.ya = 0;
e.hostile.health = lvl * lvl * 10; e.hostile.health = lvl * lvl * 10;
e.hostile.dir = 0; e.hostile.dir = 0;
e.hostile.randAttackTime = 0; e.hostile.randAttackTime = 0;
e.xr = 4; e.xr = 4;
e.yr = 3; e.yr = 3;
e.canPass = false; e.canPass = false;
switch(lvl){ switch(lvl){
case 2: e.hostile.color = 0xFFC4C4C4; break; case 2: e.hostile.color = 0xFFC4C4C4; break;
case 3: e.hostile.color = 0xFFA0A0A0; break; case 3: e.hostile.color = 0xFFA0A0A0; break;
case 4: e.hostile.color = 0xFF7A7A7A; break; case 4: e.hostile.color = 0xFF7A7A7A; break;
default: e.hostile.color = 0xFFFFFFFF; break; default: e.hostile.color = 0xFFFFFFFF; break;
} }
return e; return e;
} }
Entity newKnightEntity(int lvl, int x, int y, int level){ Entity newKnightEntity(int lvl, int x, int y, int level){
Entity e; Entity e;
e.type = ENTITY_KNIGHT; e.type = ENTITY_KNIGHT;
e.level = level; e.level = level;
e.x = x; e.x = x;
e.y = y; e.y = y;
e.hurtTime = 0; e.hurtTime = 0;
e.xKnockback = 0; e.xKnockback = 0;
e.yKnockback = 0; e.yKnockback = 0;
e.hostile.lvl = lvl; e.hostile.lvl = lvl;
e.hostile.xa = 0; e.hostile.xa = 0;
e.hostile.ya = 0; e.hostile.ya = 0;
e.hostile.health = lvl * lvl * 20; e.hostile.health = lvl * lvl * 20;
e.hostile.dir = 0; e.hostile.dir = 0;
e.xr = 4; e.xr = 4;
e.yr = 3; e.yr = 3;
e.canPass = false; e.canPass = false;
switch(lvl){ switch(lvl){
case 2: e.hostile.color = 0xFF0000C6; break; case 2: e.hostile.color = 0xFF0000C6; break;
case 3: e.hostile.color = 0xFF00A3C6; break; case 3: e.hostile.color = 0xFF00A3C6; break;
case 4: e.hostile.color = 0xFF707070; break; case 4: e.hostile.color = 0xFF707070; break;
default: e.hostile.color = 0xFFFFFFFF; break; default: e.hostile.color = 0xFFFFFFFF; break;
} }
return e; return e;
} }
Entity newSlimeEntity(int lvl, int x, int y, int level){ Entity newSlimeEntity(int lvl, int x, int y, int level){
Entity e; Entity e;
e.type = ENTITY_SLIME; e.type = ENTITY_SLIME;
e.level = level; e.level = level;
e.x = x; e.x = x;
e.y = y; e.y = y;
e.hurtTime = 0; e.hurtTime = 0;
e.xKnockback = 0; e.xKnockback = 0;
e.yKnockback = 0; e.yKnockback = 0;
e.slime.lvl = lvl; e.slime.lvl = lvl;
e.slime.xa = 0; e.slime.xa = 0;
e.slime.ya = 0; e.slime.ya = 0;
e.slime.dir = 0; e.slime.dir = 0;
e.slime.health = lvl * lvl * 5; e.slime.health = lvl * lvl * 5;
e.xr = 4; e.xr = 4;
e.yr = 3; e.yr = 3;
e.canPass = false; e.canPass = false;
switch(lvl){ switch(lvl){
case 2: e.slime.color = 0xFF8282CC; break; case 2: e.slime.color = 0xFF8282CC; break;
case 3: e.slime.color = 0xFFEFEFEF; break; case 3: e.slime.color = 0xFFEFEFEF; break;
case 4: e.slime.color = 0xFFAA6262; break; case 4: e.slime.color = 0xFFAA6262; break;
default: e.slime.color = 0xFF95DB95; break; default: e.slime.color = 0xFF95DB95; break;
} }
return e; return e;
} }
Entity newAirWizardEntity(int x, int y, int level){ Entity newAirWizardEntity(int x, int y, int level){
Entity e; Entity e;
e.type = ENTITY_AIRWIZARD; e.type = ENTITY_AIRWIZARD;
e.level = level; e.level = level;
e.x = x; e.x = x;
e.y = y; e.y = y;
e.hurtTime = 0; e.hurtTime = 0;
e.xKnockback = 0; e.xKnockback = 0;
e.yKnockback = 0; e.yKnockback = 0;
e.wizard.dir = 0; e.wizard.dir = 0;
e.wizard.health = 2000; e.wizard.health = 2000;
e.wizard.attackDelay = 0; e.wizard.attackDelay = 0;
e.wizard.attackTime = 0; e.wizard.attackTime = 0;
e.wizard.attackType = 0; e.wizard.attackType = 0;
e.wizard.xa = 0; e.wizard.xa = 0;
e.wizard.ya = 0; e.wizard.ya = 0;
e.xr = 4; e.xr = 4;
e.yr = 3; e.yr = 3;
e.canPass = true; e.canPass = true;
return e; return e;
} }
Entity newSparkEntity(Entity* parent, float xa, float ya){ Entity newSparkEntity(Entity* parent, float xa, float ya){
Entity e; Entity e;
e.type = ENTITY_SPARK; e.type = ENTITY_SPARK;
e.level = parent->level; e.level = parent->level;
e.spark.age = 0; e.spark.age = 0;
e.spark.parent = parent; e.spark.parent = parent;
e.spark.xa = xa; e.spark.xa = xa;
e.spark.ya = ya; e.spark.ya = ya;
e.spark.xx = parent->x; e.spark.xx = parent->x;
e.spark.yy = parent->y; e.spark.yy = parent->y;
e.xr = 3; e.xr = 3;
e.yr = 3; e.yr = 3;
e.canPass = true; e.canPass = true;
return e; return e;
} }
Entity newDragonEntity(int x, int y, int level) { Entity newDragonEntity(int x, int y, int level) {
Entity e; Entity e;
e.type = ENTITY_DRAGON; e.type = ENTITY_DRAGON;
e.level = level; e.level = level;
e.x = x; e.x = x;
e.y = y; e.y = y;
e.hurtTime = 0; e.hurtTime = 0;
e.xKnockback = 0; e.xKnockback = 0;
e.yKnockback = 0; e.yKnockback = 0;
e.dragon.dir = 0; e.dragon.dir = 0;
e.dragon.health = 2000; e.dragon.health = 2000;
e.dragon.attackDelay = 0; e.dragon.attackDelay = 0;
e.dragon.attackTime = 0; e.dragon.attackTime = 0;
e.dragon.attackType = 0; e.dragon.attackType = 0;
e.dragon.animTimer = 0; e.dragon.animTimer = 0;
e.dragon.xa = 0; e.dragon.xa = 0;
e.dragon.ya = 0; e.dragon.ya = 0;
e.xr = 8; e.xr = 8;
e.yr = 8; e.yr = 8;
e.canPass = false; e.canPass = false;
return e; return e;
} }
Entity newDragonFireEntity(Entity* parent, u8 type, int x, int y, float xa, float ya) { Entity newDragonFireEntity(Entity* parent, u8 type, int x, int y, float xa, float ya) {
Entity e; Entity e;
e.type = ENTITY_DRAGONPROJECTILE; e.type = ENTITY_DRAGONPROJECTILE;
e.level = parent->level; e.level = parent->level;
e.dragonFire.age = 0; e.dragonFire.age = 0;
e.dragonFire.type = type; e.dragonFire.type = type;
e.dragonFire.parent = parent; e.dragonFire.parent = parent;
e.dragonFire.xa = xa; e.dragonFire.xa = xa;
e.dragonFire.ya = ya; e.dragonFire.ya = ya;
e.dragonFire.xx = x; e.dragonFire.xx = x;
e.dragonFire.yy = y; e.dragonFire.yy = y;
e.x = (int) x; e.x = (int) x;
e.y = (int) y; e.y = (int) y;
e.xr = 3; e.xr = 3;
e.yr = 3; e.yr = 3;
e.canPass = true; e.canPass = true;
return e; return e;
} }
Entity newMagicPillarEntity(int x, int y, int level){ Entity newMagicPillarEntity(int x, int y, int level){
Entity e; Entity e;
e.type = ENTITY_MAGIC_PILLAR; e.type = ENTITY_MAGIC_PILLAR;
e.level = level; e.level = level;
e.x = x; e.x = x;
e.y = y; e.y = y;
e.xr = 3; e.xr = 3;
e.yr = 3; e.yr = 3;
e.canPass = false; e.canPass = false;
return e; return e;
} }
Entity newTextParticleEntity(char * str, u32 color, int x, int y, int level){ Entity newTextParticleEntity(char * str, u32 color, int x, int y, int level){
Entity e; Entity e;
e.type = ENTITY_TEXTPARTICLE; e.type = ENTITY_TEXTPARTICLE;
e.level = level; e.level = level;
e.textParticle.color = color; e.textParticle.color = color;
e.textParticle.age = 0; e.textParticle.age = 0;
e.textParticle.text = (char*)calloc(strlen(str),sizeof(char)); e.textParticle.text = (char*)calloc(strlen(str),sizeof(char));
strncpy(e.textParticle.text,str,strlen(str)); strncpy(e.textParticle.text,str,strlen(str));
e.x = x; e.x = x;
e.y = y; e.y = y;
e.canPass = true; e.canPass = true;
e.textParticle.xx = x; e.textParticle.xx = x;
e.textParticle.yy = y; e.textParticle.yy = y;
e.textParticle.zz = 2; e.textParticle.zz = 2;
e.textParticle.xa = gaussrand() * 0.3; e.textParticle.xa = gaussrand(false) * 0.3;
e.textParticle.ya = gaussrand() * 0.2; e.textParticle.ya = gaussrand(false) * 0.2;
e.textParticle.za = ((float)rand() / RAND_MAX) * 0.7 + 2; e.textParticle.za = ((float)rand() / RAND_MAX) * 0.7 + 2;
return e; return e;
} }
Entity newSmashParticleEntity(int x, int y, int level){ Entity newSmashParticleEntity(int x, int y, int level){
Entity e; Entity e;
e.type = ENTITY_SMASHPARTICLE; e.type = ENTITY_SMASHPARTICLE;
e.level = level; e.level = level;
e.smashParticle.age = 0; e.smashParticle.age = 0;
e.x = x; e.x = x;
e.y = y; e.y = y;
e.canPass = true; e.canPass = true;
playSound(snd_monsterHurt); playSoundPositioned(snd_monsterHurt, e.level, e.x, e.y); //TODO: This is a wierd location for the effect
return e; return e;
} }
Entity newArrowEntity(Entity* parent, int itemID, s8 xa, s8 ya, int level){ Entity newArrowEntity(Entity* parent, int itemID, s8 xa, s8 ya, int level){
Entity e; Entity e;
e.type = ENTITY_ARROW; e.type = ENTITY_ARROW;
e.level = level; e.level = level;
e.arrow.age = 0; e.arrow.age = 0;
e.arrow.parent = parent; e.arrow.parent = parent;
e.arrow.itemID = itemID; e.arrow.itemID = itemID;
e.arrow.xa = xa; e.arrow.xa = xa;
e.arrow.ya = ya; e.arrow.ya = ya;
e.x = parent->x; e.x = parent->x;
e.y = parent->y; e.y = parent->y;
e.xr = 2; e.xr = 2;
e.yr = 2; e.yr = 2;
e.canPass = false; e.canPass = false;
e.canSwim = true; e.canSwim = true;
return e; return e;
} }
Entity newGlowwormEntity(int x, int y, int level){ Entity newGlowwormEntity(int x, int y, int level){
Entity e; Entity e;
e.type = ENTITY_GLOWWORM; e.type = ENTITY_GLOWWORM;
e.level = level; e.level = level;
e.glowworm.xa = 0; e.glowworm.xa = 0;
e.glowworm.ya = 0; e.glowworm.ya = 0;
e.glowworm.randWalkTime = 0; e.glowworm.randWalkTime = 0;
e.glowworm.waitTime = 0; e.glowworm.waitTime = 0;
e.x = x; e.x = x;
e.y = y; e.y = y;
e.canPass = true; e.canPass = true;
return e; return e;
} }
Entity newNPCEntity(int type, int x, int y, int level){ Entity newNPCEntity(int type, int x, int y, int level){
Entity e; Entity e;
e.type = ENTITY_NPC; e.type = ENTITY_NPC;
e.level = level; e.level = level;
e.x = x; e.x = x;
e.y = y; e.y = y;
e.hurtTime = 0; e.hurtTime = 0;
e.xKnockback = 0; e.xKnockback = 0;
e.yKnockback = 0; e.yKnockback = 0;
e.npc.type = type; e.npc.type = type;
e.xr = 4; e.xr = 4;
e.yr = 3; e.yr = 3;
e.canPass = false; e.canPass = false;
return e; return e;
} }
void addEntityToList(Entity e, EntityManager* em){ void addEntityToList(Entity e, EntityManager* em){
e.slotNum = em->lastSlot[e.level]; e.slotNum = em->lastSlot[e.level];
em->entities[e.level][em->lastSlot[e.level]] = e; em->entities[e.level][em->lastSlot[e.level]] = e;
++em->lastSlot[e.level]; ++em->lastSlot[e.level];
} }
void removeEntityFromList(Entity * e,int level,EntityManager* em){ void removeEntityFromList(Entity * e,int level,EntityManager* em){
int i; int i;
if(em->entities[level][e->slotNum].type == ENTITY_TEXTPARTICLE) free(em->entities[level][e->slotNum].textParticle.text); 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){ 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] = em->entities[level][i + 1]; // Move the items down.
em->entities[level][i].slotNum = i; em->entities[level][i].slotNum = i;
} }
em->lastSlot[level]--; em->lastSlot[level]--;
em->entities[level][em->lastSlot[level]] = nullEntity; // Make the last slot null. em->entities[level][em->lastSlot[level]] = nullEntity; // Make the last slot null.
} }

238
source/Entity.h Normal file → Executable file
View file

@ -27,89 +27,92 @@
typedef struct Entity Entity; typedef struct Entity Entity;
typedef struct _plrd PlayerData; //in order to not include Player.h and cause all sorts of problems
typedef struct { typedef struct {
s8 ax; s8 ax;
s8 ay; s8 ay;
u8 dir; u8 dir;
s8 health; s8 health;
s8 stamina; s8 stamina;
s8 staminaRecharge; s8 staminaRecharge;
s8 staminaRechargeDelay; s8 staminaRechargeDelay;
s8 attackTimer; s8 attackTimer;
u8 spawnTrigger; u8 spawnTrigger;
bool isDead; bool isDead;
bool hasWon; bool hasWon;
bool hasWonSaved; bool hasWonSaved;
s8 endTimer; s8 endTimer;
s16 walkDist; s16 walkDist;
bool isCarrying; bool isCarrying;
bool isSwimming; bool isSwimming;
int swimTimer; int swimTimer;
int regenTimer;
int strengthTimer; int strengthTimer;
int swimBreathTimer;
int speedTimer;
int score; int score;
Inventory* inv; PlayerData *data;
Item* activeItem;
} Player; } Player;
typedef struct { typedef struct {
float xa; float xa;
float ya; float ya;
float za; float za;
float xx; float xx;
float yy; float yy;
float zz; float zz;
s16 age; s16 age;
Item item; Item item;
} EntityItem; } EntityItem;
typedef struct { typedef struct {
s16 itemID; s16 itemID;
bool active; bool active;
s8 r; // light radius for lantern. window select for chests. s8 r; // light radius for lantern.
Inventory* inv; // Points to chest inventory. Inventory* inv; // Points to chest inventory.
s16 oSel; // other selection inside the chest inv.
} EntityFurniture; } EntityFurniture;
typedef struct { typedef struct {
u8 mtype; u8 mtype;
s8 xa; s8 xa;
s8 ya; s8 ya;
s16 health; s16 health;
s8 dir; s8 dir;
s8 randWalkTime; s8 randWalkTime;
s8 walkDist; s8 walkDist;
} PassiveMob; } PassiveMob;
typedef struct { typedef struct {
s8 xa; s8 xa;
s8 ya; s8 ya;
s16 health; s16 health;
s8 dir; s8 dir;
s8 lvl; s8 lvl;
s8 randWalkTime; s8 randWalkTime;
s8 walkDist; s8 walkDist;
s8 randAttackTime; s8 randAttackTime;
u32 color; u32 color;
} HostileMob; } HostileMob;
typedef struct { typedef struct {
s8 xa; s8 xa;
s8 ya; s8 ya;
s16 health; s16 health;
s8 lvl; s8 lvl;
s8 dir; s8 dir;
s8 jumpTime; s8 jumpTime;
u32 color; u32 color;
} Slime; } Slime;
typedef struct { typedef struct {
s8 xa; s8 xa;
s8 ya; s8 ya;
s16 health; s16 health;
s8 randWalkTime; s8 randWalkTime;
s8 walkDist; s8 walkDist;
s8 dir; s8 dir;
int attackDelay; int attackDelay;
int attackTime; int attackTime;
int attackType; int attackType;
@ -117,12 +120,12 @@ typedef struct {
} AirWizard; } AirWizard;
typedef struct { typedef struct {
Entity* parent; Entity* parent;
s16 age; s16 age;
float xa; float xa;
float ya; float ya;
float xx; float xx;
float yy; float yy;
} Spark; } Spark;
typedef struct { typedef struct {
@ -134,12 +137,12 @@ typedef struct {
} Arrow; } Arrow;
typedef struct { typedef struct {
s8 xa; s8 xa;
s8 ya; s8 ya;
s16 health; s16 health;
s8 randWalkTime; s8 randWalkTime;
s8 walkDist; s8 walkDist;
s8 dir; s8 dir;
int attackDelay; int attackDelay;
int attackTime; int attackTime;
int attackType; int attackType;
@ -147,19 +150,19 @@ typedef struct {
} Dragon; } Dragon;
typedef struct { typedef struct {
Entity* parent; Entity* parent;
u8 type; u8 type;
s16 age; s16 age;
float xa; float xa;
float ya; float ya;
float xx; float xx;
float yy; float yy;
} DragonFire; } DragonFire;
typedef struct { typedef struct {
s8 xa; s8 xa;
s8 ya; s8 ya;
s8 randWalkTime; s8 randWalkTime;
s8 waitTime; s8 waitTime;
} Glowworm; } Glowworm;
@ -168,66 +171,64 @@ typedef struct {
} NPC; } NPC;
typedef struct { typedef struct {
float xa; float xa;
float ya; float ya;
float za; float za;
float xx; float xx;
float yy; float yy;
float zz; float zz;
s16 age; s16 age;
char* text; char* text;
int color; int color;
} TextParticleEntity; } TextParticleEntity;
typedef struct { typedef struct {
s16 age; s16 age;
} SmashParticleEntity; } SmashParticleEntity;
struct Entity { struct Entity {
s16 x; s16 x;
s16 y; s16 y;
s8 xKnockback,yKnockback; s8 xKnockback,yKnockback;
u8 xr,yr; u8 xr,yr;
u8 type; u8 type;
u8 level; u8 level;
s8 hurtTime; s8 hurtTime;
s16 slotNum; // Read-only. Do not mess with this. s16 slotNum; // Read-only. Do not mess with this.
bool canPass; bool canPass;
bool canSwim; bool canSwim;
union { union {
Player p; Player p;
EntityItem entityItem; EntityItem entityItem;
EntityFurniture entityFurniture; EntityFurniture entityFurniture;
PassiveMob passive; PassiveMob passive;
HostileMob hostile; HostileMob hostile;
Slime slime; Slime slime;
AirWizard wizard; AirWizard wizard;
Spark spark; Spark spark;
Arrow arrow; Arrow arrow;
Glowworm glowworm; Glowworm glowworm;
Dragon dragon; Dragon dragon;
DragonFire dragonFire; DragonFire dragonFire;
NPC npc; NPC npc;
TextParticleEntity textParticle; TextParticleEntity textParticle;
SmashParticleEntity smashParticle; SmashParticleEntity smashParticle;
}; };
}; };
typedef struct { typedef struct {
Entity entities[6][1000]; Entity entities[6][1000];
s16 lastSlot[6]; s16 lastSlot[6];
Inventory invs[301];//1 for the player, 300 for chests. Inventory invs[300];
s16 nextInv; s16 nextInv;
} EntityManager; } EntityManager;
EntityManager eManager; EntityManager eManager;
Entity nullEntity; Entity nullEntity;
s8 currentLevel;
double gaussrand();
Entity newItemEntity(Item item, int x, int y, int level); 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 newPassiveEntity(int type, int x, int y, int level);
Entity newZombieEntity(int lvl, 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 newSkeletonEntity(int lvl, int x, int y, int level);
@ -245,6 +246,3 @@ Entity newGlowwormEntity(int x, int y, int level);
Entity newNPCEntity(int type, int x, int y, int level); Entity newNPCEntity(int type, int x, int y, int level);
void addEntityToList(Entity e, EntityManager* em); void addEntityToList(Entity e, EntityManager* em);
void removeEntityFromList(Entity * e,int level,EntityManager* em); void removeEntityFromList(Entity * e,int level,EntityManager* em);

3398
source/Globals.c Normal file → Executable file

File diff suppressed because it is too large Load diff

138
source/Globals.h Normal file → Executable file
View file

@ -1,11 +1,14 @@
#pragma once #pragma once
#include <3ds.h> #include <3ds.h>
#include "SaveLoad.h" #include "Entity.h"
#include "Player.h"
#include "Input.h" #include "Input.h"
#include "MapGen.h" #include "MapGen.h"
#include "Quests.h" #include "Quests.h"
#include "icons2_png.h" #include "icons_png.h"
#include "player_png.h"
#include "Font_png.h" #include "Font_png.h"
#include "bottombg_png.h" #include "bottombg_png.h"
@ -17,18 +20,23 @@
#define MENU_TUTORIAL 2 #define MENU_TUTORIAL 2
#define MENU_ABOUT 3 #define MENU_ABOUT 3
#define MENU_SETTINGS 4 #define MENU_SETTINGS 4
#define MENU_INVENTORY 5 #define MENU_LOADGAME 5
#define MENU_CRAFTING 6 #define MENU_SETTINGS_REBIND 6
#define MENU_CONTAINER 7 #define MENU_SETTINGS_TP 7
#define MENU_WIN 8 #define MENU_MULTIPLAYER_HOST 8
#define MENU_LOSE 9 #define MENU_MULTIPLAYER_JOIN 9
#define MENU_PAUSED 10 #define MENU_MULTIPLAYER_WAIT 10
#define MENU_LOADGAME 11 #define MENU_LOADING 11
#define MENU_SETTINGS_REBIND 12
#define MENU_SETTINGS_TP 13 #define MENU_PAUSED 100
#define MENU_DUNGEON 14 #define MENU_INVENTORY 101
#define MENU_NPC 15 #define MENU_CRAFTING 102
#define MENU_MULTIPLAYER 16 #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_GIRL 0
#define NPC_PRIEST 1 #define NPC_PRIEST 1
@ -78,31 +86,32 @@
#define SWAP_UINT32(x) (((x) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | ((x) << 24)) #define SWAP_UINT32(x) (((x) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | ((x) << 24))
//WARNING: Having this set to different values in different clients will break multiplayer!
#define TESTGODMODE false
bool paused;
u32 localUID;
bool screenShot;
int loadedtp; int loadedtp;
u8 MODEL_3DS; u8 MODEL_3DS;
extern char versionText[34]; extern char versionText[34];
Entity player;
bool shouldRenderDebug; bool shouldRenderDebug;
bool shouldSpeedup; bool shouldSpeedup;
bool shouldRenderMap; bool shouldRenderMap;
u8 zoomLevel; bool UnderStrengthEffect;
char mapText[32]; bool UnderSpeedEffect;
s16 mScrollX, mScrollY; bool regening;
bool UnderSwimBreathEffect;
sf2d_texture *icons; sf2d_texture *icons;
sf2d_texture *playerSprites;
sf2d_texture *font; sf2d_texture *font;
sf2d_texture *bottombg; sf2d_texture *bottombg;
sf2d_texture * minimap[6]; sf2d_texture *minimap[6];
u8 map[6][128*128];
u8 data[6][128*128];
u8 minimapData[128*128];
u8 compassData[6][3];
u32 dirtColor[5]; u32 dirtColor[5];
u32 grassColor; u32 grassColor;
@ -124,65 +133,72 @@ char currentFileName[256];
extern u8 currentMenu; extern u8 currentMenu;
extern char fpsstr[]; extern char fpsstr[];
u8 initGame; u8 initGame;
u8 initMPGame;
u8 initBGMap; u8 initBGMap;
Item noItem; Item noItem;
int airWizardHealthDisplay; int airWizardHealthDisplay;
s16 awX, awY; s16 awX, awY;
u32 tickCount;
RecipeManager* currentRecipes;
Entity* curChestEntity;
char* currentCraftTitle;
s16 curInvSel;
bool quitGame; bool quitGame;
s8 currentSelection; s8 currentSelection;
bool isRemote;
u16 daytime; typedef struct _worldData {
int day; u8 map[6][128*128];
u8 season; u8 data[6][128*128];
bool rain;
void tickTile(int x, int y); 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); 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 tickEntity(Entity* e);
void tickTouchMap();
void tickTouchQuickSelect();
void trySpawn(int count, int level); 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); 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 intersectsEntity(int x, int y, int r, Entity* e);
bool EntityBlocksEntity(Entity* e1, Entity* e2); bool EntityBlocksEntity(Entity* e1, Entity* e2);
void EntityVsEntity(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 openCraftingMenu(PlayerData *pd, RecipeManager *rm, char *title);
void tickPlayer(); bool useEntity(PlayerData *pd, Entity* e);
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 enterDungeon(); bool isWater(s8 level, int xt, int yt);
void leaveDungeon();
void setMinimapVisible(int level, int x, int y, bool visible); void playerHurtTile(PlayerData *pd, int tile, s8 level, int xt, int yt, int damage, int dir);
bool getMinimapVisible(int level, int x, int y); void playerEntityInteract(PlayerData *pd, Entity* e);
u32 getMinimapColor(int level, int x, int y);
void initMinimapLevel(int level, bool loadUpWorld); 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 updateLevel1Map();
void reloadColors(); void reloadColors();

379
source/Ingame.c Normal file
View file

@ -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<playerCount; i++) {
initPlayer(players+i);
}
if (playerCount > 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; i<playerCount; i++) {
if(!players[i].isSpawned) {
playerSpawn(players+i);
}
}
// Init player maps
for(int i=0; i<playerCount; i++) {
initMiniMap(players+i);
}
stallCounter = 0;
}
void syncedTick() {
int i;
setListenerPosition(getLocalPlayer()->entity.level, getLocalPlayer()->entity.x, getLocalPlayer()->entity.y);
//win/death menus
for(i=0; i<playerCount; i++) {
if (players[i].entity.p.isDead) {
if (players[i].entity.p.endTimer < 1) {
players[i].ingameMenu = MENU_LOSE;
}
--players[i].entity.p.endTimer;
} else if (players[i].entity.p.hasWon) {
if (players[i].entity.p.endTimer < 1) {
players[i].ingameMenu = MENU_WIN;
}
--players[i].entity.p.endTimer;
}
}
//update worldData (daytime,season and weather)
++worldData.daytime;
if(worldData.daytime>=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; i<playerCount; i++) {
if(players[i].entity.level==level) {
hasPlayer = true;
}
}
if(!hasPlayer) continue;
//tick tiles
for (i = 0; i < 324; ++i) {
int xx = rand() & 127;
int yy = rand() & 127;
tickTile(level, xx, yy);
}
}
for(i=0; i<playerCount; i++) {
ingameMenuTickTouch(players+i);
bool inmenu = false;
if(players[i].ingameMenu != MENU_NONE) {
ingameMenuTick(players+i, players[i].ingameMenu);
inmenu = true;
}
tickPlayer(players+i, inmenu);
}
//for every active level
for(level = 0; level < 6; level++) {
if(level==5 && !dungeonActive()) continue;
bool hasPlayer = false;
for(i=0; i<playerCount; i++) {
if(players[i].entity.level==level) {
hasPlayer = true;
}
}
if(!hasPlayer) continue;
//spawn entities
if(eManager.lastSlot[level]<80 && level != 5) {
trySpawn(1, level);
}
//update entities
for (i = 0; i < eManager.lastSlot[level]; ++i) {
Entity * e = &eManager.entities[level][i];
PlayerData * p = getNearestPlayer(level, e->x, 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; i<playerCount; i++) {
renderPlayer(players+i);
}
renderWeather(getLocalPlayer()->entity.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);
}

9
source/Ingame.h Normal file
View file

@ -0,0 +1,9 @@
#pragma once
#include <3ds.h>
void startGame(bool load, char *filename);
void tickGame();
void renderGame();
void exitGame();

528
source/IngameMenu.c Normal file
View file

@ -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(amnt<ttlCst) col = 0xFF7F7F7F;
renderItemIcon(rec->costs[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);
}

8
source/IngameMenu.h Normal file
View file

@ -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);

70
source/Input.c Normal file → Executable file
View file

@ -1,23 +1,61 @@
#include "Input.h" #include "Input.h"
void toggleKey(Key* key, bool held, bool down){ void toggleKey(Key* key, bool held, bool down){
key->down = held; key->down = held;
key->clicked = down; key->clicked = down;
} }
void tickKeys(u32 held, u32 down){ void tickKeys(Inputs *inputs, u32 held, u32 down){
hidTouchRead(&k_touch); // Update touch position hidTouchRead(&(inputs->k_touch)); // Update touch position
toggleKey(&k_up, held & k_up.input, down & k_up.input); toggleKey(&(inputs->k_up), held & localInputs.k_up.input, down & localInputs.k_up.input);
toggleKey(&k_down, held & k_down.input, down & k_down.input); toggleKey(&(inputs->k_down), held & localInputs.k_down.input, down & localInputs.k_down.input);
toggleKey(&k_left, held & k_left.input, down & k_left.input); toggleKey(&(inputs->k_left), held & localInputs.k_left.input, down & localInputs.k_left.input);
toggleKey(&k_right, held & k_right.input, down & k_right.input); toggleKey(&(inputs->k_right), held & localInputs.k_right.input, down & localInputs.k_right.input);
toggleKey(&k_pause, held & k_pause.input, down & k_pause.input); toggleKey(&(inputs->k_pause), held & localInputs.k_pause.input, down & localInputs.k_pause.input);
toggleKey(&k_attack, held & k_attack.input, down & k_attack.input); toggleKey(&(inputs->k_attack), held & localInputs.k_attack.input, down & localInputs.k_attack.input);
toggleKey(&k_menu, held & k_menu.input, down & k_menu.input); toggleKey(&(inputs->k_pickup), held & localInputs.k_pickup.input, down & localInputs.k_pickup.input);
toggleKey(&k_accept, held & k_accept.input, down & k_accept.input); toggleKey(&(inputs->k_use), held & localInputs.k_use.input, down & localInputs.k_use.input);
toggleKey(&k_decline, held & k_decline.input, down & k_decline.input); toggleKey(&(inputs->k_menu), held & localInputs.k_menu.input, down & localInputs.k_menu.input);
toggleKey(&k_delete, held & k_delete.input, down & k_delete.input); toggleKey(&(inputs->k_accept), held & localInputs.k_accept.input, down & localInputs.k_accept.input);
toggleKey(&k_menuNext, held & k_menuNext.input, down & k_menuNext.input); toggleKey(&(inputs->k_decline), held & localInputs.k_decline.input, down & localInputs.k_decline.input);
toggleKey(&k_menuPrev, held & k_menuPrev.input, down & k_menuPrev.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;
}

41
source/Input.h Normal file → Executable file
View file

@ -1,24 +1,35 @@
#pragma once
#include <3ds.h> #include <3ds.h>
//only down and clicked need to be send, input is for config stuff
typedef struct { typedef struct {
bool down, clicked; bool down, clicked;
int input; int input;
} Key; } Key;
Key k_null; typedef struct {
Key k_up; Key k_null;
Key k_down; Key k_up;
Key k_left; Key k_down;
Key k_right; Key k_left;
Key k_attack; Key k_right;
Key k_menu; Key k_attack;
Key k_pause; Key k_pickup;
Key k_accept; Key k_use;
Key k_decline; Key k_menu;
Key k_delete; Key k_pause;
Key k_menuNext; Key k_accept;
Key k_menuPrev; Key k_decline;
touchPosition k_touch; 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); bool clicked(Key key);

502
source/Item.c Normal file → Executable file
View file

@ -2,165 +2,169 @@
char currentName[16]; char currentName[16];
bool isItemEmpty(Item* item){ bool isItemEmpty(Item* item) {
if(item->id < 6 || item->id > 100 || item->onlyOne == true) return false; if(item->id < 6 || item->id > 100 || item->onlyOne == true) return false;
if(item->countLevel < 1) return true; if(item->countLevel < 1) return true;
return false; return false;
} }
void pushItemToInventoryFront(Item item, Inventory * inv){ void pushItemToInventoryFront(Item item, Inventory * inv) {
if(inv->lastSlot < 300) ++inv->lastSlot; if(inv->lastSlot < 300) ++inv->lastSlot;
int i; int i;
for(i = inv->lastSlot;i > 0;--i){ for(i = inv->lastSlot;i > 0;--i){
inv->items[i] = inv->items[i-1]; // Move the items up the list. inv->items[i] = inv->items[i-1]; // Move the items up the list.
inv->items[i].slotNum = i; inv->items[i].slotNum = i;
} }
item.invPtr = (int*)inv; item.invPtr = (int*)inv;
inv->items[0] = item; inv->items[0] = item;
inv->items[0].slotNum = 0; inv->items[0].slotNum = 0;
} }
void addItemToInventory(Item item, Inventory * inv){ void addItemToInventory(Item item, Inventory * inv) {
if(!item.onlyOne){ if(!item.onlyOne){
int i; int i;
for(i = 0;i < inv->lastSlot;++i){ //Search inventory if item already exists. for(i = 0;i < inv->lastSlot;++i){ //Search inventory if item already exists.
if(inv->items[i].id == item.id){ if(inv->items[i].id == item.id){
inv->items[i].countLevel+=item.countLevel; inv->items[i].countLevel+=item.countLevel;
return; return;
} }
} }
} }
item.slotNum = inv->lastSlot; item.slotNum = inv->lastSlot;
item.invPtr = (int*)inv; item.invPtr = (int*)inv;
inv->items[inv->lastSlot] = item; inv->items[inv->lastSlot] = item;
++inv->lastSlot; ++inv->lastSlot;
} }
void removeItemFromCurrentInv(Item* item){ void removeItemFromCurrentInv(Item* item) {
removeItemFromInventory(item->slotNum, (Inventory*)item->invPtr); removeItemFromInventory(item->slotNum, (Inventory*)item->invPtr);
} }
Item nullItem; Item nullItem;
void removeItemFromInventory(int slot, Inventory * inv){ void removeItemFromInventory(int slot, Inventory * inv) {
int i; int i;
for(i = slot;i < inv->lastSlot - 1;++i){ for(i = slot;i < inv->lastSlot - 1;++i){
inv->items[i] = inv->items[i + 1]; // Move the items down. inv->items[i] = inv->items[i + 1]; // Move the items down.
inv->items[i].slotNum--; inv->items[i].slotNum--;
} }
--inv->lastSlot; --inv->lastSlot;
inv->items[inv->lastSlot] = nullItem; // Make the last slot null. inv->items[inv->lastSlot] = nullItem; // Make the last slot null.
} }
Item newItem(int id, int cLevel){ Item newItem(int id, int cLevel) {
Item item; Item item;
item.id = id; item.id = id;
if(id != ITEM_NULL){ if(id != ITEM_NULL){
if(cLevel > 999) cLevel = 999; if(cLevel > 999) cLevel = 999;
item.countLevel = cLevel; item.countLevel = cLevel;
if(id < 7 || (id > 27 && id < 51) || id > 100) item.onlyOne = true; // Tools + Furniture. if(id < 7 || (id > 27 && id < 51) || id > 100) item.onlyOne = true; // Tools + Furniture.
else item.onlyOne = false; else item.onlyOne = false;
} }
item.chestPtr = NULL; item.chestPtr = NULL;
return item; return item;
} }
Item* getItemFromInventory(int itemID, Inventory * inv){ Item* getItemFromInventory(int itemID, Inventory * inv) {
int i; int i;
for(i = 0;i < inv->lastSlot;++i){ for(i = 0;i < inv->lastSlot;++i){
if(inv->items[i].id == itemID){ if(inv->items[i].id == itemID){
return &inv->items[i]; return &inv->items[i];
} }
} }
return (Item*)NULL; return (Item*)NULL;
} }
int countItemInv(int itemID, int level, Inventory* inv){ int countItemInv(int itemID, int level, Inventory* inv) {
int i, count = 0; int i, count = 0;
for(i = 0;i < inv->lastSlot;++i){ for(i = 0;i < inv->lastSlot;++i){
if(inv->items[i].id == itemID){ if(inv->items[i].id == itemID){
if(inv->items[i].onlyOne){ if(inv->items[i].onlyOne){
if(level == inv->items[i].countLevel) count++; if(level == inv->items[i].countLevel) count++;
} else count += inv->items[i].countLevel; } else count += inv->items[i].countLevel;
} }
} }
return count; return count;
} }
char* getItemName(int itemID, int countLevel){ char* getItemName(int itemID, int countLevel) {
switch(itemID){ switch(itemID){
case TOOL_SHOVEL: case TOOL_SHOVEL:
switch(countLevel){ switch(countLevel){
case 1: return "Rock Shovel"; case 1: return "Rock Shovel";
case 2: return "Iron Shovel"; case 2: return "Iron Shovel";
case 3: return "Gold Shovel"; case 3: return "Gold Shovel";
case 4: return "Gem Shovel"; case 4: return "Gem Shovel";
default: return "Wood Shovel"; default: return "Wood Shovel";
} }
case TOOL_HOE: case TOOL_HOE:
switch(countLevel){ switch(countLevel){
case 1: return "Rock Hoe"; case 1: return "Rock Hoe";
case 2: return "Iron Hoe"; case 2: return "Iron Hoe";
case 3: return "Gold Hoe"; case 3: return "Gold Hoe";
case 4: return "Gem Hoe"; case 4: return "Gem Hoe";
default: return "Wood Hoe"; default: return "Wood Hoe";
} }
case TOOL_SWORD: case TOOL_SWORD:
switch(countLevel){ switch(countLevel){
case 1: return "Rock Sword"; case 1: return "Rock Sword";
case 2: return "Iron Sword"; case 2: return "Iron Sword";
case 3: return "Gold Sword"; case 3: return "Gold Sword";
case 4: return "Gem Sword"; case 4: return "Gem Sword";
default: return "Wood Sword"; default: return "Wood Sword";
} }
case TOOL_PICKAXE: case TOOL_PICKAXE:
switch(countLevel){ switch(countLevel){
case 1: return "Rock Pickaxe"; case 1: return "Rock Pickaxe";
case 2: return "Iron Pickaxe"; case 2: return "Iron Pickaxe";
case 3: return "Gold Pickaxe"; case 3: return "Gold Pickaxe";
case 4: return "Gem Pickaxe"; case 4: return "Gem Pickaxe";
default: return "Wood Pickaxe"; default: return "Wood Pickaxe";
} }
case TOOL_AXE: case TOOL_AXE:
switch(countLevel){ switch(countLevel){
case 1: return "Rock Axe"; case 1: return "Rock Axe";
case 2: return "Iron Axe"; case 2: return "Iron Axe";
case 3: return "Gold Axe"; case 3: return "Gold Axe";
case 4: return "Gem Axe"; case 4: return "Gem Axe";
default: return "Wood Axe"; default: return "Wood Axe";
} }
case ITEM_ANVIL: return "Anvil"; case ITEM_ANVIL: return "Anvil";
case ITEM_CHEST: return "Chest"; case ITEM_CHEST: return "Chest";
case ITEM_OVEN: return "Oven"; case ITEM_OVEN: return "Oven";
case ITEM_FURNACE: return "Furnace"; case ITEM_FURNACE: return "Furnace";
case ITEM_WORKBENCH: return "Workbench"; case ITEM_WORKBENCH: return "Workbench";
case ITEM_LANTERN: return "Lantern"; case ITEM_POTION_MAKER: return "Potion Maker";
case ITEM_POWGLOVE: return "Power Glove"; case ITEM_LANTERN: return "Lantern";
case ITEM_FLOWER: sprintf(currentName,"%d Flower", countLevel); return currentName; case ITEM_POWGLOVE: return "Power Glove";
case ITEM_WOOD: sprintf(currentName,"%d Wood", countLevel); return currentName; case ITEM_FLOWER: sprintf(currentName,"%d Flower", countLevel); return currentName;
case ITEM_STONE: sprintf(currentName,"%d Stone", countLevel); return currentName; case ITEM_WOOD: sprintf(currentName,"%d Wood", countLevel); return currentName;
case ITEM_SAND: sprintf(currentName,"%d Sand", countLevel); return currentName; case ITEM_STONE: sprintf(currentName,"%d Stone", countLevel); return currentName;
case ITEM_DIRT: sprintf(currentName,"%d Dirt", countLevel); return currentName; case ITEM_SAND: sprintf(currentName,"%d Sand", countLevel); return currentName;
case ITEM_CLOUD: sprintf(currentName,"%d Cloud", countLevel); return currentName; case ITEM_DIRT: sprintf(currentName,"%d Dirt", countLevel); return currentName;
case ITEM_ACORN: sprintf(currentName,"%d Acorn", countLevel); return currentName; case ITEM_CLOUD: sprintf(currentName,"%d Cloud", countLevel); return currentName;
case ITEM_CACTUS: sprintf(currentName,"%d Cactus", countLevel); return currentName; case ITEM_ACORN: sprintf(currentName,"%d Acorn", countLevel); return currentName;
case ITEM_SEEDS: sprintf(currentName,"%d Seeds", countLevel); return currentName; case ITEM_CACTUS: sprintf(currentName,"%d Cactus", countLevel); return currentName;
case ITEM_WHEAT: sprintf(currentName,"%d Wheat", countLevel); return currentName; case ITEM_SEEDS: sprintf(currentName,"%d Seeds", countLevel); return currentName;
case ITEM_FLESH: sprintf(currentName,"%d Flesh", countLevel); return currentName; case ITEM_WHEAT: sprintf(currentName,"%d Wheat", countLevel); return currentName;
case ITEM_BREAD: sprintf(currentName,"%d Bread", countLevel); return currentName; case ITEM_FLESH: sprintf(currentName,"%d Flesh", countLevel); return currentName;
case ITEM_APPLE: sprintf(currentName,"%d Apple", 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_GOLD_APPLE: sprintf(currentName,"%d Golden Apple", countLevel); return currentName;
case ITEM_STRENGTH_POTION: sprintf(currentName,"%d Strength Potion", countLevel); return currentName; case ITEM_STRENGTH_POTION: sprintf(currentName,"%d Strength Potion", countLevel); return currentName;
case ITEM_COAL: sprintf(currentName,"%d Coal", countLevel); return currentName; case ITEM_SPEED_POTION: sprintf(currentName,"%d Speed Potion", countLevel); return currentName;
case ITEM_IRONORE: sprintf(currentName,"%d Iron ore", countLevel); return currentName; case ITEM_REGEN_POTION: sprintf(currentName,"%d Regen Potion", countLevel); return currentName;
case ITEM_GOLDORE: sprintf(currentName,"%d Gold ore", countLevel); return currentName; case ITEM_SWIM_BREATH_POTION: sprintf(currentName,"%d Swim Potion", countLevel); return currentName;
case ITEM_IRONINGOT: sprintf(currentName,"%d Iron ingot", countLevel); return currentName; case ITEM_COAL: sprintf(currentName,"%d Coal", countLevel); return currentName;
case ITEM_GOLDINGOT: sprintf(currentName,"%d Gold ingot", countLevel); return currentName; case ITEM_IRONORE: sprintf(currentName,"%d Iron ore", countLevel); return currentName;
case ITEM_GLASS: sprintf(currentName,"%d Glass", countLevel); return currentName; case ITEM_GOLDORE: sprintf(currentName,"%d Gold ore", countLevel); return currentName;
case ITEM_GEM: sprintf(currentName,"%d Gem", countLevel); return currentName; case ITEM_IRONINGOT: sprintf(currentName,"%d Iron ingot", countLevel); return currentName;
case ITEM_SLIME: sprintf(currentName,"%d Slime", 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_LOOM: return "Loom";
case ITEM_ENCHANTER: return "Enchanter"; case ITEM_ENCHANTER: return "Enchanter";
case ITEM_WALL_WOOD: sprintf(currentName,"%d Wood Wall", countLevel); return currentName; case ITEM_WALL_WOOD: sprintf(currentName,"%d Wood Wall", countLevel); return currentName;
@ -186,92 +190,107 @@ char* getItemName(int itemID, int countLevel){
case ITEM_DRAGON_EGG: sprintf(currentName,"%d Dragon Egg", 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_DRAGON_SCALE: sprintf(currentName,"%d Dragon Scale", countLevel); return currentName;
case ITEM_BOOKSHELVES: sprintf(currentName,"%d Bookshelves", 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_MAGIC_DUST: sprintf(currentName,"%d Magic Dust", countLevel); return currentName;
case ITEM_COIN: sprintf(currentName,"%d Coins", countLevel); return currentName; case ITEM_COIN: sprintf(currentName,"%d Coins", countLevel); return currentName;
case TOOL_BUCKET: case TOOL_BUCKET:
switch(countLevel){ switch(countLevel){
case 1: return "Water Bucket"; case 1: return "Water Bucket";
case 2: return "Lava Bucket"; case 2: return "Lava Bucket";
default: return "Empty Bucket"; default: return "Empty Bucket";
} }
case TOOL_BOW: return "Bow"; case TOOL_BOW: return "Bow";
case TOOL_MAGIC_COMPASS: return "Magic Compass"; case TOOL_MAGIC_COMPASS: return "Magic Compass";
default: return ""; // null 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){ char* getBasicItemName(int itemID, int countLevel) {
switch(itemID){ switch(itemID){
case TOOL_SHOVEL: case TOOL_SHOVEL:
switch(countLevel){ switch(countLevel){
case 1: return "Rock Shovel"; case 1: return "Rock Shovel";
case 2: return "Iron Shovel"; case 2: return "Iron Shovel";
case 3: return "Gold Shovel"; case 3: return "Gold Shovel";
case 4: return "Gem Shovel"; case 4: return "Gem Shovel";
default: return "Wood Shovel"; default: return "Wood Shovel";
} }
case TOOL_HOE: case TOOL_HOE:
switch(countLevel){ switch(countLevel){
case 1: return "Rock Hoe"; case 1: return "Rock Hoe";
case 2: return "Iron Hoe"; case 2: return "Iron Hoe";
case 3: return "Gold Hoe"; case 3: return "Gold Hoe";
case 4: return "Gem Hoe"; case 4: return "Gem Hoe";
default: return "Wood Hoe"; default: return "Wood Hoe";
} }
case TOOL_SWORD: case TOOL_SWORD:
switch(countLevel){ switch(countLevel){
case 1: return "Rock Sword"; case 1: return "Rock Sword";
case 2: return "Iron Sword"; case 2: return "Iron Sword";
case 3: return "Gold Sword"; case 3: return "Gold Sword";
case 4: return "Gem Sword"; case 4: return "Gem Sword";
default: return "Wood Sword"; default: return "Wood Sword";
} }
case TOOL_PICKAXE: case TOOL_PICKAXE:
switch(countLevel){ switch(countLevel){
case 1: return "Rock Pickaxe"; case 1: return "Rock Pickaxe";
case 2: return "Iron Pickaxe"; case 2: return "Iron Pickaxe";
case 3: return "Gold Pickaxe"; case 3: return "Gold Pickaxe";
case 4: return "Gem Pickaxe"; case 4: return "Gem Pickaxe";
default: return "Wood Pickaxe"; default: return "Wood Pickaxe";
} }
case TOOL_AXE: case TOOL_AXE:
switch(countLevel){ switch(countLevel){
case 1: return "Rock Axe"; case 1: return "Rock Axe";
case 2: return "Iron Axe"; case 2: return "Iron Axe";
case 3: return "Gold Axe"; case 3: return "Gold Axe";
case 4: return "Gem Axe"; case 4: return "Gem Axe";
default: return "Wood Axe"; default: return "Wood Axe";
} }
case ITEM_ANVIL: return "Anvil"; case ITEM_ANVIL: return "Anvil";
case ITEM_CHEST: return "Chest"; case ITEM_CHEST: return "Chest";
case ITEM_OVEN: return "Oven"; case ITEM_OVEN: return "Oven";
case ITEM_FURNACE: return "Furnace"; case ITEM_FURNACE: return "Furnace";
case ITEM_WORKBENCH: return "Workbench"; case ITEM_WORKBENCH: return "Workbench";
case ITEM_LANTERN: return "Lantern"; case ITEM_POTION_MAKER: return "Potion Maker";
case ITEM_POWGLOVE: return "Power Glove"; case ITEM_LANTERN: return "Lantern";
case ITEM_FLOWER: return "Flower"; case ITEM_POWGLOVE: return "Power Glove";
case ITEM_WOOD: return "Wood"; case ITEM_FLOWER: return "Flower";
case ITEM_STONE: return "Stone"; case ITEM_WOOD: return "Wood";
case ITEM_SAND: return "Sand"; case ITEM_STONE: return "Stone";
case ITEM_DIRT: return "Dirt"; case ITEM_SAND: return "Sand";
case ITEM_CLOUD: return "Cloud"; case ITEM_DIRT: return "Dirt";
case ITEM_ACORN: return "Acorn"; case ITEM_CLOUD: return "Cloud";
case ITEM_CACTUS: return "Cactus"; case ITEM_ACORN: return "Acorn";
case ITEM_SEEDS: return "Seeds"; case ITEM_CACTUS: return "Cactus";
case ITEM_WHEAT: return "Wheat"; case ITEM_SEEDS: return "Seeds";
case ITEM_FLESH: return "Flesh"; case ITEM_WHEAT: return "Wheat";
case ITEM_BREAD: return "Bread"; case ITEM_FLESH: return "Flesh";
case ITEM_APPLE: return "Apple"; case ITEM_BREAD: return "Bread";
case ITEM_GOLD_APPLE: return "Gold Apple"; case ITEM_APPLE: return "Apple";
case ITEM_COAL: return "Coal"; case ITEM_GOLD_APPLE: return "Gold Apple";
case ITEM_IRONORE: return "Iron ore"; case ITEM_COAL: return "Coal";
case ITEM_GOLDORE: return "Gold ore"; case ITEM_IRONORE: return "Iron ore";
case ITEM_IRONINGOT: return "Iron ingot"; case ITEM_GOLDORE: return "Gold ore";
case ITEM_GOLDINGOT: return "Gold ingot"; case ITEM_IRONINGOT: return "Iron ingot";
case ITEM_GLASS: return "Glass"; case ITEM_GOLDINGOT: return "Gold ingot";
case ITEM_GEM: return "Gem"; case ITEM_GLASS: return "Glass";
case ITEM_SLIME: return "Slime"; case ITEM_GEM: return "Gem";
case ITEM_SLIME: return "Slime";
case ITEM_LOOM: return "Loom"; case ITEM_LOOM: return "Loom";
case ITEM_ENCHANTER: return "Enchanter"; case ITEM_ENCHANTER: return "Enchanter";
case ITEM_WALL_WOOD: return "Wood Wall"; case ITEM_WALL_WOOD: return "Wood Wall";
@ -297,18 +316,35 @@ char* getBasicItemName(int itemID, int countLevel){
case ITEM_DRAGON_EGG: return "Dragon Egg"; case ITEM_DRAGON_EGG: return "Dragon Egg";
case ITEM_DRAGON_SCALE: return "Dragon Scale"; case ITEM_DRAGON_SCALE: return "Dragon Scale";
case ITEM_BOOKSHELVES: return "Bookshelves"; case ITEM_BOOKSHELVES: return "Bookshelves";
case ITEM_MAGIC_DUST: return "Magic Dust"; case ITEM_MAGIC_DUST: return "Magic Dust";
case ITEM_COIN: return "Coin"; case ITEM_COIN: return "Coin";
case ITEM_STRENGTH_POTION: return "Strength Potion"; 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: case TOOL_BUCKET:
switch(countLevel){ switch(countLevel){
case 1: return "Water Bucket"; case 1: return "Water Bucket";
case 2: return "Lava Bucket"; case 2: return "Lava Bucket";
default: return "Empty Bucket"; default: return "Empty Bucket";
} }
case TOOL_BOW: return "Bow"; case TOOL_BOW: return "Bow";
case TOOL_MAGIC_COMPASS: return "Magic Compass"; case TOOL_MAGIC_COMPASS: return "Magic Compass";
default: return ""; // null 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
}
} }

33
source/Item.h Normal file → Executable file
View file

@ -45,6 +45,10 @@
#define ITEM_LOOM 34 #define ITEM_LOOM 34
#define ITEM_ENCHANTER 35 #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_WOOD 51
#define ITEM_WALL_STONE 52 #define ITEM_WALL_STONE 52
@ -71,27 +75,36 @@
#define ITEM_BOOKSHELVES 73 #define ITEM_BOOKSHELVES 73
#define ITEM_MAGIC_DUST 74 #define ITEM_MAGIC_DUST 74
#define ITEM_COIN 75 #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_BUCKET 101
#define TOOL_BOW 102 #define TOOL_BOW 102
#define TOOL_MAGIC_COMPASS 103 #define TOOL_MAGIC_COMPASS 103
#define ITEM_GOLD_APPLE 104 #define ITEM_FISHING_ROD 104
#define ITEM_STRENGTH_POTION 105 #define ITEM_SHEARS 105
#define ARMOR_TEST 120
typedef struct Inventory Inventory; typedef struct Inventory Inventory;
typedef struct { typedef struct {
s16 id; s16 id;
s16 countLevel; // Count for items, Level for tools. s16 countLevel; // Count for items, Level for tools.
s16 slotNum; // Read-only. Do not mess with this. s16 slotNum; // Read-only. Do not mess with this.
bool onlyOne; bool onlyOne;
int* invPtr; // pointer to current inventory. int* invPtr; // pointer to current inventory.
Inventory * chestPtr; // pointer to chest inventory for chest item. Inventory * chestPtr; // pointer to chest inventory for chest item.
} Item; } Item;
struct Inventory { struct Inventory {
Item items[300]; // Maximum of 300 slots in every 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. s16 lastSlot; // lastSlot can also be used to read the size of the inventory.
}; };
bool isItemEmpty(Item* item); bool isItemEmpty(Item* item);

798
source/MapGen.c Normal file → Executable file

File diff suppressed because it is too large Load diff

4
source/MapGen.h Normal file → Executable file
View file

@ -2,7 +2,6 @@
#include <math.h> #include <math.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <time.h>
#include <3ds.h> #include <3ds.h>
#include "Globals.h" #include "Globals.h"
@ -10,7 +9,6 @@
float nextFloat(); float nextFloat();
double sample(double * values, int x, int y); double sample(double * values, int x, int y);
double * Noise(int w, int h, int featureSize); double * Noise(int w, int h, int featureSize);
void newSeed();
void createAndValidateTopMap(int w, int h, int level, 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 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 createAndValidateUndergroundMap(int w, int h, int depthLevel, int level, u8 * map, u8 * data);
@ -28,4 +26,4 @@ void createVillage(int w, int h, int level, u8 * map, u8 * data);
void createDwarfHouse(int w, int h, int level, u8 * map, u8 * data); void createDwarfHouse(int w, int h, int level, u8 * map, u8 * data);
bool hasNPC; bool hasNPC;
void createDungeonRoom(int w, int h, bool dragon, int level, u8 * map, u8 * data); void createDungeonRoom(int w, int h, bool dragon, int level, u8 * map, u8 * data);

2505
source/Menu.c Normal file → Executable file

File diff suppressed because it is too large Load diff

0
source/Menu.h Normal file → Executable file
View file

482
source/MenuTutorial.c Normal file → Executable file
View file

@ -1,250 +1,260 @@
#include "MenuTutorial.h" #include "MenuTutorial.h"
u8 pageNum = 0; u8 pageNum = 0;
u8 maxPageNum = 6; u8 maxPageNum = 7;
u32 biasedCirclePad(u32 in){ u32 biasedCirclePad(u32 in){
if(in & KEY_CPAD_UP) return KEY_CPAD_UP; if(in & KEY_CPAD_UP) return KEY_CPAD_UP;
else if(in & KEY_CPAD_DOWN) return KEY_CPAD_DOWN; else if(in & KEY_CPAD_DOWN) return KEY_CPAD_DOWN;
else if(in & KEY_CPAD_LEFT) return KEY_CPAD_LEFT; else if(in & KEY_CPAD_LEFT) return KEY_CPAD_LEFT;
else if(in & KEY_CPAD_RIGHT) return KEY_CPAD_RIGHT; else if(in & KEY_CPAD_RIGHT) return KEY_CPAD_RIGHT;
else return (in & -in); else return (in & -in);
} }
u32 biasedMenuXY(u32 in){ u32 biasedMenuXY(u32 in){
if(in & KEY_X) return KEY_X; if(in & KEY_X) return KEY_X;
else if(in & KEY_Y) return KEY_Y; else if(in & KEY_Y) return KEY_Y;
else return (in & -in); else return (in & -in);
} }
/** Oh my god, this was so damn tedious to make. */ /** Oh my god, this was so damn tedious to make. */
void renderTutorialPage(bool topScreen){ void renderTutorialPage(bool topScreen){
if(topScreen){ if(topScreen){
drawTextColor("How to Play",(400-11*12)/2,12,0xFF00AFAF); drawTextColor("How to Play",(400-11*12)/2,12,0xFF00AFAF);
switch(pageNum){ switch(pageNum){
case 0: // Moving the character case 0: // Moving the character
drawTextColor("Movement",(400-8*12)/2,40,0xFF007FBF); drawTextColor("Movement",(400-8*12)/2,40,0xFF007FBF);
drawText("Press to move up",92,90); drawText("Press to move up",92,90);
renderButtonIcon(biasedCirclePad(k_up.input), 164, 88, 1); renderButtonIcon(biasedCirclePad(localInputs.k_up.input), 164, 88, 1);
drawText("Press to move down",80,120); drawText("Press to move down",80,120);
renderButtonIcon(biasedCirclePad(k_down.input), 152, 118, 1); renderButtonIcon(biasedCirclePad(localInputs.k_down.input), 152, 118, 1);
drawText("Press to move left",80,150); drawText("Press to move left",80,150);
renderButtonIcon(biasedCirclePad(k_left.input), 152, 148, 1); renderButtonIcon(biasedCirclePad(localInputs.k_left.input), 152, 148, 1);
drawText("Press to move right",74,180); drawText("Press to move right",74,180);
renderButtonIcon(biasedCirclePad(k_right.input), 146, 178, 1); renderButtonIcon(biasedCirclePad(localInputs.k_right.input), 146, 178, 1);
break; break;
case 1: // Attacking case 1: // Attacking
drawTextColor("Attacking",(400-9*12)/2,40,0xFF007FBF); drawTextColor("Attacking",(400-9*12)/2,40,0xFF007FBF);
drawText("Press to Attack",98,80); drawText("Press to Attack",98,80);
renderButtonIcon(k_attack.input & -k_attack.input, 168, 78, 1); renderButtonIcon(localInputs.k_attack.input & -localInputs.k_attack.input, 168, 78, 1);
drawText("Attack with an item to use it",26,120); drawText("Attack with an item to use it",26,120);
drawText("Use the axe to cut down trees",26,140); drawText("Use the axe to cut down trees",26,140);
drawText("Use the sword to attack enemies",14,160); drawText("Use the sword to attack enemies",14,160);
drawText("Use the shovel to dig ground",32,180); drawText("Use the shovel to dig ground",32,180);
drawText("Use the pickaxe to mine rock/ore",8,200); drawText("Use the pickaxe to mine rock/ore",8,200);
break; break;
case 2: // Inventory case 2: // Inventory
drawTextColor("Inventory",(400-9*12)/2,40,0xFF007FBF); drawTextColor("Inventory",(400-9*12)/2,40,0xFF007FBF);
drawText("Press to open the menu",56,80); drawText("Press to open the menu",56,80);
renderButtonIcon(biasedMenuXY(k_menu.input), 126, 78, 1); renderButtonIcon(biasedMenuXY(localInputs.k_menu.input), 126, 78, 1);
drawText("Press to scroll up",80,110); drawText("Press to scroll up",80,110);
renderButtonIcon(biasedCirclePad(k_up.input), 152, 108, 1); renderButtonIcon(biasedCirclePad(localInputs.k_up.input), 152, 108, 1);
drawText("Press to scroll down",68,140); drawText("Press to scroll down",68,140);
renderButtonIcon(biasedCirclePad(k_down.input), 140, 138, 1); renderButtonIcon(biasedCirclePad(localInputs.k_down.input), 140, 138, 1);
drawText("Press to select an item",50,170); drawText("Press to select an item",50,170);
renderButtonIcon(k_accept.input & -k_accept.input, 120, 168, 1); renderButtonIcon(localInputs.k_accept.input & -localInputs.k_accept.input, 120, 168, 1);
drawText("Press to close the menu",50,200); drawText("Press to close the menu",50,200);
renderButtonIcon(k_decline.input & -k_decline.input, 120, 198, 1); renderButtonIcon(localInputs.k_decline.input & -localInputs.k_decline.input, 120, 198, 1);
break; break;
case 3: // Furniture case 3: // Furniture
drawTextColor("Furniture",(400-9*12)/2,40,0xFF007FBF); drawTextColor("Furniture",(400-9*12)/2,40,0xFF007FBF);
drawText("Use furniture for item crafting",(400-31*12)/2,74); drawText("Use furniture for item crafting",(400-31*12)/2,74);
drawText("Press to open the menu",56,100); drawText("Press to open the menu",56,100);
renderButtonIcon(biasedMenuXY(k_menu.input), 126, 98, 1); renderButtonIcon(biasedMenuXY(localInputs.k_menu.input), 126, 98, 1);
drawText("while infront of the furniture",(400-30*12)/2,116); drawText("while infront of the furniture",(400-30*12)/2,116);
drawText("Use the lantern item to light",(400-29*12)/2,144); drawText("Use the lantern item to light",(400-29*12)/2,144);
drawText("up underground areas",(400-20*12)/2,160); drawText("up underground areas",(400-20*12)/2,160);
drawText("Use the power glove item to",(400-27*12)/2,184); drawText("Use the power glove item to",(400-27*12)/2,184);
drawText("pick up furniture",(400-17*12)/2,200); drawText("pick up furniture",(400-17*12)/2,200);
break; break;
case 4: // Crafting case 4: // Crafting
drawTextColor("Crafting",(400-8*12)/2,40,0xFF007FBF); drawTextColor("Crafting",(400-8*12)/2,40,0xFF007FBF);
drawText("Create new items and tools",(400-26*12)/2,74); 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("Go up to a furniture item and",(400-29*12)/2,104);
drawText("Press to open the menu",56,120); drawText("Press to open the menu",56,120);
renderButtonIcon(biasedMenuXY(k_menu.input), 126, 118, 1); renderButtonIcon(biasedMenuXY(localInputs.k_menu.input), 126, 118, 1);
drawText("Gather up the required materials",(400-32*12)/2,150); drawText("Gather up the required materials",(400-32*12)/2,150);
drawText("and then press to craft it",(400-28*12)/2,166); drawText("and then press to craft it",(400-28*12)/2,166);
renderButtonIcon(k_accept.input & -k_accept.input, 210, 164, 1); renderButtonIcon(localInputs.k_accept.input & -localInputs.k_accept.input, 210, 164, 1);
break; break;
case 5: // Farming case 5: // Farming
drawTextColor("Farming",(400-7*12)/2,40,0xFF007FBF); drawTextColor("Farming",(400-7*12)/2,40,0xFF007FBF);
drawText("Grow wheat to make bread",(400-24*12)/2,74); drawText("Grow wheat to make bread",(400-24*12)/2,74);
drawText("Dig up grass to gather seeds",(400-28*12)/2,94); 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("Use the hoe to till ground",(400-26*12)/2,114);
drawText("Harvest wheat when it is yellow",(400-31*12)/2,134); 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("Use the oven to bake bread",(400-26*12)/2,154);
drawText("It takes 4 wheat to craft bread",(400-31*12)/2,174); drawText("It takes 4 wheat to craft bread",(400-31*12)/2,174);
break; break;
case 6: // Mining case 6: // Mining
drawTextColor("Mining",(400-6*12)/2,40,0xFF007FBF); drawTextColor("Mining",(400-6*12)/2,40,0xFF007FBF);
drawText("Use a pickaxe tool for mining",(400-29*12)/2,74); drawText("Use a pickaxe tool for mining",(400-29*12)/2,74);
drawText("Mine rocks for stone",(400-20*12)/2,94); drawText("Mine rocks for stone",(400-20*12)/2,94);
drawText("Mine iron ore for iron",(400-22*12)/2,114); drawText("Mine iron ore for iron",(400-22*12)/2,114);
drawText("Mine gold ore for gold",(400-22*12)/2,134); drawText("Mine gold ore for gold",(400-22*12)/2,134);
drawText("Mine gem ore to get gems",(400-24*12)/2,154); 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("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); drawText("make an ingot inside a furnace",(400-30*12)/2,210);
break; break;
} case 7: // Potion Brewing
} else { drawTextColor("Brewing",(400-6*12)/2,40,0xFF007FBF);
switch(pageNum){ drawText("Create potions.",(400-13*12)/2,74);
case 0: // Moving the character drawText("The potions give you abilities",(400-29*12)/2,94);
render16(30,56,16,112,0);//Player up drawText("Like speed and strength",(400-22*12)/2,114);
renderButtonIcon(biasedCirclePad(k_up.input), 30,40, 2); drawText("They are hard to obtain",(400-22*12)/2,134);
render16(60,56,0,112,0);//Player down break;
renderButtonIcon(biasedCirclePad(k_down.input), 60,40, 2); }
render16(90,56,48,112,1);//Player left } else {
renderButtonIcon(biasedCirclePad(k_left.input), 90,40, 2); switch(pageNum){
render16(120,56,48,112,0);//Player right case 0: // Moving the character
renderButtonIcon(biasedCirclePad(k_right.input), 120,40, 2); render16(30,56,16,112,0);//Player up
break; renderButtonIcon(biasedCirclePad(localInputs.k_up.input), 30,40, 2);
case 1: // Attacking render16(60,56,0,112,0);//Player down
render16(60,56,0,112,0);//Player-down renderButtonIcon(biasedCirclePad(localInputs.k_down.input), 60,40, 2);
renderButtonIcon(k_attack.input & -k_attack.input, 80, 56, 2); render16(90,56,48,112,1);//Player left
renderc(60,68,16,160,16,8,2);//Slash 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 menuRenderTilePit(12,20,256,0);// grass pit
render16(12+8,20+4,256,48,0);//Tree render16(12+8,20+4,256,48,0);//Tree
renderc(12+9,20+14,16,160,16,8,0);//Slash renderc(12+9,20+14,16,160,16,8,0);//Slash
render(12+9+4,20+14,192,144,0);//Axe render(12+9+4,20+14,192,144,0);//Axe
render16(12+9,20+18,16,112,0);//Player-up render16(12+9,20+18,16,112,0);//Player-up
menuRenderTilePit(122,62,320,0);// sand pit menuRenderTilePit(122,62,320,0);// sand pit
render16(130,70,256,16,0);// hole render16(130,70,256,16,0);// hole
render16(116,70,48,112,0);//Player-right render16(116,70,48,112,0);//Player-right
renderb(136,76,16,152,0,sandColor);// Sand item renderb(136,76,16,152,0,sandColor);// Sand item
renderc(128,70,40,160,8,16,0);//Slash renderc(128,70,40,160,8,16,0);//Slash
render(130,74,0,144,0);//Shovel render(130,74,0,144,0);//Shovel
break; break;
case 2: // Inventory case 2: // Inventory
renderFrame(4,4,17,11,0xFFFF1010); renderFrame(4,4,17,11,0xFFFF1010);
renderItemStuffWithText(ITEM_APPLE,5,false,80,78); renderItemStuffWithText(ITEM_APPLE,5,false,80,78);
renderItemStuffWithText(ITEM_SLIME,11,false,80,94); renderItemStuffWithText(ITEM_SLIME,11,false,80,94);
renderItemStuffWithText(TOOL_SWORD,4,true,80,110); renderItemStuffWithText(TOOL_SWORD,4,true,80,110);
renderItemStuffWithText(ITEM_IRONORE,3,false,80,126); renderItemStuffWithText(ITEM_IRONORE,3,false,80,126);
renderItemStuffWithText(ITEM_IRONINGOT,11,false,80,142); renderItemStuffWithText(ITEM_IRONINGOT,11,false,80,142);
sf2d_draw_rectangle(64, 110, 12, 12, 0xFF); sf2d_draw_rectangle(64, 110, 12, 12, 0xFF);
drawText(">", 64, 110); drawText(">", 64, 110);
renderButtonIcon(biasedCirclePad(k_up.input), 44, 92, 1); renderButtonIcon(biasedCirclePad(localInputs.k_up.input), 44, 92, 1);
renderButtonIcon(k_accept.input & -k_accept.input, 44, 108, 1); renderButtonIcon(localInputs.k_accept.input & -localInputs.k_accept.input, 44, 108, 1);
renderButtonIcon(biasedCirclePad(k_down.input), 44, 125, 1); renderButtonIcon(biasedCirclePad(localInputs.k_down.input), 44, 125, 1);
break; break;
case 3: // Furniture case 3: // Furniture
sf2d_draw_rectangle(64, 48, 192, 32, grassColor); sf2d_draw_rectangle(64, 48, 192, 32, grassColor);
renderc(32,24,64,128,96,16,0);//Furniture entities renderc(32,24,64,128,96,16,0);//Furniture entities
renderFurniture(ITEM_WORKBENCH, 50,60); renderFurniture(ITEM_WORKBENCH, 50,60);
render16(50,46,0,112,0);//Player-down render16(50,46,0,112,0);//Player-down
renderc(50,58,16,160,16,8,2);//Slash renderc(50,58,16,160,16,8,2);//Slash
render(54,58,56,152,0);//Power glove render(54,58,56,152,0);//Power glove
render16(92,56,0,128,0);//Player(Carrying) render16(92,56,0,128,0);//Player(Carrying)
render16(92,44,128,128,0);//Workbench render16(92,44,128,128,0);//Workbench
break; break;
case 4: // Crafting case 4: // Crafting
renderFrame(11,3,19,6,0xFFFF1010); renderFrame(11,3,19,6,0xFFFF1010);
renderFrame(11,7,19,12,0xFFFF1010); renderFrame(11,7,19,12,0xFFFF1010);
renderFrame(1,3,10,12,0xFFFF1010); renderFrame(1,3,10,12,0xFFFF1010);
renderItemStuffWithText(TOOL_AXE,0,true,28,64); renderItemStuffWithText(TOOL_AXE,0,true,28,64);
renderItemIcon(TOOL_AXE, 0, 94, 32); renderItemIcon(TOOL_AXE, 0, 94, 32);
drawText("0", 206, 66); drawText("0", 206, 66);
renderItemIcon(ITEM_WOOD, 0, 94, 64); renderItemIcon(ITEM_WOOD, 0, 94, 64);
drawText("16/5", 206, 130); drawText("16/5", 206, 130);
break; break;
case 5: // Farming (Bottom screen) case 5: // Farming (Bottom screen)
renderc(24,16,352,48,112,16,0); // Wheat Stages renderc(24,16,352,48,112,16,0); // Wheat Stages
render16(20,40,352,48,0); // Farm Tile render16(20,40,352,48,0); // Farm Tile
render16(36,40,448,48,0); // Wheat Tile render16(36,40,448,48,0); // Wheat Tile
render16(52,40,448,48,0); // Wheat Tile render16(52,40,448,48,0); // Wheat Tile
render16(20,54,16,112,0); // Player (Up) render16(20,54,16,112,0); // Player (Up)
renderc(20,50,16,160,16,8,0); // Slash (Up) renderc(20,50,16,160,16,8,0); // Slash (Up)
render(19,45,40,152,0); // Seeds render(19,45,40,152,0); // Seeds
render(26,39,48,152,0); // Wheat1 render(26,39,48,152,0); // Wheat1
render(29,44,48,152,0); // Wheat2 render(29,44,48,152,0); // Wheat2
renderc(72,40,352,48,32,16,0); // Farm Tile + Seeded Wheat Tile renderc(72,40,352,48,32,16,0); // Farm Tile + Seeded Wheat Tile
render16(72,54,16,112,0); // Player (Up) render16(72,54,16,112,0); // Player (Up)
renderc(72,50,16,160,16,8,0); // Slash (Up) renderc(72,50,16,160,16,8,0); // Slash (Up)
render(76,48,40,152,0); // Seeds render(76,48,40,152,0); // Seeds
sf2d_draw_rectangle(216, 80, 32, 32, dirtColor[1]); // Dirt color for grass sf2d_draw_rectangle(216, 80, 32, 32, dirtColor[1]); // Dirt color for grass
render16(108, 40, 256, 0, 0); // Grass render16(108, 40, 256, 0, 0); // Grass
render16(124, 40,352,48,0); // Farm Tile render16(124, 40,352,48,0); // Farm Tile
render16(108,54,16,112,0); // Player (Up) render16(108,54,16,112,0); // Player (Up)
renderc(108,50,16,160,16,8,0); // Slash (Up) renderc(108,50,16,160,16,8,0); // Slash (Up)
render(112,48,72,144,0); // Gem Hoe render(112,48,72,144,0); // Gem Hoe
sf2d_draw_rectangle(112, 156, 32, 32, dirtColor[1]); // Dirt color for grass sf2d_draw_rectangle(112, 156, 32, 32, dirtColor[1]); // Dirt color for grass
render16(56, 78, 256, 0, 0); // Grass render16(56, 78, 256, 0, 0); // Grass
sf2d_draw_rectangle(80, 156, 32, 32, dirtColor[1]); // Dirt color sf2d_draw_rectangle(80, 156, 32, 32, dirtColor[1]); // Dirt color
render16(40, 78, 336, 80, 0); // Dirt Dots render16(40, 78, 336, 80, 0); // Dirt Dots
render(44, 82, 40,152,0); // Seeds render(44, 82, 40,152,0); // Seeds
render16(24,78,48,112,0); // Player (Right) render16(24,78,48,112,0); // Player (Right)
renderc(36,78,40,160,8,16,0); // Slash (Right) renderc(36,78,40,160,8,16,0); // Slash (Right)
render(38,82,32,144,0); // Gem Shovel render(38,82,32,144,0); // Gem Shovel
render(82,78,48,152,0); // Wheat render(82,78,48,152,0); // Wheat
render(90,78,48,152,0); // Wheat render(90,78,48,152,0); // Wheat
render(82,86,48,152,0); // Wheat render(82,86,48,152,0); // Wheat
render(90,86,48,152,0); // Wheat render(90,86,48,152,0); // Wheat
drawText(">",203,164); drawText(">",203,164);
render16(108,76,96,128,0); // Oven render16(108,76,96,128,0); // Oven
drawText(">",246,164); drawText(">",246,164);
render(132,82,72,152,0); // Bread render(132,82,72,152,0); // Bread
break; break;
case 6: //Mining case 6: //Mining
render16(23,32,464,48,0); // iron ore render16(23,32,464,48,0); // iron ore
render16(23,52,480,48,0); // gold ore render16(23,52,480,48,0); // gold ore
render16(23,72,496,48,0); // gem ore render16(23,72,496,48,0); // gem ore
renderb(41,38,88,152,0,ironColor); // Iron ore item renderb(41,38,88,152,0,ironColor); // Iron ore item
renderb(41,58,88,152,0,goldColor); // Gold ore item renderb(41,58,88,152,0,goldColor); // Gold ore item
render(41,78,112,152,0); // Gem item render(41,78,112,152,0); // Gem item
drawText(">",104,74); drawText(">",104,74);
drawText(">",104,114); drawText(">",104,114);
drawText(">",104,154); drawText(">",104,154);
render16(60,32,112,128,0); // Furnace render16(60,32,112,128,0); // Furnace
render16(60,52,112,128,0); // Furnace render16(60,52,112,128,0); // Furnace
render16(60,72,240,128,0); // Enchanter render16(60,72,240,128,0); // Enchanter
drawText(">",160,74); drawText(">",160,74);
drawText(">",160,114); drawText(">",160,114);
drawText(">",160,154); drawText(">",160,154);
renderb(88,36,96,152,0,ironColor); // Iron ingot item renderb(88,36,96,152,0,ironColor); // Iron ingot item
renderb(88,56,96,152,0,goldColor); // Gold ingot item renderb(88,56,96,152,0,goldColor); // Gold ingot item
renderb(88,76,152,144,0,goldColor); // Gem Pickaxe renderb(88,76,152,144,0,goldColor); // Gem Pickaxe
drawText(">",200,74); drawText(">",200,74);
drawText(">",200,114); drawText(">",200,114);
render16(106,32,64,128,0); // Anvil render16(106,32,64,128,0); // Anvil
render16(106,52,64,128,0); // Anvil render16(106,52,64,128,0); // Anvil
drawText(">",244,74); drawText(">",244,74);
drawText(">",244,114); drawText(">",244,114);
render(130,36,136,144,0); // Iron Pickaxe render(130,36,136,144,0); // Iron Pickaxe
render(130,56,144,144,0); // Gold Pickaxe render(130,56,144,144,0); // Gold Pickaxe
break; break;
} case 7: // Brewing
render16(65, 56, 240, 96, 0);
drawText(pageText,(320-(strlen(pageText))*12)/2,12); break;
if(pageNum > 0){ }
drawText("<",2,16);
renderButtonIcon(k_menuPrev.input & -k_menuPrev.input, 8, 2, 2); drawText(pageText,(320-(strlen(pageText))*12)/2,12);
} if(pageNum > 0){
if(pageNum < maxPageNum){ drawText("<",2,16);
drawText(">",306,16); renderButtonIcon(localInputs.k_menuPrev.input & -localInputs.k_menuPrev.input, 8, 2, 2);
renderButtonIcon(k_menuNext.input & -k_menuNext.input, 136, 2, 2); }
} if(pageNum < maxPageNum){
drawText("Press to exit",(320-(15*12))/2,218); drawText(">",306,16);
renderButtonIcon(k_decline.input & -k_decline.input, 140, 216, 1); 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);
}
} }

2
source/MenuTutorial.h Normal file → Executable file
View file

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <3ds.h> #include <3ds.h>
#include <string.h> #include <string.h>
#include <sf2d.h> #include <sf2d.h>
#include <dirent.h> #include <dirent.h>
#include <ctype.h> #include <ctype.h>

522
source/Network.c Normal file → Executable file
View file

@ -14,38 +14,332 @@ bool isConnected;
bool isServer; bool isServer;
size_t networkBufferSize; size_t networkBufferSize;
u32 *networkBuffer; void *networkBuffer;
udsNetworkStruct networkStruct; udsNetworkStruct networkStruct;
udsBindContext networkBindCtx; 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+currentSize<networkSendBufferWrapPos && networkSendBufferStartPos+currentSize!=networkSendBufferEndPos) {
//size of "header info" (seqid,size)
size_t extraSize = sizeof(u16) + sizeof(u16);
//data size
extraSize += *((u16*) ((networkSendBuffer+networkSendBufferStartPos+currentSize) + sizeof(u16)));
//if next packet can fit in frame include it
if(currentSize+extraSize < UDS_DATAFRAME_MAXSIZE) {
currentSize += extraSize;
} else {
break;
}
}
//send frame
if(currentSize>0) {
//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() { void networkInit() {
Result ret = udsInit(0x3000, NULL); Result ret = udsInit(0x3000, NULL);
if(R_FAILED(ret)) { if(R_FAILED(ret)) {
udsRunning = false; udsRunning = false;
} else { } else {
udsRunning = true; udsRunning = true;
scannedNetworksCount = 0; scannedNetworksCount = 0;
scannedNetworks = NULL; scannedNetworks = NULL;
isConnected = false; isConnected = false;
isServer = false; isServer = false;
networkConnectedMask = 0;
networkBufferSize = 0x4000; networkWriteBuffer = malloc(NETWORK_MAXDATASIZE);
if(networkWriteBuffer==NULL) {
networkExit();
return;
}
networkBufferSize = 0x4000;
networkBuffer = malloc(networkBufferSize); 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<UDS_MAXNODES+1; i++) {
networkSeqSendConf[i] = 0;
networkSeqRecvLast[i] = 0;
}
networkAckBuffer = malloc(sizeof(u16)+sizeof(u16)+sizeof(u16));
if(networkAckBuffer==NULL) {
networkExit();
return;
}
LightLock_Init(&sendBufferLock);
s32 prio = 0;
svcGetThreadPriority(&prio, CUR_THREAD_HANDLE);
//NOTE: It is important the networkThread is prioritized over the main thread (so substract 1) or else nothing will work
networkRunThread = true;
networkThread = threadCreate(networkThreadMain, NULL, NETWORK_STACKSIZE, prio-1, -2, false);
} }
} }
void networkExit() { void networkExit() {
//TODO: Additionally to shutting down the service, clear any left over memory! //Additionally to shutting down the service, clear any left over memory!
if(udsRunning) { if(udsRunning) {
udsRunning = false;
if(networkRunThread) {
networkRunThread = false;
threadJoin(networkThread, U64_MAX);
threadFree(networkThread);
}
//cleanup any dynamically reserved memory //cleanup any dynamically reserved memory
if(scannedNetworks!=NULL) free(scannedNetworks); if(scannedNetworks!=NULL) free(scannedNetworks);
scannedNetworks = NULL; scannedNetworks = NULL;
free(networkBuffer); if(networkWriteBuffer!=NULL) free(networkWriteBuffer);
networkWriteBuffer = NULL;
if(networkBuffer!=NULL) free(networkBuffer);
networkBuffer = NULL;
if(networkSendBuffer!=NULL) free(networkSendBuffer);
networkSendBuffer = NULL;
if(networkAckBuffer!=NULL) free(networkAckBuffer);
networkAckBuffer = NULL;
networkDisconnect(); networkDisconnect();
udsExit(); udsExit();
} }
} }
@ -61,26 +355,34 @@ bool networkAvailable() {
bool networkHost() { bool networkHost() {
if(udsRunning && !isConnected) { if(udsRunning && !isConnected) {
udsGenerateDefaultNetworkStruct(&networkStruct, NETWORK_WLANCOMMID, 0, NETWORK_MAXPLAYERS); udsGenerateDefaultNetworkStruct(&networkStruct, NETWORK_WLANCOMMID, 0, NETWORK_MAXPLAYERS);
Result ret = udsCreateNetwork(&networkStruct, NETWORK_PASSPHRASE, strlen(NETWORK_PASSPHRASE)+1, &networkBindCtx, NETWORK_CHANNEL, NETWORK_RECVBUFSIZE); Result ret = udsCreateNetwork(&networkStruct, NETWORK_PASSPHRASE, strlen(NETWORK_PASSPHRASE)+1, &networkBindCtx, NETWORK_CHANNEL, NETWORK_RECVBUFSIZE);
if(R_FAILED(ret)) { if(R_FAILED(ret)) {
return false; return false;
} else { } else {
if(udsWaitConnectionStatusEvent(false, false)) {}
udsGetConnectionStatus(&networkStatus);
udsSetNewConnectionsBlocked(false, true, false);
isConnected = true; isConnected = true;
isServer = true; isServer = true;
networkConnectedMask = 0;
return true; return true;
} }
} }
return false; return false;
} }
void networkHostStopConnections() {
udsSetNewConnectionsBlocked(true, true, false);
}
void networkScan() { void networkScan() {
if(udsRunning) { if(udsRunning) {
//reset values from last scan //reset values from last scan
if(scannedNetworks!=NULL) free(scannedNetworks); if(scannedNetworks!=NULL) free(scannedNetworks);
scannedNetworks = NULL; scannedNetworks = NULL;
scannedNetworksCount = 0; scannedNetworksCount = 0;
//scan //scan
memset(networkBuffer, 0, networkBufferSize); memset(networkBuffer, 0, networkBufferSize);
Result ret = udsScanBeacons(networkBuffer, networkBufferSize, &scannedNetworks, &scannedNetworksCount, NETWORK_WLANCOMMID, 0, NULL, isConnected); Result ret = udsScanBeacons(networkBuffer, networkBufferSize, &scannedNetworks, &scannedNetworksCount, NETWORK_WLANCOMMID, 0, NULL, isConnected);
@ -102,8 +404,8 @@ int networkGetScanCount() {
bool networkGetScanName(char *name, int pos) { bool networkGetScanName(char *name, int pos) {
if(udsRunning) { if(udsRunning) {
if(pos<0 || pos>=scannedNetworksCount) return false; if(pos<0 || pos>=scannedNetworksCount) return false;
Result ret = udsGetNodeInfoUsername(&scannedNetworks[pos].nodes[0], name); Result ret = udsGetNodeInfoUsername(&(scannedNetworks[pos].nodes[0]), name);
if(R_FAILED(ret)) { if(R_FAILED(ret)) {
//TODO: what do? //TODO: what do?
return false; return false;
@ -116,13 +418,16 @@ bool networkGetScanName(char *name, int pos) {
bool networkConnect(int pos) { bool networkConnect(int pos) {
if(udsRunning && !isConnected) { if(udsRunning && !isConnected) {
if(pos<0 || pos>=scannedNetworksCount) return false; 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); 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)) { if(R_FAILED(ret)) {
return false; return false;
} else { } else {
if(udsWaitConnectionStatusEvent(false, false)) {}
udsGetConnectionStatus(&networkStatus);
isConnected = true; isConnected = true;
isServer = false; isServer = false;
networkConnectedMask = 0;
return true; return true;
} }
} }
@ -130,73 +435,186 @@ bool networkConnect(int pos) {
} }
void networkDisconnect() { void networkDisconnect() {
//TODO: For clients this just means disconnect, for the server it means destroy the network //For clients this just means disconnect, for the server it means destroy the network
if(udsRunning && isConnected) { 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; i<UDS_MAXNODES+1; i++) {
networkSeqSendConf[i] = 0;
networkSeqRecvLast[i] = 0;
}
LightLock_Unlock(&sendBufferLock);
//With new changes citra now crashes HOST with "cannot be a router if we are not a host" when exiting game with more than 2 people
svcSleepThread(220000 * 1000); //HACK: prevent citra crash (>20*networkthreadsleep) (wait unti no more stuff gets send)
//TODO //TODO
if(isServer) { if(isServer) {
udsDisconnectNetwork();
} else {
//TODO: Clients need to cleanup too, how can I tell they got disconnected //TODO: Clients need to cleanup too, how can I tell they got disconnected
udsDestroyNetwork(); udsDestroyNetwork();
} else {
udsDisconnectNetwork();
} }
udsUnbind(&networkBindCtx); udsUnbind(&networkBindCtx);
isConnected = false; 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() { bool networkConnected() {
return isConnected; return isConnected;
}
bool networkIsServer() {
return isServer;
} }
int networkGetNodeCount() {
void networkSend(networkPacket *packet, size_t size) {
if(udsRunning && isConnected) { if(udsRunning && isConnected) {
//Server broadcasts to all clients but clients only send info to server return networkStatus.total_nodes;
if(isServer) { } else {
networkSendTo(packet, size, UDS_BROADCAST_NETWORKNODEID); return 0;
} else {
networkSendTo(packet, size, UDS_HOST_NETWORKNODEID);
}
} }
} }
//TODO: I somehow need to implement a relieable protocol on top of uds u16 networkGetLocalNodeID() {
void networkSendTo(networkPacket *packet, size_t size, u16 reciever) {
if(udsRunning && isConnected) { if(udsRunning && isConnected) {
return networkStatus.cur_NetworkNodeID;
Result ret = udsSendTo(reciever, NETWORK_CHANNEL, UDS_SENDFLAG_Default, packet, size); } else {
if(UDS_CHECK_SENDTO_FATALERROR(ret)) { return 0;
//TODO: what do?
}
} }
} }
void networkRecieve() { bool networkIsNodeConnected(u16 id) {
//TODO: Should recieve actually handle all the packets or just return them?
if(udsRunning && isConnected) { if(udsRunning && isConnected) {
size_t actualSize = 0; return networkStatus.node_bitmask & (1 << (id-1));
u16 sourceNetworkNodeID; } else {
return false;
memset(networkBuffer, 0, networkBufferSize); }
}
Result ret = udsPullPacket(&networkBindCtx, networkBuffer, networkBufferSize, &actualSize, &sourceNetworkNodeID);
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)) { if(R_FAILED(ret)) {
//TODO: what do? //TODO: what do?
return false;
} }
return true;
//actualSize will be 0 if no packet is available }
if(actualSize) { return false;
networkPacket *packet = (networkPacket*) networkBuffer; }
processPacket(packet, packet->analyze.type, sourceNetworkNodeID); 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+size<NETWORK_SENDBUFFERSIZE) {
networkSendBufferEndPos += size;
networkSendBufferWrapPos = networkSendBufferEndPos;
return networkSendBufferEndPos-size;
//we need to wrap
} else {
if(size<networkSendBufferStartPos) {
networkSendBufferEndPos = size;
return 0;
}
}
//we have wrap currently
} else {
int available = networkSendBufferStartPos - networkSendBufferEndPos - 1;
if(available>size) {
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);
}
}

60
source/Network.h Normal file → Executable file
View file

@ -3,45 +3,19 @@
#include <3ds.h> #include <3ds.h>
#define NETWORK_WLANCOMMID 0x11441850 #define NETWORK_WLANCOMMID 0x11441850
#define NETWORK_PASSPHRASE "Minicraft3DS localplay passphrase" #define NETWORK_PASSPHRASE "minicraft3dsLP"
#define NETWORK_CHANNEL 1 #define NETWORK_CHANNEL 1
#define NETWORK_RECVBUFSIZE UDS_DEFAULT_RECVBUFSIZE #define NETWORK_RECVBUFSIZE UDS_DEFAULT_RECVBUFSIZE
#define NETWORK_MAXPLAYERS UDS_MAXNODES #define NETWORK_MAXDATASIZE 1024
#define NETWORK_SENDBUFFERSIZE ((NETWORK_MAXDATASIZE+256)*10)
//packet type ids #define NETWORK_STACKSIZE (8*1024)
#define PACKET_REQUEST_MAPDATA 1
#define PACKET_MAPDATA 2
//TODO: Every other packet struct should start with a u8 type to match this #define NETWORK_MAXPLAYERS 8
typedef struct {
u8 type;
} packetAnalyze;
typedef struct { void *networkWriteBuffer;
u8 type;
u8 level;
} packetRequestMapData;
typedef struct {
u8 type;
u8 level;
u8 offset; //-> data is at offset*128 to offset*128+127
u8 map[128];
u8 data[128];
} packetMapData;
typedef struct {
union {
packetAnalyze analyze;
packetRequestMapData requestMapData;
packetMapData mapData;
};
} networkPacket;
void networkInit(); void networkInit();
void networkExit(); void networkExit();
@ -49,15 +23,25 @@ void networkExit();
bool networkAvailable(); bool networkAvailable();
bool networkHost(); bool networkHost();
void networkHostStopConnections();
void networkScan(); void networkScan();
int networkGetScanCount(); int networkGetScanCount();
bool networkGetScanName(char *name, int pos); //TODO: Name should be long enough to handle all allowed names (char[256]) bool networkGetScanName(char *name, int pos);
bool networkConnect(int pos); bool networkConnect(int pos);
void networkDisconnect(); void networkDisconnect();
bool networkConnected(); void networkStart();
bool networkIsServer();
void networkSend(networkPacket *packet, size_t size); //TODO: Should this be a pointer? Calling function needs to cleanup itself void networkUpdateStatus();
void networkSendTo(networkPacket *packet, size_t size, u16 reciever); //TODO: Should this be a pointer? Calling function needs to cleanup itself bool networkConnected();
void networkRecieve(); //TODO: Should recieve actually handle all the packets or just return them?
int networkGetNodeCount();
u16 networkGetLocalNodeID();
bool networkIsNodeConnected(u16 id);
bool networkGetNodeName(u16 id, char *name);
u16 networkGetExpectedSeqFrom(u16 id);
bool networkSeqIsLowerThan(u16 firstID, u16 secondID);
void networkSend(void *packet, size_t size);
void networkSendWaitFlush();

387
source/PacketHandler.c Normal file → Executable file
View file

@ -1,48 +1,357 @@
#include "PacketHandler.h" #include "PacketHandler.h"
#include <stdlib.h>
#include "Synchronizer.h"
#include "Globals.h" FILE *recvFile;
size_t recvFileSize;
void processPacket(networkPacket *packet, u8 type, u16 sender) { void * writeBool(void *buffer, size_t *size, bool value) {
//TODO: Differenciate the packets and process them *((bool*) buffer) = value;
if(networkIsServer()) { *(size) += sizeof(bool);
if(type==PACKET_REQUEST_MAPDATA) { return buffer + sizeof(bool);
u8 level = packet->requestMapData.level; }
if(level>=0 && level<=5) { void * writeU8(void *buffer, size_t *size, u8 value) {
//send back tile data *((u8*) buffer) = value;
for(int y=0; y<128; y++) { *(size) += sizeof(u8);
networkPacket packet = { return buffer + sizeof(u8);
.mapData = { }
.type = PACKET_MAPDATA,
.level = level, void * writeU16(void *buffer, size_t *size, u16 value) {
.offset = y *((u16*) buffer) = value;
} *(size) += sizeof(u16);
}; return buffer + sizeof(u16);
for(int x=0; x<128; x++) { }
packet.mapData.map[x] = map[level][x+y*128];
packet.mapData.data[x] = data[level][x+y*128]; void * writeU32(void *buffer, size_t *size, u32 value) {
} *((u32*) buffer) = value;
networkSendTo(&packet, sizeof(packetMapData), sender); *(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; i<playerCount; i++) {
u16 nodeID;
buffer = readU16(buffer, &size, &nodeID);
if(nodeID==networkGetLocalNodeID()) {
playerIndex = i;
} }
} else {
//TODO: Unknown packet - how to handle?
} }
} else {
if(type==PACKET_MAPDATA) { //cleanup transfer tmp file
u8 level = packet->mapData.level; FILE *file = fopen("tmpTransfer.bin", "wb");
if(file!=NULL) {
if(level>=0 && level<=5) { fclose(file);
//recieve tile data remove("tmpTransfer.bin");
//TODO: This should really check whether the values are in valid range }
int y = packet->mapData.offset;
for(int x=0; x<128; x++) { //init synchronizer
map[level][x+y*128] = packet->mapData.map[x]; synchronizerInit(seed, playerCount, playerIndex);
data[level][x+y*128] = packet->mapData.data[x];
} break;
}
case PACKET_START_FILEHEADER: {
void *data = packetGetDataStart(packet);
u8 type;
u8 id;
size_t fsize;
data = readU8(data, &size, &type);
data = readU8(data, &size, &id);
data = readSizeT(data, &size, &fsize);
recvFile = fopen("tmpTransfer.bin", "wb");
recvFileSize = fsize;
break;
}
case PACKET_START_FILEDATA: {
void *data = packetGetDataStart(packet);
size_t dsize = packetGetDataSize(size);
size_t toread = dsize;
fwrite(data, 1, toread, recvFile);
recvFileSize -= toread;
if(recvFileSize<=0) {
fclose(recvFile);
recvFile = NULL;
}
break;
}
case PACKET_START_REQUEST_IDS: {
synchronizerSendUID();
break;
}
case PACKET_START_ID: {
u8 playerID = packetGetSender(packet);
u32 uid;
void *data = packetGetDataStart(packet);
size_t dsize = packetGetDataSize(size);
data = readU32(data, &dsize, &uid);
synchronizerSetPlayerUID(playerID, uid);
synchronizerSendIfReady();
break;
}
case PACKET_START_READY: {
synchronizerSetPlayerReady(packetGetSender(packet));
if(playerLocalID==0) {
if(synchronizerAllReady()) {
sendStartSyncPacket();
synchronizerStart(); //server needs to call this here, all others do when they recieve the packet
} }
} else {
//TODO: Unknown packet - how to handle?
} }
break;
}
case PACKET_TURN_START: {
synchronizerStart();
break;
}
case PACKET_TURN_INPUT: {
synchronizerOnInputPacket(packetGetSender(packet), packetGetTurn(packet), packetGetDataStart(packet), packetGetDataSize(size));
break;
}
} }
} }
u8 packetGetID(void *packet) {
return *((u8*) packet);
}
u8 packetGetSender(void *packet) {
return *((u8*) packet+sizeof(u8));
}
u32 packetGetTurn(void *packet) {
return *((u32*) (packet+sizeof(u8)+sizeof(u8)));
}
void * packetGetDataStart(void *packet) {
return packet+sizeof(u8)+sizeof(u8)+sizeof(u32);
}
size_t packetGetDataSize(size_t size) {
return size-sizeof(u8)-sizeof(u8)-sizeof(u32);
}
size_t writeStartPacket(void *buffer, u32 seed) {
size_t size = 0;
buffer = writeU8(buffer, &size, PACKET_START);
buffer = writeU8(buffer, &size, 0);
buffer = writeU32(buffer, &size, 0);
buffer = writeU32(buffer, &size, seed);
buffer = writeU32(buffer, &size, (u32)networkGetNodeCount());
for(int i=1; i<=UDS_MAXNODES; i++) {
if(networkIsNodeConnected(i)) {
buffer = writeU16(buffer, &size, (u16)i);
}
}
return size;
}
size_t writeStartRequestPacket(void *buffer) {
size_t size = 0;
buffer = writeU8(buffer, &size, PACKET_START_REQUEST_IDS);
buffer = writeU8(buffer, &size, 0);
buffer = writeU32(buffer, &size, 0);
return size;
}
size_t writeInputPacket(void *buffer, Inputs *inputs, u8 playerID, u32 turnNumber) {
size_t size = 0;
buffer = writeU8(buffer, &size, PACKET_TURN_INPUT);
buffer = writeU8(buffer, &size, playerID);
buffer = writeU32(buffer, &size, turnNumber);
buffer = writeU16(buffer, &size, inputs->k_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);
}

31
source/PacketHandler.h Normal file → Executable file
View file

@ -1,5 +1,34 @@
#pragma once #pragma once
#include <stdlib.h>
#include "Network.h" #include "Network.h"
#include "Player.h"
void processPacket(networkPacket *packet, u8 type, u16 sender); #define PACKET_START 0
#define PACKET_START_FILEHEADER 1
#define PACKET_START_FILEDATA 2
#define PACKET_START_REQUEST_IDS 3
#define PACKET_START_ID 4
#define PACKET_START_READY 5
#define PACKET_TURN_START 10
#define PACKET_TURN_INPUT 11
void processPacket(void *packet, size_t size);
u8 packetGetID(void *packet);
u8 packetGetSender(void *packet);
u32 packetGetTurn(void *packet);
void * packetGetDataStart(void *packet);
size_t packetGetDataSize(size_t size);
size_t writeStartPacket(void *buffer, u32 seed);
size_t writeStartRequestPacket(void *buffer);
size_t writeInputPacket(void *buffer, Inputs *inputs, u8 playerID, u32 turnNumber);
bool readInputPacketData(void *buffer, size_t size, Inputs *inputs);
void sendFile(FILE *file, u8 fileType, u8 id);
void sendIDPacket(u8 playerID, u32 uid);
void sendStartReadyPacket(u8 playerID);
void sendStartSyncPacket();

657
source/Player.c Normal file
View file

@ -0,0 +1,657 @@
#include "Player.h"
#include <limits.h>
#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; i<MAX_PLAYERS; i++) {
initPlayer(players+i);
}
}
void freePlayers() {
for(int i=0; i<MAX_PLAYERS; i++) {
freePlayer(players+i);
}
}
void playerInitMiniMapData(u8 *minimapData) {
int i;
for(i = 0; i < 128 * 128; ++i) {
minimapData[i] = 0;
}
}
void playerInitEntity(PlayerData *pd) {
pd->entity.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; i<playerCount; i++) {
if(players[i].entity.level!=level) continue;
int xdif = players[i].entity.x - x;
int ydif = players[i].entity.y - y;
unsigned int dist = xdif*xdif + ydif*ydif;
if(dist<nearestDist) {
nearest = i;
nearestDist = dist;
}
}
if(nearest==-1) return NULL;
return players+nearest;
}
PlayerData* getLocalPlayer() {
return players+playerLocalID;
}
//player update functions
bool playerUseItem(PlayerData *pd) {
int aitemID = 0;
Item * aitem;
Item * item ;
switch(pd->activeItem->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; i<playerCount; i++) {
if(players[i].curChestEntity==ent) {
players[i].ingameMenu = MENU_NONE;
}
}
Item nItem = newItem(ent->entityFurniture.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;
}
}
}

96
source/Player.h Normal file
View file

@ -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);

821
source/Quests.c Normal file → Executable file
View file

@ -1,426 +1,437 @@
#include "Quests.h" #include "Quests.h"
u8 currentNPC; #include "Globals.h"
#include "Render.h"
int currentNPCMenu; void initTrades() {
int currentNPCVal; priestTrades.size = 5;
priestTrades.recipes = (Recipe*)malloc(sizeof(Recipe) * (priestTrades.size));
int currentTalkSel; priestTrades.recipes[0] = defineRecipe(ITEM_DUNGEON_KEY,1,1,ITEM_MAGIC_DUST,2);
bool currentTalkDone;
int currentTalkOptions;
char * currentTalkOption0;
char * currentTalkOption1;
char * currentTalkOption2;
char * currentTalk0;
char * currentTalk1;
char * currentTalk2;
char * currentTalk3;
char * currentTalk4;
char * currentTalk5;
void initQuests() {
questManager.size = 2;
questManager.questlines = (Questline*)malloc(sizeof(Questline) * (questManager.size));
priestTrades.size = 5;
priestTrades.recipes = (Recipe*)malloc(sizeof(Recipe) * (priestTrades.size));
priestTrades.recipes[0] = defineRecipe(ITEM_DUNGEON_KEY,1,1,ITEM_MAGIC_DUST,2);
priestTrades.recipes[1] = defineRecipe(ITEM_WIZARD_SUMMON,1,4,ITEM_CLOUD,100,ITEM_IRONINGOT,10,ITEM_BONE,10,ITEM_LEATHER,10); priestTrades.recipes[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[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[3] = defineRecipe(ITEM_COIN,1,1,ITEM_SLIME,5);
priestTrades.recipes[4] = defineRecipe(ITEM_COIN,1,1,ITEM_FLESH,5); priestTrades.recipes[4] = defineRecipe(ITEM_COIN,1,1,ITEM_FLESH,5);
farmerTrades.size = 7; farmerTrades.size = 7;
farmerTrades.recipes = (Recipe*)malloc(sizeof(Recipe) * (farmerTrades.size)); farmerTrades.recipes = (Recipe*)malloc(sizeof(Recipe) * (farmerTrades.size));
farmerTrades.recipes[0] = defineRecipe(ITEM_WHEAT,5,1,ITEM_COIN,3); farmerTrades.recipes[0] = defineRecipe(ITEM_WHEAT,5,1,ITEM_COIN,3);
farmerTrades.recipes[1] = defineRecipe(ITEM_BREAD,1,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[2] = defineRecipe(ITEM_APPLE,2,1,ITEM_COIN,4);
farmerTrades.recipes[3] = defineRecipe(ITEM_ACORN,3,1,ITEM_COIN,1); farmerTrades.recipes[3] = defineRecipe(ITEM_ACORN,3,1,ITEM_COIN,1);
farmerTrades.recipes[4] = defineRecipe(ITEM_SEEDS,4,1,ITEM_COIN,2); farmerTrades.recipes[4] = defineRecipe(ITEM_SEEDS,4,1,ITEM_COIN,2);
farmerTrades.recipes[5] = defineRecipe(ITEM_COIN,2,1,ITEM_SEEDS,5); farmerTrades.recipes[5] = defineRecipe(ITEM_COIN,2,1,ITEM_SEEDS,5);
farmerTrades.recipes[6] = defineRecipe(ITEM_COIN,1,1,ITEM_ACORN,5); farmerTrades.recipes[6] = defineRecipe(ITEM_COIN,1,1,ITEM_ACORN,5);
dwarfTrades.size = 2; dwarfTrades.size = 2;
dwarfTrades.recipes = (Recipe*)malloc(sizeof(Recipe) * (dwarfTrades.size)); dwarfTrades.recipes = (Recipe*)malloc(sizeof(Recipe) * (dwarfTrades.size));
dwarfTrades.recipes[0] = defineRecipe(ITEM_IRONINGOT,4,1,ITEM_GOLDINGOT,1); dwarfTrades.recipes[0] = defineRecipe(ITEM_IRONINGOT,4,1,ITEM_GOLDINGOT,1);
dwarfTrades.recipes[1] = defineRecipe(ITEM_GOLDINGOT,2,1,ITEM_GEM,1); dwarfTrades.recipes[1] = defineRecipe(ITEM_GOLDINGOT,2,1,ITEM_GEM,1);
//TODO: Trade Dragon Scales for something really nice //TODO: Trade Dragon Scales for something really nice
} }
void resetQuests() { void freeTrades() {
int i; free(priestTrades.recipes);
for(i=0; i<questManager.size; ++i) { free(farmerTrades.recipes);
questManager.questlines[i].currentQuest = 0; free(dwarfTrades.recipes);
questManager.questlines[i].currentQuestDone = false;
}
} }
void freeQuests() { void initQuests(QuestlineManager *questManager) {
free(questManager.questlines); if(questManager->questlines!=NULL) {
free(priestTrades.recipes); freeQuests(questManager);
free(farmerTrades.recipes); }
free(dwarfTrades.recipes);
questManager->size = 2;
questManager->questlines = (Questline*)malloc(sizeof(Questline) * (questManager->size));
} }
void openNPCMenu(int npc) { void resetQuests(QuestlineManager *questManager) {
currentNPC = npc; int i;
currentNPCVal = 0; for(i=0; i<questManager->size; ++i) {
currentMenu = MENU_NPC; questManager->questlines[i].currentQuest = 0;
currentNPCMenu = NPC_MENU_TALK; questManager->questlines[i].currentQuestDone = false;
}
currentTalkSel = 0;
currentTalkDone = false;
currentTalkOptions = 1;
currentTalkOption0 = "Bye";
currentTalkOption1 = "";
currentTalkOption2 = "";
currentTalk0 = "";
currentTalk1 = "";
currentTalk2 = "";
currentTalk3 = "";
currentTalk4 = "";
currentTalk5 = "";
//TODO: Handle upon currentNPC as well as the fitting quest progress
switch(currentNPC) {
case NPC_GIRL:
currentTalkOptions = 2;
currentTalkOption0 = "Trade";
currentTalkOption1 = "What do you do?";
currentTalk0 = "Hello!";
currentTalk1 = "It gets a bit lonely here.";
currentTalk2 = "I hope you stay...";
currentTalk3 = "But if you don't thats fine.";
currentTalk4 = "sigh";
currentTalk5 = "";
break;
case NPC_PRIEST:
currentTalkOptions = 3;
currentTalkOption1 = "Trade";
currentTalkOption2 = "Why are you so few?";
currentTalk0 = "Welcome to our small village";
currentTalk1 = "I am the leader of our group.";
currentTalk2 = "If you have anything usefull";
currentTalk3 = "for us I might be able to";
currentTalk4 = "provide something nice in";
currentTalk5 = "exchange.";
break;
case NPC_FARMER:
currentTalkOptions = 2;
currentTalkOption0 = "Maybe some other time";
currentTalkOption1 = "Trade";
currentTalk0 = "Hello friend!";
currentTalk1 = "Nice seeing somebody else";
currentTalk2 = "visit my little farm.";
currentTalk3 = "Interested in buying some";
currentTalk4 = "fresh farm goods?";
currentTalk5 = "";
break;
case NPC_LIBRARIAN:
currentTalkOptions = 2;
currentTalkOption0 = "Nothing";
currentTalkOption1 = "What are you doing here?";
if(questManager.questlines[1].currentQuest==1) {
currentTalkOptions = 3;
currentTalkOption2 = "Dwarvish language";
}
currentTalk0 = "Oh hello?";
currentTalk1 = "You must be quite brave";
currentTalk2 = "or stupid to be walking";
currentTalk3 = "around in this dungeon.";
currentTalk4 = "";
currentTalk5 = "How can I help you?";
break;
case NPC_DWARF:
if(questManager.questlines[1].currentQuest<=1) {
questManager.questlines[1].currentQuest = 1;
currentTalkOptions = 1;
currentTalkOption0 = "?";
currentTalk0 = "Dwo neal bet reck da lo";
currentTalk1 = "dhum don lir lugn at el";
currentTalk2 = "nur tor erno ur yo trad";
currentTalk3 = "thra so tir kho ukk tin";
currentTalk4 = "hel dro ic";
currentTalk5 = "";
//TODO: set to 2 once translation book has been bought from librarian(can only be done once it is 1, so the dwarf has been found once)
} else if(questManager.questlines[1].currentQuest==2) {
currentTalkOptions = 2;
currentTalkOption0 = "Not really";
currentTalkOption1 = "Trade";
currentTalk0 = "How are ya?";
currentTalk1 = "Pretty unusal meeting a";
currentTalk2 = "human down here.";
currentTalk3 = "";
currentTalk4 = "have something valuable";
currentTalk5 = "to trade?";
}
break;
}
} }
void tickTalkMenu() { void freeQuests(QuestlineManager *questManager) {
if (k_menu.clicked || k_decline.clicked) currentMenu = MENU_NONE; free(questManager->questlines);
questManager->questlines = NULL;
if (k_up.clicked){ ++currentTalkSel; if(currentTalkSel >= currentTalkOptions) currentTalkSel=0;}
if (k_down.clicked){ --currentTalkSel; if(currentTalkSel < 0) currentTalkSel=currentTalkOptions-1;}
if(k_accept.clicked){
currentTalkDone = true;
}
} }
void tickNPCMenu() { void resetNPCMenuData(NPC_MenuData *data) {
//TODO: Handle upon currentNPC as well as the fitting quest progress data->currentNPC = 0;
if(currentNPCMenu==NPC_MENU_TALK) tickTalkMenu(); data->currentNPCMenu = 0;
data->currentNPCVal = 0;
data->currentTalkSel = 0;
switch(currentNPC) { data->currentTalkDone = false;
case NPC_GIRL: data->currentTalkOptions = 0;
break; data->currentTalkOption0 = "";
case NPC_PRIEST: data->currentTalkOption1 = "";
if(currentNPCMenu==NPC_MENU_TALK && currentTalkDone) { data->currentTalkOption2 = "";
if(currentNPCVal==0) { data->currentTalk0 = "";
if(currentTalkSel==0) { data->currentTalk1 = "";
currentMenu = MENU_NONE; data->currentTalk2 = "";
} else if(currentTalkSel==1) { data->currentTalk3 = "";
currentRecipes = &priestTrades; data->currentTalk4 = "";
currentCraftTitle = "Trading"; data->currentTalk5 = "";
currentMenu = MENU_CRAFTING;
checkCanCraftRecipes(currentRecipes, player.p.inv);
sortRecipes(currentRecipes);
} else if(currentTalkSel==2) {
currentNPCVal = 1;
currentTalkSel = 0;
currentTalkDone = false;
currentTalkOptions = 1;
currentTalkOption0 = "...";
currentTalk0 = "For quite some time now this";
currentTalk1 = "village has been tyrannized";
currentTalk2 = "by a powerfull Air Wizard.";
currentTalk3 = "We are the only ones who";
currentTalk4 = "still have not given up";
currentTalk5 = "our old homes.";
}
} else if(currentNPCVal==1) {
if(currentTalkSel==0) {
currentNPCVal = 2;
currentTalkSel = 0;
currentTalkDone = false;
currentTalkOptions = 1;
currentTalkOption0 = "...";
currentTalk0 = "Most of the time the wizard";
currentTalk1 = "hides somewhere in the";
currentTalk2 = "cloudes. They can only be";
currentTalk3 = "reached by a stairwell";
currentTalk4 = "protected by an almost";
currentTalk5 = "undestroyable stone barrier.";
}
} else if(currentNPCVal==2) {
if(currentTalkSel==0) {
currentNPCVal = 3;
currentTalkSel = 0;
currentTalkDone = false;
currentTalkOptions = 1;
currentTalkOption0 = "...";
currentTalk0 = "I am guessing you would ";
currentTalk1 = "need tools atleast as";
currentTalk2 = "strong as diamonds to be";
currentTalk3 = "able to destroy it.";
currentTalk4 = "";
currentTalk5 = "";
}
} else if(currentNPCVal==3) {
if(currentTalkSel==0) {
currentNPCVal = 4;
currentTalkSel = 0;
currentTalkDone = false;
currentTalkOptions = 2;
currentTalkOption0 = "Let me do it!";
currentTalkOption1 = "I am not sure";
currentTalk0 = "I am willing to give an";
currentTalk1 = "ancient artifact passed";
currentTalk2 = "down over generations to";
currentTalk3 = "anybody who manages to";
currentTalk4 = "chase the wizard away and";
currentTalk5 = "come back with proof.";
}
} else if(currentNPCVal==4) {
currentMenu = MENU_NONE;
}
}
break;
case NPC_FARMER:
if(currentNPCMenu==NPC_MENU_TALK && currentTalkDone) {
if(currentNPCVal==0) {
if(currentTalkSel==0) {
currentMenu = MENU_NONE;
} else if(currentTalkSel==1) {
currentRecipes = &farmerTrades;
currentCraftTitle = "Trading";
currentMenu = MENU_CRAFTING;
checkCanCraftRecipes(currentRecipes, player.p.inv);
sortRecipes(currentRecipes);
}
}
}
break;
case NPC_LIBRARIAN:
if(currentNPCMenu==NPC_MENU_TALK && currentTalkDone) {
if(currentNPCVal==0) {
if(currentTalkSel==0) {
currentMenu = MENU_NONE;
} else if(currentTalkSel==1) {
currentNPCVal = 2;
currentTalkSel = 0;
currentTalkDone = false;
currentTalkOptions = 1;
currentTalkOption0 = "Ok";
currentTalk0 = "The books in this dungeon";
currentTalk1 = "house secrets that cannot be";
currentTalk2 = "found anywhere else in the";
currentTalk3 = "world. So I came to study";
currentTalk4 = "them. Most are written in";
currentTalk5 = "an ancient language.";
} else if(currentTalkSel==2) {
currentNPCVal = 1;
currentTalkSel = 0;
currentTalkDone = false;
currentTalkOptions = 2;
currentTalkOption0 = "I need to think about it";
currentTalkOption1 = "Here they are";
currentTalk0 = "So you have met a dwarf but";
currentTalk1 = "had a little communication";
currentTalk2 = "problem? I do have a dwarvish";
currentTalk3 = "translation book but I havent";
currentTalk4 = "read it yet. For 10 Gold bars";
currentTalk5 = "I will give it to you anyway.";
}
} else if(currentNPCVal==1) {
if(currentTalkSel==0) {
currentMenu = MENU_NONE;
} else if(currentTalkSel==1) {
currentNPCVal = 2;
currentTalkSel = 0;
currentTalkDone = false;
currentTalkOptions = 1;
currentTalkOption0 = "";
if(countItemInv(ITEM_GOLDINGOT,0,player.p.inv)>=10) {
//remove gold from player inventory
//TODO: Maybe I should make a generic substract items method sometime
Item* item = getItemFromInventory(ITEM_GOLDINGOT, player.p.inv);
item->countLevel -= 10;
if(item->countLevel < 1) removeItemFromInventory(item->slotNum, player.p.inv);
questManager.questlines[1].currentQuest = 2;
currentTalk0 = "Thank you these will be";
currentTalk1 = "really helpful.";
currentTalk2 = "Here take this book with";
currentTalk3 = "it you should be able to";
currentTalk4 = "easily understand anything";
currentTalk5 = "a dwarf can say.";
currentTalkOption0 = "Thanks";
} else {
currentTalk0 = "You do not seem to have";
currentTalk1 = "enough Gold Bars with you.";
currentTalk2 = "";
currentTalk3 = "Ask again when you have";
currentTalk4 = "collected the 10 Bars.";
currentTalk5 = "";
currentTalkOption0 = "Ok";
}
}
} else if(currentNPCVal==2) {
if(currentTalkSel==0) {
currentMenu = MENU_NONE;
}
}
}
break;
case NPC_DWARF:
if(questManager.questlines[1].currentQuest<=1) {
if(currentNPCMenu==NPC_MENU_TALK && currentTalkDone) {
if(currentNPCVal==0) currentMenu = MENU_NONE;
}
} else if(questManager.questlines[1].currentQuest==2) {
if(currentNPCMenu==NPC_MENU_TALK && currentTalkDone) {
if(currentTalkSel==0) {
currentMenu = MENU_NONE;
} else if(currentTalkSel==1) {
currentRecipes = &dwarfTrades;
currentCraftTitle = "Trading";
currentMenu = MENU_CRAFTING;
checkCanCraftRecipes(currentRecipes, player.p.inv);
sortRecipes(currentRecipes);
}
}
}
break;
}
} }
void renderTalkMenu(char * name) { void openNPCMenu(PlayerData *pd, int npc) {
renderFrame(1,1,24,14,0xFFFF1010); pd->ingameMenu = MENU_NPC;
drawTextColor(name,24+1,14+1,0xFF000000);
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); drawTextColor(name,24,14,0xFF6FE2E2);
drawText(currentTalk0, 32, 32); drawText(data->currentTalk0, 32, 32);
drawText(currentTalk1, 32, 48); drawText(data->currentTalk1, 32, 48);
drawText(currentTalk2, 32, 64); drawText(data->currentTalk2, 32, 64);
drawText(currentTalk3, 32, 80); drawText(data->currentTalk3, 32, 80);
drawText(currentTalk4, 32, 96); drawText(data->currentTalk4, 32, 96);
drawText(currentTalk5, 32, 112); drawText(data->currentTalk5, 32, 112);
if(currentTalkOptions>=3) drawText(currentTalkOption2, 64, 147); if(data->currentTalkOptions>=3) drawText(data->currentTalkOption2, 64, 147);
if(currentTalkOptions>=2) drawText(currentTalkOption1, 64, 171); if(data->currentTalkOptions>=2) drawText(data->currentTalkOption1, 64, 171);
if(currentTalkOptions>=1) drawText(currentTalkOption0, 64, 195); if(data->currentTalkOptions>=1) drawText(data->currentTalkOption0, 64, 195);
if(currentTalkOptions>=3 && currentTalkSel==2) drawText(">", 48, 147); if(data->currentTalkOptions>=3 && data->currentTalkSel==2) drawText(">", 48, 147);
if(currentTalkOptions>=2 && currentTalkSel==1) drawText(">", 48, 171); if(data->currentTalkOptions>=2 && data->currentTalkSel==1) drawText(">", 48, 171);
if(currentTalkOptions>=1 && currentTalkSel==0) drawText(">", 48, 195); if(data->currentTalkOptions>=1 && data->currentTalkSel==0) drawText(">", 48, 195);
} }
void renderNPCMenu(int xscr, int yscr) { void renderNPCMenu(NPC_MenuData *data) {
//TODO: Handle upon currentNPC as well as the fitting quest progress //TODO: Handle upon currentNPC as well as the fitting quest progress
switch(currentNPC) { switch(data->currentNPC) {
case NPC_GIRL: case NPC_GIRL:
if(currentNPCMenu==NPC_MENU_TALK) renderTalkMenu("Girl Jill"); if(data->currentNPCMenu==NPC_MENU_TALK) renderTalkMenu(data, "Maria");
break; break;
case NPC_PRIEST: case NPC_PRIEST:
if(currentNPCMenu==NPC_MENU_TALK) renderTalkMenu("Priest Brom"); if(data->currentNPCMenu==NPC_MENU_TALK) renderTalkMenu(data, "Priest Brom");
break; break;
case NPC_FARMER: case NPC_FARMER:
if(currentNPCMenu==NPC_MENU_TALK) renderTalkMenu("Farmer Garrow"); if(data->currentNPCMenu==NPC_MENU_TALK) renderTalkMenu(data, "Farmer Garrow");
break; break;
case NPC_LIBRARIAN: case NPC_LIBRARIAN:
if(currentNPCMenu==NPC_MENU_TALK) renderTalkMenu("Librarian Ajihad"); if(data->currentNPCMenu==NPC_MENU_TALK) renderTalkMenu(data, "Librarian Ajihad");
break; break;
case NPC_DWARF: case NPC_DWARF:
if(currentNPCMenu==NPC_MENU_TALK) renderTalkMenu("Dwarf Orik"); if(data->currentNPCMenu==NPC_MENU_TALK) renderTalkMenu(data, "Dwarf Orik");
break; break;
} }
} }

31
source/Quests.h Normal file → Executable file
View file

@ -1,32 +1,25 @@
#pragma once #pragma once
#include <stdlib.h> #include <stdlib.h>
#include <stdbool.h> #include <stdbool.h>
#include "render.h" #include "QuestsData.h"
#include "Player.h"
#include "Crafting.h" #include "Crafting.h"
#define NPC_MENU_TALK 0 #define NPC_MENU_TALK 0
typedef struct {
int currentQuest;
bool currentQuestDone;
} Questline;
typedef struct {
int size;
Questline * questlines;
} QuestlineManager;
QuestlineManager questManager;
RecipeManager priestTrades; RecipeManager priestTrades;
RecipeManager farmerTrades; RecipeManager farmerTrades;
RecipeManager dwarfTrades; RecipeManager dwarfTrades;
void initQuests(); void initTrades();
void resetQuests(); void freeTrades();
void freeQuests();
void openNPCMenu(int npc); void initQuests(QuestlineManager *questManager);
void resetQuests(QuestlineManager *questManager);
void freeQuests(QuestlineManager *questManager);
void renderNPCMenu(int xscr, int yscr); void resetNPCMenuData(NPC_MenuData *data);
void tickNPCMenu(); void openNPCMenu(PlayerData *pd, int npc);
void tickNPCMenu(PlayerData *pd);
void renderNPCMenu(NPC_MenuData *data);

35
source/QuestsData.h Normal file
View file

@ -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

820
source/Render.c Normal file → Executable file

File diff suppressed because it is too large Load diff

23
source/Render.h Normal file → Executable file
View file

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <3ds.h> #include <3ds.h>
#include <sf2d.h> #include <sf2d.h>
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include <stdlib.h> #include <stdlib.h>
#include "Globals.h" #include "Globals.h"
@ -12,6 +12,7 @@ sf2d_texture *lanternLightBake;
sf2d_texture *glowwormLightBake; sf2d_texture *glowwormLightBake;
sf2d_texture *glowwormBigLightBake; sf2d_texture *glowwormBigLightBake;
int offsetX, offsetY; int offsetX, offsetY;
int playerScale;
void render(s32 xp, s32 yp, u32 xTile, u32 yTile, u8 bits); 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 renderb(s32 xp, s32 yp, u32 xTile, u32 yTile, u8 bits, u32 color);
@ -26,25 +27,24 @@ void render32(s32 xp, s32 yp, u32 xTile, u32 yTile, u8 bits);
void renderTitle(int x, int y); void renderTitle(int x, int y);
void renderFrame(int x1, int y1, int x2, int y2, u32 bgColor); void renderFrame(int x1, int y1, int x2, int y2, u32 bgColor);
void renderTile(int i, int d, int x, int y); void renderTile(int i, int d, u8 level, int x, int y);
void renderConnectedTile4(int x, int y, u32 xTile, u32 yTile); void renderConnectedTile4(int x, int y, u32 xTile, u32 yTile);
void renderConnectedTile8(int x, int y, u32 xTile, u32 yTile); void renderConnectedTile8(int x, int y, u32 xTile, u32 yTile);
void renderBackground(int xScroll, int yScroll); void renderBackground(s8 level, int xScroll, int yScroll);
void renderMenuBackground(int xScroll, int yScroll); //Renders the darkness void renderWeather(u8 level, int xScroll, int yScroll);
void renderWeather(int xScroll, int yScroll); void renderDayNight(PlayerData *pd);
void renderDayNight();
void renderButtonIcon(u32 icon, int x, int y, float scale); void renderButtonIcon(u32 icon, int x, int y, float scale);
void bakeLights(); void bakeLights();
void freeLightBakes(); void freeLightBakes();
void renderLightsToStencil(bool force, bool invert, bool rplayer); void renderLightsToStencil(PlayerData *pd, bool force, bool invert, bool rplayer);
void resetStencilStuff(); void resetStencilStuff();
void bakeLight(sf2d_texture* texture, int x, int y, int r); void bakeLight(sf2d_texture* texture, int x, int y, int r);
void renderLight(int x, int y, sf2d_texture* texture); void renderLight(int x, int y, sf2d_texture* texture);
void renderGui(); void renderGui(PlayerData *pd);
void renderZoomedMap(); void renderZoomedMap(PlayerData *pd);
void renderPlayer(); void renderPlayer(PlayerData *pd);
void drawText(char * msg, u32 x, u32 y); void drawText(char * msg, u32 x, u32 y);
void drawSizedText(char * msg, u32 x, u32 y, float size); void drawSizedText(char * msg, u32 x, u32 y, float size);
@ -54,10 +54,11 @@ void drawSizedTextColor(char * msg, int x, int y, float size, u32 color);
void renderFurniture(int itemID, int x, int y); void renderFurniture(int itemID, int x, int y);
void renderEntity(Entity* e, 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 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 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 renderItemWithText(Item* item, int x, int y);
void renderItemStuffWithText(int itemID, int itemCL, bool onlyOne, int x, int y); void renderItemStuffWithText(int itemID, int itemCL, bool onlyOne, int x, int y);
void renderItemWithTextCentered(Item* item, int width, int y); void renderItemWithTextCentered(Item* item, int width, int y);

1028
source/SaveLoad.c Normal file → Executable file

File diff suppressed because it is too large Load diff

13
source/SaveLoad.h Normal file → Executable file
View file

@ -2,7 +2,16 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "Entity.h" #include "Entity.h"
#include "Player.h"
#include "Globals.h" #include "Globals.h"
void saveCurrentWorld(char * filename, EntityManager * eManager, Entity * player, u8 * map, u8 * mapData); #define SAVE_VERSION 1
int loadWorld(char * filename, EntityManager * eManager, Entity * player, u8 * map, u8 * mapData);
#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);

103
source/Sound.c Normal file → Executable file
View file

@ -1,77 +1,100 @@
#include "Sound.h" #include "Sound.h"
u8 soundListenerLevel;
int soundListenerX;
int soundListenerY;
void loadSound(Sound * snd, char * filename){ void loadSound(Sound * snd, char * filename){
FILE *file = fopen(filename, "rb"); FILE *file = fopen(filename, "rb");
if(file != NULL){ if(file != NULL){
fseek(file, 0, SEEK_END); fseek(file, 0, SEEK_END);
snd->size = ftell(file); snd->size = ftell(file)/2;
fseek(file, 0, SEEK_SET); fseek(file, 0, SEEK_SET);
snd->buffer = linearAlloc(snd->size); snd->buffer = linearAlloc(snd->size*sizeof(u16));
fread(snd->buffer, 1, snd->size, file); fread(snd->buffer, 1, snd->size, file);
} }
fclose(file); fclose(file);
} }
void playSound(Sound snd){ 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){ void playSoundPositioned(Sound snd, s8 level, int x, int y) {
csndPlaySound(10, SOUND_FORMAT_16BIT | SOUND_REPEAT, 44100, 1, 0, snd.buffer, snd.buffer, snd.size); if(level != soundListenerLevel) return;
int xd = soundListenerX - x;
int yd = soundListenerY - y;
if (xd * xd + yd * yd > 80 * 80) return;
csndPlaySound(8, SOUND_FORMAT_16BIT | SOUND_ONE_SHOT, 44100, 1, 0, snd.buffer, snd.buffer, snd.size);
}
void setListenerPosition(s8 level, int x, int y) {
soundListenerLevel = level;
soundListenerX = x;
soundListenerY = y;
}
void playMusic(Sound *snd){
static Sound *lastSnd;
if(lastSnd==snd) return;
lastSnd = snd;
csndPlaySound(10, SOUND_FORMAT_16BIT | SOUND_REPEAT, 44100, 1, 0, snd->buffer, snd->buffer, snd->size);
} }
void stopMusic() { void stopMusic() {
CSND_SetPlayState(8, 0); CSND_SetPlayState(8, 0);
CSND_SetPlayState(10, 0); CSND_SetPlayState(10, 0);
csndExecCmds(true); csndExecCmds(true);
} }
void updateMusic(int lvl, int time) { void updateMusic(int lvl, int time) {
switch(lvl) { switch(lvl) {
case 0: case 0:
playMusic(music_floor0); playMusic(&music_floor0);
break; break;
case 1: case 1:
if(time>6000 && time<19000) playMusic(music_floor1); if(time>6000 && time<19000) playMusic(&music_floor1);
else playMusic(music_floor1_night); else playMusic(&music_floor1_night);
break; break;
case 2: case 2:
case 3: case 3:
playMusic(music_floor23); playMusic(&music_floor23);
break; break;
case 4: case 4:
case 5: //TODO - dungeon needs own music case 5: //TODO - dungeon needs own music
playMusic(music_floor4); playMusic(&music_floor4);
break; break;
} }
} }
void loadSounds() { void loadSounds() {
loadSound(&snd_playerHurt, "resources/playerhurt.raw"); loadSound(&snd_playerHurt, "romfs:/resources/playerhurt.raw");
loadSound(&snd_playerDeath, "resources/playerdeath.raw"); loadSound(&snd_playerDeath, "romfs:/resources/death.raw");
loadSound(&snd_monsterHurt, "resources/monsterhurt.raw"); loadSound(&snd_monsterHurt, "romfs:/resources/monsterhurt.raw");
loadSound(&snd_test, "resources/test.raw"); loadSound(&snd_test, "romfs:/resources/test.raw");
loadSound(&snd_pickup, "resources/pickup.raw"); loadSound(&snd_pickup, "romfs:/resources/pickup.raw");
loadSound(&snd_bossdeath, "resources/bossdeath.raw"); loadSound(&snd_bossdeath, "romfs:/resources/bossdeath.raw");
loadSound(&snd_craft, "resources/craft.raw"); loadSound(&snd_craft, "romfs:/resources/craft.raw");
loadSound(&music_menu, "resources/music/menu.raw"); loadSound(&music_menu, "romfs:/resources/music/menu.raw");
loadSound(&music_floor0, "resources/music/floor0.raw"); loadSound(&music_floor0, "romfs:/resources/music/floor0.raw");
loadSound(&music_floor1, "resources/music/floor1.raw"); loadSound(&music_floor1, "romfs:/resources/music/floor1.raw");
loadSound(&music_floor1_night, "resources/music/floor1_night.raw"); loadSound(&music_floor1_night, "romfs:/resources/music/floor1_night.raw");
loadSound(&music_floor23, "resources/music/floor2_3.raw"); loadSound(&music_floor23, "romfs:/resources/music/floor2_3.raw");
loadSound(&music_floor4, "resources/music/floor4.raw"); loadSound(&music_floor4, "romfs:/resources/music/floor4.raw");
} }
void freeSounds(){ void freeSounds(){
linearFree(snd_playerHurt.buffer); linearFree(snd_playerHurt.buffer);
linearFree(snd_playerDeath.buffer); linearFree(snd_playerDeath.buffer);
linearFree(snd_monsterHurt.buffer); linearFree(snd_monsterHurt.buffer);
linearFree(snd_test.buffer); linearFree(snd_test.buffer);
linearFree(snd_pickup.buffer); linearFree(snd_pickup.buffer);
linearFree(snd_bossdeath.buffer); linearFree(snd_bossdeath.buffer);
linearFree(snd_craft.buffer); linearFree(snd_craft.buffer);
linearFree(music_menu.buffer); linearFree(music_menu.buffer);
linearFree(music_floor0.buffer); linearFree(music_floor0.buffer);
linearFree(music_floor1.buffer); linearFree(music_floor1.buffer);

11
source/Sound.h Normal file → Executable file
View file

@ -6,14 +6,17 @@
typedef struct { typedef struct {
u32 size; u32 size;
u8 * buffer; u8 * buffer;
} Sound; } Sound;
void loadSound(Sound * snd, char * filename); void loadSound(Sound * snd, char * filename);
void playSound(Sound snd); void playSound(Sound snd);
void playMusic(Sound snd); void playSoundPositioned(Sound snd, s8 level, int x, int y);
void setListenerPosition(s8 level, int x, int y);
void playMusic(Sound *snd);
void updateMusic(int lvl, int time); void updateMusic(int lvl, int time);
void stopMusic(); void stopMusic();
@ -33,4 +36,4 @@ Sound music_floor0;
Sound music_floor1; Sound music_floor1;
Sound music_floor1_night; Sound music_floor1_night;
Sound music_floor23; Sound music_floor23;
Sound music_floor4; Sound music_floor4;

250
source/Synchronizer.c Normal file
View file

@ -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<playerCount; i++) {
resetKeys(&(players[i].inputs));
for(int j=0; j<MAX_INPUT_BUFFER; j++) {
resetKeys(&(players[i].nextInputs[j]));
}
players[i].nextTurnReady[0] = true;
players[i].idSet = false;
players[i].ready = false;
}
players[playerLocalID].id = localUID;
players[playerLocalID].idSet = true;
players[playerLocalID].ready = true;
//switch menu
currentMenu = MENU_LOADING;
synchronizerRunning = false;
}
void synchronizerSendUID() {
sendIDPacket(playerLocalID, localUID);
}
void synchronizerSetPlayerUID(int playerID, u32 uid) {
players[playerID].id = uid;
players[playerID].idSet = true;
}
void synchronizerSendIfReady() {
//we are ready when we recieved all uids
for(int i=0; i<playerCount; i++) {
if(!players[i].idSet) {
return;
}
}
//ready -> send to server
sendStartReadyPacket(playerLocalID);
}
void synchronizerSetPlayerReady(int playerID) {
players[playerID].ready = true;
}
bool synchronizerAllReady() {
for(int i=0; i<playerCount; i++) {
if(!players[i].ready) {
return false;
}
}
return true;
}
void synchronizerStart() {
//check if save file is present
bool doLoad = false;
char *loadName = NULL;
//host and single player need to load the actual file
if(playerLocalID==0) {
FILE *file = fopen(currentFileName, "rb");
if(file!=NULL) {
fclose(file);
doLoad = true;
loadName = currentFileName;
}
//all others the transfered one
} else {
FILE *file = fopen("tmpTransfer.bin", "rb");
if(file!=NULL) {
fclose(file);
doLoad = true;
loadName = "tmpTransfer.bin";
}
}
// reset random generators
srand(synchronizerNextSeed);
gaussrand(true);
//start the game
startGame(doLoad, loadName);
//remove transfered save file from clients
if(playerLocalID!=0) {
FILE *file = fopen("tmpTransfer.bin", "rb");
if(file!=NULL) {
fclose(file);
remove("tmpTransfer.bin");
}
}
//clear menu
currentMenu = MENU_NONE;
synchronizerRunning = true;
synchronizerCurrentTicks = SYNCHRONIZER_TICKS_PER_TURN;
}
void synchronizerTick(void (*gtick)(void)) {
if(synchronizerRunning && synchronizerTurnReady()) {
synchronizerNextTurn();
// reset random generators
srand(synchronizerNextSeed);
gaussrand(true);
syncTickCount++;
//call game tick
(*gtick)();
synchronizerNextSeed = rand();
}
}
//Test if all players (including single player) input is recieved
bool synchronizerTurnReady() {
if(synchronizerCurrentTicks<SYNCHRONIZER_TICKS_PER_TURN) return true;
for(int i=0; i<playerCount; i++) {
if(!players[i].nextTurnReady[synchronizerGetTurnIndex(synchronizerLocalTurn)]) {
return false;
}
}
return true;
}
void synchronizerNextTurn() {
if(synchronizerCurrentTicks<SYNCHRONIZER_TICKS_PER_TURN) {
synchronizerCurrentTicks++;
//clicked events are only fired for the first tick per turn
for(int i=0; i<playerCount; i++) {
resetClicked(&(players[i].inputs));
}
} else {
//move nextInput of every player to currentInput
for(int i=0; i<playerCount; i++) {
players[i].inputs = players[i].nextInputs[synchronizerGetTurnIndex(synchronizerLocalTurn)];
players[i].nextTurnReady[synchronizerGetTurnIndex(synchronizerLocalTurn)] = false;
}
//Increase turn number
synchronizerLocalTurn++;
synchronizerCurrentTicks = 1;
//send local input
synchronizerSendLocalInputs();
}
}
void synchronizerSendLocalInputs() {
//scan local inputs
hidScanInput();
tickKeys(&localInputs, hidKeysHeld(), hidKeysDown());
//store local input in nextInput
players[playerLocalID].nextInputs[synchronizerGetTurnIndex(synchronizerLocalTurn)] = localInputs;
players[playerLocalID].nextTurnReady[synchronizerGetTurnIndex(synchronizerLocalTurn)] = true;
//send local input
if(playerCount>1) {
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<synchronizerLocalTurn+MAX_INPUT_BUFFER && playerID<playerCount) {
if(readInputPacketData(data, dataSize, &(players[playerID].nextInputs[synchronizerGetTurnIndex(turnNumber)]))) {
players[playerID].nextTurnReady[synchronizerGetTurnIndex(turnNumber)] = true;
}
}
}
int synchronizerGetTurnIndex(u32 turn) {
return turn%MAX_INPUT_BUFFER;
}
void synchronizerReset() {
synchronizerRunning = false;
synchronizerCurrentTicks = 0;
}
bool synchronizerIsRunning() {
return synchronizerRunning;
}
// helpers for random numbers
#define PI 3.141592654
double gaussrand(bool reset)
{
static double U, V;
static int phase = 0;
double Z;
if(reset) {
U = 0;
V = 0;
phase = 0;
return 0;
}
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;
}

29
source/Synchronizer.h Normal file
View file

@ -0,0 +1,29 @@
#pragma once
#include <3ds.h>
//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);

213
source/ZipHelper.c Normal file
View file

@ -0,0 +1,213 @@
#include "ZipHelper.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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(size<READ_SIZE) {
if(!feof(in)) {
free(read_buffer);
zipCloseFileInZip(zipfile);
zipClose(zipfile, "");
fclose(in);
return 5;
}
}
if(size>0) {
//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;
}

10
source/ZipHelper.h Normal file
View file

@ -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);

428
source/main.c Normal file → Executable file
View file

@ -1,7 +1,7 @@
#include <3ds.h> #include <3ds.h>
#include <sf2d.h> #include <sf2d.h>
#include <sfil.h> #include <sfil.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <ctype.h> #include <ctype.h>
#include <stdlib.h> #include <stdlib.h>
@ -13,193 +13,91 @@
#include "Menu.h" #include "Menu.h"
#include "texturepack.h" #include "texturepack.h"
#include "Network.h" #include "Network.h"
#include "SaveLoad.h"
#include "Ingame.h"
#include "Player.h"
#include "Synchronizer.h"
#include "PacketHandler.h"
// TODO: Dungeon is way to difficult // TODO: Dungeon is way to difficult
// -> Skeleton arrows are slower, do a little less damage // -> Skeleton arrows are slower, do a little less damage
// -> Or instead of less damage, implement a simple armor system // -> Or instead of less damage, implement a simple armor system
//TODO: Multiplayer should use normal drawing code -> so remove this first test again //TODO: Something still causes desyncs very rarely
float tmxscr = 400;
float tmyscr = 400;
float tmenuxa = 0.25;
float tmenuya = 0.25;
static aptHookCookie cookie;
void initMiniMapData() { void setupGame() {
int i; synchronizerInit(rand(), 1, 0);
for(i = 0; i < 128 * 128; ++i) { synchronizerSetPlayerUID(0, localUID);
minimapData[i] = 0; synchronizerStart();
}
}
void initMiniMap(bool loadUpWorld) {
int i;
for (i = 0; i < 5; ++i) {
initMinimapLevel(i, loadUpWorld);
}
}
void initNewMap() {
newSeed();
createAndValidateSkyMap(128, 128, 0, map[0], data[0]);
createAndValidateTopMap(128, 128, 1, map[1], data[1]);
createAndValidateUndergroundMap(128, 128, 1, 2, map[2], data[2]);
createAndValidateUndergroundMap(128, 128, 2, 3, map[3], data[3]);
createAndValidateUndergroundMap(128, 128, 3, 4, map[4], data[4]);
}
void setupGame(bool loadUpWorld, bool remote) {
currentLevel = 1;
// Reset entity manager.
memset(&eManager, 0, sizeof(eManager));
sf2d_set_clear_color(0xFF6C6D82); //sf2d_set_clear_color(RGBA8(0x82, 0x6D, 0x6C, 0xFF));
initMiniMapData();
if(!remote) {
if (!loadUpWorld) {
initNewMap();
initPlayer();
resetQuests();
airWizardHealthDisplay = 2000;
int i;
for (i = 0; i < 5; ++i) {
trySpawn(500, i);
}
addEntityToList(newAirWizardEntity(630, 820, 0), &eManager);
daytime = 6000;
day = 0;
season = 0;
rain = false;
} else {
initPlayer();
resetQuests();
loadWorld(currentFileName, &eManager, &player, (u8*) map, (u8*) data);
}
updateMusic(currentLevel, daytime);
initMiniMap(loadUpWorld);
} else {
//reset level data so no old data can somehow remain
memset(map, 0, 128*128*5 * sizeof(u8));
memset(data, 0, 128*128*5 * sizeof(u8));
currentLevel = 1;
//TODO: Can Packets get dropped - if yes, should resending be handled by network functions (so I dont need to do it everywhere)
networkPacket packet = {
.requestMapData = {
.type = PACKET_REQUEST_MAPDATA,
.level = 1
}
};
networkSend(&packet, sizeof(packetRequestMapData));
}
shouldRenderMap = false;
mScrollX = 0;
mScrollY = 0;
zoomLevel = 2;
sprintf(mapText,"x%d",zoomLevel);
initGame = 0; initGame = 0;
} }
void setupBGMap(bool loadUpWorld) { void setupGameServer() {
size_t size;
networkHostStopConnections();
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 setupBGMap() {
// Reset entity manager. // Reset entity manager.
memset(&eManager, 0, sizeof(eManager)); memset(&eManager, 0, sizeof(eManager));
sf2d_set_clear_color(0xFF6C6D82); //sf2d_set_clear_color(RGBA8(0x82, 0x6D, 0x6C, 0xFF)); sf2d_set_clear_color(0xFF6C6D82);
if(!loadUpWorld) {
newSeed(); srand(time(NULL));
createAndValidateTopMap(128, 128, 1, map[1], data[1]); createAndValidateTopMap(128, 128, 1, worldData.map[1], worldData.data[1]);
} else {
loadWorld(currentFileName, &eManager, &player, (u8*) map, (u8*) data);
}
// Reset entity manager. // Reset entity manager.
memset(&eManager, 0, sizeof(eManager)); memset(&eManager, 0, sizeof(eManager));
sf2d_set_clear_color(0xFF6C6D82); //sf2d_set_clear_color(RGBA8(0x82, 0x6D, 0x6C, 0xFF)); sf2d_set_clear_color(0xFF6C6D82);
initBGMap = 0; initBGMap = 0;
} }
static void task_apt_hook(APT_HookType hook, void* param) {
int xscr = 0, yscr = 0; switch(hook) {
void tick() { case APTHOOK_ONSUSPEND:
//TODO: Some stuff DOES need to happen even on client side paused = true;
if(!isRemote) { stopMusic();
if (player.p.isDead) { break;
if (player.p.endTimer < 1) { default:
currentMenu = MENU_LOSE; break;
} }
--player.p.endTimer;
return;
} else if (player.p.hasWon) {
if (player.p.endTimer < 1) {
currentMenu = MENU_WIN;
}
--player.p.endTimer;
return;
}
tickTouchMap();
tickTouchQuickSelect();
++daytime;
//daytime += 20;
if(daytime>=24000) {
daytime -= 24000;
++day;
//TODO: maybe make season length not as hardcoded + make the transition better (fade to black and back maybe?)
if(day%7==0) {
++season;
if(season==4) season = 0;
}
rain = false;
if(season!=3 && rand()%5==0) rain = true;
}
if(daytime==6000 && currentLevel==1) {
playMusic(music_floor1);
} else if(daytime==19000 && currentLevel==1) {
playMusic(music_floor1_night);
}
int i;
for (i = 0; i < 324; ++i) {
int xx = rand() & 127;
int yy = rand() & 127;
tickTile(xx, yy);
}
tickPlayer();
xscr = player.x - 100;
yscr = player.y - 56;
if (xscr < 16)
xscr = 16;
else if (xscr > 1832)
xscr = 1832;
if (yscr < 16)
yscr = 16;
else if (yscr > 1912)
yscr = 1912;
if(eManager.lastSlot[currentLevel]<80 && currentLevel != 5) {
trySpawn(1, currentLevel);
}
for (i = 0; i < eManager.lastSlot[currentLevel]; ++i) {
Entity * e = &eManager.entities[currentLevel][i];
if ((e->type != ENTITY_ZOMBIE && e->type != ENTITY_SKELETON && e->type != ENTITY_KNIGHT && e->type != ENTITY_SLIME && e->type != ENTITY_PASSIVE)
|| (e->type == ENTITY_GLOWWORM && (daytime>6000 || daytime<18000))
|| (e->x > player.x - 160 && e->y > player.y - 125 && e->x < player.x + 160 && e->y < player.y + 125))
tickEntity(e);
}
}
} }
//for rendering -> move to a better place
extern int xscr, yscr;
char debugText[34]; char debugText[34];
char bossHealthText[34]; char bossHealthText[34];
int main() { int main() {
romfsInit();
cfguInit(); cfguInit();
CFGU_GetSystemModel(&MODEL_3DS); CFGU_GetSystemModel(&MODEL_3DS);
FILE * file; FILE * file;
@ -213,181 +111,128 @@ int main() {
sf2d_init(); sf2d_init();
csndInit(); csndInit();
networkInit(); 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); noItem = newItem(ITEM_NULL, 0);
initMenus(); initMenus();
currentMenu = MENU_TITLE; currentMenu = MENU_TITLE;
currentSelection = 0; currentSelection = 0;
quitGame = false; 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); font = sfil_load_PNG_buffer(Font_png, SF2D_PLACE_RAM);
bottombg = sfil_load_PNG_buffer(bottombg_png, SF2D_PLACE_RAM); bottombg = sfil_load_PNG_buffer(bottombg_png, SF2D_PLACE_RAM);
loadSounds(); loadSounds();
playMusic(music_menu); playMusic(&music_menu);
bakeLights(); bakeLights();
int i; int i;
for (i = 0; i < 6; ++i) { for (i = 0; i < 6; ++i) {
minimap[i] = sf2d_create_texture(128, 128, TEXFMT_RGBA8, SF2D_PLACE_RAM); minimap[i] = sf2d_create_texture(128, 128, TEXFMT_RGBA8, SF2D_PLACE_RAM);
sf2d_texture_tile32(minimap[i]); sf2d_texture_tile32(minimap[i]);
} }
reloadColors(); reloadColors();
sf2d_set_vblank_wait(true); sf2d_set_vblank_wait(true);
sf2d_set_clear_color(0xFF); sf2d_set_clear_color(0xFF);
/* Default inputs */ /* Default inputs */
k_up.input = KEY_DUP | KEY_CPAD_UP | KEY_CSTICK_UP; localInputs.k_up.input = KEY_DUP | KEY_CPAD_UP | KEY_CSTICK_UP;
k_down.input = KEY_DDOWN | KEY_CPAD_DOWN | KEY_CSTICK_DOWN; localInputs.k_down.input = KEY_DDOWN | KEY_CPAD_DOWN | KEY_CSTICK_DOWN;
k_left.input = KEY_DLEFT | KEY_CPAD_LEFT | KEY_CSTICK_LEFT; localInputs.k_left.input = KEY_DLEFT | KEY_CPAD_LEFT | KEY_CSTICK_LEFT;
k_right.input = KEY_DRIGHT | KEY_CPAD_RIGHT | KEY_CSTICK_RIGHT; localInputs.k_right.input = KEY_DRIGHT | KEY_CPAD_RIGHT | KEY_CSTICK_RIGHT;
k_attack.input = KEY_A | KEY_B | KEY_L | KEY_ZR; localInputs.k_attack.input = KEY_A;
k_menu.input = KEY_X | KEY_Y | KEY_R | KEY_ZL; localInputs.k_pickup.input = KEY_B;
k_pause.input = KEY_START; localInputs.k_use.input = KEY_X;
k_accept.input = KEY_A; localInputs.k_menu.input = KEY_Y ;
k_decline.input = KEY_B; localInputs.k_pause.input = KEY_START;
k_delete.input = KEY_X; localInputs.k_accept.input = KEY_A;
k_menuNext.input = KEY_R; localInputs.k_decline.input = KEY_B;
k_menuPrev.input = KEY_L; 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 btnSave exists, then use that. */
if ((file = fopen("btnSave.bin", "rb"))) { if ((file = fopen("btnSave.bin", "rb"))) {
fread(&k_up.input, sizeof(int), 1, file); fread(&(localInputs.k_up.input), sizeof(int), 1, file);
fread(&k_down.input, sizeof(int), 1, file); fread(&(localInputs.k_down.input), sizeof(int), 1, file);
fread(&k_left.input, sizeof(int), 1, file); fread(&(localInputs.k_left.input), sizeof(int), 1, file);
fread(&k_right.input, sizeof(int), 1, file); fread(&(localInputs.k_right.input), sizeof(int), 1, file);
fread(&k_attack.input, sizeof(int), 1, file); fread(&(localInputs.k_attack.input), sizeof(int), 1, file);
fread(&k_menu.input, sizeof(int), 1, file); fread(&(localInputs.k_pickup.input), sizeof(int), 1, file);
fread(&k_pause.input, sizeof(int), 1, file); fread(&(localInputs.k_use.input), sizeof(int), 1, file);
fread(&k_accept.input, sizeof(int), 1, file); fread(&(localInputs.k_menu.input), sizeof(int), 1, file);
fread(&k_decline.input, sizeof(int), 1, file); fread(&(localInputs.k_pause.input), sizeof(int), 1, file);
fread(&k_delete.input, sizeof(int), 1, file); fread(&(localInputs.k_accept.input), sizeof(int), 1, file);
fread(&k_menuNext.input, sizeof(int), 1, file); fread(&(localInputs.k_decline.input), sizeof(int), 1, file);
fread(&k_menuPrev.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); fclose(file);
} }
/* If lastTP exists, then use that. */ /* If lastTP exists, then use that. */
if ((file = fopen("lastTP.bin", "r"))) { if ((file = fopen("lastTP.bin", "r"))) {
char fnbuf[256]; char fnbuf[256];
fgets(fnbuf, 256, file); // get directory to texturepack fgets(fnbuf, 256, file); // get directory to texturepack
loadTexturePack(fnbuf); loadTexturePack(fnbuf);
fclose(file); fclose(file);
} }
tickCount = 0; initPlayers();
initRecipes(); initRecipes();
initQuests(); initTrades();
aptHook(&cookie, task_apt_hook, NULL);
while (aptMainLoop()) { while (aptMainLoop()) {
++tickCount; if (paused == true) playMusic(&music_menu);
hidScanInput();
tickKeys(hidKeysHeld(), hidKeysDown());
if (quitGame) break; if (quitGame) break;
if (initGame > 0) setupGame(initGame == 1 ? true : false, isRemote); if (initGame > 0 && --initGame==0) setupGame();
if (initBGMap > 0) setupBGMap(initBGMap == 1 ? true : false); if (initMPGame > 0 && --initMPGame==0) setupGameServer();
if (initBGMap > 0 && --initBGMap==0) setupBGMap();
networkRecieve(); if (currentMenu == MENU_NONE) {
tickGame();
if (currentMenu == 0) { renderGame();
tick();
//TODO: Multiplayer should use normal drawing code -> so remove this first test again
if(!isRemote) {
sf2d_start_frame(GFX_TOP, GFX_LEFT);
offsetX = xscr;
offsetY = yscr;
sf2d_draw_rectangle(0, 0, 400, 240, 0xFF0C0C0C); //RGBA8(12, 12, 12, 255)); //You might think "real" black would be better, but it actually looks better that way
renderLightsToStencil(false, false, true);
renderBackground(xscr, yscr);
renderEntities(player.x, player.y, &eManager);
renderPlayer();
renderWeather(xscr, yscr);
resetStencilStuff();
renderDayNight();
offsetX = 0;
offsetY = 0;
if(shouldRenderDebug){
sprintf(fpsstr, "FPS: %.0f X:%d Y:%d E:%d %d", sf2d_get_fps(), player.x, player.y, eManager.lastSlot[currentLevel], player.p.strengthTimer);
drawText(fpsstr, 2, 225);
}
sf2d_end_frame();
sf2d_start_frame(GFX_BOTTOM, GFX_LEFT);
if(!shouldRenderMap){
sf2d_draw_texture(bottombg, 0, 0);
renderGui();
} else {
renderZoomedMap();
}
sf2d_end_frame();
//TODO: Multiplayer should use normal drawing code -> so remove this first test again
} else {
//TODO: Temporary way of getting back to the menu
if (k_pause.clicked){
sf2d_set_clear_color(0xFF);
currentSelection = 0;
currentMenu = MENU_TITLE;
networkDisconnect();
playMusic(music_menu);
}
tmxscr += tmenuxa;
tmyscr += tmenuya;
if (tmxscr < 16) {
tmxscr = 16;
tmenuxa = -tmenuxa;
} else if (tmxscr > 1832) {
tmxscr = 1832;
tmenuxa = -tmenuxa;
}
if (tmyscr < 16) {
tmyscr = 16;
tmenuya = -tmenuya;
} else if (tmyscr > 1792) {
tmyscr = 1792;
tmenuya = -tmenuya;
}
sf2d_start_frame(GFX_TOP, GFX_LEFT);
offsetX = (int) tmxscr; offsetY = (int) tmyscr;
renderBackground((int) tmxscr, (int) tmyscr);
offsetX = 0; offsetY = 0;
sf2d_end_frame();
sf2d_start_frame(GFX_BOTTOM, GFX_LEFT);
sf2d_end_frame();
}
} else { } else {
//input scanning ingame is handled by the synchronizer
hidScanInput();
tickKeys(&localInputs, hidKeysHeld(), hidKeysDown());
tickMenu(currentMenu); tickMenu(currentMenu);
renderMenu(currentMenu, xscr, yscr); renderMenu(currentMenu, xscr, yscr);
} }
sf2d_swapbuffers(); sf2d_swapbuffers();
} }
stopMusic(); stopMusic();
freeQuests(); freeTrades();
freeRecipes(); freeRecipes();
freePlayers();
freeLightBakes(); freeLightBakes();
sf2d_free_texture(icons); sf2d_free_texture(icons);
@ -401,6 +246,7 @@ int main() {
networkExit(); networkExit();
csndExit(); csndExit();
cfguExit(); cfguExit();
romfsExit();
sf2d_fini(); sf2d_fini();
return 0; return 0;
} }

4
source/minizip/crypt.h Normal file → Executable file
View file

@ -1,9 +1,9 @@
/* crypt.h -- base code for crypt/uncrypt ZIPfile /* 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 This code is a modified version of crypting code in Infozip distribution

7
source/minizip/ioapi.c Normal file → Executable file
View file

@ -1,9 +1,9 @@
/* ioapi.c -- IO base function header for compress/uncompress .zip /* ioapi.c -- IO base function header for compress/uncompress .zip
files using zlib + zip or unzip API 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 <stdio.h> #include <stdio.h>
@ -141,8 +141,7 @@ long ZCALLBACK fseek_file_func (opaque, stream, offset, origin)
default: return -1; default: return -1;
} }
ret = 0; ret = 0;
if (fseek((FILE *)stream, offset, fseek_origin) != 0) fseek((FILE *)stream, offset, fseek_origin);
ret = -1;
return ret; return ret;
} }

4
source/minizip/ioapi.h Normal file → Executable file
View file

@ -1,9 +1,9 @@
/* ioapi.h -- IO base function header for compress/uncompress .zip /* ioapi.h -- IO base function header for compress/uncompress .zip
files using zlib + zip or unzip API 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 #ifndef _ZLIBIOAPI_H

122
source/minizip/unzip.c Normal file → Executable file
View file

@ -1,7 +1,7 @@
/* unzip.c -- IO for uncompress .zip files using zlib /* 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 Read unzip.h for more info
*/ */
@ -103,9 +103,6 @@ typedef struct
{ {
char *read_buffer; /* internal buffer for compressed data */ char *read_buffer; /* internal buffer for compressed data */
z_stream stream; /* zLib stream structure for inflate */ 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 pos_in_zipfile; /* position in byte on the zipfile, for fseek*/
uLong stream_initialised; /* flag set if stream structure is initialised*/ 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 *pX;
{ {
uLong x ; uLong x ;
int i = 0; int i;
int err; int err;
err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i);
@ -235,7 +232,7 @@ local int unzlocal_getLong (pzlib_filefunc_def,filestream,pX)
uLong *pX; uLong *pX;
{ {
uLong x ; uLong x ;
int i = 0; int i;
int err; int err;
err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); 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)); s=(unz_s*)ALLOC(sizeof(unz_s));
if (s!=NULL) *s=us;
{ unzGoToFirstFile((unzFile)s);
*s=us;
unzGoToFirstFile((unzFile)s);
}
return (unzFile)s; return (unzFile)s;
} }
@ -614,12 +608,10 @@ local int unzlocal_GetCurrentFileInfoInternal (file,
/* we check the magic */ /* we check the magic */
if (err==UNZ_OK) if (err==UNZ_OK)
{
if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK)
err=UNZ_ERRNO; err=UNZ_ERRNO;
else if (uMagic!=0x02014b50) else if (uMagic!=0x02014b50)
err=UNZ_BADZIPFILE; err=UNZ_BADZIPFILE;
}
if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK) if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK)
err=UNZ_ERRNO; err=UNZ_ERRNO;
@ -696,13 +688,10 @@ local int unzlocal_GetCurrentFileInfoInternal (file,
uSizeRead = extraFieldBufferSize; uSizeRead = extraFieldBufferSize;
if (lSeek!=0) if (lSeek!=0)
{
if (ZSEEK(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) if (ZSEEK(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0)
lSeek=0; lSeek=0;
else else
err=UNZ_ERRNO; err=UNZ_ERRNO;
}
if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0))
if (ZREAD(s->z_filefunc, s->filestream,extraField,uSizeRead)!=uSizeRead) if (ZREAD(s->z_filefunc, s->filestream,extraField,uSizeRead)!=uSizeRead)
err=UNZ_ERRNO; err=UNZ_ERRNO;
@ -724,13 +713,10 @@ local int unzlocal_GetCurrentFileInfoInternal (file,
uSizeRead = commentBufferSize; uSizeRead = commentBufferSize;
if (lSeek!=0) if (lSeek!=0)
{
if (ZSEEK(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) if (ZSEEK(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0)
lSeek=0; lSeek=0;
else else
err=UNZ_ERRNO; err=UNZ_ERRNO;
}
if ((file_info.size_file_comment>0) && (commentBufferSize>0)) if ((file_info.size_file_comment>0) && (commentBufferSize>0))
if (ZREAD(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead) if (ZREAD(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead)
err=UNZ_ERRNO; err=UNZ_ERRNO;
@ -991,12 +977,10 @@ local int unzlocal_CheckCurrentFileCoherencyHeader (s,piSizeVar,
if (err==UNZ_OK) if (err==UNZ_OK)
{
if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK)
err=UNZ_ERRNO; err=UNZ_ERRNO;
else if (uMagic!=0x04034b50) else if (uMagic!=0x04034b50)
err=UNZ_BADZIPFILE; err=UNZ_BADZIPFILE;
}
if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK)
err=UNZ_ERRNO; err=UNZ_ERRNO;
@ -1013,9 +997,6 @@ local int unzlocal_CheckCurrentFileCoherencyHeader (s,piSizeVar,
err=UNZ_BADZIPFILE; err=UNZ_BADZIPFILE;
if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && 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)) (s->cur_file_info.compression_method!=Z_DEFLATED))
err=UNZ_BADZIPFILE; err=UNZ_BADZIPFILE;
@ -1130,9 +1111,6 @@ extern int ZEXPORT unzOpenCurrentFile3 (file, method, level, raw, password)
} }
if ((s->cur_file_info.compression_method!=0) && 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)) (s->cur_file_info.compression_method!=Z_DEFLATED))
err=UNZ_BADZIPFILE; 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; 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) && if ((s->cur_file_info.compression_method==Z_DEFLATED) &&
(!raw)) (!raw))
{ {
@ -1185,7 +1135,7 @@ extern int ZEXPORT unzOpenCurrentFile3 (file, method, level, raw, password)
err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS);
if (err == Z_OK) if (err == Z_OK)
pfile_in_zip_read_info->stream_initialised=Z_DEFLATED; pfile_in_zip_read_info->stream_initialised=1;
else else
{ {
TRYFREE(pfile_in_zip_read_info); 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->pfile_in_zip_read = pfile_in_zip_read_info;
s->encrypted = 0;
# ifndef NOUNCRYPT # ifndef NOUNCRYPT
if (password != NULL) if (password != NULL)
{ {
@ -1386,53 +1334,6 @@ extern int ZEXPORT unzReadCurrentFile (file, buf, len)
iRead += uDoCopy; iRead += uDoCopy;
} }
else 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; uLong uTotalOutBefore,uTotalOutAfter;
const Bytef *bufBefore; const Bytef *bufBefore;
@ -1611,12 +1512,8 @@ extern int ZEXPORT unzCloseCurrentFile (file)
TRYFREE(pfile_in_zip_read_info->read_buffer); TRYFREE(pfile_in_zip_read_info->read_buffer);
pfile_in_zip_read_info->read_buffer = NULL; 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); 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; pfile_in_zip_read_info->stream_initialised = 0;
TRYFREE(pfile_in_zip_read_info); TRYFREE(pfile_in_zip_read_info);
@ -1637,6 +1534,7 @@ extern int ZEXPORT unzGetGlobalComment (file, szComment, uSizeBuf)
char *szComment; char *szComment;
uLong uSizeBuf; uLong uSizeBuf;
{ {
int err=UNZ_OK;
unz_s* s; unz_s* s;
uLong uReadThis ; uLong uReadThis ;
if (file==NULL) if (file==NULL)
@ -1669,7 +1567,7 @@ extern uLong ZEXPORT unzGetOffset (file)
unz_s* s; unz_s* s;
if (file==NULL) if (file==NULL)
return 0; return UNZ_PARAMERROR;
s=(unz_s*)file; s=(unz_s*)file;
if (!s->current_file_ok) if (!s->current_file_ok)
return 0; return 0;

10
source/minizip/unzip.h Normal file → Executable file
View file

@ -1,7 +1,7 @@
/* unzip.h -- IO for uncompress .zip files using zlib /* 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 This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g
WinZip, InfoZip tools and compatible. WinZip, InfoZip tools and compatible.
@ -57,12 +57,6 @@ extern "C" {
#include "ioapi.h" #include "ioapi.h"
#endif #endif
#ifdef HAVE_BZIP2
#include "bzlib.h"
#endif
#define Z_BZIP2ED 12
#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) #if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP)
/* like the STRICT of WIN32, we define a pointer that cannot be converted /* like the STRICT of WIN32, we define a pointer that cannot be converted
from (void*) without cast */ from (void*) without cast */

170
source/minizip/zip.c Normal file → Executable file
View file

@ -1,10 +1,10 @@
/* zip.c -- IO on .zip files using zlib /* 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 27 Dec 2004 Rolf Kalbermatter
Modification to zipOpen2 to support globalComment retrieval. 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 Read zip.h for more info
*/ */
@ -320,9 +320,9 @@ local uLong ziplocal_TmzDateToDosDate(ptm,dosDate)
uLong dosDate; uLong dosDate;
{ {
uLong year = (uLong)ptm->tm_year; uLong year = (uLong)ptm->tm_year;
if (year>=1980) if (year>1980)
year-=1980; year-=1980;
else if (year>=80) else if (year>80)
year-=80; year-=80;
return return
(uLong) (((ptm->tm_mday) + (32 * (ptm->tm_mon+1)) + (512 * year)) << 16) | (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 *pX;
{ {
uLong x ; uLong x ;
int i = 0; int i;
int err; int err;
err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i);
@ -401,7 +401,7 @@ local int ziplocal_getLong (pzlib_filefunc_def,filestream,pX)
uLong *pX; uLong *pX;
{ {
uLong x ; uLong x ;
int i = 0; int i;
int err; int err;
err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); 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 Locate the Central directory of a zipfile (at the end, just before
the global comment) the global comment)
Fix from Riccardo Cohen
*/ */
local uLong ziplocal_SearchCentralDir OF(( local uLong ziplocal_SearchCentralDir OF((
const zlib_filefunc_def* pzlib_filefunc_def, const zlib_filefunc_def* pzlib_filefunc_def,
voidpf filestream)); voidpf filestream));
local uLong ziplocal_SearchCentralDir(pzlib_filefunc_def,filestream) local uLong ziplocal_SearchCentralDir(pzlib_filefunc_def,filestream)
const zlib_filefunc_def* pzlib_filefunc_def; const zlib_filefunc_def* pzlib_filefunc_def;
voidpf filestream; voidpf filestream;
{ {
unsigned char* buf; unsigned char* buf;
uLong uSizeFile; uLong uSizeFile;
uLong uBackRead; uLong uBackRead;
uLong uMaxBack=0xffff; /* maximum size of global comment */ uLong uMaxBack=0xffff; /* maximum size of global comment */
uLong uPosFound=0; uLong uPosFound=0;
if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0)
return 0; return 0;
uSizeFile = ZTELL(*pzlib_filefunc_def,filestream); uSizeFile = ZTELL(*pzlib_filefunc_def,filestream);
if (uMaxBack>uSizeFile) if (uMaxBack>uSizeFile)
uMaxBack = uSizeFile; uMaxBack = uSizeFile;
buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4);
if (buf==NULL) if (buf==NULL)
return 0; return 0;
uBackRead = 4; uBackRead = 4;
while (uBackRead<uMaxBack) while (uBackRead<uMaxBack)
{ {
uLong uReadSize,uReadPos ; uLong uReadSize,uReadPos ;
int i; int i;
if (uBackRead+BUFREADCOMMENT>uMaxBack) if (uBackRead+BUFREADCOMMENT>uMaxBack)
uBackRead = uMaxBack; uBackRead = uMaxBack;
else else
uBackRead+=BUFREADCOMMENT; uBackRead+=BUFREADCOMMENT;
uReadPos = uSizeFile-uBackRead ; uReadPos = uSizeFile-uBackRead ;
uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ?
(BUFREADCOMMENT+4) : (uSizeFile-uReadPos); (BUFREADCOMMENT+4) : (uSizeFile-uReadPos);
if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0)
break; break;
if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize)
break; break;
for (i=(int)uReadSize-3; (i--)>0;) for (i=(int)uReadSize-3; (i--)>0;)
if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) &&
((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06))
{ {
uPosFound = uReadPos+i; uPosFound = uReadPos+i;
break; break;
} }
if (uPosFound!=0) if (uPosFound!=0)
break; break;
} }
TRYFREE(buf); TRYFREE(buf);
return uPosFound; return uPosFound;
} }
#endif /* !NO_ADDFILEINEXISTINGZIP*/ #endif /* !NO_ADDFILEINEXISTINGZIP*/
/************************************************************/ /************************************************************/
@ -523,8 +521,6 @@ extern zipFile ZEXPORT zipOpen2 (pathname, append, globalcomment, pzlib_filefunc
if (ziinit.filestream == NULL) if (ziinit.filestream == NULL)
return 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.begin_pos = ZTELL(ziinit.z_filefunc,ziinit.filestream);
ziinit.in_opened_file_inzip = 0; ziinit.in_opened_file_inzip = 0;
ziinit.ci.stream_initialised = 0; ziinit.ci.stream_initialised = 0;
@ -562,10 +558,9 @@ extern zipFile ZEXPORT zipOpen2 (pathname, append, globalcomment, pzlib_filefunc
uLong size_comment; uLong size_comment;
central_pos = ziplocal_SearchCentralDir(&ziinit.z_filefunc,ziinit.filestream); central_pos = ziplocal_SearchCentralDir(&ziinit.z_filefunc,ziinit.filestream);
/* disable to allow appending to empty ZIP archive
if (central_pos==0) if (central_pos==0)
err=ZIP_ERRNO; err=ZIP_ERRNO;
*/
if (ZSEEK(ziinit.z_filefunc, ziinit.filestream, if (ZSEEK(ziinit.z_filefunc, ziinit.filestream,
central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0)
err=ZIP_ERRNO; err=ZIP_ERRNO;
@ -620,7 +615,7 @@ extern zipFile ZEXPORT zipOpen2 (pathname, append, globalcomment, pzlib_filefunc
if (size_comment>0) if (size_comment>0)
{ {
ziinit.globalcomment = (char*)ALLOC(size_comment+1); ziinit.globalcomment = ALLOC(size_comment+1);
if (ziinit.globalcomment) if (ziinit.globalcomment)
{ {
size_comment = ZREAD(ziinit.z_filefunc, ziinit.filestream,ziinit.globalcomment,size_comment); 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); 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_local, size_extrafield_local,
extrafield_global, size_extrafield_global, extrafield_global, size_extrafield_global,
comment, method, level, raw, comment, method, level, raw,
windowBits, memLevel, strategy, windowBits, memLevel, strategy,
password, crcForCrypting, versionMadeBy, flagBase) password, crcForCrypting)
zipFile file; zipFile file;
const char* filename; const char* filename;
const zip_fileinfo* zipfi; const zip_fileinfo* zipfi;
@ -714,8 +709,6 @@ extern int ZEXPORT zipOpenNewFileInZip4 (file, filename, zipfi,
int strategy; int strategy;
const char* password; const char* password;
uLong crcForCrypting; uLong crcForCrypting;
uLong versionMadeBy;
uLong flagBase;
{ {
zip_internal* zi; zip_internal* zi;
uInt size_filename; 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); else zi->ci.dosDate = ziplocal_TmzDateToDosDate(&zipfi->tmz_date,zipfi->dosDate);
} }
zi->ci.flag = flagBase; zi->ci.flag = 0;
if ((level==8) || (level==9)) if ((level==8) || (level==9))
zi->ci.flag |= 2; zi->ci.flag |= 2;
if ((level==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); ziplocal_putValue_inmemory(zi->ci.central_header,(uLong)CENTRALHEADERMAGIC,4);
/* version info */ /* 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+6,(uLong)20,2);
ziplocal_putValue_inmemory(zi->ci.central_header+8,(uLong)zi->ci.flag,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); 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.next_out = zi->ci.buffered_data;
zi->ci.stream.total_in = 0; zi->ci.stream.total_in = 0;
zi->ci.stream.total_out = 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)) 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 level;
int raw; int raw;
{ {
return zipOpenNewFileInZip4 (file, filename, zipfi, return zipOpenNewFileInZip3 (file, filename, zipfi,
extrafield_local, size_extrafield_local, extrafield_local, size_extrafield_local,
extrafield_global, size_extrafield_global, extrafield_global, size_extrafield_global,
comment, method, level, raw, comment, method, level, raw,
-MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, -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, extern int ZEXPORT zipOpenNewFileInZip (file, filename, zipfi,
extrafield_local, size_extrafield_local, extrafield_local, size_extrafield_local,
extrafield_global, size_extrafield_global, extrafield_global, size_extrafield_global,
@ -975,12 +935,10 @@ extern int ZEXPORT zipOpenNewFileInZip (file, filename, zipfi,
int method; int method;
int level; int level;
{ {
return zipOpenNewFileInZip4 (file, filename, zipfi, return zipOpenNewFileInZip2 (file, filename, zipfi,
extrafield_local, size_extrafield_local, extrafield_local, size_extrafield_local,
extrafield_global, size_extrafield_global, extrafield_global, size_extrafield_global,
comment, method, level, 0, comment, method, level, 0);
-MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
NULL, 0, VERSIONMADEBY, 0);
} }
local int zipFlushWriteBuffer(zi) local int zipFlushWriteBuffer(zi)
@ -1020,9 +978,9 @@ extern int ZEXPORT zipWriteInFileInZip (file, buf, len)
if (zi->in_opened_file_inzip == 0) if (zi->in_opened_file_inzip == 0)
return ZIP_PARAMERROR; 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.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)) 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)) if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
{ {
int tmp_err=deflateEnd(&zi->ci.stream); err=deflateEnd(&zi->ci.stream);
if (err == ZIP_OK)
err = tmp_err;
zi->ci.stream_initialised = 0; zi->ci.stream_initialised = 0;
} }
@ -1217,7 +1173,7 @@ extern int ZEXPORT zipClose (file, global_comment)
ldi = ldi->next_datablock; ldi = ldi->next_datablock;
} }
} }
free_linkedlist(&(zi->central_dir)); free_datablock(zi->central_dir.first_block);
if (err==ZIP_OK) /* Magic End */ if (err==ZIP_OK) /* Magic End */
err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)ENDHEADERMAGIC,4); err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)ENDHEADERMAGIC,4);

30
source/minizip/zip.h Normal file → Executable file
View file

@ -1,7 +1,7 @@
/* zip.h -- IO for compress .zip files using zlib /* 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 This unzip package allow creates .ZIP file, compatible with PKZip 2.04g
WinZip, InfoZip tools and compatible. WinZip, InfoZip tools and compatible.
@ -191,7 +191,8 @@ extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file,
int memLevel, int memLevel,
int strategy, int strategy,
const char* password, const char* password,
uLong crcForCrypting)); uLong crcForCtypting));
/* /*
Same than zipOpenNewFileInZip2, except Same than zipOpenNewFileInZip2, except
windowBits,memLevel,,strategy : see parameter strategy in deflateInit2 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) 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, extern int ZEXPORT zipWriteInFileInZip OF((zipFile file,
const void* buf, const void* buf,

218
source/texturepack.c Normal file → Executable file
View file

@ -1,166 +1,100 @@
#include "texturepack.h" #include "texturepack.h"
#define dir_delimter '/' #include "ZipHelper.h"
#define MAX_FILENAME 256
#define READ_SIZE 9216
#define MAX_FILENAME 256
bool texturepackUseDefaultIcons = true;
bool texturepackUseDefaultPlayer = true;
bool texturepackUseDefaultFont = true;
bool texturepackUseDefaultBottom = true;
void toLowerString(char * str){ void toLowerString(char * str){
int i; int i;
for (i = 0; str[i] != '\0'; i++)str[i] = (char)tolower((unsigned char)str[i]); for (i = 0; str[i] != '\0'; i++)str[i] = (char)tolower((unsigned char)str[i]);
} }
int getTexturePackComment(char * filename, char * cmmtBuf){ int getTexturePackComment(char * filename, char * cmmtBuf){
// Open the zip file // Open the zip file
unzFile *zipfile = unzOpen(filename); unzFile *zipfile = unzOpen(filename);
if ( zipfile == NULL ) return 1; // Error: ZipFile could not be opened. 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);
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){ int loadTexture(char * filename) {
char lowerFilename[MAX_FILENAME];
bool useDefaultIcons = true; strcpy(lowerFilename,filename);
bool useDefaultFont = true; toLowerString(lowerFilename);
bool useDefaultBottom = true;
// Open the zip file if(strcmp(lowerFilename, "icons.png") == 0){
unzFile *zipfile = unzOpen(filename); if(sfil_load_PNG_file(filename, SF2D_PLACE_RAM) == NULL){
if ( zipfile == NULL ) return 1; // Error: ZipFile could not be opened. return 0;
}
// Get info about the zip file
unz_global_info global_info;
if (unzGetGlobalInfo(zipfile, &global_info ) != UNZ_OK )
{
unzClose( zipfile );
return 2; // Error: Could not read global info
}
// Buffer to hold data read from the zip file. icons = sfil_load_PNG_file(filename, SF2D_PLACE_RAM);
char read_buffer[ READ_SIZE ]; reloadColors();
// Loop to extract all files texturepackUseDefaultIcons = false;
uLong i; } else if(strcmp(lowerFilename, "player.png") == 0){
for ( i = 0; i < global_info.number_entry; ++i ) if(sfil_load_PNG_file(filename, SF2D_PLACE_RAM) == NULL){
{ return 0;
// 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
}
// Check if this entry is NOT a directory or file. playerSprites = sfil_load_PNG_file(filename, SF2D_PLACE_RAM);
const size_t filename_length = strlen( filename );
if ( filename[ filename_length-1 ] != dir_delimter ){
if ( unzOpenCurrentFile( zipfile ) != UNZ_OK )
{
unzClose( zipfile );
return 4;
}
// Open a file to write out the data. texturepackUseDefaultPlayer = false;
FILE * out = fopen(filename, "wb" ); } else if(strcmp(lowerFilename, "font.png") == 0){
if ( out == NULL ) if(sfil_load_PNG_file(filename, SF2D_PLACE_RAM) == NULL){
{ return 0;
unzCloseCurrentFile( zipfile ); }
unzClose( zipfile );
return 5;
}
int error = UNZ_OK; font = sfil_load_PNG_file(filename, SF2D_PLACE_RAM);
do
{
error = unzReadCurrentFile( zipfile, read_buffer, READ_SIZE );
if ( error < 0 )
{
//printf( "error %d\n", error );
unzCloseCurrentFile( zipfile );
unzClose( zipfile );
return 6;
}
// Write data to file. texturepackUseDefaultFont = false;
if ( error > 0 ) } else if(strcmp(lowerFilename, "bottombg.png") == 0){
{ if(sfil_load_PNG_file(filename, SF2D_PLACE_RAM) == NULL){
fwrite( read_buffer, error, 1, out ); // You should check return of fwrite... return 0;
} }
} while ( error > 0 );
fclose(out); bottombg = sfil_load_PNG_file(filename, SF2D_PLACE_RAM);
char lowerFilename[MAX_FILENAME];
strcpy(lowerFilename,filename);
toLowerString(lowerFilename);
if(strcmp(lowerFilename,"icons.png") == 0){
if(sfil_load_PNG_file(filename, SF2D_PLACE_RAM) == NULL){
unzCloseCurrentFile( zipfile );
unzClose( zipfile );
return 7;
}
icons = sfil_load_PNG_file(filename, SF2D_PLACE_RAM);
reloadColors();
useDefaultIcons = false;
} else if(strcmp(lowerFilename,"font.png") == 0){ texturepackUseDefaultBottom = false;
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);
}
unzCloseCurrentFile( zipfile ); return 0;
}
// Go the the next entry listed in the zip file.
if ( ( i+1 ) < global_info.number_entry ) int loadTexturePack(char * filename) {
{ texturepackUseDefaultIcons = true;
if ( unzGoToNextFile( zipfile ) != UNZ_OK ) texturepackUseDefaultPlayer = true;
{ texturepackUseDefaultFont = true;
unzClose( zipfile ); texturepackUseDefaultBottom = true;
return 7;
} if(unzipAndLoad(filename, &loadTexture, NULL, ZIPHELPER_CLEANUP_FILES)!=0) {
} return 1;
} }
if(useDefaultIcons){ if(texturepackUseDefaultIcons){
icons = sfil_load_PNG_buffer(icons2_png, SF2D_PLACE_RAM); icons = sfil_load_PNG_buffer(icons_png, SF2D_PLACE_RAM);
reloadColors(); reloadColors();
} }
if(useDefaultFont) font = sfil_load_PNG_buffer(Font_png, SF2D_PLACE_RAM); if(texturepackUseDefaultPlayer) playerSprites = sfil_load_PNG_buffer(player_png, SF2D_PLACE_RAM);
if(useDefaultBottom) bottombg = sfil_load_PNG_buffer(bottombg_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);
unzClose( zipfile );
return 0;
return 0;
} }

3
source/texturepack.h Normal file → Executable file
View file

@ -1,6 +1,5 @@
#pragma once #pragma once
#include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include <sf2d.h> #include <sf2d.h>