From c1aa58c0845550983d27e9cc6faa8aaf68d7c4d2 Mon Sep 17 00:00:00 2001 From: Shamus Hammons Date: Fri, 6 May 2016 19:07:36 -0500 Subject: [PATCH 1/1] Initial commit for the Legend of A... project! --- .gitignore | 5 + makefile | 189 ++++++++++++++++++++++ res/g2-sprites1.png | Bin 0 -> 6744 bytes res/g2-sprites1.xcf | Bin 0 -> 28984 bytes res/g2-tiles1.png | Bin 0 -> 4447 bytes res/g2-tiles1.xcf | Bin 0 -> 24419 bytes res/g2-wizard.png | Bin 0 -> 12500 bytes res/spritemap1.png | Bin 0 -> 1037 bytes res/spritemap1.xcf | Bin 0 -> 1701 bytes res/test1.tmx | 11 ++ res/test2.tmx | 25 +++ res/tilemap1.png | Bin 0 -> 1520 bytes res/tilemap1.xcf | Bin 0 -> 4234 bytes src/data.cpp | 133 ++++++++++++++++ src/data.h | 29 ++++ src/legend.cpp | 372 ++++++++++++++++++++++++++++++++++++++++++++ src/legend.h | 42 +++++ src/log.cpp | 69 ++++++++ src/log.h | 22 +++ src/sound.cpp | 253 ++++++++++++++++++++++++++++++ src/sound.h | 29 ++++ src/sprite.cpp | 194 +++++++++++++++++++++++ src/sprite.h | 20 +++ src/tile.cpp | 170 ++++++++++++++++++++ src/tile.h | 19 +++ src/video.cpp | 113 ++++++++++++++ src/video.h | 28 ++++ 27 files changed, 1723 insertions(+) create mode 100644 .gitignore create mode 100644 makefile create mode 100644 res/g2-sprites1.png create mode 100644 res/g2-sprites1.xcf create mode 100644 res/g2-tiles1.png create mode 100644 res/g2-tiles1.xcf create mode 100644 res/g2-wizard.png create mode 100644 res/spritemap1.png create mode 100644 res/spritemap1.xcf create mode 100644 res/test1.tmx create mode 100644 res/test2.tmx create mode 100644 res/tilemap1.png create mode 100644 res/tilemap1.xcf create mode 100644 src/data.cpp create mode 100644 src/data.h create mode 100644 src/legend.cpp create mode 100644 src/legend.h create mode 100644 src/log.cpp create mode 100644 src/log.h create mode 100644 src/sound.cpp create mode 100644 src/sound.h create mode 100644 src/sprite.cpp create mode 100644 src/sprite.h create mode 100644 src/tile.cpp create mode 100644 src/tile.h create mode 100644 src/video.cpp create mode 100644 src/video.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5c06376 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +obj/ +pix/ +gmon.out +legend +legend.log diff --git a/makefile b/makefile new file mode 100644 index 0000000..ff9dfcd --- /dev/null +++ b/makefile @@ -0,0 +1,189 @@ +# +# Unified Makefile for Apple 2 SDL +# +# by James Hammons +# (C) 2014 Underground Software +# This software is licensed under the GPL v3 +# + +FIND = find +FINDSDL2 := $(shell which $(CROSS)sdl2-config 2> /dev/null) + +# Figure out which system we're compiling for, and set the appropriate variables + +ifeq "$(CROSS)" "" +OSTYPE := $(shell uname -a) + +# Win32 +ifeq "$(findstring Msys,$(OSTYPE))" "Msys" + +SYSTYPE = __GCCWIN32__ +EXESUFFIX = .exe +ICON = obj/icon.o +SDLLIBTYPE = --libs +MSG = Win32 on MinGW + +# Should catch both 'darwin' and 'darwin7.0' +else ifeq "$(findstring Darwin,$(OSTYPE))" "Darwin" + +SYSTYPE = __GCCUNIX__ -D_OSX_ +EXESUFFIX = +ICON = +SDLLIBTYPE = --static-libs +MSG = Mac OS X + +# *nix +else ifeq "$(findstring Linux,$(OSTYPE))" "Linux" + +SYSTYPE = __GCCUNIX__ +EXESUFFIX = +ICON = +SDLLIBTYPE = --libs +MSG = generic Unix/Linux + +# Throw error, unknown OS +else + +$(error OS TYPE UNDETECTED) + +endif +# Cross compile using MXE under Linux host +else + +SYSTYPE = __GCCWIN32__ +EXESUFFIX = .exe +ICON = obj/icon.o +SDLLIBTYPE = --libs +MSG = Win32 under MXE (cross compile) + +endif + +CC = $(CROSS)gcc +LD = $(CROSS)gcc +TARGET = legend + +SDL_CFLAGS = `$(CROSS)sdl2-config --cflags` +SDL_LIBS = `$(CROSS)sdl2-config $(SDLLIBTYPE)` -lSDL2_image +DEFINES = -D$(SYSTYPE) +GCC_DEPS = -MMD + +# Note that we use optimization level 2 instead of 3--3 doesn't seem to gain much over 2 +#CFLAGS = -MMD -Wall -Wno-switch -O2 -D$(SYSTYPE) -ffast-math -fomit-frame-pointer `sdl2-config --cflags` +#CPPFLAGS = -MMD -Wall -Wno-switch -Wno-non-virtual-dtor -O2 -D$(SYSTYPE) \ +# No optimization and w/gcov flags, so that we get an accurate picture from gcov +#CFLAGS = -MMD -Wall -Wno-switch -D$(SYSTYPE) \ +# -ffast-math -fomit-frame-pointer `sdl2-config --cflags` -fprofile-arcs -ftest-coverage +#CPPFLAGS = -MMD -Wall -Wno-switch -Wno-non-virtual-dtor -D$(SYSTYPE) \ +# -ffast-math -fomit-frame-pointer `sdl2-config --cflags` -fprofile-arcs -ftest-coverage +# No optimization for profiling with gprof... +#CFLAGS = -MMD -Wall -Wno-switch -D$(SYSTYPE) \ +# -ffast-math `sdl2-config --cflags` -pg -g +#CPPFLAGS = -MMD -Wall -Wno-switch -Wno-non-virtual-dtor -D$(SYSTYPE) \ +# -ffast-math `sdl2-config --cflags` -pg -g +# -fomit-frame-pointer `sdl2-config --cflags` -g +# -fomit-frame-pointer `sdl2-config --cflags` -DLOG_UNMAPPED_MEMORY_ACCESSES +CFLAGS = $(GCC_DEPS) -Wall -Wno-switch $(DEFINES) -ffast-math $(SDL_CFLAGS) -pg -g +CPPFLAGS = $(GCC_DEPS) -Wall -Wno-switch -Wno-non-virtual-dtor $(DEFINES) \ + -ffast-math $(SDL_CFLAGS) -pg -g + +LDFLAGS = + +#LIBS = -L/usr/local/lib -L/usr/lib `sdl2-config $(SDLLIBTYPE)` -lstdc++ -lz $(GLLIB) +# Link in the gcov library (for profiling purposes) +#LIBS = -L/usr/local/lib -L/usr/lib `sdl2-config $(SDLLIBTYPE)` -lstdc++ -lz $(GLLIB) -lgcov +# Link in the gprof lib +#LIBS = -L/usr/local/lib -L/usr/lib `sdl2-config $(SDLLIBTYPE)` -lstdc++ -lz $(GLLIB) -pg +#LIBS = -L/usr/local/lib -L/usr/lib $(SDL_LIBS) -lstdc++ -lz $(GLLIB) -pg +LIBS = $(SDL_LIBS) -lstdc++ -lz $(GLLIB) -pg + +#INCS = -I. -I./src -I/usr/local/include -I/usr/include +INCS = -I. -I./src + +OBJS = \ + obj/data.o \ + obj/log.o \ + obj/sound.o \ + obj/sprite.o \ + obj/tile.o \ + obj/video.o \ + obj/legend.o \ + $(ICON) + +all: checkenv message obj $(TARGET)$(EXESUFFIX) + @echo + @echo -e "\033[01;33m***\033[00;32m Looks like it compiled OK... Give it a whirl!\033[00m" + +# Check the compilation environment, barf if not appropriate + +checkenv: + @echo + @echo -en "\033[01;33m***\033[00;32m Checking compilation environment... \033[00m" +ifeq "$(FINDSDL2)" "" + @echo + @echo + @echo -e "\033[01;33mIt seems that you don't have the SDL 2 development libraries installed. If you" + @echo -e "have installed them, make sure that the sdl2-config file is somewhere in your" + @echo -e "path and is executable.\033[00m" + @echo +#Is there a better way to break out of the makefile? + @false; +# @break +# YES! But ignores all the echo's above... :-/ +#$(error SDL2 MISSING) + +else + @echo -e "\033[01;37mOK\033[00m" +endif + +message: + @echo + @echo -e "\033[01;33m***\033[00;32m Building Legend of Ayeron SDL2 for $(MSG)...\033[00m" + @echo + +clean: + @echo -en "\033[01;33m***\033[00;32m Cleaning out the garbage...\033[00m" + @rm -rf obj + @rm -f ./$(TARGET)$(EXESUFFIX) + @echo -e "\033[01;37mdone!\033[00m" + +obj: + @mkdir obj + +# This is only done for Win32 at the moment... + +ifneq "" "$(ICON)" +$(ICON): res/$(TARGET).rc res/$(TARGET).ico + @echo -e "\033[01;33m***\033[00;32m Processing icon...\033[00m" + @$(CROSS)windres -i res/$(TARGET).rc -o $(ICON) --include-dir=./res +endif + +obj/%.o: src/%.c + @echo -e "\033[01;33m***\033[00;32m Compiling $<...\033[00m" + @$(CC) $(CFLAGS) $(INCS) -c $< -o $@ + +obj/%.o: src/%.cpp + @echo -e "\033[01;33m***\033[00;32m Compiling $<...\033[00m" + @$(CC) $(CPPFLAGS) $(INCS) -c $< -o $@ + +#GUI compilation... +obj/%.o: src/gui/%.cpp + @echo -e "\033[01;33m***\033[00;32m Compiling $<...\033[00m" + @$(CC) $(CPPFLAGS) $(INCS) -c $< -o $@ + +$(TARGET)$(EXESUFFIX): $(OBJS) + @echo -e "\033[01;33m***\033[00;32m Linking it all together...\033[00m" + @$(LD) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) +# strip --strip-all vj$(EXESUFFIX) +# upx -9 vj$(EXESUFFIX) + +statistics: + @echo -n "Lines in source files: " + @-$(FIND) ./src -name "*.cpp" | xargs cat | wc -l + @echo -n "Lines in header files: " + @-$(FIND) ./src -name "*.h" | xargs cat | wc -l + +# Pull in dependencies autogenerated by gcc's -MMD switch +# The "-" in front in there just in case they haven't been created yet + +-include obj/*.d + diff --git a/res/g2-sprites1.png b/res/g2-sprites1.png new file mode 100644 index 0000000000000000000000000000000000000000..af3c511071a00fb90e90994bb176ced4d3acb4f4 GIT binary patch literal 6744 zcmd5=Wl&sAo1MWO-rx|zBqR_#SRhDXaCdiy;DG^xWCjZmoZuQn;oj)#MO z!kJ}vLpS(t@&+CN0QnsH4hNcQ*8R=Kg(#@V;I87)k?_#)XQZ(M0JK*M(qQcmi$__m z;YM>wT@#dAcw_KQZp3r*t-N@8Z^1gqK;Rgz-_xbZ(Ps_HK`EXG6wK=(@)Ru9x!DV2 zhHB)sUzDgc>+DB<#<7+rcv*$Nd$FIKXf1LbM=|jn((|+CMR&O5uf^&ogCW&M{#U&- zKEfyatGAW+V_*L~rjdnBv|f2nyt!KTSo6d9E+eDJx}i(WfOXm63}n;NPpHq;RQz0J zL*Q6TF2<~`;oC^@a?yN&;t=#n#>^WLfZ$8(Nl)yT-s2|NFVg?KXh6WaXI+Q6oo1~@ ztY=XYpx)_V!e%{Hx{%z50t`CDalQ_kquTL`tlaGVT%&7cdmu?gUhDo8Z5yKh+hjvA zKRxc#kers?5t0`GwU>&WQ!n#i>MWd;6EuTvuk za6KnCaTp#+f*vPq>u~i2#b^obgGw=J>e5PX=o;vpIPMof7R6+E0-GbB^yxFpBXl66@ zHT{gvq8sr!aWA!~^IgI)>G+zZhWg<--j>Q4BB_sL(~HePx?0fmgGqyMuLzv?E165r&5@;eZA0-7JX|e?gDV zmW*RKzGS7)!Nu&ES7y#7)10Rxbpk7;wE+A+=^vMnDRy7J7~+mOD)Z4A+WFS7oeI^% zcv7Cns#djk<${IV^(m8DVVI@V+@EY&;U>*wgvRVfP61?+^3NKrNgt5!D=(YRl{Jgd zHvG+N*`7!)0V=QtCyMhou#@e%=gs(%&Q@itid2X=e^}_%_S!25w+Su|n)~Rb1fKR> zy=-tH^w@Va!jWxShwoc9&5oFkR2x-tiS6Zyomdk|NW<%$j9TZ5rd$q&G|G-mFUy)W zw8bsCBEedl`9(V=lD>&hBm4#}jXCocN@xLgY1&I2iUw#+bdaac>Zq*>ZIxk{oC)&} zN%pCRL9EYE-C1rYH)Agx$SHh44#?Z?8k8>*QwgyMF7Q(;7T>W1qQYNfXNwrK?jjfb-4VDn3oQ(^Nwc-aY zVNi>GR|?Py)GDhMFvacL{$SboL_c&~GLI-nG$GM}L6N?|Qy1L1PFD;Gd_-?SZ(#B{ z$dJ9mHInZe)M!%HgjuYoP}VUx|Fu3ft}ho0%}rQPRRlY9prDkt{l00}jr)@hl#0PS$}t88`J! z8Q5zN=es|(Jf3u3@o7~kU(%mac|!i;Q)vN*@{1;l zKb)IkY^lv2MohAOB{q)Qk@9wX`^zSY@JtU>#NAb2zk z05}sg#D~6q zo_%|7N15%k0uB|m(4zPImSv2J0oUN_Cji477 zxIV$@m^-|83J%y^$NpY}2Z$Tb;BW`=$d5(WOIoUSZp<6QoANf@HkAqCr`_%(Ihivr za(E1shrr)c%f=65_nl+UU=AlLB^)|g=b(^_vocly;Lo2~3V+dz(8Gmmb%`QfX`4SF z335&OlOhhhSpr53R-(C@A!L!E%EgIJiPkV}(ysN*Q^)&7C)CVAJReQaUwIgsMS$e@8tFLiU7OzsseyMo z9J@D>zX=gh~ghK;`-(Jg2jhQo66-ZUB-6J7OcH+ZNrdqW5?!!5?G~kpauYRhhLt(B$$Y!t!v*YDj59}jrZ;L4RD1bzKX$Q$ zYhY%}Y;N#~g5FSoL{lprlA@u4x!uKNBCBl2&x5kiG-v_gzZ#oJ`B3z7-gy{bO}c&xOZ6v z+YY8@$z7`_uHd{7MLWOwWqy~P*j!$OY;Rwe+wh~vHN zo-1c}rkl6siSOMGFWvg)g;nPF*NsRG+b%+k59f`h>=fe6lKGXUB75fgS)oJqhM7Mf zPQM_gkm;7UfSX$tL6gZT9TsY3Tj;1G9FhyG~bAaGdn^MI9J~QhG`6>0a3W{}lR0SpHy%Nid zy3>+qv~$XR8phSB9_(LKolGvdMZM#-OQgOQJ z48-Twjqb};bfU{<8@~n) zs||??J!;>)^|KI0zhvyLTD)KC z2)n_=%p<2W8^w!79*JinKsF_C^!!>T#ujX8JwYz0d0s-Z_7r@MvwOgMIN4kn) z63Jykp#)#ek;iS>U2UToXlIobL^W{RIz7i7z=Q&1X-u2vh1)vm030pGPvmO}A?Ls$ zo#bx-^2LQ-t-9TvcCri}hrg2*5%;goyj`6X^?zvG1emX`=zq9nIbE*q?6zC%B(VJH zEF$SxJIconU>T4Y{fXUiY)7P|1jF8-#Ln1ZUKQ~DJ#&^^St*AOW~7$!2p=a; zx?t(35~~M<7hMGJzsBPJh(Ve8_F=Ga!ZYq1A9dwy7Jda{*Z9!3f90(%lzr-%{bn4EU}cH{Q)u`d8mN{qm1jmg6I3*nQ`E;>7GovkT2 zLafgpb`mR0CIq~VWLN~sTq>0#mVfi4y|4Pj+9t?8kpDJOe)e6*QHz0q4b6O^@rNmV zflZfB%coPc>hFA~eVq(7UMgP~)0JVek%K(Ho~UiSBf7zm!z|?vaQX=?GahKL`1K2x zH4s|y`#KP~-|%!bKxE&U8Kky(Wpc#s`~|wFwq6Vj&spg*-rS;!21YWFnRKf3}T>hqPzBNX#&3}nhg=yk6#8mbe zJ|J5zIiME|!zb8R9}9(Pu9A!|?Fbyj4h9{2PxFKfRb`cOsi!yitNIwZ-7Uh(4t9FR znaZ52UL_&9`_uwpn1L7FJ+n+AIjcqcj2Ett0-6#B`<-oxg4nQ72QN4J(3_lRe++1y z5|slI@y%EXo&ZRXr-7%0h^ZU=$9gD-NiO9EDNTqcRj_<_Y2C*{2fH^pWO*hqmC(xs zp{;L>7Umf9-?g2OE@Rbjgy~bh(aeaxcyaoSJpxjN*JWXbIFt4;V?|JeMcwM&A4{p- zAY9xKcF-Gj{|jHhy9S+LD)ll+3pUo>ah=&8-767F;IMJ2Jt(+Oz);KGqJo^d?m#cw zJCD%>e@T8NVoTvE!&alH>6U}33AatQ0dN`OVW3o+qT^Z*ZE4HV{d6F>M*awdE)?jh zc)s`KU^{=xh*<~W`dcLYLg&rL<)$y1iuzQO%L+$Ax4N*qsb=$^RUZq-rmKMSYgyz+ zI$^uF`XzISle4)DWpP;Gbs~P8DCx>WhnVD)OBQltw?th|9C;Ab`eRpVCV`*vzzLG| zJ4yIxDB#ldJCQGX0Iw>98+fUxlI2Th7cEm~g63NEQ5?gJt1jyD*;*^Rx(PeVH2N{N zwn!IRLbk6iC}sBw=Iy(?FXeMU2Laj^lu8A&%{62XPJfbIL&b)N@t}lw5cjR$G7h*M zHJCu=8W_B7XD%ik8!=QF$(K`-6}q@kI^Uc6lKGZ@3CBsKCG}ljJ3^|?y>Q3O?tGCQ zTj(+p`@>O^(*>W>*fSt6Xuh7-xvB3? z(ybMYj?b>H2|;<=cWS11O9ZAQmqfO@Tpf} ziryX?b)U^{Rr+*zK%JDg%=`)gmp`*`pT+}({6B~T00pEc<1<%uFb9ceK(`yDQT6#6 zr8CiMpnLXTP?RdRYW8T0^U`aPnF$gARwwyEl3LaFzGndJZnv52IUoRSr3E?z!Pm^^ zavkyb?XbH)fp=SG1?xkCypSBRgT0nfJp0DVN}1pyx+or(b)M2z`^Po^eJ4iF5m=j@;QU8%&L^iSjvtr*pJ${xpa2%^dJ?%GBaX-4(P*_qA5@tIVQ|zH-03yH`hKT9u z9eLy0yM$6j9L+XA?}$B`YTv~omRtNOpRy9FasZ&EDa6^*FnK=Zc{~%E`~qhISg)@# zEFh%YR2D#t$zMXU2l@7-c7*`3REEjHp?Yr6VOq|8(+{AKLoDgu1Sbe7$;AxKZrl7h zJ;n_d+%$`l+EgRn-Sw+#$2~~l#sqZrw4Q{HFfQr9)Q{&MPJ63i4g|+H>w3BFm`NqO zXiD$U-b?@EE-PoI8k0-h0J?{ZlvnN(Y>QCw&M>a9^**u4g z=*%=A9&S!&bGn6B(Af!+q0qra5V0DCE%DW0-uSub5Y>l2Zy#bZ&rDiD9gZnf(>L2c zRByhD3*jG}JXEW|He+Fj@(*+55A^#N>cu;Y(MB=~ciDb-p%@bR3wx?I))0zSVQ-T4 z<>udiumGFnpc1{1rO5^oJ?aM+2wFk=8-Hmv*Tp) zUIsGd=gTThBRWrpIx#YDSoAyHv1QZaYTD5n%q0V5YPZgWyB8>g9_FiKb}Flo0q7uepqfF4V2=$%!&lCnzoe(OmO z3npBgSJ+`ze2b{KxtUtIRPE~#?evhCn#A_?yO$Mz=DclKP(pHFVIQu7Cy&VbP9CVu zU^6DQRVP7IU}Qz-gB04S zLaS|I@7qq&#lNQ&CSgyXqE4leR9ZG{yF(}nJ%{c~OMjC%X`ynzI9?+S-Bc%M%gT^# zjE--}o9s_p2V)s0v#shFF2gL5J*Vo7!8dsxt90_ykOdyK@hyTw8L{;u^pE28*_qn#CyHkH}g zSqLO%#k}vk+s!2456Xsp)+(Km~!9f%b zs*YmHj{C9c(gaY(w)Je}DzDkO*>KAaa0nnF% MjH+~{q*m761SM literal 0 HcmV?d00001 diff --git a/res/g2-sprites1.xcf b/res/g2-sprites1.xcf new file mode 100644 index 0000000000000000000000000000000000000000..534d787b354d50c195e39ed27a085e8ba4abd70f GIT binary patch literal 28984 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 literal 0 HcmV?d00001 diff --git a/res/g2-tiles1.png b/res/g2-tiles1.png new file mode 100644 index 0000000000000000000000000000000000000000..140b12d6b3cec57938b7dd0d19dad058bca45bc8 GIT binary patch literal 4447 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 literal 0 HcmV?d00001 diff --git a/res/g2-tiles1.xcf b/res/g2-tiles1.xcf new file mode 100644 index 0000000000000000000000000000000000000000..70ee6aae97dfe63ff2a0d7ac08dfe67d71421f49 GIT binary patch literal 24419 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 literal 0 HcmV?d00001 diff --git a/res/g2-wizard.png b/res/g2-wizard.png new file mode 100644 index 0000000000000000000000000000000000000000..eece1304925f2428452d50526d6050e1728f389a GIT binary patch literal 12500 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 literal 0 HcmV?d00001 diff --git a/res/spritemap1.xcf b/res/spritemap1.xcf new file mode 100644 index 0000000000000000000000000000000000000000..cc1260a7623d99b1da58ae68f356053d1cd339b6 GIT binary patch literal 1701 zcmd^;&2G~`5XX1z_4*@?D4!>geDGl_IjEH^Zb%3Y6%v*D0$55@C$-22N`h!4qjIsh z7>O$fF22JXaI(aW6K5WQbJat<{MWH*BFYo6@=yPn*_qi{I~l~Im+on=@Al(iL+uId6j-+a0~WYb+bZui^7o(9IC#L1nL_pTNT)~4Iy{e zY-i1Gt=U^^c6-gf23z%QmI*_hp;q(TXl6l3dr|FN{${RpTKAE z6?_Lj!7s2xejD5a9q4*(5|6M1lS?QUOSn2tD>KPtnIY0xD#xxcGfb9Y$_&#&wTuEw zP$t3L~^ZtI(l(=oDsX0;}Lq4qf?XVTA_Zg$=c#o4$%&qJ=bP zRlcg@RSz1nybKGDL-gD%Xl|f+nTJ|f=7m*UHz!-vpM&p&YvM zP0#WTz&#skLpOaDyF~M8%Bp--$EzOHXKCqs9Ea$s>Cx0c^D^}{zf3*LyGXqh4=n0y z=@J(AmI%<4s=Pp5Iza3^kTOlB43NoEyTS}Ga)9vzTmz~F6rdB729C!NV*xJhRhQnz R*Z$AIpZ9GIggmMtzX5O=r=I`- literal 0 HcmV?d00001 diff --git a/res/test1.tmx b/res/test1.tmx new file mode 100644 index 0000000..bd68357 --- /dev/null +++ b/res/test1.tmx @@ -0,0 +1,11 @@ + + + + + + + + eJzV0ssKABAQheFxCW/g/d/USkmcuZWy+BcWPtMkEFFQlg13pF5b+tGLhzgPvT2NBLyq9NDZur8i3J9kzrJ087rCQ/PlLc7j9mfx0H+pW15POx/XzfP00hxa8gKT + + + diff --git a/res/test2.tmx b/res/test2.tmx new file mode 100644 index 0000000..5baa182 --- /dev/null +++ b/res/test2.tmx @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + eJztmrmO1EAQhgtY7nM5lmuBheVmd5b7RsBCuBI3CQkkQICQCJDIAAlCXgAiIohA4nwJEu77PgMQJOT8LVxyT7lr3J6xZ3q0G/wq2W2P7f6qqsvluU5El6AN0MZ+aM9A16AL0EVoUz/TZmgLNBsaDA3ysOOs7ZFVnheK5eefDrVAQ4SdC82DZkIlaIYy3m6NL49sSVjX7zfa8vPPUeZnBbQS2gptg6Yo42ugtZZl3YHuQq1U2Q/GRvvHUtK/RhXAXfKXfIZDI6BV0GpoO9QLtUXjoyPL4+ug9Za9Al2F7kH3oYkpHMZDEyw7xLrOmAK4S/4c/0OhYTnMv29ekdeT/iDvQ/OTvPgzdzn/HOeu+GZeNjdXXrHHpZ9p/iDvQ/OTPPjbHJgLW45zXjddce2a144U7pKnvG7ReYDvc6bgwFxkHuB10+QDE9dd0EJK5o80/vy7kqe8btF5QOZ/nmfJR5u/HdBOKo/zcaTzl5zlfnl9LS/k5Q8y/nmeJR+er0XQYvq/3reI820+Wl0hOcv98vpaXsjLH2xOPnn4KHSM4jqgD9pNyfpB8ytfjvK6clvzj2r5tyvzvYDK4/s4dJviOmAftIuS64D9u674TeMo/U5ua/5RS/y74s4V37a9Ad2keB1gTrx/akYeGtd65H9X3LnqQ5vDA+gEtASaH/GeYO2fRsl1vBIPjWvR+b9D8HPVcZXWgXPQeYr9SKvz09Z13/xeVP0n46yP4ji293dGvPk86Ue+fLPmBS0f5FX/y3pM8w/mzeel1XtZ+Wn8884DMk/Lesxex0sU9wHOWvt7rG3jD92KLTmuw+8V06PjuF7Q+OedB9K4nYIuU3of7TpV7iNq/YM90F7oAHQSmkVuP0mrC/PI/654Muu9Tx8trY9o3hd6ocmO+d8PHaTK9WJaXZhX/Nc6j/w7Mk61PG/OO2Q9f6tyvDyvqPxf6zxq8avleeNfh6Ej0GmK+0RpdWFR+b/WedTeI33f6+pdDxbNn+PV972u3vUg37fMu1kt1/syv6f1c3lbyw/1Wv+1/n+aNeu36Qdwvd8VbXcrXLW8oOWHeq3/1fKX/QC5rcWx5he+fb+8839W/gsizreovB/A/YFlVN4n0vo9kqtv3y/v/J+Vv8bZdzwUm5U/9/+Y+zTlOPaDpdHxpRo5FWWz8s/KNXQ/8OUv4z3te570gyVUvi40mntW/rVyDNUPfL7/+8S7rx9Ue36j+IfKrWj+Mt5D4+bKT+Y7RXvG8zT+zcb9I/SpivuV/GW8++b5oi3XHdxPNP2qHor7in+hL/S/3lgYjVfDP1TuD6FH0FOHXlPcN2I/mFQl/1Dz9GPoCfTMoTcU/w+B/YD7jPb3S+5DV7P+h27fQu8o2ccy/sB+YvyigyrzbzRnX9tJ8fdGs/0SekXJ/G/2f4DeK+PNyp/jnOOen1/yN/t/Q8+jcfk9OnT+nPc5nzN3jnNen/j5ZXx/pjhP2HmhWfhzXHNfWcvvX6FvVPv632je0kqunyOObeK479APyl6vhM6fn7+o+wuNv6k/51H8nfln9Pz8nXlGZLX7td8D5PfnZuAv6zyTt19Qst7T4vwjJetD17ofKn9Z55l1+wP0i8rrPS3OX4rjtXU/VP61xr+sA7S6IFT+afnf93h+P/xDcf5oRv5Z13Wu9+z84ar7moV/1nXdrvfsuk87P3T+RdvQ+RdtB/gP8B/gP8C/v/K3/8fb3Q/tP5LDTPM= + + + + + + eJzt2E0KgzAQBlAHCv2xPU57/6N15cJg0FKlzeS9zYCryJcJkwwDrHvGr1dwsC//7xTLFVqSvs8PVvb9LZYr/+28U05yz+mxMU+5t8G81jf5923v/O8xr7Sttj/Gynf507JXzOtW2d8PrpX7PblcYl4nU/7lPgD6VZ4T9KGc/2v3AXJZ63fzQU6f9rfzIAf93Ce598m7DgAAAAAALXsDk5UCeA== + + + diff --git a/res/tilemap1.png b/res/tilemap1.png new file mode 100644 index 0000000000000000000000000000000000000000..24d556b6c9e8b98f4bfc9e6d0afa1056bd51ebe7 GIT binary patch literal 1520 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K58911L)MWvCLma zA3R!S@$$mrseOHUf0?yP49r&3S)vuoBbPw)Nf_%HRX?auZ4e(&A!yglFU)c!ri zKWZ0W|NQ;!>0Y_{_wu)I5Bhug?(6g2)p3751uuRc|L@CbxBmaxuN4Z)rY-;1llfI# zHDr2SUHwGIhYzlmaXh@VwK}-6weaQj@LSR%l6!t1PZz&escdJlZ}GcludZEr^hL#t9?fxDA-2dB;casGUZJvAZb@bdH)qDQ0)y|KqEPk~0ZGO%A?me$Rp}(hn7U_O|GXDJI-Tw35@;Gs1 z-aRf6B(cS)LGZEf$A*%}H?GdNUvn|BE~?romu+K9$q)T|znk6tSD#&eU3hwh&4=Uh zQW0kaHRdy>J$SM?ppdV44SWB*XRGbkewA>O>FV-bZ}IIo&m;Mzw?CF|4)|Tx@T#kS zd;3|ZWVwH1K&dq22SY{}G@mg_1lAT`IpEGWYFHb)I>+0mYx3c$T zc8ZJFe|mVFZ-YX3<0|$OEA@ZfihPm7dwbTS*FN4h@^zQgCcL}u5Y2ody#8y|3r_z3 zz0dwhwoF{dB>K4Sj^roa4ZC+r#i#H35j=yJLS&bY6|~jLDN* z9_c>6UiPU_L-y0--02Ns4^=9TJddyb-7I{}fM5Oi*5?dIZ?BPKz4!a0li@>=;)z%7 z)z$vUEcmv0Vd1xS&SMKV+n#xT{r6{0jsFd6w*B96q}!iC=6AIGffw^jrJplimTFIu z^WL8R>u=ZQe{+AYzcabc?ZvUHZ~4pOG9Nr{&)@$iFNP0j*{k!nFD?^YvEz;;q0I__lGzRZCbQ4Dc`#18tl{&#_CgN^HKkieQUl&FC@=^xa4@hiFflMTFgQ?~ vOB+*>+>6Adg-;w9_Oa633j7YgAoriiccD-b+fJV>P*LpZ>gTe~DWM4fZG}-B literal 0 HcmV?d00001 diff --git a/res/tilemap1.xcf b/res/tilemap1.xcf new file mode 100644 index 0000000000000000000000000000000000000000..bd273c3d09f163f98150c23d126a2a9d69db304d GIT binary patch literal 4234 zcmdT{J#QOF8s3=|cS-7lW|+G)LBe8--2jShV8J5Q#VQV0yZ?JY)t}D4 z-#<7xJ?=j2Il^ZA{6j)3+x_A6`X#ZevJUsj1RFbDHEI`jas?fMk9CMEvc8t#ZbZy*R8*i?Sx7NnnYvUHi=MEXotRtL<7?4Q{GA==OHuj&7KUif#XmS+8e@SA>t9)GIX z^i;8^qOE9A#XB*^mM$o^y+sl2KuH-qRjsrRI)BZQUrdC~+wnA(nr`XJ*(v&ctkJnf zq57(r_X|5;S@f;628IyGTPV@TdeQRkcxWSQc}-w7u&Q{wG6f}PEi~~Mb3VWg<65nF z=kV}j8w=#Pb~0Giuu6YbF%_wX-#%mveIxp1%d1-DBFlA-AD=&Aj{N7X!AcxZtaIc) z4+h9TmS$N__)V6h$DhiP|9nvmwg!u;+$k}(bU}{%=Yt(6DTAk~mDWM$uUYc*iO@0s zSZcbZD`zLqGUgldMy@iI&$HalR~CIMt$`r~@)k<;v7R&k?|En=L;iu)z^eT1$`q8G zwa~<4%=rK}jBB<0ox?N#SRlu>lfkNnRr;&)sYo^a_90{F8w1IIv`A8&;>YJtm?Hma zYt&kb1B!Ku{HMVP`Nz^MNeRD6QuO#!De|8#s?pX6M{Fm;*wO_l@}G`&prj0*s#aPD zoxf(uPbWgh{9~!kj8dmAAN~a>#@Y{!sp>K>N|DKM6 zel6xx((-08p4J*4lK9Qn^2C%CZVZgdDAuv+%)(CCQ5_%HdCaF;_s21ifRvGW@X9Jy z@mydNqC1!x^rqYzm%`?B;+S;dO(!HWMjW`iwu+biuW7YkfRtaDCjwu<*e{WMpvQtU zP5{TR4vhM)m*p)pMh!nuGvUzm=>e@k3{HQqYqo3G>wdQssD#^}YM)TJ8Edc)pO4SiteC9g@HjY}W_c`-0I z*^8=FGBqKB9o^rl?Hm%`?B%93=wn_-X0k_S4=xAK?SSG1btAmtb4iNF^y_Ddvh z$g$vz6TtDSd&Y&u8c^l0#I_JarLE47fEp4kzv#bct#9j6ja8vPB4Vg>H(3qwL;aotTuT3O0 zEm;?&$}Z`n%@KQSerOM`5Rr!otDK!Xt>AQ-Lkv$-d{3^rdJ5GZf+(GKH znW@rLF5%cfS%hI6=m8EiDq1kWK}F-dfw_;~RB{|zjM+?GHaJsvjog$~9OwZKG;p>; zngbmLNiE_%4u2!mlNzt_=*=f`r|(s$X&CK|B0N3qS8sBRB6UIM5>; cXyEKC2M+Wv_q_j$hX2m~VH-N{Pqul#0bA?#+yDRo literal 0 HcmV?d00001 diff --git a/src/data.cpp b/src/data.cpp new file mode 100644 index 0000000..49d053c --- /dev/null +++ b/src/data.cpp @@ -0,0 +1,133 @@ +// +// Game data +// +// by James Hammons +// (C) 2014 Underground Software +// +// JLH = James Hammons +// +// WHO WHEN WHAT +// --- ---------- ------------------------------------------------------------ +// JLH 08/21/2014 Created this file +// + +#include "data.h" + + +// We use anonymous structs and cast these to Maps to be able to access them. + +static const struct { + unsigned int width, height; + uint16_t n, s, e, w, special; + unsigned char state[16 * 11 + 1]; +} map000 = { 16, 11, + 1, 0, 2, 3, 0, + "1111111100111177" + "1111211100111177" + "1111000000111117" + "1111000000011111" + "0000000000001111" + "0000000000000111" + "1111000050000000" + "1111000000000000" + "1111000000001111" + "1111111101111111" + "1111111111111111" +}; + +static const struct { + unsigned int width, height; + uint16_t n, s, e, w, special; + unsigned char state[16 * 11 + 1]; +} map001 = { 16, 11, + 0, 0, 0, 0, 0, + "1111111111111111" + "1111111111111111" + "1111000000111111" + "1111000000011111" + "1100000000001111" + "1100000000000111" + "1111000050000011" + "1111000000000011" + "1111000000001117" + "1111111100111177" + "1111111100111177" +}; + +static const struct { + unsigned int width, height; + uint16_t n, s, e, w, special; + unsigned char state[16 * 11 + 1]; +} map002 = { 16, 11, + 0, 0, 0, 0, 0, + "1111111111111111" + "1111111111111111" + "1111000000111111" + "1111000000011111" + "1100000000001111" + "1100000000000111" + "0000000000000011" + "0000000000000011" + "1111000000001111" + "1111111111111111" + "1111111111111111" +}; + +static const struct { + unsigned int width, height; + uint16_t n, s, e, w, special; + unsigned char state[16 * 11 + 1]; +} map003 = { 16, 11, + 0, 0, 0, 0, 0, + "1111111111111111" + "1111111111111111" + "1111000000111111" + "1111000000011111" + "1100005000000000" + "1100000000000000" + "1111000000000011" + "1111000000000011" + "1111000000001111" + "1111111111111111" + "1111111111111111" +}; + + +static const struct { + unsigned int width, height; + uint16_t n, s, e, w, special; + unsigned char state[16 * 11 + 1]; +} room000 = { 16, 11, + 0, 0, 0, 0, 0, + "1111111111111111" + "1111111111111111" + "1144444444444411" + "1144444444444411" + "1144444444444411" + "1144444444444411" + "1144444444444411" + "1144444444444411" + "1144444444444411" + "1111111441111111" + "1111111441111111" +}; + + +const void * maps[] = { + &map000, &map001, &map002, &map003 +}; + + +const void * rooms[] = { + &room000 +}; + + +// Misc. globals + +uint8_t roomState[256 * 256]; +uint16_t roomWidth, roomHeight; + +Map * mapSave = 0, * currentMap = 0; +uint16_t pxSave, pySave; + diff --git a/src/data.h b/src/data.h new file mode 100644 index 0000000..209901c --- /dev/null +++ b/src/data.h @@ -0,0 +1,29 @@ +#ifndef __DATA_H__ +#define __DATA_H__ + +#include + +// Struct for casting +struct Map +{ + int width; + int height; + uint16_t n, s, e, w, special; + unsigned char tiles[]; +}; + +#define NUMBER_OF_MAPS 1 +extern const void * maps[]; + +#define NUMBER_OF_ROOMS 1 +extern const void * rooms[]; + +// Globals +extern uint8_t roomState[]; +extern uint16_t roomWidth, roomHeight; + +extern Map * mapSave, * currentMap; +extern uint16_t pxSave, pySave; + +#endif // __DATA_H__ + diff --git a/src/legend.cpp b/src/legend.cpp new file mode 100644 index 0000000..11925b2 --- /dev/null +++ b/src/legend.cpp @@ -0,0 +1,372 @@ +// +// Legend of ? +// +// by James Hammons +// © 2014 Underground Software +// +// A simple action RPG; a crass appeal to sentimentalism ;-) +// +// JLH = James Hammons +// +// WHO WHEN WHAT +// --- ---------- ------------------------------------------------------------ +// JLH 08/13/2014 Created this file +// + +// STILL TO DO: +// +// + +#include "legend.h" + +#include +#include +#include +#include +#include +#include +#include +#include "log.h" +#include "sprite.h" +#include "tile.h" +#include "sound.h" +#include "video.h" + +// Debug and misc. defines + +//#define SOFT_SWITCH_DEBUGGING + +// Global variables + +//bool powerStateChangeRequested = false; + +// Local variables + +//uint8_t lastKeyPressed = 0; + +static bool running = true; // Machine running state flag... +static uint32_t startTicks; +static uint32_t frameCount; + +// Local functions (technically, they're global...) + +void DoGame(void); +//bool LoadImg(char * filename, uint8_t * ram, int size); + +// Local timer callback functions + +//static void FrameCallback(void); +//static void BlinkTimer(void); + + +// Points of interest: +// screen is 16 x 11, 16x16 tiles with status bar on top of screen +// game follows main sprite's feet, main sprite can overlap background + + +// +// Main loop +// +int main(int /*argc*/, char * /*argv*/[]) +{ + InitLog("./legend.log"); + srand(time(NULL)); // Initialize RNG + + WriteLog("About to initialize video...\n"); + + if (!InitVideo()) + { + printf("Aborting!\n"); + return -1; + } + + if (!InitTileHandler()) + { + printf("Aborting!\n"); + VideoDone(); + return -1; + } + + if (!InitSpriteHandler()) + { + printf("Aborting!\n"); + VideoDone(); + return -1; + } + + running = true; // Set running status... + WriteLog("Entering main loop...\n"); + + DoGame(); + + SoundDone(); + VideoDone(); + LogDone(); + + return 0; +} + + +void DoGame(void) +{ + startTicks = SDL_GetTicks(); + bool running = true; + SDL_Texture * map1 = CreateMapTexture(); + SDL_Texture * screen = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_ARGB8888, + SDL_TEXTUREACCESS_TARGET, 16 * 16, 11 * 16); + + bool inRoom = false; + Direction playerFacing = Down; + int playerInput = Stopped; + uint16_t px = (8 * 16) << 7; + uint16_t py = (5 * 16) << 7; + int currentFrame = 0; + int frame[] = { 1, + 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + }; + + while (running) + { + SDL_Event event; + + while (SDL_PollEvent(&event)) + { + switch (event.type) + { + case SDL_KEYDOWN: + // Use ALT+Q to exit, as well as the usual window decoration + // method +// if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod & KMOD_ALT)) + if (event.key.keysym.sym == SDLK_ESCAPE) + running = false; + else if (event.key.keysym.sym == SDLK_z) + playerInput |= Left; + else if (event.key.keysym.sym == SDLK_c) + playerInput |= Right; + else if (event.key.keysym.sym == SDLK_s) + playerInput |= Up; + else if (event.key.keysym.sym == SDLK_x) + playerInput |= Down; + + break; + + case SDL_KEYUP: + if (event.key.keysym.sym == SDLK_z) + playerInput &= ~Left; + else if (event.key.keysym.sym == SDLK_c) + playerInput &= ~Right; + else if (event.key.keysym.sym == SDLK_s) + playerInput &= ~Up; + else if (event.key.keysym.sym == SDLK_x) + playerInput &= ~Down; + + break; + } + } + + uint8_t tile[2], tileUnder[4]; + int dx = 0, dy = 0; + + // Handle player input + if (playerInput & Left) + { + playerFacing = Left; + dx = -1; + } + else if (playerInput & Right) + { + playerFacing = Right; + dx = +1; + } + else if (playerInput & Up) + { + playerFacing = Up; + dy = -1; + } + else if (playerInput & Down) + { + playerFacing = Down; + dy = +1; + } + else + { + currentFrame = 0; + } + + // Get the tiles the player is moving towards + GetTiles2(px >> 7, py >> 7, playerFacing, tile); + bool walk1 = (tile[0] == '0' || tile[0] == '4' || tile[0] == '2' || tile[0] == '6' ? true : false); + bool walk2 = (tile[1] == '0' || tile[1] == '4' || tile[1] == '2' || tile[1] == '6' ? true : false); + + int curPx = px >> 7, curPy = py >> 7; + int tileX = curPx / 16, tileY = curPy / 16; + int tileXRem = curPx % 16, tileYRem = curPy % 16; + uint16_t newPx = px + (uint16_t)(dx * (3 << 6)); + uint16_t newPy = py + (uint16_t)(dy * (3 << 6)); + int newTileX = (newPx >> 7) / 16; + int newTileY = (newPy >> 7) / 16; + + // Collision detection... + // Check to see if we're stepping on 2 tiles in the direction we're + // moving. If so, check to see what distance remains between where the + // player is and where he wants to be. If that distance is < the move + // delta, see if the tile he wants to move into can be moved into. If + // not, truncate the move so that he moves *exactly* into the next + // single space. + if (dx != 0) + { + // Check to see if way forward is clear... + if (walk1 + && ((tileYRem == 0) || ((tileYRem != 0) && walk2))) + px = newPx; + // It's blocked ahead, so see if there's still room to move forward. + // (only if we're standing on 2 tiles instead of 1!) + else if (tileXRem != 0) + { + // We didn't cross the tile boundary, so move normally. + if (newTileX == tileX) + px = newPx; + // We're trying to cross the boundary, so clamp it to the edge. + else + px = ((tileX + (dx == 1 ? 1 : 0)) * 16) << 7; + } + else + { + // Player is stuck at tileXRem == 0, so see if we can nudge + // them up or down... + if (walk1 && (tileYRem < 8)) + py = (py - (1 << 7)) & 0xFF80; + else if (walk2 && (tileYRem > 7)) + py = (py + (1 << 7)) & 0xFF80; + } + } + else if (dy != 0) + { + // Check to see if way forward is clear... + if (walk1 + && ((tileXRem == 0) || ((tileXRem != 0) && walk2))) + py = newPy; + // It's blocked ahead, so see if there's still room to move forward. + // (only if we're standing on 2 tiles instead of 1!) + else if (tileYRem != 0) + { + // We didn't cross the tile boundary, so move normally. + if (newTileY == tileY) + py = newPy; + // We're trying to cross the boundary, so clamp it to the edge. + else + py = ((tileY + (dy == 1 ? 1 : 0)) * 16) << 7; + } + else + { + // Player is stuck at tileYRem == 0, so see if we can nudge + // them left or right... + if (walk1 && (tileXRem < 8)) + px = (px - (1 << 7)) & 0xFF80; + else if (walk2 && (tileXRem > 7)) + px = (px + (1 << 7)) & 0xFF80; + } + } + + // Now that we've moved, see what we're standing on + // Overlap should be taken into account so we don't have the 'Touch It + // And Die' (TIAD) syndrome of pixel perfect collisions... + GetTiles(px >> 7, py >> 7, tileUnder); + + // *. .* .. .. + // .. .. *. .* + // See which one of the 4 tiles the player is closest to + tileXRem = (px >> 7) % 16, tileYRem = (py >> 7) % 16; + int tileNum = 0; + + if ((tileXRem < 7) && (tileYRem < 7)) + tileNum = 0; + else if ((tileXRem > 9) && (tileYRem < 7)) + tileNum = 1; + else if ((tileXRem < 7) && (tileYRem > 9)) + tileNum = 2; + else if ((tileXRem > 9) && (tileYRem > 9)) + tileNum = 3; + + // Special tile handling + if ((tileUnder[tileNum] == '2') && (dy == -1)) + { + mapSave = currentMap; + pxSave = px & 0xF800; + pySave = py & 0xF800; + uint16_t roomToEnter = currentMap->special; + inRoom = true; + RenderMap((Map *)rooms[roomToEnter], map1); + px = ((7 * 16) + 8) << 7; + py = ((10 * 16) - 0) << 7; + } + + // Check for moving to the next map screen handling + if ((dy == 1) && (py >= ((10 * 16) << 7))) + { + if (inRoom) + { + inRoom = false; + RenderMap(mapSave, map1); + px = pxSave, py = pySave; + } + else + { + uint16_t dir = currentMap->s; + RenderMap((Map *)maps[dir], map1); + py = 0; + } + } + else if ((dy == -1) && ((py & 0xFF80) == 0)) + { + uint16_t dir = currentMap->n; + RenderMap((Map *)maps[dir], map1); + py = (10 * 16) << 7; + } + else if ((dx == 1) && (px >= ((15 * 16) << 7))) + { + uint16_t dir = currentMap->e; + RenderMap((Map *)maps[dir], map1); + px = 0; + } + else if ((dx == -1) && ((px & 0xFF80) == 0)) + { + uint16_t dir = currentMap->w; + RenderMap((Map *)maps[dir], map1); + px = (15 * 16) << 7; + } + + // Do rendering... + SDL_SetRenderTarget(sdlRenderer, screen); + // This should be limited to the visible window... + SDL_RenderCopy(sdlRenderer, map1, NULL, NULL); +// DrawPlayerSprite(playerFacing, px, py); + DrawUnderlay(px >> 7, py >> 7, playerFacing); + DrawPlayerSprite2(px >> 7, py >> 7, playerFacing, frame[currentFrame]); + // Reset main renderer to main window + SDL_SetRenderTarget(sdlRenderer, NULL); + SDL_RenderCopy(sdlRenderer, screen, NULL, NULL); + SDL_RenderPresent(sdlRenderer); + + // Animation handling... + currentFrame = (currentFrame + 1) % 24; + + // In order to get our delay to come out approximately 60 Hz, we need to + // add in an extra ms every 3 frames. So this handles that problem. :-) + frameCount = (frameCount + 1) % 3; + uint32_t interval = (frameCount == 0 ? 16 : 17); + + // We'll try to keep this running at 60 Hz... + while ((SDL_GetTicks() - startTicks) < interval) + SDL_Delay(1); + + startTicks = SDL_GetTicks(); + } + + SDL_DestroyTexture(map1); +} + + diff --git a/src/legend.h b/src/legend.h new file mode 100644 index 0000000..17a1ad3 --- /dev/null +++ b/src/legend.h @@ -0,0 +1,42 @@ +#ifndef __LEGEND_H__ +#define __LEGEND_H__ + +// +// Legend of Alden(?) v1.0 +// +// by James Hammons +// + +enum Direction { Stopped=0, Left=1, Right=2, Up=4, Down=8 }; + +#include + +// Exported functions + +//void SetPowerState(void); + +// Global variables (exported) + +#if 0 +extern uint8_t ram[0x10000], rom[0x10000]; // RAM & ROM pointers +extern uint8_t ram2[0x10000]; // Auxillary RAM +extern uint8_t appleType; +extern FloppyDrive floppyDrive; +extern uint8_t lastKeyPressed; +extern bool keyDown; +extern bool openAppleDown; +extern bool closedAppleDown; +extern bool store80Mode; +extern bool vbl; +extern bool slotCXROM; +extern bool slotC3ROM; +extern bool ramrd; +extern bool ramwrt; +extern bool altzp; +extern bool ioudis; +extern bool dhires; +extern uint8_t lcState; +#endif + +#endif // __LEGEND_H__ + diff --git a/src/log.cpp b/src/log.cpp new file mode 100644 index 0000000..6994cc5 --- /dev/null +++ b/src/log.cpp @@ -0,0 +1,69 @@ +// +// Log handler +// +// by James Hammons +// (C) 2006 Underground Software +// +// JLH = James Hammons +// +// WHO WHEN WHAT +// --- ---------- ------------------------------------------------------------ +// JLH 01/03/2006 Moved includes out of header file for faster compilation +// + +#include "log.h" + +#include +#include +#include +#include + +// Maximum size of log file (10 MB ought to be enough for anybody) +#define MAX_LOG_SIZE 10000000 + +static FILE * log_stream = NULL; +static uint32_t logSize = 0; + + +bool InitLog(const char * path) +{ + log_stream = fopen(path, "wrt"); + + if (log_stream == NULL) + return false; + + return true; +} + + +void LogDone(void) +{ + if (log_stream) + fclose(log_stream); +} + + +// +// This logger is used mainly to ensure that text gets written to the log file +// even if the program crashes. The performance hit is acceptable in this case! +// +void WriteLog(const char * text, ...) +{ + if (!log_stream) + return; + + va_list arg; + + va_start(arg, text); + logSize += vfprintf(log_stream, text, arg); + va_end(arg); + + fflush(log_stream); // Make sure that text is written! + + if (logSize > MAX_LOG_SIZE) + { + fclose(log_stream); + log_stream = NULL; + } +} + diff --git a/src/log.h b/src/log.h new file mode 100644 index 0000000..f72795e --- /dev/null +++ b/src/log.h @@ -0,0 +1,22 @@ +// +// LOG.H +// + +#ifndef __LOG_H__ +#define __LOG_H__ + +// Make this header work with either C or C++ + +#ifdef __cplusplus +extern "C" { +#endif + +bool InitLog(const char *); +void LogDone(void); +void WriteLog(const char * text, ...); + +#ifdef __cplusplus +} +#endif + +#endif // __LOG_H__ diff --git a/src/sound.cpp b/src/sound.cpp new file mode 100644 index 0000000..d57ac97 --- /dev/null +++ b/src/sound.cpp @@ -0,0 +1,253 @@ +// +// Sound Interface +// +// by James Hammons +// (C) 2014 Underground Software +// +// JLH = James Hammons +// +// WHO WHEN WHAT +// --- ---------- ------------------------------------------------------------ +// JLH 12/02/2005 Fixed a problem with sound callback thread signaling the +// main thread +// JLH 12/03/2005 Fixed sound callback dropping samples when the sample buffer +// is shorter than the callback sample buffer +// + +// STILL TO DO: +// +// + +#include "sound.h" + +#include // For memset, memcpy +#include +#include "log.h" + +// Useful defines + +//#define DEBUG +//#define WRITE_OUT_WAVE + +//#define SAMPLE_RATE (44100.0) +#define SAMPLE_RATE (48000.0) +#define SAMPLES_PER_FRAME (SAMPLE_RATE / 60.0) +#define CYCLES_PER_SAMPLE (1024000.0 / SAMPLE_RATE) +//#define SOUND_BUFFER_SIZE (8192) +#define SOUND_BUFFER_SIZE (32768) + +// Global variables + + +// Local variables + +static SDL_AudioSpec desired, obtained; +static SDL_AudioDeviceID device; +static bool soundInitialized = false; +static bool speakerState = false; +static int16_t soundBuffer[SOUND_BUFFER_SIZE]; +static uint32_t soundBufferPos; +static uint64_t lastToggleCycles; +static SDL_cond * conditional = NULL; +static SDL_mutex * mutex = NULL; +static SDL_mutex * mutex2 = NULL; +static int16_t sample; +static uint8_t ampPtr = 12; // Start with -2047 - +2047 +static int16_t amplitude[17] = { 0, 1, 2, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, + 4095, 8191, 16383, 32767 }; +#ifdef WRITE_OUT_WAVE +static FILE * fp = NULL; +#endif + +// Private function prototypes + +static void SDLSoundCallback(void * userdata, Uint8 * buffer, int length); + + +// +// Initialize the SDL sound system +// +void SoundInit(void) +{ +#if 0 +// To weed out problems for now... +return; +#endif + SDL_zero(desired); + desired.freq = SAMPLE_RATE; // SDL will do conversion on the fly, if it can't get the exact rate. Nice! + desired.format = AUDIO_S16SYS; // This uses the native endian (for portability)... + desired.channels = 1; + desired.samples = 512; // Let's try a 1/2K buffer (can always go lower) + desired.callback = SDLSoundCallback; + + device = SDL_OpenAudioDevice(NULL, 0, &desired, &obtained, 0); + + if (device == 0) + { + WriteLog("Sound: Failed to initialize SDL sound.\n"); + return; + } + + conditional = SDL_CreateCond(); + mutex = SDL_CreateMutex(); + mutex2 = SDL_CreateMutex();// Let's try real signalling... + soundBufferPos = 0; + lastToggleCycles = 0; + sample = desired.silence; // ? wilwok ? yes + + SDL_PauseAudioDevice(device, 0); // Start playback! + soundInitialized = true; + WriteLog("Sound: Successfully initialized.\n"); + +#ifdef WRITE_OUT_WAVE + fp = fopen("./apple2.wav", "wb"); +#endif +} + + +// +// Close down the SDL sound subsystem +// +void SoundDone(void) +{ + if (soundInitialized) + { + SDL_PauseAudioDevice(device, 1); + SDL_CloseAudioDevice(device); + SDL_DestroyCond(conditional); + SDL_DestroyMutex(mutex); + SDL_DestroyMutex(mutex2); + WriteLog("Sound: Done.\n"); + +#ifdef WRITE_OUT_WAVE + fclose(fp); +#endif + } +} + + +void SoundPause(void) +{ + if (soundInitialized) + SDL_PauseAudioDevice(device, 1); +} + + +void SoundResume(void) +{ + if (soundInitialized) + SDL_PauseAudioDevice(device, 0); +} + + +// +// Sound card callback handler +// +static void SDLSoundCallback(void * /*userdata*/, Uint8 * buffer8, int length8) +{ +//WriteLog("SDLSoundCallback(): begin (soundBufferPos=%i)\n", soundBufferPos); + // The sound buffer should only starve when starting which will cause it to + // lag behind the emulation at most by around 1 frame... + // (Actually, this should never happen since we fill the buffer beforehand.) + // (But, then again, if the sound hasn't been toggled for a while, then this + // makes perfect sense as the buffer won't have been filled at all!) + // (Should NOT starve now, now that we properly handle frame edges...) + + // Let's try using a mutex for shared resource consumption... +//Actually, I think Lock/UnlockAudio() does this already... +//WriteLog("SDLSoundCallback: soundBufferPos = %i\n", soundBufferPos); + SDL_mutexP(mutex2); + + // Recast this as a 16-bit type... + int16_t * buffer = (int16_t *)buffer8; + uint32_t length = (uint32_t)length8 / 2; + +//WriteLog("SDLSoundCallback(): filling buffer...\n"); + if (soundBufferPos < length) + { + // The sound buffer is starved... + for(uint32_t i=0; i= (SOUND_BUFFER_SIZE - 1)) + { +//WriteLog("WriteSampleToBuffer(): Waiting for sound thread. soundBufferPos=%i, SOUNDBUFFERSIZE-1=%i\n", soundBufferPos, SOUND_BUFFER_SIZE-1); + SDL_mutexV(mutex2); // Release it so sound thread can get it, + SDL_mutexP(mutex); // Must lock the mutex for the cond to work properly... + SDL_CondWait(conditional, mutex); // Sleep/wait for the sound thread + SDL_mutexV(mutex); // Must unlock the mutex for the cond to work properly... + SDL_mutexP(mutex2); // Re-lock it until we're done with it... + } + + soundBuffer[soundBufferPos++] = sample; +//WriteLog("WriteSampleToBuffer(): SDL_mutexV(mutex2)\n"); + SDL_mutexV(mutex2); +} + + +void ToggleSpeaker(void) +{ + if (!soundInitialized) + return; + + speakerState = !speakerState; + sample = (speakerState ? amplitude[ampPtr] : -amplitude[ampPtr]); +} + + +void VolumeUp(void) +{ + // Currently set for 16-bit samples + if (ampPtr < 16) + ampPtr++; +} + + +void VolumeDown(void) +{ + if (ampPtr > 0) + ampPtr--; +} + + +uint8_t GetVolume(void) +{ + return ampPtr; +} + diff --git a/src/sound.h b/src/sound.h new file mode 100644 index 0000000..0cea590 --- /dev/null +++ b/src/sound.h @@ -0,0 +1,29 @@ +// +// SOUND.H +// +// by James Hammons +// (C) 2014 Underground Software +// + +#ifndef __SOUND_H__ +#define __SOUND_H__ + +#include + +// Global variables (exported) + + +// Functions + +void SoundInit(void); +void SoundDone(void); +void SoundPause(void); +void SoundResume(void); +void ToggleSpeaker(void); +void WriteSampleToBuffer(void); +void VolumeUp(void); +void VolumeDown(void); +uint8_t GetVolume(void); + +#endif // __SOUND_H__ + diff --git a/src/sprite.cpp b/src/sprite.cpp new file mode 100644 index 0000000..8bfe98f --- /dev/null +++ b/src/sprite.cpp @@ -0,0 +1,194 @@ +// +// Sprite handler +// +// by James Hammons +// (C) 2014 Underground Software +// +// JLH = James Hammons +// +// WHO WHEN WHAT +// --- ---------- ------------------------------------------------------------ +// JLH 08/19/2014 Created this file +// + +#include "sprite.h" +#include +#include "video.h" + + +// Global vars +SDL_Texture * sprite1 = 0; +SDL_Texture * wizard1 = 0; +SDL_Texture * underlay = 0; +SDL_Texture * underlay2 = 0; + + +bool InitSpriteHandler(void) +{ + SDL_Surface * surface = IMG_Load("res/spritemap1.png"); + + if (!surface) + { + printf("SPRITE: IMG_Load: %s\n", IMG_GetError()); + return false; + } + + sprite1 = SDL_CreateTextureFromSurface(sdlRenderer, surface); + SDL_FreeSurface(surface); + + surface = IMG_Load("res/g2-wizard.png"); + + if (!surface) + { + printf("SPRITE: IMG_Load: %s\n", IMG_GetError()); + return false; + } + + wizard1 = SDL_CreateTextureFromSurface(sdlRenderer, surface); + SDL_FreeSurface(surface); + + uint32_t ulPixels[32 * 32]; + + for(uint32_t i=0; i<32*32; i++) + ulPixels[i] = 0xFF00FF7F; + + surface = SDL_CreateRGBSurfaceFrom(ulPixels, 32, 32, 32, 32, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF); + + if (!surface) + { + printf("SPRITE: CreateRGBSurfaceFrom: %s\n", IMG_GetError()); + return false; + } + + underlay = SDL_CreateTextureFromSurface(sdlRenderer, surface); + SDL_FreeSurface(surface); + + for(uint32_t i=0; i<32*32; i++) + ulPixels[i] = 0xFF7F007F; + + surface = SDL_CreateRGBSurfaceFrom(ulPixels, 32, 32, 32, 32, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF); + + if (!surface) + { + printf("SPRITE: CreateRGBSurfaceFrom: %s\n", IMG_GetError()); + return false; + } + + underlay2 = SDL_CreateTextureFromSurface(sdlRenderer, surface); + SDL_FreeSurface(surface); + + return true; +} + + +void DrawPlayerSprite(Direction facing, int xpos, int ypos) +{ + SDL_Rect src, dst; +#if 1 + src.w = src.h = 16; + dst.x = xpos, dst.y = ypos, dst.w = dst.h = 16; + + if (facing == Up) + { + src.x = 0, src.y = 0; + } + else if (facing == Down) + { + src.x = 16, src.y = 0; + } + else if (facing == Right) + { + src.x = 32, src.y = 0; + } + else if (facing == Left) + { + src.x = 48, src.y = 0; + } + + SDL_RenderCopy(sdlRenderer, sprite1, &src, &dst); +#else + + +#endif + +#if 0 + src.w = src.h = 24; + src.x = src.y = 0; + dst.x = 100, dst.y = 100, dst.w = dst.h = 24; + + SDL_RenderCopy(sdlRenderer, wizard1, &src, &dst); +#endif +} + + +void DrawPlayerSprite2(int xpos, int ypos, Direction facing, int frame) +{ + // Adjust x/y coords for oversize sprite + xpos -= 4, ypos -= 8; + + int frameX[3] = { 48, 0, 24 }; + int frameY[3] = { 0, 24, 24 }; + SDL_Rect src, dst; + src.w = src.h = 24; + dst.x = xpos, dst.y = ypos, dst.w = dst.h = 24; + + if (facing == Up) + { + src.x = frameX[frame], src.y = (6 * 24) + frameY[frame]; + } + else if (facing == Down) + { + src.x = frameX[frame], src.y = (2 * 24) + frameY[frame]; + } + else if (facing == Right) + { + src.x = frameX[frame], src.y = (8 * 24) + frameY[frame]; + } + else if (facing == Left) + { + src.x = frameX[frame], src.y = (4 * 24) + frameY[frame]; + } + + SDL_RenderCopy(sdlRenderer, wizard1, &src, &dst); +} + + +void DrawUnderlay(int xpos, int ypos, Direction facing) +{ + SDL_Rect src, dest; + int rx = xpos % 16, ry = ypos % 16; + int gx = xpos / 16, gy = ypos / 16; + + src.x = 0, src.y = 0; + dest.x = gx * 16, dest.y = gy * 16; + + src.w = (rx == 0 ? 16 : 32); + src.h = (ry == 0 ? 16 : 32); + dest.w = src.w, dest.h = src.h; + + SDL_RenderCopy(sdlRenderer, underlay, &src, &dest); + + if (facing == Up) + { + src.h = dest.h = 16; + dest.y -= 16; + } + else if (facing == Down) + { + dest.y += src.h; + src.h = dest.h = 16; + } + else if (facing == Left) + { + src.w = dest.w = 16; + dest.x -= 16; + } + else if (facing == Right) + { + dest.x += src.w; + src.w = dest.w = 16; + } + + SDL_RenderCopy(sdlRenderer, underlay2, &src, &dest); +} + diff --git a/src/sprite.h b/src/sprite.h new file mode 100644 index 0000000..7d3492a --- /dev/null +++ b/src/sprite.h @@ -0,0 +1,20 @@ +#ifndef __SPRITE_H__ +#define __SPRITE_H__ + +#include +#include "legend.h" + +// Exported functions +bool InitSpriteHandler(void); +void DrawPlayerSprite(Direction, int, int); +void DrawPlayerSprite2(int xpos, int ypos, Direction facing, int frame); +void DrawUnderlay(int xpos, int ypos, Direction); + + +// Exported vars +extern SDL_Texture * sprite1; +extern SDL_Texture * underlay; +extern SDL_Texture * underlay2; + +#endif // __SPRITE_H__ + diff --git a/src/tile.cpp b/src/tile.cpp new file mode 100644 index 0000000..e721bcc --- /dev/null +++ b/src/tile.cpp @@ -0,0 +1,170 @@ +// +// Tile handler +// +// by James Hammons +// (C) 2014 Underground Software +// +// JLH = James Hammons +// +// WHO WHEN WHAT +// --- ---------- ------------------------------------------------------------ +// JLH 08/19/2014 Created this file +// + +#include "tile.h" +#include +#include "data.h" +#include "log.h" +#include "video.h" + + +// Global vars +SDL_Texture * tile1 = 0; +int mapCornerX = 0; +int mapCornerY = 0; + + +bool InitTileHandler(void) +{ + SDL_Surface * surface = IMG_Load("res/tilemap1.png"); + + if (!surface) + { + printf("TILE: IMG_Load: %s\n", IMG_GetError()); + return false; + } + + tile1 = SDL_CreateTextureFromSurface(sdlRenderer, surface); + SDL_FreeSurface(surface); + + return true; +} + + +SDL_Texture * CreateMapTexture(void) +{ + Map * map = (Map *)maps[0]; + + SDL_Texture * texture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_ARGB8888, +// SDL_TEXTUREACCESS_STATIC, map->width * 16, map->height * 16); + SDL_TEXTUREACCESS_TARGET, map->width * 16, map->height * 16); + + RenderMap(map, texture); + + return texture; +} + + +void RenderMap(Map * map, SDL_Texture * texture) +{ + // Sanity check... + if ((map == 0) || (texture == 0)) + return; + + if (SDL_SetRenderTarget(sdlRenderer, texture) < 0) + { + WriteLog("TILE: Could not set Render Target to map texture... (%s)\n", SDL_GetError()); + return; + } + + for(int y=0; yheight; y++) + { + for(int x=0; xwidth; x++) + { + unsigned char tile = map->tiles[(y * map->width) + x]; + SDL_Rect src, dst; + dst.x = x * 16, dst.y = y * 16, dst.w = dst.h = 16; + src.w = src.h = 16; + + if (tile == '0') + src.x = 0, src.y = 0; + else if (tile == '1') + src.x = 16, src.y = 0; + else if (tile == '2') + src.x = 32, src.y = 0; + else if (tile == '3') + src.x = 48, src.y = 0; + else if (tile == '4') + src.x = 64, src.y = 0; + else if (tile == '5') + src.x = 80, src.y = 0; + else if (tile == '6') + src.x = 96, src.y = 0; + else if (tile == '7') + src.x = 112, src.y = 0; + + SDL_RenderCopy(sdlRenderer, tile1, &src, &dst); + } + } + + SDL_SetRenderTarget(sdlRenderer, 0); + + // Set up global room state from tile map + memcpy(roomState, map->tiles, 16 * 11); + roomWidth = map->width; + roomHeight = map->height; + currentMap = map; +} + + +// +// Get tiles the player is standing on (1x1 to 2x2) +// +void GetTiles(int xpos, int ypos, uint8_t * tiles) +{ + memset(tiles, 0, 4); + + int rx = xpos % 16, ry = ypos % 16; + int gx = xpos / 16, gy = ypos / 16; + + tiles[0] = roomState[gx + 0 + ((gy + 0) * roomWidth)]; + + if (rx != 0) + tiles[1] = roomState[gx + 1 + ((gy + 0) * roomWidth)]; + + if (ry != 0) + tiles[2] = roomState[gx + 0 + ((gy + 1) * roomWidth)]; + + if ((rx != 0) && (ry != 0)) + tiles[3] = roomState[gx + 1 + ((gy + 1) * roomWidth)]; +} + + +// +// Get tiles next to the direction the player is facing (1 or 2) +// +void GetTiles2(int xpos, int ypos, Direction facing, uint8_t * tiles) +{ + tiles[0] = tiles[1] = 0; + + int rx = xpos % 16, ry = ypos % 16; + int gx = xpos / 16, gy = ypos / 16; + + int dx = 0, dy = 0, extraX = (rx != 0 ? 1 : 0), extraY = (ry != 0 ? 1 : 0); + + if (facing == Left) + dx = -1; + else if (facing == Right) + dx = +1 + extraX; + else if (facing == Up) + dy = -1; + else if (facing == Down) + dy = +1 + extraY; + + // Edge conditions... + if (((facing == Left) && (gx == 0)) + || ((facing == Right) && (gx == (roomWidth - 1))) + || ((facing == Up) && (gy == 0)) + || ((facing == Down) && (gy == (roomHeight - 1)))) + return; + + tiles[0] = roomState[(gx + dx) + ((gy + dy) * roomWidth)]; + + if (extraX && dy) + tiles[1] = roomState[(gx + 1) + ((gy + dy) * roomWidth)]; + + if (extraY && dx) + tiles[1] = roomState[(gx + dx) + ((gy + 1) * roomWidth)]; +} + + diff --git a/src/tile.h b/src/tile.h new file mode 100644 index 0000000..78bacce --- /dev/null +++ b/src/tile.h @@ -0,0 +1,19 @@ +#ifndef __TILE_H__ +#define __TILE_H__ + +#include +#include "data.h" +#include "legend.h" + +// Exported functions +bool InitTileHandler(void); +SDL_Texture * CreateMapTexture(void); +void RenderMap(Map * map, SDL_Texture * texture); +void GetTiles(int xpos, int ypos, uint8_t * tiles); +void GetTiles2(int xpos, int ypos, Direction facing, uint8_t * tiles); + +// Exported vars +extern SDL_Texture * tile1; + +#endif // __TILE_H__ + diff --git a/src/video.cpp b/src/video.cpp new file mode 100644 index 0000000..2e00561 --- /dev/null +++ b/src/video.cpp @@ -0,0 +1,113 @@ +// +// VIDEO.CPP: SDL/local hardware specific video routines +// +// by James Hammons +// +// JLH = James Hammons +// +// WHO WHEN WHAT +// --- ---------- ------------------------------------------------------------ +// + +#include "video.h" +#include // Why??? (for memset, etc... Lazy!) Dunno why, but this just strikes me as wrong... +#include +#include +#include "log.h" + + +// Exported global variables (actually, these are LOCAL global variables, EXPORTED...) + +static SDL_Window * sdlWindow = NULL; +SDL_Renderer * sdlRenderer = NULL; +static SDL_Texture * sdlTexture = NULL; +uint32_t scrBuffer[VIRTUAL_SCREEN_WIDTH * VIRTUAL_SCREEN_HEIGHT * sizeof(uint32_t)]; +bool fullscreen = false; + + +// +// Prime SDL and create surfaces +// +bool InitVideo(void) +{ + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE) != 0) + { + WriteLog("Video: Could not initialize the SDL library: %s\n", SDL_GetError()); + return false; + } + + int retVal = SDL_CreateWindowAndRenderer(VIRTUAL_SCREEN_WIDTH * 2, VIRTUAL_SCREEN_HEIGHT * 2, SDL_WINDOW_OPENGL, &sdlWindow, &sdlRenderer); + + if (retVal != 0) + { + WriteLog("Video: Could not window and/or renderer: %s\n", SDL_GetError()); + return false; + } + + // Set up SDL_image + retVal = IMG_Init(IMG_INIT_PNG); + + if (retVal != IMG_INIT_PNG) + { + WriteLog("Video: Could not init SDL_image for PNG loading!\n"); + WriteLog("IMG_Init: %s\n", IMG_GetError()); + } + + // Make the scaled rendering look smoother. + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); +// SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest"); + SDL_RenderSetLogicalSize(sdlRenderer, VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT); + + // Set the application's icon & title... +// SDL_Surface * iconSurface = SDL_CreateRGBSurfaceFrom(icon, 64, 64, 32, 64*4, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000); +// SDL_SetWindowIcon(sdlWindow, iconSurface); +// SDL_FreeSurface(iconSurface); + SDL_SetWindowTitle(sdlWindow, "Legend of ?"); + + sdlTexture = SDL_CreateTexture(sdlRenderer, + SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING, + VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT); + + WriteLog("Video: Successfully initialized.\n"); + return true; +} + + +// +// Free various SDL components +// +void VideoDone(void) +{ + WriteLog("Video: Shutting down SDL...\n"); + IMG_Quit(); + SDL_Quit(); + WriteLog("Video: Done.\n"); +} + + +// +// Render the screen buffer to the primary screen surface +// +void RenderScreenBuffer(void) +{ + SDL_UpdateTexture(sdlTexture, NULL, scrBuffer, VIRTUAL_SCREEN_WIDTH * sizeof(Uint32)); + SDL_RenderClear(sdlRenderer); + SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL); +} + + +// +// Fullscreen <-> window switching +// +void ToggleFullScreen(void) +{ + fullscreen = !fullscreen; + + int retVal = SDL_SetWindowFullscreen(sdlWindow, (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0)); + SDL_ShowCursor(fullscreen ? 0 : 1); + + if (retVal != 0) + WriteLog("Video::ToggleFullScreen: SDL error = %s\n", SDL_GetError()); +} + + diff --git a/src/video.h b/src/video.h new file mode 100644 index 0000000..98c71de --- /dev/null +++ b/src/video.h @@ -0,0 +1,28 @@ +// +// VIDEO.H: Header file +// + +#ifndef __VIDEO_H__ +#define __VIDEO_H__ + +#include +#include // For uint32_t + +//#define VIRTUAL_SCREEN_WIDTH 280 +#define VIRTUAL_SCREEN_WIDTH 16*32 +//#define VIRTUAL_SCREEN_HEIGHT 192 +#define VIRTUAL_SCREEN_HEIGHT 16*24 + +bool InitVideo(void); +void VideoDone(void); +void RenderScreenBuffer(void); +void ToggleFullScreen(void); + +// Exported crap + +extern SDL_Renderer * sdlRenderer; +extern uint32_t scrBuffer[]; +extern uint32_t mainScrBuffer[]; + +#endif // __VIDEO_H__ + -- 2.37.2