diff options
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); +} |