Compare commits

...

82 commits
v1.2 ... master

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
Elijah Bansley
ac02eeb2a7
Update README.md 2018-01-22 00:18:30 -06:00
ElijahZAwesome
6c6906defd Merge branch 'master' of https://github.com/ElijahZAwesome/Minicraft3DS 2018-01-21 23:57:38 -06:00
ElijahZAwesome
88996ed3b3 beginning work on potions 2018-01-21 23:57:22 -06:00
Elijah Bansley
b59d2abf83
Update README.md 2018-01-21 20:52:35 -06:00
Elijah Bansley
bc5540a793
Readme slight overhaul 2018-01-21 20:22:10 -06:00
Elijah Bansley
0d2e7fe139
Delete icon.icn 2018-01-21 20:20:19 -06:00
Elijah Bansley
a5addeca26
Delete banner.bnr 2018-01-21 20:20:13 -06:00
Elijah Bansley
e7863fed9a
Delete Minicraft3DS.3ds 2018-01-21 20:19:20 -06:00
ElijahZAwesome
44ec93b94b Merge branch 'master' of https://github.com/ElijahZAwesome/Minicraft3DS 2018-01-21 20:18:57 -06:00
ElijahZAwesome
7d47f5f9ca made banner audio actually work 2018-01-21 20:18:52 -06:00
Elijah Bansley
70561aa04c
Update README.md 2018-01-21 19:30:12 -06:00
ElijahZAwesome
84a85beb57 Added golden apple, toggle-able Beta Mode, and credits to me 2018-01-21 19:26:25 -06:00
Elijah Bansley
dc2c9c0f6d Added 3DS Build
This is compatible with Citra, dunno if it works on real hardware.
2017-08-26 18:59:44 -05:00
Elijah Bansley
366e973ebc Made CIA build 2017-08-26 18:26:39 -05:00
André Schweiger
3b2a0f4de8 First network test
Warning: old, unfinished code
2017-04-26 21:39:46 +02:00
Andre Schweiger
c107f82abb Menu Preparations 2017-01-30 12:40:07 +01:00
André Schweiger
a014be2c5a Begin Network Implementation 2017-01-26 23:23:58 +01:00
André Schweiger
31d576e879 Update to newest ctrulib (finally)
Also updated other dependencies, now also requires citro3d to build.
Also had to modify glowworm lights??? (small textures dont work?)
2017-01-23 21:13:48 +01:00
Andre Schweiger
3699414dcd Started work on new update
Added some small new map generation features
Added "NPCs" and "Quests" (atleast a first experiment for them)
Added magic compass to make search for stairs leass annoying
Added mostly visual season and weather effects
2017-01-07 21:54:28 +01:00
Andre Schweiger
f6e2d30ab6 Forgot to change version string 2016-05-27 12:02:32 +02:00
Andre Schweiger
3fa845e415 Fix Sound Stopping 2016-05-27 11:16:40 +02:00
Andre Schweiger
73775f875a All the Changes for 1.2.1
Sorry forgot to commit once again.
Just look in the changelog of the release
2016-02-29 14:19:32 +01:00
Andre Schweiger
9a8c8bc714 Fix How to Play Menu 2016-02-06 10:55:25 +01:00
Andre Schweiger
0ef9e89705 Rework Tilesheet
No more autocoloring!
(atleast for Maptiles)
2016-02-05 18:13:39 +01:00
Andre Schweiger
6f0ea3ef65 Fix Fireflies getting removed sometimes causing really wiered stuff 2016-01-01 22:24:14 +01:00
Andre Schweiger
9910bf462a Made Night a little lighter 2016-01-01 20:36:44 +01:00
78 changed files with 10314 additions and 5597 deletions

0
.gitattributes vendored Normal file → Executable file
View file

6
.gitignore vendored Normal file → Executable file
View file

@ -46,9 +46,15 @@ Temporary Items
*.3dsx
*.elf
*.smdh
*.cia
*.3ds
*.icn
*.bnr
build
# Eclipse
.settings
.cproject
.project
# Copyrighted Executables

34
Makefile Normal file → Executable file
View file

@ -26,15 +26,18 @@ include $(DEVKITARM)/3ds_rules
# - icon.png
# - <libctru folder>/default_icon.png
#---------------------------------------------------------------------------------
TARGET := Minicraft3DS
TARGET := result/Minicraft3DS
ICON_TARGET := icons-banners/icon
RESULT := result
BUILD := build
SOURCES := source source/minizip
DATA := data
INCLUDES := include
ROMFS := romfs
APP_TITLE := Test
APP_DESCRIPTION := ???
APP_AUTHOR := Davideesk
APP_TITLE := Minicraft 3DS
APP_DESCRIPTION := Originally created by Notch.
APP_AUTHOR := Davideesk/andre111/ElijahZAwesome/tognee
#---------------------------------------------------------------------------------
# options for code generation
@ -52,7 +55,7 @@ CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lsfil -lpng -ljpeg -lz -lsf2d -lctru -lm
LIBS := -lsfil -lpng -ljpeg -lz -lsf2d -lcitro3d -lctru -lm
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
@ -106,13 +109,7 @@ export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
ifeq ($(strip $(ICON)),)
icons := $(wildcard *.png)
ifneq (,$(findstring $(TARGET).png,$(icons)))
export APP_ICON := $(TOPDIR)/$(TARGET).png
else
ifneq (,$(findstring icon.png,$(icons)))
export APP_ICON := $(TOPDIR)/icon.png
endif
endif
export APP_ICON := $(TOPDIR)/$(ICON_TARGET).png
else
export APP_ICON := $(TOPDIR)/$(ICON)
endif
@ -121,12 +118,17 @@ ifeq ($(strip $(NO_SMDH)),)
export _3DSXFLAGS += --smdh=$(CURDIR)/$(TARGET).smdh
endif
ifneq ($(ROMFS),)
export _3DSXFLAGS += --romfs=$(CURDIR)/$(ROMFS)
endif
.PHONY: $(BUILD) clean all
#---------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
@test -d $(RESULT) || mkdir $(RESULT)
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
@ -139,12 +141,12 @@ $(TARGET)-strip.elf: $(BUILD)
@$(STRIP) $(TARGET).elf -o $(TARGET)-strip.elf
#---------------------------------------------------------------------------------
cci: $(TARGET)-strip.elf
@makerom -f cci -rsf resources/$(TARGET).rsf -target d -exefslogo -elf $(TARGET)-strip.elf -o $(TARGET).3ds
@echo "built ... sfil_sample.3ds"
@makerom -f cci -rsf resources/$(TARGET).rsf -target d -exefslogo -elf $(TARGET)-strip.elf -o $(TARGET).3ds -desc App:0x1B
@echo "built ... Minicraft3DS.3ds"
#---------------------------------------------------------------------------------
cia: $(TARGET)-strip.elf
@makerom -f cia -o $(TARGET).cia -elf $(TARGET)-strip.elf -rsf resources/$(TARGET).rsf -exefslogo -target t
@echo "built ... sfil_sample.cia"
@makerom -f cia -o $(TARGET).cia -elf $(TARGET)-strip.elf -rsf icons-banners/$(TARGET).rsf -exefslogo -target t
@echo "built ... Minicraft3DS.cia"
#---------------------------------------------------------------------------------
send: $(BUILD)
@3dslink $(TARGET).3dsx

69
README.md Normal file → Executable file
View file

@ -1,21 +1,78 @@
# Minicraft3DS
3DS Homebrew port of Notch's ludum dare game "Minicraft"
Current Version: Version 1.6.1
Dependencies:
----------
**Download:**
If you just want to download the game prebuilt check the releases tab in Github:
//TODO
For building the game yourself look below.
----------
**Dependencies:**
For building and installing the dependencies look below.
ctrulib by smea: https://github.com/smealum/ctrulib
citro3d by fincs: https://github.com/fincs/citro3d
sf2dlib by xerpi: https://github.com/xerpi/sf2dlib
sfillib by xerpi: https://github.com/xerpi/sfillib
zlib: http://www.zlib.net/
Current Version: Version 1.2
----------
**Building:**
**1. Install devkitARM by devkitPro**
- On Windows download https://sourceforge.net/projects/devkitpro/files/Automated%20Installer/
- And install atleast Minimal System and devkitARM
- This includes make, ctrulib and citro3d
**2. Install zlib, libjpeg-turbo and libpng**
- Download 3DS-Portlibs: https://github.com/devkitPro/3ds_portlibs
- Run these commands:
```
make zlib
make install-zlib
make libjpeg-turbo
make libpng
make install
```
**3. Install sf2dlib**
- Download https://github.com/xerpi/sf2dlib
- In the libsf2d directory run these commands:
```
make
make install
```
**4. Install sfillib**
- Download https://github.com/xerpi/sfillib
- In the libsfil directory run these commands:
```
make
make install
```
**5. You can now build Minicraft3DS 3dsx, elf, cia, and 3ds files by running the build.bat file.**
----------
You can do anything with the source code (besides sell it) as long as you give proper credit to the right people.
If you are going to make a mod of this version, be sure to give credit to Markus "Notch" Perrson because he did create the original game after all.
# Misc
This source code is subject to a lot of change for better optimization/cleanliness.
Forum thread: https://gbatemp.net/threads/release-beta-minicraft-3ds-homebrew-edition.399295/
Forum thread: //TODO

17
build.bat Executable file
View file

@ -0,0 +1,17 @@
@echo off
echo Building 3DSX/ELF/SMDH...
make
echo Creating banner...
bannertool makebanner -i icons-banners/banner.png -a icons-banners/audio.wav -o icons-banners/banner.bnr
echo Creating icon...
bannertool makesmdh -s "Minicraft3DS" -l "3DS Homebrew port of Notch's ludum dare game 'Minicraft', updated." -p "Davideesk/Andre111/ElijahZAwesome" -i icons-banners/icon.png -o icons-banners/icon.icn
echo Creating ROMFS...
3dstool -cvtf romfs icons-banners/romfs.bin --romfs-dir romfs/
echo Creating CIA...
makerom -f cia -o result/Minicraft3DS.cia -DAPP_ENCRYPTED=false -rsf icons-banners/Minicraft3DS.rsf -target t -exefslogo -elf result/Minicraft3DS.elf -icon icons-banners/icon.icn -banner icons-banners/banner.bnr -romfs icons-banners/romfs.bin
echo Creating 3DS/CCI...
makerom -f cci -o result/Minicraft3DS.3ds -DAPP_ENCRYPTED=true -rsf icons-banners/Minicraft3DS.rsf -target t -exefslogo -elf result/Minicraft3DS.elf -icon icons-banners/icon.icn -banner icons-banners/banner.bnr -romfs icons-banners/romfs.bin
echo Cleaning Up...
del /s result\Minicraft3DS.elf
del /s result\Minicraft3DS.smdh
pause

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: 17 KiB

BIN
data/player.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

195
icons-banners/Minicraft3DS.rsf Executable file
View file

@ -0,0 +1,195 @@
BasicInfo:
Title : "Cia Builder Example"
CompanyCode : "00"
ProductCode : "CTR-P-PWDR"
ContentType : Application
Logo : Homebrew # Nintendo / Licensed / Distributed / iQue / iQueForSystem
TitleInfo:
UniqueId : 0xFAA5C # Cia Builder Example Unique ID, you need to set your own one
Category : Application
CardInfo:
MediaSize : 128MB # 128MB / 256MB / 512MB / 1GB / 2GB / 4GB
MediaType : Card1 # Card1 / Card2
CardDevice : None # NorFlash(Pick this if you use savedata) / None
Option:
UseOnSD : true # true if App is to be installed to SD
FreeProductCode : true # Removes limitations on ProductCode
MediaFootPadding : false # If true CCI files are created with padding
EnableCrypt : false # Enables encryption for NCCH and CIA
EnableCompress : true # Compresses exefs code
AccessControlInfo:
#UseExtSaveData : true
#ExtSaveDataId: 0xff3ff
#UseExtendedSaveDataAccessControl: true
#AccessibleSaveDataIds: [0x101, 0x202, 0x303, 0x404, 0x505, 0x606]
SystemControlInfo:
SaveDataSize: 128KB
RemasterVersion: 0
StackSize: 0x40000
# DO NOT EDIT BELOW HERE OR PROGRAMS WILL NOT LAUNCH (most likely)
AccessControlInfo:
FileSystemAccess:
- Debug
- DirectSdmc
- DirectSdmcWrite
IdealProcessor : 0
AffinityMask : 1
Priority : 16
MaxCpu : 0x9E # Default
DisableDebug : false
EnableForceDebug : false
CanWriteSharedPage : false
CanUsePrivilegedPriority : false
CanUseNonAlphabetAndNumber : false
PermitMainFunctionArgument : false
CanShareDeviceMemory : false
RunnableOnSleep : false
SpecialMemoryArrange : false
CoreVersion : 2
DescVersion : 2
ReleaseKernelMajor : "02"
ReleaseKernelMinor : "33"
MemoryType : Application
HandleTableSize: 512
SystemModeExt : Legacy # Legacy(Default)/124MB/178MB Legacy:Use Old3DS SystemMode
CpuSpeed : 804MHz # 268MHz(Default)/804MHz
EnableL2Cache : true # false(default)/true
CanAccessCore2 : true
IORegisterMapping:
- 1ff50000-1ff57fff
- 1ff70000-1ff77fff
MemoryMapping:
- 1f000000-1f5fffff:r
SystemCallAccess:
ArbitrateAddress: 34
Break: 60
CancelTimer: 28
ClearEvent: 25
ClearTimer: 29
CloseHandle: 35
ConnectToPort: 45
ControlMemory: 1
CreateAddressArbiter: 33
CreateEvent: 23
CreateMemoryBlock: 30
CreateMutex: 19
CreateSemaphore: 21
CreateThread: 8
CreateTimer: 26
DuplicateHandle: 39
ExitProcess: 3
ExitThread: 9
GetCurrentProcessorNumber: 17
GetHandleInfo: 41
GetProcessId: 53
GetProcessIdOfThread: 54
GetProcessIdealProcessor: 6
GetProcessInfo: 43
GetResourceLimit: 56
GetResourceLimitCurrentValues: 58
GetResourceLimitLimitValues: 57
GetSystemInfo: 42
GetSystemTick: 40
GetThreadContext: 59
GetThreadId: 55
GetThreadIdealProcessor: 15
GetThreadInfo: 44
GetThreadPriority: 11
MapMemoryBlock: 31
OutputDebugString: 61
QueryMemory: 2
ReleaseMutex: 20
ReleaseSemaphore: 22
SendSyncRequest1: 46
SendSyncRequest2: 47
SendSyncRequest3: 48
SendSyncRequest4: 49
SendSyncRequest: 50
SetThreadPriority: 12
SetTimer: 27
SignalEvent: 24
SleepThread: 10
UnmapMemoryBlock: 32
WaitSynchronization1: 36
WaitSynchronizationN: 37
InterruptNumbers:
ServiceAccessControl:
- APT:U
- $hioFIO
- $hostio0
- $hostio1
- ac:u
- boss:U
- cam:u
- ir:rst
- cfg:u
- dlp:FKCL
- dlp:SRVR
- dsp::DSP
- frd:u
- fs:USER
- gsp::Gpu
- hid:USER
- http:C
- mic:u
- ndm:u
- news:s
- nwm::UDS
- ptm:u
- pxi:dev
- soc:U
- gsp::Lcd
- y2r:u
- ldr:ro
- ir:USER
- ir:u
- csnd:SND
- am:u
- ns:s
SystemControlInfo:
Dependency:
ac: 0x0004013000002402L
am: 0x0004013000001502L
boss: 0x0004013000003402L
camera: 0x0004013000001602L
cecd: 0x0004013000002602L
cfg: 0x0004013000001702L
codec: 0x0004013000001802L
csnd: 0x0004013000002702L
dlp: 0x0004013000002802L
dsp: 0x0004013000001a02L
friends: 0x0004013000003202L
gpio: 0x0004013000001b02L
gsp: 0x0004013000001c02L
hid: 0x0004013000001d02L
http: 0x0004013000002902L
i2c: 0x0004013000001e02L
ir: 0x0004013000003302L
mcu: 0x0004013000001f02L
mic: 0x0004013000002002L
ndm: 0x0004013000002b02L
news: 0x0004013000003502L
nim: 0x0004013000002c02L
nwm: 0x0004013000002d02L
pdn: 0x0004013000002102L
ps: 0x0004013000003102L
ptm: 0x0004013000002202L
ro: 0x0004013000003702L
socket: 0x0004013000002e02L
spi: 0x0004013000002302L
ssl: 0x0004013000002f02L

BIN
icons-banners/audio.wav Executable file

Binary file not shown.

BIN
icons-banners/banner.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6 KiB

BIN
icons-banners/icon.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 B

BIN
icons-banners/romfs.bin Normal file

Binary file not shown.

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

