Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
691 changes: 691 additions & 0 deletions AGENTS.md

Large diffs are not rendered by default.

11 changes: 9 additions & 2 deletions assets/scripts/space-shooter/levels/Level-1.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,15 @@ if file then
file:close()
end


local backgroundTexture = "assets/textures/Background/SinglePlay1.png"
-- Conditional rendering: Check if solo or multiplayer
local isSoloMode = not (ECS.capabilities and ECS.capabilities.hasNetworkSync)
local backgroundTexture

if isSoloMode then
backgroundTexture = "assets/textures/Background/SinglePlay1.png"
else
backgroundTexture = "assets/textures/Background/Multiplayer_1.png"
end

local Spawns = require("assets/scripts/space-shooter/spawns")

Expand Down
19 changes: 8 additions & 11 deletions assets/scripts/space-shooter/levels/Level-2.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,17 @@ if file then
file:close()
end

local backgroundTexture = "assets/textures/Background/SinglePlay2.png"
local isSoloMode = not (ECS.capabilities and ECS.capabilities.hasNetworkSync)
local backgroundTexture

local Spawns = require("assets/scripts/space-shooter/spawns")
if isSoloMode then
backgroundTexture = "assets/textures/Background/SinglePlay2.png"
else
backgroundTexture = "assets/textures/Background/Multiplayer_2.png"
end

local Spawns = require("assets/scripts/space-shooter/spawns")

Spawns.createCoreEntities(CurrentLevel, backgroundTexture, CurrentScore)


-- (For Multi)
-- -- Create Player Tag
-- local playerTag = ECS.createEntity()
-- ECS.addComponent(playerTag, "Transform", Transform(0, 0, 0))
-- ECS.addComponent(playerTag, "Text", Text("Player1", "assets/fonts/arial.ttf", 24, false))
-- ECS.addComponent(playerTag, "Color", Color(1.0, 1.0, 0.0))
-- ECS.addComponent(playerTag, "Follow", Follow(player, 0, 2.5, 0))

print("Level 2 loaded!")
11 changes: 7 additions & 4 deletions assets/scripts/space-shooter/levels/Level-3.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@



CurrentLevel = 3

local file = io.open("current_level.txt", "w")
Expand All @@ -9,12 +7,17 @@ if file then
file:close()
end

local isSoloMode = not (ECS.capabilities and ECS.capabilities.hasNetworkSync)
local backgroundTexture

local backgroundTexture = "assets/textures/Background/SinglePlay3.png"
if isSoloMode then
backgroundTexture = "assets/textures/Background/SinglePlay3.png"
else
backgroundTexture = "assets/textures/Background/Multiplayer_3.png"
end

local Spawns = require("assets/scripts/space-shooter/spawns")


Spawns.createCoreEntities(CurrentLevel, backgroundTexture, CurrentScore)


Expand Down
12 changes: 9 additions & 3 deletions assets/scripts/space-shooter/levels/Level-4.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,15 @@ if file then
file:close()
end



local backgroundTexture = "assets/textures/Background/SinglePlay4.png"
-- Conditional rendering: Check if solo or multiplayer
local isSoloMode = not (ECS.capabilities and ECS.capabilities.hasNetworkSync)
local backgroundTexture

if isSoloMode then
backgroundTexture = "assets/textures/Background/SinglePlay4.png"
else
backgroundTexture = "assets/textures/Background/Multiplayer_4.png"
end

local Spawns = require("assets/scripts/space-shooter/spawns")

Expand Down
4 changes: 3 additions & 1 deletion assets/scripts/space-shooter/levels/Level-5.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ if file then
file:close()
end


-- Conditional rendering: Check if solo or multiplayer
local isSoloMode = not (ECS.capabilities and ECS.capabilities.hasNetworkSync)
local backgroundTexture = "assets/textures/Background/StartSky.jpg"


local Spawns = require("assets/scripts/space-shooter/spawns")


