aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--game/CMakeLists.txt68
-rw-r--r--game/Config.h22
-rw-r--r--game/GameScene.cpp87
-rw-r--r--game/PreviewScene.cpp99
-rw-r--r--game/PreviewScene.h11
-rw-r--r--game/Random.cpp2
-rw-r--r--game/Random.h1
-rw-r--r--game/StartGameScript.cpp1
-rw-r--r--game/background/CMakeLists.txt9
-rw-r--r--game/enemy/BattleScript.cpp51
-rw-r--r--game/enemy/BattleScript.h24
-rw-r--r--game/enemy/EnemyBulletPool.cpp11
-rw-r--r--game/enemy/EnemyBulletPool.h11
-rw-r--r--game/enemy/EnemyBulletScript.cpp40
-rw-r--r--game/enemy/EnemyBulletScript.h11
-rw-r--r--game/enemy/EnemyBulletSubScene.cpp51
-rw-r--r--game/enemy/EnemyBulletSubScene.h10
-rw-r--r--game/enemy/EnemyConfig.h7
-rw-r--r--game/enemy/EnemyPool.cpp10
-rw-r--r--game/enemy/EnemyPool.h11
-rw-r--r--game/enemy/EnemyScript.cpp123
-rw-r--r--game/enemy/EnemyScript.h31
-rw-r--r--game/enemy/EnemySubScene.cpp102
-rw-r--r--game/enemy/EnemySubScene.h10
-rw-r--r--game/main.cpp9
-rw-r--r--game/menus/MenusConfig.h3
-rw-r--r--game/menus/mainmenu/ITransitionScript.cpp1
-rw-r--r--game/menus/shop/ShopMenuScene.cpp1
-rw-r--r--game/missile/MissilePool.cpp16
-rw-r--r--game/missile/MissilePool.h11
-rw-r--r--game/missile/MissileScript.cpp105
-rw-r--r--game/missile/MissileScript.h20
-rw-r--r--game/missile/MissileSubScene.cpp101
-rw-r--r--game/missile/MissileSubScene.h12
-rw-r--r--game/missile/SpawnEvent.cpp47
-rw-r--r--game/missile/SpawnEvent.h20
-rw-r--r--game/player/PlayerBulletPool.cpp11
-rw-r--r--game/player/PlayerBulletPool.h11
-rw-r--r--game/player/PlayerBulletScript.cpp41
-rw-r--r--game/player/PlayerBulletScript.h11
-rw-r--r--game/player/PlayerBulletSubScene.cpp52
-rw-r--r--game/player/PlayerBulletSubScene.h10
-rw-r--r--game/player/PlayerScript.cpp40
-rw-r--r--game/player/PlayerScript.h7
-rw-r--r--game/player/PlayerSubScene.cpp3
-rw-r--r--game/prefab/CMakeLists.txt6
-rw-r--r--game/prefab/ZapperObject.cpp118
-rw-r--r--game/prefab/ZapperObject.h40
-rw-r--r--game/prefab/ZapperPoolScript.cpp70
-rw-r--r--game/prefab/ZapperPoolScript.h33
-rw-r--r--game/prefab/ZapperPoolSubScene.cpp17
-rw-r--r--game/prefab/ZapperPoolSubScene.h19
-rw-r--r--game/preview/NpcScript.cpp32
-rw-r--r--game/preview/NpcScript.h11
-rw-r--r--game/preview/NpcSubScene.cpp69
-rw-r--r--game/preview/NpcSubScene.h10
-rw-r--r--game/preview/PrevPlayerScript.cpp132
-rw-r--r--game/preview/PrevPlayerScript.h23
-rw-r--r--game/preview/PrevPlayerSubScene.cpp86
-rw-r--r--game/preview/PrevPlayerSubScene.h10
-rw-r--r--game/preview/SmokeSubScene.cpp37
-rw-r--r--game/preview/SmokeSubScene.h10
-rw-r--r--game/scheduler/ObjectsScheduler.cpp58
-rw-r--r--game/scheduler/ObjectsScheduler.h36
-rwxr-xr-xgame/util/scrollgen36
-rw-r--r--game/workers/CollisionScript.cpp2
-rw-r--r--src/crepe/Collider.h3
-rw-r--r--src/crepe/api/Components.h16
-rw-r--r--src/crepe/api/Config.h3
-rw-r--r--src/crepe/api/Vector2.h46
-rw-r--r--src/crepe/api/Vector2.hpp15
-rw-r--r--src/test/Vector2Test.cpp10
72 files changed, 2191 insertions, 92 deletions
diff --git a/game/CMakeLists.txt b/game/CMakeLists.txt
index f81850d..e1168eb 100644
--- a/game/CMakeLists.txt
+++ b/game/CMakeLists.txt
@@ -7,27 +7,73 @@ set(CMAKE_BUILD_TYPE Debug)
project(game C CXX)
add_subdirectory(../src crepe)
-add_executable(main
+
+add_executable(main)
+
+target_sources(main PUBLIC
+ # enemy
+ enemy/BattleScript.cpp
+ enemy/EnemyPool.cpp
+ enemy/EnemyBulletScript.cpp
+ enemy/EnemyBulletSubScene.cpp
+ enemy/EnemyBulletPool.cpp
+ enemy/EnemySubScene.cpp
+ enemy/EnemyScript.cpp
+
+ #background
background/AquariumSubScene.cpp
background/AquariumScript.cpp
background/BackgroundSubScene.cpp
background/ForestParallaxScript.cpp
background/ForestSubScene.cpp
- GameScene.cpp
background/HallwaySubScene.cpp
+ background/StartSubScene.cpp
background/HallwayScript.cpp
+
+ # mainscenes
+ GameScene.cpp
+ menus/shop/ShopMenuScene.cpp
+ menus/mainmenu/MainMenuScene.cpp
+ PreviewScene.cpp
+ main.cpp
+
+ # missile
+ missile/MissilePool.cpp
+ missile/MissileScript.cpp
+ missile/MissileSubScene.cpp
+ missile/SpawnEvent.cpp
+
+ #scheduling
+ scheduler/ObjectsScheduler.cpp
+
+ # Preview
+ preview/SmokeSubScene.cpp
+ preview/NpcSubScene.cpp
+ preview/NpcScript.cpp
+ preview/PrevPlayerSubScene.cpp
+ preview/PrevPlayerScript.cpp
+
+ # scripts
+ GameScene.cpp
MoveCameraManualyScript.cpp
+ StartGameScript.cpp
+
+ # player
player/PlayerScript.cpp
player/PlayerSubScene.cpp
- StartGameScript.cpp
+ player/PlayerBulletPool.cpp
+ player/PlayerBulletScript.cpp
+ player/PlayerBulletSubScene.cpp
player/PlayerEndScript.cpp
player/PlayerAudioScript.cpp
- background/StartSubScene.cpp
+
+ # workers
workers/WorkersSubScene.cpp
workers/WorkerScript.cpp
workers/PanicFromPlayerScript.cpp
workers/CollisionScript.cpp
- main.cpp
+
+ # menus
menus/BannerSubScene.cpp
menus/ButtonSubScene.cpp
menus/IButtonScript.cpp
@@ -38,24 +84,32 @@ add_executable(main
menus/FloatingWindowSubScene.cpp
menus/IFloatingWindowScript.cpp
menus/ButtonShowCreditsSubScript.cpp
- menus/shop/ShopMenuScene.cpp
menus/mainmenu/ButtonTransitionPreviewSubScript.cpp
menus/mainmenu/ITransitionScript.cpp
- menus/mainmenu/MainMenuScene.cpp
menus/mainmenu/TransitionStartSubScript.cpp
menus/mainmenu/CreditsSubScene.cpp
menus/mainmenu/CreditsSubScript.cpp
menus/endgame/EndGameSubScene.cpp
menus/endgame/EndGameSubScript.cpp
+
+ # coins
coins/CoinSubScene.cpp
coins/CoinPoolSubScene.cpp
coins/CoinSystemScript.cpp
coins/CoinScript.cpp
+
+ # hud
hud/HudSubScene.cpp
hud/HudScript.cpp
hud/SpeedScript.cpp
+
+ #random
Random.cpp
)
+add_subdirectory(background)
+add_subdirectory(prefab)
+
target_link_libraries(main PUBLIC crepe)
+target_include_directories(main PRIVATE .)
diff --git a/game/Config.h b/game/Config.h
index c2f95c0..8fa41ba 100644
--- a/game/Config.h
+++ b/game/Config.h
@@ -1,6 +1,17 @@
#pragma once
#include "types.h"
+#include <crepe/api/Config.h>
+
+static const crepe::Config ENGINE_CONFIG {
+ .log {
+ .level = crepe::Log::Level::DEBUG,
+ },
+ .window_settings {
+ .window_title = "Jetpack joyride clone",
+ },
+};
+
static constexpr int SORT_IN_LAY_BACK_BACKGROUND = 3; // For all scenes
static constexpr int SORT_IN_LAY_BACKGROUND = 4; // For all scenes
static constexpr int SORT_IN_LAY_FORE_BACKGROUND = 5; // For all scenes
@@ -20,12 +31,16 @@ static constexpr int COLL_LAY_WALL_FRAGS = 5; // Only for GameScene
static constexpr int COLL_LAY_ZAPPER = 6; // Only for GameScene
static constexpr int COLL_LAY_LASER = 7; // Only for GameScene
static constexpr int COLL_LAY_MISSILE = 8; // Only for GameScene
+static constexpr int COLL_LAY_BULLET = 9; // Only for GameScene
+static constexpr int COLL_LAY_ENEMY = 10; // Only for GameScene
+static constexpr int COLL_LAY_PLAYER_BULLET = 11; // Only for GameScene
-static constexpr int GAME_HEIGHT = 800; // In game units
+static constexpr float GAME_HEIGHT = 800; // In game units
+static constexpr float HALLWAY_HEIGHT = 475; // In game units
-static constexpr int VIEWPORT_X = 1100; // In game units
+static constexpr float VIEWPORT_X = 1100; // In game units
// 'GAME_HEIGHT' (below) should be replaced by '500' when game development is finished
-static constexpr int VIEWPORT_Y = 500; // In game units
+static constexpr float VIEWPORT_Y = 500; // In game units
// Font settings
static constexpr const char * FONT = "Jetpackia";
@@ -46,5 +61,6 @@ static constexpr const char * PLAYER_NAME = "player";
static constexpr int PLAYER_SPEED = 7500; // In game units
static constexpr int PLAYER_GRAVITY_SCALE = 60; // In game units
+static constexpr const char * CAMERA_NAME = "camera";
// Jetpack particles
static constexpr const char * JETPACK_PARTICLES = "jetpack_particles";
diff --git a/game/GameScene.cpp b/game/GameScene.cpp
index c84ec24..34b0fcb 100644
--- a/game/GameScene.cpp
+++ b/game/GameScene.cpp
@@ -6,14 +6,26 @@
#include "coins/CoinSystemScript.h"
#include "background/BackgroundSubScene.h"
+#include "enemy/BattleScript.h"
+#include "enemy/EnemyBulletPool.h"
+#include "enemy/EnemyBulletSubScene.h"
+#include "enemy/EnemyPool.h"
+#include "enemy/EnemySubScene.h"
#include "hud/HudScript.h"
#include "hud/HudSubScene.h"
#include "hud/SpeedScript.h"
#include "menus/endgame/EndGameSubScene.h"
+#include "missile/MissilePool.h"
+#include "missile/SpawnEvent.h"
+#include "player/PlayerBulletPool.h"
+#include "player/PlayerBulletSubScene.h"
#include "player/PlayerSubScene.h"
+#include "scheduler/ObjectsScheduler.h"
+#include "prefab/ZapperPoolSubScene.h"
#include "workers/WorkersSubScene.h"
#include <cmath>
+#include <crepe/api/AI.h>
#include <crepe/api/Animator.h>
#include <crepe/api/Asset.h>
#include <crepe/api/AudioSource.h>
@@ -36,7 +48,7 @@ using namespace std;
void GameScene::load_scene() {
BackgroundSubScene background(*this);
- GameObject camera = new_object("camera", "camera", vec2(650, 0));
+ GameObject camera = new_object(CAMERA_NAME, "camera", vec2(650, 0));
camera.add_component<Camera>(
ivec2(990, 720), vec2(VIEWPORT_X, VIEWPORT_Y),
Camera::Data {
@@ -47,11 +59,20 @@ void GameScene::load_scene() {
camera.add_component<BehaviorScript>().set_script<CoinSystemScript>();
camera.add_component<BehaviorScript>().set_script<HudScript>();
camera.add_component<BehaviorScript>().set_script<SpeedScript>();
+ camera.add_component<BehaviorScript>().set_script<BattleScript>();
+ camera.add_component<BehaviorScript>().set_script<MissileSpawnEventHandler>();
+ camera.add_component<BehaviorScript>().set_script<ObjectsScheduler>();
camera.add_component<Rigidbody>(Rigidbody::Data {});
-
+ AI & enemy_path_1 = camera.add_component<AI>(400);
+ enemy_path_1.make_oval_path(100, 100, camera.transform.position, 1.5708, true);
+ AI & enemy_path_2 = camera.add_component<AI>(400);
+ enemy_path_2.make_oval_path(100, 100, {0, 0}, 1.5708, true);
+ AI & enemy_path_3 = camera.add_component<AI>(400);
+ enemy_path_3.make_oval_path(100, 100, {0, 0}, 1.5708, true);
+ // camer.add_component<AI>
PlayerSubScene player(*this);
-
+ MissilePool missile_pool(*this);
WorkersSubScene workers(*this);
GameObject floor = new_object("floor", "game_world", vec2(0, 325));
@@ -78,15 +99,23 @@ void GameScene::load_scene() {
});
ceiling.add_component<BoxCollider>(vec2(INFINITY, 200));
+ ZapperPoolSubScene {*this};
+
GameObject start_game_script = new_object("start_game_script", "script", vec2(0, 0));
start_game_script.add_component<BehaviorScript>().set_script<StartGameScript>();
//create coin pool
CoinPoolSubScene coin_system;
coin_system.create_coins(*this);
-
+ EnemyBulletPool enemy_bullet_pool;
+ enemy_bullet_pool.create_bullets(*this);
+ PlayerBulletPool player_bullet_pool;
+ player_bullet_pool.create_bullets(*this);
+ EnemyPool enemy_pool;
+ enemy_pool.create_enemies(*this);
HudSubScene hud;
hud.create(*this);
+
GameObject background_music = new_object("background_music", "audio", vec2(0, 0));
Asset background_music_asset {"asset/music/level.ogg"};
background_music.add_component<AudioSource>(background_music_asset);
@@ -95,56 +124,6 @@ void GameScene::load_scene() {
Asset boom_audio_asset {"asset/sfx/window_smash.ogg"};
boom_audio.add_component<AudioSource>(boom_audio_asset);
- // zapper, laser and missile (below) for testing purpose only!!!
- GameObject zapper = new_object("zapper", "zapper", vec2(1000, 200));
- Asset zapper_asset {"asset/obstacles/zapper/regular_zappers/zapEffect.png"};
- Sprite & zapper_sprite = zapper.add_component<Sprite>(
- zapper_asset,
- Sprite::Data {
- .sorting_in_layer = SORT_IN_LAY_OBSTACLES,
- .order_in_layer = 0,
- .size = vec2(100, 100),
- }
- );
- zapper.add_component<Rigidbody>(Rigidbody::Data {
- .body_type = Rigidbody::BodyType::KINEMATIC,
- .kinematic_collision = false,
- .collision_layer = COLL_LAY_ZAPPER,
- });
- zapper.add_component<BoxCollider>(vec2(100, 100));
- GameObject laser = new_object("laser", "laser", vec2(2000, 200));
- Asset laser_asset {"asset/obstacles/laser/laserPower.png"};
- Sprite & laser_sprite = laser.add_component<Sprite>(
- laser_asset,
- Sprite::Data {
- .sorting_in_layer = SORT_IN_LAY_OBSTACLES,
- .order_in_layer = 0,
- .size = vec2(100, 100),
- }
- );
- laser.add_component<Rigidbody>(Rigidbody::Data {
- .body_type = Rigidbody::BodyType::KINEMATIC,
- .kinematic_collision = false,
- .collision_layer = COLL_LAY_LASER,
- });
- laser.add_component<BoxCollider>(vec2(100, 100));
- GameObject missile = new_object("missile", "missile", vec2(4000, 200));
- Asset missile_asset {"asset/obstacles/missile/missile.png"};
- Sprite & missile_sprite = missile.add_component<Sprite>(
- missile_asset,
- Sprite::Data {
- .sorting_in_layer = SORT_IN_LAY_OBSTACLES,
- .order_in_layer = 0,
- .size = vec2(100, 100),
- }
- );
- missile.add_component<Rigidbody>(Rigidbody::Data {
- .body_type = Rigidbody::BodyType::KINEMATIC,
- .kinematic_collision = false,
- .collision_layer = COLL_LAY_MISSILE,
- });
- missile.add_component<BoxCollider>(vec2(100, 100));
-
EndGameSubScene endgamewindow;
endgamewindow.create(*this);
}
diff --git a/game/PreviewScene.cpp b/game/PreviewScene.cpp
new file mode 100644
index 0000000..6cd9e78
--- /dev/null
+++ b/game/PreviewScene.cpp
@@ -0,0 +1,99 @@
+#include "PreviewScene.h"
+
+#include "Config.h"
+#include "background/BackgroundSubScene.h"
+#include "missile/MissilePool.h"
+#include "missile/SpawnEvent.h"
+#include "preview/NpcSubScene.h"
+#include "preview/PrevPlayerSubScene.h"
+#include "preview/SmokeSubScene.h"
+
+#include "missile/MissileSubScene.h"
+
+#include <cmath>
+#include <crepe/api/Animator.h>
+#include <crepe/api/Asset.h>
+#include <crepe/api/AudioSource.h>
+#include <crepe/api/BehaviorScript.h>
+#include <crepe/api/BoxCollider.h>
+#include <crepe/api/Camera.h>
+#include <crepe/api/Color.h>
+#include <crepe/api/Event.h>
+#include <crepe/api/GameObject.h>
+#include <crepe/api/ParticleEmitter.h>
+#include <crepe/api/Rigidbody.h>
+#include <crepe/api/Script.h>
+#include <crepe/api/Sprite.h>
+#include <crepe/api/Transform.h>
+
+#include <crepe/ValueBroker.h>
+#include <crepe/manager/SaveManager.h>
+#include <crepe/types.h>
+#include <iostream>
+
+using namespace crepe;
+using namespace std;
+
+void PreviewScene::load_scene() {
+
+ BackgroundSubScene background(*this);
+
+ GameObject camera = new_object("camera", "camera", vec2(650, 0));
+ camera.add_component<Camera>(
+ ivec2(990, 720), vec2(VIEWPORT_X, VIEWPORT_Y),
+ Camera::Data {
+ .bg_color = Color::RED,
+ }
+ );
+ camera.add_component<Rigidbody>(Rigidbody::Data {});
+ camera.add_component<BehaviorScript>().set_script<MissileSpawnEventHandler>();
+
+ GameObject floor = new_object("floor", "game_world", vec2(0, 325));
+ floor.add_component<Rigidbody>(Rigidbody::Data {
+ .body_type = Rigidbody::BodyType::STATIC,
+ .collision_layer = COLL_LAY_BOT_TOP,
+ });
+ floor.add_component<BoxCollider>(vec2(INFINITY, 200));
+ GameObject floor_low = new_object("floor_low", "game_world", vec2(0, 350));
+ floor_low.add_component<Rigidbody>(Rigidbody::Data {
+ .body_type = Rigidbody::BodyType::STATIC,
+ .collision_layer = COLL_LAY_BOT_LOW,
+ });
+ floor_low.add_component<BoxCollider>(vec2(INFINITY, 200));
+ GameObject floor_high = new_object("floor_high", "game_world", vec2(0, 300));
+ floor_high.add_component<Rigidbody>(Rigidbody::Data {
+ .body_type = Rigidbody::BodyType::STATIC,
+ .collision_layer = COLL_LAY_BOT_HIGH,
+ });
+ GameObject ceiling = new_object("ceiling", "game_world", vec2(0, -325));
+ ceiling.add_component<Rigidbody>(Rigidbody::Data {
+ .body_type = Rigidbody::BodyType::STATIC,
+ .collision_layer = COLL_LAY_BOT_TOP,
+ });
+ ceiling.add_component<BoxCollider>(vec2(INFINITY, 200));
+ GameObject world = this->new_object("world", "TAG", vec2 {0, 0}, 0, 1);
+
+ world.add_component<Rigidbody>(Rigidbody::Data {
+ .body_type = Rigidbody::BodyType::STATIC,
+ .collision_layers = {0},
+ });
+
+ PrevPlayerSubScene player(*this);
+ NpcSubScene npc(*this);
+ SmokeSubScene smoke(*this);
+ MissilePool mpool(*this);
+
+ /*
+
+ for (int i = 0; i < 200; ++i) {
+ int row = i / 10;
+ int col = i % 10;
+ float x = col * 25 + i;
+ float y = row * 25 - 400;
+ GameObject game_coin = this->new_object("coin", "coin", vec2 {x, y}, 0, 1);
+ Coin coin(game_coin, vec2 {0, 0});
+ }
+ */
+}
+
+string PreviewScene::get_name() const { return "preview scene"; }
diff --git a/game/PreviewScene.h b/game/PreviewScene.h
new file mode 100644
index 0000000..afe911e
--- /dev/null
+++ b/game/PreviewScene.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include <crepe/api/Scene.h>
+#include <string>
+
+class PreviewScene : public crepe::Scene {
+public:
+ void load_scene();
+
+ std::string get_name() const;
+};
diff --git a/game/Random.cpp b/game/Random.cpp
index ace6245..64cf1f3 100644
--- a/game/Random.cpp
+++ b/game/Random.cpp
@@ -25,3 +25,5 @@ unsigned Random::u(unsigned upper, unsigned lower) {
unsigned x = rand() % range;
return x + lower;
}
+
+bool Random::b() { return rand() % 2; }
diff --git a/game/Random.h b/game/Random.h
index 8af9669..94f98d2 100644
--- a/game/Random.h
+++ b/game/Random.h
@@ -6,4 +6,5 @@ public:
static double d(double upper = 1.0, double lower = 0.0);
static int i(int upper, int lower = 0);
static unsigned u(unsigned upper, unsigned lower = 0);
+ static bool b();
};
diff --git a/game/StartGameScript.cpp b/game/StartGameScript.cpp
index e88b329..48055af 100644
--- a/game/StartGameScript.cpp
+++ b/game/StartGameScript.cpp
@@ -12,7 +12,6 @@ using namespace std;
void StartGameScript::fixed_update(crepe::duration_t dt) {
Transform & player_transform = this->get_components_by_name<Transform>("player").front();
-
// Create hole in wall and activate panic lamp
if (player_transform.position.x > 75 && !this->created_hole) {
Sprite & lamp_sprite = this->get_components_by_name<Sprite>("start_end").back();
diff --git a/game/background/CMakeLists.txt b/game/background/CMakeLists.txt
new file mode 100644
index 0000000..1d705f5
--- /dev/null
+++ b/game/background/CMakeLists.txt
@@ -0,0 +1,9 @@
+target_sources(main PUBLIC
+ AquariumSubScene.cpp
+ BackgroundSubScene.cpp
+ ForestParallaxScript.cpp
+ ForestSubScene.cpp
+ HallwaySubScene.cpp
+ StartSubScene.cpp
+)
+
diff --git a/game/enemy/BattleScript.cpp b/game/enemy/BattleScript.cpp
new file mode 100644
index 0000000..6d96ef6
--- /dev/null
+++ b/game/enemy/BattleScript.cpp
@@ -0,0 +1,51 @@
+#include "BattleScript.h"
+#include "EnemyScript.h"
+#include <crepe/api/AI.h>
+#include <crepe/api/BehaviorScript.h>
+#include <crepe/api/Metadata.h>
+using namespace std;
+using namespace crepe;
+
+BattleScript::BattleScript() { engine.seed(rd()); }
+void BattleScript::init() {
+ std::uniform_int_distribution<int> dist(2, 10);
+ int random_enemy_amount = dist(this->engine);
+ // this->create_battle(random_enemy_amount);
+ this->subscribe<BattleStartEvent>([this](const BattleStartEvent & e) -> bool {
+ return this->create_battle(e);
+ });
+}
+void BattleScript::fixed_update(duration_t dt) {
+ if (!battle_active) return;
+ bool enemies_alive = false;
+ RefVector<BehaviorScript> enemy_scripts
+ = this->get_components_by_tag<BehaviorScript>("enemy");
+
+ for (BehaviorScript & script : enemy_scripts) {
+ if (script.active) {
+ enemies_alive = true;
+ }
+ }
+ if (!enemies_alive) {
+ this->battle_active = false;
+ this->trigger_event<BattleWonEvent>();
+ }
+}
+bool BattleScript::create_battle(const BattleStartEvent & e) {
+ this->battle_active = true;
+ RefVector<BehaviorScript> enemy_scripts
+ = this->get_components_by_tag<BehaviorScript>("enemy");
+ std::uniform_real_distribution<float> dist(10, 30);
+ for (int i = 0; i < e.num_enemies; i++) {
+ BehaviorScript & script = enemy_scripts[i];
+ script.active = true;
+ this->queue_event<SpawnEnemyEvent>(
+ SpawnEnemyEvent {
+ .speed = dist(engine),
+ .column = i,
+ },
+ script.game_object_id
+ );
+ }
+ return false;
+}
diff --git a/game/enemy/BattleScript.h b/game/enemy/BattleScript.h
new file mode 100644
index 0000000..ddd0be1
--- /dev/null
+++ b/game/enemy/BattleScript.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <crepe/api/BehaviorScript.h>
+#include <crepe/api/Event.h>
+#include <crepe/api/Script.h>
+#include <random>
+struct BattleWonEvent : public crepe::Event {};
+
+struct BattleStartEvent : public crepe::Event {
+public:
+ int num_enemies = 0;
+};
+class BattleScript : public crepe::Script {
+public:
+ BattleScript();
+ void init() override;
+ void fixed_update(crepe::duration_t dt) override;
+
+private:
+ bool battle_active = false;
+ std::random_device rd;
+ std::default_random_engine engine;
+ bool create_battle(const BattleStartEvent & e);
+};
diff --git a/game/enemy/EnemyBulletPool.cpp b/game/enemy/EnemyBulletPool.cpp
new file mode 100644
index 0000000..3ee4816
--- /dev/null
+++ b/game/enemy/EnemyBulletPool.cpp
@@ -0,0 +1,11 @@
+#include "EnemyBulletPool.h"
+#include "EnemyBulletSubScene.h"
+using namespace std;
+
+void EnemyBulletPool::create_bullets(crepe::Scene & scn) {
+ EnemyBulletSubScene bullet;
+ int amount = 0;
+ while (amount < this->MAXIMUM_AMOUNT) {
+ amount = bullet.create(scn, amount);
+ }
+}
diff --git a/game/enemy/EnemyBulletPool.h b/game/enemy/EnemyBulletPool.h
new file mode 100644
index 0000000..ee53fc4
--- /dev/null
+++ b/game/enemy/EnemyBulletPool.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include <crepe/api/Scene.h>
+
+class EnemyBulletPool {
+public:
+ void create_bullets(crepe::Scene & scn);
+
+private:
+ static constexpr int MAXIMUM_AMOUNT = 20;
+};
diff --git a/game/enemy/EnemyBulletScript.cpp b/game/enemy/EnemyBulletScript.cpp
new file mode 100644
index 0000000..65c0c23
--- /dev/null
+++ b/game/enemy/EnemyBulletScript.cpp
@@ -0,0 +1,40 @@
+#include "EnemyBulletScript.h"
+#include <crepe/api/Camera.h>
+#include <crepe/api/Metadata.h>
+#include <crepe/api/Rigidbody.h>
+#include <iostream>
+
+#include "EnemyConfig.h"
+using namespace crepe;
+using namespace std;
+void EnemyBulletScript::init() {
+ this->subscribe<CollisionEvent>([this](const CollisionEvent & e) -> bool {
+ return this->on_collide(e);
+ });
+}
+void EnemyBulletScript::fixed_update(crepe::duration_t dt) {
+ Transform & transform = this->get_component<Transform>();
+ Camera & camera = this->get_components_by_name<Camera>("camera").front();
+ Transform & cam_transform = this->get_components_by_name<Transform>("camera").front();
+ Rigidbody & bullet_body = this->get_component<Rigidbody>();
+ //move
+ transform.position.x += bullet_body.data.linear_velocity.x * dt.count();
+ vec2 half_screen = camera.viewport_size / 2;
+ float despawn_location = cam_transform.position.x - half_screen.x - 50;
+ if (transform.position.x < despawn_location) {
+ this->despawn_bullet();
+ }
+}
+
+void EnemyBulletScript::despawn_bullet() {
+ Transform & transform = this->get_component<Transform>();
+ Rigidbody & bullet_body = this->get_component<Rigidbody>();
+ bullet_body.active = false;
+ transform.position = ENEMY_BULLET_POOL_LOCATION;
+}
+
+bool EnemyBulletScript::on_collide(const CollisionEvent & e) {
+ //cout << "collision happened with " << e.info.other.metadata.tag << endl;
+ this->despawn_bullet();
+ return false;
+}
diff --git a/game/enemy/EnemyBulletScript.h b/game/enemy/EnemyBulletScript.h
new file mode 100644
index 0000000..7dab751
--- /dev/null
+++ b/game/enemy/EnemyBulletScript.h
@@ -0,0 +1,11 @@
+#pragma once
+#include <crepe/api/BehaviorScript.h>
+#include <crepe/api/Script.h>
+
+class EnemyBulletScript : public crepe::Script {
+public:
+ void init() override;
+ void fixed_update(crepe::duration_t dt) override;
+ bool on_collide(const crepe::CollisionEvent & e);
+ void despawn_bullet();
+};
diff --git a/game/enemy/EnemyBulletSubScene.cpp b/game/enemy/EnemyBulletSubScene.cpp
new file mode 100644
index 0000000..5c31f1d
--- /dev/null
+++ b/game/enemy/EnemyBulletSubScene.cpp
@@ -0,0 +1,51 @@
+#include <string>
+
+#include "../Config.h"
+#include "EnemyConfig.h"
+#include <crepe/api/AI.h>
+#include <crepe/api/Animator.h>
+#include <crepe/api/BehaviorScript.h>
+#include <crepe/api/BoxCollider.h>
+#include <crepe/api/CircleCollider.h>
+#include <crepe/api/GameObject.h>
+#include <crepe/api/Rigidbody.h>
+#include <crepe/api/Scene.h>
+#include <crepe/api/Sprite.h>
+
+#include "../Random.h"
+#include "EnemyBulletScript.h"
+#include "EnemyBulletSubScene.h"
+#include "EnemyScript.h"
+using namespace crepe;
+using namespace std;
+int EnemyBulletSubScene::create(Scene & scn, int counter) {
+ string unique_name = "enemy_bullet_" + to_string(counter++);
+ GameObject bullet = scn.new_object(
+ unique_name.c_str(), "enemy_bullet", ENEMY_BULLET_POOL_LOCATION, 0, 1
+ );
+
+ Rigidbody & bullet_body = bullet.add_component<Rigidbody>(Rigidbody::Data {
+ .gravity_scale = 0,
+ .body_type = Rigidbody::BodyType::KINEMATIC,
+
+ .linear_velocity = vec2 {-250, 0},
+ .kinematic_collision = false,
+ .collision_layers = {COLL_LAY_MISSILE,COLL_LAY_ZAPPER},
+ .collision_layer = COLL_LAY_BULLET
+ });
+ bullet_body.active = false;
+ BoxCollider & bullet_collider = bullet.add_component<BoxCollider>(vec2(60, 30));
+ //bullet_collider.active = false;
+ Asset bullet_asset {"asset/other_effects/effect_smgbullet_x2.png"};
+ Sprite & bullet_sprite = bullet.add_component<Sprite>(
+ bullet_asset,
+ Sprite::Data {
+ .flip = {true, false},
+ .sorting_in_layer = SORT_IN_LAY_OBSTACLES,
+ .order_in_layer = 1,
+ .size = vec2(60, 0),
+ }
+ );
+ bullet.add_component<BehaviorScript>().set_script<EnemyBulletScript>();
+ return counter;
+}
diff --git a/game/enemy/EnemyBulletSubScene.h b/game/enemy/EnemyBulletSubScene.h
new file mode 100644
index 0000000..ac78ad9
--- /dev/null
+++ b/game/enemy/EnemyBulletSubScene.h
@@ -0,0 +1,10 @@
+#pragma once
+
+namespace crepe {
+class Scene;
+}
+
+class EnemyBulletSubScene {
+public:
+ int create(crepe::Scene & scn, int counter);
+};
diff --git a/game/enemy/EnemyConfig.h b/game/enemy/EnemyConfig.h
new file mode 100644
index 0000000..f7b660a
--- /dev/null
+++ b/game/enemy/EnemyConfig.h
@@ -0,0 +1,7 @@
+#pragma once
+#include <crepe/types.h>
+
+//button config
+// static constexpr crepe::vec2 PLAYER_BULLET_POOL_LOCATION = {0, -850};
+static constexpr crepe::vec2 ENEMY_BULLET_POOL_LOCATION = {0, -750};
+static constexpr crepe::vec2 ENEMY_POOL_LOCATION = {0, -650};
diff --git a/game/enemy/EnemyPool.cpp b/game/enemy/EnemyPool.cpp
new file mode 100644
index 0000000..a7179bf
--- /dev/null
+++ b/game/enemy/EnemyPool.cpp
@@ -0,0 +1,10 @@
+#include "EnemyPool.h"
+#include "EnemySubScene.h"
+using namespace std;
+void EnemyPool::create_enemies(crepe::Scene & scn) {
+ EnemySubScene enemy;
+ int amount = 0;
+ while (amount < this->MAXIMUM_AMOUNT) {
+ amount = enemy.create(scn, amount);
+ }
+}
diff --git a/game/enemy/EnemyPool.h b/game/enemy/EnemyPool.h
new file mode 100644
index 0000000..f4d6765
--- /dev/null
+++ b/game/enemy/EnemyPool.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include <crepe/api/Scene.h>
+
+class EnemyPool {
+public:
+ void create_enemies(crepe::Scene & scn);
+
+private:
+ static constexpr int MAXIMUM_AMOUNT = 10;
+};
diff --git a/game/enemy/EnemyScript.cpp b/game/enemy/EnemyScript.cpp
new file mode 100644
index 0000000..8cf8b80
--- /dev/null
+++ b/game/enemy/EnemyScript.cpp
@@ -0,0 +1,123 @@
+#include "EnemyScript.h"
+#include "../Config.h"
+#include "../Random.h"
+#include "EnemyConfig.h"
+#include <crepe/api/AI.h>
+#include <crepe/api/Animator.h>
+#include <crepe/api/BoxCollider.h>
+#include <crepe/api/ParticleEmitter.h>
+#include <crepe/api/Rigidbody.h>
+#include <crepe/api/Transform.h>
+#include <crepe/api/AudioSource.h>
+#include <crepe/api/Animator.h>
+#include <crepe/types.h>
+#include <random>
+using namespace crepe;
+using namespace std;
+EnemyScript::EnemyScript() {
+ engine.seed(rd());
+ this->last_fired = std::chrono::steady_clock::now();
+ this->shot_delay = std::chrono::duration<float>(3 + Random::f(1, 0));
+}
+void EnemyScript::init() {
+ Metadata & meta = this->get_component<Metadata>();
+ this->subscribe<SpawnEnemyEvent>(
+ [this](const SpawnEnemyEvent & e) -> bool { return this->spawn_enemy(e); },
+ meta.game_object_id
+ );
+ this->subscribe<CollisionEvent>([this](const CollisionEvent & e) -> bool {
+ return this->on_collide(e);
+ });
+};
+void EnemyScript::fixed_update(duration_t dt) {
+ if (this->alive) {
+ return;
+ }
+ Transform & transform = this->get_component<Transform>();
+ Transform & player_transform = this->get_components_by_name<Transform>("player").front();
+ Rigidbody & enemy_body = this->get_component<Rigidbody>();
+ AI & ai_component = this->get_component<AI>();
+
+ //transform.position += enemy_body.data.linear_velocity * dt.count();
+ float direction_to_player_y = player_transform.position.y - transform.position.y;
+ float distance_to_player_y = std::abs(direction_to_player_y);
+
+ float adjustment_speed = speed * (distance_to_player_y / MAX_DISTANCE);
+
+ adjustment_speed = std::clamp(adjustment_speed, MIN_SPEED, MAX_SPEED);
+ // Move the path nodes on the Y-axis
+ for (vec2 & path_node : ai_component.path) {
+ path_node.y += (direction_to_player_y > 0 ? 1 : -1) * adjustment_speed * dt.count();
+ }
+ //bullet fire logic:
+ auto now = std::chrono::steady_clock::now();
+ std::chrono::duration<float> elapsed = now - last_fired;
+ if (elapsed > shot_delay) {
+ this->shoot(transform.position, 0);
+ last_fired = now;
+ this->shot_delay = std::chrono::duration<float>(Random::f(4, 1));
+ }
+}
+bool EnemyScript::spawn_enemy(const SpawnEnemyEvent & e) {
+ this->speed = e.speed;
+ AI & ai_component = this->get_component<AI>();
+ Transform & transform = this->get_component<Transform>();
+ Camera & camera = this->get_components_by_name<Camera>("camera").front();
+ Transform & cam_transform = this->get_components_by_name<Transform>("camera").front();
+
+ vec2 half_screen = camera.viewport_size / 2;
+ float x_value = cam_transform.position.x + half_screen.x - 50 * (1 + e.column);
+ uniform_real_distribution<float> dist(
+ cam_transform.position.y - half_screen.y + 100,
+ cam_transform.position.y + half_screen.y - 100
+ );
+ float random_height = dist(engine);
+ vec2 spawn_location
+ = {cam_transform.position.x + camera.viewport_size.x / 2 + 100, random_height};
+ transform.position = spawn_location;
+ ai_component.path.clear();
+ ai_component.make_oval_path(10, 10, vec2 {x_value, random_height}, 1.5708, true);
+ ai_component.active = true;
+ this->last_fired = std::chrono::steady_clock::now();
+ return false;
+}
+
+bool EnemyScript::on_collide(const CollisionEvent & e) {
+ if (e.info.other.metadata.tag == "player_bullet") {
+ this->despawn_enemy();
+ }
+ Animator& body_animator = this->get_components<Animator>().front();
+ body_animator.data.col = 2;
+ //body_animator.play();
+ BehaviorScript & enemy_script = this->get_component<BehaviorScript>();
+ enemy_script.active = false;
+ return false;
+}
+void EnemyScript::despawn_enemy() {
+ Transform & transform = this->get_component<Transform>();
+ transform.position = ENEMY_POOL_LOCATION;
+ AI & ai_component = this->get_component<AI>();
+ // Rigidbody& enemy_body
+ ai_component.active = false;
+}
+void EnemyScript::shoot(const vec2 & location, float angle) {
+ RefVector<Transform> bullet_transforms
+ = this->get_components_by_tag<Transform>("enemy_bullet");
+
+ for (Transform & bullet_pos : bullet_transforms) {
+ if (bullet_pos.position.x == 0 && bullet_pos.position.y == -750) {
+
+ bullet_pos.position = location;
+ bullet_pos.position.x -= 20;
+ Rigidbody & bullet_body
+ = this->get_components_by_id<Rigidbody>(bullet_pos.game_object_id).front();
+ BoxCollider bullet_collider
+ = this->get_components_by_id<BoxCollider>(bullet_pos.game_object_id).front();
+ bullet_collider.active = true;
+ bullet_body.active = true;
+ AudioSource& audio = this->get_component<AudioSource>();
+ audio.play();
+ return;
+ }
+ }
+}
diff --git a/game/enemy/EnemyScript.h b/game/enemy/EnemyScript.h
new file mode 100644
index 0000000..42ecac4
--- /dev/null
+++ b/game/enemy/EnemyScript.h
@@ -0,0 +1,31 @@
+#pragma once
+#include <chrono>
+#include <crepe/api/Camera.h>
+#include <crepe/api/Event.h>
+#include <crepe/api/Script.h>
+#include <random>
+struct SpawnEnemyEvent : public crepe::Event {
+ float speed = 0;
+ int column = 0;
+};
+class EnemyScript : public crepe::Script {
+public:
+ EnemyScript();
+ void init() override;
+ void fixed_update(crepe::duration_t dt) override;
+ void shoot(const crepe::vec2 & position, float angle);
+ bool on_collide(const crepe::CollisionEvent & collisionData);
+ void despawn_enemy();
+ bool spawn_enemy(const SpawnEnemyEvent & e);
+
+private:
+ std::random_device rd;
+ std::default_random_engine engine;
+ bool alive = false;
+ float speed = 50;
+ const float MIN_SPEED = 10;
+ const float MAX_SPEED = 130;
+ const float MAX_DISTANCE = 100;
+ std::chrono::time_point<std::chrono::steady_clock> last_fired;
+ std::chrono::duration<float> shot_delay = std::chrono::duration<float>(0);
+};
diff --git a/game/enemy/EnemySubScene.cpp b/game/enemy/EnemySubScene.cpp
new file mode 100644
index 0000000..8316db9
--- /dev/null
+++ b/game/enemy/EnemySubScene.cpp
@@ -0,0 +1,102 @@
+#include <string>
+
+#include <crepe/api/AI.h>
+#include <crepe/api/Animator.h>
+#include <crepe/api/BehaviorScript.h>
+#include <crepe/api/BoxCollider.h>
+#include <crepe/api/CircleCollider.h>
+#include <crepe/api/GameObject.h>
+#include <crepe/api/Rigidbody.h>
+#include <crepe/api/Scene.h>
+#include <crepe/api/AudioSource.h>
+#include <crepe/api/Sprite.h>
+
+#include "../Config.h"
+#include "EnemyConfig.h"
+#include "EnemyScript.h"
+#include "EnemySubScene.h"
+using namespace crepe;
+using namespace std;
+//#TODO add sound
+int EnemySubScene::create(Scene & scn, int enemy_counter) {
+
+ string unique_name = "enemy_" + to_string(enemy_counter++);
+ GameObject enemy = scn.new_object(unique_name.c_str(), "enemy", ENEMY_POOL_LOCATION, 0, 1);
+
+ enemy.add_component<Rigidbody>(Rigidbody::Data {
+ .gravity_scale = 0,
+ .body_type = Rigidbody::BodyType::DYNAMIC,
+ .max_linear_velocity = 400,
+ .collision_layers = {COLL_LAY_BOT_TOP, COLL_LAY_PLAYER_BULLET},
+ .collision_layer = COLL_LAY_ENEMY,
+
+ });
+ Asset enemy_body_asset {"asset/workers/worker2Body.png"};
+ enemy.add_component<BoxCollider>(vec2(50, 50));
+ Sprite & enemy_body_sprite = enemy.add_component<Sprite>(
+ enemy_body_asset,
+ Sprite::Data {
+ .flip = {true, false},
+ .sorting_in_layer = SORT_IN_LAY_WORKERS_FRONT,
+ .order_in_layer = 0,
+ .size = vec2(0, 50),
+ }
+ );
+ Animator & body_animator = enemy.add_component<Animator>(
+ enemy_body_sprite, ivec2(32, 32), uvec2(4, 8),
+ Animator::Data {
+ .fps = 5,
+ .col = 1,
+ .row = 0,
+ .looping = false,
+ }
+ );
+ body_animator.pause();
+ enemy.add_component<BoxCollider>(vec2(40, 60), vec2(-20, 0));
+ Asset enemy_head_asset {"asset/workers/worker2Head.png"};
+ Sprite & enemy_head_sprite = enemy.add_component<Sprite>(
+ enemy_head_asset,
+ Sprite::Data {
+ .flip = {true, false},
+ .sorting_in_layer = SORT_IN_LAY_WORKERS_FRONT,
+ .order_in_layer = 1,
+ .size = vec2(0, 50),
+ .position_offset = vec2(0, -20),
+ }
+ );
+ enemy.add_component<Animator>(
+ enemy_head_sprite, ivec2(32, 32), uvec2(4, 8),
+ Animator::Data {
+ .fps = 5,
+ .looping = true,
+ }
+ );
+ enemy.add_component<CircleCollider>(25, vec2(0, -20));
+ Asset enemy_jetpack_asset {"asset/barry/jetpackDefault.png"};
+ Sprite & enemy_jetpack_sprite = enemy.add_component<Sprite>(
+ enemy_jetpack_asset,
+ Sprite::Data {
+ .flip = {true, false},
+ .sorting_in_layer = SORT_IN_LAY_WORKERS_FRONT,
+ .order_in_layer = 2,
+ .size = vec2(0, 60),
+ .position_offset = vec2(20, 0),
+ }
+ );
+ enemy_jetpack_sprite.active = true;
+ enemy.add_component<Animator>(
+ enemy_jetpack_sprite, ivec2(32, 44), uvec2(4, 4),
+ Animator::Data {
+ .fps = 5,
+ .looping = true,
+ }
+ );
+ enemy.add_component<AudioSource>(Asset("asset/sfx/bike_gun_2.ogg")).volume
+ = 0.1;
+ AI & ai_component = enemy.add_component<AI>(3000);
+ ai_component.path_follow_on();
+ BehaviorScript & enemy_script
+ = enemy.add_component<BehaviorScript>().set_script<EnemyScript>();
+ enemy_script.active = false;
+ return enemy_counter;
+}
diff --git a/game/enemy/EnemySubScene.h b/game/enemy/EnemySubScene.h
new file mode 100644
index 0000000..3899250
--- /dev/null
+++ b/game/enemy/EnemySubScene.h
@@ -0,0 +1,10 @@
+#pragma once
+
+namespace crepe {
+class Scene;
+}
+
+class EnemySubScene {
+public:
+ int create(crepe::Scene & scn, int enemy_counter);
+};
diff --git a/game/main.cpp b/game/main.cpp
index e341353..14eec99 100644
--- a/game/main.cpp
+++ b/game/main.cpp
@@ -1,17 +1,26 @@
+#include <cstdlib>
+
#include <crepe/api/Engine.h>
#include <crepe/api/Script.h>
+#include "Config.h"
#include "GameScene.h"
+#include "PreviewScene.h"
#include "menus/mainmenu/MainMenuScene.h"
#include "menus/shop/ShopMenuScene.h"
using namespace crepe;
int main() {
+ srand(time(NULL));
+
+ Config::get_instance() = ENGINE_CONFIG;
+
Engine gameloop;
gameloop.add_scene<MainMenuScene>();
gameloop.add_scene<ShopMenuScene>();
gameloop.add_scene<GameScene>();
+ gameloop.add_scene<PreviewScene>();
return gameloop.main();
}
diff --git a/game/menus/MenusConfig.h b/game/menus/MenusConfig.h
index 6ec5689..3e357a5 100644
--- a/game/menus/MenusConfig.h
+++ b/game/menus/MenusConfig.h
@@ -3,10 +3,9 @@
//generic menu config
static constexpr int STARTING_SORTING_IN_LAYER = 7;
-static constexpr const char * CAMERA_NAME = "camera";
//Scene names
static constexpr const char * START_SCENE = "scene1";
-static constexpr const char * PREVIEW_SCENE = "scene1";
+static constexpr const char * PREVIEW_SCENE = "preview scene";
static constexpr const char * SHOP_SCENE = "shopmenu";
static constexpr const char * MAINMENU_SCENE = "mainmenu";
//button config
diff --git a/game/menus/mainmenu/ITransitionScript.cpp b/game/menus/mainmenu/ITransitionScript.cpp
index cd929a0..54b0875 100644
--- a/game/menus/mainmenu/ITransitionScript.cpp
+++ b/game/menus/mainmenu/ITransitionScript.cpp
@@ -1,6 +1,7 @@
#include "ITransitionScript.h"
#include "MainMenuConfig.h"
+#include "../../Config.h"
#include "../MenusConfig.h"
#include <crepe/api/Camera.h>
diff --git a/game/menus/shop/ShopMenuScene.cpp b/game/menus/shop/ShopMenuScene.cpp
index d1ea81d..5ada0d3 100644
--- a/game/menus/shop/ShopMenuScene.cpp
+++ b/game/menus/shop/ShopMenuScene.cpp
@@ -6,6 +6,7 @@
#include "../ButtonSubScene.h"
#include "../MenusConfig.h"
#include "types.h"
+#include "../../Config.h"
#include <crepe/api/Camera.h>
#include <crepe/api/Sprite.h>
diff --git a/game/missile/MissilePool.cpp b/game/missile/MissilePool.cpp
new file mode 100644
index 0000000..e549210
--- /dev/null
+++ b/game/missile/MissilePool.cpp
@@ -0,0 +1,16 @@
+#include "MissilePool.h"
+#include "MissileSubScene.h"
+
+#include <crepe/api/Scene.h>
+
+using namespace std;
+using namespace crepe;
+
+MissilePool::MissilePool(Scene & scn) {
+ int amount = 0;
+ MissileSubScene missile;
+ while (amount < this->MAX_MISSILE_COUNT) {
+ missile.create(scn);
+ amount++;
+ }
+}
diff --git a/game/missile/MissilePool.h b/game/missile/MissilePool.h
new file mode 100644
index 0000000..296701e
--- /dev/null
+++ b/game/missile/MissilePool.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include <crepe/api/Scene.h>
+
+class MissilePool {
+public:
+ MissilePool(crepe::Scene & scn);
+
+private:
+ static constexpr unsigned int MAX_MISSILE_COUNT = 5;
+};
diff --git a/game/missile/MissileScript.cpp b/game/missile/MissileScript.cpp
new file mode 100644
index 0000000..6d0e40e
--- /dev/null
+++ b/game/missile/MissileScript.cpp
@@ -0,0 +1,105 @@
+#include "MissileScript.h"
+#include "../Config.h"
+#include "api/BehaviorScript.h"
+
+#include <crepe/api/Animator.h>
+#include <crepe/api/AudioSource.h>
+#include <crepe/api/Transform.h>
+#include <crepe/system/CollisionSystem.h>
+#include <crepe/types.h>
+
+#include <cmath>
+#include <crepe/api/AI.h>
+#include <crepe/api/KeyCodes.h>
+#include <crepe/api/Sprite.h>
+
+using namespace std;
+using namespace crepe;
+
+void MissileScript::init() {
+ subscribe<CollisionEvent>([this](const CollisionEvent & ev) -> bool {
+ return this->on_collision(ev);
+ });
+ this->seeking_disabled = false;
+}
+void MissileScript::kill_missile() {
+ auto animations = this->get_components<Animator>();
+ auto sprites = this->get_components<Sprite>();
+ auto & fly_sound = this->get_components<AudioSource>().front().get();
+ auto & this_script = this->get_components<BehaviorScript>().back().get();
+
+ animations[0].get().active = false;
+ animations[1].get().active = false;
+ animations[2].get().active = true;
+ sprites[0].get().active = false;
+ sprites[1].get().active = false;
+ sprites[2].get().active = true;
+
+ this_script.active = false;
+ this->seeking_disabled = false;
+
+ fly_sound.stop();
+}
+void MissileScript::activate() {
+ auto anim = this->get_components<Animator>();
+ auto sprites = this->get_components<Sprite>();
+
+ anim[0].get().active = true;
+ anim[1].get().active = true;
+ anim[2].get().stop();
+ sprites[0].get().active = true;
+ sprites[1].get().active = true;
+ sprites[2].get().active = false;
+}
+
+bool MissileScript::on_collision(const CollisionEvent & ev) {
+ auto & explosion_sound = this->get_components<AudioSource>().back().get();
+
+ this->kill_missile();
+ explosion_sound.play();
+
+ return false;
+}
+
+bool MissileScript::is_in_x_range(const Transform & missile, const Transform & player) {
+ return fabs(missile.position.x - player.position.x) <= this->X_RANGE;
+}
+
+void MissileScript::fixed_update(crepe::duration_t dt) {
+ auto & explosion_anim = this->get_components<Animator>().back().get();
+ auto & missile = this->get_component<Transform>();
+ auto & m_ai = this->get_component<AI>();
+
+ const auto & player = this->get_components_by_name<Transform>("player").front().get();
+ const auto & cam = this->get_components_by_name<Transform>("camera").front().get();
+ const auto & velocity = this->get_component<Rigidbody>().data.linear_velocity;
+
+ if (missile.position.x < (cam.position.x - VIEWPORT_X / 1.8)) {
+ this->kill_missile();
+ return;
+ }
+
+ // check if animation is at the end
+ if (explosion_anim.data.row == 7) {
+ this->activate();
+ this->seeking_disabled = false;
+ }
+
+ if (this->seeking_disabled) {
+ m_ai.seek_off();
+ } else {
+ m_ai.seek_target = player.position;
+ m_ai.seek_on();
+
+ if (is_in_x_range(missile, player)) {
+ this->seeking_disabled = true;
+ m_ai.seek_off();
+ }
+ }
+
+ vec2 angle_pos = velocity;
+ float angle = atan2(angle_pos.y, angle_pos.x) * (180 / M_PI);
+
+ missile.rotation = angle;
+ missile.position += velocity * dt.count();
+}
diff --git a/game/missile/MissileScript.h b/game/missile/MissileScript.h
new file mode 100644
index 0000000..a492e18
--- /dev/null
+++ b/game/missile/MissileScript.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include <crepe/api/Script.h>
+
+class MissileScript : public crepe::Script {
+private:
+ bool on_collision(const crepe::CollisionEvent & ev);
+
+ bool seeking_disabled;
+
+ // will be used to calculate when ai will be stopped
+ static constexpr int X_RANGE = 90;
+ bool is_in_x_range(const crepe::Transform & missile, const crepe::Transform & player);
+ void kill_missile();
+ void activate();
+
+public:
+ void init();
+ void fixed_update(crepe::duration_t dt);
+};
diff --git a/game/missile/MissileSubScene.cpp b/game/missile/MissileSubScene.cpp
new file mode 100644
index 0000000..db49f88
--- /dev/null
+++ b/game/missile/MissileSubScene.cpp
@@ -0,0 +1,101 @@
+#include "MissileSubScene.h"
+#include "../Config.h"
+#include "../missile/MissileScript.h"
+
+#include <crepe/api/AI.h>
+#include <crepe/api/Animator.h>
+#include <crepe/api/AudioSource.h>
+#include <crepe/api/BehaviorScript.h>
+#include <crepe/api/CircleCollider.h>
+#include <crepe/api/Scene.h>
+#include <crepe/api/Sprite.h>
+#include <crepe/types.h>
+#include <random>
+
+using namespace crepe;
+
+void MissileSubScene::create(crepe::Scene & scn) {
+ std::random_device rd;
+ std::mt19937 gen(rd());
+
+ GameObject missle = scn.new_object("missile", "missile", {0, 0}, 0, 1);
+
+ Asset missle_ss {"asset/obstacles/missile/missile.png"};
+ Asset missle_thruster_ss {"asset/obstacles/missile/missileEffects.png"};
+ Asset missile_explosion_ss {"asset/obstacles/missile/missileExplosion.png"};
+ Asset explosion_sound {"asset/sfx/rocket_explode_1.ogg"};
+ Asset missile_fire {"asset/sfx/missile_launch.ogg"};
+
+ missle.add_component<BehaviorScript>().set_script<MissileScript>().active = false;
+
+ auto & sound = missle.add_component<AudioSource>(missile_fire);
+ sound.volume = 0.1;
+ auto & sound2 = missle.add_component<AudioSource>(explosion_sound);
+ sound2.volume = 0.1;
+
+ // sprites
+ auto & missle_sprite = missle.add_component<Sprite>(
+ missle_ss,
+ Sprite::Data {
+ .flip = {true, false},
+ .sorting_in_layer = SORT_IN_LAY_OBSTACLES,
+ .size = {0, 35},
+ }
+ );
+
+ auto & missle_thruster_sprite = missle.add_component<Sprite>(
+ missle_thruster_ss,
+ Sprite::Data {
+ .flip = {true, false},
+ .sorting_in_layer = SORT_IN_LAY_OBSTACLES,
+ .size = {0, 35},
+ .position_offset = {-20, 0},
+ }
+ );
+
+ auto & missile_explosion_sprite = missle.add_component<Sprite>(
+ missile_explosion_ss,
+ Sprite::Data {
+ .sorting_in_layer = SORT_IN_LAY_OBSTACLES,
+ .size = {0, 50},
+ }
+ );
+
+ // Animations
+ missle.add_component<Animator>(
+ missle_sprite, ivec2 {32, 32}, uvec2 {4, 1},
+ Animator::Data {
+ .looping = true,
+ }
+ );
+
+ missle.add_component<Animator>(
+ missle_thruster_sprite, ivec2 {64, 64}, uvec2 {4, 2},
+ Animator::Data {
+ .looping = true,
+ }
+ );
+
+ auto & explosion_anim = missle.add_component<Animator>(
+ missile_explosion_sprite, ivec2 {64, 64}, uvec2 {8, 1},
+ Animator::Data {
+ .fps = 10,
+ }
+ );
+
+ missile_explosion_sprite.active = false;
+ explosion_anim.active = false;
+
+ std::uniform_int_distribution<> dist(140, 200);
+ missle.add_component<Rigidbody>(Rigidbody::Data {
+ .body_type = Rigidbody::BodyType::KINEMATIC,
+ .max_linear_velocity = static_cast<float>(dist(gen)),
+ .kinematic_collision = false,
+ .collision_layers = {COLL_LAY_PLAYER, COLL_LAY_BOT_TOP},
+ .collision_layer = COLL_LAY_MISSILE,
+ });
+
+ missle.add_component<CircleCollider>(3);
+
+ auto & missle_ai = missle.add_component<AI>(1000);
+}
diff --git a/game/missile/MissileSubScene.h b/game/missile/MissileSubScene.h
new file mode 100644
index 0000000..9ea422a
--- /dev/null
+++ b/game/missile/MissileSubScene.h
@@ -0,0 +1,12 @@
+#pragma once
+
+namespace crepe {
+class Scene;
+}
+
+class MissileSubScene {
+public:
+ MissileSubScene() = default;
+
+ void create(crepe::Scene & scn);
+};
diff --git a/game/missile/SpawnEvent.cpp b/game/missile/SpawnEvent.cpp
new file mode 100644
index 0000000..03a9b8c
--- /dev/null
+++ b/game/missile/SpawnEvent.cpp
@@ -0,0 +1,47 @@
+#include "SpawnEvent.h"
+
+#include <crepe/api/Animator.h>
+#include <crepe/api/AudioSource.h>
+#include <crepe/api/BehaviorScript.h>
+#include <crepe/api/Camera.h>
+#include <crepe/api/Sprite.h>
+#include <crepe/api/Transform.h>
+
+#include <cstdlib>
+#include <random>
+
+using namespace crepe;
+
+void MissileSpawnEventHandler::init() {
+ subscribe<MissileSpawnEvent>([this](const MissileSpawnEvent & ev) -> bool {
+ return this->on_event(ev);
+ });
+}
+
+std::random_device rd;
+std::mt19937 gen(rd());
+
+bool MissileSpawnEventHandler::on_event(const MissileSpawnEvent & event) {
+ auto missile_sprites = this->get_components_by_name<Sprite>("missile");
+ auto missile_transforms = this->get_components_by_name<Transform>("missile");
+ auto missile_behaviorscripts = this->get_components_by_name<BehaviorScript>("missile");
+ auto missile_audiosources = this->get_components_by_name<AudioSource>("missile");
+ auto & camera_transform = this->get_components_by_name<Transform>("camera").front().get();
+
+ for (size_t i = 0; i < missile_behaviorscripts.size(); ++i) {
+ auto & script = missile_behaviorscripts[i].get();
+ if (script.active) continue;
+ script.active = true;
+
+ missile_audiosources[i * 2].get().play();
+
+ auto & transform = missile_transforms[i].get();
+ transform.position.x = camera_transform.position.x + this->MISSILE_OFFSET;
+ std::uniform_int_distribution<> dist(this->MIN_RANGE, this->MAX_RANGE);
+ transform.position.y = dist(gen);
+
+ break;
+ }
+
+ return false;
+}
diff --git a/game/missile/SpawnEvent.h b/game/missile/SpawnEvent.h
new file mode 100644
index 0000000..ce301fd
--- /dev/null
+++ b/game/missile/SpawnEvent.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include <crepe/api/Event.h>
+#include <crepe/api/Script.h>
+
+#include "../Config.h"
+
+struct MissileSpawnEvent : public crepe::Event {};
+
+class MissileSpawnEventHandler : public crepe::Script {
+private:
+ static constexpr int MISSILE_OFFSET = VIEWPORT_X / 1.8;
+ static constexpr int RANGE = GAME_HEIGHT / 4;
+ static constexpr int MIN_RANGE = -RANGE;
+ static constexpr int MAX_RANGE = RANGE;
+
+public:
+ void init();
+ bool on_event(const MissileSpawnEvent & ev);
+};
diff --git a/game/player/PlayerBulletPool.cpp b/game/player/PlayerBulletPool.cpp
new file mode 100644
index 0000000..5285ec8
--- /dev/null
+++ b/game/player/PlayerBulletPool.cpp
@@ -0,0 +1,11 @@
+#include "PlayerBulletPool.h"
+#include "PlayerBulletSubScene.h"
+using namespace std;
+
+void PlayerBulletPool::create_bullets(crepe::Scene & scn) {
+ PlayerBulletSubScene bullet;
+ int amount = 0;
+ while (amount < this->MAXIMUM_AMOUNT) {
+ amount = bullet.create(scn, amount);
+ }
+}
diff --git a/game/player/PlayerBulletPool.h b/game/player/PlayerBulletPool.h
new file mode 100644
index 0000000..9618d54
--- /dev/null
+++ b/game/player/PlayerBulletPool.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include <crepe/api/Scene.h>
+
+class PlayerBulletPool {
+public:
+ void create_bullets(crepe::Scene & scn);
+
+private:
+ static constexpr int MAXIMUM_AMOUNT = 20;
+};
diff --git a/game/player/PlayerBulletScript.cpp b/game/player/PlayerBulletScript.cpp
new file mode 100644
index 0000000..a76b7eb
--- /dev/null
+++ b/game/player/PlayerBulletScript.cpp
@@ -0,0 +1,41 @@
+
+#include <crepe/api/Camera.h>
+#include <crepe/api/Metadata.h>
+#include <crepe/api/Rigidbody.h>
+
+#include "PlayerBulletScript.h"
+
+using namespace crepe;
+using namespace std;
+void PlayerBulletScript::init() {
+ this->subscribe<CollisionEvent>([this](const CollisionEvent & e) -> bool {
+ return this->on_collide(e);
+ });
+}
+void PlayerBulletScript::fixed_update(crepe::duration_t dt) {
+ Transform & transform = this->get_component<Transform>();
+ Camera & camera = this->get_components_by_name<Camera>("camera").front();
+ Transform & cam_transform = this->get_components_by_name<Transform>("camera").front();
+ Rigidbody & bullet_body = this->get_component<Rigidbody>();
+ transform.rotation += bullet_body.data.angular_velocity;
+ transform.position += bullet_body.data.linear_velocity * dt.count();
+ vec2 half_screen = camera.viewport_size / 2;
+ float despawn_location = cam_transform.position.x + half_screen.x + 50;
+ if (transform.position.x > despawn_location) {
+ this->despawn_bullet();
+ }
+}
+
+void PlayerBulletScript::despawn_bullet() {
+ Transform & transform = this->get_component<Transform>();
+ Rigidbody & bullet_body = this->get_component<Rigidbody>();
+ bullet_body.active = false;
+ BehaviorScript & bullet_script = this->get_component<BehaviorScript>();
+ bullet_script.active = false;
+ transform.position = {0, -850};
+}
+
+bool PlayerBulletScript::on_collide(const CollisionEvent & e) {
+ this->despawn_bullet();
+ return false;
+}
diff --git a/game/player/PlayerBulletScript.h b/game/player/PlayerBulletScript.h
new file mode 100644
index 0000000..0637790
--- /dev/null
+++ b/game/player/PlayerBulletScript.h
@@ -0,0 +1,11 @@
+#pragma once
+#include <crepe/api/BehaviorScript.h>
+#include <crepe/api/Script.h>
+
+class PlayerBulletScript : public crepe::Script {
+public:
+ void init() override;
+ void fixed_update(crepe::duration_t dt) override;
+ bool on_collide(const crepe::CollisionEvent & e);
+ void despawn_bullet();
+};
diff --git a/game/player/PlayerBulletSubScene.cpp b/game/player/PlayerBulletSubScene.cpp
new file mode 100644
index 0000000..2d237de
--- /dev/null
+++ b/game/player/PlayerBulletSubScene.cpp
@@ -0,0 +1,52 @@
+#include <string>
+
+#include "../Config.h"
+#include <crepe/api/AI.h>
+#include <crepe/api/Animator.h>
+#include <crepe/api/BehaviorScript.h>
+#include <crepe/api/BoxCollider.h>
+#include <crepe/api/CircleCollider.h>
+#include <crepe/api/GameObject.h>
+#include <crepe/api/Rigidbody.h>
+#include <crepe/api/Scene.h>
+#include <crepe/api/Sprite.h>
+
+#include "PlayerBulletScript.h"
+#include "PlayerBulletSubScene.h"
+#include "PlayerScript.h"
+using namespace crepe;
+using namespace std;
+int PlayerBulletSubScene::create(Scene & scn, int counter) {
+ string unique_name = "player_bullet_" + to_string(counter++);
+ GameObject player_bullet
+ = scn.new_object(unique_name.c_str(), "player_bullet", vec2 {0, -850}, 0, 1);
+
+ Rigidbody & player_bullet_body = player_bullet.add_component<Rigidbody>(Rigidbody::Data {
+ .gravity_scale = 0,
+ .body_type = Rigidbody::BodyType::KINEMATIC,
+ .linear_velocity = vec2 {400, 0},
+ .angular_velocity = 10,
+ .kinematic_collision = false,
+ .collision_layers = {COLL_LAY_ENEMY,COLL_LAY_ZAPPER},
+
+ .collision_layer = COLL_LAY_PLAYER_BULLET,
+
+ });
+ player_bullet_body.active = false;
+ BoxCollider & player_bullet_collider
+ = player_bullet.add_component<BoxCollider>(vec2(30, 30));
+
+ Asset player_bullet_asset {"asset/other_effects/crepe.png"};
+ Sprite & player_bullet_sprite = player_bullet.add_component<Sprite>(
+ player_bullet_asset,
+ Sprite::Data {
+ .flip = {true, false},
+ .sorting_in_layer = SORT_IN_LAY_OBSTACLES,
+ .order_in_layer = 1,
+ .size = vec2(30, 0),
+ }
+ );
+ player_bullet.add_component<BehaviorScript>().set_script<PlayerBulletScript>().active
+ = false;
+ return counter;
+}
diff --git a/game/player/PlayerBulletSubScene.h b/game/player/PlayerBulletSubScene.h
new file mode 100644
index 0000000..72eda62
--- /dev/null
+++ b/game/player/PlayerBulletSubScene.h
@@ -0,0 +1,10 @@
+#pragma once
+
+namespace crepe {
+class Scene;
+}
+
+class PlayerBulletSubScene {
+public:
+ int create(crepe::Scene & scn, int counter);
+};
diff --git a/game/player/PlayerScript.cpp b/game/player/PlayerScript.cpp
index d45a519..57819c0 100644
--- a/game/player/PlayerScript.cpp
+++ b/game/player/PlayerScript.cpp
@@ -1,9 +1,10 @@
#include "PlayerScript.h"
#include "../Config.h"
-
+#include "../enemy/BattleScript.h"
#include <crepe/api/Animator.h>
#include <crepe/api/AudioSource.h>
+#include <crepe/api/BoxCollider.h>
#include <crepe/api/ParticleEmitter.h>
#include <crepe/api/Rigidbody.h>
#include <crepe/api/Transform.h>
@@ -16,8 +17,8 @@ void PlayerScript::init() {
subscribe<CollisionEvent>([this](const CollisionEvent & ev) -> bool {
return this->on_collision(ev);
});
+ this->last_fired = std::chrono::steady_clock::now();
}
-
bool PlayerScript::on_collision(const CollisionEvent & ev) {
BehaviorScript & play_scr = this->get_components_by_name<BehaviorScript>("player").front();
BehaviorScript & end_scr = this->get_components_by_name<BehaviorScript>("player").back();
@@ -59,7 +60,8 @@ bool PlayerScript::on_collision(const CollisionEvent & ev) {
audio.play();
return false;
- } else if (ev.info.other.metadata.tag == "missile") {
+ } else if (ev.info.other.metadata.tag == "missile"
+ || ev.info.other.metadata.tag == "enemy_bullet") {
for (Animator & anim : animators) {
anim.active = true;
anim.set_anim(5);
@@ -92,6 +94,15 @@ void PlayerScript::fixed_update(crepe::duration_t dt) {
}
Rigidbody & rb = this->get_components_by_name<Rigidbody>("player").front();
+ if (this->get_key_state(Keycode::ENTER)) {
+
+ auto now = std::chrono::steady_clock::now();
+ std::chrono::duration<float> elapsed = now - last_fired;
+ if (elapsed > shot_delay) {
+ this->shoot(transform.position, 0);
+ last_fired = now;
+ }
+ }
if (this->get_key_state(Keycode::SPACE)) {
rb.add_force_linear(vec2(0, -PLAYER_GRAVITY_SCALE / 2.5) * dt.count() / 0.02);
if (prev_anim != 1) {
@@ -139,3 +150,26 @@ void PlayerScript::fixed_update(crepe::duration_t dt) {
}
}
}
+
+void PlayerScript::shoot(const vec2 & location, float angle) {
+ RefVector<Transform> bullet_transforms
+ = this->get_components_by_tag<Transform>("player_bullet");
+
+ for (Transform & bullet_pos : bullet_transforms) {
+ if (bullet_pos.position.x == 0 && bullet_pos.position.y == -850) {
+
+ bullet_pos.position = location;
+ bullet_pos.position.x += 20;
+ Rigidbody & bullet_body
+ = this->get_components_by_id<Rigidbody>(bullet_pos.game_object_id).front();
+ BoxCollider bullet_collider
+ = this->get_components_by_id<BoxCollider>(bullet_pos.game_object_id).front();
+ bullet_body.active = true;
+ BehaviorScript & bullet_script
+ = this->get_components_by_id<BehaviorScript>(bullet_pos.game_object_id)
+ .front();
+ bullet_script.active = true;
+ return;
+ }
+ }
+}
diff --git a/game/player/PlayerScript.h b/game/player/PlayerScript.h
index 482b40d..e7d860a 100644
--- a/game/player/PlayerScript.h
+++ b/game/player/PlayerScript.h
@@ -1,8 +1,8 @@
#pragma once
+#include <chrono>
#include <crepe/api/Event.h>
#include <crepe/api/Script.h>
-
class PlayerScript : public crepe::Script {
public:
void init();
@@ -10,8 +10,13 @@ public:
private:
bool on_collision(const crepe::CollisionEvent & ev);
+ // bool on_key_up(const crepe::KeyReleaseEvent& ev);
+ void shoot(const crepe::vec2 & location, float angle);
private:
int prev_anim = 0;
+ std::chrono::time_point<std::chrono::steady_clock> last_fired;
+ std::chrono::duration<float> shot_delay = std::chrono::duration<float>(0.5);
+
int current_jetpack_sound = 0;
};
diff --git a/game/player/PlayerSubScene.cpp b/game/player/PlayerSubScene.cpp
index f136605..c4d689a 100644
--- a/game/player/PlayerSubScene.cpp
+++ b/game/player/PlayerSubScene.cpp
@@ -149,7 +149,8 @@ PlayerSubScene::PlayerSubScene(Scene & scn) {
.body_type = Rigidbody::BodyType::DYNAMIC,
.linear_velocity = vec2(PLAYER_SPEED * 0.02, 0),
.collision_layers
- = {COLL_LAY_BOT_TOP, COLL_LAY_ZAPPER, COLL_LAY_LASER, COLL_LAY_MISSILE},
+ = {COLL_LAY_BOT_TOP, COLL_LAY_ZAPPER, COLL_LAY_LASER, COLL_LAY_MISSILE, COLL_LAY_BULLET
+ },
.collision_layer = COLL_LAY_PLAYER,
});
player.add_component<BehaviorScript>().set_script<PlayerScript>().active = false;
diff --git a/game/prefab/CMakeLists.txt b/game/prefab/CMakeLists.txt
new file mode 100644
index 0000000..6c36ef2
--- /dev/null
+++ b/game/prefab/CMakeLists.txt
@@ -0,0 +1,6 @@
+target_sources(main PUBLIC
+ ZapperObject.cpp
+ ZapperPoolSubScene.cpp
+ ZapperPoolScript.cpp
+)
+
diff --git a/game/prefab/ZapperObject.cpp b/game/prefab/ZapperObject.cpp
new file mode 100644
index 0000000..24bbbd2
--- /dev/null
+++ b/game/prefab/ZapperObject.cpp
@@ -0,0 +1,118 @@
+#include <crepe/api/Transform.h>
+
+#include "Config.h"
+#include "ZapperObject.h"
+
+using namespace crepe;
+
+ZapperObject::ZapperObject(crepe::GameObject && base)
+ : GameObject(std::move(base)),
+ sprite {
+ .orb_start = add_component<Sprite>(
+ Asset {"asset/obstacles/zapper/orbAnim.png"},
+ Sprite::Data {
+ .sorting_in_layer = SORT_IN_LAY_OBSTACLES,
+ .order_in_layer = 1,
+ .size = vec2(0, 50) * SCALE,
+ }
+ ),
+ .orb_end = add_component<Sprite>(
+ sprite.orb_start.source,
+ Sprite::Data {
+ .flip = {true, true},
+ .sorting_in_layer = SORT_IN_LAY_OBSTACLES,
+ .order_in_layer = 1,
+ .size = vec2(0, 50) * SCALE,
+ }
+ ),
+ .glow_start = add_component<Sprite>(
+ Asset {"asset/obstacles/zapper/regular_zappers/glow.png"},
+ Sprite::Data {
+ .sorting_in_layer = SORT_IN_LAY_OBSTACLES,
+ .order_in_layer = -1,
+ .size = vec2(128, 128) * SCALE,
+ }
+ ),
+ .glow_end = add_component<Sprite>(
+ sprite.glow_start.source,
+ Sprite::Data {
+ .flip = {true, true},
+ .sorting_in_layer = SORT_IN_LAY_OBSTACLES,
+ .order_in_layer = -1,
+ .size = vec2(128, 128) * SCALE,
+ }
+ ),
+ .beam = add_component<Sprite>(
+ Asset {"asset/obstacles/zapper/regular_zappers/zapEffect.png"},
+ Sprite::Data {
+ .sorting_in_layer = SORT_IN_LAY_OBSTACLES,
+ .order_in_layer = 0,
+ .size = vec2(0, 40 * SCALE),
+ .angle_offset = 90,
+ }
+ ),
+ },
+ animator {
+ .orb_start = add_component<Animator>(
+ sprite.orb_start, ivec2(62, 42), uvec2(4, 1),
+ Animator::Data {
+ .fps = 10,
+ .looping = true,
+ }
+ ),
+ .orb_end = add_component<Animator>(
+ sprite.orb_end, ivec2(62, 42), uvec2(4, 1), animator.orb_start.data
+ ),
+ .glow_start = add_component<Animator>(
+ sprite.glow_start, ivec2(128, 128), uvec2(16, 1),
+ Animator::Data {
+ .fps = 30,
+ .looping = true,
+ }
+ ),
+ .glow_end = add_component<Animator>(
+ sprite.glow_end, ivec2(128, 128), uvec2(16, 1), animator.glow_start.data
+ ),
+ },
+ body {add_component<Rigidbody>(Rigidbody::Data {
+ .body_type = Rigidbody::BodyType::KINEMATIC,
+ .kinematic_collision = false,
+ .collision_layer = COLL_LAY_ZAPPER,
+ })},
+ collider {add_component<BoxCollider>(vec2(0, 0))} {
+ this->set_active(false);
+}
+
+void ZapperObject::place(const crepe::vec2 & position, float rotation, float length) {
+ this->transform.position = position;
+ this->transform.rotation = rotation;
+
+ vec2 offset = vec2(0, 1) * length / 2;
+
+ this->sprite.orb_start.data.position_offset = offset;
+ this->sprite.glow_start.data.position_offset = offset;
+ this->sprite.orb_end.data.position_offset = -offset;
+ this->sprite.glow_end.data.position_offset = -offset;
+
+ this->sprite.beam.data.size.x = length;
+
+ this->collider.dimensions = offset.rotate(rotation) * 2 + vec2(30, 30) * SCALE;
+}
+
+void ZapperObject::set_active(bool active) {
+ this->sprite.orb_start.active = active;
+ this->sprite.orb_end.active = active;
+ this->sprite.glow_start.active = active;
+ this->sprite.glow_end.active = active;
+ this->sprite.beam.active = active;
+
+ this->animator.orb_start.active = active;
+ this->animator.orb_end.active = active;
+ this->animator.glow_start.active = active;
+ this->animator.glow_end.active = active;
+
+ this->body.active = active;
+ this->collider.active = active;
+
+ this->active = active;
+}
diff --git a/game/prefab/ZapperObject.h b/game/prefab/ZapperObject.h
new file mode 100644
index 0000000..42edc51
--- /dev/null
+++ b/game/prefab/ZapperObject.h
@@ -0,0 +1,40 @@
+#pragma once
+
+#include <crepe/api/Animator.h>
+#include <crepe/api/BoxCollider.h>
+#include <crepe/api/GameObject.h>
+#include <crepe/api/Rigidbody.h>
+#include <crepe/api/Sprite.h>
+
+class ZapperObject : public crepe::GameObject {
+public:
+ ZapperObject(crepe::GameObject &&);
+
+public:
+ bool active = true;
+
+ struct {
+ crepe::Sprite & orb_start;
+ crepe::Sprite & orb_end;
+ crepe::Sprite & glow_start;
+ crepe::Sprite & glow_end;
+ crepe::Sprite & beam;
+ } sprite;
+
+ struct {
+ crepe::Animator & orb_start;
+ crepe::Animator & orb_end;
+ crepe::Animator & glow_start;
+ crepe::Animator & glow_end;
+ } animator;
+
+ crepe::Rigidbody & body;
+ crepe::BoxCollider & collider;
+
+private:
+ static constexpr float SCALE = 0.8;
+
+public:
+ void place(const crepe::vec2 & position, float rotation, float length);
+ void set_active(bool active);
+};
diff --git a/game/prefab/ZapperPoolScript.cpp b/game/prefab/ZapperPoolScript.cpp
new file mode 100644
index 0000000..b832ddd
--- /dev/null
+++ b/game/prefab/ZapperPoolScript.cpp
@@ -0,0 +1,70 @@
+#include <crepe/api/Camera.h>
+
+#include "../Config.h"
+#include "../Random.h"
+
+#include "ZapperPoolScript.h"
+#include "ZapperPoolSubScene.h"
+#include "util/OptionalRef.h"
+
+using namespace crepe;
+using namespace std;
+
+ZapperPoolScript::ZapperPoolScript(std::vector<ZapperObject> && pool) : pool(pool) {}
+
+void ZapperPoolScript::init() {
+ subscribe<CreateZapperEvent>([this](const CreateZapperEvent &) {
+ this->spawn_random();
+ return true;
+ });
+
+ camera_transform = get_components_by_name<Transform>(CAMERA_NAME).back();
+ camera_camera = get_components_by_name<Camera>(CAMERA_NAME).back();
+}
+
+void ZapperPoolScript::fixed_update(crepe::duration_t) {
+ float threshold
+ = camera_transform->position.x - camera_camera->viewport_size.x / 2 - OFFSCREEN_MARGIN;
+ for (ZapperObject & zapper : this->pool) {
+ if (!zapper.active) continue;
+
+ if (zapper.transform.position.x < threshold) zapper.set_active(false);
+ }
+}
+
+void ZapperPoolScript::spawn_random() {
+ OptionalRef<ZapperObject> zapper = this->get_next_zapper();
+ if (!zapper) return; // pool exhausted
+
+ bool horizontal = Random::b();
+ vec2 pos;
+ float rotation, length;
+ pos.x
+ = camera_transform->position.x + camera_camera->viewport_size.x / 2 + OFFSCREEN_MARGIN;
+
+ if (horizontal) {
+ rotation = 90;
+ length = Random::f(400, 200);
+ pos.y = Random::f(0.5, -0.5) * HALLWAY_HEIGHT;
+ // align zapper to right edge of camera
+ pos.x -= MAX_LENGTH - (length / 2);
+ } else {
+ rotation = 0;
+ length = Random::f(200, 75);
+ // ensure zapper doesn't crash into ceiling or floor
+ pos.y = Random::f(0.5, -0.5) * (HALLWAY_HEIGHT - length);
+ // align zapper to right edge of camera
+ pos.x -= MAX_LENGTH;
+ }
+
+ zapper->place(pos, rotation, length);
+ zapper->set_active(true);
+}
+
+OptionalRef<ZapperObject> ZapperPoolScript::get_next_zapper() {
+ for (ZapperObject & zapper : this->pool) {
+ if (zapper.active) continue;
+ return zapper;
+ }
+ return {};
+}
diff --git a/game/prefab/ZapperPoolScript.h b/game/prefab/ZapperPoolScript.h
new file mode 100644
index 0000000..2208c80
--- /dev/null
+++ b/game/prefab/ZapperPoolScript.h
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <crepe/api/Script.h>
+
+#include "ZapperObject.h"
+#include "util/OptionalRef.h"
+
+class ZapperPoolSubScene;
+
+class ZapperPoolScript : public crepe::Script {
+public:
+ ZapperPoolScript(std::vector<ZapperObject> && pool);
+
+ void init();
+ void fixed_update(crepe::duration_t);
+
+ unsigned i = 0;
+
+private:
+ std::vector<ZapperObject> pool;
+
+private:
+ crepe::OptionalRef<crepe::Transform> camera_transform;
+ crepe::OptionalRef<crepe::Camera> camera_camera;
+ crepe::OptionalRef<ZapperObject> get_next_zapper();
+
+private:
+ void spawn_random();
+
+private:
+ static constexpr float MAX_LENGTH = 400;
+ static constexpr float OFFSCREEN_MARGIN = 50 + MAX_LENGTH;
+};
diff --git a/game/prefab/ZapperPoolSubScene.cpp b/game/prefab/ZapperPoolSubScene.cpp
new file mode 100644
index 0000000..a52aa75
--- /dev/null
+++ b/game/prefab/ZapperPoolSubScene.cpp
@@ -0,0 +1,17 @@
+#include <crepe/api/BehaviorScript.h>
+
+#include "ZapperPoolScript.h"
+#include "ZapperPoolSubScene.h"
+
+using namespace crepe;
+using namespace std;
+
+ZapperPoolSubScene::ZapperPoolSubScene(Scene & scene)
+ : controller {scene.new_object("controller")} {
+
+ vector<ZapperObject> pool;
+ for (size_t i = 0; i < this->POOL_SIZE; i++)
+ pool.emplace_back(scene.new_object("zapper", "zapper"));
+ BehaviorScript & behavior = this->controller.add_component<BehaviorScript>();
+ behavior.set_script<ZapperPoolScript>(std::move(pool));
+}
diff --git a/game/prefab/ZapperPoolSubScene.h b/game/prefab/ZapperPoolSubScene.h
new file mode 100644
index 0000000..6f6e297
--- /dev/null
+++ b/game/prefab/ZapperPoolSubScene.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include <crepe/api/Event.h>
+#include <crepe/api/GameObject.h>
+#include <crepe/api/Scene.h>
+#include <crepe/util/OptionalRef.h>
+
+class CreateZapperEvent : public crepe::Event {};
+
+class ZapperPoolSubScene {
+public:
+ ZapperPoolSubScene(crepe::Scene & scene);
+
+private:
+ crepe::GameObject controller;
+
+private:
+ static constexpr size_t POOL_SIZE = 4;
+};
diff --git a/game/preview/NpcScript.cpp b/game/preview/NpcScript.cpp
new file mode 100644
index 0000000..c4148f2
--- /dev/null
+++ b/game/preview/NpcScript.cpp
@@ -0,0 +1,32 @@
+#include "NpcScript.h"
+
+#include <crepe/api/Sprite.h>
+#include <crepe/api/Transform.h>
+#include <crepe/manager/SaveManager.h>
+
+using namespace std;
+using namespace crepe;
+
+void NpcScript::init() {}
+void NpcScript::fixed_update(duration_t dt) {
+ auto & rb = this->get_component<Rigidbody>();
+ auto & npc = this->get_component<Sprite>();
+ auto & transform = this->get_component<Transform>();
+
+ if (transform.position.x < -990) {
+ rb.data.linear_velocity.x *= -1;
+ }
+ if (transform.position.x > 990) {
+ rb.data.linear_velocity.x *= -1;
+ }
+
+ if (rb.data.linear_velocity.x < 0) {
+ npc.data.flip = {true, false};
+ } else {
+ npc.data.flip = {false, false};
+ }
+
+ auto & savemgr = this->get_save_manager();
+ savemgr.set("npc_x", transform.position.x);
+ savemgr.set("npc_y", transform.position.y);
+}
diff --git a/game/preview/NpcScript.h b/game/preview/NpcScript.h
new file mode 100644
index 0000000..8d856fd
--- /dev/null
+++ b/game/preview/NpcScript.h
@@ -0,0 +1,11 @@
+
+#include <crepe/api/Script.h>
+
+class NpcScript : public crepe::Script {
+
+private:
+
+public:
+ void init();
+ void fixed_update(crepe::duration_t dt);
+};
diff --git a/game/preview/NpcSubScene.cpp b/game/preview/NpcSubScene.cpp
new file mode 100644
index 0000000..bd6cfb2
--- /dev/null
+++ b/game/preview/NpcSubScene.cpp
@@ -0,0 +1,69 @@
+
+
+#include "NpcSubScene.h"
+
+#include "../Config.h"
+#include "NpcScript.h"
+
+#include <crepe/ValueBroker.h>
+#include <crepe/api/Animator.h>
+#include <crepe/api/BehaviorScript.h>
+#include <crepe/api/Scene.h>
+#include <crepe/api/Sprite.h>
+#include <crepe/manager/SaveManager.h>
+
+using namespace crepe;
+
+NpcSubScene::NpcSubScene(Scene & scn) {
+ auto & savemgr = scn.get_save_manager();
+ ValueBroker npc_x = savemgr.get<float>("npc_x", 500);
+ ValueBroker npc_y = savemgr.get<float>("npc_y", 0);
+
+ GameObject npc = scn.new_object("npc", "npc_tag", vec2 {npc_x.get(), npc_y.get()}, 0, 1);
+ Asset npc_body {"asset/workers/worker1Body.png"};
+ Asset npc_head {"asset/workers/worker1Head.png"};
+
+ auto & npc_body_sprite = npc.add_component<Sprite>(
+ npc_body,
+ Sprite::Data {
+ .sorting_in_layer = SORT_IN_LAY_PLAYER,
+ .size = {0, 50},
+ }
+ );
+ auto & npc_head_sprite = npc.add_component<Sprite>(
+ npc_head,
+ Sprite::Data {
+ .sorting_in_layer = SORT_IN_LAY_PLAYER,
+ .size = {0, 50},
+ .position_offset = {0, -20},
+ }
+ );
+
+ npc.add_component<Animator>(
+ npc_body_sprite, ivec2 {32, 32}, uvec2 {4, 8},
+ Animator::Data {
+ .fps = 5,
+ .looping = true,
+ }
+ );
+ npc.add_component<Animator>(
+ npc_head_sprite, ivec2 {32, 32}, uvec2 {4, 8},
+ Animator::Data {
+ .fps = 5,
+ .looping = true,
+ }
+ );
+ npc.add_component<BoxCollider>(vec2 {50, 50});
+
+ npc.add_component<Rigidbody>(Rigidbody::Data {
+ .mass = 10,
+ .gravity_scale = 1,
+ .body_type = Rigidbody::BodyType::DYNAMIC,
+ .linear_velocity = {-50, 0},
+ //.max_linear_velocity = 40,
+ .collision_layers = {COLL_LAY_BOT_TOP, COLL_LAY_PLAYER},
+ .collision_layer = COLL_LAY_PLAYER,
+ });
+
+ npc.add_component<BehaviorScript>().set_script<NpcScript>();
+}
diff --git a/game/preview/NpcSubScene.h b/game/preview/NpcSubScene.h
new file mode 100644
index 0000000..a226195
--- /dev/null
+++ b/game/preview/NpcSubScene.h
@@ -0,0 +1,10 @@
+#pragma once
+
+namespace crepe {
+class Scene;
+}
+
+class NpcSubScene {
+public:
+ NpcSubScene(crepe::Scene & scn);
+};
diff --git a/game/preview/PrevPlayerScript.cpp b/game/preview/PrevPlayerScript.cpp
new file mode 100644
index 0000000..2657b8d
--- /dev/null
+++ b/game/preview/PrevPlayerScript.cpp
@@ -0,0 +1,132 @@
+#include "PrevPlayerScript.h"
+
+#include "../missile/SpawnEvent.h"
+#include "api/Transform.h"
+#include <crepe/api/AudioSource.h>
+#include <crepe/api/Camera.h>
+#include <crepe/manager/SaveManager.h>
+#include <iostream>
+#include <ostream>
+
+using namespace crepe;
+
+bool PrevPlayerScript::key_pressed(const KeyPressEvent & ev) {
+ switch (ev.key) {
+ case Keycode::A:
+ this->get_component<Rigidbody>().data.linear_velocity.x = -move_speed;
+ this->body->data.flip = {true, false};
+ this->head->data.flip = {true, false};
+ break;
+ case Keycode::D:
+ this->get_component<Rigidbody>().data.linear_velocity.x = move_speed;
+ this->body->data.flip = {false, false};
+ this->head->data.flip = {false, false};
+ break;
+
+ case Keycode::SPACE:
+ this->get_component<Rigidbody>().data.linear_velocity.y = -move_speed;
+ break;
+ case Keycode::D0:
+ this->body_anim->set_anim(0);
+ this->head_anim->set_anim(0);
+ break;
+ case Keycode::D1:
+ this->body_anim->set_anim(1);
+ this->head_anim->set_anim(1);
+ break;
+ case Keycode::D2:
+ this->body_anim->set_anim(2);
+ this->head_anim->set_anim(2);
+ break;
+ case Keycode::D3:
+ this->body_anim->set_anim(3);
+ this->head_anim->set_anim(3);
+ break;
+ case Keycode::D4:
+ this->body_anim->set_anim(4);
+ this->head_anim->set_anim(4);
+ break;
+ case Keycode::D5:
+ this->body_anim->set_anim(5);
+ this->head_anim->set_anim(5);
+ break;
+ case Keycode::D6:
+ this->body_anim->set_anim(6);
+ this->head_anim->set_anim(6);
+ break;
+ case Keycode::D7:
+ this->body_anim->set_anim(7);
+ this->head_anim->set_anim(7);
+ break;
+ case Keycode::LEFT:
+ this->head->data.angle_offset -= 1;
+ break;
+ case Keycode::RIGHT:
+ this->head->data.angle_offset += 1;
+ break;
+ case Keycode::UP:
+ this->head->data.scale_offset += 0.1;
+ break;
+ case Keycode::DOWN:
+ this->head->data.scale_offset -= 0.1;
+ break;
+ case Keycode::P:
+ this->get_component<AudioSource>().play();
+ break;
+ case Keycode::Q:
+ this->get_components_by_name<Camera>("camera").front().get().data.zoom -= 0.01;
+ break;
+ case Keycode::E:
+ this->get_components_by_name<Camera>("camera").front().get().data.zoom += 0.01;
+ break;
+ case Keycode::J:
+ this->get_components_by_name<Transform>("camera").front().get().position.x
+ -= move_speed;
+ break;
+ case Keycode::K:
+ this->get_components_by_name<Transform>("camera").front().get().position.y
+ -= move_speed;
+ break;
+ case Keycode::L:
+ this->get_components_by_name<Transform>("camera").front().get().position.x
+ += move_speed;
+ break;
+ case Keycode::I:
+ this->get_components_by_name<Transform>("camera").front().get().position.y
+ += move_speed;
+ break;
+ case Keycode::M:
+ trigger_event<MissileSpawnEvent>(MissileSpawnEvent {});
+ break;
+ //todo
+ case Keycode::PAGE_UP:
+ case Keycode::PAGE_DOWN:
+ case Keycode::HOME:
+ break;
+ default:
+ break;
+ }
+ return false;
+}
+
+void PrevPlayerScript::init() {
+ auto animations = this->get_components<Animator>();
+ body_anim = animations[0];
+ head_anim = animations[1];
+
+ auto sprites = this->get_components<Sprite>();
+ body = sprites[0];
+ head = sprites[1];
+
+ subscribe<KeyPressEvent>([this](const KeyPressEvent & ev) -> bool {
+ return this->key_pressed(ev);
+ });
+};
+
+void PrevPlayerScript::fixed_update(crepe::duration_t dt) {
+ auto & savemgr = this->get_save_manager();
+ const auto & pos = this->get_component<Transform>().position;
+
+ savemgr.set("player_x", pos.x);
+ savemgr.set("player_y", pos.y);
+};
diff --git a/game/preview/PrevPlayerScript.h b/game/preview/PrevPlayerScript.h
new file mode 100644
index 0000000..cc3184e
--- /dev/null
+++ b/game/preview/PrevPlayerScript.h
@@ -0,0 +1,23 @@
+
+#include <crepe/api/Event.h>
+#include <crepe/api/Script.h>
+#include <crepe/util/OptionalRef.h>
+
+#include <crepe/api/Animator.h>
+#include <crepe/api/Sprite.h>
+
+class PrevPlayerScript : public crepe::Script {
+private:
+ crepe::OptionalRef<crepe::Animator> head_anim;
+ crepe::OptionalRef<crepe::Animator> body_anim;
+ crepe::OptionalRef<crepe::Sprite> head;
+ crepe::OptionalRef<crepe::Sprite> body;
+
+private:
+ float move_speed = 100;
+
+private:
+ void init();
+ void fixed_update(crepe::duration_t dt);
+ bool key_pressed(const crepe::KeyPressEvent & ev);
+};
diff --git a/game/preview/PrevPlayerSubScene.cpp b/game/preview/PrevPlayerSubScene.cpp
new file mode 100644
index 0000000..b59a0af
--- /dev/null
+++ b/game/preview/PrevPlayerSubScene.cpp
@@ -0,0 +1,86 @@
+
+#include "PrevPlayerSubScene.h"
+
+#include "../Config.h"
+#include "PrevPlayerScript.h"
+
+#include <crepe/api/Animator.h>
+#include <crepe/api/AudioSource.h>
+#include <crepe/api/BehaviorScript.h>
+#include <crepe/api/Scene.h>
+#include <crepe/api/Sprite.h>
+
+#include <crepe/ValueBroker.h>
+#include <crepe/manager/SaveManager.h>
+
+using namespace crepe;
+
+PrevPlayerSubScene::PrevPlayerSubScene(Scene & scn) {
+ auto & savemgr = scn.get_save_manager();
+
+ ValueBroker player_x = savemgr.get<float>("player_x", 500);
+ ValueBroker player_y = savemgr.get<float>("player_y", -100);
+
+ GameObject player
+ = scn.new_object("player", "TAG", vec2 {player_x.get(), player_y.get()}, 0, 1);
+ Asset player_body_asset {"asset/barry/defaultBody.png"};
+ Sprite & player_body_sprite = player.add_component<Sprite>(
+ player_body_asset,
+ Sprite::Data {
+ .sorting_in_layer = SORT_IN_LAY_PLAYER,
+ .order_in_layer = 0,
+ .size = vec2(0, 50),
+ }
+ );
+ player.add_component<Animator>(
+ player_body_sprite, ivec2(32, 32), uvec2(4, 8),
+ Animator::Data {
+ .fps = 5,
+ .looping = true,
+ }
+ );
+ Asset player_head_asset {"asset/barry/defaultHead.png"};
+ Sprite & player_head_sprite = player.add_component<Sprite>(
+ player_head_asset,
+ Sprite::Data {
+ .sorting_in_layer = SORT_IN_LAY_PLAYER,
+ .order_in_layer = 1,
+ .size = vec2(0, 50),
+ .position_offset = vec2(0, -20),
+ }
+ );
+ player.add_component<Animator>(
+ player_head_sprite, ivec2(32, 32), uvec2(4, 8),
+ Animator::Data {
+ .fps = 5,
+ .looping = true,
+ }
+ );
+ Asset player_jetpack_asset {"asset/barry/jetpackDefault.png"};
+ Sprite & player_jetpack_sprite = player.add_component<Sprite>(
+ player_jetpack_asset,
+ Sprite::Data {
+ .sorting_in_layer = SORT_IN_LAY_PLAYER,
+ .order_in_layer = 2,
+ .size = vec2(0, 60),
+ .position_offset = vec2(-20, 0),
+ }
+ );
+ player_jetpack_sprite.active = false;
+ player.add_component<Animator>(
+ player_jetpack_sprite, ivec2(32, 44), uvec2(4, 4),
+ Animator::Data {
+ .fps = 5,
+ .looping = true,
+ }
+ );
+ player.add_component<Rigidbody>(Rigidbody::Data {
+ .gravity_scale = 20,
+ .body_type = Rigidbody::BodyType::DYNAMIC,
+ .linear_velocity = vec2(100, 0),
+ .collision_layers = {COLL_LAY_BOT_TOP},
+ .collision_layer = COLL_LAY_PLAYER,
+ });
+ player.add_component<BoxCollider>(vec2(50, 50));
+ player.add_component<BehaviorScript>().set_script<PrevPlayerScript>();
+}
diff --git a/game/preview/PrevPlayerSubScene.h b/game/preview/PrevPlayerSubScene.h
new file mode 100644
index 0000000..a61f341
--- /dev/null
+++ b/game/preview/PrevPlayerSubScene.h
@@ -0,0 +1,10 @@
+#pragma once
+
+namespace crepe {
+class Scene;
+}
+
+class PrevPlayerSubScene {
+public:
+ PrevPlayerSubScene(crepe::Scene & scn);
+};
diff --git a/game/preview/SmokeSubScene.cpp b/game/preview/SmokeSubScene.cpp
new file mode 100644
index 0000000..e363f95
--- /dev/null
+++ b/game/preview/SmokeSubScene.cpp
@@ -0,0 +1,37 @@
+
+#include "SmokeSubScene.h"
+
+#include "../Config.h"
+
+#include <crepe/api/ParticleEmitter.h>
+#include <crepe/api/Scene.h>
+#include <crepe/api/Sprite.h>
+
+using namespace crepe;
+
+SmokeSubScene::SmokeSubScene(Scene & scn) {
+ GameObject smoke = scn.new_object("smoke_particle", "TAG", vec2 {500, -210}, 0, 1);
+
+ Asset smoke_ss {"asset/particles/smoke.png"};
+
+ auto & smoke_sprite = smoke.add_component<Sprite>(
+ smoke_ss,
+ Sprite::Data {
+ .sorting_in_layer = SORT_IN_LAY_PARTICLES_FOREGROUND,
+ .size = {0, 30},
+ }
+ );
+
+ smoke.add_component<ParticleEmitter>(
+ smoke_sprite,
+ ParticleEmitter::Data {
+ .offset = {0, -60},
+ .max_particles = 10,
+ .emission_rate = 25,
+ .min_angle = 60,
+ .max_angle = 120,
+ .begin_lifespan = 1,
+ .end_lifespan = 2,
+ }
+ );
+}
diff --git a/game/preview/SmokeSubScene.h b/game/preview/SmokeSubScene.h
new file mode 100644
index 0000000..93d8a2d
--- /dev/null
+++ b/game/preview/SmokeSubScene.h
@@ -0,0 +1,10 @@
+#pragma once
+
+namespace crepe {
+class Scene;
+}
+
+class SmokeSubScene {
+public:
+ SmokeSubScene(crepe::Scene & scn);
+};
diff --git a/game/scheduler/ObjectsScheduler.cpp b/game/scheduler/ObjectsScheduler.cpp
new file mode 100644
index 0000000..02d84c1
--- /dev/null
+++ b/game/scheduler/ObjectsScheduler.cpp
@@ -0,0 +1,58 @@
+
+
+#include "ObjectsScheduler.h"
+
+#include "../Random.h"
+#include "../Config.h"
+#include "../missile/SpawnEvent.h"
+#include "api/Rigidbody.h"
+#include "api/Transform.h"
+#include "enemy/BattleScript.h"
+#include "prefab/ZapperPoolSubScene.h"
+
+using namespace crepe;
+void ObjectsScheduler::preset_0() { trigger_event<MissileSpawnEvent>(MissileSpawnEvent {}); }
+void ObjectsScheduler::preset_1() { trigger_event<MissileSpawnEvent>(MissileSpawnEvent {}); }
+void ObjectsScheduler::preset_2() { trigger_event<CreateZapperEvent>(CreateZapperEvent {}); }
+void ObjectsScheduler::preset_3() {}
+void ObjectsScheduler::preset_4() {}
+void ObjectsScheduler::boss_fight_1() {
+ this->get_components_by_name<Rigidbody>("camera").front().get().data.linear_velocity.x = 0;
+ this->get_components_by_name<Rigidbody>("player").front().get().data.linear_velocity.x = 0;
+ this->trigger_event<BattleStartEvent>(BattleStartEvent{.num_enemies = 2});
+}
+
+bool ObjectsScheduler::boss_fight_1_event() {
+ this->get_components_by_name<Rigidbody>("camera").front().get().data.linear_velocity.x = PLAYER_SPEED * 0.02;
+ this->get_components_by_name<Rigidbody>("player").front().get().data.linear_velocity.x = PLAYER_SPEED * 0.02;
+ return false;
+}
+
+void ObjectsScheduler::init() {
+ this->obstacles.push_back([this]() { preset_0(); });
+ this->obstacles.push_back([this]() { preset_1(); });
+ this->obstacles.push_back([this]() { preset_2(); });
+
+ this->obstacles.push_back([this]() { boss_fight_1(); });
+
+ // subscribe to battlewonevent
+ this->subscribe<BattleWonEvent>([this](const BattleWonEvent & ev) -> bool {
+ return this->boss_fight_1_event();
+ });
+}
+
+void ObjectsScheduler::fixed_update(duration_t dt) {
+ int pos_x
+ = (int) this->get_components_by_name<Transform>("camera").front().get().position.x;
+
+ int boss_check = (pos_x - this->start_offset) / this->boss_fight_interval;
+ if (boss_check > this->last_boss_check) {
+ this->obstacles.back()();
+ this->last_boss_check = boss_check;
+ }
+ int obstacle_check = (pos_x - this->start_offset) / this->obstacle_interval;
+ if (obstacle_check > this->last_obstacle_check) {
+ this->obstacles[Random::i(this->obstacles.size() - 1, 0)]();
+ this->last_obstacle_check = obstacle_check;
+ }
+}
diff --git a/game/scheduler/ObjectsScheduler.h b/game/scheduler/ObjectsScheduler.h
new file mode 100644
index 0000000..56d72cb
--- /dev/null
+++ b/game/scheduler/ObjectsScheduler.h
@@ -0,0 +1,36 @@
+#pragma once
+
+
+
+#include "api/Script.h"
+#include <functional>
+#include <vector>
+
+
+class ObjectsScheduler : public crepe::Script {
+
+private:
+ std::vector<std::function<void()>> obstacles;
+
+ int last_boss_check = 0;
+ int last_obstacle_check = 0;
+
+ int boss_fight_interval = 5000;
+ int obstacle_interval = 350;
+ int start_offset = 1300;
+
+private:
+ void preset_0();
+ void preset_1();
+ void preset_2();
+ void preset_3();
+ void preset_4();
+ void boss_fight_1();
+
+ bool boss_fight_1_event();
+
+public:
+ void init();
+ void fixed_update(crepe::duration_t dt);
+
+};
diff --git a/game/util/scrollgen b/game/util/scrollgen
new file mode 100755
index 0000000..0389107
--- /dev/null
+++ b/game/util/scrollgen
@@ -0,0 +1,36 @@
+#!/bin/sh
+INPUT="$1"
+FRAMES="$2"
+
+die() {
+ echo "$@"
+ exit 1
+}
+check_command() {
+ cmd="$1"
+ command -v "$cmd" > /dev/null || die "command '$cmd' not found"
+}
+
+check_command magick
+check_command identify
+[ "$#" -eq 2 ] || die "usage: $0 <input image> <frame count>"
+[ -e "$INPUT" ] || die "file not found: $INPUT"
+[ "$FRAMES" -gt 0 ] || die "invalid frame count: $FRAMES"
+
+tile_width=$(identify -format "%w" "$INPUT")
+tile_height=$(identify -format "%h" "$INPUT")
+
+OUTPUT="$INPUT.scroll.png"
+magick -size "${tile_width}x$(( $tile_height * $FRAMES ))" 'xc:#ff00ff00' "$OUTPUT"
+
+for i in $(seq 0 $(( $FRAMES - 1 ))); do
+ offset_x=$(( $tile_width * $i / $FRAMES ))
+ offset_y=$(( i * $tile_height ))
+
+ magick "$OUTPUT" "$INPUT" -geometry "+${offset_x}+${offset_y}" -composite "$OUTPUT"
+ magick "$OUTPUT" "$INPUT" -geometry "+$(( $offset_x - $tile_width ))+${offset_y}" -composite "$OUTPUT"
+ echo "+${offset_x}+${offset_y}"
+done
+
+# magick -size ${total_width}x${sprite_height} xc:none canvas.png
+
diff --git a/game/workers/CollisionScript.cpp b/game/workers/CollisionScript.cpp
index deaf0ee..625044d 100644
--- a/game/workers/CollisionScript.cpp
+++ b/game/workers/CollisionScript.cpp
@@ -49,7 +49,7 @@ bool CollisionScript::on_collision(const CollisionEvent & ev) {
bs_panic.active = false;
return false;
- } else if (ev.info.other.metadata.tag == "missile") {
+ } else if (ev.info.other.metadata.tag == "missile" || ev.info.other.metadata.tag == "enemy_bullet") {
for (Animator & anim : animators) {
anim.active = false;
anim.set_anim(3);
diff --git a/src/crepe/Collider.h b/src/crepe/Collider.h
index 42ccfd4..4344f15 100644
--- a/src/crepe/Collider.h
+++ b/src/crepe/Collider.h
@@ -5,6 +5,9 @@
namespace crepe {
+/**
+ * \brief Base collider class
+ */
class Collider : public Component {
public:
Collider(game_object_id_t id, const vec2 & offset);
diff --git a/src/crepe/api/Components.h b/src/crepe/api/Components.h
new file mode 100644
index 0000000..fa0663d
--- /dev/null
+++ b/src/crepe/api/Components.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "AI.h"
+#include "Animator.h"
+#include "AudioSource.h"
+#include "BehaviorScript.h"
+#include "BoxCollider.h"
+#include "Button.h"
+#include "Camera.h"
+#include "CircleCollider.h"
+#include "Metadata.h"
+#include "ParticleEmitter.h"
+#include "Rigidbody.h"
+#include "Sprite.h"
+#include "Text.h"
+#include "Transform.h"
diff --git a/src/crepe/api/Config.h b/src/crepe/api/Config.h
index 9c226a3..7475528 100644
--- a/src/crepe/api/Config.h
+++ b/src/crepe/api/Config.h
@@ -60,7 +60,8 @@ struct Config final {
struct {
//! default screen size in pixels
ivec2 default_size = {1280, 720};
- std::string window_title = "Jetpack joyride clone";
+ //! default window title
+ std::string window_title = "crepe window";
} window_settings;
//! Asset loading options
diff --git a/src/crepe/api/Vector2.h b/src/crepe/api/Vector2.h
index 52e1bb6..6613641 100644
--- a/src/crepe/api/Vector2.h
+++ b/src/crepe/api/Vector2.h
@@ -1,5 +1,7 @@
#pragma once
+#include <format>
+
namespace crepe {
//! 2D vector
@@ -11,55 +13,55 @@ struct Vector2 {
T y = 0;
//! Subtracts another vector from this vector and returns the result.
- Vector2 operator-(const Vector2<T> & other) const;
+ Vector2<T> operator-(const Vector2<T> & other) const;
//! Subtracts a scalar value from both components of this vector and returns the result.
- Vector2 operator-(T scalar) const;
+ Vector2<T> operator-(T scalar) const;
//! Adds another vector to this vector and returns the result.
- Vector2 operator+(const Vector2<T> & other) const;
+ Vector2<T> operator+(const Vector2<T> & other) const;
//! Adds a scalar value to both components of this vector and returns the result.
- Vector2 operator+(T scalar) const;
+ Vector2<T> operator+(T scalar) const;
//! Multiplies this vector by another vector element-wise and returns the result.
- Vector2 operator*(const Vector2<T> & other) const;
+ Vector2<T> operator*(const Vector2<T> & other) const;
//! Multiplies this vector by a scalar and returns the result.
- Vector2 operator*(T scalar) const;
+ Vector2<T> operator*(T scalar) const;
//! Divides this vector by another vector element-wise and returns the result.
- Vector2 operator/(const Vector2<T> & other) const;
+ Vector2<T> operator/(const Vector2<T> & other) const;
//! Divides this vector by a scalar and returns the result.
- Vector2 operator/(T scalar) const;
+ Vector2<T> operator/(T scalar) const;
//! Adds another vector to this vector and updates this vector.
- Vector2 & operator+=(const Vector2<T> & other);
+ Vector2<T> & operator+=(const Vector2<T> & other);
//! Adds a scalar value to both components of this vector and updates this vector.
- Vector2 & operator+=(T other);
+ Vector2<T> & operator+=(T other);
//! Subtracts another vector from this vector and updates this vector.
- Vector2 & operator-=(const Vector2<T> & other);
+ Vector2<T> & operator-=(const Vector2<T> & other);
//! Subtracts a scalar value from both components of this vector and updates this vector.
- Vector2 & operator-=(T other);
+ Vector2<T> & operator-=(T other);
//! Multiplies this vector by another vector element-wise and updates this vector.
- Vector2 & operator*=(const Vector2<T> & other);
+ Vector2<T> & operator*=(const Vector2<T> & other);
//! Multiplies this vector by a scalar and updates this vector.
- Vector2 & operator*=(T other);
+ Vector2<T> & operator*=(T other);
//! Divides this vector by another vector element-wise and updates this vector.
- Vector2 & operator/=(const Vector2<T> & other);
+ Vector2<T> & operator/=(const Vector2<T> & other);
//! Divides this vector by a scalar and updates this vector.
- Vector2 & operator/=(T other);
+ Vector2<T> & operator/=(T other);
//! Returns the negation of this vector.
- Vector2 operator-() const;
+ Vector2<T> operator-() const;
//! Checks if this vector is equal to another vector.
bool operator==(const Vector2<T> & other) const;
@@ -89,12 +91,20 @@ struct Vector2 {
T distance_squared(const Vector2<T> & other) const;
//! Returns the perpendicular vector to this vector.
- Vector2 perpendicular() const;
+ Vector2<T> perpendicular() const;
//! Checks if both components of the vector are NaN.
bool is_nan() const;
+
+ //! Rotate this vector clockwise by \c deg degrees
+ Vector2<T> rotate(float deg) const;
};
} // namespace crepe
+template <typename T>
+struct std::formatter<crepe::Vector2<T>> : std::formatter<std::string> {
+ format_context::iterator format(crepe::Vector2<T> vec, format_context & ctx) const;
+};
+
#include "Vector2.hpp"
diff --git a/src/crepe/api/Vector2.hpp b/src/crepe/api/Vector2.hpp
index e195760..30441d2 100644
--- a/src/crepe/api/Vector2.hpp
+++ b/src/crepe/api/Vector2.hpp
@@ -168,4 +168,19 @@ bool Vector2<T>::is_nan() const {
return std::isnan(x) && std::isnan(y);
}
+template <class T>
+Vector2<T> Vector2<T>::rotate(float deg) const {
+ float rad = -deg / 180 * M_PI;
+ return {
+ x * std::cos(rad) - y * std::sin(rad),
+ x * std::sin(rad) + y * std::cos(rad),
+ };
+}
+
} // namespace crepe
+
+template <typename T>
+std::format_context::iterator
+std::formatter<crepe::Vector2<T>>::format(crepe::Vector2<T> vec, format_context & ctx) const {
+ return formatter<string>::format(std::format("{{{}, {}}}", vec.x, vec.y), ctx);
+}
diff --git a/src/test/Vector2Test.cpp b/src/test/Vector2Test.cpp
index 1e21af9..b17f95a 100644
--- a/src/test/Vector2Test.cpp
+++ b/src/test/Vector2Test.cpp
@@ -1,6 +1,7 @@
#include <gtest/gtest.h>
#include <crepe/api/Vector2.h>
+#include <crepe/types.h>
using namespace crepe;
@@ -530,3 +531,12 @@ TEST_F(Vector2Test, Perpendicular) {
EXPECT_FLOAT_EQ(result4.x, -4.0f);
EXPECT_FLOAT_EQ(result4.y, 3.0f);
}
+
+TEST_F(Vector2Test, Rotate) {
+ vec2 foo {0, 1};
+
+ foo = foo.rotate(90);
+ const float GOOD_ENOUGH = 0.001;
+ EXPECT_NEAR(foo.x, 1, GOOD_ENOUGH);
+ EXPECT_NEAR(foo.y, 0, GOOD_ENOUGH);
+}