@ -1,163 +1,193 @@
#include "Crafting.h"
void cloneRecipeManager(RecipeManager *from, RecipeManager *to) {
//free old manager recipes
free(to->recipes);
//copy over recipes
to->size = from->size;
to->recipes = (Recipe*)malloc(sizeof(Recipe) * to->size);
memcpy(to->recipes, from->recipes, sizeof(Recipe) * to->size);
}
void checkCanCraftRecipes(RecipeManager * rm, Inventory * inv){
int i, j;
for(i = 0; i < rm->size; i++){
rm->recipes[i].canCraft = true;
for(j = 0; j < rm->recipes[i].numOfCosts; j++){
if(countItemInv(rm->recipes[i].costs[j].costItem,0,inv) < rm->recipes[i].costs[j].costAmount){
rm->recipes[i].canCraft = false;
}
}
}
int i, j;
for(i = 0; i < rm->size; i++){
rm->recipes[i].canCraft = true;
for(j = 0; j < rm->recipes[i].numOfCosts; j++){
if(countItemInv(rm->recipes[i].costs[j].costItem,0,inv) < rm->recipes[i].costs[j].costAmount){
rm->recipes[i].canCraft = false;
}
}
}
}
int compareCanCraft(const void * ra, const void * rb) {
Recipe* r1 = (Recipe*)ra;
Recipe* r2 = (Recipe*)rb;
Recipe* r1 = (Recipe*)ra;
Recipe* r2 = (Recipe*)rb;
if (r1->canCraft && !r2->canCraft) return -1;
if (!r1->canCraft && r2->canCraft) return 1;
if (!r1->canCraft && r2->canCraft) return 1;
return r1->order - r2->order; // Needed for stable sorting.
}
void sortRecipes(RecipeManager * rm){
qsort(rm->recipes,rm->size,sizeof(Recipe),compareCanCraft);
qsort(rm->recipes,rm->size,sizeof(Recipe),compareCanCraft);
}
void deductCost(Cost c, Inventory * inv){
Item* item = getItemFromInventory(c.costItem, inv);
if(!item->onlyOne){
item->countLevel -= c.costAmount;
if(item->countLevel < 1) removeItemFromInventory(item->slotNum, inv);
} else {
removeItemFromInventory(item->slotNum, inv);
}
Item* item = getItemFromInventory(c.costItem, inv);
if(!item->onlyOne){
item->countLevel -= c.costAmount;
if(item->countLevel < 1) removeItemFromInventory(item->slotNum, inv);
} else {
removeItemFromInventory(item->slotNum, inv);
}
}
bool craftItem(RecipeManager * rm, Recipe* r, Inventory* inv){
if(r->canCraft){
int i;
for(i=0;i<r->numOfCosts;++i) deductCost(r->costs[i], inv);
Item item = newItem(r->itemResult,r->itemAmountLevel);
if(r->canCraft){
int i;
for(i=0;i<r->numOfCosts;++i) deductCost(r->costs[i], inv);
Item item = newItem(r->itemResult,r->itemAmountLevel);
if(!item.onlyOne && countItemInv(item.id,item.countLevel,inv) > 0){
getItemFromInventory(item.id, inv)->countLevel += r->itemAmountLevel;
} else{
pushItemToInventoryFront(item, inv);
}
checkCanCraftRecipes(rm, inv);
sortRecipes(rm);
return true;
}
return false;
if(!item.onlyOne && countItemInv(item.id,item.countLevel,inv) > 0){
getItemFromInventory(item.id, inv)->countLevel += r->itemAmountLevel;
} else{
pushItemToInventoryFront(item, inv);
}
checkCanCraftRecipes(rm, inv);
sortRecipes(rm);
return true;
}
return false;
}
Cost newCost(int i, int c){
Cost nc;
nc.costItem = i;
nc.costAmount = c;
return nc;
Cost nc;
nc.costItem = i;
nc.costAmount = c;
return nc;
}
u8 curPlace = 0;
Recipe defineRecipe(int item, int amountOrLevel, int numArgs, ...){
Recipe r;
r.itemResult = item;
r.itemAmountLevel = amountOrLevel;
r.numOfCosts = numArgs;
int i;
va_list al;
numArgs <<= 1; // Did this to get rid of a warning.
va_start(al,numArgs);
for(i=0;i<r.numOfCosts;++i) r.costs[i] = newCost(va_arg(al,int), va_arg(al,int));
va_end(al);
r.order = curPlace;
curPlace++;
return r;
Recipe r;
r.itemResult = item;
r.itemAmountLevel = amountOrLevel;
r.numOfCosts = numArgs;
int i;
va_list al;
numArgs <<= 1; // Did this to get rid of a warning.
va_start(al,numArgs);
for(i=0;i<r.numOfCosts;++i) r.costs[i] = newCost(va_arg(al,int), va_arg(al,int));
va_end(al);
r.order = curPlace;
curPlace++;
return r;
}
void initRecipes(){
curPlace = 0;
curPlace = 0;
workbenchRecipes.size = 22;
workbenchRecipes.recipes = (Recipe*)malloc(sizeof(Recipe) * (workbenchRecipes.size));
workbenchRecipes.recipes[0] = defineRecipe(ITEM_WORKBENCH,1,1,ITEM_WOOD,20);
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);
inHeadRecipes.size = 2;
inHeadRecipes.recipes = (Recipe*)malloc(sizeof(Recipe) * (inHeadRecipes.size));
inHeadRecipes.recipes[0] = defineRecipe(ITEM_WORKBENCH,1,1,ITEM_WOOD,10);
inHeadRecipes.recipes[1] = defineRecipe(ITEM_TORCH,1,2,ITEM_WOOD,1,ITEM_COAL,1);
workbenchRecipes.size = 24;
workbenchRecipes.recipes = (Recipe*)malloc(sizeof(Recipe) * (workbenchRecipes.size));
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[7] = defineRecipe(TOOL_SWORD,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[10] = defineRecipe(TOOL_PICKAXE,0,1,ITEM_WOOD,5);
workbenchRecipes.recipes[11] = defineRecipe(TOOL_SHOVEL,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[9] = defineRecipe(TOOL_HOE,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[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[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[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[18] = defineRecipe(TOOL_SHOVEL,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[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[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[20] = defineRecipe(ITEM_WALL_WOOD,1,1,ITEM_WOOD,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 = 16;
anvilRecipes.recipes = (Recipe*)malloc(sizeof(Recipe) * (anvilRecipes.size));
anvilRecipes.recipes[0] = defineRecipe(TOOL_SWORD,2,2,ITEM_WOOD,5,ITEM_IRONINGOT,5);
anvilRecipes.recipes[1] = defineRecipe(TOOL_AXE,2,2,ITEM_WOOD,5,ITEM_IRONINGOT,5);
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[4] = defineRecipe(TOOL_SHOVEL,2,2,ITEM_WOOD,5,ITEM_IRONINGOT,5);
anvilRecipes.size = 19;
anvilRecipes.recipes = (Recipe*)malloc(sizeof(Recipe) * (anvilRecipes.size));
anvilRecipes.recipes[0] = defineRecipe(TOOL_SWORD,2,2,ITEM_WOOD,5,ITEM_IRONINGOT,5);
anvilRecipes.recipes[1] = defineRecipe(TOOL_AXE,2,2,ITEM_WOOD,5,ITEM_IRONINGOT,5);
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[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[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[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[10] = defineRecipe(TOOL_SHOVEL,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[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[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[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[14] = defineRecipe(ITEM_WALL_IRON,1,1,ITEM_IRONINGOT,2);
anvilRecipes.recipes[15] = defineRecipe(ITEM_WALL_GOLD,1,1,ITEM_GOLDINGOT,2);
anvilRecipes.recipes[16] = defineRecipe(ITEM_COIN,3,1,ITEM_IRONINGOT,1);
anvilRecipes.recipes[17] = defineRecipe(ITEM_POTION_MAKER,1,3,ITEM_IRONINGOT,5,ITEM_GOLDINGOT,25,ITEM_GEM,5);
anvilRecipes.recipes[18] = defineRecipe(ITEM_SHEARS,1,1,ITEM_IRONINGOT,2);
furnaceRecipes.size = 3;
furnaceRecipes.recipes = (Recipe*)malloc(sizeof(Recipe) * (furnaceRecipes.size));
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);
furnaceRecipes.recipes[2] = defineRecipe(ITEM_GLASS,1,2,ITEM_SAND,4,ITEM_COAL,1);
furnaceRecipes.size = 3;
furnaceRecipes.recipes = (Recipe*)malloc(sizeof(Recipe) * (furnaceRecipes.size));
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);
furnaceRecipes.recipes[2] = defineRecipe(ITEM_GLASS,1,2,ITEM_SAND,4,ITEM_COAL,1);
ovenRecipes.size = 3;
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[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.recipes[0] = defineRecipe(ITEM_STRING,1,1,ITEM_WOOL,1);
loomRecipes.size = 2;
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.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[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[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[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[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[5] = defineRecipe(ITEM_ARROW_GEM,1,3,ITEM_WOOD,1,ITEM_GEM,3,ITEM_STRING,1);
enchanterRecipes.recipes[6] = defineRecipe(ITEM_WALL_GEM,1,1,ITEM_GEM,10);
enchanterRecipes.recipes[7] = defineRecipe(ITEM_WIZARD_SUMMON,1,4,ITEM_CLOUD,100,ITEM_IRONINGOT,10,ITEM_BONE,10,ITEM_LEATHER,10);
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 */
void freeRecipes(){
free(workbenchRecipes.recipes);
free(ovenRecipes.recipes);
free(furnaceRecipes.recipes);
free(anvilRecipes.recipes);
free(inHeadRecipes.recipes);
free(workbenchRecipes.recipes);
free(ovenRecipes.recipes);
free(furnaceRecipes.recipes);
free(anvilRecipes.recipes);
free(loomRecipes.recipes);
free(enchanterRecipes.recipes);
free(potionMakerRecipes.recipes);
}

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

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

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

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

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

@ -19,96 +19,115 @@
#define ENTITY_KNIGHT 13
#define ENTITY_GLOWWORM 14
#define ENTITY_DRAGON 15
#define ENTITY_DRAGONPROJECTILE 16
#define ENTITY_MAGIC_PILLAR 17
#define ENTITY_NPC 18
typedef struct Entity Entity;
typedef struct _plrd PlayerData; //in order to not include Player.h and cause all sorts of problems
typedef struct {
s8 ax;
s8 ay;
u8 dir;
s8 health;
s8 stamina;
s8 staminaRecharge;
s8 staminaRechargeDelay;
s8 attackTimer;
u8 spawnTrigger;
bool isDead;
bool hasWon;
bool hasWonSaved;
s8 endTimer;
s16 walkDist;
bool isCarrying;
bool isSwimming;
int swimTimer;
s8 ax;
s8 ay;
u8 dir;
s8 health;
s8 stamina;
s8 staminaRecharge;
s8 staminaRechargeDelay;
s8 attackTimer;
u8 spawnTrigger;
bool isDead;
bool hasWon;
bool hasWonSaved;
s8 endTimer;
s16 walkDist;
bool isCarrying;
bool isSwimming;
int swimTimer;
int regenTimer;
int strengthTimer;
int swimBreathTimer;
int speedTimer;
int score;
Inventory* inv;
Item* activeItem;
PlayerData *data;
} Player;
typedef struct {
float xa;
float ya;
float za;
float xx;
float yy;
float zz;
s16 age;
Item item;
float xa;
float ya;
float za;
float xx;
float yy;
float zz;
s16 age;
Item item;
} EntityItem;
typedef struct {
s16 itemID;
bool active;
s8 r; // light radius for lantern. window select for chests.
Inventory* inv; // Points to chest inventory.
s16 oSel; // other selection inside the chest inv.
s16 itemID;
bool active;
s8 r; // light radius for lantern.
Inventory* inv; // Points to chest inventory.
} EntityFurniture;
typedef struct {
u8 mtype;
s8 xa;
s8 ya;
s8 ya;
s16 health;
s8 dir;
s8 randWalkTime;
s8 walkDist;
s8 dir;
s8 randWalkTime;
s8 walkDist;
} PassiveMob;
typedef struct {
s8 xa;
s8 ya;
s16 health;
s8 dir;
s8 lvl;
s8 randWalkTime;
s8 walkDist;
s8 xa;
s8 ya;
s16 health;
s8 dir;
s8 lvl;
s8 randWalkTime;
s8 walkDist;
s8 randAttackTime;
u32 color;
u32 color;
} HostileMob;
typedef struct {
s8 xa;
s8 ya;
s16 health;
s8 lvl;
s8 dir;
s8 jumpTime;
u32 color;
s8 xa;
s8 ya;
s16 health;
s8 lvl;
s8 dir;
s8 jumpTime;
u32 color;
} Slime;
typedef struct {
s8 xa;
s8 ya;
s16 health;
s8 randWalkTime;
s8 walkDist;
s8 dir;
s8 xa;
s8 ya;
s16 health;
s8 randWalkTime;
s8 walkDist;
s8 dir;
int attackDelay;
int attackTime;
int attackType;
s8 spriteAdjust;
} AirWizard;
typedef struct {
Entity* parent;
s16 age;
float xa;
float ya;
float xx;
float yy;
} Spark;
typedef struct {
Entity* parent;
s16 age;
@ -118,80 +137,98 @@ typedef struct {
} Arrow;
typedef struct {
Entity* parent;
s16 age;
float xa;
float ya;
float xx;
float yy;
} Spark;
s8 xa;
s8 ya;
s16 health;
s8 randWalkTime;
s8 walkDist;
s8 dir;
int attackDelay;
int attackTime;
int attackType;
int animTimer;
} Dragon;
typedef struct {
s8 xa;
s8 ya;
s8 randWalkTime;
Entity* parent;
u8 type;
s16 age;
float xa;
float ya;
float xx;
float yy;
} DragonFire;
typedef struct {
s8 xa;
s8 ya;
s8 randWalkTime;
s8 waitTime;
} Glowworm;
typedef struct {
float xa;
float ya;
float za;
float xx;
float yy;
float zz;
s16 age;
char* text;
int color;
u8 type;
} NPC;
typedef struct {
float xa;
float ya;
float za;
float xx;
float yy;
float zz;
s16 age;
char* text;
int color;
} TextParticleEntity;
typedef struct {
s16 age;
s16 age;
} SmashParticleEntity;
struct Entity {
s16 x;
s16 y;
s8 xKnockback,yKnockback;
u8 xr,yr;
u8 type;
u8 level;
s8 hurtTime;
s16 slotNum; // Read-only. Do not mess with this.
bool canPass;
bool canSwim;
union {
Player p;
EntityItem entityItem;
EntityFurniture entityFurniture;
s16 x;
s16 y;
s8 xKnockback,yKnockback;
u8 xr,yr;
u8 type;
u8 level;
s8 hurtTime;
s16 slotNum; // Read-only. Do not mess with this.
bool canPass;
bool canSwim;
union {
Player p;
EntityItem entityItem;
EntityFurniture entityFurniture;
PassiveMob passive;
HostileMob hostile;
Slime slime;
AirWizard wizard;
Spark spark;
HostileMob hostile;
Slime slime;
AirWizard wizard;
Spark spark;
Arrow arrow;
Glowworm glowworm;
TextParticleEntity textParticle;
SmashParticleEntity smashParticle;
};
Dragon dragon;
DragonFire dragonFire;
NPC npc;
TextParticleEntity textParticle;
SmashParticleEntity smashParticle;
};
};
typedef struct {
Entity entities[6][1000];
Entity wizardSparks[120];
s16 lastSlot[6];
Inventory invs[301];//1 for the player, 300 for chests.
s16 nextInv;
Entity entities[6][1000];
s16 lastSlot[6];
Inventory invs[300];
s16 nextInv;
} EntityManager;
EntityManager eManager;
Entity nullEntity;
s8 currentLevel;
double gaussrand();
Entity newItemEntity(Item item, int x, int y, int level);
Entity newFurnitureEntity(int itemID,Inventory * invPtr, int x, int y, int level);
Entity newFurnitureEntity(int itemID, Inventory * invPtr, int x, int y, int level);
Entity newPassiveEntity(int type, int x, int y, int level);
Entity newZombieEntity(int lvl, int x, int y, int level);
Entity newSkeletonEntity(int lvl, int x, int y, int level);
@ -199,12 +236,13 @@ Entity newKnightEntity(int lvl, int x, int y, int level);
Entity newSlimeEntity(int lvl, int x, int y, int level);
Entity newAirWizardEntity(int x, int y, int level);
Entity newSparkEntity(Entity* parent, float xa, float ya);
Entity newDragonEntity(int x, int y, int level);
Entity newDragonFireEntity(Entity* parent, u8 type, int x, int y, float xa, float ya);
Entity newMagicPillarEntity(int x, int y, int level);
Entity newTextParticleEntity(char * str, u32 color, int xa, int ya, int level);
Entity newSmashParticleEntity(int xa, int ya, int level);
Entity newArrowEntity(Entity* parent, int itemID, s8 xa, s8 ya, int level);
Entity newGlowwormEntity(int x, int y, int level);
Entity newNPCEntity(int type, int x, int y, int level);
void addEntityToList(Entity e, EntityManager* em);
void removeEntityFromList(Entity * e,int level,EntityManager* em);

3011
source/Globals.c Normal file → Executable file

File diff suppressed because it is too large Load diff

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

@ -1,10 +1,14 @@
#pragma once
#include <3ds.h>
#include "SaveLoad.h"
#include "Entity.h"
#include "Player.h"
#include "Input.h"
#include "MapGen.h"
#include "Quests.h"
#include "icons2_png.h"
#include "icons_png.h"
#include "player_png.h"
#include "Font_png.h"
#include "bottombg_png.h"
@ -16,16 +20,29 @@
#define MENU_TUTORIAL 2
#define MENU_ABOUT 3
#define MENU_SETTINGS 4
#define MENU_INVENTORY 5
#define MENU_CRAFTING 6
#define MENU_CONTAINER 7
#define MENU_WIN 8
#define MENU_LOSE 9
#define MENU_PAUSED 10
#define MENU_LOADGAME 11
#define MENU_SETTINGS_REBIND 12
#define MENU_SETTINGS_TP 13
#define MENU_DUNGEON 14
#define MENU_LOADGAME 5
#define MENU_SETTINGS_REBIND 6
#define MENU_SETTINGS_TP 7
#define MENU_MULTIPLAYER_HOST 8
#define MENU_MULTIPLAYER_JOIN 9
#define MENU_MULTIPLAYER_WAIT 10
#define MENU_LOADING 11
#define MENU_PAUSED 100
#define MENU_INVENTORY 101
#define MENU_CRAFTING 102
#define MENU_CONTAINER 103
#define MENU_WIN 104
#define MENU_LOSE 105
#define MENU_DUNGEON 106
#define MENU_NPC 107
#define MENU_CHARACTER_CUSTOMIZE 108
#define NPC_GIRL 0
#define NPC_PRIEST 1
#define NPC_FARMER 2
#define NPC_LIBRARIAN 3
#define NPC_DWARF 4
#define TILE_NULL 255
#define TILE_GRASS 0
@ -59,31 +76,42 @@
#define TILE_DUNGEON_WALL 27
#define TILE_DUNGEON_FLOOR 28
#define TILE_DUNGEON_ENTRANCE 29
#define TILE_MAGIC_BARRIER 30
#define TILE_BOOKSHELVES 31
#define TILE_WOOD_FLOOR 32
#define TILE_MYCELIUM 33
#define TILE_MUSHROOM_BROWN 34
#define TILE_MUSHROOM_RED 35
#define TILE_ICE 36
#define SWAP_UINT32(x) (((x) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | ((x) << 24))
bool screenShot;
//WARNING: Having this set to different values in different clients will break multiplayer!
#define TESTGODMODE false
bool paused;
u32 localUID;
int loadedtp;
u8 MODEL_3DS;
extern char versionText[34];
Entity player;
bool shouldRenderDebug;
bool shouldSpeedup;
bool shouldRenderMap;
u8 zoomLevel;
char mapText[32];
s16 mScrollX, mScrollY;
bool UnderStrengthEffect;
bool UnderSpeedEffect;
bool regening;
bool UnderSwimBreathEffect;
sf2d_texture *icons;
sf2d_texture *playerSprites;
sf2d_texture *font;
sf2d_texture *bottombg;
sf2d_texture * minimap[6];
u8 map[6][128*128];
u8 data[6][128*128];
sf2d_texture *minimap[6];
u32 dirtColor[5];
u32 grassColor;
@ -96,60 +124,81 @@ u32 ironColor;
u32 goldColor;
u32 gemColor;
u32 dungeonColor[2];
u32 myceliumColor;
u32 mushroomColor;
u32 snowColor;
u32 iceColor;
char currentFileName[256];
extern u8 currentMenu;
extern char fpsstr[];
u8 initGame;
u8 initMPGame;
u8 initBGMap;
Item noItem;
int airWizardHealthDisplay;
s16 awX, awY;
u32 tickCount;
RecipeManager* currentRecipes;
Entity* curChestEntity;
s16 curInvSel;
bool quitGame;
s8 currentSelection;
u16 daytime;
typedef struct _worldData {
u8 map[6][128*128];
u8 data[6][128*128];
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);
s8 itemTileInteract(int tile, Item* item, int x, int y, int px, int py, int dir);
s8 itemTileInteract(int tile, PlayerData *pd, Item *item, s8 level, int x, int y, int px, int py, int dir);
void tickEntity(Entity* e);
void tickTouchMap();
void tickTouchQuickSelect();
void trySpawn(int count, int level);
int getTile(int x, int y);
int getTile(s8 level, int x, int y);
void setTile(int id, s8 level, int x, int y);
int getData(s8 level, int x, int y);
void setData(int id, s8 level, int x, int y);
u32 getTileColor(int tile);
void setTile(int id, int x, int y);
int getData(int x, int y);
void setData(int id, int x, int y);
bool intersectsEntity(int x, int y, int r, Entity* e);
bool EntityBlocksEntity(Entity* e1, Entity* e2);
void EntityVsEntity(Entity* e1, Entity* e2);
void entityTileInteract(Entity* e, int tile,int x, int y);
bool ItemVsEntity(PlayerData *pd, Item *item, Entity *e, int dir);
void entityTileInteract(Entity* e, int tile, s8 level, int x, int y);
void initPlayer();
void tickPlayer();
void playerAttack();
bool isSwimming();
bool playerUseEnergy(int amount);
void playerHurtTile(int tile, int xt, int yt, int damage, int dir);
bool playerIntersectsEntity(Entity* e);
void playerEntityInteract(Entity* e);
void playerSetActiveItem(Item * item);
void openCraftingMenu(PlayerData *pd, RecipeManager *rm, char *title);
bool useEntity(PlayerData *pd, Entity* e);
void enterDungeon();
void leaveDungeon();
bool isWater(s8 level, int xt, int yt);
void initMinimapLevel(int level, bool loadUpWorld);
void playerHurtTile(PlayerData *pd, int tile, s8 level, int xt, int yt, int damage, int dir);
void playerEntityInteract(PlayerData *pd, Entity* e);
bool dungeonActive();
void enterDungeon(PlayerData *pd);
void leaveDungeon(PlayerData *pd);
void setMinimapVisible(PlayerData *pd, int level, int x, int y, bool visible);
bool getMinimapVisible(PlayerData *pd, int level, int x, int y);
u32 getMinimapColor(PlayerData *pd, int level, int x, int y);
void initMinimapLevel(PlayerData *pd, int level);
void updateLevel1Map();
void reloadColors();

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

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

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

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

@ -1,24 +1,35 @@
#pragma once
#include <3ds.h>
//only down and clicked need to be send, input is for config stuff
typedef struct {
bool down, clicked;
int input;
} Key;
Key k_null;
Key k_up;
Key k_down;
Key k_left;
Key k_right;
Key k_attack;
Key k_menu;
Key k_pause;
Key k_accept;
Key k_decline;
Key k_delete;
Key k_menuNext;
Key k_menuPrev;
touchPosition k_touch;
typedef struct {
Key k_null;
Key k_up;
Key k_down;
Key k_left;
Key k_right;
Key k_attack;
Key k_pickup;
Key k_use;
Key k_menu;
Key k_pause;
Key k_accept;
Key k_decline;
Key k_delete;
Key k_menuNext;
Key k_menuPrev;
touchPosition k_touch;
} Inputs;
void tickKeys(u32 held, u32 down);
Inputs localInputs;
void tickKeys(Inputs *inputs, u32 held, u32 down);
void resetKeys(Inputs *inputs);
void resetClicked(Inputs *inputs);
bool clicked(Key key);

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

@ -2,162 +2,168 @@
char currentName[16];
bool isItemEmpty(Item* item){
if(item->id < 6 || item->id > 100 || item->onlyOne == true) return false;
if(item->countLevel < 1) return true;
return false;
bool isItemEmpty(Item* item) {
if(item->id < 6 || item->id > 100 || item->onlyOne == true) return false;
if(item->countLevel < 1) return true;
return false;
}
void pushItemToInventoryFront(Item item, Inventory * inv){
if(inv->lastSlot < 300) ++inv->lastSlot;
int i;
for(i = inv->lastSlot;i > 0;--i){
inv->items[i] = inv->items[i-1]; // Move the items up the list.
inv->items[i].slotNum = i;
}
item.invPtr = (int*)inv;
inv->items[0] = item;
inv->items[0].slotNum = 0;
void pushItemToInventoryFront(Item item, Inventory * inv) {
if(inv->lastSlot < 300) ++inv->lastSlot;
int i;
for(i = inv->lastSlot;i > 0;--i){
inv->items[i] = inv->items[i-1]; // Move the items up the list.
inv->items[i].slotNum = i;
}
item.invPtr = (int*)inv;
inv->items[0] = item;
inv->items[0].slotNum = 0;
}
void addItemToInventory(Item item, Inventory * inv){
if(!item.onlyOne){
int i;
for(i = 0;i < inv->lastSlot;++i){ //Search inventory if item already exists.
if(inv->items[i].id == item.id){
inv->items[i].countLevel+=item.countLevel;
return;
}
}
}
void addItemToInventory(Item item, Inventory * inv) {
if(!item.onlyOne){
int i;
for(i = 0;i < inv->lastSlot;++i){ //Search inventory if item already exists.
if(inv->items[i].id == item.id){
inv->items[i].countLevel+=item.countLevel;
return;
}
}
}
item.slotNum = inv->lastSlot;
item.invPtr = (int*)inv;
inv->items[inv->lastSlot] = item;
++inv->lastSlot;
item.slotNum = inv->lastSlot;
item.invPtr = (int*)inv;
inv->items[inv->lastSlot] = item;
++inv->lastSlot;
}
void removeItemFromCurrentInv(Item* item){
removeItemFromInventory(item->slotNum, (Inventory*)item->invPtr);
void removeItemFromCurrentInv(Item* item) {
removeItemFromInventory(item->slotNum, (Inventory*)item->invPtr);
}
Item nullItem;
void removeItemFromInventory(int slot, Inventory * inv){
int i;
for(i = slot;i < inv->lastSlot - 1;++i){
inv->items[i] = inv->items[i + 1]; // Move the items down.
inv->items[i].slotNum--;
}
--inv->lastSlot;
inv->items[inv->lastSlot] = nullItem; // Make the last slot null.
void removeItemFromInventory(int slot, Inventory * inv) {
int i;
for(i = slot;i < inv->lastSlot - 1;++i){
inv->items[i] = inv->items[i + 1]; // Move the items down.
inv->items[i].slotNum--;
}
--inv->lastSlot;
inv->items[inv->lastSlot] = nullItem; // Make the last slot null.
}
Item newItem(int id, int cLevel){
Item item;
item.id = id;
if(id != ITEM_NULL){
if(cLevel > 999) cLevel = 999;
item.countLevel = cLevel;
if(id < 7 || (id > 27 && id < 51) || id > 100) item.onlyOne = true; // Tools + Furniture.
else item.onlyOne = false;
}
item.chestPtr = NULL;
return item;
Item newItem(int id, int cLevel) {
Item item;
item.id = id;
if(id != ITEM_NULL){
if(cLevel > 999) cLevel = 999;
item.countLevel = cLevel;
if(id < 7 || (id > 27 && id < 51) || id > 100) item.onlyOne = true; // Tools + Furniture.
else item.onlyOne = false;
}
item.chestPtr = NULL;
return item;
}
Item* getItemFromInventory(int itemID, Inventory * inv){
int i;
for(i = 0;i < inv->lastSlot;++i){
if(inv->items[i].id == itemID){
return &inv->items[i];
}
}
return (Item*)NULL;
Item* getItemFromInventory(int itemID, Inventory * inv) {
int i;
for(i = 0;i < inv->lastSlot;++i){
if(inv->items[i].id == itemID){
return &inv->items[i];
}
}
return (Item*)NULL;
}
int countItemInv(int itemID, int level, Inventory* inv){
int i, count = 0;
for(i = 0;i < inv->lastSlot;++i){
if(inv->items[i].id == itemID){
if(inv->items[i].onlyOne){
if(level == inv->items[i].countLevel) count++;
} else count += inv->items[i].countLevel;
}
}
return count;
int countItemInv(int itemID, int level, Inventory* inv) {
int i, count = 0;
for(i = 0;i < inv->lastSlot;++i){
if(inv->items[i].id == itemID){
if(inv->items[i].onlyOne){
if(level == inv->items[i].countLevel) count++;
} else count += inv->items[i].countLevel;
}
}
return count;
}
char* getItemName(int itemID, int countLevel){
switch(itemID){
case TOOL_SHOVEL:
switch(countLevel){
case 1: return "Rock Shovel";
case 2: return "Iron Shovel";
case 3: return "Gold Shovel";
case 4: return "Gem Shovel";
default: return "Wood Shovel";
}
case TOOL_HOE:
switch(countLevel){
case 1: return "Rock Hoe";
case 2: return "Iron Hoe";
case 3: return "Gold Hoe";
case 4: return "Gem Hoe";
default: return "Wood Hoe";
}
case TOOL_SWORD:
switch(countLevel){
case 1: return "Rock Sword";
case 2: return "Iron Sword";
case 3: return "Gold Sword";
case 4: return "Gem Sword";
default: return "Wood Sword";
}
case TOOL_PICKAXE:
switch(countLevel){
case 1: return "Rock Pickaxe";
case 2: return "Iron Pickaxe";
case 3: return "Gold Pickaxe";
case 4: return "Gem Pickaxe";
default: return "Wood Pickaxe";
}
case TOOL_AXE:
switch(countLevel){
case 1: return "Rock Axe";
case 2: return "Iron Axe";
case 3: return "Gold Axe";
case 4: return "Gem Axe";
default: return "Wood Axe";
}
case ITEM_ANVIL: return "Anvil";
case ITEM_CHEST: return "Chest";
case ITEM_OVEN: return "Oven";
case ITEM_FURNACE: return "Furnace";
case ITEM_WORKBENCH: return "Workbench";
case ITEM_LANTERN: return "Lantern";
case ITEM_POWGLOVE: return "Power Glove";
case ITEM_FLOWER: sprintf(currentName,"%d Flower", countLevel); return currentName;
case ITEM_WOOD: sprintf(currentName,"%d Wood", countLevel); return currentName;
case ITEM_STONE: sprintf(currentName,"%d Stone", countLevel); return currentName;
case ITEM_SAND: sprintf(currentName,"%d Sand", countLevel); return currentName;
case ITEM_DIRT: sprintf(currentName,"%d Dirt", countLevel); return currentName;
case ITEM_CLOUD: sprintf(currentName,"%d Cloud", countLevel); return currentName;
case ITEM_ACORN: sprintf(currentName,"%d Acorn", countLevel); return currentName;
case ITEM_CACTUS: sprintf(currentName,"%d Cactus", countLevel); return currentName;
case ITEM_SEEDS: sprintf(currentName,"%d Seeds", countLevel); return currentName;
case ITEM_WHEAT: sprintf(currentName,"%d Wheat", countLevel); return currentName;
case ITEM_FLESH: sprintf(currentName,"%d Flesh", countLevel); return currentName;
case ITEM_BREAD: sprintf(currentName,"%d Bread", countLevel); return currentName;
case ITEM_APPLE: sprintf(currentName,"%d Apple", countLevel); return currentName;
case ITEM_COAL: sprintf(currentName,"%d Coal", countLevel); return currentName;
case ITEM_IRONORE: sprintf(currentName,"%d Iron ore", countLevel); return currentName;
case ITEM_GOLDORE: sprintf(currentName,"%d Gold ore", countLevel); return currentName;
case ITEM_IRONINGOT: sprintf(currentName,"%d Iron ingot", countLevel); return currentName;
case ITEM_GOLDINGOT: sprintf(currentName,"%d Gold ingot", countLevel); return currentName;
case ITEM_GLASS: sprintf(currentName,"%d Glass", countLevel); return currentName;
case ITEM_GEM: sprintf(currentName,"%d Gem", countLevel); return currentName;
case ITEM_SLIME: sprintf(currentName,"%d Slime", countLevel); return currentName;
char* getItemName(int itemID, int countLevel) {
switch(itemID){
case TOOL_SHOVEL:
switch(countLevel){
case 1: return "Rock Shovel";
case 2: return "Iron Shovel";
case 3: return "Gold Shovel";
case 4: return "Gem Shovel";
default: return "Wood Shovel";
}
case TOOL_HOE:
switch(countLevel){
case 1: return "Rock Hoe";
case 2: return "Iron Hoe";
case 3: return "Gold Hoe";
case 4: return "Gem Hoe";
default: return "Wood Hoe";
}
case TOOL_SWORD:
switch(countLevel){
case 1: return "Rock Sword";
case 2: return "Iron Sword";
case 3: return "Gold Sword";
case 4: return "Gem Sword";
default: return "Wood Sword";
}
case TOOL_PICKAXE:
switch(countLevel){
case 1: return "Rock Pickaxe";
case 2: return "Iron Pickaxe";
case 3: return "Gold Pickaxe";
case 4: return "Gem Pickaxe";
default: return "Wood Pickaxe";
}
case TOOL_AXE:
switch(countLevel){
case 1: return "Rock Axe";
case 2: return "Iron Axe";
case 3: return "Gold Axe";
case 4: return "Gem Axe";
default: return "Wood Axe";
}
case ITEM_ANVIL: return "Anvil";
case ITEM_CHEST: return "Chest";
case ITEM_OVEN: return "Oven";
case ITEM_FURNACE: return "Furnace";
case ITEM_WORKBENCH: return "Workbench";
case ITEM_POTION_MAKER: return "Potion Maker";
case ITEM_LANTERN: return "Lantern";
case ITEM_POWGLOVE: return "Power Glove";
case ITEM_FLOWER: sprintf(currentName,"%d Flower", countLevel); return currentName;
case ITEM_WOOD: sprintf(currentName,"%d Wood", countLevel); return currentName;
case ITEM_STONE: sprintf(currentName,"%d Stone", countLevel); return currentName;
case ITEM_SAND: sprintf(currentName,"%d Sand", countLevel); return currentName;
case ITEM_DIRT: sprintf(currentName,"%d Dirt", countLevel); return currentName;
case ITEM_CLOUD: sprintf(currentName,"%d Cloud", countLevel); return currentName;
case ITEM_ACORN: sprintf(currentName,"%d Acorn", countLevel); return currentName;
case ITEM_CACTUS: sprintf(currentName,"%d Cactus", countLevel); return currentName;
case ITEM_SEEDS: sprintf(currentName,"%d Seeds", countLevel); return currentName;
case ITEM_WHEAT: sprintf(currentName,"%d Wheat", countLevel); return currentName;
case ITEM_FLESH: sprintf(currentName,"%d Flesh", countLevel); return currentName;
case ITEM_BREAD: sprintf(currentName,"%d Bread", countLevel); return currentName;
case ITEM_APPLE: sprintf(currentName,"%d Apple", countLevel); return currentName;
case ITEM_GOLD_APPLE: sprintf(currentName,"%d Golden Apple", countLevel); return currentName;
case ITEM_STRENGTH_POTION: sprintf(currentName,"%d Strength Potion", countLevel); return currentName;
case ITEM_SPEED_POTION: sprintf(currentName,"%d Speed Potion", countLevel); return currentName;
case ITEM_REGEN_POTION: sprintf(currentName,"%d Regen Potion", countLevel); return currentName;
case ITEM_SWIM_BREATH_POTION: sprintf(currentName,"%d Swim Potion", countLevel); return currentName;
case ITEM_COAL: sprintf(currentName,"%d Coal", countLevel); return currentName;
case ITEM_IRONORE: sprintf(currentName,"%d Iron ore", countLevel); return currentName;
case ITEM_GOLDORE: sprintf(currentName,"%d Gold ore", countLevel); return currentName;
case ITEM_IRONINGOT: sprintf(currentName,"%d Iron ingot", countLevel); return currentName;
case ITEM_GOLDINGOT: sprintf(currentName,"%d Gold ingot", countLevel); return currentName;
case ITEM_GLASS: sprintf(currentName,"%d Glass", countLevel); return currentName;
case ITEM_GEM: sprintf(currentName,"%d Gem", countLevel); return currentName;
case ITEM_SLIME: sprintf(currentName,"%d Slime", countLevel); return currentName;
case ITEM_LOOM: return "Loom";
case ITEM_ENCHANTER: return "Enchanter";
@ -181,87 +187,109 @@ char* getItemName(int itemID, int countLevel){
case ITEM_BONE: sprintf(currentName,"%d Bone", countLevel); return currentName;
case ITEM_DUNGEON_KEY: sprintf(currentName,"%d Dungeon Key", countLevel); return currentName;
case ITEM_WIZARD_SUMMON: sprintf(currentName,"%d Wizard Summon", countLevel); return currentName;
case ITEM_DRAGON_EGG: sprintf(currentName,"%d Dragon Egg", countLevel); return currentName;
case ITEM_DRAGON_SCALE: sprintf(currentName,"%d Dragon Scale", countLevel); return currentName;
case ITEM_BOOKSHELVES: sprintf(currentName,"%d Bookshelves", countLevel); return currentName;
case ITEM_MAGIC_DUST: sprintf(currentName,"%d Magic Dust", countLevel); return currentName;
case ITEM_COIN: sprintf(currentName,"%d Coins", countLevel); return currentName;
case TOOL_BUCKET:
switch(countLevel){
case 1: return "Water Bucket";
case 2: return "Lava Bucket";
default: return "Empty Bucket";
}
case 1: return "Water Bucket";
case 2: return "Lava Bucket";
default: return "Empty Bucket";
}
case TOOL_BOW: return "Bow";
default: return ""; // null
}
case TOOL_MAGIC_COMPASS: return "Magic Compass";
case TOOL_CLAYMORE:
switch(countLevel){
case 1: return "Rock Claymore";
case 2: return "Iron Claymore";
case 3: return "Gold Claymore";
case 4: return "Gem Claymore";
default: return "Wood Claymore";
}
case ITEM_TORCH: return "Torch";
case ITEM_BED: return "Bed";
case ITEM_FISHING_ROD: return "Fishing Rod";
case ITEM_SHEARS: return "Shears";
case ITEM_FISH_RAW: sprintf(currentName,"%d Fish", countLevel); return currentName;
case ITEM_FISH_COOKED: sprintf(currentName,"%d Cooked Fish", countLevel); return currentName;
default: return ""; // null
}
}
char* getBasicItemName(int itemID, int countLevel){
switch(itemID){
case TOOL_SHOVEL:
switch(countLevel){
case 1: return "Rock Shovel";
case 2: return "Iron Shovel";
case 3: return "Gold Shovel";
case 4: return "Gem Shovel";
default: return "Wood Shovel";
}
case TOOL_HOE:
switch(countLevel){
case 1: return "Rock Hoe";
case 2: return "Iron Hoe";
case 3: return "Gold Hoe";
case 4: return "Gem Hoe";
default: return "Wood Hoe";
}
case TOOL_SWORD:
switch(countLevel){
case 1: return "Rock Sword";
case 2: return "Iron Sword";
case 3: return "Gold Sword";
case 4: return "Gem Sword";
default: return "Wood Sword";
}
case TOOL_PICKAXE:
switch(countLevel){
case 1: return "Rock Pickaxe";
case 2: return "Iron Pickaxe";
case 3: return "Gold Pickaxe";
case 4: return "Gem Pickaxe";
default: return "Wood Pickaxe";
}
case TOOL_AXE:
switch(countLevel){
case 1: return "Rock Axe";
case 2: return "Iron Axe";
case 3: return "Gold Axe";
case 4: return "Gem Axe";
default: return "Wood Axe";
}
case ITEM_ANVIL: return "Anvil";
case ITEM_CHEST: return "Chest";
case ITEM_OVEN: return "Oven";
case ITEM_FURNACE: return "Furnace";
case ITEM_WORKBENCH: return "Workbench";
case ITEM_LANTERN: return "Lantern";
case ITEM_POWGLOVE: return "Power Glove";
case ITEM_FLOWER: return "Flower";
case ITEM_WOOD: return "Wood";
case ITEM_STONE: return "Stone";
case ITEM_SAND: return "Sand";
case ITEM_DIRT: return "Dirt";
case ITEM_CLOUD: return "Cloud";
case ITEM_ACORN: return "Acorn";
case ITEM_CACTUS: return "Cactus";
case ITEM_SEEDS: return "Seeds";
case ITEM_WHEAT: return "Wheat";
case ITEM_FLESH: return "Flesh";
case ITEM_BREAD: return "Bread";
case ITEM_APPLE: return "Apple";
case ITEM_COAL: return "Coal";
case ITEM_IRONORE: return "Iron ore";
case ITEM_GOLDORE: return "Gold ore";
case ITEM_IRONINGOT: return "Iron ingot";
case ITEM_GOLDINGOT: return "Gold ingot";
case ITEM_GLASS: return "Glass";
case ITEM_GEM: return "Gem";
case ITEM_SLIME: return "Slime";
char* getBasicItemName(int itemID, int countLevel) {
switch(itemID){
case TOOL_SHOVEL:
switch(countLevel){
case 1: return "Rock Shovel";
case 2: return "Iron Shovel";
case 3: return "Gold Shovel";
case 4: return "Gem Shovel";
default: return "Wood Shovel";
}
case TOOL_HOE:
switch(countLevel){
case 1: return "Rock Hoe";
case 2: return "Iron Hoe";
case 3: return "Gold Hoe";
case 4: return "Gem Hoe";
default: return "Wood Hoe";
}
case TOOL_SWORD:
switch(countLevel){
case 1: return "Rock Sword";
case 2: return "Iron Sword";
case 3: return "Gold Sword";
case 4: return "Gem Sword";
default: return "Wood Sword";
}
case TOOL_PICKAXE:
switch(countLevel){
case 1: return "Rock Pickaxe";
case 2: return "Iron Pickaxe";
case 3: return "Gold Pickaxe";
case 4: return "Gem Pickaxe";
default: return "Wood Pickaxe";
}
case TOOL_AXE:
switch(countLevel){
case 1: return "Rock Axe";
case 2: return "Iron Axe";
case 3: return "Gold Axe";
case 4: return "Gem Axe";
default: return "Wood Axe";
}
case ITEM_ANVIL: return "Anvil";
case ITEM_CHEST: return "Chest";
case ITEM_OVEN: return "Oven";
case ITEM_FURNACE: return "Furnace";
case ITEM_WORKBENCH: return "Workbench";
case ITEM_POTION_MAKER: return "Potion Maker";
case ITEM_LANTERN: return "Lantern";
case ITEM_POWGLOVE: return "Power Glove";
case ITEM_FLOWER: return "Flower";
case ITEM_WOOD: return "Wood";
case ITEM_STONE: return "Stone";
case ITEM_SAND: return "Sand";
case ITEM_DIRT: return "Dirt";
case ITEM_CLOUD: return "Cloud";
case ITEM_ACORN: return "Acorn";
case ITEM_CACTUS: return "Cactus";
case ITEM_SEEDS: return "Seeds";
case ITEM_WHEAT: return "Wheat";
case ITEM_FLESH: return "Flesh";
case ITEM_BREAD: return "Bread";
case ITEM_APPLE: return "Apple";
case ITEM_GOLD_APPLE: return "Gold Apple";
case ITEM_COAL: return "Coal";
case ITEM_IRONORE: return "Iron ore";
case ITEM_GOLDORE: return "Gold ore";
case ITEM_IRONINGOT: return "Iron ingot";
case ITEM_GOLDINGOT: return "Gold ingot";
case ITEM_GLASS: return "Glass";
case ITEM_GEM: return "Gem";
case ITEM_SLIME: return "Slime";
case ITEM_LOOM: return "Loom";
case ITEM_ENCHANTER: return "Enchanter";
@ -285,14 +313,38 @@ char* getBasicItemName(int itemID, int countLevel){
case ITEM_BONE: return "Bone";
case ITEM_DUNGEON_KEY: return "Dungeon Key";
case ITEM_WIZARD_SUMMON: return "Wizard Summon";
case ITEM_DRAGON_EGG: return "Dragon Egg";
case ITEM_DRAGON_SCALE: return "Dragon Scale";
case ITEM_BOOKSHELVES: return "Bookshelves";
case ITEM_MAGIC_DUST: return "Magic Dust";
case ITEM_COIN: return "Coin";
case ITEM_STRENGTH_POTION: return "Strength Potion";
case ITEM_SPEED_POTION: return "Speed Potion";
case ITEM_REGEN_POTION: return "Regen Potion";
case ITEM_SWIM_BREATH_POTION: return "Water Potion";
case TOOL_BUCKET:
switch(countLevel){
case 1: return "Water Bucket";
case 2: return "Lava Bucket";
default: return "Empty Bucket";
}
case 1: return "Water Bucket";
case 2: return "Lava Bucket";
default: return "Empty Bucket";
}
case TOOL_BOW: return "Bow";
default: return ""; // null
}
case TOOL_MAGIC_COMPASS: return "Magic Compass";
case TOOL_CLAYMORE:
switch(countLevel){
case 1: return "Rock Claymore";
case 2: return "Iron Claymore";
case 3: return "Gold Claymore";
case 4: return "Gem Claymore";
default: return "Wood Claymore";
}
case ITEM_TORCH: return "Torch";
case ITEM_BED: return "Bed";
case ITEM_FISHING_ROD: return "Fishing Rod";
case ITEM_SHEARS: return "Shears";
case ITEM_FISH_RAW: return "Fish";
case ITEM_FISH_COOKED: return "Cooked Fish";
default: return ""; // null
}
}

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

@ -45,6 +45,10 @@
#define ITEM_LOOM 34
#define ITEM_ENCHANTER 35
#define ITEM_POTION_MAKER 36
#define TOOL_CLAYMORE 37
#define ITEM_TORCH 38
#define ITEM_BED 39
#define ITEM_WALL_WOOD 51
#define ITEM_WALL_STONE 52
@ -66,24 +70,41 @@
#define ITEM_BONE 68
#define ITEM_DUNGEON_KEY 69
#define ITEM_WIZARD_SUMMON 70
#define ITEM_DRAGON_EGG 71
#define ITEM_DRAGON_SCALE 72
#define ITEM_BOOKSHELVES 73
#define ITEM_MAGIC_DUST 74
#define ITEM_COIN 75
#define ITEM_GOLD_APPLE 76
#define ITEM_STRENGTH_POTION 77
#define ITEM_SPEED_POTION 78
#define ITEM_REGEN_POTION 79
#define ITEM_SWIM_BREATH_POTION 80
#define ITEM_FISH_RAW 81
#define ITEM_FISH_COOKED 82
#define TOOL_BUCKET 101
#define TOOL_BOW 102
#define TOOL_MAGIC_COMPASS 103
#define ITEM_FISHING_ROD 104
#define ITEM_SHEARS 105
#define ARMOR_TEST 120
typedef struct Inventory Inventory;
typedef struct {
s16 id;
s16 countLevel; // Count for items, Level for tools.
s16 slotNum; // Read-only. Do not mess with this.
bool onlyOne;
int* invPtr; // pointer to current inventory.
Inventory * chestPtr; // pointer to chest inventory for chest item.
s16 id;
s16 countLevel; // Count for items, Level for tools.
s16 slotNum; // Read-only. Do not mess with this.
bool onlyOne;
int* invPtr; // pointer to current inventory.
Inventory * chestPtr; // pointer to chest inventory for chest item.
} Item;
struct Inventory {
Item items[300]; // Maximum of 300 slots in every inventory.
s16 lastSlot; // lastSlot can also be used to read the size of the inventory.
Item items[300]; // Maximum of 300 slots in every inventory.
s16 lastSlot; // lastSlot can also be used to read the size of the inventory.
};
bool isItemEmpty(Item* item);

743
source/MapGen.c Normal file → Executable file
View file

@ -3,8 +3,11 @@
int w = 0;
int h = 0;
u8 randomTile[] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 2};
int randomTileSize = 10;
float nextFloat(){
return (float)rand()/RAND_MAX;
return (float)rand()/RAND_MAX;
}
double sample(double * values, int x, int y) {
@ -12,65 +15,66 @@ double sample(double * values, int x, int y) {
}
double * Noise(int width, int height, int featureSize) {
w = width;
h = height;
double * values = malloc(sizeof(double) * w * h);
int x, y;
for(x = 0; x < w; x+=featureSize){
for(y = 0; y < w; y+=featureSize){
values[(x & (w - 1)) + (y & (h - 1)) * w] = nextFloat() * 2 - 1;
w = width;
h = height;
double * values = malloc(sizeof(double) * w * h);
int x, y;
for(x = 0; x < w; x+=featureSize){
for(y = 0; y < w; y+=featureSize){
values[(x & (w - 1)) + (y & (h - 1)) * w] = nextFloat() * 2 - 1;
}
}
int stepSize = featureSize;
double scale = 1.0 / w;
double scaleMod = 1;
do {
int halfStep = stepSize / 2;
for(x = 0; x < w; x+=stepSize){
for(y = 0; y < w; y+=stepSize){
double a = sample(values,x, y);
double b = sample(values,x + stepSize, y);
double c = sample(values,x, y + stepSize);
double d = sample(values,x + stepSize, y + stepSize);
double e = (a + b + c + d) / 4.0 + (nextFloat() * 2 - 1) * stepSize * scale;
values[((x + halfStep) & (w - 1)) + ((y + halfStep) & (h - 1)) * w] = e;
}
}
for(x = 0; x < w; x+=stepSize){
for(y = 0; y < w; y+=stepSize){
double a = sample(values,x, y);
double b = sample(values,x + stepSize, y);
double c = sample(values,x, y + stepSize);
double d = sample(values,x + halfStep, y + halfStep);
double e = sample(values,x + halfStep, y - halfStep);
double f = sample(values,x - halfStep, y + halfStep);
double H = (a + b + d + e) / 4.0 + (nextFloat() * 2 - 1) * stepSize * scale * 0.5;
double g = (a + c + d + f) / 4.0 + (nextFloat() * 2 - 1) * stepSize * scale * 0.5;
values[((x + halfStep) & (w - 1)) + (y & (h - 1)) * w] = H;
values[(x & (w - 1)) + ((y + halfStep) & (h - 1)) * w] = g;
}
}
int stepSize = featureSize;
double scale = 1.0 / w;
double scaleMod = 1;
do {
int halfStep = stepSize / 2;
for(x = 0; x < w; x+=stepSize){
for(y = 0; y < w; y+=stepSize){
double a = sample(values,x, y);
double b = sample(values,x + stepSize, y);
double c = sample(values,x, y + stepSize);
double d = sample(values,x + stepSize, y + stepSize);
double e = (a + b + c + d) / 4.0 + (nextFloat() * 2 - 1) * stepSize * scale;
values[((x + halfStep) & (w - 1)) + ((y + halfStep) & (h - 1)) * w] = e;
}
}
for(x = 0; x < w; x+=stepSize){
for(y = 0; y < w; y+=stepSize){
double a = sample(values,x, y);
double b = sample(values,x + stepSize, y);
double c = sample(values,x, y + stepSize);
double d = sample(values,x + halfStep, y + halfStep);
double e = sample(values,x + halfStep, y - halfStep);
double f = sample(values,x - halfStep, y + halfStep);
double H = (a + b + d + e) / 4.0 + (nextFloat() * 2 - 1) * stepSize * scale * 0.5;
double g = (a + c + d + f) / 4.0 + (nextFloat() * 2 - 1) * stepSize * scale * 0.5;
values[((x + halfStep) & (w - 1)) + (y & (h - 1)) * w] = H;
values[(x & (w - 1)) + ((y + halfStep) & (h - 1)) * w] = g;
}
}
stepSize /= 2;
scale *= (scaleMod + 0.8);
scaleMod *= 0.3;
} while (stepSize > 1);
return values;
}
void newSeed(){
srand(time(NULL));
stepSize /= 2;
scale *= (scaleMod + 0.8);
scaleMod *= 0.3;
} while (stepSize > 1);
return values;
}
void createAndValidateTopMap(int w, int h, u8 * map, u8 * data) {
//TODO: Will need to reset entity manager if generation is retried
void createAndValidateTopMap(int w, int h, int level, u8 * map, u8 * data) {
do {
createTopMap(w, h, map, data);
//reset Entities
(&eManager)->lastSlot[level] = 0;
(&eManager)->entities[level][0] = nullEntity;
createTopMap(w, h, level, map, data);
int count[256]={[0 ... 255] = 0};
int i;
for (i = 0; i < w * h; ++i) count[map[i] & 0xff]++;
int i;
for (i = 0; i < w * h; ++i) count[map[i] & 0xff]++;
if (count[TILE_ROCK & 0xff] < 100) continue;
if (count[TILE_SAND & 0xff] < 100) continue;
if (count[TILE_GRASS & 0xff] < 100) continue;
@ -81,33 +85,41 @@ void createAndValidateTopMap(int w, int h, u8 * map, u8 * data) {
} while (true);
}
void createAndValidateUndergroundMap(int w, int h,int depthLevel, u8 * map, u8 * data) {
void createAndValidateUndergroundMap(int w, int h, int depthLevel, int level, u8 * map, u8 * data) {
do {
createUndergroundMap(w, h, depthLevel, map, data);
//reset Entities
(&eManager)->lastSlot[level] = 0;
(&eManager)->entities[level][0] = nullEntity;
createUndergroundMap(w, h, depthLevel, level, map, data);
int count[256]={[0 ... 255] = 0};
int i = 0;
for (i = 0; i < w * h; ++i)count[map[i] & 0xff]++;
int i = 0;
for (i = 0; i < w * h; ++i)count[map[i] & 0xff]++;
if (count[TILE_ROCK & 0xff] < 100) continue;
if (count[TILE_DIRT & 0xff] < 100) continue;
switch(depthLevel){
case 1: if (count[TILE_IRONORE & 0xff] < 20) continue; break;
case 2: if (count[TILE_GOLDORE & 0xff] < 20) continue; break;
case 3: if (count[TILE_GEMORE & 0xff] < 20) continue; break;
}
case 1: if (count[TILE_IRONORE & 0xff] < 20) continue; break;
case 2: if (count[TILE_GOLDORE & 0xff] < 20 || count[TILE_MYCELIUM & 0xff] < 40) continue; break;
case 3: if (count[TILE_GEMORE & 0xff] < 20) continue; break;
}
if (depthLevel < 3) if (count[TILE_STAIRS_DOWN & 0xff] < 2) continue;
return;
} while (true);
}
void createAndValidateDungeonMap(int w, int h, u8 * map, u8 * data) {
void createAndValidateDungeonMap(int w, int h, int level, u8 * map, u8 * data) {
do {
createDungeonMap(w, h, map, data);
//reset Entities
(&eManager)->lastSlot[level] = 0;
(&eManager)->entities[level][0] = nullEntity;
createDungeonMap(w, h, level, map, data);
int count[256]={[0 ... 255] = 0};
int i = 0;
for (i = 0; i < w * h; ++i)count[map[i] & 0xff]++;
int i = 0;
for (i = 0; i < w * h; ++i)count[map[i] & 0xff]++;
if (count[TILE_DUNGEON_WALL & 0xff] < 100) continue;
if (count[TILE_DUNGEON_FLOOR & 0xff] < 100) continue;
@ -115,13 +127,17 @@ void createAndValidateDungeonMap(int w, int h, u8 * map, u8 * data) {
} while (true);
}
void createAndValidateSkyMap(int w, int h, u8 * map, u8 * data) {
void createAndValidateSkyMap(int w, int h, int level, u8 * map, u8 * data) {
do {
createSkyMap(w, h, map, data);
//reset Entities
(&eManager)->lastSlot[level] = 0;
(&eManager)->entities[level][0] = nullEntity;
createSkyMap(w, h, level, map, data);
int count[256]={[0 ... 255] = 0};
int i = 0;
for (i = 0; i < w * h; ++i)count[map[i] & 0xff]++;
int i = 0;
for (i = 0; i < w * h; ++i)count[map[i] & 0xff]++;
if (count[TILE_CLOUD & 0xff] < 1600) continue;
if (count[TILE_STAIRS_DOWN & 0xff] < 2) continue;
@ -131,7 +147,7 @@ void createAndValidateSkyMap(int w, int h, u8 * map, u8 * data) {
void createTopMap(int w, int h, u8 * map, u8 * data) {
void createTopMap(int w, int h, int level, u8 * map, u8 * data) {
double* mnoise1 = Noise(w, h, 16);
double* mnoise2 = Noise(w, h, 16);
double* mnoise3 = Noise(w, h, 16);
@ -141,8 +157,8 @@ void createTopMap(int w, int h, u8 * map, u8 * data) {
int x,y,i,j,k,xx,yy;
for(x = 0; x < w; ++x){
for(y = 0; y < w; ++y){
int i = x + y * w;
for(y = 0; y < w; ++y){
int i = x + y * w;
double val = fabs(noise1[i] - noise2[i]) * 3 - 2;
double mval = fabs(mnoise1[i] - mnoise2[i]);
@ -164,10 +180,10 @@ void createTopMap(int w, int h, u8 * map, u8 * data) {
} else {
map[i] = TILE_GRASS;
}
}
}
}
}
for (i = 0; i < w * h / 2800; ++i) {
for (i = 0; i < w * h / 2800; ++i) {
int xs = rand()%w;
int ys = rand()%h;
for (k = 0; k < 10; ++k) {
@ -176,16 +192,18 @@ void createTopMap(int w, int h, u8 * map, u8 * data) {
for (j = 0; j < 100; ++j) {
int xo = x + (rand()%5) - (rand()%5);
int yo = y + (rand()%5) - (rand()%5);
for (yy = yo - 1;yy <= yo + 1; ++yy){
for (yy = yo - 1;yy <= yo + 1; ++yy){
for(xx = xo - 1; xx <= xo + 1; ++xx){
if (xx >= 0 && yy >= 0 && xx < w && yy < h) if (map[xx + yy * w] == TILE_GRASS) map[xx + yy * w] = TILE_SAND;
}
}
}
}
}
}
}
}
for (i = 0; i < w * h / 400; ++i) {
createVillage(w, h, level, map, data);
for (i = 0; i < w * h / 400; ++i) {
x = rand()%w;
y = rand()%h;
for (j = 0; j < 200; ++j) {
@ -199,7 +217,7 @@ void createTopMap(int w, int h, u8 * map, u8 * data) {
}
}
for (i = 0; i < w * h / 800; ++i) {
for (i = 0; i < w * h / 800; ++i) {
x = rand()%w;
y = rand()%h;
for (j = 0; j < 30;++j) {
@ -225,13 +243,13 @@ void createTopMap(int w, int h, u8 * map, u8 * data) {
}
}
int sCount, attempts = 0;
int sCount, attempts = 0;
for (sCount = 0; sCount < 4;) {
xx = rand()%w;
yy = rand()%h;
if (xx >= 0 && yy >= 0 && xx < w && yy < h) {
if (map[xx + yy * w] == TILE_ROCK)
{
{
map[xx + yy * w] = TILE_STAIRS_DOWN;
map[xx + (yy+1) * w] = TILE_ROCK;
map[(xx+1) + yy * w] = TILE_ROCK;
@ -241,20 +259,20 @@ void createTopMap(int w, int h, u8 * map, u8 * data) {
map[(xx+1) + (yy+1) * w] = TILE_ROCK;
map[(xx+1) + (yy-1) * w] = TILE_ROCK;
map[(xx-1) + (yy+1) * w] = TILE_ROCK;
++sCount;
++sCount;
}
}
if(attempts < (w*h/100)) ++attempts; else break;
}
free(mnoise1);
free(mnoise2);
free(mnoise3);
free(noise1);
free(noise2);
return;
free(mnoise1);
free(mnoise2);
free(mnoise3);
free(noise1);
free(noise2);
return;
}
void createUndergroundMap(int w, int h,int depthLevel, u8 * map, u8 * data) {
void createUndergroundMap(int w, int h, int depthLevel, int level, u8 * map, u8 * data) {
double* mnoise1 = Noise(w, h, 16);
double* mnoise2 = Noise(w, h, 16);
double* mnoise3 = Noise(w, h, 16);
@ -270,10 +288,11 @@ void createUndergroundMap(int w, int h,int depthLevel, u8 * map, u8 * data) {
double* noise1 = Noise(w, h, 32);
double* noise2 = Noise(w, h, 32);
int x, y;
int x,y,i,j,k,xx,yy;
for(x = 0; x < w; ++x){
for(y = 0; y < w; ++y){
int i = x + y * w;
for(y = 0; y < w; ++y){
int i = x + y * w;
double val = fabs(noise1[i] - noise2[i]) * 3 - 2;
@ -305,10 +324,43 @@ void createUndergroundMap(int w, int h,int depthLevel, u8 * map, u8 * data) {
} else {
map[i] = TILE_ROCK;
}
}
}
int i,j;
for (i = 0; i < w * h / 400; ++i) {
}
}
//generate dwarf house
if(depthLevel==3) {
createDwarfHouse(w, h, level, map, data);
//generate mushroom patches
} else if(depthLevel==2) {
for (i = 0; i < w * h / 5400; ++i) {
int xs = rand()%w;
int ys = rand()%h;
for (k = 0; k < 10; ++k) {
x = xs + (rand()%13) - 6;
y = ys + (rand()%13) - 6;
for (j = 0; j < 100; ++j) {
int xo = x + (rand()%5) - (rand()%5);
int yo = y + (rand()%5) - (rand()%5);
for (yy = yo - 1;yy <= yo + 1; ++yy){
for(xx = xo - 1; xx <= xo + 1; ++xx){
if (xx >= 0 && yy >= 0 && xx < w && yy < h) {
if (map[xx + yy * w] == TILE_DIRT) {
map[xx + yy * w] = TILE_MYCELIUM;
if(rand()%20==0) {
map[xx + yy * w] = TILE_MUSHROOM_BROWN + rand()%2; //BROWN or RED (=BROWN+1)
data[xx + yy * w] = rand()%2;
}
}
}
}
}
}
}
}
}
//generate ores
for (i = 0; i < w * h / 400; ++i) {
int x = rand()%w;
int y = rand()%h;
for(j = 0; j < 30; ++j) {
@ -321,13 +373,15 @@ void createUndergroundMap(int w, int h,int depthLevel, u8 * map, u8 * data) {
}
}
}
if (depthLevel < 3){
int sCount, attempts = 0;
//generate stairs down
if (depthLevel < 3){
int sCount, attempts = 0;
for (sCount = 0; sCount < 4;) {
int xx = rand()%w;
int yy = rand()%h;
if (xx >= 0 && yy >= 0 && xx < w && yy < h) {
if (map[xx + yy * w] == TILE_ROCK){
if (map[xx + yy * w] == TILE_ROCK){
map[xx + yy * w] = TILE_STAIRS_DOWN;
map[xx + (yy+1) * w] = TILE_ROCK;
map[(xx+1) + yy * w] = TILE_ROCK;
@ -338,124 +392,44 @@ void createUndergroundMap(int w, int h,int depthLevel, u8 * map, u8 * data) {
map[(xx+1) + (yy-1) * w] = TILE_ROCK;
map[(xx-1) + (yy+1) * w] = TILE_ROCK;
++sCount;
}
}
}
if(attempts < (w*h/100)) ++attempts; else break;
}
}
free(mnoise1);
free(mnoise2);
free(mnoise3);
free(nnoise1);
free(nnoise2);
free(nnoise3);
free(wnoise1);
free(wnoise2);
free(wnoise3);
free(noise1);
free(noise2);
}
//generate dungeon entrance
if(depthLevel==3) {
map[w/2+0 + (h/2+0) * w] = TILE_DUNGEON_ENTRANCE;
map[w/2-1 + (h/2+0) * w] = TILE_DIRT;
map[w/2+1 + (h/2+0) * w] = TILE_DIRT;
map[w/2+0 + (h/2-1) * w] = TILE_DIRT;
map[w/2+1 + (h/2+1) * w] = TILE_DIRT;
map[w/2-1 + (h/2-1) * w] = TILE_DUNGEON_WALL;
map[w/2-1 + (h/2+1) * w] = TILE_DUNGEON_WALL;
map[w/2+1 + (h/2-1) * w] = TILE_DUNGEON_WALL;
map[w/2+1 + (h/2+1) * w] = TILE_DUNGEON_WALL;
}
free(mnoise1);
free(mnoise2);
free(mnoise3);
free(nnoise1);
free(nnoise2);
free(nnoise3);
free(wnoise1);
free(wnoise2);
free(wnoise3);
free(noise1);
free(noise2);
}
void createDungeonRoom(int w, int h, u8 * map, u8 * data) {
int tries;
void createDungeonMap(int w, int h, int level, u8 * map, u8 * data) {
hasNPC = false;
for(tries=0; tries<100; ++tries) {
int x = 5+(rand()%(w-10));
int y = 5+(rand()%(h-10));
int xr;
int yr;
int wr = 10+(rand()%11);
int hr = 10+(rand()&11);
int xp;
int yp;
int i;
if(x+wr > w-5) wr = (w-5) - x;
if(y+hr > h-5) hr = (h-5) - y;
//check instersection
bool allowed = true;
for(xr = x; xr < x+wr; ++xr) {
for(yr = y; yr < y+hr; ++yr) {
i = xr + yr * w;
//255 for paths so rooms can overlap paths
if(map[i]!=TILE_DUNGEON_WALL && map[i]!=255) {
allowed = false;
break;
}
}
if(!allowed) break;
}
if(!allowed) continue;
//create room
for(xr = x; xr < x+wr; ++xr) {
for(yr = y; yr < y+hr; ++yr) {
i = xr + yr * w;
map[i] = TILE_DUNGEON_FLOOR;
}
}
//Create path back to existing stuff
xp = x;
yp = y;
i = xp + yp * w;
bool checkForFloor = false;
bool xFirst = (rand()%2)==0;
while((checkForFloor && (map[i]!=TILE_DUNGEON_FLOOR && map[i]!=255)) || (!checkForFloor && (map[i]==TILE_DUNGEON_FLOOR || map[i]==255))) {
if(checkForFloor) {
//TODO check for dungeon entrance: if(map[i]==TILE_DUNGEON_ENTRANCE) break;
//make connection
map[i] = 255;
}
//move
if(xFirst) {
if(xp > w/2) --xp;
else if(xp < w/2) ++xp;
else if(yp > h/2) --yp;
else if(yp < h/2) ++yp;
else break;
} else {
if(yp > h/2) --yp;
else if(yp < h/2) ++yp;
else if(xp > w/2) --xp;
else if(xp < w/2) ++xp;
else break;
}
i = xp + yp * w;
//search for end of current room
if(!checkForFloor && (map[i]!=TILE_DUNGEON_FLOOR && map[i]!=255)) checkForFloor = true;
}
//dekorate room
bool lava = (rand()%4)==0;
bool pillars = (rand()%4)==0;
for(xr = x; xr < x+wr; ++xr) {
for(yr = y; yr < y+hr; ++yr) {
i = xr + yr * w;
if(lava && xr > x+1 && xr < x+wr-2 && yr > y+1 && yr < y+hr-2) {
map[i] = TILE_LAVA;
} else if(pillars && xr > x && xr < x+wr-1 && yr > y && yr < y+hr-1 && xr%2 == 0 && yr%2 == 0) {
map[i] = TILE_DUNGEON_WALL;
} else {
if(rand()%50==0) map[i] = TILE_IRONORE + (rand()%3);
}
}
}
break;
}
}
void createDungeonMap(int w, int h, u8 * map, u8 * data) {
int i, x, y;
for(x = 0; x < w; ++x){
for(y = 0; y < w; ++y){
@ -464,15 +438,19 @@ void createDungeonMap(int w, int h, u8 * map, u8 * data) {
//Startroom
if (x >= (w/2-5) && x <= (w/2+5) && y >= (h/2-5) && y <= (h/2+5) ) {
map[i] = TILE_DUNGEON_FLOOR;
data[i] = randomTile[rand()%randomTileSize];
} else {
map[i] = TILE_DUNGEON_WALL;
data[i] = 0;
}
data[i] = 0;
}
}
//create dragon chamber(only call once and before other rooms)
createDungeonRoom(w, h, true, level, map, data);
for(i = 0; i < 40; ++i) {
createDungeonRoom(w, h, map, data);
createDungeonRoom(w, h, false, level, map, data);
}
//replace paths with actual dungeon floor
@ -482,6 +460,7 @@ void createDungeonMap(int w, int h, u8 * map, u8 * data) {
if (map[i]==255) {
map[i] = TILE_DUNGEON_FLOOR;
data[i] = randomTile[rand()%randomTileSize];
}
}
}
@ -500,7 +479,7 @@ void createDungeonMap(int w, int h, u8 * map, u8 * data) {
map[w/2-1 + (h/2-1) * w] = TILE_DUNGEON_WALL;
}
void createSkyMap(int w, int h, u8 * map, u8 * data) {
void createSkyMap(int w, int h, int level, u8 * map, u8 * data) {
double* noise1 = Noise(w, h, 8);
double* noise2 = Noise(w, h, 8);
int x, y;
@ -560,3 +539,339 @@ void createSkyMap(int w, int h, u8 * map, u8 * data) {
free(noise2);
}
//"Subgenerators"
void findFeatureLocation(int fw, int fh, int * accepted, int aLength, int maxTries, int w, int h, u8 * map, u8 * data) {
int leastNonFitting = fw * fh;
int tries;
//find the location with the least non fitting tiles out of some randomly tried ones
for(tries=0; tries<maxTries; ++tries) {
int x = rand()%(w-fw);
int y = rand()%(h-fh);
int nonFitting = 0;
int xp;
int yp;
int i;
int a;
bool fits;
for(xp=x; xp<x+fw; ++xp) {
for(yp=y; yp<y+fh; ++yp) {
i = xp + yp * w;
fits = false;
for(a=0; a<aLength; ++a) {
if(map[i]==accepted[a]) {
fits = true;
break;
}
}
if(!fits) {
nonFitting++;
}
}
}
if(nonFitting<leastNonFitting) {
featureX = x;
featureY = y;
leastNonFitting = nonFitting;
}
}
}
void createVillageHouse(int hid, int x, int y, int hw, int hh, int ex, int ey, int w, int h, int level, u8 * map, u8 * data) {
//create wall and floor
int xp = x;
int yp = y;
for(xp=x; xp<x+hw; ++xp) {
for(yp=y; yp<y+hh; ++yp) {
if(xp==x || xp==x+hw-1 || yp==y || yp==y+hh-1) map[xp + yp * w] = TILE_WOOD_WALL;
else map[xp + yp * w] = TILE_WOOD_FLOOR;
}
}
//recreate entrance
map[ex + ey * w] = TILE_WOOD_FLOOR;
//create npcs
if(hid==0) {
addEntityToList(newNPCEntity(NPC_GIRL, (x+hw/2) << 4, (y+hh/2) << 4, level), &eManager);
} else if(hid==1) {
addEntityToList(newNPCEntity(NPC_PRIEST, (x+hw/2) << 4, (y+hh/2) << 4, level), &eManager);
} else if(hid==2) {
addEntityToList(newNPCEntity(NPC_FARMER, (x+hw/2) << 4, (y+hh/2) << 4, level), &eManager);
//TODO: maybe create farm?
for(xp=x; xp<x+hw; ++xp) {
map[xp + yp * w] = TILE_WHEAT;
map[xp + (yp+1) * w] = TILE_WHEAT;
}
}
//add Latern Entity
int loffx = -12;
int loffy = -12;
if(hid==2) loffx = 12;
addEntityToList(newFurnitureEntity(ITEM_LANTERN, NULL, ((x+hw/2) << 4) + loffx, ((y+hh/2) << 4) + loffy, level), &eManager);
}
void createVillage(int w, int h, int level, u8 * map, u8 * data) {
int vw = 17;
int vh = 17;
int accepted[] = {TILE_GRASS};
findFeatureLocation(vw, vh, accepted, 1, 500, w, h, map, data);
int x = featureX;
int y = featureY;
int cx = x + vw/2;
int cy = y + vh/2;
int px;
int py;
int hw;
int hh;
int hx;
int hy;
int ex;
int ey;
//"well" in the middle
map[(cx-1) + (cy-1) * w] = TILE_SAND;
map[(cx+0) + (cy-1) * w] = TILE_SAND;
map[(cx+1) + (cy-1) * w] = TILE_SAND;
map[(cx-1) + (cy+0) * w] = TILE_SAND;
map[(cx+0) + (cy+0) * w] = TILE_WATER;
map[(cx+1) + (cy+0) * w] = TILE_SAND;
map[(cx-1) + (cy+1) * w] = TILE_SAND;
map[(cx+0) + (cy+1) * w] = TILE_SAND;
map[(cx+1) + (cy+1) * w] = TILE_SAND;
//"paths" outwards leading to the "houses"
//left
px = cx-1;
py = cy-1 + rand()%3;
while(px>x) {
map[px + py * w] = TILE_SAND;
--px;
}
hw = 4 + rand()%2;
hh = 4 + rand()%2;
hx = px + 1;
hy = py - hh + 2 + rand()%(hh-2);
ex = px + hw;
ey = py;
createVillageHouse(0, hx, hy, hw, hh, ex, ey, w, h, level, map, data);
//top
px = cx-1 + rand()%3;
py = cy-1;
while(py>y) {
map[px + py * w] = TILE_SAND;
--py;
}
hw = 5 + rand()%2;
hh = 4 + rand()%2;
hx = px - hw + 2 + rand()%(hw-2);
hy = py + 1;
ex = px;
ey = py+hh;
createVillageHouse(1, hx, hy, hw, hh, ex, ey, w, h, level, map, data);
//right
px = cx+1;
py = cy-1 + rand()%3;
while(px<x+vw) {
map[px + py * w] = TILE_SAND;
++px;
}
hw = 4 + rand()%2;
hh = 4 + rand()%2;
hx = px - hw;
hy = py - hh + 2 + rand()%(hh-2);
ex = px - hw;
ey = py;
createVillageHouse(2, hx, hy, hw, hh, ex, ey, w, h, level, map, data);
}
void createDwarfHouse(int w, int h, int level, u8 * map, u8 * data) {
int vw = 7;
int vh = 7;
int accepted[] = {TILE_ROCK};
findFeatureLocation(vw, vh, accepted, 1, 500, w, h, map, data);
int x = featureX;
int y = featureY;
int xp;
int yp;
for(xp=x; xp<x+vw; ++xp) {
for(yp=y; yp<y+vh; ++yp) {
if(xp==x || xp==x+vw-1 || yp==y || yp==y+vh-1) map[xp + yp * w] = TILE_STONE_WALL;
else map[xp + yp * w] = TILE_DIRT;
}
}
//entrance
xp = x+vw/2;
map[xp + (yp-1) * w] = TILE_DIRT;
//pillars
map[(x+2) + (y+2) * w] = TILE_STONE_WALL;
map[(x+vw-1-2) + (y+2) * w] = TILE_STONE_WALL;
addEntityToList(newNPCEntity(NPC_DWARF, ((x+vw/2) << 4) + 8, (y+vh/2) << 4, level), &eManager);
addEntityToList(newFurnitureEntity(ITEM_LANTERN, NULL, (x+1+1) << 4, (y+1+3) << 4, level), &eManager);
addEntityToList(newFurnitureEntity(ITEM_LANTERN, NULL, (x+vw-1-1) << 4, (y+1+3) << 4, level), &eManager);
}
void createDungeonRoom(int w, int h, bool dragon, int level, u8 * map, u8 * data) {
int tries;
for(tries=0; tries<100; ++tries) {
int x = 5+(rand()%(w-10 -10));
int y = 5+(rand()%(h-10 -10));
int xr;
int yr;
int wr = 10+(rand()%11);
int hr = 10+(rand()&11);
int xp;
int yp;
int i;
//create Dragonroom
if(dragon) {
wr = 20;
hr = 20;
x = 5 + (rand()%2)*(w-5*2-wr);
y = 5 + (rand()%2)*(h-5*2-hr);
}
if(x+wr > w-5) wr = (w-5) - x;
if(y+hr > h-5) hr = (h-5) - y;
//check instersection
bool allowed = true;
for(xr = x-1; xr < x+wr+1; ++xr) {
for(yr = y-1; yr < y+hr+1; ++yr) {
i = xr + yr * w;
//255 for paths so rooms can overlap paths
if(map[i]!=TILE_DUNGEON_WALL && map[i]!=255) {
allowed = false;
break;
}
}
if(!allowed) break;
}
if(!allowed) continue;
//create room
for(xr = x; xr < x+wr; ++xr) {
for(yr = y; yr < y+hr; ++yr) {
i = xr + yr * w;
map[i] = TILE_DUNGEON_FLOOR;
data[i] = randomTile[rand()%randomTileSize];
}
}
//Create path back to existing stuff
xp = x + wr/2;
yp = y + hr/2;
i = xp + yp * w;
bool checkForFloor = false;
bool xFirst = (rand()%2)==0;
while((checkForFloor && (map[i]!=TILE_DUNGEON_FLOOR && map[i]!=255)) || (!checkForFloor && (map[i]==TILE_DUNGEON_FLOOR || map[i]==255))) {
if(checkForFloor) {
//make connection
map[i] = 255;
}
//move
if(xFirst) {
if(xp > w/2) --xp;
else if(xp < w/2) ++xp;
else if(yp > h/2) --yp;
else if(yp < h/2) ++yp;
else break;
} else {
if(yp > h/2) --yp;
else if(yp < h/2) ++yp;
else if(xp > w/2) --xp;
else if(xp < w/2) ++xp;
else break;
}
i = xp + yp * w;
//search for end of current room
if(!checkForFloor && (map[i]!=TILE_DUNGEON_FLOOR && map[i]!=255)) checkForFloor = true;
}
//dekorate dragon room
if(dragon) {
for(xr = x; xr < x+wr; ++xr) {
for(yr = y; yr < y+hr; ++yr) {
i = xr + yr * w;
if((xr==x+1 || xr==x+wr-2 || yr==y+1 || yr==y+hr-2) && (xr!=x && xr!=x+wr-1 && yr!=y && yr!=y+hr-1)) {
map[i] = TILE_MAGIC_BARRIER;
}
}
}
//add Dragon Entity
addEntityToList(newDragonEntity((x+wr/2) << 4, (y+hr/2) << 4, level), &eManager);
break;
}
//dekorate room
bool lava = (rand()%4)==0;
bool pillars = (rand()%4)==0;
bool books = (rand()%4)==0;
for(xr = x; xr < x+wr; ++xr) {
for(yr = y; yr < y+hr; ++yr) {
i = xr + yr * w;
if(lava && xr > x+1 && xr < x+wr-2 && yr > y+1 && yr < y+hr-2) {
map[i] = TILE_LAVA;
data[i] = 0;
} else if(pillars && xr > x && xr < x+wr-1 && yr > y && yr < y+hr-1 && xr%2 == 0 && yr%2 == 0) {
map[i] = TILE_DUNGEON_WALL;
data[i] = 0;
} else if(books && (xr>x && xr<x+wr-1 && yr>y && yr<y+hr-1 && yr%2==0)) {
map[i] = TILE_BOOKSHELVES;
data[i] = rand()%3;
if(!hasNPC && rand()%50) {
hasNPC = true;
addEntityToList(newNPCEntity(NPC_LIBRARIAN, (xr << 4) + 8, ((yr+1) << 4) + 8, level), &eManager);
}
} else {
//add magic pillars for dragon barrier
if(xr==x+wr/2 && yr==y+hr/2) {
int pcount = 0;
int i = 0;
for (i = 0; i < eManager.lastSlot[5]; ++i) {
Entity * e = &eManager.entities[5][i];
if(e->type == ENTITY_MAGIC_PILLAR) {
++pcount;
}
}
if(pcount<8) {
addEntityToList(newMagicPillarEntity((xr << 4) + 8, (yr << 4) + 8, level), &eManager);
}
continue;
}
if(rand()%50==0) map[i] = TILE_IRONORE + (rand()%3);
}
}
}
break;
}
}

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

@ -2,7 +2,6 @@
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <3ds.h>
#include "Globals.h"
@ -10,12 +9,21 @@
float nextFloat();
double sample(double * values, int x, int y);
double * Noise(int w, int h, int featureSize);
void newSeed();
void createAndValidateTopMap(int w, int h, u8 * map, u8 * data);
void createTopMap(int w, int h, u8 * map, u8 * data);
void createAndValidateUndergroundMap(int w, int h,int depthLevel, u8 * map, u8 * data);
void createUndergroundMap(int w, int h,int depthLevel, u8 * map, u8 * data);
void createAndValidateDungeonMap(int w, int h, u8 * map, u8 * data);
void createDungeonMap(int w, int h, u8 * map, u8 * data);
void createAndValidateSkyMap(int w, int h, u8 * map, u8 * data);
void createSkyMap(int w, int h, u8 * map, u8 * data);
void createAndValidateTopMap(int w, int h, int level, u8 * map, u8 * data);
void createTopMap(int w, int h, int level, u8 * map, u8 * data);
void createAndValidateUndergroundMap(int w, int h, int depthLevel, int level, u8 * map, u8 * data);
void createUndergroundMap(int w, int h, int depthLevel, int level, u8 * map, u8 * data);
void createAndValidateDungeonMap(int w, int h, int level, u8 * map, u8 * data);
void createDungeonMap(int w, int h, int level, u8 * map, u8 * data);
void createAndValidateSkyMap(int w, int h, int level, u8 * map, u8 * data);
void createSkyMap(int w, int h, int level, u8 * map, u8 * data);
int featureX;
int featureY;
void findFeatureLocation(int fw, int fh, int * accepted, int aLength, int maxTries, int w, int h, u8 * map, u8 * data);
void createVillage(int w, int h, int level, u8 * map, u8 * data);
void createDwarfHouse(int w, int h, int level, u8 * map, u8 * data);
bool hasNPC;
void createDungeonRoom(int w, int h, bool dragon, int level, u8 * map, u8 * data);

2425
source/Menu.c Normal file → Executable file

File diff suppressed because it is too large Load diff

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

@ -2,8 +2,12 @@
#include "MenuTutorial.h"
#include "texturepack.h"
#include "Quests.h"
#include "Network.h"
void initMenus();
void renderMenu(int menu,int xscr,int yscr);
void tickMenu(int menu);
void menuRenderTilePit(int x,int y,int xt,int yt,u32 color);
void menuRenderTilePit(int x,int y,int xt,int yt);

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

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

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

620
source/Network.c Executable file
View file

@ -0,0 +1,620 @@
#include "Network.h"
#include "PacketHandler.h"
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
bool udsRunning;
size_t scannedNetworksCount;
udsNetworkScanInfo *scannedNetworks;
bool isConnected;
bool isServer;
size_t networkBufferSize;
void *networkBuffer;
udsNetworkStruct networkStruct;
udsBindContext networkBindCtx;
udsConnectionStatus networkStatus;
u16 networkConnectedMask;
//new code
//structure in buffer is u16(seqID),u16(size),size(data), ...
void *networkSendBuffer;
size_t networkSendBufferStartPos;
size_t networkSendBufferEndPos;
size_t networkSendBufferWrapPos;
u16 networkSeqSendNext;
u16 networkSeqSendConf[UDS_MAXNODES+1];
u16 networkSeqRecvLast[UDS_MAXNODES+1];
void *networkAckBuffer;
//async internal send/recieve handling
Thread networkThread;
volatile bool networkRunThread;
LightLock sendBufferLock;
void networkHandleSend();
void networkHandleRecieve();
void clearSendAckedBuffer();
bool sendAck(u16 target, u16 ack);
void networkThreadMain(void *arg) {
while(networkRunThread) {
if(udsRunning && isConnected) {
networkUpdateStatus();
networkHandleRecieve();
networkHandleSend();
}
//TODO: Set meaningfull value, WARNING: Setting this near 1ms (1000*1000) will make everything super laggy, higher values actually work better!
svcSleepThread(10000 * 1000);
}
}
void networkUpdateStatus() {
/*for(int i=0; i<10; i++) {
Result ret = udsGetConnectionStatus(&networkStatus);
if(!R_FAILED(ret)) {
return;
}
}*/
if(udsWaitConnectionStatusEvent(false, false)) {
udsGetConnectionStatus(&networkStatus);
}
}
void networkHandleRecieve() {
bool recieved = false;
do {
recieved = false;
size_t actualSize = 0;
u16 sourceNetworkNodeID;
u32 ackToSend = 0;
memset(networkBuffer, 0, networkBufferSize);
Result ret = udsPullPacket(&networkBindCtx, networkBuffer, networkBufferSize, &actualSize, &sourceNetworkNodeID);
if(R_FAILED(ret)) {
//TODO: what do?
//actualSize will be 0 if no packet is available
} else if(actualSize) {
void *readPointer = networkBuffer;
//ack frame
if(actualSize==sizeof(u16)) {
networkSeqSendConf[sourceNetworkNodeID] = *((u16*) readPointer);
clearSendAckedBuffer();
//normal frame
} else {
while(actualSize>0) {
//read seqID and size
u16 seqID = *((u16*) readPointer);
readPointer += sizeof(u16);
actualSize -= sizeof(u16);
u16 size = *((u16*) readPointer);
readPointer += sizeof(u16);
actualSize -= sizeof(u16);
//if the seq id was expected handle the packet
u16 nextID = networkGetExpectedSeqFrom(sourceNetworkNodeID);
if(seqID==nextID) {
networkSeqRecvLast[sourceNetworkNodeID] = seqID;
ackToSend = seqID;
//handle data - TODO: WARNING: Do not send sizeof(u16) packets or else they will get confused with this one
if(size==sizeof(u16)) {
networkConnectedMask = *((u16*) readPointer);
} else {
processPacket(readPointer, size);
}
} else if(networkSeqIsLowerThan(seqID, nextID)) {
ackToSend = seqID;
}
readPointer += size;
actualSize -= size;
}
if(ackToSend!=0) {
if(sendAck(sourceNetworkNodeID, ackToSend)) {
}
}
}
recieved = true;
}
} while(recieved);
}
void networkHandleSend() {
if(networkSendBufferStartPos!=networkSendBufferEndPos) {
LightLock_Lock(&sendBufferLock);
//determine send size
size_t currentSize = 0;
while(networkSendBufferStartPos+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() {
Result ret = udsInit(0x3000, NULL);
if(R_FAILED(ret)) {
udsRunning = false;
} else {
udsRunning = true;
scannedNetworksCount = 0;
scannedNetworks = NULL;
isConnected = false;
isServer = false;
networkConnectedMask = 0;
networkWriteBuffer = malloc(NETWORK_MAXDATASIZE);
if(networkWriteBuffer==NULL) {
networkExit();
return;
}
networkBufferSize = 0x4000;
networkBuffer = malloc(networkBufferSize);
if(networkBuffer==NULL) {
networkExit();
return;
}
networkSendBufferStartPos = 0;
networkSendBufferEndPos = 0;
networkSendBufferWrapPos = 0;
networkSendBuffer = malloc(NETWORK_SENDBUFFERSIZE);
if(networkSendBuffer==NULL) {
networkExit();
return;
}
networkSeqSendNext = 1;
for(int i=0; i<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() {
//Additionally to shutting down the service, clear any left over memory!
if(udsRunning) {
udsRunning = false;
if(networkRunThread) {
networkRunThread = false;
threadJoin(networkThread, U64_MAX);
threadFree(networkThread);
}
//cleanup any dynamically reserved memory
if(scannedNetworks!=NULL) free(scannedNetworks);
scannedNetworks = NULL;
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();
udsExit();
}
}
bool networkAvailable() {
return udsRunning;
}
bool networkHost() {
if(udsRunning && !isConnected) {
udsGenerateDefaultNetworkStruct(&networkStruct, NETWORK_WLANCOMMID, 0, NETWORK_MAXPLAYERS);
Result ret = udsCreateNetwork(&networkStruct, NETWORK_PASSPHRASE, strlen(NETWORK_PASSPHRASE)+1, &networkBindCtx, NETWORK_CHANNEL, NETWORK_RECVBUFSIZE);
if(R_FAILED(ret)) {
return false;
} else {
if(udsWaitConnectionStatusEvent(false, false)) {}
udsGetConnectionStatus(&networkStatus);
udsSetNewConnectionsBlocked(false, true, false);
isConnected = true;
isServer = true;
networkConnectedMask = 0;
return true;
}
}
return false;
}
void networkHostStopConnections() {
udsSetNewConnectionsBlocked(true, true, false);
}
void networkScan() {
if(udsRunning) {
//reset values from last scan
if(scannedNetworks!=NULL) free(scannedNetworks);
scannedNetworks = NULL;
scannedNetworksCount = 0;
//scan
memset(networkBuffer, 0, networkBufferSize);
Result ret = udsScanBeacons(networkBuffer, networkBufferSize, &scannedNetworks, &scannedNetworksCount, NETWORK_WLANCOMMID, 0, NULL, isConnected);
if(R_FAILED(ret)) {
//TODO: what do?
scannedNetworksCount = 0;
}
}
}
int networkGetScanCount() {
if(udsRunning) {
return scannedNetworksCount;
} else {
return 0;
}
}
bool networkGetScanName(char *name, int pos) {
if(udsRunning) {
if(pos<0 || pos>=scannedNetworksCount) return false;
Result ret = udsGetNodeInfoUsername(&(scannedNetworks[pos].nodes[0]), name);
if(R_FAILED(ret)) {
//TODO: what do?
return false;
}
return true;
}
return false;
}
bool networkConnect(int pos) {
if(udsRunning && !isConnected) {
if(pos<0 || pos>=scannedNetworksCount) return false;
Result ret = udsConnectNetwork(&scannedNetworks[pos].network, NETWORK_PASSPHRASE, strlen(NETWORK_PASSPHRASE)+1, &networkBindCtx, UDS_BROADCAST_NETWORKNODEID, UDSCONTYPE_Client, NETWORK_CHANNEL, NETWORK_RECVBUFSIZE);
if(R_FAILED(ret)) {
return false;
} else {
if(udsWaitConnectionStatusEvent(false, false)) {}
udsGetConnectionStatus(&networkStatus);
isConnected = true;
isServer = false;
networkConnectedMask = 0;
return true;
}
}
return false;
}
void networkDisconnect() {
//For clients this just means disconnect, for the server it means destroy the network
if(udsRunning && isConnected) {
isConnected = false;
LightLock_Lock(&sendBufferLock);
//reset send buffer
networkSendBufferStartPos = 0;
networkSendBufferEndPos = 0;
networkSendBufferWrapPos = 0;
//reset ack status
networkSeqSendNext = 1;
for(int i=0; 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
if(isServer) {
//TODO: Clients need to cleanup too, how can I tell they got disconnected
udsDestroyNetwork();
} else {
udsDisconnectNetwork();
}
udsUnbind(&networkBindCtx);
isServer = false;
}
}
void networkStart() {
//TODO: This sends the node_bitmask from server to everyone else, because it is uncorrect on some clients?
if(udsRunning && isConnected && isServer) {
void *buffer = networkWriteBuffer;
*((u16*) buffer) = networkStatus.node_bitmask;
networkConnectedMask = networkStatus.node_bitmask;
networkSend(networkWriteBuffer, sizeof(u16));
networkSendWaitFlush();
}
}
bool networkConnected() {
return isConnected;
}
int networkGetNodeCount() {
if(udsRunning && isConnected) {
return networkStatus.total_nodes;
} else {
return 0;
}
}
u16 networkGetLocalNodeID() {
if(udsRunning && isConnected) {
return networkStatus.cur_NetworkNodeID;
} else {
return 0;
}
}
bool networkIsNodeConnected(u16 id) {
if(udsRunning && isConnected) {
return networkStatus.node_bitmask & (1 << (id-1));
} else {
return false;
}
}
bool networkGetNodeName(u16 id, char *name) {
if(udsRunning && isConnected && networkIsNodeConnected(id)) {
udsNodeInfo nodeInfo;
udsGetNodeInformation(id, &nodeInfo);
Result ret = udsGetNodeInfoUsername(&nodeInfo, name);
if(R_FAILED(ret)) {
//TODO: what do?
return false;
}
return true;
}
return false;
}
u16 networkGetExpectedSeqFrom(u16 id) {
u16 nextID = networkSeqRecvLast[id];
nextID += 1;
if(nextID==0) {
nextID = 1;
}
return nextID;
}
bool networkSeqIsLowerThan(u16 firstID, u16 secondID) {
if (secondID < 100) {
return (firstID < secondID) || (firstID > 65536-100);
} else if (secondID > 65536-100) {
return (firstID < secondID) && (firstID > 100);
} else {
return (firstID < secondID);
}
}
int fitInSendBuffer(size_t size) {
//add "header" length
size += sizeof(u16)*2;
//we have no wrap currently
if(networkSendBufferStartPos<=networkSendBufferEndPos) {
//and can fit without wrap
if(networkSendBufferEndPos+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);
}
}

47
source/Network.h Executable file
View file

@ -0,0 +1,47 @@
#pragma once
#include <3ds.h>
#define NETWORK_WLANCOMMID 0x11441850
#define NETWORK_PASSPHRASE "minicraft3dsLP"
#define NETWORK_CHANNEL 1
#define NETWORK_RECVBUFSIZE UDS_DEFAULT_RECVBUFSIZE
#define NETWORK_MAXDATASIZE 1024
#define NETWORK_SENDBUFFERSIZE ((NETWORK_MAXDATASIZE+256)*10)
#define NETWORK_STACKSIZE (8*1024)
#define NETWORK_MAXPLAYERS 8
void *networkWriteBuffer;
void networkInit();
void networkExit();
bool networkAvailable();
bool networkHost();
void networkHostStopConnections();
void networkScan();
int networkGetScanCount();
bool networkGetScanName(char *name, int pos);
bool networkConnect(int pos);
void networkDisconnect();
void networkStart();
void networkUpdateStatus();
bool networkConnected();
int networkGetNodeCount();
u16 networkGetLocalNodeID();
bool networkIsNodeConnected(u16 id);
bool networkGetNodeName(u16 id, char *name);
u16 networkGetExpectedSeqFrom(u16 id);
bool networkSeqIsLowerThan(u16 firstID, u16 secondID);
void networkSend(void *packet, size_t size);
void networkSendWaitFlush();

357
source/PacketHandler.c Executable file
View file

@ -0,0 +1,357 @@
#include "PacketHandler.h"
#include <stdlib.h>
#include "Synchronizer.h"
FILE *recvFile;
size_t recvFileSize;
void * writeBool(void *buffer, size_t *size, bool value) {
*((bool*) buffer) = value;
*(size) += sizeof(bool);
return buffer + sizeof(bool);
}
void * writeU8(void *buffer, size_t *size, u8 value) {
*((u8*) buffer) = value;
*(size) += sizeof(u8);
return buffer + sizeof(u8);
}
void * writeU16(void *buffer, size_t *size, u16 value) {
*((u16*) buffer) = value;
*(size) += sizeof(u16);
return buffer + sizeof(u16);
}
void * writeU32(void *buffer, size_t *size, u32 value) {
*((u32*) buffer) = value;
*(size) += sizeof(u32);
return buffer + sizeof(u32);
}
void * writeSizeT(void *buffer, size_t *size, size_t value) {
*((size_t*) buffer) = value;
*(size) += sizeof(size_t);
return buffer + sizeof(size_t);
}
void * readBool(void *buffer, size_t *size, bool *value) {
*value = *((bool*) buffer);
*(size) -= sizeof(bool);
return buffer + sizeof(bool);
}
void * readU8(void *buffer, size_t *size, u8 *value) {
*value = *((u8*) buffer);
*(size) -= sizeof(u8);
return buffer + sizeof(u8);
}
void * readU16(void *buffer, size_t *size, u16 *value) {
*value = *((u16*) buffer);
*(size) -= sizeof(u16);
return buffer + sizeof(u16);
}
void * readU32(void *buffer, size_t *size, u32 *value) {
*value = *((u32*) buffer);
*(size) -= sizeof(u32);
return buffer + sizeof(u32);
}
void * readSizeT(void *buffer, size_t *size, size_t *value) {
*value = *((size_t*) buffer);
*(size) -= sizeof(size_t);
return buffer + sizeof(size_t);
}
void processPacket(void *packet, size_t size) {
//Differenciate the packets and process them
switch(packetGetID(packet)) {
case PACKET_START: {
void *buffer = packetGetDataStart(packet);
size = packetGetDataSize(size);
//find player index based on network node id
//and set player uuid in synchronizer
u32 seed;
u32 playerCount = 1;
int playerIndex = 0;
buffer = readU32(buffer, &size, &seed);
buffer = readU32(buffer, &size, &playerCount);
for(int i=0; i<playerCount; i++) {
u16 nodeID;
buffer = readU16(buffer, &size, &nodeID);
if(nodeID==networkGetLocalNodeID()) {
playerIndex = i;
}
}
//cleanup transfer tmp file
FILE *file = fopen("tmpTransfer.bin", "wb");
if(file!=NULL) {
fclose(file);
remove("tmpTransfer.bin");
}
//init synchronizer
synchronizerInit(seed, playerCount, playerIndex);
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
}
}
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);
}

34
source/PacketHandler.h Executable file
View file

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

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

437
source/Quests.c Executable file
View file

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

25
source/Quests.h Executable file
View file

@ -0,0 +1,25 @@
#pragma once
#include <stdlib.h>
#include <stdbool.h>
#include "QuestsData.h"
#include "Player.h"
#include "Crafting.h"
#define NPC_MENU_TALK 0
RecipeManager priestTrades;
RecipeManager farmerTrades;
RecipeManager dwarfTrades;
void initTrades();
void freeTrades();
void initQuests(QuestlineManager *questManager);
void resetQuests(QuestlineManager *questManager);
void freeQuests(QuestlineManager *questManager);
void resetNPCMenuData(NPC_MenuData *data);
void openNPCMenu(PlayerData *pd, int npc);
void tickNPCMenu(PlayerData *pd);
void renderNPCMenu(NPC_MenuData *data);

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

837
source/Render.c Normal file → Executable file

File diff suppressed because it is too large Load diff

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

@ -12,40 +12,39 @@ sf2d_texture *lanternLightBake;
sf2d_texture *glowwormLightBake;
sf2d_texture *glowwormBigLightBake;
int offsetX, offsetY;
int playerScale;
void render(s32 xp, s32 yp, u32 xTile, u32 yTile, u8 bits);
void renderb(s32 xp, s32 yp, u32 xTile, u32 yTile, u8 bits, u32 color);
void renderr(s32 xp, s32 yp, u32 xTile, u32 yTile, u8 bits, float rotate);
void renderc(s32 xp, s32 yp, u32 xTile, u32 yTile, int sizeX, int sizeY,
u8 bits);
void renderbc(s32 xp, s32 yp, u32 xTile, u32 yTile, int sizeX, int sizeY,
u8 bits, u32 color);
void renderc(s32 xp, s32 yp, u32 xTile, u32 yTile, int sizeX, int sizeY, u8 bits);
void renderbc(s32 xp, s32 yp, u32 xTile, u32 yTile, int sizeX, int sizeY, u8 bits, u32 color);
void render16(s32 xp, s32 yp, u32 xTile, u32 yTile, u8 bits);
void render16c(s32 xp, s32 yp, u32 xTile, u32 yTile, u8 bits, float scaleX,
float scaleY);
void render16c(s32 xp, s32 yp, u32 xTile, u32 yTile, u8 bits, float scaleX, float scaleY);
void render16b(s32 xp, s32 yp, u32 xTile, u32 yTile, u8 bits, u32 color);
void render16s(s32 xp, s32 yp, u32 tile, u8 bits, u32 color);
void render32(s32 xp, s32 yp, u32 xTile, u32 yTile, u8 bits);
void renderTitle(int x, int y);
void renderFrame(int x1, int y1, int x2, int y2, u32 bgColor);
void renderTile(int i, int d, int x, int y);
void renderConnectedTile4(int x, int y, u32 xTile, u32 yTile, u32 color);
void renderConnectedTile8(int x, int y, u32 xTile, u32 yTile, u32 color);
void renderBackground(int xScroll, int yScroll);
void renderMenuBackground(int xScroll, int yScroll); //Renders the darkness
void renderDayNight();
void renderTile(int i, int d, u8 level, int x, int y);
void renderConnectedTile4(int x, int y, u32 xTile, u32 yTile);
void renderConnectedTile8(int x, int y, u32 xTile, u32 yTile);
void renderBackground(s8 level, int xScroll, int yScroll);
void renderWeather(u8 level, int xScroll, int yScroll);
void renderDayNight(PlayerData *pd);
void renderButtonIcon(u32 icon, int x, int y, float scale);
void bakeLights();
void freeLightBakes();
void renderLightsToStencil(bool force, bool invert, bool rplayer);
void renderLightsToStencil(PlayerData *pd, bool force, bool invert, bool rplayer);
void resetStencilStuff();
void bakeLight(sf2d_texture* texture, int x, int y, int r);
void renderLight(int x, int y, sf2d_texture* texture);
void renderGui();
void renderZoomedMap();
void renderPlayer();
void renderGui(PlayerData *pd);
void renderZoomedMap(PlayerData *pd);
void renderPlayer(PlayerData *pd);
void drawText(char * msg, u32 x, u32 y);
void drawSizedText(char * msg, u32 x, u32 y, float size);
@ -55,12 +54,11 @@ void drawSizedTextColor(char * msg, int x, int y, float size, u32 color);
void renderFurniture(int itemID, int x, int y);
void renderEntity(Entity* e, int x, int y);
void renderEntities(int x, int y, EntityManager* em);
void renderEntities(u8 level, int x, int y, EntityManager* em);
void renderRecipes(RecipeManager * r, int xo, int yo, int x1, int y1,
int selected);
void renderItemList(Inventory * inv, int xo, int yo, int x1, int y1,
int selected);
void renderRecipes(RecipeManager * r, int xo, int yo, int x1, int y1, int selected);
void renderItemList(Inventory * inv, int xo, int yo, int x1, int y1, int selected);
void renderArmorList(Inventory * inv, int xo, int yo, int x1, int y1, int selected);
void renderItemWithText(Item* item, int x, int y);
void renderItemStuffWithText(int itemID, int itemCL, bool onlyOne, int x, int y);
void renderItemWithTextCentered(Item* item, int width, int y);

934
source/SaveLoad.c Normal file → Executable file
View file

@ -1,329 +1,663 @@
#include "SaveLoad.h"
s16 calculateImportantEntites(EntityManager * eManager, int level){
int i;
s16 count = 0;
for(i = 0; i < eManager->lastSlot[level]; ++i){
switch(eManager->entities[level][i].type){
case ENTITY_AIRWIZARD:
case ENTITY_SLIME:
case ENTITY_ZOMBIE:
case ENTITY_SKELETON:
case ENTITY_KNIGHT:
case ENTITY_ITEM:
case ENTITY_FURNITURE:
case ENTITY_PASSIVE:
case ENTITY_GLOWWORM:
count++;
break;
}
}
return count;
}
#include "ZipHelper.h"
bool entityIsImportant(Entity * e){
switch(e->type){
case ENTITY_AIRWIZARD:
case ENTITY_SLIME:
case ENTITY_ZOMBIE:
switch(e->type){
case ENTITY_AIRWIZARD:
case ENTITY_SLIME:
case ENTITY_ZOMBIE:
case ENTITY_SKELETON:
case ENTITY_KNIGHT:
case ENTITY_ITEM:
case ENTITY_FURNITURE:
case ENTITY_ITEM:
case ENTITY_FURNITURE:
case ENTITY_PASSIVE:
case ENTITY_GLOWWORM:
return true;
default:
return false;
}
case ENTITY_DRAGON:
case ENTITY_NPC:
return true;
default:
return false;
}
}
void saveCurrentWorld(char * filename, EntityManager * eManager, Entity * player, u8 * map, u8 * mapData){
FILE * file = fopen(filename, "wb");
int i,j;
s16 calculateImportantEntites(EntityManager * eManager, int level){
int i;
s16 count = 0;
for(i = 0; i < eManager->lastSlot[level]; ++i){
if(entityIsImportant(&eManager->entities[level][i])){
count++;
}
}
return count;
}
// Player Data
fwrite(&player->p.score, sizeof(int), 1, file);
fwrite(&player->p.hasWonSaved, sizeof(bool), 1, file);
fwrite(&player->p.health, sizeof(s16), 1, file);
fwrite(&player->x, sizeof(s16), 1, file);
fwrite(&player->y, sizeof(s16), 1, file);
fwrite(&currentLevel, sizeof(s8), 1, file);
//helper methods
char **files;
int fileCount;
// Inventory Data
fwrite(&eManager->nextInv, sizeof(s16), 1, file); // write amount of inventories.
for(i = 0; i < eManager->nextInv; ++i) {
fwrite(&eManager->invs[i].lastSlot, sizeof(s16), 1, file); // write amount of items in inventory;
for(j = 0; j < eManager->invs[i].lastSlot; ++j) {
fwrite(&eManager->invs[i].items[j].id, sizeof(s16), 1, file); // write ID of item
fwrite(&eManager->invs[i].items[j].countLevel, sizeof(s16), 1, file); // write count/level of item
fwrite(&eManager->invs[i].items[j].onlyOne, sizeof(bool), 1, file);
if(eManager->invs[i].items[j].id == ITEM_CHEST){
int invIndex = eManager->invs[i].items[j].chestPtr - eManager->invs;
fwrite(&invIndex, sizeof(int), 1, file);
}
}
}
void saveTrackFileReset() {
if(files!=NULL) {
for(int i=0; i<fileCount; i++) {
free(files[i]);
}
free(files);
}
// Entity Data
for(i = 0; i < 5; ++i){
int amount = calculateImportantEntites(eManager,i);
fwrite(&amount, sizeof(s16), 1, file); // read amount of entities in level.
for(j = 0; j < eManager->lastSlot[i]; ++j){
if(!entityIsImportant(&eManager->entities[i][j])) continue;
fwrite(&eManager->entities[i][j].type, sizeof(s16), 1, file); // write entity's type ID
fwrite(&eManager->entities[i][j].x, sizeof(s16), 1, file); // write entity's x coordinate
fwrite(&eManager->entities[i][j].y, sizeof(s16), 1, file); // write entity's y coordinate
switch(eManager->entities[i][j].type){
case ENTITY_AIRWIZARD:
fwrite(&eManager->entities[i][j].wizard.health, sizeof(s16), 1, file);
break;
case ENTITY_SLIME:
fwrite(&eManager->entities[i][j].slime.health, sizeof(s16), 1, file);
fwrite(&eManager->entities[i][j].slime.lvl, sizeof(s8), 1, file);
break;
case ENTITY_ZOMBIE:
files = NULL;
fileCount = 0;
}
int saveTrackFile(char *filename) {
//test if entry allready present
for(int i=0; i<fileCount; i++) {
if(strcmp(filename, files[i])==0) {
return 0;
}
}
//add new entry
fileCount++;
char **newFiles = realloc(files, fileCount*sizeof(char*));
if(!newFiles) {
for(int i=0; i<fileCount-1; i++) {
free(files[i]);
}
free(files);
files = NULL;
return 1;
}
files = newFiles;
files[fileCount-1] = malloc(strlen(filename)+1);
strcpy(files[fileCount-1], filename);
return 0;
}
void saveDeleteTrackedFiles() {
for(int i=0; i<fileCount; i++) {
remove(files[i]);
}
}
bool saveFileCopy(char *target, char*source) {
char buffer[SAVE_COPYBUFFER_SIZE];
FILE *in = fopen(source, "rb");
if(in==NULL) {
return false;
}
FILE *out = fopen(target, "wb");
if(out==NULL) {
fclose(out);
return false;
}
size_t size;
do {
size = fread(buffer, 1, SAVE_COPYBUFFER_SIZE, in);
if(size>0) {
fwrite(buffer, 1, size, out);
}
} while(size>0);
fclose(in);
fclose(out);
return true;
}
//internal save methods
void saveInventory(Inventory *inv, EntityManager *eManager, FILE *file) {
fwrite(&inv->lastSlot, sizeof(s16), 1, file); // write amount of items in inventory;
for(int j = 0; j < inv->lastSlot; ++j) {
fwrite(&(inv->items[j].id), sizeof(s16), 1, file); // write ID of item
fwrite(&(inv->items[j].countLevel), sizeof(s16), 1, file); // write count/level of item
fwrite(&(inv->items[j].onlyOne), sizeof(bool), 1, file);
if(inv->items[j].id == ITEM_CHEST){
int invIndex = inv->items[j].chestPtr - eManager->invs;
fwrite(&invIndex, sizeof(int), 1, file);
}
}
}
void saveWorldInternal(char *filename, EntityManager *eManager, WorldData *worldData) {
FILE * file = fopen(filename, "wb"); //TODO: should be checked
int i, j;
//write savefile version
int version = SAVE_VERSION;
fwrite(&version, sizeof(int), 1, file);
// Inventory Data
fwrite(&eManager->nextInv, sizeof(s16), 1, file); // write amount of inventories.
for(i = 0; i < eManager->nextInv; ++i) {
saveInventory(&(eManager->invs[i]), eManager, file);
}
// Entity Data
for(i = 0; i < 5; ++i) { //for every level (except dungeon of course)
int amount = calculateImportantEntites(eManager,i);
fwrite(&amount, sizeof(s16), 1, file); // read amount of entities in level.
for(j = 0; j < eManager->lastSlot[i]; ++j){
if(!entityIsImportant(&eManager->entities[i][j])) continue;
fwrite(&eManager->entities[i][j].type, sizeof(s16), 1, file); // write entity's type ID
fwrite(&eManager->entities[i][j].x, sizeof(s16), 1, file); // write entity's x coordinate
fwrite(&eManager->entities[i][j].y, sizeof(s16), 1, file); // write entity's y coordinate
switch(eManager->entities[i][j].type){
case ENTITY_AIRWIZARD:
fwrite(&eManager->entities[i][j].wizard.health, sizeof(s16), 1, file);
break;
case ENTITY_SLIME:
fwrite(&eManager->entities[i][j].slime.health, sizeof(s16), 1, file);
fwrite(&eManager->entities[i][j].slime.lvl, sizeof(s8), 1, file);
break;
case ENTITY_ZOMBIE:
case ENTITY_SKELETON:
case ENTITY_KNIGHT:
fwrite(&eManager->entities[i][j].hostile.health, sizeof(s16), 1, file);
fwrite(&eManager->entities[i][j].hostile.lvl, sizeof(s8), 1, file);
break;
case ENTITY_ITEM:
fwrite(&eManager->entities[i][j].entityItem.item.id, sizeof(s16), 1, file);
fwrite(&eManager->entities[i][j].entityItem.item.countLevel, sizeof(s16), 1, file);
fwrite(&eManager->entities[i][j].entityItem.age, sizeof(s16), 1, file);
break;
case ENTITY_FURNITURE:
fwrite(&eManager->entities[i][j].entityFurniture.itemID, sizeof(s16), 1, file);
int invIndex = eManager->entities[i][j].entityFurniture.inv - eManager->invs;
fwrite(&invIndex, sizeof(int), 1, file);
break;
fwrite(&eManager->entities[i][j].hostile.health, sizeof(s16), 1, file);
fwrite(&eManager->entities[i][j].hostile.lvl, sizeof(s8), 1, file);
break;
case ENTITY_ITEM:
fwrite(&eManager->entities[i][j].entityItem.item.id, sizeof(s16), 1, file);
fwrite(&eManager->entities[i][j].entityItem.item.countLevel, sizeof(s16), 1, file);
fwrite(&eManager->entities[i][j].entityItem.age, sizeof(s16), 1, file);
break;
case ENTITY_FURNITURE:
fwrite(&eManager->entities[i][j].entityFurniture.itemID, sizeof(s16), 1, file);
int invIndex = eManager->entities[i][j].entityFurniture.inv - eManager->invs;
fwrite(&invIndex, sizeof(int), 1, file);
break;
case ENTITY_PASSIVE:
fwrite(&eManager->entities[i][j].passive.health, sizeof(s16), 1, file);
fwrite(&eManager->entities[i][j].passive.mtype, sizeof(u8), 1, file);
break;
}
}
}
fwrite(&eManager->entities[i][j].passive.mtype, sizeof(u8), 1, file);
break;
case ENTITY_DRAGON:
fwrite(&eManager->entities[i][j].dragon.health, sizeof(s16), 1, file);
break;
case ENTITY_NPC:
fwrite(&eManager->entities[i][j].npc.type, sizeof(u8), 1, file);
break;
}
}
}
// Map Data
// Day/season Data
fwrite(&worldData->daytime, sizeof(u16), 1, file);
fwrite(&worldData->day, sizeof(int), 1, file);
fwrite(&worldData->season, sizeof(u8), 1, file);
fwrite(&worldData->rain, sizeof(bool), 1, file);
// Compass Data
fwrite(worldData->compassData, sizeof(u8), 6*3, file); //x,y of choosen stair and count per level
// Map Data
//Don't write or load dungeon, so only first 5 levels not 6
fwrite(map, sizeof(u8), 128*128*5, file); // Map Tile IDs, 128*128*5 bytes = 80KB
fwrite(mapData, sizeof(u8), 128*128*5, file); // Map Tile Data (Damage done to trees/rocks, age of wheat & saplings, etc). 80KB
fwrite(worldData->map, sizeof(u8), 128*128*5, file); // Map Tile IDs, 128*128*5 bytes = 80KB
fwrite(worldData->data, sizeof(u8), 128*128*5, file); // Map Tile Data (Damage done to trees/rocks, age of wheat & saplings, etc). 80KB
fwrite(&daytime, sizeof(u16), 1, file);
fclose(file);
fclose(file);
}
int loadWorld(char * filename, EntityManager * eManager, Entity * player, u8 * map, u8 * mapData){
FILE * file;
int i,j;
void savePlayerInternal(char *filename, PlayerData *player, EntityManager *eManager) {
FILE * file = fopen(filename, "wb"); //TODO: should be checked
if ((file = fopen(filename, "rb"))){
int i;
fread(&player->p.score, sizeof(int), 1, file);
fread(&player->p.hasWonSaved, sizeof(bool), 1, file);
fread(&player->p.health, sizeof(s16), 1, file);
fread(&player->x, sizeof(s16), 1, file);
fread(&player->y, sizeof(s16), 1, file);
fread(&currentLevel, sizeof(s8), 1, file);
//write savefile version
int version = SAVE_VERSION;
fwrite(&version, sizeof(int), 1, file);
fread(&eManager->nextInv, sizeof(s16), 1, file);
for(i = 0; i < eManager->nextInv; ++i) {
fread(&eManager->invs[i].lastSlot, sizeof(s16), 1, file); // read amount of items in inventory;
for(j = 0; j < eManager->invs[i].lastSlot; ++j) {
fread(&eManager->invs[i].items[j].id, sizeof(s16), 1, file); // write ID of item
fread(&eManager->invs[i].items[j].countLevel, sizeof(s16), 1, file); // write count/level of item
fread(&eManager->invs[i].items[j].onlyOne, sizeof(bool), 1, file);
eManager->invs[i].items[j].invPtr = (int*)&eManager->invs[i]; // setup Inventory pointer
eManager->invs[i].items[j].slotNum = j; // setup slot number
if(eManager->invs[i].items[j].id == ITEM_CHEST){ // for chest item specifically.
int invIndex;
fread(&invIndex, sizeof(int), 1, file);
eManager->invs[i].items[j].chestPtr = (Inventory*)&eManager->invs[invIndex]; // setup chest inventory pointer.
}
}
}
// basic player info
fwrite(&player->score, sizeof(int), 1, file);
fwrite(&player->isSpawned, sizeof(bool), 1, file);
fwrite(&player->entity.p.hasWonSaved, sizeof(bool), 1, file);
fwrite(&player->entity.p.health, sizeof(s16), 1, file);
fwrite(&player->entity.x, sizeof(s16), 1, file);
fwrite(&player->entity.y, sizeof(s16), 1, file);
fwrite(&player->entity.level, sizeof(s8), 1, file);
for(i = 0; i < 5; ++i){
fread(&eManager->lastSlot[i], sizeof(s16), 1, file); // read amount of entities in level.
for(j = 0; j < eManager->lastSlot[i]; ++j){
fread(&eManager->entities[i][j].type, sizeof(s16), 1, file); // read entity's type ID
fread(&eManager->entities[i][j].x, sizeof(s16), 1, file); // read entity's x coordinate
fread(&eManager->entities[i][j].y, sizeof(s16), 1, file); // read entity's y coordinate
eManager->entities[i][j].slotNum = j;
switch(eManager->entities[i][j].type){
case ENTITY_SMASHPARTICLE:
eManager->entities[i][j].level = i;
eManager->entities[i][j].smashParticle.age = 300;
eManager->entities[i][j].canPass = true;
break;
case ENTITY_TEXTPARTICLE:
eManager->entities[i][j].level = i;
eManager->entities[i][j].canPass = true;
eManager->entities[i][j].textParticle.age = 59;
eManager->entities[i][j].textParticle.text = NULL;
eManager->entities[i][j].textParticle.xx = eManager->entities[i][j].x;
eManager->entities[i][j].textParticle.yy = eManager->entities[i][j].y;
eManager->entities[i][j].textParticle.zz = 2;
eManager->entities[i][j].textParticle.xa = 0;
eManager->entities[i][j].textParticle.ya = 0;
eManager->entities[i][j].textParticle.za = 0;
break;
case ENTITY_SPARK:
eManager->entities[i][j].level = i;
eManager->entities[i][j].spark.age = 300;
break;
case ENTITY_AIRWIZARD:
fread(&eManager->entities[i][j].wizard.health, sizeof(s16), 1, file);
eManager->entities[i][j].level = i;
eManager->entities[i][j].hurtTime = 0;
eManager->entities[i][j].xKnockback = 0;
eManager->entities[i][j].yKnockback = 0;
eManager->entities[i][j].wizard.dir = 0;
eManager->entities[i][j].wizard.attackDelay = 0;
eManager->entities[i][j].wizard.attackTime = 0;
eManager->entities[i][j].wizard.attackType = 0;
eManager->entities[i][j].wizard.xa = 0;
eManager->entities[i][j].wizard.ya = 0;
eManager->entities[i][j].xr = 4;
eManager->entities[i][j].yr = 3;
eManager->entities[i][j].canPass = true;
break;
case ENTITY_SLIME:
fread(&eManager->entities[i][j].slime.health, sizeof(s16), 1, file);
fread(&eManager->entities[i][j].slime.lvl, sizeof(s8), 1, file);
eManager->entities[i][j].level = i;
eManager->entities[i][j].hurtTime = 0;
eManager->entities[i][j].xKnockback = 0;
eManager->entities[i][j].yKnockback = 0;
eManager->entities[i][j].slime.xa = 0;
eManager->entities[i][j].slime.ya = 0;
eManager->entities[i][j].slime.dir = 0;
eManager->entities[i][j].xr = 4;
eManager->entities[i][j].yr = 3;
eManager->entities[i][j].canPass = false;
switch(eManager->entities[i][j].slime.lvl){
case 2: eManager->entities[i][j].slime.color = 0xFF8282CC; break;
case 3: eManager->entities[i][j].slime.color = 0xFFEFEFEF; break;
case 4: eManager->entities[i][j].slime.color = 0xFFAA6262; break;
default: eManager->entities[i][j].slime.color = 0xFF95DB95; break;
}
break;
case ENTITY_ZOMBIE:
fread(&eManager->entities[i][j].hostile.health, sizeof(s16), 1, file);
fread(&eManager->entities[i][j].hostile.lvl, sizeof(s8), 1, file);
eManager->entities[i][j].level = i;
eManager->entities[i][j].hurtTime = 0;
eManager->entities[i][j].xKnockback = 0;
eManager->entities[i][j].yKnockback = 0;
eManager->entities[i][j].hostile.dir = 0;
eManager->entities[i][j].xr = 4;
eManager->entities[i][j].yr = 3;
eManager->entities[i][j].canPass = false;
switch(eManager->entities[i][j].hostile.lvl){
case 2: eManager->entities[i][j].hostile.color = 0xFF8282CC; break;
case 3: eManager->entities[i][j].hostile.color = 0xFFEFEFEF; break;
case 4: eManager->entities[i][j].hostile.color = 0xFFAA6262; break;
default: eManager->entities[i][j].hostile.color = 0xFF95DB95; break;
}
break;
case ENTITY_SKELETON:
fread(&eManager->entities[i][j].hostile.health, sizeof(s16), 1, file);
fread(&eManager->entities[i][j].hostile.lvl, sizeof(s8), 1, file);
eManager->entities[i][j].level = i;
eManager->entities[i][j].hurtTime = 0;
eManager->entities[i][j].xKnockback = 0;
eManager->entities[i][j].yKnockback = 0;
eManager->entities[i][j].hostile.dir = 0;
eManager->entities[i][j].hostile.randAttackTime = 0;
eManager->entities[i][j].xr = 4;
eManager->entities[i][j].yr = 3;
eManager->entities[i][j].canPass = false;
switch(eManager->entities[i][j].hostile.lvl){
case 2: eManager->entities[i][j].hostile.color = 0xFFC4C4C4; break;
case 3: eManager->entities[i][j].hostile.color = 0xFFA0A0A0; break;
case 4: eManager->entities[i][j].hostile.color = 0xFF7A7A7A; break;
default: eManager->entities[i][j].hostile.color = 0xFFFFFFFF; break;
}
break;
case ENTITY_KNIGHT:
fread(&eManager->entities[i][j].hostile.health, sizeof(s16), 1, file);
fread(&eManager->entities[i][j].hostile.lvl, sizeof(s8), 1, file);
eManager->entities[i][j].level = i;
eManager->entities[i][j].hurtTime = 0;
eManager->entities[i][j].xKnockback = 0;
eManager->entities[i][j].yKnockback = 0;
eManager->entities[i][j].hostile.dir = 0;
eManager->entities[i][j].xr = 4;
eManager->entities[i][j].yr = 3;
eManager->entities[i][j].canPass = false;
switch(eManager->entities[i][j].hostile.lvl){
case 2: eManager->entities[i][j].hostile.color = 0xFF0000C6; break;
case 3: eManager->entities[i][j].hostile.color = 0xFF00A3C6; break;
case 4: eManager->entities[i][j].hostile.color = 0xFF707070; break;
default: eManager->entities[i][j].hostile.color = 0xFFFFFFFF; break;
}
break;
case ENTITY_ITEM:
//eManager->entities[i][j].entityItem.item = newItem(0,0);
fread(&eManager->entities[i][j].entityItem.item.id, sizeof(s16), 1, file);
fread(&eManager->entities[i][j].entityItem.item.countLevel, sizeof(s16), 1, file);
fread(&eManager->entities[i][j].entityItem.age, sizeof(s16), 1, file);
eManager->entities[i][j].level = i;
eManager->entities[i][j].entityItem.age = 0;
eManager->entities[i][j].xr = 3;
eManager->entities[i][j].yr = 3;
eManager->entities[i][j].canPass = false;
eManager->entities[i][j].entityItem.xx = eManager->entities[i][j].x;
eManager->entities[i][j].entityItem.yy = eManager->entities[i][j].y;
eManager->entities[i][j].entityItem.zz = 2;
eManager->entities[i][j].entityItem.xa = 0;
eManager->entities[i][j].entityItem.ya = 0;
eManager->entities[i][j].entityItem.za = 0;
break;
case ENTITY_FURNITURE:
fread(&eManager->entities[i][j].entityFurniture.itemID, sizeof(s16), 1, file);
int invIndex;
fread(&invIndex, sizeof(int), 1, file);
eManager->entities[i][j].entityFurniture.inv = &eManager->invs[invIndex];
eManager->entities[i][j].xr = 3;
eManager->entities[i][j].yr = 3;
eManager->entities[i][j].canPass = false;
if(eManager->entities[i][j].entityFurniture.itemID == ITEM_LANTERN) eManager->entities[i][j].entityFurniture.r = 8;
break;
case ENTITY_PASSIVE:
fread(&eManager->entities[i][j].passive.health, sizeof(s16), 1, file);
fread(&eManager->entities[i][j].passive.mtype, sizeof(u8), 1, file);
eManager->entities[i][j].level = i;
eManager->entities[i][j].hurtTime = 0;
eManager->entities[i][j].xKnockback = 0;
eManager->entities[i][j].yKnockback = 0;
eManager->entities[i][j].passive.dir = 0;
eManager->entities[i][j].xr = 4;
eManager->entities[i][j].yr = 3;
eManager->entities[i][j].canPass = false;
break;
case ENTITY_GLOWWORM:
eManager->entities[i][j].glowworm.xa = 0;
eManager->entities[i][j].glowworm.ya = 0;
eManager->entities[i][j].glowworm.randWalkTime = 0;
eManager->entities[i][j].glowworm.waitTime = 0;
break;
}
}
}
//Don't write or load dungeon, so only first 5 levels not 6
fread(map, sizeof(u8), 128*128*5, file);
fread(mapData, sizeof(u8), 128*128*5, file);
saveInventory(&(player->inventory), eManager, file);
//set to startvalue incase file is old and doesn't contain saved time
daytime = 6001;
fread(&daytime, sizeof(u16), 1, file);
// Sprite info
fwrite(&(player->sprite.choosen), sizeof(bool), 1, file);
fwrite(&(player->sprite.legs), sizeof(u8), 1, file);
fwrite(&(player->sprite.body), sizeof(u8), 1, file);
fwrite(&(player->sprite.arms), sizeof(u8), 1, file);
fwrite(&(player->sprite.head), sizeof(u8), 1, file);
fwrite(&(player->sprite.eyes), sizeof(u8), 1, file);
fclose(file);
return 0;
}
return 1;
// Minimap Data
fwrite(player->minimapData, sizeof(u8), 128*128, file); // Minimap, visibility data 16KB
// Quest Data
fwrite(&(player->questManager.size), sizeof(int), 1, file);
for(i = 0; i < player->questManager.size; ++i) {
fwrite(&(player->questManager.questlines[i].currentQuest), sizeof(int), 1, file);
fwrite(&(player->questManager.questlines[i].currentQuestDone), sizeof(bool), 1, file);
}
//Potion Data
fwrite(&UnderStrengthEffect, sizeof(bool), 1, file);
fwrite(&UnderSpeedEffect, sizeof(bool), 1, file);
fwrite(&regening, sizeof(bool), 1, file);
fwrite(&UnderSwimBreathEffect, sizeof(bool), 1, file);
fwrite(&player->entity.p.strengthTimer, sizeof(int), 1, file);
fwrite(&player->entity.p.speedTimer, sizeof(int), 1, file);
fwrite(&player->entity.p.swimBreathTimer, sizeof(int), 1, file);
fwrite(&player->entity.p.regenTimer, sizeof(int), 1, file);
fclose(file);
}
//internal load methods
void loadInventory(Inventory *inv, EntityManager *eManager, FILE *file) {
fread(&(inv->lastSlot), sizeof(s16), 1, file); // read amount of items in inventory;
for(int j = 0; j < inv->lastSlot; ++j) {
fread(&(inv->items[j].id), sizeof(s16), 1, file); // write ID of item
fread(&(inv->items[j].countLevel), sizeof(s16), 1, file); // write count/level of item
fread(&(inv->items[j].onlyOne), sizeof(bool), 1, file);
inv->items[j].invPtr = (int*)inv; // setup Inventory pointer
inv->items[j].slotNum = j; // setup slot number
if(inv->items[j].id == ITEM_CHEST){ // for chest item specifically.
int invIndex;
fread(&invIndex, sizeof(int), 1, file);
inv->items[j].chestPtr = (Inventory*)&eManager->invs[invIndex]; // setup chest inventory pointer.
}
}
}
void loadWorldInternal(char *filename, EntityManager *eManager, WorldData *worldData) {
FILE * file = fopen(filename, "rb"); //TODO: should be checked
int i, j;
//read savefile version
int version;
fread(&version, sizeof(int), 1, file);
// Inventory Data
fread(&eManager->nextInv, sizeof(s16), 1, file);
for(i = 0; i < eManager->nextInv; ++i) {
loadInventory(&(eManager->invs[i]), eManager, file);
}
// Entity Data
for(i = 0; i < 5; ++i){
fread(&eManager->lastSlot[i], sizeof(s16), 1, file); // read amount of entities in level.
for(j = 0; j < eManager->lastSlot[i]; ++j){
fread(&eManager->entities[i][j].type, sizeof(s16), 1, file); // read entity's type ID
fread(&eManager->entities[i][j].x, sizeof(s16), 1, file); // read entity's x coordinate
fread(&eManager->entities[i][j].y, sizeof(s16), 1, file); // read entity's y coordinate
eManager->entities[i][j].slotNum = j;
switch(eManager->entities[i][j].type){
case ENTITY_SMASHPARTICLE:
eManager->entities[i][j].level = i;
eManager->entities[i][j].smashParticle.age = 300;
eManager->entities[i][j].canPass = true;
break;
case ENTITY_TEXTPARTICLE:
eManager->entities[i][j].level = i;
eManager->entities[i][j].canPass = true;
eManager->entities[i][j].textParticle.age = 59;
eManager->entities[i][j].textParticle.text = NULL;
eManager->entities[i][j].textParticle.xx = eManager->entities[i][j].x;
eManager->entities[i][j].textParticle.yy = eManager->entities[i][j].y;
eManager->entities[i][j].textParticle.zz = 2;
eManager->entities[i][j].textParticle.xa = 0;
eManager->entities[i][j].textParticle.ya = 0;
eManager->entities[i][j].textParticle.za = 0;
break;
case ENTITY_SPARK:
eManager->entities[i][j].level = i;
eManager->entities[i][j].spark.age = 300;
break;
case ENTITY_AIRWIZARD:
fread(&eManager->entities[i][j].wizard.health, sizeof(s16), 1, file);
eManager->entities[i][j].level = i;
eManager->entities[i][j].hurtTime = 0;
eManager->entities[i][j].xKnockback = 0;
eManager->entities[i][j].yKnockback = 0;
eManager->entities[i][j].wizard.dir = 0;
eManager->entities[i][j].wizard.attackDelay = 0;
eManager->entities[i][j].wizard.attackTime = 0;
eManager->entities[i][j].wizard.attackType = 0;
eManager->entities[i][j].wizard.xa = 0;
eManager->entities[i][j].wizard.ya = 0;
eManager->entities[i][j].xr = 4;
eManager->entities[i][j].yr = 3;
eManager->entities[i][j].canPass = true;
break;
case ENTITY_SLIME:
fread(&eManager->entities[i][j].slime.health, sizeof(s16), 1, file);
fread(&eManager->entities[i][j].slime.lvl, sizeof(s8), 1, file);
eManager->entities[i][j].level = i;
eManager->entities[i][j].hurtTime = 0;
eManager->entities[i][j].xKnockback = 0;
eManager->entities[i][j].yKnockback = 0;
eManager->entities[i][j].slime.xa = 0;
eManager->entities[i][j].slime.ya = 0;
eManager->entities[i][j].slime.dir = 0;
eManager->entities[i][j].xr = 4;
eManager->entities[i][j].yr = 3;
eManager->entities[i][j].canPass = false;
switch(eManager->entities[i][j].slime.lvl){
case 2: eManager->entities[i][j].slime.color = 0xFF8282CC; break;
case 3: eManager->entities[i][j].slime.color = 0xFFEFEFEF; break;
case 4: eManager->entities[i][j].slime.color = 0xFFAA6262; break;
default: eManager->entities[i][j].slime.color = 0xFF95DB95; break;
}
break;
case ENTITY_ZOMBIE:
fread(&eManager->entities[i][j].hostile.health, sizeof(s16), 1, file);
fread(&eManager->entities[i][j].hostile.lvl, sizeof(s8), 1, file);
eManager->entities[i][j].level = i;
eManager->entities[i][j].hurtTime = 0;
eManager->entities[i][j].xKnockback = 0;
eManager->entities[i][j].yKnockback = 0;
eManager->entities[i][j].hostile.dir = 0;
eManager->entities[i][j].xr = 4;
eManager->entities[i][j].yr = 3;
eManager->entities[i][j].canPass = false;
switch(eManager->entities[i][j].hostile.lvl){
case 2: eManager->entities[i][j].hostile.color = 0xFF8282CC; break;
case 3: eManager->entities[i][j].hostile.color = 0xFFEFEFEF; break;
case 4: eManager->entities[i][j].hostile.color = 0xFFAA6262; break;
default: eManager->entities[i][j].hostile.color = 0xFF95DB95; break;
}
break;
case ENTITY_SKELETON:
fread(&eManager->entities[i][j].hostile.health, sizeof(s16), 1, file);
fread(&eManager->entities[i][j].hostile.lvl, sizeof(s8), 1, file);
eManager->entities[i][j].level = i;
eManager->entities[i][j].hurtTime = 0;
eManager->entities[i][j].xKnockback = 0;
eManager->entities[i][j].yKnockback = 0;
eManager->entities[i][j].hostile.dir = 0;
eManager->entities[i][j].hostile.randAttackTime = 0;
eManager->entities[i][j].xr = 4;
eManager->entities[i][j].yr = 3;
eManager->entities[i][j].canPass = false;
switch(eManager->entities[i][j].hostile.lvl){
case 2: eManager->entities[i][j].hostile.color = 0xFFC4C4C4; break;
case 3: eManager->entities[i][j].hostile.color = 0xFFA0A0A0; break;
case 4: eManager->entities[i][j].hostile.color = 0xFF7A7A7A; break;
default: eManager->entities[i][j].hostile.color = 0xFFFFFFFF; break;
}
break;
case ENTITY_KNIGHT:
fread(&eManager->entities[i][j].hostile.health, sizeof(s16), 1, file);
fread(&eManager->entities[i][j].hostile.lvl, sizeof(s8), 1, file);
eManager->entities[i][j].level = i;
eManager->entities[i][j].hurtTime = 0;
eManager->entities[i][j].xKnockback = 0;
eManager->entities[i][j].yKnockback = 0;
eManager->entities[i][j].hostile.dir = 0;
eManager->entities[i][j].xr = 4;
eManager->entities[i][j].yr = 3;
eManager->entities[i][j].canPass = false;
switch(eManager->entities[i][j].hostile.lvl){
case 2: eManager->entities[i][j].hostile.color = 0xFF0000C6; break;
case 3: eManager->entities[i][j].hostile.color = 0xFF00A3C6; break;
case 4: eManager->entities[i][j].hostile.color = 0xFF707070; break;
default: eManager->entities[i][j].hostile.color = 0xFFFFFFFF; break;
}
break;
case ENTITY_ITEM:
//eManager->entities[i][j].entityItem.item = newItem(0,0);
fread(&eManager->entities[i][j].entityItem.item.id, sizeof(s16), 1, file);
fread(&eManager->entities[i][j].entityItem.item.countLevel, sizeof(s16), 1, file);
fread(&eManager->entities[i][j].entityItem.age, sizeof(s16), 1, file);
eManager->entities[i][j].level = i;
eManager->entities[i][j].entityItem.age = 0;
eManager->entities[i][j].xr = 3;
eManager->entities[i][j].yr = 3;
eManager->entities[i][j].canPass = false;
eManager->entities[i][j].entityItem.xx = eManager->entities[i][j].x;
eManager->entities[i][j].entityItem.yy = eManager->entities[i][j].y;
eManager->entities[i][j].entityItem.zz = 2;
eManager->entities[i][j].entityItem.xa = 0;
eManager->entities[i][j].entityItem.ya = 0;
eManager->entities[i][j].entityItem.za = 0;
break;
case ENTITY_FURNITURE:
fread(&eManager->entities[i][j].entityFurniture.itemID, sizeof(s16), 1, file);
int invIndex;
fread(&invIndex, sizeof(int), 1, file);
eManager->entities[i][j].entityFurniture.inv = &eManager->invs[invIndex];
eManager->entities[i][j].xr = 3;
eManager->entities[i][j].yr = 3;
eManager->entities[i][j].canPass = false;
if(eManager->entities[i][j].entityFurniture.itemID == ITEM_LANTERN) eManager->entities[i][j].entityFurniture.r = 8;
break;
case ENTITY_PASSIVE:
fread(&eManager->entities[i][j].passive.health, sizeof(s16), 1, file);
fread(&eManager->entities[i][j].passive.mtype, sizeof(u8), 1, file);
eManager->entities[i][j].level = i;
eManager->entities[i][j].hurtTime = 0;
eManager->entities[i][j].xKnockback = 0;
eManager->entities[i][j].yKnockback = 0;
eManager->entities[i][j].passive.dir = 0;
eManager->entities[i][j].xr = 4;
eManager->entities[i][j].yr = 3;
eManager->entities[i][j].canPass = false;
break;
case ENTITY_GLOWWORM:
eManager->entities[i][j].glowworm.xa = 0;
eManager->entities[i][j].glowworm.ya = 0;
eManager->entities[i][j].glowworm.randWalkTime = 0;
eManager->entities[i][j].glowworm.waitTime = 0;
break;
case ENTITY_DRAGON:
fread(&eManager->entities[i][j].dragon.health, sizeof(s16), 1, file);
eManager->entities[i][j].level = i;
eManager->entities[i][j].hurtTime = 0;
eManager->entities[i][j].xKnockback = 0;
eManager->entities[i][j].yKnockback = 0;
eManager->entities[i][j].dragon.dir = 0;
eManager->entities[i][j].dragon.attackDelay = 0;
eManager->entities[i][j].dragon.attackTime = 0;
eManager->entities[i][j].dragon.attackType = 0;
eManager->entities[i][j].dragon.animTimer = 0;
eManager->entities[i][j].dragon.xa = 0;
eManager->entities[i][j].dragon.ya = 0;
eManager->entities[i][j].xr = 8;
eManager->entities[i][j].yr = 8;
eManager->entities[i][j].canPass = true;
break;
case ENTITY_NPC:
fread(&eManager->entities[i][j].npc.type, sizeof(u8), 1, file);
eManager->entities[i][j].level = i;
eManager->entities[i][j].hurtTime = 0;
eManager->entities[i][j].xKnockback = 0;
eManager->entities[i][j].yKnockback = 0;
eManager->entities[i][j].xr = 4;
eManager->entities[i][j].yr = 3;
eManager->entities[i][j].canPass = false;
break;
}
}
}
// Day/season Data
fread(&worldData->daytime, sizeof(u16), 1, file);
fread(&worldData->day, sizeof(int), 1, file);
fread(&worldData->season, sizeof(u8), 1, file);
fread(&worldData->rain, sizeof(bool), 1, file);
// Compass Data
fread(worldData->compassData, sizeof(u8), 6*3, file); //x,y of choosen stair and count per level
// Map Data
//Don't write or load dungeon, so only first 5 levels not 6
fread(worldData->map, sizeof(u8), 128*128*5, file); // Map Tile IDs, 128*128*5 bytes = 80KB
fread(worldData->data, sizeof(u8), 128*128*5, file); // Map Tile Data (Damage done to trees/rocks, age of wheat & saplings, etc). 80KB
fclose(file);
}
void loadPlayerInternal(char *filename, PlayerData *player, EntityManager *eManager) {
FILE * file = fopen(filename, "rb"); //TODO: should be checked
int i;
//read savefile version
int version;
fread(&version, sizeof(int), 1, file);
// basic player info
fread(&player->score, sizeof(int), 1, file);
fread(&player->isSpawned, sizeof(bool), 1, file);
fread(&player->entity.p.hasWonSaved, sizeof(bool), 1, file);
fread(&player->entity.p.health, sizeof(s16), 1, file);
fread(&player->entity.x, sizeof(s16), 1, file);
fread(&player->entity.y, sizeof(s16), 1, file);
fread(&player->entity.level, sizeof(s8), 1, file);
loadInventory(&(player->inventory), eManager, file);
// Sprite info
fread(&(player->sprite.choosen), sizeof(bool), 1, file);
fread(&(player->sprite.legs), sizeof(u8), 1, file);
fread(&(player->sprite.body), sizeof(u8), 1, file);
fread(&(player->sprite.arms), sizeof(u8), 1, file);
fread(&(player->sprite.head), sizeof(u8), 1, file);
fread(&(player->sprite.eyes), sizeof(u8), 1, file);
// Minimap Data
fread(player->minimapData, sizeof(u8), 128*128, file); // Minimap, visibility data 16KB
// Quest Data
fread(&(player->questManager.size), sizeof(int), 1, file);
for(i = 0; i < player->questManager.size; ++i) {
fread(&(player->questManager.questlines[i].currentQuest), sizeof(int), 1, file);
fread(&(player->questManager.questlines[i].currentQuestDone), sizeof(bool), 1, file);
}
//Potion Data
fread(&UnderStrengthEffect, sizeof(bool), 1, file);
fread(&UnderSpeedEffect, sizeof(bool), 1, file);
fread(&regening, sizeof(bool), 1, file);
fread(&UnderSwimBreathEffect, sizeof(bool), 1, file);
fread(&player->entity.p.strengthTimer, sizeof(int), 1, file);
fread(&player->entity.p.speedTimer, sizeof(int), 1, file);
fread(&player->entity.p.swimBreathTimer, sizeof(int), 1, file);
fread(&player->entity.p.regenTimer, sizeof(int), 1, file);
fclose(file);
}
bool saveWorld(char *filename, EntityManager *eManager, WorldData *worldData, PlayerData *players, int playerCount) {
//check if old save file exists
bool exists = false;
FILE *testFile = fopen(filename, "rb");
if(testFile) {
fclose(testFile);
exists = true;
}
saveTrackFileReset();
if(exists) {
//create backup copy
char *filenameBackup = malloc(sizeof(filename)+4+1);
if(filenameBackup==NULL) {
return false;
}
strcpy(filenameBackup, filename);
strcat(filenameBackup, ".bak");
if(!saveFileCopy(filenameBackup, filename)) {
return false;
}
//extract files and keep track of references
if(unzipAndLoad(filename, &saveTrackFile, SAVE_COMMENT, ZIPHELPER_KEEP_FILES)!=0) {
saveDeleteTrackedFiles();
return false;
}
remove(filename);
}
//save world data
saveWorldInternal("main.wld", eManager, worldData);
saveTrackFile("main.wld");
//save player data of active players
for(int i=0; i<playerCount; i++) {
char playerFilename[50];
playerFilename[0] = '\0';
sprintf(playerFilename, "%lu.plr", players[i].id);
savePlayerInternal(playerFilename, players+i, eManager);
saveTrackFile(playerFilename);
}
//zip all tracked files
if(zipFiles(filename, files, fileCount, ZIPHELPER_REPLACE, SAVE_COMMENT)!=0) {
remove(filename);
}
saveDeleteTrackedFiles();
return true;
}
bool loadHadWorld;
EntityManager *loadEManager;
WorldData *loadWorldData;
PlayerData *loadPlayers;
int loadPlayerCount;
int loadFile(char *filename) {
//load world
if(strcmp(filename, "main.wld")==0) {
loadWorldInternal(filename, loadEManager, loadWorldData);
loadHadWorld = true;
}
//load player data of active players
for(int i=0; i<playerCount; i++) {
char playerFilename[50];
playerFilename[0] = '\0';
sprintf(playerFilename, "%lu.plr", players[i].id);
if(strcmp(filename, playerFilename)==0) {
loadPlayerInternal(filename, loadPlayers+i, loadEManager);
}
}
return 0;
}
bool loadWorld(char *filename, EntityManager *eManager, WorldData *worldData, PlayerData *players, int playerCount) {
//check if save file exists
bool exists = false;
FILE *testFile = fopen(filename, "rb");
if(testFile) {
fclose(testFile);
exists = true;
}
if(!exists) return false;
loadHadWorld = false;
loadEManager = eManager;
loadWorldData = worldData;
loadPlayers = players;
loadPlayerCount = playerCount;
//extract files
if(unzipAndLoad(filename, &loadFile, SAVE_COMMENT, ZIPHELPER_CLEANUP_FILES)!=0) {
return false;
}
if(!loadHadWorld) {
return false;
}
return true;
}

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

@ -2,7 +2,16 @@
#include <stdio.h>
#include <string.h>
#include "Entity.h"
#include "Player.h"
#include "Globals.h"
void saveCurrentWorld(char * filename, EntityManager * eManager, Entity * player, u8 * map, u8 * mapData);
int loadWorld(char * filename, EntityManager * eManager, Entity * player, u8 * map, u8 * mapData);
#define SAVE_VERSION 1
#define SAVE_COMMENT "Minicraft3DSSave"
#define SAVE_COPYBUFFER_SIZE 4096
//void saveCurrentWorld(char * filename, EntityManager * eManager, Entity * player, u8 * map, u8 * mapData, WorldData *worldData);
//int loadWorld(char * filename, EntityManager * eManager, Entity * player, u8 * map, u8 * mapData, WorldData *worldData);
bool saveWorld(char *filename, EntityManager *eManager, WorldData *worldData, PlayerData *players, int playerCount);
bool loadWorld(char *filename, EntityManager *eManager, WorldData *worldData, PlayerData *players, int playerCount);

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

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

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

@ -6,15 +6,19 @@
typedef struct {
u32 size;
u8 * buffer;
u32 size;
u8 * buffer;
} Sound;
void loadSound(Sound * snd, char * filename);
void playSound(Sound snd);
void playMusic(Sound snd);
void playSoundPositioned(Sound snd, s8 level, int x, int y);
void setListenerPosition(s8 level, int x, int y);
void playMusic(Sound *snd);
void updateMusic(int lvl, int time);
void stopMusic();
void loadSounds();
void freeSounds();

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

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

@ -12,159 +12,143 @@
#include "MapGen.h"
#include "Menu.h"
#include "texturepack.h"
#include "Network.h"
#include "SaveLoad.h"
#include "Ingame.h"
#include "Player.h"
#include "Synchronizer.h"
#include "PacketHandler.h"
void initMiniMap(bool loadUpWorld) {
int i;
for (i = 0; i < 5; ++i) {
initMinimapLevel(i, loadUpWorld);
}
}
// TODO: Dungeon is way to difficult
// -> Skeleton arrows are slower, do a little less damage
// -> Or instead of less damage, implement a simple armor system
void initNewMap() {
newSeed();
createAndValidateSkyMap(128, 128, map[0], data[0]);
createAndValidateTopMap(128, 128, map[1], data[1]);
createAndValidateUndergroundMap(128, 128, 1, map[2], data[2]);
createAndValidateUndergroundMap(128, 128, 2, map[3], data[3]);
createAndValidateUndergroundMap(128, 128, 3, map[4], data[4]);
}
//TODO: Something still causes desyncs very rarely
void setupGame(bool loadUpWorld) {
currentLevel = 1;
static aptHookCookie cookie;
// Reset entity manager.
memset(&eManager, 0, sizeof(eManager));
sf2d_set_clear_color(0xFF6C6D82); //sf2d_set_clear_color(RGBA8(0x82, 0x6D, 0x6C, 0xFF));
void setupGame() {
synchronizerInit(rand(), 1, 0);
synchronizerSetPlayerUID(0, localUID);
synchronizerStart();
if (!loadUpWorld) {
initNewMap();
initPlayer();
airWizardHealthDisplay = 2000;
int i;
for (i = 0; i < 5; ++i) {
trySpawn(500, i);
}
addEntityToList(newAirWizardEntity(630, 820, 0), &eManager);
daytime = 6000;
} else {
initPlayer();
loadWorld(currentFileName, &eManager, &player, (u8*) map, (u8*) data);
}
updateMusic(currentLevel, daytime);
initMiniMap(loadUpWorld);
shouldRenderMap = false;
mScrollX = 0;
mScrollY = 0;
zoomLevel = 2;
sprintf(mapText,"x%d",zoomLevel);
initGame = 0;
}
int xscr = 0, yscr = 0;
void tick() {
if (player.p.isDead) {
if (player.p.endTimer < 1) {
currentMenu = MENU_LOSE;
}
--player.p.endTimer;
return;
} else if (player.p.hasWon) {
if (player.p.endTimer < 1) {
currentMenu = MENU_WIN;
}
--player.p.endTimer;
return;
}
void setupGameServer() {
size_t size;
tickTouchMap();
tickTouchQuickSelect();
networkHostStopConnections();
++daytime;
//daytime += 20;
if(daytime>=24000) {
daytime -= 24000;
}
if(daytime==6000 && currentLevel==1) {
playMusic(music_floor1);
} else if(daytime==19000 && currentLevel==1) {
playMusic(music_floor1_night);
}
networkStart();
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;
//send start info (seed)
size = writeStartPacket(networkWriteBuffer, rand());
networkSend(networkWriteBuffer, size);
processPacket(networkWriteBuffer, size);
networkSendWaitFlush();
if(eManager.lastSlot[currentLevel]<80) {
trySpawn(1, currentLevel);
}
//send save file if loading
FILE *file = fopen(currentFileName, "rb");
if(file!=NULL) {
sendFile(file, 0, 0);
networkSendWaitFlush();
fclose(file);
}
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);
}
//send start command
size = writeStartRequestPacket(networkWriteBuffer);
networkSend(networkWriteBuffer, size);
processPacket(networkWriteBuffer, size);
initMPGame = 0;
}
void clearScreen(int* data, u8 fill, int size) {
int i;
for (i = 0; i < size / 4; ++i)
data[i] = 0xFF000000;
void setupBGMap() {
// Reset entity manager.
memset(&eManager, 0, sizeof(eManager));
sf2d_set_clear_color(0xFF6C6D82);
srand(time(NULL));
createAndValidateTopMap(128, 128, 1, worldData.map[1], worldData.data[1]);
// Reset entity manager.
memset(&eManager, 0, sizeof(eManager));
sf2d_set_clear_color(0xFF6C6D82);
initBGMap = 0;
}
static void task_apt_hook(APT_HookType hook, void* param) {
switch(hook) {
case APTHOOK_ONSUSPEND:
paused = true;
stopMusic();
break;
default:
break;
}
}
//for rendering -> move to a better place
extern int xscr, yscr;
char debugText[34];
char bossHealthText[34];
int main() {
romfsInit();
cfguInit();
CFGU_GetSystemModel(&MODEL_3DS);
FILE * file;
shouldRenderDebug = true;
shouldRenderDebug = false;
if ((file = fopen("settings.bin", "r"))) {
fread(&shouldRenderDebug,sizeof(bool),1,file);
fread(&shouldSpeedup,sizeof(bool),1,file);
osSetSpeedupEnable(shouldSpeedup);
fclose(file);
}
sf2d_init();
sf2d_init();
csndInit();
networkInit();
srand(time(NULL));
//load or create localUID
if ((file = fopen("m3ds_uid.bin", "rb"))) {
fread(&localUID, sizeof(u32), 1, file);
fclose(file);
} else {
localUID = (((u32) (rand()%256))<<24) | (((u32) (rand()%256))<<16) | (((u32) (rand()%256))<<8) | (((u32) (rand()%256)));
if ((file = fopen("m3ds_uid.bin", "wb"))) {
fwrite(&localUID, sizeof(u32), 1, file);
fclose(file);
}
}
noItem = newItem(ITEM_NULL, 0);
initMenus();
currentMenu = MENU_TITLE;
currentSelection = 0;
quitGame = false;
initBGMap = 1;
icons = sfil_load_PNG_buffer(icons2_png, SF2D_PLACE_RAM);
icons = sfil_load_PNG_buffer(icons_png, SF2D_PLACE_RAM);
playerSprites = sfil_load_PNG_buffer(player_png, SF2D_PLACE_RAM);
font = sfil_load_PNG_buffer(Font_png, SF2D_PLACE_RAM);
bottombg = sfil_load_PNG_buffer(bottombg_png, SF2D_PLACE_RAM);
loadSounds();
playMusic(music_menu);
playMusic(&music_menu);
bakeLights();
int i;
for (i = 0; i < 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]);
}
@ -175,33 +159,37 @@ int main() {
sf2d_set_clear_color(0xFF);
/* Default inputs */
k_up.input = KEY_DUP | KEY_CPAD_UP | KEY_CSTICK_UP;
k_down.input = KEY_DDOWN | KEY_CPAD_DOWN | KEY_CSTICK_DOWN;
k_left.input = KEY_DLEFT | KEY_CPAD_LEFT | KEY_CSTICK_LEFT;
k_right.input = KEY_DRIGHT | KEY_CPAD_RIGHT | KEY_CSTICK_RIGHT;
k_attack.input = KEY_A | KEY_B | KEY_L | KEY_ZR;
k_menu.input = KEY_X | KEY_Y | KEY_R | KEY_ZL;
k_pause.input = KEY_START;
k_accept.input = KEY_A;
k_decline.input = KEY_B;
k_delete.input = KEY_X;
k_menuNext.input = KEY_R;
k_menuPrev.input = KEY_L;
localInputs.k_up.input = KEY_DUP | KEY_CPAD_UP | KEY_CSTICK_UP;
localInputs.k_down.input = KEY_DDOWN | KEY_CPAD_DOWN | KEY_CSTICK_DOWN;
localInputs.k_left.input = KEY_DLEFT | KEY_CPAD_LEFT | KEY_CSTICK_LEFT;
localInputs.k_right.input = KEY_DRIGHT | KEY_CPAD_RIGHT | KEY_CSTICK_RIGHT;
localInputs.k_attack.input = KEY_A;
localInputs.k_pickup.input = KEY_B;
localInputs.k_use.input = KEY_X;
localInputs.k_menu.input = KEY_Y ;
localInputs.k_pause.input = KEY_START;
localInputs.k_accept.input = KEY_A;
localInputs.k_decline.input = KEY_B;
localInputs.k_delete.input = KEY_X;
localInputs.k_menuNext.input = KEY_R | KEY_ZR;
localInputs.k_menuPrev.input = KEY_L | KEY_ZL;
/* If btnSave exists, then use that. */
if ((file = fopen("btnSave.bin", "rb"))) {
fread(&k_up.input, sizeof(int), 1, file);
fread(&k_down.input, sizeof(int), 1, file);
fread(&k_left.input, sizeof(int), 1, file);
fread(&k_right.input, sizeof(int), 1, file);
fread(&k_attack.input, sizeof(int), 1, file);
fread(&k_menu.input, sizeof(int), 1, file);
fread(&k_pause.input, sizeof(int), 1, file);
fread(&k_accept.input, sizeof(int), 1, file);
fread(&k_decline.input, sizeof(int), 1, file);
fread(&k_delete.input, sizeof(int), 1, file);
fread(&k_menuNext.input, sizeof(int), 1, file);
fread(&k_menuPrev.input, sizeof(int), 1, file);
fread(&(localInputs.k_up.input), sizeof(int), 1, file);
fread(&(localInputs.k_down.input), sizeof(int), 1, file);
fread(&(localInputs.k_left.input), sizeof(int), 1, file);
fread(&(localInputs.k_right.input), sizeof(int), 1, file);
fread(&(localInputs.k_attack.input), sizeof(int), 1, file);
fread(&(localInputs.k_pickup.input), sizeof(int), 1, file);
fread(&(localInputs.k_use.input), sizeof(int), 1, file);
fread(&(localInputs.k_menu.input), sizeof(int), 1, file);
fread(&(localInputs.k_pause.input), sizeof(int), 1, file);
fread(&(localInputs.k_accept.input), sizeof(int), 1, file);
fread(&(localInputs.k_decline.input), sizeof(int), 1, file);
fread(&(localInputs.k_delete.input), sizeof(int), 1, file);
fread(&(localInputs.k_menuNext.input), sizeof(int), 1, file);
fread(&(localInputs.k_menuPrev.input), sizeof(int), 1, file);
fclose(file);
}
@ -213,54 +201,26 @@ int main() {
fclose(file);
}
tickCount = 0;
initPlayers();
initRecipes();
initTrades();
aptHook(&cookie, task_apt_hook, NULL);
while (aptMainLoop()) {
++tickCount;
hidScanInput();
tickKeys(hidKeysHeld(), hidKeysDown());
if (paused == true) playMusic(&music_menu);
if (quitGame) break;
if (initGame > 0) setupGame(initGame == 1 ? true : false);
if (initGame > 0 && --initGame==0) setupGame();
if (initMPGame > 0 && --initMPGame==0) setupGameServer();
if (initBGMap > 0 && --initBGMap==0) setupBGMap();
if (currentMenu == 0) {
tick();
sf2d_start_frame(GFX_TOP, GFX_LEFT);
offsetX = xscr;
offsetY = yscr;
sf2d_draw_rectangle(0, 0, 400, 240, 0xFF0C0C0C); //RGBA8(12, 12, 12, 255)); //You might think "real" black would be better, but it actually looks better that way
renderLightsToStencil(false, false, true);
renderBackground(xscr, yscr);
renderEntities(player.x, player.y, &eManager);
renderPlayer();
resetStencilStuff();
renderDayNight();
offsetX = 0;
offsetY = 0;
if(shouldRenderDebug){
sprintf(fpsstr, " FPS: %.0f, X:%d, Y:%d, E:%d", sf2d_get_fps(), player.x, player.y, eManager.lastSlot[currentLevel]);
drawText(fpsstr, 2, 225);
}
sf2d_end_frame();
sf2d_start_frame(GFX_BOTTOM, GFX_LEFT);
if(!shouldRenderMap){
sf2d_draw_texture(bottombg, 0, 0);
renderGui();
} else {
renderZoomedMap();
}
sf2d_end_frame();
if (currentMenu == MENU_NONE) {
tickGame();
renderGame();
} else {
//input scanning ingame is handled by the synchronizer
hidScanInput();
tickKeys(&localInputs, hidKeysHeld(), hidKeysDown());
tickMenu(currentMenu);
renderMenu(currentMenu, xscr, yscr);
}
@ -268,7 +228,11 @@ int main() {
sf2d_swapbuffers();
}
stopMusic();
freeTrades();
freeRecipes();
freePlayers();
freeLightBakes();
sf2d_free_texture(icons);
@ -279,8 +243,10 @@ int main() {
sf2d_free_texture(minimap[4]);
sf2d_free_texture(minimap[5]);
freeSounds();
networkExit();
csndExit();
cfguExit();
romfsExit();
sf2d_fini();
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
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

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
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>
@ -141,8 +141,7 @@ long ZCALLBACK fseek_file_func (opaque, stream, offset, origin)
default: return -1;
}
ret = 0;
if (fseek((FILE *)stream, offset, fseek_origin) != 0)
ret = -1;
fseek((FILE *)stream, offset, fseek_origin);
return ret;
}

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

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

@ -1,7 +1,7 @@
/* unzip.c -- IO for uncompress .zip files using zlib
Version 1.01h, December 28th, 2009
Version 1.01e, February 12th, 2005
Copyright (C) 1998-2009 Gilles Vollant
Copyright (C) 1998-2005 Gilles Vollant
Read unzip.h for more info
*/
@ -103,9 +103,6 @@ typedef struct
{
char *read_buffer; /* internal buffer for compressed data */
z_stream stream; /* zLib stream structure for inflate */
#ifdef HAVE_BZIP2
bz_stream bstream; /* bzLib stream structure for bziped */
#endif
uLong pos_in_zipfile; /* position in byte on the zipfile, for fseek*/
uLong stream_initialised; /* flag set if stream structure is initialised*/
@ -207,7 +204,7 @@ local int unzlocal_getShort (pzlib_filefunc_def,filestream,pX)
uLong *pX;
{
uLong x ;
int i = 0;
int i;
int err;
err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i);
@ -235,7 +232,7 @@ local int unzlocal_getLong (pzlib_filefunc_def,filestream,pX)
uLong *pX;
{
uLong x ;
int i = 0;
int i;
int err;
err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i);
@ -494,11 +491,8 @@ extern unzFile ZEXPORT unzOpen2 (path, pzlib_filefunc_def)
s=(unz_s*)ALLOC(sizeof(unz_s));
if (s!=NULL)
{
*s=us;
unzGoToFirstFile((unzFile)s);
}
*s=us;
unzGoToFirstFile((unzFile)s);
return (unzFile)s;
}
@ -614,12 +608,10 @@ local int unzlocal_GetCurrentFileInfoInternal (file,
/* we check the magic */
if (err==UNZ_OK)
{
if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK)
err=UNZ_ERRNO;
else if (uMagic!=0x02014b50)
err=UNZ_BADZIPFILE;
}
if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK)
err=UNZ_ERRNO;
@ -696,13 +688,10 @@ local int unzlocal_GetCurrentFileInfoInternal (file,
uSizeRead = extraFieldBufferSize;
if (lSeek!=0)
{
if (ZSEEK(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0)
lSeek=0;
else
err=UNZ_ERRNO;
}
if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0))
if (ZREAD(s->z_filefunc, s->filestream,extraField,uSizeRead)!=uSizeRead)
err=UNZ_ERRNO;
@ -724,13 +713,10 @@ local int unzlocal_GetCurrentFileInfoInternal (file,
uSizeRead = commentBufferSize;
if (lSeek!=0)
{
if (ZSEEK(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0)
lSeek=0;
else
err=UNZ_ERRNO;
}
if ((file_info.size_file_comment>0) && (commentBufferSize>0))
if (ZREAD(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead)
err=UNZ_ERRNO;
@ -991,12 +977,10 @@ local int unzlocal_CheckCurrentFileCoherencyHeader (s,piSizeVar,
if (err==UNZ_OK)
{
if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK)
err=UNZ_ERRNO;
else if (uMagic!=0x04034b50)
err=UNZ_BADZIPFILE;
}
if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK)
err=UNZ_ERRNO;
@ -1013,9 +997,6 @@ local int unzlocal_CheckCurrentFileCoherencyHeader (s,piSizeVar,
err=UNZ_BADZIPFILE;
if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) &&
/* #ifdef HAVE_BZIP2 */
(s->cur_file_info.compression_method!=Z_BZIP2ED) &&
/* #endif */
(s->cur_file_info.compression_method!=Z_DEFLATED))
err=UNZ_BADZIPFILE;
@ -1130,9 +1111,6 @@ extern int ZEXPORT unzOpenCurrentFile3 (file, method, level, raw, password)
}
if ((s->cur_file_info.compression_method!=0) &&
/* #ifdef HAVE_BZIP2 */
(s->cur_file_info.compression_method!=Z_BZIP2ED) &&
/* #endif */
(s->cur_file_info.compression_method!=Z_DEFLATED))
err=UNZ_BADZIPFILE;
@ -1146,34 +1124,6 @@ extern int ZEXPORT unzOpenCurrentFile3 (file, method, level, raw, password)
pfile_in_zip_read_info->stream.total_out = 0;
if ((s->cur_file_info.compression_method==Z_BZIP2ED) &&
(!raw))
{
#ifdef HAVE_BZIP2
pfile_in_zip_read_info->bstream.bzalloc = (void *(*) (void *, int, int))0;
pfile_in_zip_read_info->bstream.bzfree = (free_func)0;
pfile_in_zip_read_info->bstream.opaque = (voidpf)0;
pfile_in_zip_read_info->bstream.state = (voidpf)0;
pfile_in_zip_read_info->stream.zalloc = (alloc_func)0;
pfile_in_zip_read_info->stream.zfree = (free_func)0;
pfile_in_zip_read_info->stream.opaque = (voidpf)0;
pfile_in_zip_read_info->stream.next_in = (voidpf)0;
pfile_in_zip_read_info->stream.avail_in = 0;
err=BZ2_bzDecompressInit(&pfile_in_zip_read_info->bstream, 0, 0);
if (err == Z_OK)
pfile_in_zip_read_info->stream_initialised=Z_BZIP2ED;
else
{
TRYFREE(pfile_in_zip_read_info);
return err;
}
#else
pfile_in_zip_read_info->raw=1;
#endif
}
else
if ((s->cur_file_info.compression_method==Z_DEFLATED) &&
(!raw))
{
@ -1185,7 +1135,7 @@ extern int ZEXPORT unzOpenCurrentFile3 (file, method, level, raw, password)
err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS);
if (err == Z_OK)
pfile_in_zip_read_info->stream_initialised=Z_DEFLATED;
pfile_in_zip_read_info->stream_initialised=1;
else
{
TRYFREE(pfile_in_zip_read_info);
@ -1213,8 +1163,6 @@ extern int ZEXPORT unzOpenCurrentFile3 (file, method, level, raw, password)
s->pfile_in_zip_read = pfile_in_zip_read_info;
s->encrypted = 0;
# ifndef NOUNCRYPT
if (password != NULL)
{
@ -1386,53 +1334,6 @@ extern int ZEXPORT unzReadCurrentFile (file, buf, len)
iRead += uDoCopy;
}
else
if (pfile_in_zip_read_info->compression_method==Z_BZIP2ED)
{
#ifdef HAVE_BZIP2
uLong uTotalOutBefore,uTotalOutAfter;
const Bytef *bufBefore;
uLong uOutThis;
pfile_in_zip_read_info->bstream.next_in = pfile_in_zip_read_info->stream.next_in;
pfile_in_zip_read_info->bstream.avail_in = pfile_in_zip_read_info->stream.avail_in;
pfile_in_zip_read_info->bstream.total_in_lo32 = pfile_in_zip_read_info->stream.total_in;
pfile_in_zip_read_info->bstream.total_in_hi32 = 0;
pfile_in_zip_read_info->bstream.next_out = pfile_in_zip_read_info->stream.next_out;
pfile_in_zip_read_info->bstream.avail_out = pfile_in_zip_read_info->stream.avail_out;
pfile_in_zip_read_info->bstream.total_out_lo32 = pfile_in_zip_read_info->stream.total_out;
pfile_in_zip_read_info->bstream.total_out_hi32 = 0;
uTotalOutBefore = pfile_in_zip_read_info->bstream.total_out_lo32;
bufBefore = pfile_in_zip_read_info->bstream.next_out;
err=BZ2_bzDecompress(&pfile_in_zip_read_info->bstream);
uTotalOutAfter = pfile_in_zip_read_info->bstream.total_out_lo32;
uOutThis = uTotalOutAfter-uTotalOutBefore;
pfile_in_zip_read_info->crc32 =
crc32(pfile_in_zip_read_info->crc32,bufBefore,
(uInt)(uOutThis));
pfile_in_zip_read_info->rest_read_uncompressed -=
uOutThis;
iRead += (uInt)(uTotalOutAfter - uTotalOutBefore);
pfile_in_zip_read_info->stream.next_in = pfile_in_zip_read_info->bstream.next_in;
pfile_in_zip_read_info->stream.avail_in = pfile_in_zip_read_info->bstream.avail_in;
pfile_in_zip_read_info->stream.total_in = pfile_in_zip_read_info->bstream.total_in_lo32;
pfile_in_zip_read_info->stream.next_out = pfile_in_zip_read_info->bstream.next_out;
pfile_in_zip_read_info->stream.avail_out = pfile_in_zip_read_info->bstream.avail_out;
pfile_in_zip_read_info->stream.total_out = pfile_in_zip_read_info->bstream.total_out_lo32;
if (err==BZ_STREAM_END)
return (iRead==0) ? UNZ_EOF : iRead;
if (err!=BZ_OK)
break;
#endif
}
else
{
uLong uTotalOutBefore,uTotalOutAfter;
const Bytef *bufBefore;
@ -1611,12 +1512,8 @@ extern int ZEXPORT unzCloseCurrentFile (file)
TRYFREE(pfile_in_zip_read_info->read_buffer);
pfile_in_zip_read_info->read_buffer = NULL;
if (pfile_in_zip_read_info->stream_initialised == Z_DEFLATED)
if (pfile_in_zip_read_info->stream_initialised)
inflateEnd(&pfile_in_zip_read_info->stream);
#ifdef HAVE_BZIP2
else if (pfile_in_zip_read_info->stream_initialised == Z_BZIP2ED)
BZ2_bzDecompressEnd(&pfile_in_zip_read_info->bstream);
#endif
pfile_in_zip_read_info->stream_initialised = 0;
TRYFREE(pfile_in_zip_read_info);
@ -1637,6 +1534,7 @@ extern int ZEXPORT unzGetGlobalComment (file, szComment, uSizeBuf)
char *szComment;
uLong uSizeBuf;
{
int err=UNZ_OK;
unz_s* s;
uLong uReadThis ;
if (file==NULL)
@ -1669,7 +1567,7 @@ extern uLong ZEXPORT unzGetOffset (file)
unz_s* s;
if (file==NULL)
return 0;
return UNZ_PARAMERROR;
s=(unz_s*)file;
if (!s->current_file_ok)
return 0;

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

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

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

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

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

@ -1,7 +1,7 @@
/* zip.h -- IO for compress .zip files using zlib
Version 1.01h, December 28th, 2009
Version 1.01e, February 12th, 2005
Copyright (C) 1998-2009 Gilles Vollant
Copyright (C) 1998-2005 Gilles Vollant
This unzip package allow creates .ZIP file, compatible with PKZip 2.04g
WinZip, InfoZip tools and compatible.
@ -191,7 +191,8 @@ extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file,
int memLevel,
int strategy,
const char* password,
uLong crcForCrypting));
uLong crcForCtypting));
/*
Same than zipOpenNewFileInZip2, except
windowBits,memLevel,,strategy : see parameter strategy in deflateInit2
@ -199,29 +200,6 @@ extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file,
crcForCtypting : crc of file to compress (needed for crypting)
*/
extern int ZEXPORT zipOpenNewFileInZip4 OF((zipFile file,
const char* filename,
const zip_fileinfo* zipfi,
const void* extrafield_local,
uInt size_extrafield_local,
const void* extrafield_global,
uInt size_extrafield_global,
const char* comment,
int method,
int level,
int raw,
int windowBits,
int memLevel,
int strategy,
const char* password,
uLong crcForCrypting,
uLong versionMadeBy,
uLong flagBase));
/*
Same than zipOpenNewFileInZip4, except
versionMadeBy : value for Version made by field
flag : value for flag field (compression level info will be added)
*/
extern int ZEXPORT zipWriteInFileInZip OF((zipFile file,
const void* buf,

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

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

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

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