Expand Down
29 changes: 20 additions & 9 deletions assets/scripts/space-shooter/spawns.lua
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ end
-- ============================================================================
-- BULLET
-- ============================================================================
function Spawns.spawnBullet(x, y, z, isEnemy)
function Spawns.spawnBullet(x, y, z, isEnemy, ownerId)
local e = ECS.createEntity()

local speed = isEnemy and -config.bullet.speed or config.bullet.speed
Expand All @@ -194,6 +194,9 @@ function Spawns.spawnBullet(x, y, z, isEnemy)
ECS.addComponent(e, "Tag", Tag({tag}))
ECS.addComponent(e, "Bullet", Bullet(config.bullet.damage))
ECS.addComponent(e, "Life", Life(config.bullet.life))
if ownerId then
ECS.addComponent(e, "Owner", { id = ownerId })
end

-- Physics
local phys = Physic(0.1, 0.0, true, false)
Expand Down Expand Up @@ -225,24 +228,25 @@ end
-- ============================================================================
-- BACKGROUND
-- ============================================================================
function Spawns.createBackground(texturePath)
function Spawns.createBackground(texturePath, scaleX, scaleY)
if not hasRendering() then return end

local tex = texturePath or "assets/textures/Background/StartSky.jpg"
print("[Spawns] Creating Parallax Background with texture: " .. tex)
local sx = scaleX or 60
local sy = scaleY or 40

-- Layer 1 (Far Stars)
local bg1 = ECS.createEntity()
ECS.addComponent(bg1, "Transform", Transform(0, 0, -10, 3.14, 0, 0, -60, 40, 1))
ECS.addComponent(bg1, "Transform", Transform(0, 0, -10, 3.14, 0, 0, -sx, sy, 1))
ECS.addComponent(bg1, "Mesh", Mesh("assets/models/quad.obj", tex))
ECS.addComponent(bg1, "Color", Color(1.0, 1.0, 1.0))
ECS.addComponent(bg1, "Background", Background(-2.0, 60.0, -60.0))
ECS.addComponent(bg1, "Background", Background(-2.0, sx, -sx))

local bg2 = ECS.createEntity()
ECS.addComponent(bg2, "Transform", Transform(60.0, 0, -10.01, 3.14, 0, 0, 60, 40, 1))
ECS.addComponent(bg2, "Transform", Transform(sx, 0, -10.01, 3.14, 0, 0, sx, sy, 1))
ECS.addComponent(bg2, "Mesh", Mesh("assets/models/quad.obj", tex))
ECS.addComponent(bg2, "Color", Color(1.0, 1.0, 1.0))
ECS.addComponent(bg2, "Background", Background(-2.0, 60.0, -60.0))
ECS.addComponent(bg2, "Background", Background(-2.0, sx, -sx))

print("[Spawns] Background entities created: " .. bg1 .. ", " .. bg2)
end
Expand Down Expand Up @@ -278,9 +282,16 @@ function Spawns.createCoreEntities(level, backgroundTexture)

-- Create Background
local bgTex = backgroundTexture or ("assets/textures/Background/SinglePlay" .. tostring(level) .. ".png")
Spawns.createBackground(bgTex)
local isMultiplayer = ECS.capabilities and ECS.capabilities.hasNetworkSync

if isMultiplayer then
-- Use larger scale for multiplayer backgrounds to ensure full screen coverage
Spawns.createBackground(bgTex, 80, 60)
else
Spawns.createBackground(bgTex)
Spawns.createScore(CurrentScore)
end

Spawns.createScore(CurrentScore)

end

Expand Down
40 changes: 33 additions & 7 deletions assets/scripts/space-shooter/systems/CollisionSystem.lua
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,13 @@ function CollisionSystem.handlePlayerEnemy(playerId, enemyId)
life.amount = life.amount - damage
life.invulnerableTime = invulnTime

