├── .gitignore ├── 00-hello_world ├── Makefile └── main.c ├── 01-pong ├── Makefile ├── assets │ ├── fonts │ │ └── font.ttf │ └── sounds │ │ ├── paddle_hit.wav │ │ ├── score.wav │ │ └── wall_hit.wav ├── main.c ├── settings.h └── src │ ├── ball.c │ ├── ball.h │ ├── fonts.c │ ├── fonts.h │ ├── hitbox.c │ ├── hitbox.h │ ├── paddle.c │ ├── paddle.h │ ├── pong.c │ ├── pong.h │ ├── sounds.c │ └── sounds.h ├── 02-flappy_bird ├── Makefile ├── Settings.cpp ├── Settings.hpp ├── assets │ ├── fonts │ │ ├── flappy.ttf │ │ └── font.ttf │ ├── graphics │ │ ├── background.png │ │ ├── bird.png │ │ ├── ground.png │ │ └── log.png │ └── sounds │ │ ├── explosion.wav │ │ ├── hurt.wav │ │ ├── jump.wav │ │ ├── marios_way.ogg │ │ └── score.wav ├── main.cpp └── src │ ├── Bird.cpp │ ├── Bird.hpp │ ├── Factory.hpp │ ├── Game.cpp │ ├── Game.hpp │ ├── Log.cpp │ ├── Log.hpp │ ├── LogPair.cpp │ ├── LogPair.hpp │ ├── World.cpp │ ├── World.hpp │ ├── states │ ├── BaseState.hpp │ ├── CountDownState.cpp │ ├── CountDownState.hpp │ ├── PlayingState.cpp │ ├── PlayingState.hpp │ ├── StateMachine.cpp │ ├── StateMachine.hpp │ ├── TitleScreenState.cpp │ └── TitleScreenState.hpp │ ├── text_utilities.cpp │ └── text_utilities.hpp ├── 03-breakout ├── assets │ ├── fonts │ │ └── font.ttf │ ├── graphics │ │ ├── arrows.png │ │ ├── background.png │ │ ├── breakout.png │ │ └── hearts.png │ └── sounds │ │ ├── brick_hit_1.wav │ │ ├── brick_hit_2.wav │ │ ├── grow_up.wav │ │ ├── high_score.wav │ │ ├── hurt.wav │ │ ├── level_complete.wav │ │ ├── life.wav │ │ ├── music.ogg │ │ ├── paddle_hit.wav │ │ ├── pause.wav │ │ ├── selected.wav │ │ └── wall_hit.wav ├── main.py ├── requirements.txt ├── settings.py └── src │ ├── Ball.py │ ├── Breakout.py │ ├── Brick.py │ ├── BrickSet.py │ ├── Paddle.py │ ├── powerups │ ├── PowerUp.py │ ├── TwoMoreBall.py │ └── __init__.py │ ├── states │ ├── EnterHighScoreState.py │ ├── GameOverState.py │ ├── HighScoreState.py │ ├── PaddleSelectState.py │ ├── PauseState.py │ ├── PlayState.py │ ├── ServeState.py │ ├── StartState.py │ ├── VictoryState.py │ └── __init__.py │ └── utilities │ ├── __init__.py │ ├── frames.py │ ├── highscores.py │ └── level_maker.py ├── 04-match3 ├── assets │ ├── fonts │ │ └── font.ttf │ ├── graphics │ │ ├── background.png │ │ └── match3.png │ └── sounds │ │ ├── clock.wav │ │ ├── error.wav │ │ ├── game-over.wav │ │ ├── match.wav │ │ ├── music.mp3 │ │ ├── music2.mp3 │ │ ├── music3.mp3 │ │ ├── next-level.wav │ │ └── select.wav ├── main.py ├── requirements.txt ├── settings.py └── src │ ├── Board.py │ ├── Match3.py │ ├── Tile.py │ ├── frames_utility.py │ └── states │ ├── BeginGameState.py │ ├── GameOverState.py │ ├── PlayState.py │ ├── StartState.py │ └── __init__.py ├── 05-super_martian ├── assets │ ├── fonts │ │ └── font.ttf │ ├── graphics │ │ ├── creatures.png │ │ ├── martian.png │ │ └── tileset.png │ ├── sounds │ │ ├── count.wav │ │ ├── jump.wav │ │ ├── music_grassland.ogg │ │ ├── music_intro.ogg │ │ ├── pickup_coin.wav │ │ └── timer.wav │ └── tilemaps │ │ ├── creatures.tsx │ │ ├── level1.tmx │ │ └── tiles.tsx ├── main.py ├── requirements.txt ├── settings.py └── src │ ├── Camera.py │ ├── Clock.py │ ├── Creature.py │ ├── GameEntity.py │ ├── GameItem.py │ ├── GameLevel.py │ ├── GameObject.py │ ├── Player.py │ ├── SuperMartian.py │ ├── Tile.py │ ├── Tilemap.py │ ├── definitions │ ├── creatures.py │ ├── items.py │ └── tiles.py │ ├── loaders │ ├── TmxLevelLoader.py │ └── __init__.py │ ├── mixins │ ├── AnimatedMixin.py │ ├── CollidableMixin.py │ ├── DrawableMixin.py │ └── __init__.py │ └── states │ ├── entities │ ├── BaseEntityState.py │ ├── creatures_states │ │ ├── SnailWalkState.py │ │ └── __init__.py │ └── player_states │ │ ├── DeadState.py │ │ ├── FallState.py │ │ ├── IdleState.py │ │ ├── JumpState.py │ │ ├── WalkState.py │ │ └── __init__.py │ └── game_states │ ├── GameOverState.py │ ├── PauseState.py │ ├── PlayState.py │ ├── StartState.py │ └── __init__.py ├── 06-princess ├── assets │ ├── fonts │ │ └── princess.otf │ ├── graphics │ │ ├── background.png │ │ ├── character_pot_lift.png │ │ ├── character_pot_walk.png │ │ ├── character_swing_sword.png │ │ ├── character_walk.png │ │ ├── entities.png │ │ ├── hearts.png │ │ ├── switches.png │ │ └── tilesheet.png │ └── sounds │ │ ├── door.wav │ │ ├── dungeon_music.mp3 │ │ ├── game_over_music.mp3 │ │ ├── heart_taken.wav │ │ ├── hit_enemy.wav │ │ ├── hit_player.wav │ │ ├── pot_wall.wav │ │ ├── start_music.mp3 │ │ └── sword.wav ├── lib │ ├── class.lua │ ├── knife │ │ ├── event.lua │ │ └── timer.lua │ └── push.lua ├── main.lua ├── settings.lua └── src │ ├── Animation.lua │ ├── Entity.lua │ ├── GameObject.lua │ ├── Hitbox.lua │ ├── Player.lua │ ├── Projectile.lua │ ├── StateMachine.lua │ ├── definitions │ ├── entity.lua │ └── game_objects.lua │ ├── states │ ├── BaseState.lua │ ├── entity │ │ ├── EntityIdleState.lua │ │ ├── EntityWalkState.lua │ │ └── player │ │ │ ├── PlayerIdleState.lua │ │ │ ├── PlayerPotIdleState.lua │ │ │ ├── PlayerPotLiftState.lua │ │ │ ├── PlayerPotWalkState.lua │ │ │ ├── PlayerSwingSwordState.lua │ │ │ └── PlayerWalkState.lua │ └── game │ │ ├── GameOverState.lua │ │ ├── PlayState.lua │ │ └── StartState.lua │ ├── utilities │ └── quads.lua │ └── world │ ├── Doorway.lua │ ├── Dungeon.lua │ └── Room.lua ├── 07-ultimate_fantasy ├── assets │ ├── fonts │ │ ├── finalf.ttf │ │ └── font.ttf │ ├── graphics │ │ ├── background.png │ │ ├── characters │ │ │ ├── healer_f.png │ │ │ ├── healer_m.png │ │ │ ├── mage_f.png │ │ │ ├── mage_m.png │ │ │ ├── ranger_f.png │ │ │ ├── ranger_m.png │ │ │ ├── townfolk_f.png │ │ │ ├── townfolk_m.png │ │ │ ├── warrior_f.png │ │ │ └── warrior_m.png │ │ ├── cursor_right.png │ │ ├── cursor_up.png │ │ ├── enemies │ │ │ ├── man_eater_flower.png │ │ │ ├── pumpking.png │ │ │ ├── slime.png │ │ │ ├── small_worm.png │ │ │ └── snake.png │ │ └── sheet.png │ └── sounds │ │ ├── arrows.wav │ │ ├── battle.mp3 │ │ ├── blip.wav │ │ ├── exp.wav │ │ ├── flame.ogg │ │ ├── game_over.mp3 │ │ ├── hit.wav │ │ ├── intro.mp3 │ │ ├── levelup.wav │ │ ├── powerup.wav │ │ ├── run.wav │ │ ├── the_end.mp3 │ │ ├── town.mp3 │ │ ├── victory.wav │ │ └── world.mp3 ├── lib │ ├── class.lua │ ├── knife │ │ ├── event.lua │ │ └── timer.lua │ └── push.lua ├── main.lua ├── settings.lua └── src │ ├── Animation.lua │ ├── StateMachine.lua │ ├── definitions │ ├── entity.lua │ └── tile_id.lua │ ├── entity │ ├── BattleEntity.lua │ ├── Character.lua │ ├── Enemy.lua │ ├── Entity.lua │ ├── NPC.lua │ └── Party.lua │ ├── gui │ ├── Menu.lua │ ├── Panel.lua │ ├── ProgressBar.lua │ ├── Selection.lua │ └── Textbox.lua │ ├── states │ ├── BaseState.lua │ ├── StateStack.lua │ ├── entity │ │ ├── CharacterIdleState.lua │ │ ├── CharacterWalkState.lua │ │ ├── EnemyBattleState.lua │ │ ├── EntityBaseState.lua │ │ ├── NPCIdleState.lua │ │ ├── PartyBaseState.lua │ │ ├── PartyIdleState.lua │ │ └── PartyWalkState.lua │ └── game │ │ ├── BattleMenuState.lua │ │ ├── BattleMessageState.lua │ │ ├── BattleState.lua │ │ ├── DialogueState.lua │ │ ├── FadeInState.lua │ │ ├── FadeOutState.lua │ │ ├── GameOverState.lua │ │ ├── PlayState.lua │ │ ├── SelectActionState.lua │ │ ├── SelectCharacterState.lua │ │ ├── SelectTargetState.lua │ │ ├── ShowTextState.lua │ │ ├── StartState.lua │ │ ├── StatsMenuState.lua │ │ ├── TakeTurnState.lua │ │ └── TheEndState.lua │ ├── utilities │ └── quads.lua │ └── world │ ├── Region.lua │ ├── Tile.lua │ ├── TileMap.lua │ └── World.lua ├── 08-throw_a_bird ├── .gitattributes ├── .gitignore ├── assets │ ├── fonts │ │ ├── RifficFree-Bold.ttf │ │ └── riffic_medium.font │ └── textures │ │ ├── buildings │ │ ├── debrisWood_1.png │ │ ├── elementStone011.png │ │ ├── elementStone014.png │ │ ├── elementStone046.png │ │ ├── elementWood012.png │ │ ├── elementWood015.png │ │ └── elementWood047.png │ │ ├── characters │ │ ├── alienBlue_square.png │ │ ├── alienGreen_round.png │ │ └── parrot.png │ │ └── world │ │ ├── Clouds 2.png │ │ ├── Clouds 7.png │ │ ├── Forest Tree 13.png │ │ ├── Forest Tree 6.png │ │ ├── Forest Tree 7.png │ │ ├── Mountain 1.png │ │ ├── Mountain 2.png │ │ ├── Walking Platforms 8.png │ │ └── ground1px.png ├── game.project ├── input │ └── game.input_binding └── main │ ├── atlas │ ├── buildings.atlas │ ├── characters.atlas │ └── world.atlas │ ├── collections │ └── main.collection │ ├── game_objects │ ├── alien_round.go │ ├── alien_square.go │ ├── debris.go │ ├── parrot.go │ ├── stone.go │ └── wood.go │ └── scripts │ ├── debris.script │ ├── destructible.script │ ├── main.script │ ├── player.gui │ └── wind.script ├── LICENSE ├── README.md └── complementary_contents ├── 01-input_handler ├── command_pattern │ ├── Action.py │ ├── ActionJump.py │ ├── ActionShoot.py │ ├── Actor.py │ ├── InputHandler.py │ ├── main.py │ └── settings.py ├── observer_pattern │ ├── Actor.py │ ├── InputHandler.py │ ├── Listener.py │ ├── main.py │ └── settings.py └── requirements.txt ├── 02-anonymous_functions ├── anonymous_functions0.py ├── anonymous_functions1.py ├── anonymous_functions2.py ├── anonymous_functions3-problem.py └── anonymous_functions3-solution.py ├── 03-timers ├── 1-every │ ├── every0.py │ ├── every1.py │ ├── every2.py │ └── font.ttf ├── 2-tween │ ├── tween0.py │ ├── tween1.py │ └── tween2.py ├── 3-chain │ ├── chain0.py │ └── chain1.py └── requirements.txt └── 04-scenes_tutorial ├── requirements.txt └── tutorial ├── README.md ├── assets ├── graphics │ ├── creatures.png │ └── tileset.png └── scenes │ ├── 01.json │ ├── 01.tmx │ ├── 01.yaml │ ├── creatures.tsx │ └── tiles.tsx ├── main.py ├── settings.py └── src ├── Scene.py ├── Tutorial.py └── scene_loader ├── JsonSceneLoader.py ├── TmxSceneLoader.py ├── YamlSceneLoader.py └── __init__.py /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | **/hello_world 3 | **/main 4 | **/*.o 5 | **/build 6 | **/.venv 7 | **/__pycache__ 8 | **/.internal 9 | **/build 10 | **.externalToolBuilders 11 | **.DS_Store 12 | **Thumbs.db 13 | **.lock-wscript 14 | ***.pyc 15 | **.cproject 16 | **builtins 17 | **debug.keystore 18 | **debug.keystore.pass.txt 19 | **manifest.private.der 20 | **manifest.public.der 21 | !08-throw_a_bird/main -------------------------------------------------------------------------------- /00-hello_world/Makefile: -------------------------------------------------------------------------------- 1 | CC = clang 2 | 3 | LIBS = -lallegro -lallegro_font 4 | 5 | all: main 6 | 7 | main: main.c 8 | $(CC) $@.c -o $@ $(LIBS) 9 | 10 | .PHONY: 11 | clean: 12 | $(RM) main 13 | -------------------------------------------------------------------------------- /01-pong/Makefile: -------------------------------------------------------------------------------- 1 | CC = clang 2 | 3 | INCLUDE = -I. 4 | 5 | BUILD_DIR = build 6 | 7 | OBJS = paddle.o ball.o pong.o fonts.o hitbox.o sounds.o 8 | 9 | LIBS = -lallegro -lallegro_font -lallegro_primitives -lallegro_font -lallegro_ttf -lallegro_audio -lallegro_acodec $(BUILD_DIR)/*.o 10 | 11 | main: main.c $(BUILD_DIR) $(OBJS) 12 | $(CC) $(INCLUDE) $< -o $@ $(LIBS) 13 | 14 | hitbox.o: src/hitbox.c src/hitbox.h 15 | $(CC) -c $(INCLUDE) $< -o $(BUILD_DIR)/$@ 16 | 17 | fonts.o: src/fonts.c src/fonts.h 18 | $(CC) -c $(INCLUDE) $< -o $(BUILD_DIR)/$@ 19 | 20 | sounds.o: src/sounds.c src/sounds.h 21 | $(CC) -c $(INCLUDE) $< -o $(BUILD_DIR)/$@ 22 | 23 | paddle.o: src/paddle.c src/paddle.h 24 | $(CC) -c $(INCLUDE) $< -o $(BUILD_DIR)/$@ 25 | 26 | ball.o: src/ball.c src/ball.h 27 | $(CC) -c $(INCLUDE) $< -o $(BUILD_DIR)/$@ 28 | 29 | pong.o: src/pong.c src/pong.h 30 | $(CC) -c $(INCLUDE) $< -o $(BUILD_DIR)/$@ 31 | 32 | $(BUILD_DIR): 33 | mkdir -p $@ 34 | 35 | .PHONY: 36 | clean: 37 | $(RM) $(BUILD_DIR)/$(OBJS) main -------------------------------------------------------------------------------- /01-pong/assets/fonts/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/01-pong/assets/fonts/font.ttf -------------------------------------------------------------------------------- /01-pong/assets/sounds/paddle_hit.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/01-pong/assets/sounds/paddle_hit.wav -------------------------------------------------------------------------------- /01-pong/assets/sounds/score.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/01-pong/assets/sounds/score.wav -------------------------------------------------------------------------------- /01-pong/assets/sounds/wall_hit.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/01-pong/assets/sounds/wall_hit.wav -------------------------------------------------------------------------------- /01-pong/settings.h: -------------------------------------------------------------------------------- 1 | /* 2 | ISPPV1 2023 3 | Study Case: Pong 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the definition of some macros with values to set the pong game. 9 | */ 10 | 11 | #pragma once 12 | 13 | #ifndef MIN 14 | #define MIN(a, b) ((a < b) ? a : b) 15 | #endif 16 | 17 | #ifndef MAX 18 | #define MAX(a, b) ((a > b) ? a : b) 19 | #endif 20 | 21 | #define WINDOW_WIDTH 1280 22 | #define WINDOW_HEIGHT 720 23 | #define TABLE_WIDTH 432 24 | #define TABLE_HEIGHT 243 25 | #define PADDLE_WIDTH 5 26 | #define PADDLE_HEIGHT 20 27 | #define PADDLE_X_OFFSET 10 28 | #define PADDLE_Y_OFFSET 30 29 | #define PADDLE_SPEED 200 30 | #define BALL_SIZE 4 31 | #define MID_LINE_WIDTH 2 32 | #define FPS 60.0 33 | #define MAX_POINTS 5 34 | 35 | -------------------------------------------------------------------------------- /01-pong/src/ball.c: -------------------------------------------------------------------------------- 1 | /* 2 | ISPPV1 2023 3 | Study Case: Pong 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the definition of the functions to init a pong ball, 9 | update it, and render it. 10 | */ 11 | 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | void init_ball(struct Ball* ball, float x, float y, float s) 18 | { 19 | ball->x = x; 20 | ball->y = y; 21 | ball->width = s; 22 | ball->height = s; 23 | ball->vx = 0; 24 | ball->vy = 0; 25 | } 26 | 27 | void build_ball_hitbox(struct Ball ball, struct Hitbox* hitbox) 28 | { 29 | hitbox->x1 = ball.x; 30 | hitbox->y1 = ball.y; 31 | hitbox->x2 = ball.x + ball.width; 32 | hitbox->y2 = ball.y + ball.height; 33 | } 34 | 35 | void update_ball(struct Ball* ball, float dt) 36 | { 37 | ball->x += ball->vx * dt; 38 | ball->y += ball->vy * dt; 39 | } 40 | 41 | void render_ball(struct Ball ball) 42 | { 43 | al_draw_filled_rectangle( 44 | ball.x, ball.y, ball.x + ball.width, ball.y + ball.height, al_map_rgb(255, 255, 255) 45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /01-pong/src/ball.h: -------------------------------------------------------------------------------- 1 | /* 2 | ISPPV1 2023 3 | Study Case: Pong 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the definition of a pong ball and the declaration 9 | of the functions to init it, update it, and render it. 10 | */ 11 | 12 | #pragma once 13 | 14 | #include 15 | 16 | struct Ball 17 | { 18 | float x; 19 | float y; 20 | float width; 21 | float height; 22 | float vx; 23 | float vy; 24 | }; 25 | 26 | void init_ball(struct Ball* ball, float x, float y, float s); 27 | 28 | void build_ball_hitbox(struct Ball ball, struct Hitbox* hitbox); 29 | 30 | void update_ball(struct Ball* ball, float dt); 31 | 32 | void render_ball(struct Ball ball); 33 | -------------------------------------------------------------------------------- /01-pong/src/fonts.c: -------------------------------------------------------------------------------- 1 | /* 2 | ISPPV1 2023 3 | Study Case: Pong 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the definition of functions to create fonts and destroy them. 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | void create_fonts(struct Fonts* fonts) 15 | { 16 | fonts->large_font = al_load_ttf_font("assets/fonts/font.ttf", 16, ALLEGRO_TTF_MONOCHROME); 17 | fonts->score_font = al_load_ttf_font("assets/fonts/font.ttf", 32, ALLEGRO_TTF_MONOCHROME); 18 | } 19 | 20 | void destroy_fonts(struct Fonts* fonts) 21 | { 22 | al_destroy_font(fonts->large_font); 23 | al_destroy_font(fonts->score_font); 24 | } 25 | -------------------------------------------------------------------------------- /01-pong/src/fonts.h: -------------------------------------------------------------------------------- 1 | /* 2 | ISPPV1 2023 3 | Study Case: Pong 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the definition of a struct to load fonts and the 9 | declaratrion of functions to create them and destroy them. 10 | */ 11 | 12 | #pragma once 13 | 14 | #include 15 | 16 | struct Fonts 17 | { 18 | ALLEGRO_FONT* large_font; 19 | ALLEGRO_FONT* score_font; 20 | }; 21 | 22 | void create_fonts(struct Fonts* fonts); 23 | 24 | void destroy_fonts(struct Fonts* fonts); 25 | -------------------------------------------------------------------------------- /01-pong/src/hitbox.c: -------------------------------------------------------------------------------- 1 | /* 2 | ISPPV1 2023 3 | Study Case: Pong 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the definition of the function to determine 9 | whether two hitboxs collide. 10 | */ 11 | 12 | #include 13 | 14 | int collides(struct Hitbox hitbox1, struct Hitbox hitbox2) 15 | { 16 | if (hitbox1.x1 > hitbox2.x2 || hitbox2.x1 > hitbox1.x2) 17 | { 18 | return 0; 19 | } 20 | 21 | if (hitbox1.y1 > hitbox2.y2 || hitbox2.y1 > hitbox1.y2) 22 | { 23 | return 0; 24 | } 25 | 26 | return 1; 27 | } 28 | -------------------------------------------------------------------------------- /01-pong/src/hitbox.h: -------------------------------------------------------------------------------- 1 | /* 2 | ISPPV1 2023 3 | Study Case: Pong 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the definition of a hitbox and a function to determine 9 | whether two hitboxs collide. 10 | */ 11 | 12 | #pragma once 13 | 14 | struct Hitbox 15 | { 16 | float x1; 17 | float y1; 18 | float x2; 19 | float y2; 20 | }; 21 | 22 | int collides(struct Hitbox hitbox1, struct Hitbox hitbox2); 23 | -------------------------------------------------------------------------------- /01-pong/src/paddle.c: -------------------------------------------------------------------------------- 1 | /* 2 | ISPPV1 2023 3 | Study Case: Pong 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the definition of the functions to init a pong paddle, 9 | update it, and render it. 10 | */ 11 | 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | void init_paddle(struct Paddle* paddle, float x, float y, float w, float h) 18 | { 19 | paddle->x = x; 20 | paddle->y = y; 21 | paddle->width = w; 22 | paddle->height = h; 23 | paddle->vy = 0; 24 | } 25 | 26 | void build_paddle_hitbox(struct Paddle paddle, struct Hitbox* hitbox) 27 | { 28 | hitbox->x1 = paddle.x; 29 | hitbox->y1 = paddle.y; 30 | hitbox->x2 = paddle.x + paddle.width; 31 | hitbox->y2 = paddle.y + paddle.height; 32 | } 33 | 34 | void update_paddle(struct Paddle* paddle, float dt) 35 | { 36 | paddle->y += paddle->vy * dt; 37 | 38 | paddle->y = MAX(0, MIN(paddle->y, TABLE_HEIGHT - PADDLE_HEIGHT)); 39 | } 40 | 41 | void render_paddle(struct Paddle paddle) 42 | { 43 | al_draw_filled_rectangle( 44 | paddle.x, paddle.y, paddle.x + paddle.width, paddle.y + paddle.height, 45 | al_map_rgb(255, 255, 255) 46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /01-pong/src/paddle.h: -------------------------------------------------------------------------------- 1 | /* 2 | ISPPV1 2023 3 | Study Case: Pong 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the definition of a pong paddle and the declaration 9 | of the functions to init it, update it, and render it. 10 | */ 11 | 12 | #pragma once 13 | 14 | #include 15 | 16 | struct Paddle 17 | { 18 | float x; 19 | float y; 20 | float width; 21 | float height; 22 | float vy; 23 | }; 24 | 25 | void init_paddle(struct Paddle* paddle, float x, float y, float w, float h); 26 | 27 | void build_paddle_hitbox(struct Paddle paddle, struct Hitbox* hitbox); 28 | 29 | void update_paddle(struct Paddle* paddle, float dt); 30 | 31 | void render_paddle(struct Paddle paddle); 32 | -------------------------------------------------------------------------------- /01-pong/src/pong.h: -------------------------------------------------------------------------------- 1 | /* 2 | ISPPV1 2023 3 | Study Case: Pong 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the definition of a pong game and the declaration 9 | of the functions to init it, update it, and render it. 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | enum PongState 17 | { 18 | START, 19 | SERVE, 20 | PLAY, 21 | DONE 22 | }; 23 | 24 | struct Pong 25 | { 26 | struct Paddle player1; 27 | struct Paddle player2; 28 | struct Ball ball; 29 | 30 | enum PongState state; 31 | 32 | int player1_score; 33 | int player2_score; 34 | int serving_player; 35 | int winning_player; 36 | 37 | struct Sounds* sounds; 38 | }; 39 | 40 | void init_pong(struct Pong* pong, struct Sounds* sounds); 41 | 42 | void handle_input_pong(struct Pong* pong, ALLEGRO_KEYBOARD_STATE* state); 43 | 44 | void update_pong(struct Pong* pong, double dt); 45 | 46 | void render_pong(struct Pong pong, struct Fonts fonts); 47 | -------------------------------------------------------------------------------- /01-pong/src/sounds.c: -------------------------------------------------------------------------------- 1 | /* 2 | ISPPV1 2023 3 | Study Case: Pong 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the definition of functions to create sounds and destroy them. 9 | */ 10 | 11 | #include 12 | 13 | #include 14 | 15 | void create_sounds(struct Sounds* sounds) 16 | { 17 | sounds->paddle_hit = al_load_sample("assets/sounds/paddle_hit.wav"); 18 | sounds->wall_hit = al_load_sample("assets/sounds/wall_hit.wav"); 19 | sounds->score = al_load_sample("assets/sounds/score.wav"); 20 | al_reserve_samples(3); 21 | 22 | assert(sounds->paddle_hit != NULL); 23 | assert(sounds->wall_hit != NULL); 24 | assert(sounds->score != NULL); 25 | } 26 | 27 | void destroy_sounds(struct Sounds* sounds) 28 | { 29 | al_destroy_sample(sounds->paddle_hit); 30 | al_destroy_sample(sounds->wall_hit); 31 | al_destroy_sample(sounds->score); 32 | } 33 | -------------------------------------------------------------------------------- /01-pong/src/sounds.h: -------------------------------------------------------------------------------- 1 | /* 2 | ISPPV1 2023 3 | Study Case: Pong 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the definition of a struct to load sounds and the 9 | declaratrion of functions to create them and destroy them. 10 | */ 11 | 12 | #pragma once 13 | 14 | #include 15 | 16 | struct Sounds 17 | { 18 | ALLEGRO_SAMPLE* paddle_hit; 19 | ALLEGRO_SAMPLE* wall_hit; 20 | ALLEGRO_SAMPLE* score; 21 | }; 22 | 23 | void create_sounds(struct Sounds* sounds); 24 | 25 | void destroy_sounds(struct Sounds* sounds); 26 | -------------------------------------------------------------------------------- /02-flappy_bird/assets/fonts/flappy.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/02-flappy_bird/assets/fonts/flappy.ttf -------------------------------------------------------------------------------- /02-flappy_bird/assets/fonts/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/02-flappy_bird/assets/fonts/font.ttf -------------------------------------------------------------------------------- /02-flappy_bird/assets/graphics/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/02-flappy_bird/assets/graphics/background.png -------------------------------------------------------------------------------- /02-flappy_bird/assets/graphics/bird.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/02-flappy_bird/assets/graphics/bird.png -------------------------------------------------------------------------------- /02-flappy_bird/assets/graphics/ground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/02-flappy_bird/assets/graphics/ground.png -------------------------------------------------------------------------------- /02-flappy_bird/assets/graphics/log.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/02-flappy_bird/assets/graphics/log.png -------------------------------------------------------------------------------- /02-flappy_bird/assets/sounds/explosion.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/02-flappy_bird/assets/sounds/explosion.wav -------------------------------------------------------------------------------- /02-flappy_bird/assets/sounds/hurt.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/02-flappy_bird/assets/sounds/hurt.wav -------------------------------------------------------------------------------- /02-flappy_bird/assets/sounds/jump.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/02-flappy_bird/assets/sounds/jump.wav -------------------------------------------------------------------------------- /02-flappy_bird/assets/sounds/marios_way.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/02-flappy_bird/assets/sounds/marios_way.ogg -------------------------------------------------------------------------------- /02-flappy_bird/assets/sounds/score.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/02-flappy_bird/assets/sounds/score.wav -------------------------------------------------------------------------------- /02-flappy_bird/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ISPPV1 2023 3 | Study Case: Flappy Bird 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the main program to run the game. 9 | */ 10 | 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | int main() 17 | { 18 | Settings::init(); 19 | 20 | Game game{}; 21 | 22 | sf::Clock clock; 23 | sf::Time dt; 24 | 25 | while (game.get_window().isOpen()) 26 | { 27 | sf::Event event; 28 | 29 | while (game.get_window().pollEvent(event)) 30 | { 31 | if (event.type == sf::Event::Closed || (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape)) 32 | { 33 | game.get_window().close(); 34 | } 35 | else if (event.type == sf::Event::MouseButtonPressed || event.type == sf::Event::KeyPressed) 36 | { 37 | game.handle_inputs(event); 38 | } 39 | } 40 | 41 | game.update(dt.asSeconds()); 42 | game.render(); 43 | 44 | dt = clock.restart(); 45 | } 46 | 47 | return EXIT_SUCCESS; 48 | } 49 | -------------------------------------------------------------------------------- /02-flappy_bird/src/Bird.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ISPPV1 2023 3 | Study Case: Flappy Bird 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the definition of the class Bird. 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | Bird::Bird(float _x, float _y, float w, float h) noexcept 15 | : x{_x}, y{_y}, width{w}, height{h}, vy{0.f}, sprite{Settings::textures["bird"]} 16 | { 17 | sprite.setPosition(x, y); 18 | } 19 | 20 | sf::FloatRect Bird::get_collision_rect() const noexcept 21 | { 22 | return sf::FloatRect{x, y, width, height}; 23 | } 24 | 25 | void Bird::jump() noexcept 26 | { 27 | if (!jumping) 28 | { 29 | jumping = true; 30 | } 31 | } 32 | 33 | void Bird::update(float dt) noexcept 34 | { 35 | vy += Settings::GRAVITY * dt; 36 | 37 | if (jumping) 38 | { 39 | Settings::sounds["jump"].play(); 40 | vy = -Settings::JUMP_TAKEOFF_SPEED; 41 | jumping = false; 42 | } 43 | 44 | y += vy * dt; 45 | sprite.setPosition(x, y); 46 | } 47 | 48 | void Bird::render(sf::RenderTarget& target) const noexcept 49 | { 50 | target.draw(sprite); 51 | } -------------------------------------------------------------------------------- /02-flappy_bird/src/Bird.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | ISPPV1 2023 3 | Study Case: Flappy Bird 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the declaration of the class Bird. 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | 15 | class Bird 16 | { 17 | public: 18 | Bird(float _x, float _y, float w, float h) noexcept; 19 | 20 | Bird(const Bird&) = delete; 21 | 22 | Bird& operator = (Bird) = delete; 23 | 24 | sf::FloatRect get_collision_rect() const noexcept; 25 | 26 | void jump() noexcept; 27 | 28 | void update(float dt) noexcept; 29 | 30 | void render(sf::RenderTarget& target) const noexcept; 31 | 32 | private: 33 | float x; 34 | float y; 35 | float width; 36 | float height; 37 | float vy; 38 | sf::Sprite sprite; 39 | bool jumping{false}; 40 | }; 41 | -------------------------------------------------------------------------------- /02-flappy_bird/src/Factory.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | template 10 | class Factory 11 | { 12 | public: 13 | template 14 | std::shared_ptr create(N1 x, N2 y, Args... args) 15 | { 16 | static_assert(std::is_convertible::value, "Template argument N1 should be convertible to float."); 17 | static_assert(std::is_convertible::value, "Template argument N2 should be convertible to float."); 18 | static_assert(std::is_constructible::value); 19 | 20 | if (buffer.size() > 0) 21 | { 22 | auto object = buffer.back(); 23 | object->reset(float(x), float(y)); 24 | buffer.pop_back(); 25 | return object; 26 | } 27 | return std::make_shared(float(x), float(y), args...); 28 | } 29 | 30 | void remove(std::shared_ptr object) 31 | { 32 | buffer.push_back(object); 33 | } 34 | 35 | private: 36 | std::list> buffer; 37 | }; 38 | -------------------------------------------------------------------------------- /02-flappy_bird/src/Game.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | ISPPV1 2023 3 | Study Case: Flappy Bird 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the declaration of the class Game. 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | class Game 19 | { 20 | public: 21 | Game(); 22 | 23 | Game(const Game&) = delete; 24 | 25 | Game& operator = (Game) = delete; 26 | 27 | sf::RenderWindow& get_window() noexcept; 28 | 29 | void handle_inputs(const sf::Event& event) noexcept; 30 | 31 | void update(float dt) noexcept; 32 | 33 | void render() noexcept; 34 | 35 | private: 36 | sf::RenderWindow render_window; 37 | sf::RenderTexture render_texture; 38 | sf::Sprite render_sprite; 39 | 40 | StateMachine state_machine; 41 | 42 | bool bird_is_dead{false}; 43 | int score{0}; 44 | }; 45 | -------------------------------------------------------------------------------- /02-flappy_bird/src/Log.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ISPPV1 2023 3 | Study Case: Flappy Bird 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the definition of the class Log. 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | Log::Log(float _x, float _y, bool _inverted) noexcept 15 | : x{_x}, y{_y}, inverted{_inverted}, sprite{Settings::textures["Log"]} 16 | { 17 | if (inverted) 18 | { 19 | sprite.rotate(180.f); 20 | } 21 | } 22 | 23 | sf::FloatRect Log::get_collision_rect() const noexcept 24 | { 25 | if (!inverted) 26 | { 27 | return sf::FloatRect{x, y, Settings::LOG_WIDTH, Settings::LOG_HEIGHT}; 28 | } 29 | 30 | return sf::FloatRect{x - Settings::LOG_WIDTH, y - Settings::LOG_HEIGHT, Settings::LOG_WIDTH, Settings::LOG_HEIGHT}; 31 | } 32 | 33 | void Log::update(float _x) noexcept 34 | { 35 | x = _x; 36 | 37 | if (inverted) 38 | { 39 | x += Settings::LOG_WIDTH; 40 | } 41 | 42 | sprite.setPosition(x, y); 43 | } 44 | 45 | void Log::render(sf::RenderTarget& target) const noexcept 46 | { 47 | target.draw(sprite); 48 | } 49 | -------------------------------------------------------------------------------- /02-flappy_bird/src/Log.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | ISPPV1 2023 3 | Study Case: Flappy Bird 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the declaration of the class Log. 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | 15 | class Log 16 | { 17 | public: 18 | Log(float _x, float _y, bool inverted) noexcept; 19 | 20 | sf::FloatRect get_collision_rect() const noexcept; 21 | 22 | void update(float _x) noexcept; 23 | 24 | void render(sf::RenderTarget& target) const noexcept; 25 | 26 | private: 27 | float x; 28 | float y; 29 | bool inverted; 30 | sf::Sprite sprite; 31 | }; -------------------------------------------------------------------------------- /02-flappy_bird/src/LogPair.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ISPPV1 2023 3 | Study Case: Flappy Bird 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the definition of the class LogPair. 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | LogPair::LogPair(float _x, float _y) noexcept 15 | : x{_x}, y{_y}, 16 | top{x, y + Settings::LOG_HEIGHT, true}, 17 | bottom{x, y + Settings::LOGS_GAP + Settings::LOG_HEIGHT, false} 18 | { 19 | 20 | } 21 | 22 | bool LogPair::collides(const sf::FloatRect& rect) const noexcept 23 | { 24 | return top.get_collision_rect().intersects(rect) || bottom.get_collision_rect().intersects(rect); 25 | } 26 | 27 | void LogPair::update(float dt) noexcept 28 | { 29 | x += -Settings::MAIN_SCROLL_SPEED * dt; 30 | 31 | top.update(x); 32 | bottom.update(x); 33 | } 34 | 35 | void LogPair::render(sf::RenderTarget& target) const noexcept 36 | { 37 | top.render(target); 38 | bottom.render(target); 39 | } 40 | 41 | bool LogPair::is_out_of_game() const noexcept 42 | { 43 | return x < -Settings::LOG_WIDTH; 44 | } 45 | 46 | bool LogPair::update_scored(const sf::FloatRect& rect) noexcept 47 | { 48 | if (scored) 49 | { 50 | return false; 51 | } 52 | 53 | if (rect.left > x + Settings::LOG_WIDTH) 54 | { 55 | scored = true; 56 | return true; 57 | } 58 | 59 | return false; 60 | } 61 | 62 | void LogPair::reset(float _x, float _y) noexcept 63 | { 64 | x = _x; 65 | y = _y; 66 | scored = false; 67 | } -------------------------------------------------------------------------------- /02-flappy_bird/src/LogPair.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | ISPPV1 2023 3 | Study Case: Flappy Bird 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the declaration of the class LogPair. 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | 16 | class LogPair 17 | { 18 | public: 19 | LogPair(float _x, float _y) noexcept; 20 | 21 | bool collides(const sf::FloatRect& rect) const noexcept; 22 | 23 | void update(float dt) noexcept; 24 | 25 | void render(sf::RenderTarget& target) const noexcept; 26 | 27 | bool is_out_of_game() const noexcept; 28 | 29 | bool update_scored(const sf::FloatRect& rect) noexcept; 30 | 31 | void reset(float _x, float _y) noexcept; 32 | 33 | private: 34 | float x; 35 | float y; 36 | Log top; 37 | Log bottom; 38 | 39 | bool scored{false}; 40 | }; -------------------------------------------------------------------------------- /02-flappy_bird/src/World.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | ISPPV1 2023 3 | Study Case: Flappy Bird 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the declaration of the class World. 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #include 20 | #include 21 | 22 | class World 23 | { 24 | public: 25 | World(bool _generate_logs = false) noexcept; 26 | 27 | World(const World& world) = delete; 28 | 29 | World& operator = (World) = delete; 30 | 31 | void reset(bool _generate_logs) noexcept; 32 | 33 | bool collides(const sf::FloatRect& rect) const noexcept; 34 | 35 | bool update_scored(const sf::FloatRect& rect) noexcept; 36 | 37 | void update(float dt) noexcept; 38 | 39 | void render(sf::RenderTarget& target) const noexcept; 40 | private: 41 | bool generate_logs; 42 | 43 | sf::Sprite background; 44 | sf::Sprite ground; 45 | 46 | float background_x{0.f}; 47 | float ground_x{0.f}; 48 | 49 | Factory log_factory; 50 | 51 | std::list> logs; 52 | 53 | std::mt19937 rng; 54 | 55 | float logs_spawn_timer{0.f}; 56 | float last_log_y{0.f}; 57 | }; 58 | -------------------------------------------------------------------------------- /02-flappy_bird/src/states/BaseState.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | ISPPV1 2023 3 | Study Case: Flappy Bird 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the declaration of the class BaseState. 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | 15 | #include 16 | 17 | class StateMachine; 18 | class World; 19 | class Bird; 20 | 21 | class BaseState 22 | { 23 | public: 24 | BaseState(StateMachine* sm) : state_machine(sm) {} 25 | 26 | virtual ~BaseState() {} 27 | 28 | virtual void enter(std::shared_ptr world, std::shared_ptr bird) noexcept {} 29 | 30 | virtual void exit() noexcept {} 31 | 32 | virtual void handle_inputs(const sf::Event& event) noexcept {} 33 | 34 | virtual void update(float dt) noexcept {} 35 | 36 | virtual void render(sf::RenderTarget& target) const noexcept {} 37 | 38 | 39 | protected: 40 | StateMachine* state_machine; 41 | }; 42 | -------------------------------------------------------------------------------- /02-flappy_bird/src/states/CountDownState.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ISPPV1 2023 3 | Study Case: Flappy Bird 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the definition of the class CountDownBaseState. 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | CountDownState::CountDownState(StateMachine* sm) noexcept 17 | : BaseState{sm} 18 | { 19 | 20 | } 21 | 22 | void CountDownState::enter(std::shared_ptr _world, std::shared_ptr _bird) noexcept 23 | { 24 | world = std::make_shared(false); 25 | } 26 | 27 | void CountDownState::update(float dt) noexcept 28 | { 29 | timer += dt; 30 | 31 | if (timer >= 1.f) 32 | { 33 | timer = 0.f; 34 | --counter; 35 | 36 | if (counter == 0) 37 | { 38 | state_machine->change_state("playing", world); 39 | } 40 | } 41 | 42 | world->update(dt); 43 | } 44 | 45 | void CountDownState::render(sf::RenderTarget& target) const noexcept 46 | { 47 | world->render(target); 48 | render_text(target, Settings::VIRTUAL_WIDTH / 2, Settings::VIRTUAL_HEIGHT / 2, std::to_string(counter), Settings::HUGE_TEXT_SIZE, "font", sf::Color::White, true); 49 | } -------------------------------------------------------------------------------- /02-flappy_bird/src/states/CountDownState.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | ISPPV1 2023 3 | Study Case: Flappy Bird 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the declaration of the class CountDownBaseState. 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | 16 | class CountDownState: public BaseState 17 | { 18 | public: 19 | CountDownState(StateMachine* sm) noexcept; 20 | 21 | void enter(std::shared_ptr _world = nullptr, std::shared_ptr _bird = nullptr) noexcept override; 22 | 23 | void update(float dt) noexcept override; 24 | 25 | void render(sf::RenderTarget& target) const noexcept override; 26 | 27 | private: 28 | std::shared_ptr world; 29 | int counter{3}; 30 | float timer{0.f}; 31 | }; 32 | -------------------------------------------------------------------------------- /02-flappy_bird/src/states/PlayingState.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | ISPPV1 2023 3 | Study Case: Flappy Bird 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the declaration of the class PlayingBaseState. 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | class PlayingState: public BaseState 18 | { 19 | 20 | public: 21 | PlayingState(StateMachine* sm) noexcept; 22 | 23 | void enter(std::shared_ptr _world = nullptr, std::shared_ptr _bird = nullptr) noexcept override; 24 | 25 | void handle_inputs(const sf::Event& event) noexcept override; 26 | 27 | void update(float dt) noexcept override; 28 | 29 | void render(sf::RenderTarget& target) const noexcept override; 30 | 31 | private: 32 | std::shared_ptr bird; 33 | std::shared_ptr world; 34 | int score{0}; 35 | }; 36 | -------------------------------------------------------------------------------- /02-flappy_bird/src/states/StateMachine.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ISPPV1 2023 3 | Study Case: Flappy Bird 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the definition of the class StateMachine. 9 | */ 10 | 11 | #include 12 | 13 | StateMachine::StateMachine(const std::initializer_list>& init_states) noexcept 14 | : states{init_states.begin(), init_states.end()} 15 | { 16 | 17 | } 18 | 19 | void StateMachine::change_state(const std::string& state_name, std::shared_ptr world, std::shared_ptr bird) noexcept 20 | { 21 | auto it = states.find(state_name); 22 | 23 | if (it == states.end()) 24 | { 25 | return; 26 | } 27 | 28 | current_state->exit(); 29 | current_state = it->second(this); 30 | current_state->enter(world, bird); 31 | } 32 | 33 | void StateMachine::handle_inputs(const sf::Event& event) noexcept 34 | { 35 | current_state->handle_inputs(event); 36 | } 37 | 38 | void StateMachine::update(float dt) noexcept 39 | { 40 | current_state->update(dt); 41 | } 42 | 43 | void StateMachine::render(sf::RenderTarget& target) const noexcept 44 | { 45 | current_state->render(target); 46 | } -------------------------------------------------------------------------------- /02-flappy_bird/src/states/StateMachine.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | ISPPV1 2023 3 | Study Case: Flappy Bird 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the declaration of the class StateMachine. 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | class StateMachine 21 | { 22 | public: 23 | using StateBuilder = std::function(StateMachine*)>; 24 | 25 | StateMachine(const std::initializer_list>& init_states = {}) noexcept; 26 | 27 | void change_state(const std::string& state_name, std::shared_ptr world = nullptr, std::shared_ptr bird = nullptr) noexcept; 28 | 29 | void handle_inputs(const sf::Event& event) noexcept; 30 | 31 | void update(float dt) noexcept; 32 | 33 | void render(sf::RenderTarget& target) const noexcept; 34 | 35 | private: 36 | std::unordered_map states; 37 | std::shared_ptr current_state{std::make_shared(this)}; 38 | }; 39 | -------------------------------------------------------------------------------- /02-flappy_bird/src/states/TitleScreenState.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ISPPV1 2023 3 | Study Case: Flappy Bird 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the declaration of the class TitleScreenState. 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | TitleScreenState::TitleScreenState(StateMachine* sm) noexcept 17 | : BaseState{sm}, world{} 18 | { 19 | 20 | } 21 | 22 | void TitleScreenState::handle_inputs(const sf::Event& event) noexcept 23 | { 24 | if (event.key.code == sf::Keyboard::Enter) 25 | { 26 | state_machine->change_state("count_down"); 27 | } 28 | } 29 | 30 | void TitleScreenState::update(float dt) noexcept 31 | { 32 | world.update(dt); 33 | } 34 | 35 | void TitleScreenState::render(sf::RenderTarget& target) const noexcept 36 | { 37 | world.render(target); 38 | render_text(target, Settings::VIRTUAL_WIDTH / 2, Settings::VIRTUAL_HEIGHT / 3, "Flappy Bird", Settings::FLAPPY_TEXT_SIZE, "flappy", sf::Color::White, true); 39 | render_text(target, Settings::VIRTUAL_WIDTH / 2, 2 * Settings::VIRTUAL_HEIGHT / 3, "Press Enter to start", Settings::MEDIUM_TEXT_SIZE, "font", sf::Color::White, true); 40 | } -------------------------------------------------------------------------------- /02-flappy_bird/src/states/TitleScreenState.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | ISPPV1 2023 3 | Study Case: Flappy Bird 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the definition of the class TitleScreenState. 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | 16 | class TitleScreenState: public BaseState 17 | { 18 | public: 19 | TitleScreenState(StateMachine* sm) noexcept; 20 | 21 | void handle_inputs(const sf::Event& event) noexcept override; 22 | 23 | void update(float dt) noexcept override; 24 | 25 | void render(sf::RenderTarget& target) const noexcept override; 26 | 27 | private: 28 | World world; 29 | }; 30 | -------------------------------------------------------------------------------- /02-flappy_bird/src/text_utilities.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ISPPV1 2023 3 | Study Case: Flappy Bird 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the definition of utility functions to build texts. 9 | */ 10 | 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | void center_text(sf::Text& text) noexcept 17 | { 18 | sf::Vector2f pos = text.getPosition(); 19 | float center_x = text.getGlobalBounds().width / 2.f; 20 | float center_y = text.getGlobalBounds().height / 2.f; 21 | float local_center_x = round(center_x + text.getLocalBounds().left); 22 | float local_center_y = round(center_y + text.getLocalBounds().top); 23 | text.setOrigin(sf::Vector2f{local_center_x, local_center_y}); 24 | text.setPosition(pos); 25 | } 26 | 27 | void render_text(sf::RenderTarget& target, float x, float y, const std::string& text_str, int size, const std::string& font_name, const sf::Color& color, bool center) noexcept 28 | { 29 | sf::Text text; 30 | text.setFont(Settings::fonts[font_name]); 31 | text.setString(text_str); 32 | text.setCharacterSize(size); 33 | text.setFillColor(sf::Color::Black); 34 | text.move(x + 2, y + 2); 35 | if (center) 36 | { 37 | center_text(text); 38 | } 39 | target.draw(text); 40 | text.setFillColor(sf::Color::White); 41 | text.move(-2, -2); 42 | target.draw(text); 43 | } 44 | -------------------------------------------------------------------------------- /02-flappy_bird/src/text_utilities.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | ISPPV1 2023 3 | Study Case: Flappy Bird 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the declaration of utility functions to build texts. 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | 15 | #include 16 | 17 | void render_text(sf::RenderTarget& target, float x, float y, const std::string& text_str, int size, const std::string& font_name, const sf::Color& color, bool center = false) noexcept; 18 | -------------------------------------------------------------------------------- /03-breakout/assets/fonts/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/03-breakout/assets/fonts/font.ttf -------------------------------------------------------------------------------- /03-breakout/assets/graphics/arrows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/03-breakout/assets/graphics/arrows.png -------------------------------------------------------------------------------- /03-breakout/assets/graphics/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/03-breakout/assets/graphics/background.png -------------------------------------------------------------------------------- /03-breakout/assets/graphics/breakout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/03-breakout/assets/graphics/breakout.png -------------------------------------------------------------------------------- /03-breakout/assets/graphics/hearts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/03-breakout/assets/graphics/hearts.png -------------------------------------------------------------------------------- /03-breakout/assets/sounds/brick_hit_1.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/03-breakout/assets/sounds/brick_hit_1.wav -------------------------------------------------------------------------------- /03-breakout/assets/sounds/brick_hit_2.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/03-breakout/assets/sounds/brick_hit_2.wav -------------------------------------------------------------------------------- /03-breakout/assets/sounds/grow_up.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/03-breakout/assets/sounds/grow_up.wav -------------------------------------------------------------------------------- /03-breakout/assets/sounds/high_score.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/03-breakout/assets/sounds/high_score.wav -------------------------------------------------------------------------------- /03-breakout/assets/sounds/hurt.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/03-breakout/assets/sounds/hurt.wav -------------------------------------------------------------------------------- /03-breakout/assets/sounds/level_complete.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/03-breakout/assets/sounds/level_complete.wav -------------------------------------------------------------------------------- /03-breakout/assets/sounds/life.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/03-breakout/assets/sounds/life.wav -------------------------------------------------------------------------------- /03-breakout/assets/sounds/music.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/03-breakout/assets/sounds/music.ogg -------------------------------------------------------------------------------- /03-breakout/assets/sounds/paddle_hit.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/03-breakout/assets/sounds/paddle_hit.wav -------------------------------------------------------------------------------- /03-breakout/assets/sounds/pause.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/03-breakout/assets/sounds/pause.wav -------------------------------------------------------------------------------- /03-breakout/assets/sounds/selected.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/03-breakout/assets/sounds/selected.wav -------------------------------------------------------------------------------- /03-breakout/assets/sounds/wall_hit.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/03-breakout/assets/sounds/wall_hit.wav -------------------------------------------------------------------------------- /03-breakout/main.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Breakout 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the main program to run the game. 9 | """ 10 | 11 | import settings 12 | from src.Breakout import Breakout 13 | 14 | if __name__ == "__main__": 15 | game = Breakout( 16 | "Breakout", 17 | settings.WINDOW_WIDTH, 18 | settings.WINDOW_HEIGHT, 19 | settings.VIRTUAL_WIDTH, 20 | settings.VIRTUAL_HEIGHT, 21 | ) 22 | game.exec() 23 | -------------------------------------------------------------------------------- /03-breakout/requirements.txt: -------------------------------------------------------------------------------- 1 | https://github.com/R3mmurd/Gale/archive/main.zip -------------------------------------------------------------------------------- /03-breakout/src/Breakout.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Breakout 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the class Breakout as a specialization of gale.Game 9 | """ 10 | 11 | import pygame 12 | 13 | from gale.game import Game 14 | from gale.state import StateMachine 15 | from gale.input_handler import InputData 16 | 17 | import settings 18 | 19 | from src import states 20 | 21 | 22 | class Breakout(Game): 23 | def init(self) -> None: 24 | self.state_machine = StateMachine( 25 | { 26 | "start": states.StartState, 27 | "high_score": states.HighScoreState, 28 | "enter_high_score": states.EnterHighScoreState, 29 | "game_over": states.GameOverState, 30 | "paddle_select": states.PaddleSelectState, 31 | "serve": states.ServeState, 32 | "play": states.PlayState, 33 | "victory": states.VictoryState, 34 | "pause": states.PauseState, 35 | } 36 | ) 37 | self.state_machine.change("start") 38 | pygame.mixer_music.load(settings.BASE_DIR / "assets" / "sounds" / "music.ogg") 39 | pygame.mixer_music.play(loops=-1) 40 | 41 | def update(self, dt: float) -> None: 42 | self.state_machine.update(dt) 43 | 44 | def render(self, surface: pygame.Surface) -> None: 45 | surface.blit(settings.TEXTURES["background"], (0, 0)) 46 | self.state_machine.render(surface) 47 | 48 | def on_input(self, input_id: str, input_data: InputData) -> None: 49 | if input_id == "quit" and input_data.pressed: 50 | self.quit() 51 | else: 52 | self.state_machine.on_input(input_id, input_data) 53 | -------------------------------------------------------------------------------- /03-breakout/src/Paddle.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Breakout 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the class Paddle. 9 | """ 10 | 11 | import pygame 12 | 13 | import settings 14 | 15 | 16 | class Paddle: 17 | def __init__(self, x: int, y: int) -> None: 18 | self.x = x 19 | self.y = y 20 | self.width = 64 21 | self.height = 16 22 | 23 | # By default, the blue paddle 24 | self.skin = 0 25 | 26 | # By default, the 64-pixels-width paddle. 27 | self.size = 1 28 | 29 | self.texture = settings.TEXTURES["spritesheet"] 30 | self.frames = settings.FRAMES["paddles"] 31 | 32 | # The paddle only move horizontally 33 | self.vx = 0 34 | 35 | def resize(self, size: int) -> None: 36 | self.size = size 37 | self.width = (self.size + 1) * 32 38 | 39 | def dec_size(self): 40 | self.resize(max(0, self.size - 1)) 41 | 42 | def inc_size(self): 43 | self.resize(min(3, self.size + 1)) 44 | 45 | def get_collision_rect(self) -> pygame.Rect: 46 | return pygame.Rect(self.x, self.y, self.width, self.height) 47 | 48 | def update(self, dt: float) -> None: 49 | next_x = self.x + self.vx * dt 50 | 51 | if self.vx < 0: 52 | self.x = max(0, next_x) 53 | else: 54 | self.x = min(settings.VIRTUAL_WIDTH - self.width, next_x) 55 | 56 | def render(self, surface: pygame.Surface) -> None: 57 | surface.blit(self.texture, (self.x, self.y), self.frames[self.skin][self.size]) 58 | -------------------------------------------------------------------------------- /03-breakout/src/powerups/PowerUp.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Breakout 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the base class PowerUp as an abstract class. 9 | """ 10 | 11 | from typing import TypeVar, Any 12 | 13 | import pygame 14 | 15 | import settings 16 | 17 | 18 | class PowerUp: 19 | """ 20 | The base power-up. 21 | """ 22 | 23 | def __init__(self, x: int, y: int, frame: int) -> None: 24 | self.x = x 25 | self.y = y 26 | self.vy = settings.POWERUP_SPEED 27 | self.active = True 28 | self.frame = frame 29 | 30 | def get_collision_rect(self) -> pygame.Rect: 31 | return pygame.Rect(self.x, self.y, 16, 16) 32 | 33 | def collides(self, obj: Any) -> bool: 34 | return self.get_collision_rect().colliderect(obj.get_collision_rect()) 35 | 36 | def update(self, dt: float) -> None: 37 | if self.y > settings.VIRTUAL_HEIGHT: 38 | self.active = False 39 | 40 | self.y += self.vy * dt 41 | 42 | def render(self, surface: pygame.Surface) -> None: 43 | surface.blit( 44 | settings.TEXTURES["spritesheet"], 45 | (self.x, self.y), 46 | settings.FRAMES["powerups"][self.frame], 47 | ) 48 | 49 | def take(self, play_state: TypeVar("PlayState")) -> None: 50 | raise NotImplementedError 51 | -------------------------------------------------------------------------------- /03-breakout/src/powerups/TwoMoreBall.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Breakout 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the specialization of PowerUp to add two more ball to the game. 9 | """ 10 | 11 | import random 12 | from typing import TypeVar 13 | 14 | from gale.factory import Factory 15 | 16 | import settings 17 | from src.Ball import Ball 18 | from src.powerups.PowerUp import PowerUp 19 | 20 | 21 | class TwoMoreBall(PowerUp): 22 | """ 23 | Power-up to add two more ball to the game. 24 | """ 25 | 26 | def __init__(self, x: int, y: int) -> None: 27 | super().__init__(x, y, 8) 28 | self.ball_factory = Factory(Ball) 29 | 30 | def take(self, play_state: TypeVar("PlayState")) -> None: 31 | paddle = play_state.paddle 32 | 33 | for _ in range(2): 34 | b = self.ball_factory.create(paddle.x + paddle.width // 2 - 4, paddle.y - 8) 35 | settings.SOUNDS["paddle_hit"].stop() 36 | settings.SOUNDS["paddle_hit"].play() 37 | 38 | b.vx = random.randint(-80, 80) 39 | b.vy = random.randint(-170, -100) 40 | play_state.balls.append(b) 41 | 42 | self.active = False 43 | -------------------------------------------------------------------------------- /03-breakout/src/powerups/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Breakout 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This module contains all of the power ups. 9 | """ 10 | 11 | from src.powerups.PowerUp import PowerUp 12 | from src.powerups.TwoMoreBall import TwoMoreBall 13 | 14 | (PowerUp, TwoMoreBall) 15 | -------------------------------------------------------------------------------- /03-breakout/src/states/GameOverState.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Breakout 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the class to define the Game Over state. 9 | """ 10 | 11 | import pygame 12 | 13 | from gale.input_handler import InputData 14 | from gale.state import BaseState 15 | from gale.text import render_text 16 | 17 | import settings 18 | 19 | 20 | class GameOverState(BaseState): 21 | def enter(self, score: int) -> None: 22 | self.score = score 23 | 24 | def on_input(self, input_id: str, input_data: InputData) -> None: 25 | if input_id == "enter" and input_data.pressed: 26 | self.state_machine.change("enter_high_score", score=self.score) 27 | 28 | def render(self, surface: pygame.Surface) -> None: 29 | render_text( 30 | surface, 31 | "Game Over", 32 | settings.FONTS["large"], 33 | settings.VIRTUAL_WIDTH // 2, 34 | settings.VIRTUAL_HEIGHT // 2 - 30, 35 | (255, 255, 255), 36 | center=True, 37 | ) 38 | render_text( 39 | surface, 40 | f"Final Score: {self.score}", 41 | settings.FONTS["medium"], 42 | settings.VIRTUAL_WIDTH // 2, 43 | settings.VIRTUAL_HEIGHT // 2, 44 | (255, 255, 255), 45 | center=True, 46 | ) 47 | render_text( 48 | surface, 49 | "Press Enter!", 50 | settings.FONTS["medium"], 51 | settings.VIRTUAL_WIDTH // 2, 52 | settings.VIRTUAL_HEIGHT // 2 + 20, 53 | (255, 255, 255), 54 | center=True, 55 | ) 56 | -------------------------------------------------------------------------------- /03-breakout/src/states/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Breakout 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This module contains all of the states of the Breakout game. 9 | """ 10 | 11 | from src.states.StartState import StartState 12 | from src.states.HighScoreState import HighScoreState 13 | from src.states.EnterHighScoreState import EnterHighScoreState 14 | from src.states.GameOverState import GameOverState 15 | from src.states.PaddleSelectState import PaddleSelectState 16 | from src.states.ServeState import ServeState 17 | from src.states.PlayState import PlayState 18 | from src.states.VictoryState import VictoryState 19 | from src.states.PauseState import PauseState 20 | 21 | ( 22 | StartState, 23 | HighScoreState, 24 | EnterHighScoreState, 25 | GameOverState, 26 | PaddleSelectState, 27 | ServeState, 28 | PlayState, 29 | VictoryState, 30 | PauseState, 31 | ) 32 | -------------------------------------------------------------------------------- /03-breakout/src/utilities/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/03-breakout/src/utilities/__init__.py -------------------------------------------------------------------------------- /03-breakout/src/utilities/highscores.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Breakout 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains util functions to load and store highscores. 9 | """ 10 | 11 | import os 12 | from typing import List, Any 13 | 14 | import settings 15 | 16 | # Path of home user directory 17 | USER_HOME = os.path.expanduser("~") 18 | 19 | BREAKOUT_DIR = os.path.join(USER_HOME, ".breakout") 20 | 21 | HIGHSCORES_PATH = os.path.join(BREAKOUT_DIR, "highscores.dat") 22 | 23 | 24 | def read_highscores() -> List[List[Any]]: 25 | if not os.path.exists(BREAKOUT_DIR): 26 | os.mkdir(BREAKOUT_DIR) 27 | 28 | with open(HIGHSCORES_PATH, "a"): 29 | pass 30 | 31 | highscores = [] 32 | 33 | with open(HIGHSCORES_PATH, "r") as f: 34 | for line in f: 35 | line = line[:-1] 36 | line = line.split(":") 37 | line[-1] = int(line[-1]) 38 | highscores.append(line) 39 | 40 | return highscores 41 | 42 | 43 | def write_highscores(highscores: List[List[Any]]) -> None: 44 | with open(HIGHSCORES_PATH, "w") as f: 45 | for line in highscores: 46 | line[-1] = str(line[-1]) 47 | line = ":".join(line) 48 | f.write(f"{line}\n") 49 | -------------------------------------------------------------------------------- /04-match3/assets/fonts/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/04-match3/assets/fonts/font.ttf -------------------------------------------------------------------------------- /04-match3/assets/graphics/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/04-match3/assets/graphics/background.png -------------------------------------------------------------------------------- /04-match3/assets/graphics/match3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/04-match3/assets/graphics/match3.png -------------------------------------------------------------------------------- /04-match3/assets/sounds/clock.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/04-match3/assets/sounds/clock.wav -------------------------------------------------------------------------------- /04-match3/assets/sounds/error.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/04-match3/assets/sounds/error.wav -------------------------------------------------------------------------------- /04-match3/assets/sounds/game-over.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/04-match3/assets/sounds/game-over.wav -------------------------------------------------------------------------------- /04-match3/assets/sounds/match.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/04-match3/assets/sounds/match.wav -------------------------------------------------------------------------------- /04-match3/assets/sounds/music.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/04-match3/assets/sounds/music.mp3 -------------------------------------------------------------------------------- /04-match3/assets/sounds/music2.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/04-match3/assets/sounds/music2.mp3 -------------------------------------------------------------------------------- /04-match3/assets/sounds/music3.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/04-match3/assets/sounds/music3.mp3 -------------------------------------------------------------------------------- /04-match3/assets/sounds/next-level.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/04-match3/assets/sounds/next-level.wav -------------------------------------------------------------------------------- /04-match3/assets/sounds/select.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/04-match3/assets/sounds/select.wav -------------------------------------------------------------------------------- /04-match3/main.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Match-3 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the main program to run the game. 9 | """ 10 | 11 | import settings 12 | from src.Match3 import Match3 13 | 14 | if __name__ == "__main__": 15 | match3 = Match3( 16 | "Match 3", 17 | settings.WINDOW_WIDTH, 18 | settings.WINDOW_HEIGHT, 19 | settings.VIRTUAL_WIDTH, 20 | settings.VIRTUAL_HEIGHT, 21 | ) 22 | match3.exec() 23 | -------------------------------------------------------------------------------- /04-match3/requirements.txt: -------------------------------------------------------------------------------- 1 | https://github.com/R3mmurd/Gale/archive/main.zip -------------------------------------------------------------------------------- /04-match3/src/Match3.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Match-3 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the class Match3 as a specialization of gale.Game 9 | """ 10 | 11 | import pygame 12 | 13 | from gale.game import Game 14 | from gale.input_handler import InputData 15 | from gale.state import StateMachine 16 | 17 | import settings 18 | from src import states 19 | 20 | 21 | class Match3(Game): 22 | def init(self) -> None: 23 | pygame.mixer.music.play(loops=-1) 24 | self.state_machine = StateMachine( 25 | { 26 | "start": lambda sm: states.StartState(sm, self), 27 | "begin": states.BeginGameState, 28 | "play": states.PlayState, 29 | "game-over": states.GameOverState, 30 | } 31 | ) 32 | self.state_machine.change("start") 33 | self.background_x = 0 34 | 35 | def update(self, dt: float) -> None: 36 | self.background_x -= settings.BACKGROUND_SCROLL_SPEED * dt 37 | 38 | if self.background_x <= settings.BACKGROUND_LOOPING_POINT: 39 | self.background_x = 0 40 | 41 | self.state_machine.update(dt) 42 | 43 | def render(self, surface: pygame.Surface) -> None: 44 | surface.blit(settings.TEXTURES["background"], (self.background_x, 0)) 45 | self.state_machine.render(surface) 46 | 47 | def on_input(self, input_id: str, input_data: InputData) -> None: 48 | if input_id == "quit" and input_data.pressed: 49 | self.quit() 50 | else: 51 | self.state_machine.on_input(input_id, input_data) 52 | -------------------------------------------------------------------------------- /04-match3/src/Tile.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Match-3 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the class Tile. 9 | """ 10 | 11 | import pygame 12 | 13 | import settings 14 | 15 | 16 | class Tile: 17 | def __init__(self, i: int, j: int, color: int, variety: int) -> None: 18 | self.i = i 19 | self.j = j 20 | self.x = self.j * settings.TILE_SIZE 21 | self.y = self.i * settings.TILE_SIZE 22 | self.color = color 23 | self.variety = variety 24 | self.alpha_surface = pygame.Surface( 25 | (settings.TILE_SIZE, settings.TILE_SIZE), pygame.SRCALPHA 26 | ) 27 | 28 | def render(self, surface: pygame.Surface, offset_x: int, offset_y: int) -> None: 29 | self.alpha_surface.blit( 30 | settings.TEXTURES["tiles"], 31 | (0, 0), 32 | settings.FRAMES["tiles"][self.color][self.variety], 33 | ) 34 | pygame.draw.rect( 35 | self.alpha_surface, 36 | (34, 32, 52, 200), 37 | pygame.Rect(0, 0, settings.TILE_SIZE, settings.TILE_SIZE), 38 | border_radius=7, 39 | ) 40 | surface.blit(self.alpha_surface, (self.x + 2 + offset_x, self.y + 2 + offset_y)) 41 | surface.blit( 42 | settings.TEXTURES["tiles"], 43 | (self.x + offset_x, self.y + offset_y), 44 | settings.FRAMES["tiles"][self.color][self.variety], 45 | ) 46 | -------------------------------------------------------------------------------- /04-match3/src/frames_utility.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Match-3 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains a function to fetch and store the tile frames. 9 | """ 10 | 11 | from typing import List 12 | 13 | import pygame 14 | 15 | import settings 16 | 17 | 18 | def generate_tile_frames(spritesheet: pygame.Surface) -> List[List[pygame.Rect]]: 19 | frames = [] 20 | 21 | x, y = 0, 0 22 | 23 | rows_counter = 0 24 | 25 | # There are 9 rows 26 | for _ in range(9): 27 | # There are two sets of 6 tiles of the same color and different 28 | # variety. 29 | for _ in range(2): 30 | frames.append([]) 31 | 32 | for _ in range(6): 33 | frames[rows_counter].append( 34 | pygame.Rect(x, y, settings.TILE_SIZE, settings.TILE_SIZE) 35 | ) 36 | x += settings.TILE_SIZE 37 | 38 | rows_counter += 1 39 | 40 | y += settings.TILE_SIZE 41 | x = 0 42 | 43 | return frames 44 | -------------------------------------------------------------------------------- /04-match3/src/states/__init__.py: -------------------------------------------------------------------------------- 1 | from src.states.StartState import StartState 2 | from src.states.BeginGameState import BeginGameState 3 | from src.states.PlayState import PlayState 4 | from src.states.GameOverState import GameOverState 5 | 6 | (StartState, BeginGameState, PlayState, GameOverState) 7 | -------------------------------------------------------------------------------- /05-super_martian/assets/fonts/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/05-super_martian/assets/fonts/font.ttf -------------------------------------------------------------------------------- /05-super_martian/assets/graphics/creatures.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/05-super_martian/assets/graphics/creatures.png -------------------------------------------------------------------------------- /05-super_martian/assets/graphics/martian.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/05-super_martian/assets/graphics/martian.png -------------------------------------------------------------------------------- /05-super_martian/assets/graphics/tileset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/05-super_martian/assets/graphics/tileset.png -------------------------------------------------------------------------------- /05-super_martian/assets/sounds/count.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/05-super_martian/assets/sounds/count.wav -------------------------------------------------------------------------------- /05-super_martian/assets/sounds/jump.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/05-super_martian/assets/sounds/jump.wav -------------------------------------------------------------------------------- /05-super_martian/assets/sounds/music_grassland.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/05-super_martian/assets/sounds/music_grassland.ogg -------------------------------------------------------------------------------- /05-super_martian/assets/sounds/music_intro.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/05-super_martian/assets/sounds/music_intro.ogg -------------------------------------------------------------------------------- /05-super_martian/assets/sounds/pickup_coin.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/05-super_martian/assets/sounds/pickup_coin.wav -------------------------------------------------------------------------------- /05-super_martian/assets/sounds/timer.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/05-super_martian/assets/sounds/timer.wav -------------------------------------------------------------------------------- /05-super_martian/assets/tilemaps/creatures.tsx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /05-super_martian/assets/tilemaps/tiles.tsx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /05-super_martian/main.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Super Martian (Platformer) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the main program to run the game. 9 | """ 10 | 11 | import settings 12 | from src.SuperMartian import SuperMartian 13 | 14 | if __name__ == "__main__": 15 | super_martian = SuperMartian( 16 | "Super Martian", 17 | settings.WINDOW_WIDTH, 18 | settings.WINDOW_HEIGHT, 19 | settings.VIRTUAL_WIDTH, 20 | settings.VIRTUAL_HEIGHT, 21 | ) 22 | super_martian.exec() 23 | -------------------------------------------------------------------------------- /05-super_martian/requirements.txt: -------------------------------------------------------------------------------- 1 | https://github.com/R3mmurd/Gale/archive/main.zip 2 | -------------------------------------------------------------------------------- /05-super_martian/src/Camera.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Super Martian (Platformer) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the class Camera. 9 | """ 10 | 11 | import pygame 12 | 13 | from src.GameEntity import GameEntity 14 | 15 | 16 | class Camera: 17 | def __init__(self, x: int, y: int, width: int, height: int) -> None: 18 | self.x = x 19 | self.y = y 20 | self.width = width 21 | self.height = height 22 | self.collision_boundaries = None 23 | self.following = None 24 | 25 | def attach_to(self, entity: GameEntity) -> None: 26 | self.following = entity 27 | 28 | def set_collision_boundaries(self, rect: pygame.Rect) -> None: 29 | self.collision_boundaries = rect 30 | 31 | def update(self) -> None: 32 | if self.following is not None: 33 | self.x = self.following.x + self.following.width // 2 - self.width // 2 34 | self.y = self.following.y + self.following.height // 2 - self.height // 2 35 | 36 | if self.collision_boundaries is not None: 37 | self.x = max( 38 | self.collision_boundaries.x, 39 | min( 40 | self.x, 41 | self.collision_boundaries.x 42 | + self.collision_boundaries.width 43 | - self.width, 44 | ), 45 | ) 46 | self.y = max( 47 | self.collision_boundaries.y, 48 | min( 49 | self.y, 50 | self.collision_boundaries.y 51 | + self.collision_boundaries.height 52 | - self.height, 53 | ), 54 | ) 55 | 56 | def get_rect(self) -> pygame.Rect: 57 | return pygame.Rect(self.x, self.y, self.width, self.height) 58 | -------------------------------------------------------------------------------- /05-super_martian/src/Clock.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2024 3 | Study Case: Super Martian (Platformer) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the class Clock. 9 | """ 10 | 11 | 12 | class Clock: 13 | def __init__(self, time: int) -> None: 14 | self.time = time 15 | 16 | def count_up(self) -> None: 17 | self.time += 1 18 | 19 | def count_down(self) -> None: 20 | self.time = max(0, self.time - 1) 21 | 22 | def __str__(self) -> str: 23 | return str(self.time) 24 | -------------------------------------------------------------------------------- /05-super_martian/src/Creature.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Super Martian (Platformer) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the class Creature. 9 | """ 10 | 11 | from typing import TypeVar 12 | 13 | from src.GameEntity import GameEntity 14 | 15 | 16 | class Creature(GameEntity): 17 | def __init__( 18 | self, 19 | x: float, 20 | y, 21 | width: float, 22 | height: float, 23 | game_level: TypeVar("GameLevel"), 24 | **definition 25 | ) -> None: 26 | super().__init__( 27 | x, 28 | y, 29 | width, 30 | height, 31 | definition["texture_id"], 32 | game_level, 33 | states={ 34 | state_name: lambda sm: state_class(self, sm) 35 | for state_name, state_class in definition["states"].items() 36 | }, 37 | animation_defs=definition["animation_defs"], 38 | ) 39 | self.walk_speed = definition["walk_speed"] 40 | self.state_machine.change(definition["first_state"], self.flipped) 41 | -------------------------------------------------------------------------------- /05-super_martian/src/GameItem.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Super Martian (Platformer) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the class GameItem. 9 | """ 10 | 11 | from typing import Callable, TypeVar, Any, Optional 12 | 13 | from src.GameObject import GameObject 14 | 15 | 16 | class GameItem(GameObject): 17 | def __init__( 18 | self, 19 | collidable: bool, 20 | consumable: bool, 21 | on_collide: Optional[Callable[[TypeVar("GameItem"), Any], Any]] = None, 22 | on_consume: Optional[Callable[[TypeVar("GameItem"), Any], Any]] = None, 23 | *args, 24 | **kwargs 25 | ) -> None: 26 | super().__init__(*args, **kwargs) 27 | self.collidable = collidable 28 | self.consumable = consumable 29 | self._on_collide = on_collide 30 | self._on_consume = on_consume 31 | self.active = True 32 | 33 | def respawn(self, x: Optional[float] = None, y: Optional[float] = None) -> None: 34 | if x is not None: 35 | self.x = x 36 | if y is not None: 37 | self.y = y 38 | self.active = True 39 | 40 | def on_collide(self, another: Any) -> Any: 41 | if not self.collidable or self._on_collide is None: 42 | return None 43 | return self._on_collide(self, another) 44 | 45 | def on_consume(self, consumer: Any) -> Any: 46 | if not self.consumable or self._on_consume is None: 47 | return None 48 | self.active = False 49 | return self._on_consume(self, consumer) 50 | -------------------------------------------------------------------------------- /05-super_martian/src/GameObject.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Super Martian (Platformer) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the base class GameObject. 9 | """ 10 | 11 | from typing import Dict 12 | 13 | from src import mixins 14 | 15 | 16 | class GameObject(mixins.DrawableMixin, mixins.CollidableMixin): 17 | # Object sides 18 | TOP = "top" 19 | RIGHT = "right" 20 | BOTTOM = "bottom" 21 | LEFT = "left" 22 | 23 | DEFAULT_SOLIDNESS = {TOP: False, RIGHT: False, BOTTOM: False, LEFT: False} 24 | 25 | def __init__( 26 | self, 27 | x: float, 28 | y: float, 29 | width: float, 30 | height: float, 31 | texture_id: str, 32 | frame_index: int, 33 | solidness: Dict[str, bool], 34 | ) -> None: 35 | self.x = x 36 | self.y = y 37 | self.width = width 38 | self.height = height 39 | self.texture_id = texture_id 40 | self.frame_index = frame_index 41 | self.solidness = solidness 42 | self.flipped = False 43 | 44 | def collides_on(self, another: mixins.CollidableMixin, side: str) -> bool: 45 | return self.is_solid_on(side) and self.collides(another) 46 | 47 | def is_solid_on(self, side: str) -> bool: 48 | return self.solidness[side] 49 | -------------------------------------------------------------------------------- /05-super_martian/src/Player.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Super Martian (Platformer) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the class Player. 9 | """ 10 | 11 | from typing import TypeVar 12 | 13 | from gale.input_handler import InputData 14 | 15 | from src.GameEntity import GameEntity 16 | from src.states.entities import player_states 17 | 18 | 19 | class Player(GameEntity): 20 | def __init__(self, x: int, y: int, game_level: TypeVar("GameLevel")) -> None: 21 | super().__init__( 22 | x, 23 | y, 24 | 16, 25 | 20, 26 | "martian", 27 | game_level, 28 | states={ 29 | "idle": lambda sm: player_states.IdleState(self, sm), 30 | "walk": lambda sm: player_states.WalkState(self, sm), 31 | "jump": lambda sm: player_states.JumpState(self, sm), 32 | "fall": lambda sm: player_states.FallState(self, sm), 33 | "dead": lambda sm: player_states.DeadState(self, sm), 34 | }, 35 | animation_defs={ 36 | "idle": {"frames": [0]}, 37 | "walk": {"frames": [9, 10], "interval": 0.15}, 38 | "jump": {"frames": [2]}, 39 | }, 40 | ) 41 | self.score = 0 42 | self.coins_counter = {54: 0, 55: 0, 61: 0, 62: 0} 43 | 44 | def on_input(self, input_id: str, input_data: InputData) -> None: 45 | self.state_machine.on_input(input_id, input_data) 46 | -------------------------------------------------------------------------------- /05-super_martian/src/SuperMartian.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Super Martian (Platformer) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the base class SuperMartian as a specialization of gale.Game 9 | """ 10 | 11 | import pygame 12 | 13 | from gale.game import Game 14 | from gale.input_handler import InputData 15 | from gale.state import StateMachine 16 | 17 | from src.states import game_states 18 | 19 | 20 | class SuperMartian(Game): 21 | def init(self) -> None: 22 | self.state_machine = StateMachine( 23 | { 24 | "start": game_states.StartState, 25 | "play": game_states.PlayState, 26 | "game_over": game_states.GameOverState, 27 | "pause": game_states.PauseState, 28 | } 29 | ) 30 | self.state_machine.change("start") 31 | 32 | def update(self, dt: float) -> None: 33 | self.state_machine.update(dt) 34 | 35 | def render(self, surface: pygame.Surface) -> None: 36 | self.state_machine.render(surface) 37 | 38 | def on_input(self, input_id: str, input_data: InputData) -> None: 39 | if input_id == "quit" and input_data.pressed: 40 | self.quit() 41 | else: 42 | self.state_machine.on_input(input_id, input_data) 43 | -------------------------------------------------------------------------------- /05-super_martian/src/Tile.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Super Martian (Platformer) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the base class Tile. 9 | """ 10 | 11 | from typing import Dict 12 | 13 | from src.GameObject import GameObject 14 | 15 | 16 | class Tile(GameObject): 17 | def __init__( 18 | self, 19 | i: int, 20 | j: int, 21 | width: int, 22 | height: int, 23 | frame_index: int, 24 | soliness: Dict[str, bool], 25 | ) -> None: 26 | self.i = i 27 | self.j = j 28 | super().__init__( 29 | self.j * width, 30 | self.i * height, 31 | width, 32 | height, 33 | "tiles", 34 | frame_index, 35 | soliness, 36 | ) 37 | -------------------------------------------------------------------------------- /05-super_martian/src/definitions/creatures.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Super Martian (Platformer) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the definition for creatures. 9 | """ 10 | 11 | from typing import Dict, Any 12 | 13 | from src.states.entities import creatures_states 14 | 15 | CREATURES: Dict[int, Dict[str, Any]] = { 16 | 48: { 17 | "texture_id": "creatures", 18 | "walk_speed": 10, 19 | "animation_defs": {"walk": {"frames": [48, 49], "interval": 0.25}}, 20 | "states": {"walk": creatures_states.SnailWalkState}, 21 | "first_state": "walk", 22 | }, 23 | 52: { 24 | "texture_id": "creatures", 25 | "walk_speed": 15, 26 | "animation_defs": {"walk": {"frames": [52, 53], "interval": 0.18}}, 27 | "states": {"walk": creatures_states.SnailWalkState}, 28 | "first_state": "walk", 29 | }, 30 | } 31 | -------------------------------------------------------------------------------- /05-super_martian/src/definitions/tiles.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Super Martian (Platformer) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the definition for tiles. 9 | """ 10 | 11 | from typing import Dict, Any 12 | 13 | TILES: Dict[int, Dict[str, Any]] = { 14 | # Ground 15 | 0: {"solidness": dict(top=True, right=False, bottom=False, left=False)}, 16 | 1: {"solidness": dict(top=True, right=False, bottom=False, left=False)}, 17 | 2: {"solidness": dict(top=True, right=False, bottom=False, left=False)}, 18 | 3: {"solidness": dict(top=True, right=False, bottom=False, left=False)}, 19 | 4: {"solidness": dict(top=True, right=False, bottom=False, left=False)}, 20 | 5: {"solidness": dict(top=True, right=False, bottom=False, left=False)}, 21 | 10: {"solidness": dict(top=True, right=False, bottom=False, left=False)}, 22 | # Blocks 23 | 6: {"solidness": dict(top=True, right=True, bottom=True, left=True)}, 24 | 17: {"solidness": dict(top=True, right=True, bottom=True, left=True)}, 25 | 41: {"solidness": dict(top=True, right=True, bottom=True, left=True)}, 26 | } 27 | -------------------------------------------------------------------------------- /05-super_martian/src/loaders/__init__.py: -------------------------------------------------------------------------------- 1 | from src.loaders.TmxLevelLoader import TmxLevelLoader 2 | 3 | (TmxLevelLoader,) 4 | -------------------------------------------------------------------------------- /05-super_martian/src/mixins/AnimatedMixin.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Super Martian (Platformer) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the AnimatedMixin. 9 | """ 10 | 11 | from typing import Dict, Any 12 | 13 | from gale.animation import Animation 14 | 15 | 16 | class AnimatedMixin: 17 | def generate_animations(self, animation_defs: Dict[str, Dict[str, Any]]) -> None: 18 | for animation_id, values in animation_defs.items(): 19 | animation = Animation( 20 | values["frames"], 21 | values.get("interval", 0), # Given interval or zero 22 | loops=values.get("loops"), # Given loops or None 23 | ) 24 | self.animations[animation_id] = animation 25 | 26 | def change_animation(self, animation_id: str) -> None: 27 | new_animation = self.animations[animation_id] 28 | if new_animation != self.current_animation: 29 | self.current_animation = new_animation 30 | self.current_animation.reset() 31 | self.frame_index = self.current_animation.get_current_frame() 32 | 33 | def update(self, dt: float) -> None: 34 | self.current_animation.update(dt) 35 | self.frame_index = self.current_animation.get_current_frame() 36 | -------------------------------------------------------------------------------- /05-super_martian/src/mixins/CollidableMixin.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Super Martian (Platformer) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the CollidableMixin. 9 | """ 10 | 11 | from typing import Any 12 | 13 | import pygame 14 | 15 | 16 | class CollidableMixin: 17 | def get_collision_rect(self) -> pygame.Rect: 18 | return pygame.Rect(self.x, self.y, self.width, self.height) 19 | 20 | def collides(self, another: Any) -> bool: 21 | return self.get_collision_rect().colliderect(another.get_collision_rect()) 22 | -------------------------------------------------------------------------------- /05-super_martian/src/mixins/DrawableMixin.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Super Martian (Platformer) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the DrawableMixin. 9 | """ 10 | 11 | import pygame 12 | 13 | import settings 14 | 15 | 16 | class DrawableMixin: 17 | def render(self, surface: pygame.Surface) -> None: 18 | texture = settings.TEXTURES[self.texture_id] 19 | frame = settings.FRAMES[self.texture_id][self.frame_index] 20 | image = pygame.Surface((frame.width, frame.height), pygame.SRCALPHA) 21 | image.fill((0, 0, 0, 0)) 22 | image.blit(texture, (0, 0), frame) 23 | 24 | if self.flipped: 25 | image = pygame.transform.flip(image, True, False) 26 | 27 | surface.blit(image, (self.x, self.y)) 28 | -------------------------------------------------------------------------------- /05-super_martian/src/mixins/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Super Martian (Platformer) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This module contains all of the mixins for the game. 9 | """ 10 | 11 | from src.mixins.AnimatedMixin import AnimatedMixin 12 | from src.mixins.CollidableMixin import CollidableMixin 13 | from src.mixins.DrawableMixin import DrawableMixin 14 | 15 | (AnimatedMixin, CollidableMixin, DrawableMixin) 16 | -------------------------------------------------------------------------------- /05-super_martian/src/states/entities/BaseEntityState.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Super Martian (Platformer) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the base class BaseEntityState. 9 | """ 10 | 11 | from typing import TypeVar 12 | 13 | from gale.state import BaseState, StateMachine 14 | 15 | 16 | class BaseEntityState(BaseState): 17 | def __init__( 18 | self, entity: TypeVar("GameEntity"), state_machine: StateMachine 19 | ) -> None: 20 | super().__init__(state_machine) 21 | self.entity = entity 22 | -------------------------------------------------------------------------------- /05-super_martian/src/states/entities/creatures_states/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Super Martian (Platformer) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This module contains all of the creature states. 9 | """ 10 | 11 | from src.states.entities.creatures_states.SnailWalkState import SnailWalkState 12 | 13 | (SnailWalkState,) 14 | -------------------------------------------------------------------------------- /05-super_martian/src/states/entities/player_states/DeadState.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Super Martian (Platformer) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the class DeadState for player. 9 | """ 10 | 11 | from src.states.entities.BaseEntityState import BaseEntityState 12 | 13 | 14 | class DeadState(BaseEntityState): 15 | def enter(self) -> None: 16 | self.entity.is_dead = True 17 | -------------------------------------------------------------------------------- /05-super_martian/src/states/entities/player_states/FallState.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Super Martian (Platformer) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the class FallState for player. 9 | """ 10 | 11 | from gale.input_handler import InputData 12 | 13 | import settings 14 | from src.states.entities.BaseEntityState import BaseEntityState 15 | 16 | 17 | class FallState(BaseEntityState): 18 | def enter(self) -> None: 19 | self.entity.change_animation("jump") 20 | 21 | def update(self, dt: float) -> None: 22 | self.entity.vy += settings.GRAVITY * dt 23 | 24 | # If there is a collision on the right, correct x. Else, correct x if there is collision on the left. 25 | self.entity.handle_tilemap_collision_on_right() or self.entity.handle_tilemap_collision_on_left() 26 | 27 | if self.entity.handle_tilemap_collision_on_bottom(): 28 | self.entity.vy = 0 29 | if self.entity.vx > 0: 30 | self.entity.change_state("walk", "right") 31 | elif self.entity.vx < 0: 32 | self.entity.change_state("walk", "left") 33 | else: 34 | self.entity.change_state("idle") 35 | 36 | def on_input(self, input_id: str, input_data: InputData) -> None: 37 | if input_id == "move_left": 38 | if input_data.pressed: 39 | self.entity.vx = -settings.PLAYER_SPEED 40 | self.entity.flipped = True 41 | elif input_data.released and self.entity.vx <= 0: 42 | self.entity.vx = 0 43 | 44 | elif input_id == "move_right": 45 | if input_data.pressed: 46 | self.entity.vx = settings.PLAYER_SPEED 47 | self.entity.flipped = False 48 | elif input_data.released and self.entity.vx >= 0: 49 | self.entity.vx = 0 50 | -------------------------------------------------------------------------------- /05-super_martian/src/states/entities/player_states/IdleState.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Super Martian (Platformer) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the class IdleState for player. 9 | """ 10 | 11 | from gale.input_handler import InputData 12 | 13 | from src.states.entities.BaseEntityState import BaseEntityState 14 | 15 | 16 | class IdleState(BaseEntityState): 17 | def enter(self) -> None: 18 | self.entity.vx = 0 19 | self.entity.vy = 0 20 | self.entity.change_animation("idle") 21 | 22 | def update(self, dt: float) -> None: 23 | if self.entity.handle_tilemap_collision_on_bottom(): 24 | self.entity.vy = 0 25 | 26 | def on_input(self, input_id: str, input_data: InputData) -> None: 27 | if input_id == "move_left" and input_data.pressed: 28 | self.entity.flipped = True 29 | self.entity.change_state("walk", "left") 30 | elif input_id == "move_right" and input_data.pressed: 31 | self.entity.flipped = True 32 | self.entity.change_state("walk", "right") 33 | elif input_id == "jump" and input_data.pressed: 34 | self.entity.change_state("jump") 35 | -------------------------------------------------------------------------------- /05-super_martian/src/states/entities/player_states/JumpState.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Super Martian (Platformer) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the class JumpState for player. 9 | """ 10 | 11 | from gale.input_handler import InputData 12 | 13 | import settings 14 | from src.states.entities.BaseEntityState import BaseEntityState 15 | 16 | 17 | class JumpState(BaseEntityState): 18 | def enter(self) -> None: 19 | self.entity.change_animation("jump") 20 | self.entity.vy = -settings.GRAVITY / 3 21 | settings.SOUNDS["jump"].play() 22 | 23 | def update(self, dt: float) -> None: 24 | self.entity.vy += settings.GRAVITY * dt 25 | 26 | # If there is a collision on the right, correct x. Else, correct x if there is collision on the left. 27 | self.entity.handle_tilemap_collision_on_right() or self.entity.handle_tilemap_collision_on_left() 28 | 29 | if self.entity.handle_tilemap_collision_on_top(): 30 | self.entity.vy = 0 31 | 32 | if self.entity.vy >= 0: 33 | self.entity.change_state("fall") 34 | 35 | def on_input(self, input_id: str, input_data: InputData) -> None: 36 | if input_id == "move_left": 37 | if input_data.pressed: 38 | self.entity.vx = -settings.PLAYER_SPEED 39 | self.entity.flipped = True 40 | elif input_data.released and self.entity.vx <= 0: 41 | self.entity.vx = 0 42 | 43 | elif input_id == "move_right": 44 | if input_data.pressed: 45 | self.entity.vx = settings.PLAYER_SPEED 46 | self.entity.flipped = False 47 | elif input_data.released and self.entity.vx >= 0: 48 | self.entity.vx = 0 49 | -------------------------------------------------------------------------------- /05-super_martian/src/states/entities/player_states/WalkState.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Super Martian (Platformer) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the class WalkState for player. 9 | """ 10 | 11 | from gale.input_handler import InputData 12 | 13 | import settings 14 | from src.states.entities.BaseEntityState import BaseEntityState 15 | 16 | 17 | class WalkState(BaseEntityState): 18 | def enter(self, direction: str) -> None: 19 | self.entity.flipped = direction == "left" 20 | self.entity.vx = settings.PLAYER_SPEED 21 | if self.entity.flipped: 22 | self.entity.vx *= -1 23 | self.entity.change_animation("walk") 24 | 25 | def update(self, dt: float) -> None: 26 | if not self.entity.check_floor(): 27 | self.entity.change_state("fall") 28 | 29 | # If there is a collision on the right, correct x. Else, correct x if there is collision on the left. 30 | self.entity.handle_tilemap_collision_on_right() or self.entity.handle_tilemap_collision_on_left() 31 | 32 | def on_input(self, input_id: str, input_data: InputData) -> None: 33 | if input_id == "move_left": 34 | if input_data.pressed: 35 | self.entity.vx = -settings.PLAYER_SPEED 36 | self.entity.flipped = True 37 | elif input_data.released and self.entity.vx <= 0: 38 | self.entity.change_state("idle") 39 | 40 | elif input_id == "move_right": 41 | if input_data.pressed: 42 | self.entity.vx = settings.PLAYER_SPEED 43 | self.entity.flipped = False 44 | elif input_data.released and self.entity.vx >= 0: 45 | self.entity.change_state("idle") 46 | elif input_id == "jump" and input_data.pressed: 47 | self.entity.change_state("jump") 48 | -------------------------------------------------------------------------------- /05-super_martian/src/states/entities/player_states/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Super Martian (Platformer) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This module contains all of the player states. 9 | """ 10 | 11 | from src.states.entities.player_states.DeadState import DeadState 12 | from src.states.entities.player_states.FallState import FallState 13 | from src.states.entities.player_states.IdleState import IdleState 14 | from src.states.entities.player_states.JumpState import JumpState 15 | from src.states.entities.player_states.WalkState import WalkState 16 | 17 | (DeadState, FallState, IdleState, JumpState, WalkState) 18 | -------------------------------------------------------------------------------- /05-super_martian/src/states/game_states/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Study Case: Super Martian (Platformer) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This module contains all of the game states. 9 | """ 10 | 11 | from src.states.game_states.GameOverState import GameOverState 12 | from src.states.game_states.PauseState import PauseState 13 | from src.states.game_states.PlayState import PlayState 14 | from src.states.game_states.StartState import StartState 15 | 16 | (GameOverState, PauseState, PlayState, StartState) 17 | -------------------------------------------------------------------------------- /06-princess/assets/fonts/princess.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/06-princess/assets/fonts/princess.otf -------------------------------------------------------------------------------- /06-princess/assets/graphics/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/06-princess/assets/graphics/background.png -------------------------------------------------------------------------------- /06-princess/assets/graphics/character_pot_lift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/06-princess/assets/graphics/character_pot_lift.png -------------------------------------------------------------------------------- /06-princess/assets/graphics/character_pot_walk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/06-princess/assets/graphics/character_pot_walk.png -------------------------------------------------------------------------------- /06-princess/assets/graphics/character_swing_sword.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/06-princess/assets/graphics/character_swing_sword.png -------------------------------------------------------------------------------- /06-princess/assets/graphics/character_walk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/06-princess/assets/graphics/character_walk.png -------------------------------------------------------------------------------- /06-princess/assets/graphics/entities.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/06-princess/assets/graphics/entities.png -------------------------------------------------------------------------------- /06-princess/assets/graphics/hearts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/06-princess/assets/graphics/hearts.png -------------------------------------------------------------------------------- /06-princess/assets/graphics/switches.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/06-princess/assets/graphics/switches.png -------------------------------------------------------------------------------- /06-princess/assets/graphics/tilesheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/06-princess/assets/graphics/tilesheet.png -------------------------------------------------------------------------------- /06-princess/assets/sounds/door.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/06-princess/assets/sounds/door.wav -------------------------------------------------------------------------------- /06-princess/assets/sounds/dungeon_music.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/06-princess/assets/sounds/dungeon_music.mp3 -------------------------------------------------------------------------------- /06-princess/assets/sounds/game_over_music.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/06-princess/assets/sounds/game_over_music.mp3 -------------------------------------------------------------------------------- /06-princess/assets/sounds/heart_taken.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/06-princess/assets/sounds/heart_taken.wav -------------------------------------------------------------------------------- /06-princess/assets/sounds/hit_enemy.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/06-princess/assets/sounds/hit_enemy.wav -------------------------------------------------------------------------------- /06-princess/assets/sounds/hit_player.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/06-princess/assets/sounds/hit_player.wav -------------------------------------------------------------------------------- /06-princess/assets/sounds/pot_wall.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/06-princess/assets/sounds/pot_wall.wav -------------------------------------------------------------------------------- /06-princess/assets/sounds/start_music.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/06-princess/assets/sounds/start_music.mp3 -------------------------------------------------------------------------------- /06-princess/assets/sounds/sword.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/06-princess/assets/sounds/sword.wav -------------------------------------------------------------------------------- /06-princess/main.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: The Legend of the Princess (ARPG) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the configure the framework. 9 | ]] 10 | require 'settings' 11 | 12 | function love.load() 13 | math.randomseed(os.time()) 14 | love.window.setTitle("The Legend of the Princess") 15 | love.graphics.setDefaultFilter('nearest', 'nearest') 16 | 17 | push:setupScreen(VIRTUAL_WIDTH, VIRTUAL_HEIGHT, WINDOW_WIDTH, WINDOW_HEIGHT, { 18 | fullscreen = false, 19 | resizable = false 20 | }) 21 | 22 | stateMachine = StateMachine { 23 | ['start'] = function() return StartState() end, 24 | ['play'] = function() return PlayState() end, 25 | ['game-over'] = function() return GameOverState() end 26 | } 27 | 28 | stateMachine:change('start') 29 | 30 | love.keyboard.keysPressed = {} 31 | end 32 | 33 | function love.keypressed(key) 34 | love.keyboard.keysPressed[key] = true 35 | end 36 | 37 | function love.keyboard.wasPressed(key) 38 | return love.keyboard.keysPressed[key] 39 | end 40 | 41 | function love.update(dt) 42 | Timer.update(dt) 43 | stateMachine:update(dt) 44 | love.keyboard.keysPressed = {} 45 | end 46 | 47 | function love.draw() 48 | push:start() 49 | stateMachine:render() 50 | push:finish() 51 | end 52 | -------------------------------------------------------------------------------- /06-princess/src/Animation.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: The Legend of the Princess (ARPG) 4 | 5 | Author: Colton Ogden 6 | cogden@cs50.harvard.edu 7 | 8 | This file contains the class Animation. 9 | ]] 10 | Animation = Class{} 11 | 12 | function Animation:init(def) 13 | self.frames = def.frames 14 | self.interval = def.interval 15 | self.texture = def.texture 16 | self.looping = def.looping or true 17 | 18 | self.timer = 0 19 | self.currentFrame = 1 20 | 21 | -- used to see if we've seen a whole loop of the animation 22 | self.timesPlayed = 0 23 | end 24 | 25 | function Animation:refresh() 26 | self.timer = 0 27 | self.currentFrame = 1 28 | self.timesPlayed = 0 29 | end 30 | 31 | function Animation:update(dt) 32 | -- if not a looping animation and we've played at least once, exit 33 | if not self.looping and self.timesPlayed > 0 then 34 | return 35 | end 36 | 37 | -- no need to update if animation is only one frame 38 | if #self.frames > 1 then 39 | self.timer = self.timer + dt 40 | 41 | if self.timer > self.interval then 42 | self.timer = self.timer % self.interval 43 | 44 | self.currentFrame = math.max(1, (self.currentFrame + 1) % (#self.frames + 1)) 45 | 46 | -- if we've looped back to the beginning, record 47 | if self.currentFrame == 1 then 48 | self.timesPlayed = self.timesPlayed + 1 49 | end 50 | end 51 | end 52 | end 53 | 54 | function Animation:getCurrentFrame() 55 | return self.frames[self.currentFrame] 56 | end 57 | -------------------------------------------------------------------------------- /06-princess/src/GameObject.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: The Legend of the Princess (ARPG) 4 | 5 | Author: Colton Ogden 6 | cogden@cs50.harvard.edu 7 | 8 | Modified by Alejandro Mujica (alejandro.j.mujic4@gmail.com) for teaching purpose. 9 | 10 | This file contains the class GameObject. 11 | ]] 12 | GameObject = Class{} 13 | 14 | function GameObject:init(def, x, y) 15 | -- string identifying this object type 16 | self.type = def.type 17 | 18 | self.texture = def.texture 19 | self.frame = def.frame or 1 20 | 21 | -- whether it acts as an obstacle or not 22 | self.solid = def.solid 23 | 24 | self.defaultState = def.defaultState 25 | self.state = self.defaultState 26 | self.states = def.states 27 | 28 | -- dimensions 29 | self.x = x 30 | self.y = y 31 | self.width = def.width 32 | self.height = def.height 33 | 34 | -- default empty collision callback 35 | self.onCollide = function() end 36 | 37 | -- variable for consumable objects 38 | self.consumable = def.consumable 39 | 40 | -- onConsume function an empty function if it is not specified 41 | self.onConsume = def.onConsume or function() end 42 | 43 | -- an object could be taken or not 44 | self.takeable = def.takeable 45 | self.taken = false 46 | end 47 | 48 | function GameObject:update(dt) 49 | 50 | end 51 | 52 | function GameObject:render(adjacentOffsetX, adjacentOffsetY) 53 | love.graphics.draw(TEXTURES[self.texture], FRAMES[self.texture][self.states[self.state].frame or self.frame], 54 | self.x + adjacentOffsetX, self.y + adjacentOffsetY) 55 | end 56 | -------------------------------------------------------------------------------- /06-princess/src/Hitbox.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: The Legend of the Princess (ARPG) 4 | 5 | Author: Colton Ogden 6 | cogden@cs50.harvard.edu 7 | 8 | This file contains the class Hitbox. 9 | ]] 10 | Hitbox = Class{} 11 | 12 | function Hitbox:init(x, y, width, height) 13 | self.x = x 14 | self.y = y 15 | self.width = width 16 | self.height = height 17 | end -------------------------------------------------------------------------------- /06-princess/src/Player.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: The Legend of the Princess (ARPG) 4 | 5 | Author: Colton Ogden 6 | cogden@cs50.harvard.edu 7 | 8 | Modified by Alejandro Mujica (alejandro.j.mujic4@gmail.com) for teaching purpose. 9 | 10 | This file contains the class Player. 11 | ]] 12 | Player = Class{__includes = Entity} 13 | 14 | function Player:init(def) 15 | Entity.init(self, def) 16 | end 17 | 18 | function Player:update(dt) 19 | Entity.update(self, dt) 20 | end 21 | 22 | function Player:collides(target) 23 | local selfY, selfHeight = self.y + self.height / 2, self.height - self.height / 2 24 | 25 | return not (self.x + self.width < target.x or self.x > target.x + target.width or 26 | selfY + selfHeight < target.y or selfY > target.y + target.height) 27 | end 28 | 29 | function Player:render() 30 | Entity.render(self) 31 | -- love.graphics.setColor(love.math.colorFromBytes(255, 0, 255, 255)) 32 | -- love.graphics.rectangle('line', self.x, self.y, self.width, self.height) 33 | -- love.graphics.setColor(love.math.colorFromBytes(255, 255, 255, 255)) 34 | end 35 | -------------------------------------------------------------------------------- /06-princess/src/StateMachine.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: The Legend of the Princess (ARPG) 4 | 5 | Author: Colton Ogden 6 | cogden@cs50.harvard.edu 7 | 8 | This file contains the class StateMachine. 9 | ]] 10 | StateMachine = Class{} 11 | 12 | function StateMachine:init(states) 13 | self.empty = { 14 | render = function() end, 15 | update = function() end, 16 | processAI = function() end, 17 | enter = function() end, 18 | exit = function() end 19 | } 20 | 21 | self.states = states or {} -- [name] -> [function that returns states] 22 | self.current = self.empty 23 | end 24 | 25 | function StateMachine:change(stateName, enterParams) 26 | assert(self.states[stateName]) -- state must exist. 27 | self.current:exit() 28 | self.current = self.states[stateName]() 29 | self.current:enter(enterParams) 30 | end 31 | 32 | function StateMachine:update(dt) 33 | self.current:update(dt) 34 | end 35 | 36 | function StateMachine:render() 37 | self.current:render() 38 | end 39 | 40 | function StateMachine:processAI(params, dt) 41 | self.current:processAI(params, dt) 42 | end 43 | -------------------------------------------------------------------------------- /06-princess/src/definitions/game_objects.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: The Legend of the Princess (ARPG) 4 | 5 | Author: Colton Ogden 6 | cogden@cs50.harvard.edu 7 | 8 | Modified by Alejandro Mujica (alejandro.j.mujic4@gmail.com) for teaching purpose. 9 | 10 | This file contains the definition for game objects. 11 | ]] 12 | GAME_OBJECT_DEFS = { 13 | ['switch'] = { 14 | type = 'switch', 15 | texture = 'switches', 16 | frame = 2, 17 | width = 16, 18 | height = 16, 19 | solid = false, 20 | defaultState = 'unpressed', 21 | states = { 22 | ['unpressed'] = { 23 | frame = 2 24 | }, 25 | ['pressed'] = { 26 | frame = 1 27 | } 28 | } 29 | }, 30 | ['pot'] = { 31 | type = 'pot', 32 | texture = 'tiles', 33 | frame = 16, 34 | width = 16, 35 | height = 16, 36 | solid = true, 37 | consumable = false, 38 | defaultState = 'default', 39 | takeable = true, 40 | states = { 41 | ['default'] = { 42 | frame = 16 43 | } 44 | } 45 | }, 46 | -- definition of heart as a consumable object type 47 | ['heart'] = { 48 | type = 'heart', 49 | texture = 'hearts', 50 | frame = 5, 51 | width = 16, 52 | height = 16, 53 | solid = false, 54 | consumable = true, 55 | defaultState = 'default', 56 | states = { 57 | ['default'] = { 58 | frame = 5 59 | } 60 | }, 61 | onConsume = function(player) 62 | player:heal(2) 63 | SOUNDS['heart-taken']:play() 64 | end 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /06-princess/src/states/BaseState.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: The Legend of the Princess (ARPG) 4 | 5 | Author: Colton Ogden 6 | cogden@cs50.harvard.edu 7 | 8 | Modified by Alejandro Mujica (alejandro.j.mujic4@gmail.com) for teaching purpose. 9 | 10 | This file contains the base class BaseState. 11 | ]] 12 | BaseState = Class{} 13 | 14 | function BaseState:init() end 15 | function BaseState:enter() end 16 | function BaseState:exit() end 17 | function BaseState:update(dt) end 18 | function BaseState:render() end 19 | function BaseState:processAI(params, dt) end 20 | -------------------------------------------------------------------------------- /06-princess/src/states/entity/EntityIdleState.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: The Legend of the Princess (ARPG) 4 | 5 | Author: Colton Ogden 6 | cogden@cs50.harvard.edu 7 | 8 | Modified by Alejandro Mujica (alejandro.j.mujic4@gmail.com) for teaching purpose. 9 | 10 | This file contains the class EntityIdleState. 11 | ]] 12 | EntityIdleState = Class{__includes = BaseState} 13 | 14 | function EntityIdleState:init(entity, dungeon) 15 | self.entity = entity 16 | self.dungeon = dungeon 17 | 18 | self.entity:changeAnimation('idle-' .. self.entity.direction) 19 | 20 | -- used for AI waiting 21 | self.waitDuration = 0 22 | self.waitTimer = 0 23 | end 24 | 25 | function EntityIdleState:processAI(params, dt) 26 | if self.waitDuration == 0 then 27 | self.waitDuration = math.random(5) 28 | else 29 | self.waitTimer = self.waitTimer + dt 30 | 31 | if self.waitTimer > self.waitDuration then 32 | self.entity:changeState('walk') 33 | end 34 | end 35 | end 36 | 37 | function EntityIdleState:render() 38 | local anim = self.entity.currentAnimation 39 | love.graphics.draw(TEXTURES[anim.texture], FRAMES[anim.texture][anim:getCurrentFrame()], 40 | math.floor(self.entity.x - self.entity.offsetX), math.floor(self.entity.y - self.entity.offsetY)) 41 | 42 | -- love.graphics.setColor(love.math.colorFromBytes(255, 0, 255, 255)) 43 | -- love.graphics.rectangle('line', self.entity.x, self.entity.y, self.entity.width, self.entity.height) 44 | -- love.graphics.setColor(love.math.colorFromBytes(255, 255, 255, 255)) 45 | end -------------------------------------------------------------------------------- /06-princess/src/states/entity/player/PlayerPotIdleState.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: The Legend of the Princess (ARPG) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the class PlayerPotIdleState. 9 | ]] 10 | PlayerPotIdleState = Class{__includes = BaseState} 11 | 12 | function PlayerPotIdleState:init(player, dungeon) 13 | self.player = player 14 | self.dungeon = dungeon 15 | self.player:changeAnimation('pot-idle-' .. self.player.direction) 16 | end 17 | 18 | function PlayerPotIdleState:enter(params) 19 | self.pot = params.pot 20 | end 21 | 22 | function PlayerPotIdleState:update(dt) 23 | if love.keyboard.isDown('left') or love.keyboard.isDown('right') or 24 | love.keyboard.isDown('up') or love.keyboard.isDown('down') then 25 | self.player:changeState('pot-walk', { 26 | pot = self.pot 27 | }) 28 | end 29 | 30 | if love.keyboard.wasPressed('enter') or love.keyboard.wasPressed('return') then 31 | table.insert(self.dungeon.currentRoom.projectiles, Projectile(self.pot, self.player.direction)) 32 | self.player:changeState('idle') 33 | end 34 | end 35 | 36 | function PlayerPotIdleState:render() 37 | local anim = self.player.currentAnimation 38 | love.graphics.draw(TEXTURES[anim.texture], FRAMES[anim.texture][anim:getCurrentFrame()], 39 | math.floor(self.player.x - self.player.offsetX), math.floor(self.player.y - self.player.offsetY)) 40 | self.pot:render(0, 0) 41 | end -------------------------------------------------------------------------------- /06-princess/src/states/entity/player/PlayerPotLiftState.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: The Legend of the Princess (ARPG) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the class PlayerPotLiftState. 9 | ]] 10 | PlayerPotLiftState = Class{__includes = BaseState} 11 | 12 | function PlayerPotLiftState:init(player, dungeon) 13 | self.player = player 14 | self.dungeon = dungeon 15 | self.player:changeAnimation('pot-lift-' .. self.player.direction) 16 | end 17 | 18 | function PlayerPotLiftState:enter(params) 19 | self.pot = params.pot 20 | self.pot.taken = true 21 | self.player.currentAnimation:refresh() 22 | Timer.tween(0.3, { 23 | [self.pot] = { 24 | x = self.player.x, 25 | y = self.player.y - self.pot.height / 2 26 | } 27 | }) 28 | end 29 | 30 | function PlayerPotLiftState:update(dt) 31 | if self.player.currentAnimation.timesPlayed > 0 then 32 | self.player.currentAnimation.timesPlayed = 0 33 | self.player:changeState('pot-idle', { 34 | pot = self.pot 35 | }) 36 | end 37 | end 38 | 39 | function PlayerPotLiftState:render() 40 | local anim = self.player.currentAnimation 41 | love.graphics.draw(TEXTURES[anim.texture], FRAMES[anim.texture][anim:getCurrentFrame()], 42 | math.floor(self.player.x - self.player.offsetX), math.floor(self.player.y - self.player.offsetY)) 43 | self.pot:render(0, 0) 44 | end -------------------------------------------------------------------------------- /06-princess/src/states/game/GameOverState.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: The Legend of the Princess (ARPG) 4 | 5 | Author: Colton Ogden 6 | cogden@cs50.harvard.edu 7 | 8 | Modified by Alejandro Mujica (alejandro.j.mujic4@gmail.com) for teaching purpose. 9 | 10 | This file contains the class GameOverState for the game. 11 | ]] 12 | GameOverState = Class{__includes = BaseState} 13 | 14 | function GameOverState:init() 15 | SOUNDS['game-over-music']:play() 16 | end 17 | 18 | function GameOverState:exit() 19 | SOUNDS['game-over-music']:stop() 20 | end 21 | 22 | function GameOverState:update(dt) 23 | if love.keyboard.wasPressed('enter') or love.keyboard.wasPressed('return') then 24 | stateMachine:change('start') 25 | end 26 | 27 | if love.keyboard.wasPressed('escape') then 28 | love.event.quit() 29 | end 30 | end 31 | 32 | function GameOverState:render() 33 | love.graphics.setFont(FONTS['princess']) 34 | love.graphics.setColor(love.math.colorFromBytes(175, 53, 42, 255)) 35 | love.graphics.printf('GAME OVER', 0, VIRTUAL_HEIGHT / 2 - 48, VIRTUAL_WIDTH, 'center') 36 | 37 | love.graphics.setFont(FONTS['princess-small']) 38 | love.graphics.printf('Press Enter', 0, VIRTUAL_HEIGHT / 2 + 16, VIRTUAL_WIDTH, 'center') 39 | love.graphics.setColor(love.math.colorFromBytes(255, 255, 255, 255)) 40 | end 41 | -------------------------------------------------------------------------------- /06-princess/src/states/game/StartState.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: The Legend of the Princess (ARPG) 4 | 5 | Author: Colton Ogden 6 | cogden@cs50.harvard.edu 7 | 8 | Modified by Alejandro Mujica (alejandro.j.mujic4@gmail.com) for teaching purpose. 9 | 10 | This file contains the class StartState for the game. 11 | ]] 12 | StartState = Class{__includes = BaseState} 13 | 14 | function StartState:init() 15 | SOUNDS['start-music']:setLooping(true) 16 | SOUNDS['start-music']:play() 17 | end 18 | 19 | function StartState:exit() 20 | SOUNDS['start-music']:stop() 21 | end 22 | 23 | function StartState:update(dt) 24 | if love.keyboard.wasPressed('escape') then 25 | love.event.quit() 26 | end 27 | 28 | if love.keyboard.wasPressed('enter') or love.keyboard.wasPressed('return') then 29 | stateMachine:change('play') 30 | end 31 | end 32 | 33 | function StartState:render() 34 | love.graphics.draw(TEXTURES['background'], 0, 0, 0, 35 | VIRTUAL_WIDTH / TEXTURES['background']:getWidth(), 36 | VIRTUAL_HEIGHT / TEXTURES['background']:getHeight()) 37 | 38 | love.graphics.setFont(FONTS['princess']) 39 | 40 | love.graphics.setColor(love.math.colorFromBytes(34, 34, 34, 255)) 41 | love.graphics.printf('The Legend of the Princess', 2, VIRTUAL_HEIGHT / 2 - 30, VIRTUAL_WIDTH, 'center') 42 | 43 | love.graphics.setColor(love.math.colorFromBytes(175, 53, 42, 255)) 44 | love.graphics.printf('The Legend of the Princess', 0, VIRTUAL_HEIGHT / 2 - 32, VIRTUAL_WIDTH, 'center') 45 | 46 | love.graphics.setColor(love.math.colorFromBytes(255, 255, 255, 255)) 47 | love.graphics.setFont(FONTS['princess-small']) 48 | love.graphics.printf('Press Enter', 0, VIRTUAL_HEIGHT / 2 + 64, VIRTUAL_WIDTH, 'center') 49 | end -------------------------------------------------------------------------------- /06-princess/src/utilities/quads.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: The Legend of the Princess (ARPG) 4 | 5 | Author: Colton Ogden 6 | cogden@cs50.harvard.edu 7 | 8 | Modified by Alejandro Mujica (alejandro.j.mujic4@gmail.com) for teaching purpose. 9 | 10 | This file contains a util function to generate quads for the textures. 11 | ]] 12 | function generateQuads(spriteSheet, tileWidth, tileHeight) 13 | local sheetWidth = spriteSheet:getWidth() / tileWidth 14 | local sheetHeight = spriteSheet:getHeight() / tileHeight 15 | 16 | local quadCounter = 1 17 | local quads = {} 18 | 19 | for y = 0, sheetHeight - 1 do 20 | for x = 0, sheetWidth - 1 do 21 | quads[quadCounter] = 22 | love.graphics.newQuad( 23 | x * tileWidth, y * tileHeight, tileWidth, tileHeight, spriteSheet:getDimensions() 24 | ) 25 | quadCounter = quadCounter + 1 26 | end 27 | end 28 | return quads 29 | end 30 | -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/fonts/finalf.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/fonts/finalf.ttf -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/fonts/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/fonts/font.ttf -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/graphics/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/graphics/background.png -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/graphics/characters/healer_f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/graphics/characters/healer_f.png -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/graphics/characters/healer_m.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/graphics/characters/healer_m.png -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/graphics/characters/mage_f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/graphics/characters/mage_f.png -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/graphics/characters/mage_m.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/graphics/characters/mage_m.png -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/graphics/characters/ranger_f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/graphics/characters/ranger_f.png -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/graphics/characters/ranger_m.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/graphics/characters/ranger_m.png -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/graphics/characters/townfolk_f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/graphics/characters/townfolk_f.png -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/graphics/characters/townfolk_m.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/graphics/characters/townfolk_m.png -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/graphics/characters/warrior_f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/graphics/characters/warrior_f.png -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/graphics/characters/warrior_m.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/graphics/characters/warrior_m.png -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/graphics/cursor_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/graphics/cursor_right.png -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/graphics/cursor_up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/graphics/cursor_up.png -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/graphics/enemies/man_eater_flower.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/graphics/enemies/man_eater_flower.png -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/graphics/enemies/pumpking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/graphics/enemies/pumpking.png -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/graphics/enemies/slime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/graphics/enemies/slime.png -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/graphics/enemies/small_worm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/graphics/enemies/small_worm.png -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/graphics/enemies/snake.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/graphics/enemies/snake.png -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/graphics/sheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/graphics/sheet.png -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/sounds/arrows.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/sounds/arrows.wav -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/sounds/battle.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/sounds/battle.mp3 -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/sounds/blip.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/sounds/blip.wav -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/sounds/exp.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/sounds/exp.wav -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/sounds/flame.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/sounds/flame.ogg -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/sounds/game_over.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/sounds/game_over.mp3 -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/sounds/hit.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/sounds/hit.wav -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/sounds/intro.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/sounds/intro.mp3 -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/sounds/levelup.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/sounds/levelup.wav -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/sounds/powerup.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/sounds/powerup.wav -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/sounds/run.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/sounds/run.wav -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/sounds/the_end.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/sounds/the_end.mp3 -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/sounds/town.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/sounds/town.mp3 -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/sounds/victory.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/sounds/victory.wav -------------------------------------------------------------------------------- /07-ultimate_fantasy/assets/sounds/world.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/07-ultimate_fantasy/assets/sounds/world.mp3 -------------------------------------------------------------------------------- /07-ultimate_fantasy/main.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: Ultimate Fantasy (RPG) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the configure the framework. 9 | ]] 10 | require 'settings' 11 | 12 | function love.load() 13 | math.randomseed(os.time()) 14 | love.window.setTitle('Ultimate Fantasy') 15 | love.graphics.setDefaultFilter('nearest', 'nearest') 16 | 17 | push:setupScreen(VIRTUAL_WIDTH, VIRTUAL_HEIGHT, WINDOW_WIDTH, WINDOW_HEIGHT, { 18 | fullscreen = false, 19 | vsync = true, 20 | resizable = true 21 | }) 22 | 23 | SOUNDS['intro']:setLooping(true) 24 | SOUNDS['town']:setLooping(true) 25 | SOUNDS['town']:setVolume(0.5) 26 | SOUNDS['world']:setLooping(true) 27 | SOUNDS['world']:setVolume(0.5) 28 | SOUNDS['battle']:setLooping(true) 29 | 30 | stateStack = StateStack() 31 | stateStack:push(StartState()) 32 | 33 | love.keyboard.keysPressed = {} 34 | end 35 | 36 | function love.resize(w, h) 37 | push:resize(w, h) 38 | end 39 | 40 | function love.keypressed(key) 41 | if key == 'escape' then 42 | love.event.quit() 43 | end 44 | love.keyboard.keysPressed[key] = true 45 | end 46 | 47 | function love.keyboard.wasPressed(key) 48 | return love.keyboard.keysPressed[key] 49 | end 50 | 51 | function love.update(dt) 52 | Timer.update(dt) 53 | stateStack:update(dt) 54 | love.keyboard.keysPressed = {} 55 | end 56 | 57 | function love.draw() 58 | push:start() 59 | stateStack:render() 60 | push:finish() 61 | end -------------------------------------------------------------------------------- /07-ultimate_fantasy/src/Animation.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: Ultimate Fantasy (RPG) 4 | 5 | Author: Colton Ogden 6 | cogden@cs50.harvard.edu 7 | 8 | This class contains the class Animation. 9 | ]] 10 | Animation = Class{} 11 | 12 | function Animation:init(def) 13 | self.frames = def.frames 14 | self.interval = def.interval 15 | self.texture = def.texture 16 | self.looping = def.looping or true 17 | 18 | self.timer = 0 19 | self.currentFrame = 1 20 | 21 | -- used to see if we've seen a whole loop of the animation 22 | self.timesPlayed = 0 23 | end 24 | 25 | function Animation:refresh() 26 | self.timer = 0 27 | self.currentFrame = 1 28 | self.timesPlayed = 0 29 | end 30 | 31 | function Animation:update(dt) 32 | -- if not a looping animation and we've played at least once, exit 33 | if not self.looping and self.timesPlayed > 0 then 34 | return 35 | end 36 | 37 | -- no need to update if animation is only one frame 38 | if #self.frames > 1 then 39 | self.timer = self.timer + dt 40 | 41 | if self.timer > self.interval then 42 | self.timer = self.timer % self.interval 43 | 44 | self.currentFrame = math.max(1, (self.currentFrame + 1) % (#self.frames + 1)) 45 | 46 | -- if we've looped back to the beginning, record 47 | if self.currentFrame == 1 then 48 | self.timesPlayed = self.timesPlayed + 1 49 | end 50 | end 51 | end 52 | end 53 | 54 | function Animation:getCurrentFrame() 55 | return self.frames[self.currentFrame] 56 | end 57 | -------------------------------------------------------------------------------- /07-ultimate_fantasy/src/StateMachine.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: Ultimate Fantasy (RPG) 4 | 5 | Author: Colton Ogden 6 | cogden@cs50.harvard.edu 7 | 8 | This class contains the class StateMachine. 9 | ]] 10 | StateMachine = Class{} 11 | 12 | function StateMachine:init(states) 13 | self.empty = { 14 | render = function() end, 15 | update = function() end, 16 | processAI = function() end, 17 | enter = function() end, 18 | exit = function() end 19 | } 20 | self.states = states or {} 21 | self.current = self.empty 22 | end 23 | 24 | function StateMachine:change(stateName, enterParams) 25 | assert(self.states[stateName]) 26 | self.current:exit() 27 | self.current = self.states[stateName]() 28 | self.current:enter(enterParams) 29 | end 30 | 31 | function StateMachine:update(dt) 32 | self.current:update(dt) 33 | end 34 | 35 | function StateMachine:render() 36 | self.current:render() 37 | end 38 | 39 | function StateMachine:processAI(params, dt) 40 | self.current:processAI(params, dt) 41 | end 42 | -------------------------------------------------------------------------------- /07-ultimate_fantasy/src/definitions/tile_id.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: Ultimate Fantasy (RPG) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This class contains the definition for tile ids. 9 | ]] 10 | TILE_WIDTH = VIRTUAL_WIDTH / TILE_SIZE 11 | TILE_HEIGHT = VIRTUAL_HEIGHT / TILE_SIZE 12 | 13 | TILE_IDS = { 14 | ['grass'] = {46, 47}, 15 | ['flowers'] = {16, 24, 32, 40, 48, 56, 64, 72}, 16 | ['empty'] = 101, 17 | ['tall-grass'] = 42, 18 | ['half-tall-grass'] = 50, 19 | ['top-left-fence'] = 73, 20 | ['top-fence'] = 74, 21 | ['top-right-fence'] = 75, 22 | ['left-fence'] = 81, 23 | ['right-fence'] = 83, 24 | ['bottom-left-fence'] = 89, 25 | ['bottom-fence'] = 90, 26 | ['bottom-right-fence'] = 91, 27 | ['border-left-fence'] = 65, 28 | ['border-right-fence'] = 66, 29 | ['border-top-left-fence'] = 88, 30 | ['border-bottom-left-fence'] = 87, 31 | ['border-top-right-fence'] = 96, 32 | ['border-bottom-right-fence'] = 95 33 | } 34 | -------------------------------------------------------------------------------- /07-ultimate_fantasy/src/entity/BattleEntity.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: Ultimate Fantasy (RPG) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This class contains the class BattleEntity. 9 | ]] 10 | BattleEntity = Class{__includes = Entity} 11 | 12 | function BattleEntity:init(def) 13 | Entity.init(self, def) 14 | self.class = def.class 15 | 16 | self.actions = def.actions 17 | 18 | self.level = def.level or 1 19 | 20 | self.dead = def.dead 21 | 22 | self.baseHP = def.baseHP 23 | self.baseAttack = def.baseAttack 24 | self.baseDefense = def.baseDefense 25 | self.baseMagic = def.baseMagic 26 | 27 | self.HP = self.baseHP 28 | self.attack = self.baseAttack 29 | self.defense = self.baseDefense 30 | self.magic = self.baseMagic 31 | 32 | self.currentHP = self.HP 33 | end 34 | 35 | function BattleEntity:damage(amount) 36 | self.currentHP = self.currentHP - amount 37 | if self.currentHP <= 0 then 38 | self.dead = true 39 | end 40 | end 41 | 42 | function BattleEntity:heal(amount) 43 | if not self.dead then 44 | self.currentHP = math.min(self.HP, self.currentHP + amount) 45 | end 46 | end 47 | 48 | function BattleEntity:computeAttack() 49 | return math.floor(math.random()/2*self.attack + math.random()/4*self.magic) 50 | end 51 | 52 | function BattleEntity:computeDefense() 53 | return math.floor(math.random()/4*self.defense + math.random()/8*self.magic) 54 | end 55 | 56 | function BattleEntity:computeHealing() 57 | return math.floor(math.random()*2*self.magic) 58 | end 59 | -------------------------------------------------------------------------------- /07-ultimate_fantasy/src/entity/Enemy.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: Ultimate Fantasy (RPG) 4 | 5 | Author: Colton Ogden 6 | cogden@cs50.harvard.edu 7 | 8 | Modified by Alejandro Mujica (alejandro.j.mujic4@gmail.com) 9 | 10 | This class contains the class Entity. 11 | ]] 12 | Enemy = Class{__includes = BattleEntity} 13 | 14 | function Enemy:init(def) 15 | BattleEntity.init(self, def) 16 | statusGenerated = def.statusGenerated 17 | end 18 | -------------------------------------------------------------------------------- /07-ultimate_fantasy/src/entity/Entity.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: Ultimate Fantasy (RPG) 4 | 5 | Author: Colton Ogden 6 | cogden@cs50.harvard.edu 7 | 8 | Modified by Alejandro Mujica (alejandro.j.mujic4@gmail.com) 9 | 10 | This class contains the class Entity. 11 | ]] 12 | Entity = Class{} 13 | 14 | function Entity:init(def) 15 | self.name = def.name 16 | 17 | self.direction = def.direction or 'down' 18 | self.texture = def.texture 19 | 20 | self.animations = self:createAnimations(def.animations) 21 | 22 | self.mapX = def.mapX 23 | self.mapY = def.mapY 24 | 25 | self.width = def.width 26 | self.height = def.height 27 | 28 | self.x = (self.mapX - 1) * TILE_SIZE 29 | 30 | -- halfway raised on the tile just to simulate height/perspective 31 | self.y = (self.mapY - 1) * TILE_SIZE - self.height / 2 32 | end 33 | 34 | function Entity:changeState(name) 35 | self.stateMachine:change(name) 36 | end 37 | 38 | function Entity:changeAnimation(name) 39 | self.currentAnimation = self.animations[name] 40 | end 41 | 42 | function Entity:createAnimations(animationDefs) 43 | local animations = {} 44 | 45 | for k, animationDef in pairs(animationDefs) do 46 | animations[k] = Animation { 47 | texture = self.texture, 48 | frames = animationDef.frames, 49 | interval = animationDef.interval 50 | } 51 | end 52 | 53 | return animations 54 | end 55 | 56 | --[[ 57 | Called when we interact with this entity, as by pressing enter. 58 | ]] 59 | function Entity:onInteract() 60 | 61 | end 62 | 63 | function Entity:processAI(params, dt) 64 | self.stateMachine:processAI(params, dt) 65 | end 66 | 67 | function Entity:update(dt) 68 | self.currentAnimation:update(dt) 69 | self.stateMachine:update(dt) 70 | end 71 | 72 | function Entity:render() 73 | self.stateMachine:render() 74 | end 75 | -------------------------------------------------------------------------------- /07-ultimate_fantasy/src/entity/NPC.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: Ultimate Fantasy (RPG) 4 | 5 | Author: Alejandro Mujics 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This class contains the class NPC. 9 | ]] 10 | NPC = Class{__includes = Entity} 11 | 12 | function NPC:init(def) 13 | Entity.init(self, def) 14 | end 15 | 16 | function NPC:onInteract() 17 | local text = ENTITY_DEFS.npcs.texts[math.random(#ENTITY_DEFS.npcs.texts)] 18 | stateStack:push(DialogueState(self.name .. ': ' .. text)) 19 | end 20 | -------------------------------------------------------------------------------- /07-ultimate_fantasy/src/gui/Menu.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: Ultimate Fantasy (RPG) 4 | 5 | Author: Colton Ogden 6 | cogden@cs50.harvard.edu 7 | 8 | This class contains the class Menu. 9 | ]] 10 | Menu = Class{} 11 | 12 | function Menu:init(def) 13 | self.panel = Panel(def.x, def.y, def.width, def.height) 14 | 15 | self.selection = Selection { 16 | items = def.items, 17 | x = def.x, 18 | y = def.y, 19 | width = def.width, 20 | height = def.height, 21 | showCursor = def.showCursor, 22 | font = def.font 23 | } 24 | end 25 | 26 | function Menu:update(dt) 27 | self.selection:update(dt) 28 | end 29 | 30 | function Menu:render() 31 | self.panel:render() 32 | self.selection:render() 33 | end 34 | -------------------------------------------------------------------------------- /07-ultimate_fantasy/src/gui/Panel.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: Ultimate Fantasy (RPG) 4 | 5 | Author: Colton Ogden 6 | cogden@cs50.harvard.edu 7 | 8 | This class contains the class Panel. 9 | ]] 10 | Panel = Class{} 11 | 12 | function Panel:init(x, y, width, height) 13 | self.x = x 14 | self.y = y 15 | self.width = width 16 | self.height = height 17 | 18 | self.visible = true 19 | end 20 | 21 | function Panel:update(dt) 22 | 23 | end 24 | 25 | function Panel:render() 26 | if self.visible then 27 | love.graphics.setColor(love.math.colorFromBytes(255, 255, 255, 255)) 28 | love.graphics.rectangle('fill', self.x, self.y, self.width, self.height, 3) 29 | love.graphics.setColor(love.math.colorFromBytes(56, 56, 56, 255)) 30 | love.graphics.rectangle('fill', self.x + 2, self.y + 2, self.width - 4, self.height - 4, 3) 31 | love.graphics.setColor(love.math.colorFromBytes(255, 255, 255, 255)) 32 | end 33 | end 34 | 35 | function Panel:toggle() 36 | self.visible = not self.visible 37 | end 38 | -------------------------------------------------------------------------------- /07-ultimate_fantasy/src/gui/ProgressBar.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: Ultimate Fantasy (RPG) 4 | 5 | Author: Colton Ogden 6 | cogden@cs50.harvard.edu 7 | 8 | This class contains the class ProgressBar. 9 | ]] 10 | ProgressBar = Class{} 11 | 12 | function ProgressBar:init(def) 13 | self.x = def.x 14 | self.y = def.y 15 | 16 | self.width = def.width 17 | self.height = def.height 18 | 19 | self.color = def.color 20 | 21 | self.value = def.value 22 | self.max = def.max 23 | end 24 | 25 | function ProgressBar:setMax(max) 26 | self.max = max 27 | end 28 | 29 | function ProgressBar:setValue(value) 30 | self.value = value 31 | end 32 | 33 | function ProgressBar:update() 34 | 35 | end 36 | 37 | function ProgressBar:render() 38 | -- multiplier on width based on progress 39 | local renderWidth = (self.value / self.max) * self.width 40 | 41 | -- draw main bar, with calculated width based on value / max 42 | love.graphics.setColor(love.math.colorFromBytes(self.color.r, self.color.g, self.color.b, 255)) 43 | 44 | if self.value > 0 then 45 | love.graphics.rectangle('fill', self.x, self.y, renderWidth, self.height, 3) 46 | end 47 | 48 | -- draw outline around actual bar 49 | love.graphics.setColor(love.math.colorFromBytes(0, 0, 0, 255)) 50 | love.graphics.rectangle('line', self.x, self.y, self.width, self.height, 3) 51 | love.graphics.setColor(love.math.colorFromBytes(255, 255, 255, 255)) 52 | end 53 | -------------------------------------------------------------------------------- /07-ultimate_fantasy/src/states/BaseState.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: Ultimate Fantasy (RPG) 4 | 5 | Author: Colton Ogden 6 | cogden@cs50.harvard.edu 7 | 8 | This class contains the class BaseState. 9 | ]] 10 | BaseState = Class{} 11 | 12 | function BaseState:init() end 13 | function BaseState:enter() end 14 | function BaseState:exit() end 15 | function BaseState:update(dt) end 16 | function BaseState:render() end -------------------------------------------------------------------------------- /07-ultimate_fantasy/src/states/StateStack.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: Ultimate Fantasy (RPG) 4 | 5 | Author: Colton Ogden 6 | cogden@cs50.harvard.edu 7 | 8 | Modified by Alejandro Mujica (alejandro.j.mujic4@gmail.com) 9 | 10 | This class contains the class StateStack. 11 | ]] 12 | StateStack = Class{} 13 | 14 | function StateStack:init() 15 | self.states = {} 16 | end 17 | 18 | function StateStack:update(dt) 19 | assert(#self.states > 0) 20 | self.states[#self.states]:update(dt) 21 | end 22 | 23 | function StateStack:processAI(params, dt) 24 | assert(#self.states > 0) 25 | self.states[#self.states]:processAI(params, dt) 26 | end 27 | 28 | function StateStack:render() 29 | for i, state in ipairs(self.states) do 30 | state:render() 31 | end 32 | end 33 | 34 | function StateStack:clear() 35 | self.states = {} 36 | end 37 | 38 | function StateStack:push(state) 39 | table.insert(self.states, state) 40 | state:enter() 41 | end 42 | 43 | function StateStack:pop() 44 | assert(#self.states > 0) 45 | self.states[#self.states]:exit() 46 | table.remove(self.states) 47 | end 48 | -------------------------------------------------------------------------------- /07-ultimate_fantasy/src/states/entity/CharacterIdleState.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: Ultimate Fantasy (RPG) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the class CharacterIdleState. 9 | ]] 10 | CharacterIdleState = Class{__includes = EntityBaseState} 11 | 12 | function CharacterIdleState:init(entity) 13 | self.entity = entity 14 | self.entity:changeAnimation('idle-' .. self.entity.direction) 15 | end 16 | -------------------------------------------------------------------------------- /07-ultimate_fantasy/src/states/entity/CharacterWalkState.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: Ultimate Fantasy (RPG) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the class CharacterWalkState. 9 | ]] 10 | CharacterWalkState = Class{__includes = EntityBaseState} 11 | 12 | function CharacterWalkState:init(entity) 13 | self.entity = entity 14 | self.entity:changeAnimation('walk-' .. self.entity.direction) 15 | end 16 | -------------------------------------------------------------------------------- /07-ultimate_fantasy/src/states/entity/EnemyBattleState.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: Ultimate Fantasy (RPG) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the class EnemyBattleState. 9 | ]] 10 | EnemyBattleState = Class{__includes = EntityBaseState} 11 | 12 | function EnemyBattleState:init(entity) 13 | self.entity = entity 14 | self.entity:changeAnimation('default') 15 | end 16 | -------------------------------------------------------------------------------- /07-ultimate_fantasy/src/states/entity/EntityBaseState.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: Ultimate Fantasy (RPG) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the class EntityBaseState. 9 | ]] 10 | EntityBaseState = Class{} 11 | 12 | function EntityBaseState:init(entity) 13 | self.entity = entity 14 | end 15 | 16 | function EntityBaseState:update(dt) end 17 | function EntityBaseState:enter() end 18 | function EntityBaseState:exit() end 19 | function EntityBaseState:processAI(params, dt) end 20 | 21 | function EntityBaseState:render() 22 | local anim = self.entity.currentAnimation 23 | love.graphics.draw(TEXTURES[anim.texture], FRAMES[anim.texture][anim:getCurrentFrame()], 24 | math.floor(self.entity.x), math.floor(self.entity.y)) 25 | end -------------------------------------------------------------------------------- /07-ultimate_fantasy/src/states/entity/NPCIdleState.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: Ultimate Fantasy (RPG) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the class NPCIdleState. 9 | ]] 10 | NPCIdleState = Class{__includes = EntityBaseState} 11 | 12 | function NPCIdleState:init(entity) 13 | self.entity = entity 14 | self.entity:changeAnimation('idle-' .. self.entity.direction) 15 | end 16 | -------------------------------------------------------------------------------- /07-ultimate_fantasy/src/states/entity/PartyBaseState.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: Ultimate Fantasy (RPG) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the class PartyBaseState. 9 | ]] 10 | PartyBaseState = Class{} 11 | 12 | function PartyBaseState:init(party) 13 | self.party = party 14 | end 15 | 16 | function PartyBaseState:update(dt) end 17 | function PartyBaseState:enter() end 18 | function PartyBaseState:exit() end 19 | function PartyBaseState:processAI(params, dt) end 20 | 21 | function PartyBaseState:render() 22 | for k, c in pairs(self.party.characters) do 23 | if not c.dead then 24 | c:render() 25 | end 26 | end 27 | end -------------------------------------------------------------------------------- /07-ultimate_fantasy/src/states/entity/PartyIdleState.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: Ultimate Fantasy (RPG) 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the class PartyIdleState. 9 | ]] 10 | PartyIdleState = Class{__includes = PartyBaseState} 11 | 12 | function PartyIdleState:init(party) 13 | PartyBaseState.init(self, party) 14 | for k, c in pairs(self.party.characters) do 15 | c:changeAnimation('idle-' .. c.direction) 16 | end 17 | end 18 | 19 | function PartyIdleState:update(dt) 20 | if love.keyboard.isDown('left') then 21 | self.party:changeState('walk', {direction = 'left'}) 22 | elseif love.keyboard.isDown('right') then 23 | self.party:changeState('walk', {direction = 'right'}) 24 | elseif love.keyboard.isDown('up') then 25 | self.party:changeState('walk', {direction = 'up'}) 26 | elseif love.keyboard.isDown('down') then 27 | self.party:changeState('walk', {direction = 'down'}) 28 | end 29 | end -------------------------------------------------------------------------------- /07-ultimate_fantasy/src/states/game/BattleMessageState.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: Ultimate Fantasy (RPG) 4 | 5 | Author: Colton Ogden 6 | cogden@cs50.harvard.edu 7 | 8 | This class contains the class BaseMessageState. 9 | ]] 10 | BattleMessageState = Class{__includes = BaseState} 11 | 12 | function BattleMessageState:init(battleState, msg, onClose, canInput) 13 | self.battleState = battleState 14 | self.textbox = Textbox(0, VIRTUAL_HEIGHT - 64, VIRTUAL_WIDTH, 64, msg, FONTS['medium']) 15 | 16 | -- function to be called once this message is popped 17 | self.onClose = onClose or function() end 18 | 19 | -- whether we can detect input with this or not; true by default 20 | self.canInput = canInput 21 | 22 | -- default input to true if nothing was passed in 23 | if self.canInput == nil then self.canInput = true end 24 | end 25 | 26 | function BattleMessageState:update(dt) 27 | for k, e in pairs(self.battleState.enemies) do 28 | e:update(dt) 29 | end 30 | if self.canInput then 31 | self.textbox:update(dt) 32 | 33 | if self.textbox:isClosed() then 34 | stateStack:pop() 35 | self.onClose() 36 | end 37 | end 38 | end 39 | 40 | function BattleMessageState:render() 41 | self.textbox:render() 42 | end -------------------------------------------------------------------------------- /07-ultimate_fantasy/src/states/game/DialogueState.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: Ultimate Fantasy (RPG) 4 | 5 | Author: Colton Ogden 6 | cogden@cs50.harvard.edu 7 | 8 | This class contains the class DialogueState. 9 | ]] 10 | DialogueState = Class{__includes = BaseState} 11 | 12 | function DialogueState:init(text, callback) 13 | self.textbox = Textbox(6, 6, VIRTUAL_WIDTH - 12, 64, text, FONTS['small']) 14 | self.callback = callback or function() end 15 | end 16 | 17 | function DialogueState:update(dt) 18 | self.textbox:update(dt) 19 | 20 | if self.textbox:isClosed() then 21 | self.callback() 22 | stateStack:pop() 23 | end 24 | end 25 | 26 | function DialogueState:render() 27 | self.textbox:render() 28 | end -------------------------------------------------------------------------------- /07-ultimate_fantasy/src/states/game/FadeInState.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: Ultimate Fantasy (RPG) 4 | 5 | Author: Colton Ogden 6 | cogden@cs50.harvard.edu 7 | 8 | This class contains the class FadeInState. 9 | ]] 10 | FadeInState = Class{__includes = BaseState} 11 | 12 | function FadeInState:init(color, time, onFadeComplete) 13 | self.r = color.r 14 | self.g = color.g 15 | self.b = color.b 16 | self.opacity = 0 17 | self.time = time 18 | 19 | Timer.tween(self.time, { 20 | [self] = {opacity = 255} 21 | }) 22 | :finish(function() 23 | stateStack:pop() 24 | onFadeComplete() 25 | end) 26 | end 27 | 28 | function FadeInState:render() 29 | love.graphics.setColor(love.math.colorFromBytes(self.r, self.g, self.b, self.opacity)) 30 | love.graphics.rectangle('fill', 0, 0, VIRTUAL_WIDTH, VIRTUAL_HEIGHT) 31 | 32 | love.graphics.setColor(love.math.colorFromBytes(255, 255, 255, 255)) 33 | end -------------------------------------------------------------------------------- /07-ultimate_fantasy/src/states/game/FadeOutState.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: Ultimate Fantasy (RPG) 4 | 5 | Author: Colton Ogden 6 | cogden@cs50.harvard.edu 7 | 8 | This class contains the class FadeOutState. 9 | ]] 10 | FadeOutState = Class{__includes = BaseState} 11 | 12 | function FadeOutState:init(color, time, onFadeComplete) 13 | self.opacity = 255 14 | self.r = color.r 15 | self.g = color.g 16 | self.b = color.b 17 | self.time = time 18 | 19 | Timer.tween(self.time, { 20 | [self] = {opacity = 0} 21 | }) 22 | :finish(function() 23 | stateStack:pop() 24 | onFadeComplete() 25 | end) 26 | end 27 | 28 | function FadeOutState:render() 29 | love.graphics.setColor(love.math.colorFromBytes(self.r, self.g, self.b, self.opacity)) 30 | love.graphics.rectangle('fill', 0, 0, VIRTUAL_WIDTH, VIRTUAL_HEIGHT) 31 | 32 | love.graphics.setColor(love.math.colorFromBytes(255, 255, 255, 255)) 33 | end -------------------------------------------------------------------------------- /07-ultimate_fantasy/src/states/game/GameOverState.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: Ultimate Fantasy (RPG) 4 | 5 | Author: Alejandro Mujica 6 | aledrums@gmail.com 7 | 8 | This class contains the class GameOverState. 9 | ]] 10 | GameOverState = Class{__includes = BaseState} 11 | 12 | function GameOverState:update(dt) 13 | if love.keyboard.wasPressed('return') or love.keyboard.wasPressed('enter') then 14 | for k, s in pairs(SOUNDS) do 15 | s:stop() 16 | end 17 | stateStack:clear() 18 | stateStack:push(StartState()) 19 | end 20 | end 21 | 22 | function GameOverState:render() 23 | love.graphics.clear(0, 0, 0, 255) 24 | love.graphics.setColor(255, 255, 255, 255) 25 | 26 | love.graphics.setFont(FONTS['medium']) 27 | love.graphics.printf('Your party was defeated!', 0, 10, VIRTUAL_WIDTH, 'center') 28 | 29 | love.graphics.setFont(FONTS['large']) 30 | love.graphics.setColor(255, 255, 255, 255) 31 | love.graphics.printf('Game Over', 0, VIRTUAL_HEIGHT / 2 - 32, VIRTUAL_WIDTH, 'center') 32 | end -------------------------------------------------------------------------------- /07-ultimate_fantasy/src/states/game/PlayState.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: Ultimate Fantasy (RPG) 4 | 5 | Author: Alejandro Mujica 6 | aledrums@gmail.com 7 | 8 | This class contains the class PlayState. 9 | ]] 10 | PlayState = Class{__includes = BaseState} 11 | 12 | function PlayState:init(def) 13 | world = World(def) 14 | end 15 | 16 | function PlayState:update(dt) 17 | world:update(dt) 18 | end 19 | 20 | function PlayState:render() 21 | world:render() 22 | end 23 | -------------------------------------------------------------------------------- /07-ultimate_fantasy/src/states/game/ShowTextState.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: Ultimate Fantasy (RPG) 4 | 5 | Author: Alejandro Mujica 6 | aledrums@gmail.com 7 | 8 | This class contains the class ShowTextState. 9 | ]] 10 | ShowTextState = Class{__includes = BaseState} 11 | 12 | function ShowTextState:init(color, text, onShowTextComplete) 13 | self.r = color.r 14 | self.g = color.g 15 | self.b = color.b 16 | self.opacity = 0 17 | self.text = string.upper(text) 18 | 19 | Timer.tween(1, { 20 | [self] = {opacity = 255} 21 | }) 22 | :finish(function() 23 | Timer.tween(2, { 24 | [self] = {opacity = 0} 25 | }) 26 | :finish(function() 27 | stateStack:pop() 28 | onShowTextComplete() 29 | end) 30 | end) 31 | end 32 | 33 | function ShowTextState:render() 34 | love.graphics.setColor(love.math.colorFromBytes(self.r, self.g, self.b, self.opacity)) 35 | love.graphics.setFont(FONTS['large']) 36 | love.graphics.printf(self.text, 2, VIRTUAL_HEIGHT / 2 - 30, VIRTUAL_WIDTH, 'center') 37 | love.graphics.setColor(love.math.colorFromBytes(255, 255, 255, 255)) 38 | end -------------------------------------------------------------------------------- /07-ultimate_fantasy/src/states/game/StartState.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: Ultimate Fantasy (RPG) 4 | 5 | Author: Alejandro Mujica 6 | aledrums@gmail.com 7 | 8 | This class contains the class StartState. 9 | ]] 10 | StartState = Class{__includes = BaseState} 11 | 12 | function StartState:init() 13 | SOUNDS['intro']:play() 14 | end 15 | 16 | function StartState:update(dt) 17 | if love.keyboard.wasPressed('enter') or love.keyboard.wasPressed('return') then 18 | stateStack:push(FadeInState({ 19 | r = 0, g = 0, b = 0 20 | }, 1, 21 | function() 22 | stateStack:pop() 23 | stateStack:push(SelectCharacterState({character = 1})) 24 | stateStack:push(FadeOutState({ 25 | r = 0, g = 0, b = 0 26 | }, 0.5, 27 | function() end)) 28 | end)) 29 | end 30 | end 31 | 32 | function StartState:render() 33 | love.graphics.draw(TEXTURES['background'], 0, 0, 0, 34 | VIRTUAL_WIDTH / TEXTURES['background']:getWidth(), 35 | VIRTUAL_HEIGHT / TEXTURES['background']:getHeight()) 36 | 37 | love.graphics.setFont(FONTS['ff']) 38 | love.graphics.setColor(love.math.colorFromBytes(34, 34, 34, 255)) 39 | love.graphics.printf('ULTIMATE FANTASY', 2, VIRTUAL_HEIGHT / 2 - 30, VIRTUAL_WIDTH, 'center') 40 | 41 | love.graphics.setColor(love.math.colorFromBytes(212, 175, 55, 255)) 42 | love.graphics.printf('ULTIMATE FANTASY', 0, VIRTUAL_HEIGHT / 2 - 32, VIRTUAL_WIDTH, 'center') 43 | 44 | love.graphics.setColor(love.math.colorFromBytes(255, 255, 255, 255)) 45 | love.graphics.setFont(FONTS['ff-small']) 46 | love.graphics.printf('PRESS ENTER', 0, VIRTUAL_HEIGHT / 2 + 64, VIRTUAL_WIDTH, 'center') 47 | end -------------------------------------------------------------------------------- /07-ultimate_fantasy/src/states/game/TheEndState.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: Ultimate Fantasy (RPG) 4 | 5 | Author: Alejandro Mujica 6 | aledrums@gmail.com 7 | 8 | This class contains the class TheEndState. 9 | ]] 10 | TheEndState = Class{__includes = BaseState} 11 | 12 | function TheEndState:update(dt) 13 | if love.keyboard.wasPressed('return') or love.keyboard.wasPressed('enter') then 14 | for k, s in pairs(SOUNDS) do 15 | s:stop() 16 | end 17 | stateStack:clear() 18 | stateStack:push(StartState()) 19 | end 20 | end 21 | 22 | function TheEndState:render() 23 | love.graphics.clear(0, 0, 0, 255) 24 | love.graphics.setColor(255, 255, 255, 255) 25 | 26 | love.graphics.setFont(FONTS['medium']) 27 | love.graphics.printf('The man-eater flower has been defeated and the curse has been broken. Thanks!', 28 | 0, 10, VIRTUAL_WIDTH, 'center') 29 | 30 | love.graphics.setFont(FONTS['large']) 31 | love.graphics.setColor(255, 255, 255, 255) 32 | love.graphics.printf('The end', 0, VIRTUAL_HEIGHT / 2 - 32, VIRTUAL_WIDTH, 'center') 33 | end -------------------------------------------------------------------------------- /07-ultimate_fantasy/src/utilities/quads.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: Ultimate Fantasy (RPG) 4 | 5 | Author: Colton Ogden 6 | cogden@cs50.harvard.edu 7 | 8 | Modified by Alejandro Mujica (alejandro.j.mujic4@gmail.com) 9 | 10 | This class contains a util function to generate quads for textures. 11 | ]] 12 | function generateQuads(spriteSheet, tileWidth, tileHeight) 13 | local sheetWidth = spriteSheet:getWidth() / tileWidth 14 | local sheetHeight = spriteSheet:getHeight() / tileHeight 15 | 16 | local quadCounter = 1 17 | local quads = {} 18 | 19 | for y = 0, sheetHeight - 1 do 20 | for x = 0, sheetWidth - 1 do 21 | quads[quadCounter] = 22 | love.graphics.newQuad( 23 | x * tileWidth, y * tileHeight, tileWidth, tileHeight, spriteSheet:getDimensions() 24 | ) 25 | quadCounter = quadCounter + 1 26 | end 27 | end 28 | return quads 29 | end -------------------------------------------------------------------------------- /07-ultimate_fantasy/src/world/Tile.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: Ultimate Fantasy (RPG) 4 | 5 | Author: Colton Ogden 6 | cogden@cs50.harvard.edu 7 | 8 | Modified by Alejandro Mujica (alejandro.j.mujic4@gmail.com) 9 | 10 | This class contains the class Entity. 11 | ]] 12 | Tile = Class{} 13 | 14 | function Tile:init(x, y, id) 15 | self.x = x 16 | self.y = y 17 | self.id = id 18 | end 19 | 20 | function Tile:render(offsetX, offsetY) 21 | love.graphics.draw(TEXTURES['tiles'], FRAMES['tiles'][self.id], 22 | (self.x - 1 + offsetX) * TILE_SIZE, (self.y - 1 + offsetY) * TILE_SIZE) 23 | end 24 | -------------------------------------------------------------------------------- /07-ultimate_fantasy/src/world/TileMap.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ISPPV1 2023 3 | Study Case: Ultimate Fantasy (RPG) 4 | 5 | Author: Colton Ogden 6 | cogden@cs50.harvard.edu 7 | 8 | Modified by Alejandro Mujica (alejandro.j.mujic4@gmail.com) 9 | 10 | This class contains the class Entity. 11 | ]] 12 | TileMap = Class{} 13 | 14 | function TileMap:init(width, height, offset) 15 | self.tiles = {} 16 | self.width = width 17 | self.height = height 18 | self.offsetX = offset and offset.x or 0 19 | self.offsetY = offset and offset.y or 0 20 | end 21 | 22 | function TileMap:render() 23 | for y = 1, self.height do 24 | for x = 1, self.width do 25 | self.tiles[y][x]:render(self.offsetX, self.offsetY) 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /08-throw_a_bird/.gitattributes: -------------------------------------------------------------------------------- 1 | # Defold Protocol Buffer Text Files (https://github.com/github/linguist/issues/5091) 2 | *.animationset linguist-language=JSON5 3 | *.atlas linguist-language=JSON5 4 | *.camera linguist-language=JSON5 5 | *.collection linguist-language=JSON5 6 | *.collectionfactory linguist-language=JSON5 7 | *.collectionproxy linguist-language=JSON5 8 | *.collisionobject linguist-language=JSON5 9 | *.cubemap linguist-language=JSON5 10 | *.display_profiles linguist-language=JSON5 11 | *.factory linguist-language=JSON5 12 | *.font linguist-language=JSON5 13 | *.gamepads linguist-language=JSON5 14 | *.go linguist-language=JSON5 15 | *.gui linguist-language=JSON5 16 | *.input_binding linguist-language=JSON5 17 | *.label linguist-language=JSON5 18 | *.material linguist-language=JSON5 19 | *.mesh linguist-language=JSON5 20 | *.model linguist-language=JSON5 21 | *.particlefx linguist-language=JSON5 22 | *.render linguist-language=JSON5 23 | *.sound linguist-language=JSON5 24 | *.sprite linguist-language=JSON5 25 | *.spinemodel linguist-language=JSON5 26 | *.spinescene linguist-language=JSON5 27 | *.texture_profiles linguist-language=JSON5 28 | *.tilemap linguist-language=JSON5 29 | *.tilesource linguist-language=JSON5 30 | 31 | # Defold JSON Files 32 | *.buffer linguist-language=JSON 33 | 34 | # Defold GLSL Shaders 35 | *.fp linguist-language=GLSL 36 | *.vp linguist-language=GLSL 37 | 38 | # Defold Lua Files 39 | *.editor_script linguist-language=Lua 40 | *.render_script linguist-language=Lua 41 | *.script linguist-language=Lua 42 | *.gui_script linguist-language=Lua 43 | -------------------------------------------------------------------------------- /08-throw_a_bird/.gitignore: -------------------------------------------------------------------------------- 1 | /.internal 2 | /build 3 | .externalToolBuilders 4 | .DS_Store 5 | Thumbs.db 6 | .lock-wscript 7 | *.pyc 8 | .project 9 | .cproject 10 | builtins 11 | debug.keystore 12 | debug.keystore.pass.txt 13 | manifest.private.der 14 | manifest.public.der -------------------------------------------------------------------------------- /08-throw_a_bird/assets/fonts/RifficFree-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/08-throw_a_bird/assets/fonts/RifficFree-Bold.ttf -------------------------------------------------------------------------------- /08-throw_a_bird/assets/fonts/riffic_medium.font: -------------------------------------------------------------------------------- 1 | font: "/assets/fonts/RifficFree-Bold.ttf" 2 | material: "/builtins/fonts/font.material" 3 | size: 30 4 | -------------------------------------------------------------------------------- /08-throw_a_bird/assets/textures/buildings/debrisWood_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/08-throw_a_bird/assets/textures/buildings/debrisWood_1.png -------------------------------------------------------------------------------- /08-throw_a_bird/assets/textures/buildings/elementStone011.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/08-throw_a_bird/assets/textures/buildings/elementStone011.png -------------------------------------------------------------------------------- /08-throw_a_bird/assets/textures/buildings/elementStone014.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/08-throw_a_bird/assets/textures/buildings/elementStone014.png -------------------------------------------------------------------------------- /08-throw_a_bird/assets/textures/buildings/elementStone046.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/08-throw_a_bird/assets/textures/buildings/elementStone046.png -------------------------------------------------------------------------------- /08-throw_a_bird/assets/textures/buildings/elementWood012.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/08-throw_a_bird/assets/textures/buildings/elementWood012.png -------------------------------------------------------------------------------- /08-throw_a_bird/assets/textures/buildings/elementWood015.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/08-throw_a_bird/assets/textures/buildings/elementWood015.png -------------------------------------------------------------------------------- /08-throw_a_bird/assets/textures/buildings/elementWood047.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/08-throw_a_bird/assets/textures/buildings/elementWood047.png -------------------------------------------------------------------------------- /08-throw_a_bird/assets/textures/characters/alienBlue_square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/08-throw_a_bird/assets/textures/characters/alienBlue_square.png -------------------------------------------------------------------------------- /08-throw_a_bird/assets/textures/characters/alienGreen_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/08-throw_a_bird/assets/textures/characters/alienGreen_round.png -------------------------------------------------------------------------------- /08-throw_a_bird/assets/textures/characters/parrot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/08-throw_a_bird/assets/textures/characters/parrot.png -------------------------------------------------------------------------------- /08-throw_a_bird/assets/textures/world/Clouds 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/08-throw_a_bird/assets/textures/world/Clouds 2.png -------------------------------------------------------------------------------- /08-throw_a_bird/assets/textures/world/Clouds 7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/08-throw_a_bird/assets/textures/world/Clouds 7.png -------------------------------------------------------------------------------- /08-throw_a_bird/assets/textures/world/Forest Tree 13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/08-throw_a_bird/assets/textures/world/Forest Tree 13.png -------------------------------------------------------------------------------- /08-throw_a_bird/assets/textures/world/Forest Tree 6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/08-throw_a_bird/assets/textures/world/Forest Tree 6.png -------------------------------------------------------------------------------- /08-throw_a_bird/assets/textures/world/Forest Tree 7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/08-throw_a_bird/assets/textures/world/Forest Tree 7.png -------------------------------------------------------------------------------- /08-throw_a_bird/assets/textures/world/Mountain 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/08-throw_a_bird/assets/textures/world/Mountain 1.png -------------------------------------------------------------------------------- /08-throw_a_bird/assets/textures/world/Mountain 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/08-throw_a_bird/assets/textures/world/Mountain 2.png -------------------------------------------------------------------------------- /08-throw_a_bird/assets/textures/world/Walking Platforms 8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/08-throw_a_bird/assets/textures/world/Walking Platforms 8.png -------------------------------------------------------------------------------- /08-throw_a_bird/assets/textures/world/ground1px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/08-throw_a_bird/assets/textures/world/ground1px.png -------------------------------------------------------------------------------- /08-throw_a_bird/game.project: -------------------------------------------------------------------------------- 1 | [project] 2 | title = throw_a_bird3 3 | 4 | [bootstrap] 5 | main_collection = /main/collections/main.collectionc 6 | render = /builtins/render/default.renderc 7 | 8 | [input] 9 | game_binding = /input/game.input_bindingc 10 | use_accelerometer = 0 11 | 12 | [ios] 13 | app_icon_120x120 = /assets/app_icons/icon_120.png 14 | app_icon_180x180 = /assets/app_icons/icon_180.png 15 | app_icon_76x76 = /assets/app_icons/icon_76.png 16 | app_icon_152x152 = /assets/app_icons/icon_152.png 17 | app_icon_57x57 = /assets/app_icons/icon_57.png 18 | app_icon_114x114 = /assets/app_icons/icon_114.png 19 | app_icon_72x72 = /assets/app_icons/icon_72.png 20 | app_icon_144x144 = /assets/app_icons/icon_144.png 21 | app_icon_167x167 = /assets/app_icons/icon_167.png 22 | bundle_identifier = com.example.todo 23 | icons_asset = /assets/Assets.car 24 | 25 | [osx] 26 | bundle_identifier = com.example.todo 27 | 28 | [android] 29 | input_method = HiddenInputField 30 | 31 | [script] 32 | shared_state = 1 33 | 34 | [physics] 35 | gravity_y = -1000.0 36 | scale = 0.02 37 | max_collisions = 128 38 | max_contacts = 256 39 | 40 | -------------------------------------------------------------------------------- /08-throw_a_bird/input/game.input_binding: -------------------------------------------------------------------------------- 1 | mouse_trigger { 2 | input: MOUSE_BUTTON_1 3 | action: "touch" 4 | } 5 | -------------------------------------------------------------------------------- /08-throw_a_bird/main/atlas/buildings.atlas: -------------------------------------------------------------------------------- 1 | images { 2 | image: "/assets/textures/buildings/elementStone011.png" 3 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 4 | } 5 | images { 6 | image: "/assets/textures/buildings/elementWood012.png" 7 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 8 | } 9 | images { 10 | image: "/assets/textures/buildings/elementStone014.png" 11 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 12 | } 13 | images { 14 | image: "/assets/textures/buildings/elementStone046.png" 15 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 16 | } 17 | images { 18 | image: "/assets/textures/buildings/elementWood015.png" 19 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 20 | } 21 | images { 22 | image: "/assets/textures/buildings/elementWood047.png" 23 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 24 | } 25 | images { 26 | image: "/assets/textures/buildings/debrisWood_1.png" 27 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 28 | } 29 | margin: 0 30 | extrude_borders: 2 31 | inner_padding: 0 32 | -------------------------------------------------------------------------------- /08-throw_a_bird/main/atlas/characters.atlas: -------------------------------------------------------------------------------- 1 | images { 2 | image: "/assets/textures/characters/alienBlue_square.png" 3 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 4 | } 5 | images { 6 | image: "/assets/textures/characters/alienGreen_round.png" 7 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 8 | } 9 | images { 10 | image: "/assets/textures/characters/parrot.png" 11 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 12 | } 13 | margin: 0 14 | extrude_borders: 2 15 | inner_padding: 0 16 | -------------------------------------------------------------------------------- /08-throw_a_bird/main/atlas/world.atlas: -------------------------------------------------------------------------------- 1 | images { 2 | image: "/assets/textures/world/Clouds 2.png" 3 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 4 | } 5 | images { 6 | image: "/assets/textures/world/Clouds 7.png" 7 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 8 | } 9 | images { 10 | image: "/assets/textures/world/Forest Tree 6.png" 11 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 12 | } 13 | images { 14 | image: "/assets/textures/world/Forest Tree 7.png" 15 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 16 | } 17 | images { 18 | image: "/assets/textures/world/Forest Tree 13.png" 19 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 20 | } 21 | images { 22 | image: "/assets/textures/world/Mountain 1.png" 23 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 24 | } 25 | images { 26 | image: "/assets/textures/world/Mountain 2.png" 27 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 28 | } 29 | images { 30 | image: "/assets/textures/world/Walking Platforms 8.png" 31 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 32 | } 33 | images { 34 | image: "/assets/textures/world/ground1px.png" 35 | sprite_trim_mode: SPRITE_TRIM_MODE_OFF 36 | } 37 | margin: 0 38 | extrude_borders: 2 39 | inner_padding: 0 40 | -------------------------------------------------------------------------------- /08-throw_a_bird/main/game_objects/debris.go: -------------------------------------------------------------------------------- 1 | components { 2 | id: "debris" 3 | component: "/main/scripts/debris.script" 4 | position { 5 | x: 0.0 6 | y: 0.0 7 | z: 0.0 8 | } 9 | rotation { 10 | x: 0.0 11 | y: 0.0 12 | z: 0.0 13 | w: 1.0 14 | } 15 | properties { 16 | id: "image" 17 | value: "debrisWood_1" 18 | type: PROPERTY_TYPE_HASH 19 | } 20 | } 21 | embedded_components { 22 | id: "sprite" 23 | type: "sprite" 24 | data: "tile_set: \"/main/atlas/buildings.atlas\"\n" 25 | "default_animation: \"debrisWood_1\"\n" 26 | "material: \"/builtins/materials/sprite.material\"\n" 27 | "blend_mode: BLEND_MODE_ALPHA\n" 28 | "" 29 | position { 30 | x: 0.0 31 | y: 0.0 32 | z: 0.0 33 | } 34 | rotation { 35 | x: 0.0 36 | y: 0.0 37 | z: 0.0 38 | w: 1.0 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /08-throw_a_bird/main/game_objects/parrot.go: -------------------------------------------------------------------------------- 1 | embedded_components { 2 | id: "sprite" 3 | type: "sprite" 4 | data: "default_animation: \"parrot\"\n" 5 | "material: \"/builtins/materials/sprite.material\"\n" 6 | "blend_mode: BLEND_MODE_ALPHA\n" 7 | "textures {\n" 8 | " sampler: \"texture_sampler\"\n" 9 | " texture: \"/main/atlas/characters.atlas\"\n" 10 | "}\n" 11 | "" 12 | position { 13 | x: 0.0 14 | y: 0.0 15 | z: 0.0 16 | } 17 | rotation { 18 | x: 0.0 19 | y: 0.0 20 | z: 0.0 21 | w: 1.0 22 | } 23 | } 24 | embedded_components { 25 | id: "collisionobject" 26 | type: "collisionobject" 27 | data: "collision_shape: \"\"\n" 28 | "type: COLLISION_OBJECT_TYPE_DYNAMIC\n" 29 | "mass: 2500.0\n" 30 | "friction: 1.0\n" 31 | "restitution: 0.1\n" 32 | "group: \"bird\"\n" 33 | "mask: \"destructible\"\n" 34 | "mask: \"ground\"\n" 35 | "mask: \"wind\"\n" 36 | "embedded_collision_shape {\n" 37 | " shapes {\n" 38 | " shape_type: TYPE_SPHERE\n" 39 | " position {\n" 40 | " x: 0.0\n" 41 | " y: 0.0\n" 42 | " z: 0.0\n" 43 | " }\n" 44 | " rotation {\n" 45 | " x: 0.0\n" 46 | " y: 0.0\n" 47 | " z: 0.0\n" 48 | " w: 1.0\n" 49 | " }\n" 50 | " index: 0\n" 51 | " count: 1\n" 52 | " id: \"\"\n" 53 | " }\n" 54 | " data: 35.0395\n" 55 | "}\n" 56 | "linear_damping: 0.1\n" 57 | "angular_damping: 0.7\n" 58 | "locked_rotation: false\n" 59 | "bullet: false\n" 60 | "" 61 | position { 62 | x: 0.0 63 | y: 0.0 64 | z: 0.0 65 | } 66 | rotation { 67 | x: 0.0 68 | y: 0.0 69 | z: 0.0 70 | w: 1.0 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /08-throw_a_bird/main/scripts/debris.script: -------------------------------------------------------------------------------- 1 | go.property("image", hash("debrisWood_1")) 2 | 3 | function init(self) 4 | msg.post("#sprite", "play_animation", { id = self.image }) 5 | 6 | local to = go.get_world_position() - vmath.vector3(math.random(-10, 10), 150, 0) 7 | go.animate(".", "position", go.PLAYBACK_ONCE_FORWARD, to, go.EASING_INCUBIC, 0.5, 0, function() 8 | go.delete() 9 | end) 10 | go.animate(".", "euler.z", go.PLAYBACK_ONCE_FORWARD, math.random(360), go.EASING_INCUBIC, 0.5) 11 | end 12 | -------------------------------------------------------------------------------- /08-throw_a_bird/main/scripts/destructible.script: -------------------------------------------------------------------------------- 1 | go.property("energy", 200) 2 | go.property("undamaged", hash("elementStone011")) 3 | go.property("damaged", hash("elementStone014")) 4 | go.property("almost_destroyed", hash("elementStone046")) 5 | go.property("debris", true) 6 | 7 | function init(self) 8 | self.mass = go.get("#collisionobject", "mass") 9 | self.initial_energy = self.energy 10 | self.damage_levels = { self.almost_destroyed, self.damaged, self.undamaged } 11 | end 12 | 13 | function on_message(self, message_id, message, sender) 14 | if message_id == hash("collision_response") then 15 | local other_url = msg.url(nil, message.other_id, "collisionobject") 16 | local other_speed = vmath.length(go.get(other_url, "linear_velocity")) 17 | local other_mass = go.get(other_url, "mass") 18 | 19 | local speed = other_speed 20 | if message.group == hash("ground") then 21 | speed = vmath.length(go.get("#collisionobject", "linear_velocity")) 22 | other_mass = 1000 23 | end 24 | 25 | -- Only apply damage if the speed is high enough 26 | if speed > 20 then 27 | local damage = speed * 0.01 * other_mass / self.mass 28 | self.energy = self.energy - damage 29 | 30 | if self.energy <= 0 then 31 | go.delete() 32 | 33 | if self.debris then 34 | i = 1, 5 do 35 | local pos = go.get_world_position() + vmath.vector3(math.random(-30, 30), math.random(-30, 30), 0) 36 | local rot = vmath.quat_rotation_z(math.rad(math.random(360))) 37 | local scale = math.random(5, 8) / 10 38 | factory.create("#factory", pos, rot, {}, scale) 39 | end 40 | self.debris = false 41 | end 42 | else 43 | local damage_level = math.ceil(3 * self.energy / self.initial_energy) 44 | msg.post("#sprite", "play_animation", { id = self.damage_levels[damage_level] }) 45 | end 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /08-throw_a_bird/main/scripts/player.gui: -------------------------------------------------------------------------------- 1 | script: "" 2 | fonts { 3 | name: "riffic_medium" 4 | font: "/assets/fonts/riffic_medium.font" 5 | } 6 | background_color { 7 | x: 0.0 8 | y: 0.0 9 | z: 0.0 10 | w: 0.0 11 | } 12 | nodes { 13 | position { 14 | x: 903.0 15 | y: 589.0 16 | z: 0.0 17 | w: 1.0 18 | } 19 | rotation { 20 | x: 0.0 21 | y: 0.0 22 | z: 0.0 23 | w: 1.0 24 | } 25 | scale { 26 | x: 1.0 27 | y: 1.0 28 | z: 1.0 29 | w: 1.0 30 | } 31 | size { 32 | x: 200.0 33 | y: 100.0 34 | z: 0.0 35 | w: 1.0 36 | } 37 | color { 38 | x: 0.0 39 | y: 0.0 40 | z: 0.0 41 | w: 1.0 42 | } 43 | type: TYPE_TEXT 44 | blend_mode: BLEND_MODE_ALPHA 45 | text: "0" 46 | font: "riffic_medium" 47 | id: "score" 48 | xanchor: XANCHOR_NONE 49 | yanchor: YANCHOR_NONE 50 | pivot: PIVOT_CENTER 51 | outline { 52 | x: 1.0 53 | y: 1.0 54 | z: 1.0 55 | w: 1.0 56 | } 57 | shadow { 58 | x: 1.0 59 | y: 1.0 60 | z: 1.0 61 | w: 1.0 62 | } 63 | adjust_mode: ADJUST_MODE_FIT 64 | line_break: false 65 | layer: "" 66 | inherit_alpha: true 67 | alpha: 1.0 68 | outline_alpha: 1.0 69 | shadow_alpha: 1.0 70 | template_node_child: false 71 | text_leading: 1.0 72 | text_tracking: 0.0 73 | custom_type: 0 74 | enabled: true 75 | visible: true 76 | } 77 | material: "/builtins/materials/gui.material" 78 | adjust_reference: ADJUST_REFERENCE_PARENT 79 | max_nodes: 512 80 | -------------------------------------------------------------------------------- /08-throw_a_bird/main/scripts/wind.script: -------------------------------------------------------------------------------- 1 | go.property("strength", vmath.vector3(900000, 0, 0)) 2 | 3 | function on_message(self, message_id, message, sender) 4 | if message_id == hash("collision_response") then 5 | msg.post(msg.url(nil, message.other_id, "collisionobject"), "apply_force", { force = self.strength, position = message.other_position }) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Alejandro Mujica 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VideoGameProgramming 2 | 3 | Source codes for the elective course of Video Game Programming at Universidad de Los Andes, VE. 4 | -------------------------------------------------------------------------------- /complementary_contents/01-input_handler/command_pattern/Action.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2024 3 | Input handler by commands. 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the interface of an action to be executed 9 | on an target. This interface represents the Command. 10 | """ 11 | 12 | from typing import NoReturn 13 | 14 | from Actor import Actor 15 | 16 | 17 | class Action: 18 | def execute(self, target: Actor) -> NoReturn: 19 | raise NotImplementedError() 20 | -------------------------------------------------------------------------------- /complementary_contents/01-input_handler/command_pattern/ActionJump.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2024 3 | Input handler by commands. 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the class JumpAction as specialization of Action. 9 | """ 10 | 11 | from Action import Action 12 | from Actor import Actor 13 | 14 | 15 | class ActionJump(Action): 16 | def execute(self, target: Actor) -> None: 17 | print(f"{target} jumps") 18 | -------------------------------------------------------------------------------- /complementary_contents/01-input_handler/command_pattern/ActionShoot.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2024 3 | Input handler by commands. 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the class ShootAction as specialization of Action. 9 | """ 10 | 11 | from Action import Action 12 | from Actor import Actor 13 | 14 | 15 | class ActionShoot(Action): 16 | def execute(self, target: Actor) -> None: 17 | print(f"{target} shoots") 18 | -------------------------------------------------------------------------------- /complementary_contents/01-input_handler/command_pattern/Actor.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2024 3 | Input handler by commands. 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the class Actor that represents any elements 9 | the actions are executed on. 10 | """ 11 | 12 | 13 | class Actor: 14 | def __init__(self, name: str) -> None: 15 | self.name = name 16 | 17 | def __str__(self) -> str: 18 | return self.name 19 | -------------------------------------------------------------------------------- /complementary_contents/01-input_handler/command_pattern/InputHandler.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2024 3 | Input handler by commands. 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the class InputHandler that retrieves the events 9 | and matches them with their associated actions. 10 | """ 11 | 12 | import sys 13 | import pygame 14 | 15 | import settings 16 | 17 | from Action import Action 18 | 19 | 20 | class InputHandler: 21 | @staticmethod 22 | def handle_input() -> Action: 23 | for event in pygame.event.get(): 24 | if event.type == pygame.QUIT: 25 | sys.exit() 26 | elif event.type == pygame.KEYDOWN: 27 | keyboard_binding = settings.input_binding.get("keyboard") 28 | if keyboard_binding is not None: 29 | action_class = keyboard_binding.get(event.key) 30 | if action_class is not None: 31 | return action_class() 32 | elif event.type == pygame.MOUSEBUTTONDOWN: 33 | mouse_binding = settings.input_binding.get("mouse") 34 | if mouse_binding is not None: 35 | action_class = mouse_binding.get(event.button) 36 | if action_class is not None: 37 | return action_class() 38 | return None 39 | -------------------------------------------------------------------------------- /complementary_contents/01-input_handler/command_pattern/main.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2024 3 | Input handler by commands. 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the main program to build a window an test the 9 | input handler. 10 | """ 11 | 12 | import pygame 13 | 14 | from Actor import Actor 15 | from InputHandler import InputHandler 16 | 17 | if __name__ == "__main__": 18 | pygame.display.set_mode((800, 600)) 19 | pygame.display.set_caption("Input Handler Example by Commands") 20 | actor = Actor("Player") 21 | while True: 22 | action = InputHandler.handle_input() 23 | if action is not None: 24 | action.execute(actor) 25 | -------------------------------------------------------------------------------- /complementary_contents/01-input_handler/command_pattern/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2024 3 | Input handler by commands. 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the input binding set up. 9 | """ 10 | 11 | import pygame 12 | 13 | from ActionJump import ActionJump 14 | from ActionShoot import ActionShoot 15 | 16 | input_binding = { 17 | "keyboard": { 18 | pygame.K_SPACE: ActionJump, 19 | }, 20 | "mouse": {1: ActionShoot, 3: ActionJump}, 21 | } 22 | -------------------------------------------------------------------------------- /complementary_contents/01-input_handler/observer_pattern/Actor.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2024 3 | Input handler by observers. 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the class Actor, a Listener of input events. 9 | """ 10 | 11 | from typing import Any 12 | 13 | from Listener import Listener 14 | 15 | 16 | class Actor(Listener): 17 | def __init__(self, name: str) -> None: 18 | self.name = name 19 | 20 | def __str__(self) -> str: 21 | return self.name 22 | 23 | def on_input(self, input_id: str, event: Any) -> None: 24 | if input_id == "jump" and event.pressed: 25 | print(f"{self} is jumping") 26 | elif input_id == "shoot" and event.pressed: 27 | print(f"{self} is shooting") 28 | elif input_id == "rotate_left": 29 | print(f"{self} is rotating left") 30 | elif input_id == "rotate_right": 31 | print(f"{self} is rotating right") 32 | elif input_id == "rotate_up": 33 | print(f"{self} is rotating up") 34 | elif input_id == "rotate_down": 35 | print(f"{self} is rotating down") 36 | elif input_id == "zoom_in": 37 | print(f"{self} is zooming in") 38 | elif input_id == "zoom_out": 39 | print(f"{self} is zooming out") 40 | elif input_id == "next": 41 | print(f"{self} is going to the next item") 42 | elif input_id == "previous": 43 | print(f"{self} is going to the previous item") 44 | -------------------------------------------------------------------------------- /complementary_contents/01-input_handler/observer_pattern/Listener.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2024 3 | Input handler by observers. 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the class Listener, the observer interface. 9 | """ 10 | 11 | from typing import Any, NoReturn 12 | 13 | 14 | class Listener: 15 | def on_input(input_id: str, event: Any) -> NoReturn: 16 | raise NotImplementedError() 17 | -------------------------------------------------------------------------------- /complementary_contents/01-input_handler/observer_pattern/main.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2024 3 | Input handler by commands. 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the main program to build a window an test the 9 | input handler. 10 | """ 11 | 12 | import pygame 13 | 14 | from Actor import Actor 15 | from InputHandler import InputHandler 16 | 17 | if __name__ == "__main__": 18 | screen = pygame.display.set_mode((800, 600)) 19 | pygame.display.set_caption("Input Handler Example by Observers") 20 | screen.fill((255, 255, 255)) 21 | pygame.display.update() 22 | actor = Actor("Player") 23 | InputHandler.register(actor) 24 | while True: 25 | InputHandler.handle_input() 26 | -------------------------------------------------------------------------------- /complementary_contents/01-input_handler/observer_pattern/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2024 3 | Input handler by commands. 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains the input binding set up. 9 | """ 10 | 11 | import pygame 12 | 13 | input_binding = { 14 | "keyboard": { 15 | pygame.K_SPACE: "jump", 16 | }, 17 | "mouse": {1: "shoot", 3: "jump"}, 18 | "motion": { 19 | (0, 1): "rotate_down", 20 | (0, -1): "rotate_up", 21 | (-1, 0): "rotate_left", 22 | (1, 0): "rotate_right", 23 | }, 24 | "wheel": { 25 | (0, 1): "zoom_in", 26 | (0, -1): "zoom_out", 27 | (1, 0): "next", 28 | (-1, 0): "previous", 29 | }, 30 | } 31 | -------------------------------------------------------------------------------- /complementary_contents/01-input_handler/requirements.txt: -------------------------------------------------------------------------------- 1 | pygame -------------------------------------------------------------------------------- /complementary_contents/02-anonymous_functions/anonymous_functions0.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Anonymous Functions 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains an example of passing a lambda function 9 | as an argument to a higher-level function. 10 | """ 11 | from typing import Callable, Any 12 | 13 | 14 | def higher_order_function(f: Callable) -> Any: 15 | print("Executing f") 16 | return f() 17 | 18 | 19 | def another_function() -> None: 20 | print("Hello") 21 | 22 | 23 | result = higher_order_function(lambda: 10) 24 | print(result) 25 | 26 | result = higher_order_function(lambda: 10 > 9) 27 | print(result) 28 | 29 | result = higher_order_function(another_function) 30 | print(result) 31 | -------------------------------------------------------------------------------- /complementary_contents/02-anonymous_functions/anonymous_functions1.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Anonymous Functions 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains an example of passing a lambda function 9 | as an argument to a higher-level function with variadic arguments. 10 | """ 11 | from typing import Callable, Any 12 | 13 | 14 | def higher_order_function(f: Callable, *args: tuple, **kwargs: dict) -> Any: 15 | print(f"Executing f({args}, {kwargs})") 16 | return f(*args, **kwargs) 17 | 18 | 19 | result = higher_order_function(lambda x, y: x**y, 2, 3) 20 | print(result) 21 | 22 | result = higher_order_function(lambda a, b, c: a * b + c, 1, 2, 3) 23 | print(result) 24 | 25 | result = higher_order_function(lambda a, b, c: a * b + c, a=1, c=2, b=3) 26 | print(result) 27 | -------------------------------------------------------------------------------- /complementary_contents/02-anonymous_functions/anonymous_functions2.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Anonymous Functions 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains an example of a function that returns an anonymous function. 9 | """ 10 | from typing import Callable 11 | 12 | 13 | def build_power_function(x: float, y: float) -> Callable: 14 | return lambda: x**y 15 | 16 | 17 | def build_power_three_function() -> Callable: 18 | return lambda x: x**3 19 | 20 | 21 | power2_3 = build_power_function(2, 3) 22 | print(power2_3()) 23 | 24 | power2_4 = build_power_function(2, 4) 25 | print(power2_4()) 26 | 27 | power_3 = build_power_three_function() 28 | print(po) 29 | -------------------------------------------------------------------------------- /complementary_contents/02-anonymous_functions/anonymous_functions3-problem.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Anonymous Functions 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains an example that shows the dynamic binding of the values to a variable. 9 | """ 10 | 11 | 12 | def print_the_value_of_i(i: int) -> None: 13 | print(i) 14 | 15 | 16 | fs = [] 17 | 18 | for i in range(5): 19 | fs.append(lambda: print_the_value_of_i(i)) 20 | 21 | for f in fs: 22 | f() 23 | -------------------------------------------------------------------------------- /complementary_contents/02-anonymous_functions/anonymous_functions3-solution.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Anonymous Functions 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains an example that solves the previous problem by creating a closure. 9 | """ 10 | from typing import Callable 11 | 12 | 13 | def create_print_the_value_of_i(i: int) -> Callable: 14 | def print_the_value_of_i() -> None: 15 | print(i) 16 | 17 | return print_the_value_of_i 18 | 19 | 20 | def create_print_the_value_of_i(i: int) -> Callable: 21 | return lambda: print(i) 22 | 23 | 24 | fs = [] 25 | 26 | for i in range(5): 27 | fs.append(create_print_the_value_of_i(i)) 28 | 29 | for f in fs: 30 | f() 31 | -------------------------------------------------------------------------------- /complementary_contents/03-timers/1-every/every0.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Timers, every 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains an example of how to create a timer that increment 9 | counter every one second. 10 | """ 11 | import pygame 12 | 13 | from gale.input_handler import InputHandler, KEY_ESCAPE, InputData 14 | from gale.game import Game 15 | from gale.text import render_text 16 | 17 | InputHandler.set_keyboard_action(KEY_ESCAPE, "quit") 18 | 19 | 20 | class TimerGame(Game): 21 | def init(self) -> None: 22 | self.timer = 0 23 | self.interval = 1 24 | self.counter = 0 25 | self.font = pygame.font.Font("font.ttf", 64) 26 | InputHandler.register_listener(self) 27 | 28 | def update(self, dt: float) -> None: 29 | self.timer += dt 30 | 31 | if self.timer >= self.interval: 32 | self.timer %= self.interval 33 | self.counter += 1 34 | 35 | def render(self, surface: pygame.Surface) -> None: 36 | render_text( 37 | surface, 38 | str(self.counter), 39 | self.font, 40 | 640, 41 | 360, 42 | (255, 255, 255), 43 | center=True, 44 | ) 45 | 46 | def on_input(self, input_id: str, input_data: InputData) -> None: 47 | if input_id == "quit" and input_data.pressed: 48 | self.quit() 49 | 50 | 51 | if __name__ == "__main__": 52 | timer_game = TimerGame("Timer 0", 1280, 720) 53 | timer_game.exec() 54 | -------------------------------------------------------------------------------- /complementary_contents/03-timers/1-every/every2.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Timers, every 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains an example of how to create three timers that increment 9 | counters every one, two, and four seconds respectively by using gale.timer.Timer.every. 10 | """ 11 | from typing import Callable 12 | 13 | import pygame 14 | 15 | from gale.input_handler import InputHandler, KEY_ESCAPE, InputData 16 | from gale.game import Game 17 | from gale.text import render_text 18 | from gale.timer import Timer 19 | 20 | InputHandler.set_keyboard_action(KEY_ESCAPE, "quit") 21 | 22 | 23 | class TimerGame(Game): 24 | def init(self) -> None: 25 | self.intervals = [1, 2, 4] 26 | self.counters = [0, 0, 0] 27 | 28 | def inc(i: int) -> Callable[[], None]: 29 | def inc_i(): 30 | self.counters[i] += 1 31 | 32 | return inc_i 33 | 34 | for i in range(len(self.intervals)): 35 | Timer.every(self.intervals[i], inc(i)) 36 | 37 | self.font = pygame.font.Font("font.ttf", 64) 38 | InputHandler.register_listener(self) 39 | 40 | def render(self, surface: pygame.Surface) -> None: 41 | for i in range(len(self.counters)): 42 | render_text( 43 | surface, 44 | str(self.counters[i]), 45 | self.font, 46 | 640, 47 | i * 100 + 260, 48 | (255, 255, 255), 49 | center=True, 50 | ) 51 | 52 | def on_input(self, input_id: str, input_data: InputData) -> None: 53 | if input_id == "quit" and input_data.pressed: 54 | self.quit() 55 | 56 | 57 | if __name__ == "__main__": 58 | timer_game = TimerGame("Timer 0", 1280, 720) 59 | timer_game.exec() 60 | -------------------------------------------------------------------------------- /complementary_contents/03-timers/1-every/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/complementary_contents/03-timers/1-every/font.ttf -------------------------------------------------------------------------------- /complementary_contents/03-timers/2-tween/tween0.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Timers, every 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains an example that moves a rectangle from the left to the right 9 | by interpolating its position through time. 10 | """ 11 | import pygame 12 | 13 | from gale.game import Game 14 | from gale.input_handler import InputHandler, KEY_ESCAPE, InputData 15 | 16 | InputHandler.set_keyboard_action(KEY_ESCAPE, "quit") 17 | 18 | WINDOW_WIDTH = 1280 19 | WINDOW_HEIGHT = 720 20 | 21 | MOVE_DURATION = 3 22 | 23 | 24 | class TweenGame(Game): 25 | SQUARE_SIZE = 50 26 | 27 | def init(self) -> None: 28 | self.rect = pygame.Rect( 29 | 0, 30 | WINDOW_HEIGHT // 2 - self.SQUARE_SIZE // 2, 31 | self.SQUARE_SIZE, 32 | self.SQUARE_SIZE, 33 | ) 34 | # end X position for our interpolation 35 | self.end_x = WINDOW_WIDTH - self.SQUARE_SIZE 36 | self.timer = 0 37 | InputHandler.register_listener(self) 38 | 39 | def update(self, dt: float) -> None: 40 | self.timer += dt 41 | self.rect.x = min(self.end_x, self.end_x * (self.timer / MOVE_DURATION)) 42 | 43 | def render(self, surface: pygame.Surface) -> None: 44 | pygame.draw.rect(surface, (255, 255, 255), self.rect) 45 | 46 | def on_input(self, input_id: str, input_data: InputData) -> None: 47 | if input_id == "quit" and input_data.pressed: 48 | self.quit() 49 | 50 | 51 | if __name__ == "__main__": 52 | tween_game = TweenGame("Tween Game", WINDOW_WIDTH, WINDOW_HEIGHT, fps=60) 53 | tween_game.exec() 54 | -------------------------------------------------------------------------------- /complementary_contents/03-timers/2-tween/tween2.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Timers, every 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains an example that generates 1000 random rectangles and moves them 9 | from the left to the right by interpolating their position through time 10 | by using gale.timer.Timer.tween. 11 | """ 12 | import random 13 | 14 | import pygame 15 | 16 | from gale.game import Game 17 | from gale.input_handler import InputHandler, KEY_ESCAPE, InputData 18 | from gale.timer import Timer 19 | 20 | InputHandler.set_keyboard_action(KEY_ESCAPE, "quit") 21 | 22 | WINDOW_WIDTH = 1280 23 | WINDOW_HEIGHT = 720 24 | 25 | TIMER_MAX = 10 26 | 27 | 28 | class TweenGame(Game): 29 | SQUARE_SIZE = 50 30 | 31 | def init(self) -> None: 32 | self.rects = [] 33 | 34 | for _ in range(1000): 35 | y = random.randint(0, WINDOW_HEIGHT - self.SQUARE_SIZE) 36 | 37 | rect = pygame.Rect(0, y, self.SQUARE_SIZE, self.SQUARE_SIZE) 38 | rate = random.uniform(0.5, TIMER_MAX) 39 | 40 | self.rects.append(rect) 41 | Timer.tween(rate, [(rect, {"left": WINDOW_WIDTH - self.SQUARE_SIZE})]) 42 | 43 | InputHandler.register_listener(self) 44 | 45 | def render(self, surface: pygame.Surface) -> None: 46 | for rect in self.rects: 47 | pygame.draw.rect(surface, (255, 255, 255), rect) 48 | 49 | def on_input(self, input_id: str, input_data: InputData) -> None: 50 | if input_id == "quit" and input_data.pressed: 51 | self.quit() 52 | 53 | 54 | if __name__ == "__main__": 55 | tween_game = TweenGame("Tween Game", WINDOW_WIDTH, WINDOW_HEIGHT) 56 | tween_game.exec() 57 | -------------------------------------------------------------------------------- /complementary_contents/03-timers/3-chain/chain1.py: -------------------------------------------------------------------------------- 1 | """ 2 | ISPPV1 2023 3 | Timers, every 4 | 5 | Author: Alejandro Mujica 6 | alejandro.j.mujic4@gmail.com 7 | 8 | This file contains an example that shows how to create a chain of 9 | interpolated movements by using gale.timer.Timer.tween and using the on_finish function. 10 | """ 11 | import pygame 12 | 13 | from gale.game import Game 14 | from gale.input_handler import InputHandler, KEY_ESCAPE, InputData 15 | from gale.timer import Timer 16 | 17 | InputHandler.set_keyboard_action(KEY_ESCAPE, "quit") 18 | 19 | WINDOW_WIDTH = 1280 20 | WINDOW_HEIGHT = 720 21 | 22 | MOVEMENT_TIME = 2 23 | 24 | 25 | class ChainGame(Game): 26 | 27 | SQUARE_SIZE = 50 28 | 29 | def init(self) -> None: 30 | self.rect = pygame.Rect(0, 0, self.SQUARE_SIZE, self.SQUARE_SIZE) 31 | 32 | Timer.tween( 33 | MOVEMENT_TIME, 34 | [(self.rect, {"left": WINDOW_WIDTH - self.SQUARE_SIZE})], 35 | on_finish=lambda: Timer.tween( 36 | MOVEMENT_TIME, 37 | [(self.rect, {"top": WINDOW_HEIGHT - self.SQUARE_SIZE})], 38 | on_finish=lambda: Timer.tween( 39 | MOVEMENT_TIME, 40 | [(self.rect, {"left": 0})], 41 | on_finish=lambda: Timer.tween( 42 | MOVEMENT_TIME, [(self.rect, {"top": 0})] 43 | ), 44 | ), 45 | ), 46 | ) 47 | 48 | InputHandler.register_listener(self) 49 | 50 | def render(self, surface: pygame.Surface) -> None: 51 | pygame.draw.rect(surface, (255, 255, 255), self.rect) 52 | 53 | def on_input(self, input_id: str, input_data: InputData) -> None: 54 | if input_id == "quit" and input_data.pressed: 55 | self.quit() 56 | 57 | 58 | if __name__ == "__main__": 59 | chain_game = ChainGame("Chain Game", WINDOW_WIDTH, WINDOW_HEIGHT) 60 | chain_game.exec() 61 | -------------------------------------------------------------------------------- /complementary_contents/03-timers/requirements.txt: -------------------------------------------------------------------------------- 1 | https://github.com/R3mmurd/Gale/archive/main.zip -------------------------------------------------------------------------------- /complementary_contents/04-scenes_tutorial/requirements.txt: -------------------------------------------------------------------------------- 1 | https://github.com/R3mmurd/Gale/archive/main.zip 2 | pyyaml 3 | -------------------------------------------------------------------------------- /complementary_contents/04-scenes_tutorial/tutorial/README.md: -------------------------------------------------------------------------------- 1 | # Tutorial -------------------------------------------------------------------------------- /complementary_contents/04-scenes_tutorial/tutorial/assets/graphics/creatures.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/complementary_contents/04-scenes_tutorial/tutorial/assets/graphics/creatures.png -------------------------------------------------------------------------------- /complementary_contents/04-scenes_tutorial/tutorial/assets/graphics/tileset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3mmurd/VideoGameProgramming/48a178d7e3f107346665dbae4053e507103477b3/complementary_contents/04-scenes_tutorial/tutorial/assets/graphics/tileset.png -------------------------------------------------------------------------------- /complementary_contents/04-scenes_tutorial/tutorial/assets/scenes/creatures.tsx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /complementary_contents/04-scenes_tutorial/tutorial/assets/scenes/tiles.tsx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /complementary_contents/04-scenes_tutorial/tutorial/main.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module was autogenerated by gale. 3 | """ 4 | import settings 5 | from src.Tutorial import Tutorial 6 | 7 | if __name__ == "__main__": 8 | game = Tutorial( 9 | "Tutorial", 10 | settings.WINDOW_WIDTH, 11 | settings.WINDOW_HEIGHT, 12 | settings.VIRTUAL_WIDTH, 13 | settings.VIRTUAL_HEIGHT, 14 | ) 15 | game.exec() 16 | -------------------------------------------------------------------------------- /complementary_contents/04-scenes_tutorial/tutorial/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module was autogenerated by gale. 3 | """ 4 | import pathlib 5 | 6 | import pygame 7 | 8 | from gale import frames 9 | from gale import input_handler 10 | 11 | from src import scene_loader 12 | 13 | input_handler.InputHandler.set_keyboard_action(input_handler.KEY_ESCAPE, "quit") 14 | 15 | # Size we want to emulate 16 | VIRTUAL_WIDTH = 400 17 | VIRTUAL_HEIGHT = 192 18 | 19 | # Size of our actual window 20 | WINDOW_WIDTH = VIRTUAL_WIDTH * 4 21 | WINDOW_HEIGHT = VIRTUAL_HEIGHT * 4 22 | 23 | BASE_DIR = pathlib.Path(__file__).parent 24 | 25 | TEXTURES = { 26 | "tiles": pygame.image.load(BASE_DIR / "assets" / "graphics" / "tileset.png"), 27 | "creatures": pygame.image.load(BASE_DIR / "assets" / "graphics" / "creatures.png"), 28 | } 29 | 30 | FRAMES = { 31 | "tiles": frames.generate_frames(TEXTURES["tiles"], 16, 16), 32 | "creatures": frames.generate_frames(TEXTURES["creatures"], 16, 16), 33 | } 34 | 35 | SceneLoader = scene_loader.YamlSceneLoader 36 | 37 | 38 | def build_scene_path(scene_number: int) -> pathlib.Path: 39 | return BASE_DIR / "assets" / "scenes" / f"{scene_number:02}" 40 | -------------------------------------------------------------------------------- /complementary_contents/04-scenes_tutorial/tutorial/src/Scene.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | 3 | import settings 4 | 5 | 6 | class Scene: 7 | def __init__(self) -> None: 8 | self.level = 1 9 | self.tilemap = {} 10 | self.items = [] 11 | self.creatures = [] 12 | settings.SceneLoader().load(self, settings.build_scene_path(self.level)) 13 | 14 | def render(self, surface: pygame.Surface) -> None: 15 | for layer in self.tilemap["layers"]: 16 | for i in range(self.tilemap["rows"]): 17 | for j in range(self.tilemap["cols"]): 18 | surface.blit( 19 | settings.TEXTURES["tiles"], 20 | layer[i][j]["position"], 21 | settings.FRAMES["tiles"][layer[i][j]["frame_index"]], 22 | ) 23 | 24 | for item in self.items: 25 | surface.blit( 26 | settings.TEXTURES["tiles"], 27 | item["position"], 28 | settings.FRAMES["tiles"][item["frame_index"]], 29 | ) 30 | 31 | for creature in self.creatures: 32 | surface.blit( 33 | settings.TEXTURES["creatures"], 34 | creature["position"], 35 | settings.FRAMES["creatures"][creature["frame_index"]], 36 | ) 37 | -------------------------------------------------------------------------------- /complementary_contents/04-scenes_tutorial/tutorial/src/Tutorial.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module was autogenerated by gale. 3 | """ 4 | import pygame 5 | 6 | from gale.game import Game 7 | from gale.input_handler import InputData, InputHandler, InputListener 8 | 9 | from src.Scene import Scene 10 | 11 | 12 | class Tutorial(Game, InputListener): 13 | def init(self) -> None: 14 | self.scene = Scene() 15 | InputHandler.register_listener(self) 16 | 17 | def render(self, surface: pygame.Surface) -> None: 18 | self.scene.render(surface) 19 | 20 | def on_input(self, input_id: str, input_data: InputData) -> None: 21 | if input_id == "quit" and input_data.pressed: 22 | self.quit() 23 | -------------------------------------------------------------------------------- /complementary_contents/04-scenes_tutorial/tutorial/src/scene_loader/__init__.py: -------------------------------------------------------------------------------- 1 | from src.scene_loader.TmxSceneLoader import TmxSceneLoader 2 | from src.scene_loader.JsonSceneLoader import JsonSceneLoader 3 | from src.scene_loader.YamlSceneLoader import YamlSceneLoader 4 | 5 | (TmxSceneLoader, JsonSceneLoader, YamlSceneLoader) 6 | --------------------------------------------------------------------------------