Add new maps and tileset for the game world

- Created room1.tmx and room1.lua for the first room in the game, including tiles, collision objects, and NPCs.
- Added room2.tmx and room2.lua for the second room, featuring tiles, collision objects, and an enemy.
- Introduced a world template to manage map transitions and layout.
- Implemented a new tileset (castle.tsx and castle.lua) for the castle theme, including tile properties and image references.
This commit is contained in:
2026-05-01 17:43:39 +02:00
parent ca9edf5f0c
commit 1b6eb9152c
35 changed files with 1747 additions and 4 deletions

View File

@@ -0,0 +1,30 @@
---@class ArenaHazard : Bullet
local ArenaHazard, super = Class(Bullet)
---@param x number # The X position of the bullet
---@param y number # The Y position of the bullet
---@param rot number # The rotation (in radians) of the bullet
function ArenaHazard:init(x, y, rot)
-- Last argument = sprite path
super.init(self, x, y, "bullets/arenahazard")
-- Top-center origin point (will be rotated around it)
self:setOrigin(0.5, 0)
-- The hitbox where the player will be damaged by the bullet (affected by scale and rotation)
self:setHitbox(0, 0, self.width, 8)
-- Rotation of the bullet (in radians)
self.rotation = rot
-- Don't destroy this bullet when it damages the player
self.destroy_on_hit = false
end
function ArenaHazard:update()
-- For more complicated bullet behaviours, code here gets called every update
super.update(self)
end
return ArenaHazard

View File

@@ -0,0 +1,24 @@
---@class SmallBullet : Bullet
local SmallBullet, super = Class(Bullet)
---@param x number # The X position of the bullet
---@param y number # The Y position of the bullet
---@param dir number # The dir (in radians) of the bullet
---@param speed number # The speed the bullet will move at in the specified direction
function SmallBullet:init(x, y, dir, speed)
-- Last argument = sprite path
super.init(self, x, y, "bullets/smallbullet")
-- Move the bullet in dir radians (0 = right, pi = left, clockwise rotation)
self.physics.direction = dir
-- Speed the bullet moves (pixels per frame at 30FPS)
self.physics.speed = speed
end
function SmallBullet:update()
-- For more complicated bullet behaviours, code here gets called every update
super.update(self)
end
return SmallBullet

View File

@@ -0,0 +1,24 @@
return {
-- The inclusion of the below line tells the language server that the first parameter of the cutscene is `BattleCutscene`.
-- This allows it to fetch us useful documentation that shows all of the available cutscene functions while writing our cutscenes!
---@param cutscene BattleCutscene
susie_punch = function(cutscene, battler, enemy)
-- Open textbox and wait for completion
cutscene:text("* Susie threw a punch at\nthe dummy.")
-- Hurt the target enemy for 1 damage
Assets.playSound("damage")
enemy:hurt(1, battler)
-- Wait 1 second
cutscene:wait(1)
-- Susie text
cutscene:text("* You,[wait:5] uh,[wait:5] look like a weenie.[wait:5]\n* I don't like beating up\npeople like that.", "nervous_side", "susie")
if cutscene:getCharacter("ralsei") then
-- Ralsei text, if he's in the party
cutscene:text("* Aww,[wait:5] Susie!", "blush_pleased", "ralsei")
end
end
}

View File

@@ -0,0 +1,21 @@
local Dummy, super = Class(Encounter)
function Dummy:init()
super.init(self)
-- Text displayed at the bottom of the screen at the start of the encounter
self.text = "* The tutorial begins...?"
-- Battle music ("battle" is rude buster)
self.music = "battle"
-- Enables the purple grid battle background
self.background = true
-- Add the dummy enemy to the encounter
self:addEnemy("dummy")
--- Uncomment this line to add another!
--self:addEnemy("dummy")
end
return Dummy

View File