-- Broadcast hit sound to all clients
ECS.broadcastNetworkMessage("PLAY_SOUND", "player_hit:effects/hit.wav:100") -- change the sound
-- Play hit sound
if not ECS.capabilities.hasNetworkSync then
ECS.sendMessage("SoundPlay", "player_hit:effects/hit.wav:100")
else
-- Broadcast hit sound to all clients
ECS.broadcastNetworkMessage("PLAY_SOUND", "player_hit:effects/hit.wav:100")
end
if color then
color.r = 1.0
color.g = 0.0
Expand All @@ -170,15 +175,31 @@ function CollisionSystem.handleEnemyBullet(enemyId, bulletId)
-- DAMAGE APPLICATION: Kill enemy on bullet hit
life.amount = 0

-- Broadcast explosion sound to all clients
ECS.broadcastNetworkMessage("PLAY_SOUND", "explosion_" .. enemyId .. ":effects/explosion.wav:90")
-- Play explosion sound
if not ECS.capabilities.hasNetworkSync then
ECS.sendMessage("SoundPlay", "explosion_" .. enemyId .. ":effects/explosion.wav:90")
else
-- Broadcast explosion sound to all clients
ECS.broadcastNetworkMessage("PLAY_SOUND", "explosion_" .. enemyId .. ":effects/explosion.wav:90")
end
end

-- Increase score for the player who shot the bullet
local ownerComp = ECS.getComponent(bulletId, "Owner")
if ownerComp and ownerComp.id then
local playerScore = ECS.getComponent(ownerComp.id, "Score")
if playerScore then
playerScore.value = playerScore.value + config.score.kill
print("[DEBUG] Player " .. ownerComp.id .. " score: " .. playerScore.value)
end
end

-- Increase score (authority guaranteed)
-- Increase global score
local scoreEntities = ECS.getEntitiesWith({"Score"})
if #scoreEntities > 0 then
local scoreComp = ECS.getComponent(scoreEntities[1], "Score")
scoreComp.value = scoreComp.value + config.score.kill
print("[DEBUG] Global score: " .. scoreComp.value)
end

-- Destroy bullet after hit
Expand All @@ -194,8 +215,13 @@ function CollisionSystem.handlePlayerBonus(playerId, bonusId)
if bonus then
ECS.addComponent(playerId, "PowerUp", PowerUp(bonus.duration, 0.2))

-- Broadcast powerup sound to all clients
ECS.broadcastNetworkMessage("PLAY_SOUND", "powerup:effects/powerup.wav:100")
-- Play powerup sound
if not ECS.capabilities.hasNetworkSync then
ECS.sendMessage("SoundPlay", "powerup:effects/powerup.wav:100")
else
-- Broadcast powerup sound to all clients
ECS.broadcastNetworkMessage("PLAY_SOUND", "powerup:effects/powerup.wav:100")
end

-- Destroy bonus after collection
local bonusLife = ECS.getComponent(bonusId, "Life")
Expand Down
8 changes: 5 additions & 3 deletions assets/scripts/space-shooter/systems/EnemySystem.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ EnemySystem.spawnTimer = 0
EnemySystem.spawnInterval = config.enemy.spawnInterval or 2.0
EnemySystem.gameTime = 0
EnemySystem.enemyShootTimers = {}
EnemySystem.shootInterval = 5.0
EnemySystem.baseSpawnInterval = config.enemy.spawnInterval or 2.0
EnemySystem.baseSpeed = config.enemy.speed or 5.0

Expand Down Expand Up @@ -45,6 +44,10 @@ function EnemySystem.update(dt)
return
end

EnemySystem.gameTime = EnemySystem.gameTime + dt
local level = _G.CurrentLevel or 1
EnemySystem.shootInterval = math.max(0.3, 2.6 / (level ^ 0.5))

EnemySystem.spawnTimer = EnemySystem.spawnTimer + dt
if EnemySystem.spawnTimer >= EnemySystem.spawnInterval then
EnemySystem.spawnTimer = 0
Expand Down Expand Up @@ -190,7 +193,7 @@ function EnemySystem.spawnEnemy()
print("DEBUG: Spawning enemy at x=" .. x .. ", y=" .. y .. ", SCREEN_WIDTH=" .. _G.SCREEN_WIDTH)

