diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..58e2eea --- /dev/null +++ b/Makefile @@ -0,0 +1,200 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") +endif + +TOPDIR ?= $(CURDIR) +include $(DEVKITPRO)/libnx/switch_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +# EXEFS_SRC is the optional input directory containing data copied into exefs, if anything this normally should only contain "main.npdm". +# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional) +# +# NO_ICON: if set to anything, do not use icon. +# NO_NACP: if set to anything, no .nacp file is generated. +# APP_TITLE is the name of the app stored in the .nacp file (Optional) +# APP_AUTHOR is the author of the app stored in the .nacp file (Optional) +# APP_VERSION is the version of the app stored in the .nacp file (Optional) +# APP_TITLEID is the titleID of the app stored in the .nacp file (Optional) +# ICON is the filename of the icon (.jpg), relative to the project folder. +# If not set, it attempts to use one of the following (in this order): +# - .jpg +# - icon.jpg +# - /default_icon.jpg +#--------------------------------------------------------------------------------- +APP_TITLE := 2PlanesSwitch +APP_AUTHOR := mactec +APP_VERSION := 1.0.1 +ICON := icon.jpg +TARGET := $(notdir $(CURDIR)) +BUILD := build +SOURCES := src +DATA := data +INCLUDES := include +EXEFS_SRC := exefs_src +ROMFS := romfs + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE + +CFLAGS := -g -Wall -O3 -ffunction-sections \ + $(ARCH) $(DEFINES) + +CFLAGS += $(INCLUDE) -D__SWITCH__ + +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=c++14 + +ASFLAGS := -g $(ARCH) +LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) + +LIBS := -lSDL2_mixer -lSDL2main -lSDL2 -lSDL2_mixer -lSDL2_image -lSDL2_ttf \ + -lfreetype -lbz2 -lpng -lz -ljpeg \ + -lvorbisidec -logg -lmpg123 -lmodplug -lstdc++ \ + -lnx -lm -lout123 + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(PORTLIBS) $(LIBNX) + + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES_BIN := $(addsuffix .o,$(BINFILES)) +export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) +export OFILES := $(OFILES_BIN) $(OFILES_SRC) +export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC) + +ifeq ($(strip $(ICON)),) + icons := $(wildcard *.jpg) + ifneq (,$(findstring $(TARGET).jpg,$(icons))) + export APP_ICON := $(TOPDIR)/$(TARGET).jpg + else + ifneq (,$(findstring icon.jpg,$(icons))) + export APP_ICON := $(TOPDIR)/icon.jpg + endif + endif +else + export APP_ICON := $(TOPDIR)/$(ICON) +endif + +ifeq ($(strip $(NO_ICON)),) + export NROFLAGS += --icon=$(APP_ICON) +endif + +ifeq ($(strip $(NO_NACP)),) + export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp +endif + +ifneq ($(APP_TITLEID),) + export NACPFLAGS += --titleid=$(APP_TITLEID) +endif + +ifneq ($(ROMFS),) + export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS) +endif + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).pfs0 $(TARGET).nso $(TARGET).nro $(TARGET).nacp $(TARGET).elf + + +#--------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +all : $(OUTPUT).pfs0 $(OUTPUT).nro + +$(OUTPUT).pfs0 : $(OUTPUT).nso + +$(OUTPUT).nso : $(OUTPUT).elf + +ifeq ($(strip $(NO_NACP)),) +$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp +else +$(OUTPUT).nro : $(OUTPUT).elf +endif + +$(OUTPUT).elf : $(OFILES) + +$(OFILES_SRC) : $(HFILES_BIN) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o %_bin.h : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/README.md b/README.md index 503d0cc..cdd86d3 100644 --- a/README.md +++ b/README.md @@ -1 +1,10 @@ -2Planes_SWITCH +## 2Planes +Nintendo Switch homebrew game + +![img](https://i.imgur.com/ryDRCeh.png) + +### Compilation +In order to compile you need to download devkitPro and switch port of SDL library. + +### Release +[Download](https://github.com/mactec0/2Planes_SWITCH/releases/) \ No newline at end of file diff --git a/icon.jpg b/icon.jpg new file mode 100644 index 0000000..b09c5f9 Binary files /dev/null and b/icon.jpg differ diff --git a/romfs/BOMB.png b/romfs/BOMB.png new file mode 100644 index 0000000..0c773ac Binary files /dev/null and b/romfs/BOMB.png differ diff --git a/romfs/Plane1.png b/romfs/Plane1.png new file mode 100644 index 0000000..76560c1 Binary files /dev/null and b/romfs/Plane1.png differ diff --git a/romfs/Plane2.png b/romfs/Plane2.png new file mode 100644 index 0000000..d5da2a0 Binary files /dev/null and b/romfs/Plane2.png differ diff --git a/romfs/background1.png b/romfs/background1.png new file mode 100644 index 0000000..01fcd7b Binary files /dev/null and b/romfs/background1.png differ diff --git a/romfs/background2.png b/romfs/background2.png new file mode 100644 index 0000000..0f6c377 Binary files /dev/null and b/romfs/background2.png differ diff --git a/romfs/crash_0.png b/romfs/crash_0.png new file mode 100644 index 0000000..a1a394b Binary files /dev/null and b/romfs/crash_0.png differ diff --git a/romfs/crash_1.png b/romfs/crash_1.png new file mode 100644 index 0000000..88ee1ab Binary files /dev/null and b/romfs/crash_1.png differ diff --git a/romfs/crash_10.png b/romfs/crash_10.png new file mode 100644 index 0000000..6f95c55 Binary files /dev/null and b/romfs/crash_10.png differ diff --git a/romfs/crash_11.png b/romfs/crash_11.png new file mode 100644 index 0000000..8f4e6fb Binary files /dev/null and b/romfs/crash_11.png differ diff --git a/romfs/crash_12.png b/romfs/crash_12.png new file mode 100644 index 0000000..95160ba Binary files /dev/null and b/romfs/crash_12.png differ diff --git a/romfs/crash_13.png b/romfs/crash_13.png new file mode 100644 index 0000000..674e98d Binary files /dev/null and b/romfs/crash_13.png differ diff --git a/romfs/crash_2.png b/romfs/crash_2.png new file mode 100644 index 0000000..cfce00d Binary files /dev/null and b/romfs/crash_2.png differ diff --git a/romfs/crash_3.png b/romfs/crash_3.png new file mode 100644 index 0000000..eaf2058 Binary files /dev/null and b/romfs/crash_3.png differ diff --git a/romfs/crash_4.png b/romfs/crash_4.png new file mode 100644 index 0000000..5726fc1 Binary files /dev/null and b/romfs/crash_4.png differ diff --git a/romfs/crash_5.png b/romfs/crash_5.png new file mode 100644 index 0000000..a811dee Binary files /dev/null and b/romfs/crash_5.png differ diff --git a/romfs/crash_6.png b/romfs/crash_6.png new file mode 100644 index 0000000..3754a21 Binary files /dev/null and b/romfs/crash_6.png differ diff --git a/romfs/crash_7.png b/romfs/crash_7.png new file mode 100644 index 0000000..efe5887 Binary files /dev/null and b/romfs/crash_7.png differ diff --git a/romfs/crash_8.png b/romfs/crash_8.png new file mode 100644 index 0000000..b745f4b Binary files /dev/null and b/romfs/crash_8.png differ diff --git a/romfs/crash_9.png b/romfs/crash_9.png new file mode 100644 index 0000000..f744f86 Binary files /dev/null and b/romfs/crash_9.png differ diff --git a/romfs/engine.wav b/romfs/engine.wav new file mode 100644 index 0000000..ed2d71f Binary files /dev/null and b/romfs/engine.wav differ diff --git a/romfs/explosion.wav b/romfs/explosion.wav new file mode 100644 index 0000000..296207f Binary files /dev/null and b/romfs/explosion.wav differ diff --git a/romfs/heart.png b/romfs/heart.png new file mode 100644 index 0000000..8f6476d Binary files /dev/null and b/romfs/heart.png differ diff --git a/romfs/icon.jpg b/romfs/icon.jpg new file mode 100644 index 0000000..0e34dd8 Binary files /dev/null and b/romfs/icon.jpg differ diff --git a/romfs/icon.png b/romfs/icon.png new file mode 100644 index 0000000..c519f9e Binary files /dev/null and b/romfs/icon.png differ diff --git a/romfs/laser_beam.wav b/romfs/laser_beam.wav new file mode 100644 index 0000000..3b3faf4 Binary files /dev/null and b/romfs/laser_beam.wav differ diff --git a/romfs/lilliput_steps.ttf b/romfs/lilliput_steps.ttf new file mode 100644 index 0000000..7c2317d Binary files /dev/null and b/romfs/lilliput_steps.ttf differ diff --git a/romfs/machine_gun.wav b/romfs/machine_gun.wav new file mode 100644 index 0000000..5ba0c87 Binary files /dev/null and b/romfs/machine_gun.wav differ diff --git a/romfs/menu_background.png b/romfs/menu_background.png new file mode 100644 index 0000000..74dbc10 Binary files /dev/null and b/romfs/menu_background.png differ diff --git a/romfs/menu_click.wav b/romfs/menu_click.wav new file mode 100644 index 0000000..ed981f1 Binary files /dev/null and b/romfs/menu_click.wav differ diff --git a/romfs/menu_select.wav b/romfs/menu_select.wav new file mode 100644 index 0000000..eb24447 Binary files /dev/null and b/romfs/menu_select.wav differ diff --git a/romfs/menu_select2.wav b/romfs/menu_select2.wav new file mode 100644 index 0000000..825ecd3 Binary files /dev/null and b/romfs/menu_select2.wav differ diff --git a/romfs/rocket.wav b/romfs/rocket.wav new file mode 100644 index 0000000..f47ff85 Binary files /dev/null and b/romfs/rocket.wav differ diff --git a/romfs/round_begin.wav b/romfs/round_begin.wav new file mode 100644 index 0000000..9a621d8 Binary files /dev/null and b/romfs/round_begin.wav differ diff --git a/romfs/round_end.wav b/romfs/round_end.wav new file mode 100644 index 0000000..990e53b Binary files /dev/null and b/romfs/round_end.wav differ diff --git a/romfs/settings_0.png b/romfs/settings_0.png new file mode 100644 index 0000000..90bfede Binary files /dev/null and b/romfs/settings_0.png differ diff --git a/romfs/settings_1.png b/romfs/settings_1.png new file mode 100644 index 0000000..073bb51 Binary files /dev/null and b/romfs/settings_1.png differ diff --git a/romfs/smoke_0.png b/romfs/smoke_0.png new file mode 100644 index 0000000..62b8b86 Binary files /dev/null and b/romfs/smoke_0.png differ diff --git a/romfs/smoke_1.png b/romfs/smoke_1.png new file mode 100644 index 0000000..917cce2 Binary files /dev/null and b/romfs/smoke_1.png differ diff --git a/romfs/smoke_2.png b/romfs/smoke_2.png new file mode 100644 index 0000000..ff11460 Binary files /dev/null and b/romfs/smoke_2.png differ diff --git a/romfs/smoke_3.png b/romfs/smoke_3.png new file mode 100644 index 0000000..1446624 Binary files /dev/null and b/romfs/smoke_3.png differ diff --git a/romfs/smoke_4.png b/romfs/smoke_4.png new file mode 100644 index 0000000..f63529c Binary files /dev/null and b/romfs/smoke_4.png differ diff --git a/romfs/smoke_5.png b/romfs/smoke_5.png new file mode 100644 index 0000000..5505c7c Binary files /dev/null and b/romfs/smoke_5.png differ diff --git a/romfs/sniper_rifle.wav b/romfs/sniper_rifle.wav new file mode 100644 index 0000000..6c3aab0 Binary files /dev/null and b/romfs/sniper_rifle.wav differ diff --git a/src/CustomPaintCallbacks.cpp b/src/CustomPaintCallbacks.cpp new file mode 100644 index 0000000..156c608 --- /dev/null +++ b/src/CustomPaintCallbacks.cpp @@ -0,0 +1,13 @@ +#include "CustomPaintCallbacks.h" +#include "GUI.h" + +void MainMenuCallback(CPanel *panel){ + render->Printf(vec2_t(SCREEN_WIDTH/2, 140), true, {255, 255, 255}, fonts_ids::MENU_LOGO, (char*)"2PLANES"); + render->Printf(vec2_t(20, 680), false, {255, 255, 255}, fonts_ids::MENU_1, (char*)"by mactec"); +} + + +void SettingsCallback(CPanel *panel){ + vec2_t pos=vec2_t(200,80); + render->DrawTexture(texture_manager->GetTexture(textures_ids::SETTINGS_0+panel->GetEntry(ELEMENTS::CONTROLLER_MODE_LIST)->GetValue()), pos); +} \ No newline at end of file diff --git a/src/CustomPaintCallbacks.h b/src/CustomPaintCallbacks.h new file mode 100644 index 0000000..ba251f6 --- /dev/null +++ b/src/CustomPaintCallbacks.h @@ -0,0 +1,8 @@ +#pragma once +#include "Render.h" + +class CPanel; + +void MainMenuCallback(CPanel *panel); + +void SettingsCallback(CPanel *panel); \ No newline at end of file diff --git a/src/Entity.cpp b/src/Entity.cpp new file mode 100644 index 0000000..e662e81 --- /dev/null +++ b/src/Entity.cpp @@ -0,0 +1,383 @@ +#include "Entity.h" + + + +CEntity::CEntity(int id){ + this->id=id; + plane_left=id%2; + weapons[WEAPON_MACHINEGUN] = new CWeaponMachineGun(); + weapons[WEAPON_ROCKETLAUNCHER] = new CWeaponRocketLauncher(); + weapons[WEAPON_SNIPERRIFLE] = new CWeaponSniperRifle(); + weapons[WEAPON_LASER] = new CWeaponLaser(); + primary_weapon=weapons[WEAPON_MACHINEGUN]; + secondary_weapon=weapons[WEAPON_ROCKETLAUNCHER]; + destroyed=false; + + spawn_protect_timer.SetAlarm(12000); + round_begin_lock_timer.SetAlarm(5000); + crash_timer.SetAlarm(392); + smoke_timer.SetAlarm(25); + + lifes=0; + + sound_manager->PlaySound(sound_effects_ids::ENGINE_1+plane_left,-1); + + Reset(); +} + +CEntity::~CEntity(){ + for(int i=0;i<4;i++) + delete weapons[i]; +} + + +void CEntity::Reset(){ + angle = 0.f; + velocity = 0.f; + state = STATE_NORMAL; + on_ground=true; + health=100; + + pos.y=SCREEN_HEIGHT-GROUND-PLANE_SIZE; + pos.x=(plane_left)?40:(SCREEN_WIDTH-40); + + SpawnProtect(); + + round_begin_lock=true; + round_begin_lock_timer.Start(); + + sound_manager->SetVolume(sound_effects_ids::ENGINE_1+plane_left,0); + + primary_weapon->Reset(); + secondary_weapon->Reset(); + vsmoke.clear(); +} + + +void CEntity::ClampVelocity(){ + if(velocity>max_speed) + velocity=max_speed; + + if (velocity<0) + velocity=0; +} + + +void CEntity::NormalizeAngle(){ + if(angle>360.f) + angle-=360.f; + if(angle<0) + angle+=360.f; +} + + + +void CEntity::Draw(){ + + if(state==STATE_CRASHED){ + auto elapsed=crash_timer.GetElapsed(); + if(!crash_timer.Alarm()){ + int tex_id=floor(elapsed/28); + vec2_t crash_pos=pos+vec2_t(-32,-24); + render->DrawTexture(texture_manager->GetTexture(textures_ids::CRASH_0+tex_id), crash_pos); + }else{ + destroyed=true; + } + }else if(state==STATE_NORMAL){ + + primary_weapon->Draw(pos); + secondary_weapon->Draw(pos); + render->DrawPlaneTexture(texture_manager->GetTexture(textures_ids::PLANE_1+id-1), pos, angle); + + //crosshair + SDL_SetRenderDrawColor(render->GetRenderer(),0,0,0,255); + vec2_t cross_pos=pos; + float cross_dist=35.f+(velocity*180.f); + cross_pos.y+=sin((plane_left?angle:angle+180.f)*M_PI/180.f)*cross_dist; + cross_pos.x+=cos((plane_left?angle:angle+180.f)*M_PI/180.f)*cross_dist; + + SDL_Rect cross_rect; + cross_rect.x=(int)cross_pos.x-4; + cross_rect.y=(int)cross_pos.y; + cross_rect.w=9; + cross_rect.h=1; + SDL_RenderFillRect(render->GetRenderer(),&cross_rect); + cross_rect.x=(int)cross_pos.x; + cross_rect.y=(int)cross_pos.y-4; + cross_rect.w=1; + cross_rect.h=9; + SDL_RenderFillRect(render->GetRenderer(),&cross_rect); + + for(auto &smoke: vsmoke){ + float h=50-health; + if(h>40.f) + h=40.f; + float max_alpha=255.f*(h/40.f); + float diff=SDL_GetTicks()-smoke.born_time; + if(diff>210.f) + diff=210.f; + float alpha=(1.f-(diff)/210.f)*max_alpha; + if(alpha>max_alpha) + alpha=max_alpha; + SDL_SetTextureAlphaMod(texture_manager->GetTexture(textures_ids::SMOKE_0+smoke.tex_id),(int)alpha); + render->DrawTexture(texture_manager->GetTexture(textures_ids::SMOKE_0+smoke.tex_id), smoke.pos); + + } + + } + + + +} + + +void CEntity::Control(float delta){ + if(spawn_protection){ + int alpha=SDL_GetTicks()%800; + if(alpha>400) + alpha=800-alpha; + alpha/=4; + SDL_SetTextureAlphaMod(texture_manager->GetTexture(textures_ids::PLANE_1+id-1),((int)alpha%200+55)); + + if(spawn_protect_timer.Alarm()){ + spawn_protection=false; + SDL_SetTextureAlphaMod(texture_manager->GetTexture(textures_ids::PLANE_1+id-1),255); + } + } + + if(round_begin_lock_timer.Alarm()) + round_begin_lock=false; + + + if(state==STATE_NORMAL&&!round_begin_lock){ + + if(health<50&&smoke_timer.Alarm()){ + vsmoke.push_back(SSmokePart(pos,rand()%6,SDL_GetTicks())); + smoke_timer.Start(); + } + + vsmoke.erase( + std::remove_if(vsmoke.begin(), vsmoke.end(), + [](const SSmokePart & smoke) { return (smoke.born_time+210IsPressed(joycon_keys::BUTTON_B)){ + velocity+=accel*delta; + }else{ + velocity-=(accel*delta)/2.f; + } + + ClampVelocity(); + + sound_manager->SetVolume(sound_effects_ids::ENGINE_1+plane_left,velocity*200.f); + + if(key_manager->IsPressed(joycon_keys::BUTTON_DPAD_LEFT)|| + key_manager->IsPressed(joycon_keys::BUTTON_LSTICK_LEFT)){ + if(!on_ground||velocity>0.26){ + angle+=velocity*delta*(plane_left?-0.42:0.42); + } + } + + if(key_manager->IsPressed(joycon_keys::BUTTON_DPAD_RIGHT)|| + key_manager->IsPressed(joycon_keys::BUTTON_LSTICK_RIGHT)){ + if(!on_ground||velocity>0.26){ + angle+=velocity*delta*(plane_left?0.42:-0.42); + } + } + + NormalizeAngle(); + + + float real_gravity=(gravity-velocity)*delta; + if(real_gravity>=0) + if(!on_ground) + pos.y+=real_gravity; + + + + vec2_t vmove((delta*velocity)*cos(angle*M_PI/180.f), + (delta*velocity)*sin(angle*M_PI/180.f)); + + + + pos+=(plane_left)?vmove:-vmove; + + if(pos.y+PLANE_SIZE>=SCREEN_HEIGHT-GROUND){ + pos.y=(SCREEN_HEIGHT-GROUND)-PLANE_SIZE; + on_ground=true; + }else{ + on_ground=false; + } + + if(pos.y<-400.f) + pos.y=-400.f; + + + if(SCREEN_WIDTH10):(on_ground&&angle<350&&angle>30)){ + sound_manager->SetVolume(sound_effects_ids::ENGINE_1+plane_left,0); + Crash(); + return; + } + + + if(on_ground&&velocity<0.18){ + if(plane_left){ + if(angle>330.f){ + if(velocity!=0.f){ + angle+=0.006f*delta*(1/velocity); + }else{ + angle+=0.006f*delta*1.4f; + } + if(angle<330.f) + angle=0.f; + } + }else{ + if(angle<30.f){ + if(velocity!=0){ + angle-=0.006f*delta*(1/velocity); + }else{ + angle-=0.006f*delta*1.4f; + } + if(angle<0.f) + angle=0.f; + } + } + } + + primary_weapon->Simulate(delta); + if(!spawn_protection&&!on_ground&&key_manager->IsPressed(joycon_keys::BUTTON_Y)){ + vec2_t plane_center=vec2_t(pos.x,pos.y+5); + primary_weapon->Fire(plane_center,plane_left?angle:angle+180.f,plane_left); + } + + secondary_weapon->Simulate(delta); + if(!spawn_protection&&!on_ground&&key_manager->IsPressed(joycon_keys::BUTTON_A)){ + vec2_t plane_center=vec2_t(pos.x,pos.y+5); + secondary_weapon->Fire(plane_center,plane_left?angle:angle+180.f,plane_left); + } + + + if(health<=0){ + Crash(); + } + } +} + +void CEntity::DrawHealth(){ + const int hbar_width=160; + const int hbar_height=28; + + SDL_SetRenderDrawColor(render->GetRenderer(),0,0,0,255); + SDL_Rect hbar_rect; + hbar_rect.y=45; + hbar_rect.w=hbar_width; + hbar_rect.h=hbar_height; + hbar_rect.x=plane_left?12:(SCREEN_WIDTH-(hbar_width+12)); + SDL_RenderFillRect(render->GetRenderer(),&hbar_rect); + + + int lifes_start_x=plane_left?12:SCREEN_WIDTH-12; + for(int i=0;iDrawTexture(texture_manager->GetTexture(textures_ids::HEART), heart_pos); + } + + + hbar_rect.x+=2; + hbar_rect.y+=2; + hbar_rect.h-=4; + hbar_rect.w-=4; + SDL_SetRenderDrawColor(render->GetRenderer(),200,0,0,255); + SDL_RenderFillRect(render->GetRenderer(),&hbar_rect); + + hbar_rect.w=(float)hbar_rect.w*((float)health/100.f); + SDL_SetRenderDrawColor(render->GetRenderer(),0,200,0,255); + SDL_RenderFillRect(render->GetRenderer(),&hbar_rect); + +} + + +bool CEntity::CheckCollision(vec2_t &plane_pos,int &dmg){ + if(primary_weapon&&primary_weapon->CheckCollision(plane_pos)){ + dmg=primary_weapon->GetDamage(); + return true; + } + if(secondary_weapon&&secondary_weapon->CheckCollision(plane_pos)){ + dmg=secondary_weapon->GetDamage(); + return true; + } + return false; +} + +vec2_t CEntity::GetPos(){ + return pos; +} + + +void CEntity::Hurt(int dmg){ + health-=dmg; + if(health<0) + health=0; +} + + + + +void CEntity::SetPrimaryWeapon(int weapon_ID){ + primary_weapon=weapons[weapon_ID]; +} + +void CEntity::SetSecondaryWeapon(int weapon_ID){ + secondary_weapon=weapons[weapon_ID]; +} + + +void CEntity::SpawnProtect(){ + spawn_protection=true; + spawn_protect_timer.Start(); +} + +void CEntity::Crash(){ + crash_timer.Start(); + state=STATE_CRASHED; + vsmoke.clear(); + lifes--; + sound_manager->PlaySound(sound_effects_ids::EXPLOSION); +} + +bool CEntity::IsDestroyed(){ + if(state==STATE_CRASHED&&destroyed) + return true; + return false; +} + +bool CEntity::IsProtected(){ + return spawn_protection; +} + + +void CEntity::SetLifes(int lifes){ + this->lifes=lifes; +} + + +int CEntity::GetLifes(){ + return lifes; +} + +int CEntity::GetHealth(){ + return health; +} + + +void CEntity::MuteEngine(){ + sound_manager->SetVolume(sound_effects_ids::ENGINE_1+plane_left,0); +} + diff --git a/src/Entity.h b/src/Entity.h new file mode 100644 index 0000000..8ea812a --- /dev/null +++ b/src/Entity.h @@ -0,0 +1,132 @@ +#pragma once +#include "vec2.h" +#include "Render.h" +#include "KeyManager.h" +#include "TextureManager.h" +#include "SoundManager.h" +#include +#include +#include + + +#include +#include +#include +#include + +#include "Timer.h" +#include "WeaponMachineGun.h" +#include "WeaponRocketLauncher.h" +#include "WeaponSniperRifle.h" +#include "WeaponLaser.h" + + +#define PLANE_SIZE 42 +#define PLANE_RADIUS PLANE_SIZE/2 + +#define GROUND 70 +#define PLANE_MAX 2 + + +enum{ + STATE_NORMAL, + STATE_CRASHED, + STATE_RESPAWN, +}; + +enum{ + PLANE_RED, + PLANE_BLUE +}; + + +struct SSmokePart{ + SSmokePart(vec2_t pos, + int tex_id, + Uint32 born_time){ + this->pos=pos; + this->tex_id=tex_id; + this->born_time=born_time; + + this->pos.y-=32; + this->pos.x-=32; + + } + vec2_t pos; + int tex_id; + float angle; + Uint32 born_time; +}; + +class CEntity{ + const float max_speed=0.5; + const float accel=0.00015; + const float gravity=0.4; + vec2_t pos; + float angle; + float velocity; + bool on_ground; + int id; + int state; + CWeapon *weapons[WEAPON_MAX]; + CWeapon *primary_weapon, *secondary_weapon; + int health, lifes; + bool plane_left; + + bool spawn_protection; + Timer spawn_protect_timer; + + bool round_begin_lock; + Timer round_begin_lock_timer; + + bool destroyed; + Timer crash_timer; + + Timer smoke_timer; + std::vector vsmoke; + + void ClampVelocity(); + + void NormalizeAngle(); + +public: + CEntity(int id); + ~CEntity(); + + void Reset(); + + void Draw(); + + void Control(float delta); + + void DrawHealth(); + + bool CheckCollision(vec2_t &plane_pos, int &dmg); + + vec2_t GetPos(); + + void Hurt(int dmg); + + void SetPrimaryWeapon(int weapon_ID); + void SetSecondaryWeapon(int weapon_ID); + + void SpawnProtect(); + + void Crash(); + + bool IsDestroyed(); + + bool IsProtected(); + + void SetLifes(int lifes); + + int GetLifes(); + + int GetHealth(); + + void MuteEngine(); +}; + +extern CEntity * entities[PLANE_MAX]; + + diff --git a/src/FontManager.cpp b/src/FontManager.cpp new file mode 100644 index 0000000..ca886ab --- /dev/null +++ b/src/FontManager.cpp @@ -0,0 +1,55 @@ +#include "FontManager.h" + + +CFontManager::CFontManager(){ + initialized=false; + + if(TTF_Init()==-1){ + return; + } + + fonts[fonts_ids::MENU_1] = TTF_OpenFont("romfs:/lilliput_steps.ttf",24); + fonts[fonts_ids::MENU_2] = TTF_OpenFont("romfs:/lilliput_steps.ttf",18); + fonts[fonts_ids::MENU_3] = TTF_OpenFont("romfs:/lilliput_steps.ttf",32); + fonts[fonts_ids::MENU_LOGO] = TTF_OpenFont("romfs:/lilliput_steps.ttf",56); + + + for(int i=0;iinitialized; +} + + +CFontManager::~CFontManager(){ + + for(int i=0;i=fonts_ids::FONTS_MAX) + return fonts[0]; + return fonts[ID]; +} + + + diff --git a/src/FontManager.h b/src/FontManager.h new file mode 100644 index 0000000..acf8d3f --- /dev/null +++ b/src/FontManager.h @@ -0,0 +1,31 @@ +#pragma once +#include +#include +#include +#include "Render.h" + +enum fonts_ids{ + MENU_1, + MENU_2, + MENU_3, + MENU_LOGO, + FONTS_MAX + +}; + +class CFontManager{ + TTF_Font *fonts[fonts_ids::FONTS_MAX]; + bool initialized; + + TTF_Font* LoadFont(const char *path, int ID, int size); + +public: + + CFontManager(); + ~CFontManager(); + bool IsInitialized(); + + TTF_Font* GetFont(int ID); +}; + +extern CFontManager *font_manager; diff --git a/src/GUI.cpp b/src/GUI.cpp new file mode 100644 index 0000000..58b7e6e --- /dev/null +++ b/src/GUI.cpp @@ -0,0 +1,400 @@ +#include "GUI.h" + + +CGUI::CGUI(){ + visible=true; + current_panel=PANELS::MAINMENU; + + panels.push_back(new CPanel(PANELS::MAINMENU,490,300,(void*)&MainMenuCallback)); + panels.push_back(new CPanel(PANELS::SETTINGS,490,500,(void*)&SettingsCallback)); + panels.push_back(new CPanel(PANELS::PLAY_SELECT,490,40)); + panels.push_back(new CPanel(PANELS::PAUSE,490,320)); + + panels[PANELS::MAINMENU]->AddEntry(new CEntryButton("PLAY", ELEMENTS::PLAY_BUTTON)); + panels[PANELS::MAINMENU]->AddEntry(new CEntryButton("CONTROLS", ELEMENTS::CONTROLS_BUTTON)); + panels[PANELS::MAINMENU]->AddEntry(new CEntryButton("QUIT", ELEMENTS::QUIT_BUTTON)); + + + std::string *control_modes = new std::string[2]; + control_modes[0] = "TWO CONTROLLERS"; + control_modes[1] = "JOY CONS"; + panels[PANELS::SETTINGS]->AddEntry(new CEntryList("CONTROL MODE:",ELEMENTS::CONTROLLER_MODE_LIST, control_modes,2)); + panels[PANELS::SETTINGS]->AddEntry(new CEntryButton("BACK TO MAINMENU", ELEMENTS::MAINMENU_BUTTON)); + + + + + std::string *map_list = new std::string[2]; + map_list[0] = "SNOWY"; + map_list[1] = "GREEN"; + panels[PANELS::PLAY_SELECT]->AddEntry(new CEntryList("MAP SELECTION:",ELEMENTS::MAP_SELECTION_LIST, map_list,2)); + + std::string *lives_list = new std::string[4]; + lives_list[0] = "1"; + lives_list[1] = "3"; + lives_list[2] = "6"; + lives_list[3] = "9"; + panels[PANELS::PLAY_SELECT]->AddEntry(new CEntryList("MAX LIVES:",ELEMENTS::LIVES_SELECTION_LIST, lives_list,4,1)); + + std::string *wpn_list = new std::string[4]; + wpn_list[0] = "MACHINE GUN"; + wpn_list[1] = "RPG"; + wpn_list[2] = "SNIPER RIFLE"; + wpn_list[3] = "LASER GUN"; + panels[PANELS::PLAY_SELECT]->AddEntry(new CEntryList("P1 PRIMARY WEAPON:",ELEMENTS::P1_WPN_SELECTION_1_LIST, wpn_list,4)); + panels[PANELS::PLAY_SELECT]->AddEntry(new CEntryList("P1 SECONDARY WEAPON:",ELEMENTS::P1_WPN_SELECTION_2_LIST, wpn_list,4,1)); + panels[PANELS::PLAY_SELECT]->AddEntry(new CEntryList("P2 PRIMARY WEAPON:",ELEMENTS::P2_WPN_SELECTION_1_LIST, wpn_list,4)); + panels[PANELS::PLAY_SELECT]->AddEntry(new CEntryList("P2 SECONDARY WEAPON:",ELEMENTS::P2_WPN_SELECTION_2_LIST, wpn_list,4,1)); + + panels[PANELS::PLAY_SELECT]->AddEntry(new CEntryButton("START", ELEMENTS::START_BUTTON)); + + + panels[PANELS::PAUSE]->AddEntry(new CEntryButton("RETURN TO GAME", ELEMENTS::RETURN_TO_GAME_BUTTON)); + panels[PANELS::PAUSE]->AddEntry(new CEntryButton("BACK TO MAINMENU", ELEMENTS::MAINMENU_BUTTON)); + + + list_to_remove.push_back(map_list); + list_to_remove.push_back(lives_list); + list_to_remove.push_back(wpn_list); + list_to_remove.push_back(control_modes); + +} + +CGUI::~CGUI(){ + for(auto &to_remove:list_to_remove){ + delete [] to_remove; + } +} + +bool CGUI::IsVisible(){ + return visible; +} + +void CGUI::SetVisibility(bool visible){ + this->visible=visible; +} + +void CGUI::Control(float delta){ + const float background_move=100.f; + background_pos.x+=delta*background_move; + background_pos.y+=delta*background_move; + + + key_manager->SetController(0); + + int last_entry_ID=panels[current_panel]->GetCurrentEntryID(); + if(key_manager->IsClicked(joycon_keys::BUTTON_DPAD_UP)|| + key_manager->IsClicked(joycon_keys::BUTTON_LSTICK_UP)){ + panels[current_panel]->Previous(); + } + if(key_manager->IsClicked(joycon_keys::BUTTON_DPAD_DOWN)|| + key_manager->IsClicked(joycon_keys::BUTTON_LSTICK_DOWN)){ + panels[current_panel]->Next(); + } + + if(panels[current_panel]->GetCurrentEntryID()!=last_entry_ID) + sound_manager->PlaySound(sound_effects_ids::MENU_SELECT); + + panels[current_panel]->Control(delta); + + switch(current_panel){ + case PANELS::MAINMENU: + if(panels[PANELS::MAINMENU]->GetEntry(ELEMENTS::PLAY_BUTTON)->Clicked()){ + current_panel=PANELS::PLAY_SELECT; + } + if(panels[PANELS::MAINMENU]->GetEntry(ELEMENTS::CONTROLS_BUTTON)->Clicked()){ + current_panel=PANELS::SETTINGS; + } + if(panels[PANELS::MAINMENU]->GetEntry(ELEMENTS::QUIT_BUTTON)->Clicked()){ + game_state->Set(GAME_STATES::QUIT_GAME); + } + break; + case PANELS::SETTINGS: + key_manager->UseJoyCons(panels[PANELS::SETTINGS]->GetEntry(ELEMENTS::CONTROLLER_MODE_LIST)->GetValue()); + key_manager->ClearAll(); + if(panels[PANELS::SETTINGS]->GetEntry(ELEMENTS::MAINMENU_BUTTON)->Clicked()){ + current_panel=PANELS::MAINMENU; + } + break; + case PANELS::PLAY_SELECT: + if(panels[PANELS::PLAY_SELECT]->GetEntry(ELEMENTS::START_BUTTON)->Clicked()){ + if(panels[PANELS::PLAY_SELECT]->GetEntry(ELEMENTS::P1_WPN_SELECTION_1_LIST)->GetValue()== + panels[PANELS::PLAY_SELECT]->GetEntry(ELEMENTS::P1_WPN_SELECTION_2_LIST)->GetValue()|| + panels[PANELS::PLAY_SELECT]->GetEntry(ELEMENTS::P2_WPN_SELECTION_1_LIST)->GetValue()== + panels[PANELS::PLAY_SELECT]->GetEntry(ELEMENTS::P2_WPN_SELECTION_2_LIST)->GetValue()){ + //error, you can't choose the same weapon twice + }else{ + current_panel=PANELS::PAUSE; + visible=false; + int lifes[]={1,3,6,9}; + for(int i=0;iReset(); + entities[i]->SetLifes(lifes[panels[PANELS::PLAY_SELECT]->GetEntry(ELEMENTS::LIVES_SELECTION_LIST)->GetValue()]); + entities[i]->SetPrimaryWeapon(panels[PANELS::PLAY_SELECT]->GetEntry(ELEMENTS::P1_WPN_SELECTION_1_LIST+i*2)->GetValue()); + entities[i]->SetSecondaryWeapon(panels[PANELS::PLAY_SELECT]->GetEntry(ELEMENTS::P1_WPN_SELECTION_2_LIST+i*2)->GetValue()); + } + sound_manager->PlaySound(sound_effects_ids::ROUND_BEGIN); + game_state->Set(GAME_STATES::IN_GAME); + active_map=maps[panels[PANELS::PLAY_SELECT]->GetEntry(ELEMENTS::MAP_SELECTION_LIST)->GetValue()]; + + } + } + break; + case PANELS::PAUSE: + for(int i=0;iMuteEngine(); + + if(panels[PANELS::PAUSE]->GetEntry(ELEMENTS::RETURN_TO_GAME_BUTTON)->Clicked()){ + visible=false; + } + if(panels[PANELS::PAUSE]->GetEntry(ELEMENTS::MAINMENU_BUTTON)->Clicked()){ + current_panel=PANELS::MAINMENU; + } + + break; + } +} + +void CGUI::Draw(){ + DrawBackground(); + panels[current_panel]->Draw(); +} + +void CGUI::DrawBackground(){ + const int BACKGROUND_WIDTH=1280,BACKGROUND_HEIGHT=640; + if(background_pos.x>BACKGROUND_WIDTH) + background_pos.x-=BACKGROUND_WIDTH; + if(background_pos.y>BACKGROUND_HEIGHT) + background_pos.y-=BACKGROUND_HEIGHT; + + render->DrawTexture(texture_manager->GetTexture(MENU_BACKGROUND), background_pos); + + if(background_pos.y+BACKGROUND_HEIGHT<720){ + vec2_t next_background_pos=vec2_t(background_pos.x,background_pos.y+BACKGROUND_HEIGHT); + render->DrawTexture(texture_manager->GetTexture(MENU_BACKGROUND), next_background_pos); + } + if(background_pos.y>0){ + vec2_t next_background_pos=vec2_t(background_pos.x,background_pos.y-BACKGROUND_HEIGHT); + render->DrawTexture(texture_manager->GetTexture(MENU_BACKGROUND), next_background_pos); + } + if(background_pos.x>0){ + vec2_t next_background_pos=vec2_t(background_pos.x-BACKGROUND_WIDTH,background_pos.y); + render->DrawTexture(texture_manager->GetTexture(MENU_BACKGROUND), next_background_pos); + if(next_background_pos.y+BACKGROUND_HEIGHT<720){ + next_background_pos=vec2_t(next_background_pos.x,next_background_pos.y+BACKGROUND_HEIGHT); + render->DrawTexture(texture_manager->GetTexture(MENU_BACKGROUND), next_background_pos); + } + } + if(background_pos.x>0&&background_pos.y>0){ + vec2_t next_background_pos=vec2_t(background_pos.x-BACKGROUND_WIDTH,background_pos.y-BACKGROUND_HEIGHT); + render->DrawTexture(texture_manager->GetTexture(MENU_BACKGROUND), next_background_pos); + } +} + +int CGUI::GetActivePanel(){ + return current_panel; +} + +void CGUI::SetPause(){ + visible=true; +} + +void CGUI::SetActivePanel(int panel){ + current_panel=panel; +} + +CPanel::CPanel(int ID, int x, int y, void *callback){ + this->ID=ID; + this->x=x; + this->y=y; + current_entry_id=0; + custom_paint_callback=callback; +} + +CPanel::~CPanel(){ + +} + +void CPanel::Control(float delta){ + int entry_id=0; + for(auto & entry: entries){ + entry->Control(current_entry_id==entry_id); + entry_id++; + } +} + +void CPanel::Draw(){ + int draw_x=x,draw_y=y; + int entry_id=0; + for(auto & entry: entries){ + entry->Draw(current_entry_id==entry_id,draw_x,draw_y); + entry_id++; + draw_y+=100; + } + if(custom_paint_callback){ + typedef void (*custom_paint_callback_t)(CPanel *panel); + custom_paint_callback_t paint_callback_call=(custom_paint_callback_t)custom_paint_callback; + paint_callback_call(this); + } +} +CEntry *CPanel::GetEntry(int ID){ + for(auto & entry: entries){ + if(entry->ID==ID) + return entry; + } + return NULL; +} + +void CPanel::AddEntry(CEntry* entry){ + entries.push_back(entry); +} + +int CPanel::GetCurrentEntryID(){ + return current_entry_id; +} + +void CPanel::Previous(){ + current_entry_id--; + if(current_entry_id<0) + current_entry_id=0; +} + +void CPanel::Next(){ + current_entry_id++; + if(current_entry_id>=entries.size()) + current_entry_id=entries.size()-1; +} + + +CEntryButton::CEntryButton(std::string text, int ID){ + this->text=text; + this->ID=ID; + clicked=false; +} + +CEntryButton::~CEntryButton(){ + +} + +void CEntryButton::Control(bool active){ + if(key_manager->IsClicked(joycon_keys::BUTTON_A)&&active){ + clicked=true; + sound_manager->PlaySound(sound_effects_ids::MENU_CLICK); + } +} + + +void CEntryButton::Draw(bool active, int x, int y){ + SDL_SetRenderDrawColor(render->GetRenderer(),0,41,108,255); + if(active) + SDL_SetRenderDrawColor(render->GetRenderer(),0,31,77,255); + + SDL_Rect rect; + rect.x=(int)x; + rect.y=(int)y; + rect.w=300; + rect.h=42; + SDL_RenderFillRect(render->GetRenderer(),&rect); + + SDL_Color color = {255, 255, 255}; + if(active) + render->Printf(vec2_t(x+rect.w/2,y+rect.h/2), true, color, fonts_ids::MENU_1, (char*)"[%s]", text.c_str()); + else + render->Printf(vec2_t(x+rect.w/2,y+rect.h/2), true, color, fonts_ids::MENU_1, (char*)"%s", text.c_str()); + + SDL_SetRenderDrawColor(render->GetRenderer(),0,20,50,255); + for(int i=0;i<4;i++){ + rect.x--; + rect.y--; + rect.w+=2; + rect.h+=2; + SDL_RenderDrawRect(render->GetRenderer(),&rect); + } +} + +bool CEntryButton::Clicked(){ + if(clicked){ + clicked=false; + return true; + } + return false; +} + +int CEntryButton::GetValue(){ + return (int)clicked; +} + + +CEntryList::CEntryList(std::string text, int ID, std::string *list, int list_size,int start_value){ + this->text=text; + this->ID=ID; + this->list=list; + this->list_size=list_size; + value=start_value; +} + +CEntryList::~CEntryList(){ + +} + +void CEntryList::Control(bool active){ + int last_value = value; + if(active&&(key_manager->IsClicked(joycon_keys::BUTTON_DPAD_RIGHT)|| + key_manager->IsClicked(joycon_keys::BUTTON_LSTICK_RIGHT))){ + value++; + } + if(active&&(key_manager->IsClicked(joycon_keys::BUTTON_DPAD_LEFT)|| + key_manager->IsClicked(joycon_keys::BUTTON_LSTICK_LEFT))){ + value--; + } + if(value<0) + value=0; + if(value>=list_size) + value=list_size-1; + + if(value!=last_value){ + sound_manager->PlaySound(sound_effects_ids::MENU_SELECT2); + } +} + +void CEntryList::Draw(bool active, int x, int y){ + SDL_SetRenderDrawColor(render->GetRenderer(),0,41,108,255); + if(active) + SDL_SetRenderDrawColor(render->GetRenderer(),0,31,77,255); + + SDL_Rect rect; + rect.x=(int)x; + rect.y=(int)y; + rect.w=300; + rect.h=42; + SDL_RenderFillRect(render->GetRenderer(),&rect); + + SDL_Color color = {255, 255, 255}; + if(active) + render->Printf(vec2_t(x+rect.w/2,y+rect.h/2), true, color, fonts_ids::MENU_1, (char*)"< %s >", list[value].c_str()); + else + render->Printf(vec2_t(x+rect.w/2,y+rect.h/2), true, color, fonts_ids::MENU_1, (char*)"%s", list[value].c_str()); + + + + SDL_Color color2 = {140, 140, 140}; + render->Printf(vec2_t(x-5,y-25), false, color2, fonts_ids::MENU_2, (char*)"%s", text.c_str()); + + SDL_SetRenderDrawColor(render->GetRenderer(),0,20,50,255); + for(int i=0;i<4;i++){ + rect.x--; + rect.y--; + rect.w+=2; + rect.h+=2; + SDL_RenderDrawRect(render->GetRenderer(),&rect); + } +} + +bool CEntryList::Clicked(){ + return false; +} + +int CEntryList::GetValue(){ + return value; +} + diff --git a/src/GUI.h b/src/GUI.h new file mode 100644 index 0000000..7880b02 --- /dev/null +++ b/src/GUI.h @@ -0,0 +1,128 @@ +#pragma once +#include "vec2.h" +#include "Render.h" +#include "CustomPaintCallbacks.h" +#include "TextureManager.h" +#include "SoundManager.h" +#include "KeyManager.h" +#include "Landscape.h" +#include "GameState.h" +#include "Entity.h" +#include +#include + +enum PANELS{ + MAINMENU, + SETTINGS, + PLAY_SELECT, + PAUSE +}; + +enum ELEMENTS{ + PLAY_BUTTON, + CONTROLS_BUTTON, + QUIT_BUTTON, + + CONTROLLER_MODE_LIST, + + START_BUTTON, + MAP_SELECTION_LIST, + LIVES_SELECTION_LIST, + P1_WPN_SELECTION_1_LIST, + P1_WPN_SELECTION_2_LIST, + P2_WPN_SELECTION_1_LIST, + P2_WPN_SELECTION_2_LIST, + + RETURN_TO_GAME_BUTTON, + MAINMENU_BUTTON + +}; + +class CEntry { +public: + int ID; + virtual void Control(bool active) = 0; + virtual void Draw(bool active, int x, int y) = 0; + virtual bool Clicked() = 0; + virtual int GetValue() = 0; +}; + +class CEntryButton: public CEntry{ + bool clicked; + std::string text; + +public: + CEntryButton(std::string text, int ID); + ~CEntryButton(); + + virtual void Control(bool active); + virtual void Draw(bool active, int x, int y); + virtual bool Clicked(); + virtual int GetValue(); + +}; + + +class CEntryList: public CEntry{ + int value; + std::string text; + std::string *list; + int list_size; + +public: + CEntryList(std::string text, int ID, std::string *list, int list_size, int start_value=0); + ~CEntryList(); + + virtual void Control(bool active); + virtual void Draw(bool active, int x, int y); + virtual bool Clicked(); + virtual int GetValue(); + +}; + +class CPanel{ + int ID; + int x,y; + int current_entry_id; + std::vector entries; + void *custom_paint_callback; +public: + CPanel(int ID, int x, int y, void *callback=NULL); + ~CPanel(); + + void AddEntry(CEntry* entry); + void Control(float delta); + void Draw(); + void Previous(); + void Next(); + int GetCurrentEntryID(); + CEntry *GetEntry(int ID); + +}; + + +class CGUI{ + int current_panel; + vec2_t background_pos; + std::vector panels; + bool visible; + + std::vector list_to_remove; + + void DrawBackground(); +public: + + CGUI(); + ~CGUI(); + + void SetVisibility(bool visible); + void SetPause(); + bool IsVisible(); + void Control(float delta); + void Draw(); + void SetActivePanel(int panel); + int GetActivePanel(); + +}; + +extern CGUI *gui; diff --git a/src/Game.cpp b/src/Game.cpp new file mode 100644 index 0000000..02bcfca --- /dev/null +++ b/src/Game.cpp @@ -0,0 +1,197 @@ + #include "Game.h" + +CGame::CGame(){ + after_death_timer.SetAlarm(4000); + winner=0; + + + Result rc = romfsInit(); + if (R_FAILED(rc)){ + game_state->Set(GAME_STATES::QUIT_GAME); + return; + } + + game_state = new CGameState(); + + key_manager = new CKeyManager(); + + render = new CGameRender(); + if(!render->IsInitialized()){ + printf("render initialization error\n"); + game_state->Set(GAME_STATES::QUIT_GAME); + return; + } + + texture_manager = new CTextureManager(); + if(!texture_manager->IsInitialized()){ + printf("texture_manager initialization error\n"); + game_state->Set(GAME_STATES::QUIT_GAME); + return; + } + + + font_manager = new CFontManager(); + if(!font_manager->IsInitialized()){ + printf("font_manager initialization error\n"); + game_state->Set(GAME_STATES::QUIT_GAME); + return; + } + + + sound_manager = new CSoundManager(); + if(!sound_manager->IsInitialized()){ + printf("sound_manager initialization error\n"); + game_state->Set(GAME_STATES::QUIT_GAME); + return; + } + + + gui = new CGUI(); + + for(int i=0; iSetFramerateLimit(60); + + for (int i = 0; i < 2; i++) { + if (SDL_JoystickOpen(i) == NULL) { + printf("SDL_JoystickOpen: %s\n", SDL_GetError()); + game_state->Set(GAME_STATES::QUIT_GAME); + return; + } + } + + maps[LANDSCAPE_SNOW] = new LandscapeSnow(); + maps[LANDSCAPE_GREEN] = new LandscapeGreen(); + active_map = maps[LANDSCAPE_SNOW]; + + last_time=SDL_GetTicks(); +} + +CGame::~CGame(){ + for(int i=0;iBeginFrame(); + + key_manager->CleanPressed(); + + SDL_Event event; + while (SDL_PollEvent(&event)) { + if(event.type==SDL_JOYBUTTONUP) + key_manager->Set(event.jbutton.button,false,event.jbutton.which); + + if(event.type==SDL_JOYBUTTONDOWN) + key_manager->Set(event.jbutton.button,true,event.jbutton.which); + } + + if(!gui->IsVisible()){ + + for(int i=0;iSetController(i); + entities[i]->Control((float)dtime); + } + + active_map->Control(fdtime); + + active_map->DrawBackground(); + + entities[0]->Draw(); + entities[1]->Draw(); + + active_map->DrawForeground(); + + entities[0]->DrawHealth(); + entities[1]->DrawHealth(); + + for(int i=0;iIsProtected()) + continue; + int dmg=0; + vec2_t pos =entities[j]->GetPos(); + if(entities[i]->CheckCollision(pos,dmg)){ + entities[j]->Hurt(dmg); + } + } + } + + + key_manager->SetController(0); + if(game_state->Get()==GAME_STATES::IN_GAME) + if(key_manager->IsClicked(joycon_keys::BUTTON_PLUS)|| + key_manager->IsClicked(joycon_keys::BUTTON_MINUS)){ + gui->SetPause(); + } + + if(game_state->Get()==GAME_STATES::IN_GAME) + for(int i=0;iIsDestroyed()) + continue; + game_state->Set(GAME_STATES::AFTER_ROUND); + sound_manager->PlaySound(sound_effects_ids::ROUND_END); + after_death_timer.Start(); + if(entities[i]->GetLifes()==0){ + game_state->Set(GAME_STATES::AFTER_GAME); + winner=i?1:2; + break; + } + } + + + if(game_state->Get()==GAME_STATES::AFTER_ROUND&&after_death_timer.Alarm()){ + for(int i=0;iReset(); + } + sound_manager->PlaySound(sound_effects_ids::ROUND_BEGIN); + game_state->Set(GAME_STATES::IN_GAME); + } + + if(game_state->Get()==GAME_STATES::AFTER_GAME){ + SDL_Color color = {0, 0, 0}; + std::string end_message="PLAYER "+std::to_string(winner)+" WON!!!"; + render->Printf(vec2_t(SCREEN_WIDTH/2,SCREEN_HEIGHT/2), true, color, fonts_ids::MENU_3, (char*)"%s", end_message.c_str()); + + if(after_death_timer.Alarm()){ + game_state->Set(GAME_STATES::IN_GAME); + gui->SetActivePanel(PANELS::MAINMENU); + gui->SetVisibility(true); + for(int i=0;iMuteEngine(); + } + + } + + }else{ + gui->Control(fdtime); + gui->Draw(); + } + + + + render->SwapBuffers(); + +} + +bool CGame::Quit(){ + return (game_state->Get()==GAME_STATES::QUIT_GAME); +} diff --git a/src/Game.h b/src/Game.h new file mode 100644 index 0000000..9cd6fe2 --- /dev/null +++ b/src/Game.h @@ -0,0 +1,37 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Landscape_green.h" +#include "Landscape_snow.h" +#include "TextureManager.h" +#include "SoundManager.h" +#include "FontManager.h" +#include "KeyManager.h" +#include "GameState.h" +#include "Render.h" +#include "Entity.h" +#include "Timer.h" +#include "GUI.h" + + +class CGame{ + Timer after_death_timer; + Uint32 last_time; + int winner; + +public: + CGame(); + ~CGame(); + void Run(); + bool Quit(); + +}; \ No newline at end of file diff --git a/src/GameState.cpp b/src/GameState.cpp new file mode 100644 index 0000000..1c4c499 --- /dev/null +++ b/src/GameState.cpp @@ -0,0 +1,18 @@ +#include "GameState.h" + + +CGameState::CGameState(){ + state=GAME_STATES::IN_GAME; +} + +CGameState::~CGameState(){ + +} + +void CGameState::Set(int new_state){ + state=new_state; +} + +int CGameState::Get(){ + return state; +} \ No newline at end of file diff --git a/src/GameState.h b/src/GameState.h new file mode 100644 index 0000000..702d707 --- /dev/null +++ b/src/GameState.h @@ -0,0 +1,21 @@ +#pragma once + +enum GAME_STATES{ + IN_GAME, + AFTER_ROUND, + AFTER_GAME, + QUIT_GAME +}; + + +class CGameState{ + int state; +public: + CGameState(); + ~CGameState(); + + void Set(int new_state); + int Get(); +}; + +extern CGameState* game_state; \ No newline at end of file diff --git a/src/KeyManager.cpp b/src/KeyManager.cpp new file mode 100644 index 0000000..f88137b --- /dev/null +++ b/src/KeyManager.cpp @@ -0,0 +1,103 @@ +#include "KeyManager.h" + + +CKeyManager::CKeyManager(){ + current_controller=0; + use_joycons=false; + for(int i=0;i=2) + which=1; + + if(use_joycons){ + which=to_joycon_key[id].which; + id=to_joycon_key[id].key_id; + }else if(which==joycon_right){ + if(id==BUTTON_LSTICK_LEFT) + id=BUTTON_LSTICK_RIGHT; + else if(id==BUTTON_LSTICK_RIGHT) + id=BUTTON_LSTICK_LEFT; + + if(id==BUTTON_DPAD_LEFT) + id=BUTTON_DPAD_RIGHT; + else if(id==BUTTON_DPAD_RIGHT) + id=BUTTON_DPAD_LEFT; + } + + keys_pressed[id][which]=value; + if(value) + keys_clicked[id][which]=true; +} + +void CKeyManager::UseJoyCons(bool use){ + use_joycons=use; +} + +bool CKeyManager::IsUsingJoyCons(){ + return use_joycons; +} + + +void CKeyManager::ClearAll(){ + for(int i=0;i + +#define joycon_left 0 +#define joycon_right 1 + +enum joycon_keys{ + BUTTON_A=0, BUTTON_B, BUTTON_X, BUTTON_Y, + BUTTON_LSTICK, BUTTON_RSTICK, + BUTTON_L, BUTTON_R, + BUTTON_ZL, BUTTON_ZR, + BUTTON_PLUS, BUTTON_MINUS, + BUTTON_DPAD_LEFT, BUTTON_DPAD_UP, BUTTON_DPAD_RIGHT, BUTTON_DPAD_DOWN, + BUTTON_LSTICK_LEFT, BUTTON_LSTICK_UP, BUTTON_LSTICK_RIGHT, BUTTON_LSTICK_DOWN, + BUTTON_RSTICK_LEFT, BUTTON_RSTICK_UP, BUTTON_RSTICK_RIGHT, BUTTON_RSTICK_DOWN, + MAX_BUTTONS +}; + +struct SControllerRemap{ + SControllerRemap(){ + which=-1; + key_id=-1; + } + SControllerRemap(int key_id, int which){ + this->which=which; + this->key_id=key_id; + } + int which; + int key_id; +}; + +class CKeyManager{ + bool keys_pressed[MAX_BUTTONS][2]; + bool keys_clicked[MAX_BUTTONS][2]; + int current_controller; + bool use_joycons; + std::map to_joycon_key; +public: + + CKeyManager(); + ~CKeyManager(); + + bool IsClicked(int id); + bool IsPressed(int id); + + void CleanPressed(); + + void Set(int id, bool value, int which); + + void SetController(int id); + + void UseJoyCons(bool use); + + bool IsUsingJoyCons(); + + void ClearAll(); +}; + +extern CKeyManager *key_manager; \ No newline at end of file diff --git a/src/Landscape.h b/src/Landscape.h new file mode 100644 index 0000000..ddbea79 --- /dev/null +++ b/src/Landscape.h @@ -0,0 +1,22 @@ +#pragma once +#include +#include +#include +#include "Render.h" +#include "TextureManager.h" + +#define NUMBER_OF_MAPS 2 + +class Landscape{ +public: + Landscape(){} + virtual ~Landscape(){} + + virtual void DrawBackground()=0; + virtual void DrawForeground()=0; + virtual void Control(float delta)=0; + +}; + +extern Landscape * active_map; +extern Landscape * maps[NUMBER_OF_MAPS]; diff --git a/src/Landscape_green.cpp b/src/Landscape_green.cpp new file mode 100644 index 0000000..db75768 --- /dev/null +++ b/src/Landscape_green.cpp @@ -0,0 +1,23 @@ +#include "Landscape_green.h" + + +LandscapeGreen::LandscapeGreen(){ + SDL_Surface *img_surface=texture_manager->GetSurface(BACKGROUND_2); + background_raw=img_surface->pixels; +} + +LandscapeGreen::~LandscapeGreen(){ + +} + +void LandscapeGreen::DrawBackground(){ + render->DrawRawFast(background_raw); +} + +void LandscapeGreen::DrawForeground(){ +} + +void LandscapeGreen::Control(float delta){ + +} + diff --git a/src/Landscape_green.h b/src/Landscape_green.h new file mode 100644 index 0000000..f127f63 --- /dev/null +++ b/src/Landscape_green.h @@ -0,0 +1,25 @@ +#pragma once +#include +#include +#include +#include "Landscape.h" +#include "Timer.h" +#include "vec2.h" + + +#define LANDSCAPE_GREEN 1 + +class LandscapeGreen :public Landscape{ + void *background_raw; + +public: + + LandscapeGreen(); + ~LandscapeGreen(); + + virtual void DrawBackground(); + virtual void DrawForeground(); + + virtual void Control(float delta); + +}; diff --git a/src/Landscape_snow.cpp b/src/Landscape_snow.cpp new file mode 100644 index 0000000..44e3dcf --- /dev/null +++ b/src/Landscape_snow.cpp @@ -0,0 +1,61 @@ +#include "Landscape_snow.h" + + +LandscapeSnow::LandscapeSnow(){ + SDL_Surface *img_surface=texture_manager->GetSurface(BACKGROUND_1); + background_raw=img_surface->pixels; + snow_particles_timer.SetAlarm(40); +} + +LandscapeSnow::~LandscapeSnow(){ + +} + +void LandscapeSnow::DrawBackground(){ + render->DrawRawFast(background_raw); +} + + +void LandscapeSnow::DrawForeground(){ + + SDL_SetRenderDrawColor(render->GetRenderer(),255,255,255,255); + SDL_Rect part_pos; + + for(auto & particle : vParticles){ + part_pos.x=(int)particle.Pos.x; + part_pos.y=(int)particle.Pos.y; + part_pos.w=particle.size; + part_pos.h=particle.size; + SDL_RenderFillRect(render->GetRenderer(),&part_pos); + } +} + +void LandscapeSnow::Control(float delta){ + if(snow_particles_timer.Alarm()){ + if(vParticles.size()665){ + return true; + }return false; + }), vParticles.end()); + + +} + diff --git a/src/Landscape_snow.h b/src/Landscape_snow.h new file mode 100644 index 0000000..75b311f --- /dev/null +++ b/src/Landscape_snow.h @@ -0,0 +1,35 @@ +#pragma once +#include +#include +#include +#include "Landscape.h" +#include "Timer.h" +#include "vec2.h" + +#define MAX_PARTICLES 256 + +#define LANDSCAPE_SNOW 0 + +struct SnowParticle{ + vec2_t Pos; + float size; + float velocity; +}; + + +class LandscapeSnow :public Landscape{ + std::vector vParticles; + void *background_raw; + Timer snow_particles_timer; + +public: + + LandscapeSnow(); + ~LandscapeSnow(); + + virtual void DrawBackground(); + virtual void DrawForeground(); + + virtual void Control(float delta); + +}; diff --git a/src/Render.cpp b/src/Render.cpp new file mode 100644 index 0000000..56cf2f3 --- /dev/null +++ b/src/Render.cpp @@ -0,0 +1,200 @@ +#include "Render.h" + + +CGameRender::CGameRender(){ + initialized=true; + + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_JOYSTICK) < 0) { + printf("SDL_Init: %s\n", SDL_GetError()); + initialized = false; + return; + } + + SDL_CreateWindowAndRenderer(SCREEN_WIDTH, SCREEN_HEIGHT, 0, &window, &renderer); + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + SDL_RenderPresent(renderer); + + frame_cap=0; + + frame_count=0; + fps_timer=0; + fps=0; + show_fps=false; + + fps_surface=NULL; + fps_tex=NULL; + +} + +CGameRender::~CGameRender(){ + + +} + +void CGameRender::SetFramerateLimit(int limit){ + frame_cap=limit; +} + + +void CGameRender::BeginFrame(){ + begin_frame_time=SDL_GetTicks(); +} + +void CGameRender::SwapBuffers(){ + static bool first_run = true; + if(first_run){ + first_run=false; + CreateFPSTexture(); + } + + if(show_fps){ + vec2_t fps_pos=vec2_t(10.f,10.f); + this->DrawTexture(fps_tex, fps_pos); + } + + + SDL_RenderPresent(renderer); + + frame_count++; + int current_fps_timer = SDL_GetTicks(); + if (current_fps_timer > fps_timer + 1000) { + fps_timer=current_fps_timer; + fps=frame_count; + frame_count=0; + CreateFPSTexture(); + } + + float frame_diff=current_fps_timer-begin_frame_time; + if(frame_cap&&(frame_diff<1000/frame_cap)){ + SDL_Delay((1000/frame_cap)-frame_diff); + } +} + +bool CGameRender::IsInitialized(){ + return this->initialized; +} + +void CGameRender::DrawTexture(SDL_Texture* texture, vec2_t &vpos){ + int w, h; + SDL_QueryTexture(texture, NULL, NULL, &w, &h); + SDL_Rect rect; + rect.h = h; + rect.w = w; + rect.x = (int)vpos.x; + rect.y = (int)vpos.y; + SDL_RenderCopy(renderer, texture, NULL, &rect); +} + + +void CGameRender::DrawTexture(SDL_Texture* texture, vec2_t &vpos, float angle){ + SDL_Point sprite_center; + int w, h; + SDL_RendererFlip flip = SDL_FLIP_NONE; + SDL_QueryTexture(texture, NULL, NULL, &w, &h); + SDL_Rect rect; + rect.h = h; + rect.w = w; + rect.x = (int)vpos.x; + rect.y = (int)vpos.y; + sprite_center.x = rect.w/2; sprite_center.y=rect.h/2; + SDL_RenderCopyEx(renderer, texture, NULL, &rect, angle, &sprite_center,flip); +} + +void CGameRender::DrawPlaneTexture(SDL_Texture* texture, vec2_t &vpos, float angle){ + SDL_Point sprite_center; + SDL_Rect rect; + rect.x=(int)vpos.x-21; + rect.y=(int)vpos.y-21; + rect.w=42; + rect.h=42; + SDL_RendererFlip flip = SDL_FLIP_NONE; + sprite_center.x = 21; sprite_center.y=21; + SDL_RenderCopyEx(renderer, texture, NULL, &rect, angle, &sprite_center,flip); + +} + +SDL_Renderer* CGameRender::GetRenderer(){ + return renderer; +} + +SDL_Window* CGameRender::GetWindow(){ + return window; +} + + + +void CGameRender::CreateFPSTexture(){ + if(!show_fps) + return; + if(fps_surface) + SDL_FreeSurface(fps_surface); + if(fps_tex) + SDL_DestroyTexture(fps_tex); + + char Buffer[256] = { 0x00 }; + + sprintf (Buffer, "FPS: %d ", fps); + + + SDL_Color White = {50, 240, 125}; + fps_surface = TTF_RenderText_Solid(font_manager->GetFont(0), Buffer, White); + + + if(!fps_surface) + return; + + fps_tex = SDL_CreateTextureFromSurface(this->GetRenderer(), fps_surface); + + if(!fps_tex){ + SDL_FreeSurface(fps_surface); + return; + } + + +} + +void CGameRender::Printf(vec2_t pos, bool center, SDL_Color col, int Font, char* _Input, ...){ + char Buffer[1024] = { '\0' }; + + va_list Args; + + va_start(Args, _Input); + vsprintf(Buffer, _Input, Args); + va_end(Args); + + SDL_Surface* surfaceMessage = TTF_RenderText_Solid(font_manager->GetFont(Font), Buffer, col); + + if(!surfaceMessage) + return; + + SDL_Texture* Message = SDL_CreateTextureFromSurface(this->GetRenderer(), surfaceMessage); + + if(!Message){ + SDL_FreeSurface(surfaceMessage); + return; + } + + if(center){ + int w, h; + SDL_QueryTexture(Message, NULL, NULL, &w, &h); + pos.x-=w/2; + pos.y-=h/2; + + } + + this->DrawTexture(Message, pos); + + SDL_FreeSurface(surfaceMessage); + SDL_DestroyTexture(Message); +} + + +void CGameRender::DrawRawFast(void *raw){ + SDL_Surface * screen = SDL_GetWindowSurface(window); + memcpy(screen->pixels, raw, 1280*720*4); +} + + + + diff --git a/src/Render.h b/src/Render.h new file mode 100644 index 0000000..f4380c6 --- /dev/null +++ b/src/Render.h @@ -0,0 +1,55 @@ +#pragma once +#include +#include +#include +#include +#include +#include "vec2.h" +#include "FontManager.h" + +#define SCREEN_WIDTH 1280 +#define SCREEN_HEIGHT 720 + +class CGameRender{ + bool initialized; + SDL_Window *window; + SDL_Renderer* renderer; + + float frame_cap; + unsigned int begin_frame_time, fps_timer; + int frame_count; + int fps; + bool show_fps; + + SDL_Surface* fps_surface; + SDL_Texture* fps_tex; + + void CreateFPSTexture(); + +public: + CGameRender(); + ~CGameRender(); + + void DrawTexture(SDL_Texture* texture, vec2_t &vpos); + void DrawTexture(SDL_Texture* texture, vec2_t &vpos, float angle); + void DrawPlaneTexture(SDL_Texture* texture, vec2_t &vpos, float angle); + + void SwapBuffers(); + + void BeginFrame(); + + bool IsInitialized(); + + SDL_Renderer* GetRenderer(); + + SDL_Window* GetWindow(); + + void SetFramerateLimit(int limit); + + void Printf(vec2_t pos, bool center, SDL_Color col, int Font, char* _Input, ...); + + void DrawRawFast(void *raw); + +}; + +extern CGameRender *render; \ No newline at end of file diff --git a/src/SoundManager.cpp b/src/SoundManager.cpp new file mode 100644 index 0000000..f918487 --- /dev/null +++ b/src/SoundManager.cpp @@ -0,0 +1,68 @@ +#include "SoundManager.h" + + +CSoundManager::CSoundManager(){ + initialized=false; + + if(Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0 ) + return; + + if(!LoadSound("romfs:/machine_gun.wav", sound_effects_ids::MACHINE_GUN_SHOT)) + return; + if(!LoadSound("romfs:/rocket.wav", sound_effects_ids::ROCKET_SHOT)) + return; + if(!LoadSound("romfs:/sniper_rifle.wav", sound_effects_ids::SNIPER_RIFLE_SHOT)) + return; + if(!LoadSound("romfs:/laser_beam.wav", sound_effects_ids::LASER_BEAM_SHOT)) + return; + if(!LoadSound("romfs:/menu_select.wav", sound_effects_ids::MENU_SELECT)) + return; + if(!LoadSound("romfs:/menu_select2.wav", sound_effects_ids::MENU_SELECT2)) + return; + if(!LoadSound("romfs:/menu_click.wav", sound_effects_ids::MENU_CLICK)) + return; + if(!LoadSound("romfs:/round_begin.wav", sound_effects_ids::ROUND_BEGIN)) + return; + if(!LoadSound("romfs:/round_end.wav", sound_effects_ids::ROUND_END)) + return; + if(!LoadSound("romfs:/explosion.wav", sound_effects_ids::EXPLOSION)) + return; + if(!LoadSound("romfs:/engine.wav", sound_effects_ids::ENGINE_1)) + return; + if(!LoadSound("romfs:/engine.wav", sound_effects_ids::ENGINE_2)) + return; + + + initialized=true; + +} + +bool CSoundManager::IsInitialized(){ + return this->initialized; +} + +Mix_Chunk* CSoundManager::LoadSound(const char *path, int ID){ + sounds[ID] = Mix_LoadWAV(path); + return sounds[ID]; +} + + + +CSoundManager::~CSoundManager(){ + for(int i=0;i +#include +#include +#include "Render.h" + +enum sound_effects_ids{ + MACHINE_GUN_SHOT, + ROCKET_SHOT, + SNIPER_RIFLE_SHOT, + LASER_BEAM_SHOT, + + MENU_SELECT, + MENU_SELECT2, + MENU_CLICK, + ROUND_BEGIN, + ROUND_END, + + EXPLOSION, + + ENGINE_1, + ENGINE_2, + + SOUND_EFFECTS_MAX +}; + +class CSoundManager{ + Mix_Chunk *sounds[sound_effects_ids::SOUND_EFFECTS_MAX]; + bool initialized; + + Mix_Chunk* LoadSound(const char *path, int ID); + +public: + + CSoundManager(); + ~CSoundManager(); + bool IsInitialized(); + + int PlaySound(int id, int loops=0); + + void StopSound(int id); + + void SetVolume(int id,int volume); +}; + +extern CSoundManager *sound_manager; diff --git a/src/TextureManager.cpp b/src/TextureManager.cpp new file mode 100644 index 0000000..7857dbe --- /dev/null +++ b/src/TextureManager.cpp @@ -0,0 +1,78 @@ +#include "TextureManager.h" + + +CTextureManager::CTextureManager(){ + initialized=false; + + if(!LoadTexture("romfs:/Plane1.png",textures_ids::PLANE_1)) + return; + if(!LoadTexture("romfs:/Plane2.png",textures_ids::PLANE_2)) + return; + if(!LoadTexture("romfs:/menu_background.png",textures_ids::MENU_BACKGROUND)) + return; + if(!LoadTexture("romfs:/background1.png",textures_ids::BACKGROUND_1)) + return; + if(!LoadTexture("romfs:/background2.png",textures_ids::BACKGROUND_2)) + return; + if(!LoadTexture("romfs:/BOMB.png",textures_ids::BOMB)) + return; + for(int i=0;i<14;i++){ + std::string fname="romfs:/crash_"+std::to_string(i)+".png"; + if(!LoadTexture(fname.c_str(),textures_ids::CRASH_0+i)) + return; + + } + for(int i=0;i<6;i++){ + std::string fname="romfs:/smoke_"+std::to_string(i)+".png"; + if(!LoadTexture(fname.c_str(),textures_ids::SMOKE_0+i)) + return; + } + if(!LoadTexture("romfs:/heart.png",textures_ids::HEART)) + return; + if(!LoadTexture("romfs:/settings_0.png",textures_ids::SETTINGS_0)) + return; + if(!LoadTexture("romfs:/settings_1.png",textures_ids::SETTINGS_1)) + return; + + + initialized=true; + + +} + +CTextureManager::~CTextureManager(){ + for(int i=0;iinitialized; +} + +SDL_Surface *CTextureManager::LoadSurface( const char* path ) +{ + SDL_Surface* loadedSurface = IMG_Load( path ); + return loadedSurface; +} + + +SDL_Texture* CTextureManager::LoadTexture(const char* path, int ID){ + + surfaces[ID] = LoadSurface(path); + if(!surfaces[ID]) + return NULL; + textures[ID] = SDL_CreateTextureFromSurface(render->GetRenderer(), surfaces[ID]); + return textures[ID]; +} + + +SDL_Texture* CTextureManager::GetTexture(int ID){ + return textures[ID]; +} + +SDL_Surface* CTextureManager::GetSurface(int ID){ + return surfaces[ID]; +} + diff --git a/src/TextureManager.h b/src/TextureManager.h new file mode 100644 index 0000000..3b7afee --- /dev/null +++ b/src/TextureManager.h @@ -0,0 +1,70 @@ +#pragma once +#include +#include +#include "Render.h" + +#include +#include + + +enum textures_ids{ + PLANE_1, + PLANE_2, + + MENU_BACKGROUND, + + BACKGROUND_1, + BACKGROUND_2, + + BOMB, + + CRASH_0, + CRASH_1, + CRASH_2, + CRASH_3, + CRASH_4, + CRASH_5, + CRASH_6, + CRASH_7, + CRASH_8, + CRASH_9, + CRASH_10, + CRASH_11, + CRASH_12, + CRASH_13, + + SMOKE_0, + SMOKE_1, + SMOKE_2, + SMOKE_3, + SMOKE_4, + SMOKE_5, + + HEART, + + SETTINGS_0, + SETTINGS_1, + + TEX_MAX + +}; + +class CTextureManager{ + SDL_Surface *surfaces[TEX_MAX]; + SDL_Texture *textures[TEX_MAX]; + bool initialized; + + SDL_Surface* LoadSurface(const char *path); + SDL_Texture* LoadTexture(const char *path, int ID); + +public: + + CTextureManager(); + ~CTextureManager(); + bool IsInitialized(); + + SDL_Surface* GetSurface(int ID); + SDL_Texture* GetTexture(int ID); +}; + +extern CTextureManager *texture_manager; \ No newline at end of file diff --git a/src/Timer.cpp b/src/Timer.cpp new file mode 100644 index 0000000..1b73614 --- /dev/null +++ b/src/Timer.cpp @@ -0,0 +1,30 @@ +#include "Timer.h" + +Timer::Timer(){ + time_start=0; + limit=0; +} + +Timer::Timer(int limit){ + SetAlarm(limit); +} + +Timer::~Timer(){ + +} + +void Timer::SetAlarm(int limit){ + this->limit=limit; +} + +void Timer::Start(){ + time_start=SDL_GetTicks(); +} + +bool Timer::Alarm(){ + return (time_start+limit + +class Timer{ + Uint32 limit, time_start; +public: + Timer(); + Timer(int limit); + ~Timer(); + void SetAlarm(int limit); + void Start(); + bool Alarm(); + Uint32 GetElapsed(); +}; \ No newline at end of file diff --git a/src/Weapon.cpp b/src/Weapon.cpp new file mode 100644 index 0000000..65803f3 --- /dev/null +++ b/src/Weapon.cpp @@ -0,0 +1,7 @@ +#include "Weapon.h" + +int CWeapon::GetDamage(){ + if(bonus_rand_damage==0) + return base_damage; + return base_damage+rand()%bonus_rand_damage; +} \ No newline at end of file diff --git a/src/Weapon.h b/src/Weapon.h new file mode 100644 index 0000000..79dce3c --- /dev/null +++ b/src/Weapon.h @@ -0,0 +1,48 @@ +#pragma once +#include +#include +#include +#include "vec2.h" +#include "Render.h" +#include "SoundManager.h" +#include "TextureManager.h" +#include +#include "Timer.h" + +enum{ + WEAPON_MACHINEGUN, + WEAPON_ROCKETLAUNCHER, + WEAPON_SNIPERRIFLE, + WEAPON_LASER, + WEAPON_MAX +}; + +struct SBullet{ + SBullet(vec2_t pos, float angle){ + this->pos=pos; + this->angle=angle; + } + SBullet(){ + pos=vec2_t(0.f,0.f); + angle=0.f; + } + vec2_t pos; + float angle; +}; + +class CWeapon{ +protected: + Uint32 fire_rate, last_fire; + int base_damage, bonus_rand_damage; +public: + + CWeapon(){} + virtual ~CWeapon(){}; + + virtual void Fire(vec2_t &pos, float angle, bool plane_left = false) = 0; + virtual void Simulate(float delta) = 0; + virtual void Draw(vec2_t &plane_pos) = 0; + virtual void Reset() = 0; + virtual bool CheckCollision(vec2_t &plane_pos) = 0; + int GetDamage(); +}; diff --git a/src/WeaponLaser.cpp b/src/WeaponLaser.cpp new file mode 100644 index 0000000..993035a --- /dev/null +++ b/src/WeaponLaser.cpp @@ -0,0 +1,95 @@ +#include "WeaponLaser.h" +#include "Entity.h" + +CWeaponLaser::CWeaponLaser(){ + fire_rate=1200; + last_fire=0; + last_attack=0; + base_damage=3; + bonus_rand_damage=1; + this->Reset(); +} + +CWeaponLaser::~CWeaponLaser(){ + +} + +void CWeaponLaser::Fire(vec2_t &pos, float angle, bool plane_left){ + Uint32 curtime=SDL_GetTicks(); + + this->angle=angle*M_PI/180.f; + this->pos=pos; + + if(to_fire){ + to_fire=false; + } + + if(can_fire_again&&curtime>last_fire+fire_rate){ + active = true; + can_fire_again=false; + last_fire=curtime; + sound_channel=sound_manager->PlaySound(sound_effects_ids::LASER_BEAM_SHOT); + } +} + +void CWeaponLaser::Simulate(float delta){ + if(to_fire){ + if(!can_fire_again) + sound_manager->StopSound(sound_channel); + active=false; + can_fire_again=true; + } + + + + to_fire=true; + + if(last_fire+480GetRenderer(),125,0,250,255); + + vec2_t start_pos=plane_pos; + + vec2_t next_pos = start_pos; + next_pos.x+=cos(angle)*1500.f; + next_pos.y+=sin(angle)*1500.f; + SDL_RenderDrawLine(render->GetRenderer(), (int)start_pos.x, (int)start_pos.y, (int)next_pos.x, (int)next_pos.y); + + SDL_SetRenderDrawColor(render->GetRenderer(),220,0,200,255); + start_pos+=vec2_t(1.f,1.f); + next_pos+=vec2_t(1.f,1.f); + SDL_RenderDrawLine(render->GetRenderer(), (int)start_pos.x, (int)start_pos.y, (int)next_pos.x, (int)next_pos.y); + +} + +bool CWeaponLaser::CheckCollision(vec2_t &plane_pos){ + if(!active) + return false; + + float collision_radius=PLANE_RADIUS+2; + float distance=plane_pos.Distance(pos); + vec2_t next_pos = pos; + next_pos.x+=cos(angle)*distance; + next_pos.y+=sin(angle)*distance; + + Uint32 curtime=SDL_GetTicks(); + if(last_attack+30 + +class CWeaponLaser : public CWeapon{ + bool active; + vec2_t pos; + float angle; + bool to_fire; + bool can_fire_again; + Uint32 last_attack; + int sound_channel; + +public: + CWeaponLaser(); + ~CWeaponLaser(); + + void Fire(vec2_t &pos, float angle, bool plane_left); + void Simulate(float delta); + void Draw(vec2_t &plane_pos); + void Reset(); + bool CheckCollision(vec2_t &plane_pos); + +}; diff --git a/src/WeaponMachineGun.cpp b/src/WeaponMachineGun.cpp new file mode 100644 index 0000000..182fa80 --- /dev/null +++ b/src/WeaponMachineGun.cpp @@ -0,0 +1,119 @@ +#include "WeaponMachineGun.h" +#include "Entity.h" + +CWeaponMachineGun::CWeaponMachineGun(){ + fire_rate=140; + last_fire=0; + base_damage=5; + bonus_rand_damage=10; + current_overheat=0.f; + fired_this_tick=false; +} + +CWeaponMachineGun::~CWeaponMachineGun(){ + +} + +void CWeaponMachineGun::Fire(vec2_t &pos, float angle, bool plane_left){ + Uint32 curtime=SDL_GetTicks(); + fired_this_tick=true; + float current_fire_rate=fire_rate+(current_overheat/max_overheat_time)*overheat_fire_rate_loss; + if(curtime>last_fire+current_fire_rate){ + last_fire=curtime; + int recoil=rand()%10-5; + angle+=(float)recoil; + angle*=M_PI/180.f; + vbullets.push_back(SBullet(pos,angle)); + sound_manager->PlaySound(sound_effects_ids::MACHINE_GUN_SHOT); + } +} + +void CWeaponMachineGun::Simulate(float delta){ + + if(fired_this_tick){ + current_overheat+=delta; + }else{ + current_overheat-=delta*4.f; + } + + if(current_overheat<0.f) + current_overheat=0.f; + if(current_overheat>max_overheat_time) + current_overheat=max_overheat_time; + + for(std::vector::iterator it = vbullets.begin(); it != vbullets.end(); ){ + vec2_t vmove=vec2_t((delta*velocity)*cos(it->angle),(delta*velocity)*sin(it->angle)); + it->pos+=vmove; + if (it->pos.x<-2|| + it->pos.x>SCREEN_WIDTH+2|| + it->pos.y<-2|| + it->pos.y>SCREEN_HEIGHT+2) { + it = vbullets.erase(it); + } else { + ++it; + } + } + + fired_this_tick=false; + +} + +void CWeaponMachineGun::Draw(vec2_t &plane_pos){ + SDL_SetRenderDrawColor(render->GetRenderer(),0,0,0,255); + SDL_Rect bullet_rect; + for(auto &bullet : vbullets){ + bullet_rect.x=(int)bullet.pos.x-2; + bullet_rect.y=(int)bullet.pos.y-2; + bullet_rect.w=4; + bullet_rect.h=4; + SDL_RenderFillRect(render->GetRenderer(),&bullet_rect); + } + + + if(current_overheat!=0.f){ + SetOverHeatColor(); + float overheat_bar_width=30.f; + overheat_bar_width-=overheat_bar_width*(current_overheat/max_overheat_time); + if(overheat_bar_width<1) + overheat_bar_width=1; + SDL_Rect bar_rect; + bar_rect.x=(int)plane_pos.x-14; + bar_rect.y=(int)plane_pos.y-20; + bar_rect.h=3; + bar_rect.w=(int)overheat_bar_width; + SDL_RenderFillRect(render->GetRenderer(),&bar_rect); + } + +} + +void CWeaponMachineGun::SetOverHeatColor(){ + const float max_color=220.f; + float overheat=(current_overheat/max_overheat_time); + if(overheat<0.5f){ + float p=overheat/0.5f; + float r=max_color*p; + SDL_SetRenderDrawColor(render->GetRenderer(),(int)r,(int)max_color,0,255); + }else{ + float p=(overheat-0.5f)/0.5f; + float g=max_color-max_color*p; + SDL_SetRenderDrawColor(render->GetRenderer(),(int)max_color,(int)g,0,255); + } +} + +bool CWeaponMachineGun::CheckCollision(vec2_t &plane_pos){ + float dist=PLANE_RADIUS+2; + for(std::vector::iterator it = vbullets.begin(); it != vbullets.end(); ){ + if (plane_pos.Distance(it->pos) + +class CWeaponMachineGun : public CWeapon{ + std::vector vbullets; + const float velocity=0.72f; + + const float max_overheat_time=3000.f; + const float overheat_fire_rate_loss=200.f; + float current_overheat; + bool fired_this_tick; + +public: + CWeaponMachineGun(); + ~CWeaponMachineGun(); + + void Fire(vec2_t &pos, float angle, bool plane_left); + void Simulate(float delta); + void Draw(vec2_t &plane_pos); + void Reset(); + bool CheckCollision(vec2_t &plane_pos); + + void SetOverHeatColor(); +}; diff --git a/src/WeaponRocketLauncher.cpp b/src/WeaponRocketLauncher.cpp new file mode 100644 index 0000000..9075e33 --- /dev/null +++ b/src/WeaponRocketLauncher.cpp @@ -0,0 +1,88 @@ +#include "WeaponRocketLauncher.h" +#include "Entity.h" + +CWeaponRocketLauncher::CWeaponRocketLauncher(){ + fire_rate=2000; + last_fire=0; + base_damage=100; + bonus_rand_damage=0; + active=false; +} + +CWeaponRocketLauncher::~CWeaponRocketLauncher(){ + +} + +void CWeaponRocketLauncher::Fire(vec2_t &pos, float angle, bool plane_left){ + if(active) + return; + Uint32 curtime=SDL_GetTicks(); + float current_fire_rate=fire_rate; + if(curtime>last_fire+current_fire_rate){ + last_fire=curtime; + int recoil=rand()%4-2; + fall_angle=angle+(plane_left?90.f:-90.f); + angle+=(float)recoil; + angle*=M_PI/180.f; + fall_angle*=M_PI/180.f; + active=true; + this->pos=pos; + this->angle=angle; + fall_velocity=max_fall_velocity; + velocity=start_velocity; + sound_manager->PlaySound(sound_effects_ids::ROCKET_SHOT); + } +} + +void CWeaponRocketLauncher::Simulate(float delta){ + if(!active) + return; + + delta/=1000; + + if(velocity0) + fall_velocity-=acceleration*delta; + if(fall_velocity<0) + fall_velocity=0; + + vec2_t vmove; + vmove.x = velocity*cos(angle)*delta; + vmove.y = velocity*sin(angle)*delta; + + vmove.x += fall_velocity*cos(fall_angle)*delta; + vmove.y += fall_velocity*sin(fall_angle)*delta; + + pos+=vmove; + + if(pos.x+5<0|| + pos.x-5>SCREEN_WIDTH|| + pos.y+5<0|| + pos.y-5>SCREEN_HEIGHT){ + active=false; + } + +} + +void CWeaponRocketLauncher::Draw(vec2_t &plane_pos){ + if(!active) + return; + vec2_t draw_pos=vec2_t(pos.x-9,pos.y-9); + render->DrawTexture(texture_manager->GetTexture(BOMB), draw_pos, angle*180.f/M_PI); +} + +bool CWeaponRocketLauncher::CheckCollision(vec2_t &plane_pos){ + float dist=PLANE_RADIUS+9; + if (active&&plane_pos.Distance(pos) + +class CWeaponRocketLauncher : public CWeapon{ + const float start_velocity = 350.f; + const float max_velocity = 1700.f; + const float acceleration = 2650.f; + const float max_fall_velocity = 550.f; + vec2_t pos; + float fall_velocity, velocity; + float angle, fall_angle; + bool active; + +public: + CWeaponRocketLauncher(); + ~CWeaponRocketLauncher(); + + void Fire(vec2_t &pos, float angle, bool plane_left); + void Simulate(float delta); + void Draw(vec2_t &plane_pos); + void Reset(); + bool CheckCollision(vec2_t &plane_pos); + +}; diff --git a/src/WeaponSniperRifle.cpp b/src/WeaponSniperRifle.cpp new file mode 100644 index 0000000..0b10905 --- /dev/null +++ b/src/WeaponSniperRifle.cpp @@ -0,0 +1,72 @@ +#include "WeaponSniperRifle.h" +#include "Entity.h" + +CWeaponSniperRifle::CWeaponSniperRifle(){ + fire_rate=1500; + last_fire=0; + base_damage=65; + bonus_rand_damage=20; +} + +CWeaponSniperRifle::~CWeaponSniperRifle(){ + +} + +void CWeaponSniperRifle::Fire(vec2_t &pos, float angle, bool plane_left){ + Uint32 curtime=SDL_GetTicks(); + if(curtime>last_fire+fire_rate){ + sound_manager->PlaySound(sound_effects_ids::SNIPER_RIFLE_SHOT); + last_fire=curtime; + angle*=M_PI/180.f; + vbullets.push_back(SBullet(pos,angle)); + } +} + +void CWeaponSniperRifle::Simulate(float delta){ + for(std::vector::iterator it = vbullets.begin(); it != vbullets.end(); ){ + vec2_t vmove=vec2_t((delta*velocity)*cos(it->angle),(delta*velocity)*sin(it->angle)); + it->pos+=vmove; + if (it->pos.x<-2|| + it->pos.x>SCREEN_WIDTH+2|| + it->pos.y<-2|| + it->pos.y>SCREEN_HEIGHT+2) { + it = vbullets.erase(it); + } else { + ++it; + } + } +} + +void CWeaponSniperRifle::Draw(vec2_t &plane_pos){ + SDL_SetRenderDrawColor(render->GetRenderer(),0,0,0,255); + SDL_Rect bullet_rect; + for(auto &bullet : vbullets){ + bullet_rect.x=(int)bullet.pos.x-2; + bullet_rect.y=(int)bullet.pos.y-2; + bullet_rect.w=4; + bullet_rect.h=4; + SDL_RenderFillRect(render->GetRenderer(),&bullet_rect); + } +} + +bool CWeaponSniperRifle::CheckCollision(vec2_t &plane_pos){ + float collision_radius=PLANE_RADIUS+2; + for(std::vector::iterator it = vbullets.begin(); it != vbullets.end(); ){ + float distance=plane_pos.Distance(it->pos); + vec2_t next_pos = it->pos; + next_pos.x+=cos(it->angle)*distance; + next_pos.y+=sin(it->angle)*distance; + if (plane_pos.Distance(next_pos) + +class CWeaponSniperRifle : public CWeapon{ + std::vector vbullets; + const float velocity=2.92f; + +public: + CWeaponSniperRifle(); + ~CWeaponSniperRifle(); + + void Fire(vec2_t &pos, float angle, bool plane_left); + void Simulate(float delta); + void Draw(vec2_t &plane_pos); + void Reset(); + bool CheckCollision(vec2_t &plane_pos); + +}; diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..0e78372 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,28 @@ +#include "Game.h" + +CTextureManager *texture_manager; +CSoundManager *sound_manager; +CFontManager *font_manager; +CKeyManager *key_manager; +CGameState* game_state; +CGameRender *render; +Landscape * maps[NUMBER_OF_MAPS]; +Landscape * active_map; +CGUI *gui; +CEntity * entities[PLANE_MAX]; + +int main(void){ + srand(time(NULL)); + + CGame game; + + while(!game.Quit()) + game.Run(); + + SDL_Quit(); + romfsExit(); + return 0; +} + + + diff --git a/src/vec2.h b/src/vec2.h new file mode 100644 index 0000000..12715f5 --- /dev/null +++ b/src/vec2.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +class vec2_t +{ +public: + float x, y; + inline void Init(float ix, float iy) + { + x = ix; y = iy; + } + vec2_t() { x = 0.f; y = 0.f; }; + vec2_t(float X, float Y) { x = X; y = Y; }; + vec2_t(float *vec) { x = vec[0]; y = vec[1]; }; + float operator[](int i) const { if (i == 0) return x; return y; }; + float& operator[](int i) { if (i == 0) return x; return y; }; + + bool operator==(const vec2_t& v) { if(x==v.x&&y==v.y)return true; return false; } + bool operator!=(const vec2_t& v) { if(x!=v.x||y!=v.y)return true; return false; } + + inline vec2_t operator-(const vec2_t& v) { return vec2_t(x - v.x, y - v.y); } + inline vec2_t operator+(const vec2_t& v) { return vec2_t(x + v.x, y + v.y); } + inline vec2_t operator*(const float n) { return vec2_t(x*n, y*n); } + inline vec2_t operator-() { return vec2_t(-x, -y); } + float LengthSqr(void) { return (x*x + y*y); } + float Length(void) { return (x*x + y*y); } + float Length2D(void) { return sqrtf(x*x + y*y); } + float VectorLength(void) { return sqrt(x*x + y*y); } + inline vec2_t& operator+=(const vec2_t &v) { x += v.x; y += v.y; return *this; } + inline vec2_t& operator-=(const vec2_t &v) { x -= v.x; y -= v.y; return *this; } + inline vec2_t& operator/=(const vec2_t &v) { x /= v.x; y /= v.y; return *this; } + inline vec2_t& operator/=(const float v) { x /= v; y /= v; return *this; } + float Distance(vec2_t &v) { + float dx=x-v.x; + float dy=y-v.y; + return sqrtf(dx*dx+dy*dy); + } + +};