@@ -0,0 +1,96 @@
local Dummy, super = Class(EnemyBattler)
function Dummy:init()
super.init(self)
-- Enemy name
self.name = "Dummy"
-- Sets the actor, which handles the enemy's sprites (see scripts/data/actors/dummy.lua)
self:setActor("dummy")
-- Enemy health
self.max_health = 450
self.health = 450
-- Enemy attack (determines bullet damage)
self.attack = 4
-- Enemy defense (usually 0)
self.defense = 0
-- Enemy reward
self.money = 100
-- Mercy given when sparing this enemy before its spareable (20% for basic enemies)
self.spare_points = 20
-- List of possible wave ids, randomly picked each turn
self.waves = {
"basic",
"aiming",
"movingarena"
}
-- Dialogue randomly displayed in the enemy's speech bubble
self.dialogue = {
"..."
}
-- Check text (automatically has "ENEMY NAME - " at the start)
self.check = "AT 4 DF 0\n* Cotton heart and button eye\n* Looks just like a fluffy guy."
-- Text randomly displayed at the bottom of the screen each turn
self.text = {
"* The dummy gives you a soft\nsmile.",
"* The power of fluffy boys is\nin the air.",
"* Smells like cardboard.",
}
-- Text displayed at the bottom of the screen when the enemy has low health
self.low_health_text = "* The dummy looks like it's\nabout to fall over."
-- Register act called "Smile"
self:registerAct("Smile")
-- Register party act with Ralsei called "Tell Story"
-- (second argument is description, usually empty)
self:registerAct("Tell Story", "", {"ralsei"})
end
function Dummy:onAct(battler, name)
if name == "Smile" then
-- Give the enemy 100% mercy
self:addMercy(100)
-- Change this enemy's dialogue for 1 turn
self.dialogue_override = "... ^^"
-- Act text (since it's a list, multiple textboxes)
return {
"* You smile.[wait:5]\n* The dummy smiles back.",
"* It seems the dummy just wanted\nto see you happy."
}
elseif name == "Tell Story" then
-- Loop through all enemies
for _, enemy in ipairs(Game.battle.enemies) do
-- Make the enemy tired
enemy:setTired(true)
end
return "* You and Ralsei told the dummy\na bedtime story.\n* The enemies became [color:blue]TIRED[color:reset]..."
elseif name == "Standard" then --X-Action
-- Give the enemy 50% mercy
self:addMercy(50)
if battler.chara.id == "ralsei" then
-- R-Action text
return "* Ralsei bowed politely.\n* The dummy spiritually bowed\nin return."
elseif battler.chara.id == "susie" then
-- S-Action: start a cutscene (see scripts/battle/cutscenes/dummy.lua)
Game.battle:startActCutscene("dummy", "susie_punch")
return
else
-- Text for any other character (like Noelle)
return "* "..battler.chara:getName().." straightened the\ndummy's hat."
end
end
-- If the act is none of the above, run the base onAct function
-- (this handles the Check act)
return super.onAct(self, battler, name)
end
return Dummy

View File

@@ -0,0 +1,30 @@
local Aiming, super = Class(Wave)
function Aiming:onStart()
-- Every 0.5 seconds...
self.timer:every(1 / 2, function()
-- Get all enemies that selected this wave as their attack
local attackers = self:getAttackers()
-- Loop through all attackers
for _, attacker in ipairs(attackers) do
-- Get the attacker's center position
local x, y = attacker:getRelativePos(attacker.width / 2, attacker.height / 2)
-- Get the angle between the bullet position and the soul's position
local angle = MathUtils.angle(x, y, Game.battle.soul.x, Game.battle.soul.y)
-- Spawn smallbullet angled towards the player with speed 8 (see scripts/battle/bullets/smallbullet.lua)
self:spawnBullet("smallbullet", x, y, angle, 8)
end
end)
end
function Aiming:update()
-- Code here gets called every frame
super.update(self)
end
return Aiming

View File

@@ -0,0 +1,25 @@
local Basic, super = Class(Wave)
function Basic:onStart()
-- Every 0.33 seconds...
self.timer:every(1 / 3, function()
-- Our X position is offscreen, to the right
local x = SCREEN_WIDTH + 20
-- Get a random Y position between the top and the bottom of the arena
local y = MathUtils.random(Game.battle.arena.top, Game.battle.arena.bottom)
-- Spawn smallbullet going left with speed 8 (see scripts/battle/bullets/smallbullet.lua)
local bullet = self:spawnBullet("smallbullet", x, y, math.rad(180), 8)
-- Dont remove the bullet offscreen, because we spawn it offscreen
bullet.remove_offscreen = false
end)
end
function Basic:update()
-- Code here gets called every frame
super.update(self)
end
return Basic

View File

@@ -0,0 +1,38 @@
local MovingArena, super = Class(Wave)
function MovingArena:init()
super.init(self)
-- Initialize timer
self.siner = 0
end
function MovingArena:onStart()
-- Get the arena object
local arena = Game.battle.arena
-- Spawn spikes on top of arena
self:spawnBulletTo(Game.battle.arena, "arenahazard", arena.width / 2, 0, math.rad(0))
-- Spawn spikes on bottom of arena (rotated 180 degrees)
self:spawnBulletTo(Game.battle.arena, "arenahazard", arena.width / 2, arena.height, math.rad(180))
-- Store starting arena position
self.arena_start_x = arena.x
self.arena_start_y = arena.y
end
function MovingArena:update()
-- Increment timer for arena movement
self.siner = self.siner + DT
-- Calculate the arena Y offset
local offset = math.sin(self.siner * 1.5) * 60
-- Move the arena
Game.battle.arena:setPosition(self.arena_start_x, self.arena_start_y + offset)
super.update(self)
end
return MovingArena