local level = _G.CurrentLevel or 1
local difficultyMultiplier = (1.0 + (EnemySystem.gameTime / 30.0)) * level
local difficultyMultiplier = (1.0 + (EnemySystem.gameTime / 30.0)) * (level ^ 0.3)
local speed = EnemySystem.baseSpeed * difficultyMultiplier
if config.enemy.maxSpeed and speed > config.enemy.maxSpeed then speed = config.enemy.maxSpeed end

Expand All @@ -210,7 +213,6 @@ function EnemySystem.resetDifficulty()
local level = _G.CurrentLevel or 1
-- Decrease spawn interval for higher levels (faster spawns), but cap at 0.5s minimum
EnemySystem.spawnInterval = math.max(0.5, EnemySystem.baseSpawnInterval / (level ^ 0.5))
EnemySystem.shootInterval = math.max(1.0, 6.0 - level)
end

-- Subscribe to global event if possible, or poll?
Expand Down
43 changes: 30 additions & 13 deletions assets/scripts/space-shooter/systems/LifeSystem.lua
Original file line number Diff line number Diff line change
Expand Up @@ -58,21 +58,32 @@ function LifeSystem.update(dt)

-- In multiplayer server mode, broadcast enemy death to clients
if enemy and ECS.capabilities.hasNetworkSync and not life.deathEventSent then
local phys = ECS.getComponent(id, "Physic")
local vx, vy, vz = 0, 0, 0
if phys then
vx, vy, vz = phys.vx or 0, phys.vy or 0, phys.vz or 0
-- Don't broadcast for boundary deaths
if not (t.x < -50 or t.x > 50 or t.y < -30 or t.y > 30) then
local phys = ECS.getComponent(id, "Physic")
local vx, vy, vz = 0, 0, 0
if phys then
vx, vy, vz = phys.vx or 0, phys.vy or 0, phys.vz or 0
end
if t then
local msg = string.format("%s %f %f %f %f %f %f", id, t.x, t.y, t.z, vx, vy, vz)
ECS.broadcastNetworkMessage("ENEMY_DEAD", msg)
life.deathEventSent = true
end
end
if t then
local msg = string.format("%s %f %f %f %f %f %f", id, t.x, t.y, t.z, vx, vy, vz)
ECS.broadcastNetworkMessage("ENEMY_DEAD", msg)
life.deathEventSent = true
end
end

-- In Solo Mode (Authority + Rendering), spawn explosion locally
if enemy and ECS.capabilities.hasRendering then
Spawns.createExplosion(t.x, t.y, t.z)
-- Don't spawn explosion for boundary deaths
if not (t.x < -50 or t.x > 50 or t.y < -30 or t.y > 30) then
Spawns.createExplosion(t.x, t.y, t.z)

-- Play enemy death sound
if not ECS.capabilities.hasNetworkSync then
ECS.sendMessage("SoundPlay", "enemy_death_" .. id .. ":effects/explosion.wav:90")
end
end
end

local finalScore = nil
Expand All @@ -87,9 +98,15 @@ function LifeSystem.update(dt)
end
print("GAME OVER: Player died! Final Score: " .. finalScore)

-- Broadcast stop music and gameover sound to clients
ECS.broadcastNetworkMessage("STOP_MUSIC", "bgm")
ECS.broadcastNetworkMessage("PLAY_SOUND", "gameover:effects/gameover.wav:100")
-- Play stop music and gameover sound
if not ECS.capabilities.hasNetworkSync then
ECS.sendMessage("MusicStop", "bgm")
ECS.sendMessage("SoundPlay", "gameover:effects/gameover.wav:100")
else
-- Broadcast stop music and gameover sound to clients
ECS.broadcastNetworkMessage("STOP_MUSIC", "bgm")
ECS.broadcastNetworkMessage("PLAY_SOUND", "gameover:effects/gameover.wav:100")
end

local netId = ECS.getComponent(id, "NetworkId")
if netId then
Expand Down
Loading