diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..11d6ad4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+.vscode/
+*.tiled-session
\ No newline at end of file
diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index 9d9cd18..0000000
--- a/.gitmodules
+++ /dev/null
@@ -1,3 +0,0 @@
-[submodule "Kristal"]
- path = Kristal
- url = https://github.com/KristalTeam/Kristal
diff --git a/Kristal b/Kristal
deleted file mode 160000
index 4f6de32..0000000
--- a/Kristal
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 4f6de329a7336d85c732aaef704533dc37544c98
diff --git a/assets/sounds/squeak.wav b/assets/sounds/squeak.wav
new file mode 100644
index 0000000..2d6c494
Binary files /dev/null and b/assets/sounds/squeak.wav differ
diff --git a/assets/sprites/bullets/arenahazard.png b/assets/sprites/bullets/arenahazard.png
new file mode 100644
index 0000000..b2dca05
Binary files /dev/null and b/assets/sprites/bullets/arenahazard.png differ
diff --git a/assets/sprites/bullets/smallbullet.png b/assets/sprites/bullets/smallbullet.png
new file mode 100644
index 0000000..2eff0db
Binary files /dev/null and b/assets/sprites/bullets/smallbullet.png differ
diff --git a/assets/sprites/enemies/dummy/idle.png b/assets/sprites/enemies/dummy/idle.png
new file mode 100644
index 0000000..280f9dd
Binary files /dev/null and b/assets/sprites/enemies/dummy/idle.png differ
diff --git a/assets/sprites/npcs/starwalker.png b/assets/sprites/npcs/starwalker.png
new file mode 100644
index 0000000..ee74969
Binary files /dev/null and b/assets/sprites/npcs/starwalker.png differ
diff --git a/assets/sprites/npcs/wall_1.png b/assets/sprites/npcs/wall_1.png
new file mode 100644
index 0000000..174a9fb
Binary files /dev/null and b/assets/sprites/npcs/wall_1.png differ
diff --git a/assets/sprites/npcs/wall_2.png b/assets/sprites/npcs/wall_2.png
new file mode 100644
index 0000000..2877f59
Binary files /dev/null and b/assets/sprites/npcs/wall_2.png differ
diff --git a/assets/sprites/tilesets/castle.png b/assets/sprites/tilesets/castle.png
new file mode 100644
index 0000000..e1f9d81
Binary files /dev/null and b/assets/sprites/tilesets/castle.png differ
diff --git a/halloween_hack.tiled-project b/halloween_hack.tiled-project
new file mode 100644
index 0000000..7b0999c
--- /dev/null
+++ b/halloween_hack.tiled-project
@@ -0,0 +1,12 @@
+{
+ "automappingRulesFile": "",
+ "commands": [
+ ],
+ "extensionsPath": "extensions",
+ "folders": [
+ "scripts/world"
+ ],
+ "objectTypesFile": "",
+ "propertyTypes": [
+ ]
+}
diff --git a/mod.json b/mod.json
new file mode 100644
index 0000000..61445f0
--- /dev/null
+++ b/mod.json
@@ -0,0 +1,70 @@
+{
+ // The ID of your project. Should be unique!!
+ "id": "halloween_hack",
+ // Displays on the main menu.
+ "name": "Halloween_hack",
+ // Displays underneath the name. Optional.
+ "subtitle": "",
+
+ // The version of your project.
+ "version": "v1.0.0",
+ // What version of the engine your project was made with.
+ "engineVer": "v0.10.0-dev",
+
+ // The Deltarune chapter you'd like to base your project off of.
+ // Do keep in mind that you can control chapter-specific features
+ // one by one using the config below.
+ "chapter": 4,
+
+ // The map that you start in when first starting the project.
+ "map": "room1",
+
+ // The party. The first character is the player.
+ "party": ["kris", "susie", "ralsei"],
+
+ // The inventory. Contains three darkburgers, a cell phone and a shadow crystal by default.
+ "inventory": {
+ "items": ["glowshard", "darkburger", "darkburger", "darkburger"],
+ "key_items": ["cell_phone", "shadowcrystal"]
+ },
+
+ // Equipment for your party. Not specifying equipment defaults to the following.
+ "equipment": {
+ "kris": {
+ "weapon": "wood_blade",
+ "armor": ["amber_card", "amber_card"]
+ },
+ "susie": {
+ "weapon": "mane_ax",
+ "armor": ["amber_card", "amber_card"]
+ },
+ "ralsei": {
+ "weapon": "red_scarf",
+ "armor": ["amber_card", "amber_card"]
+ }
+ },
+
+ // Should never be true, but just in case. Restarts the entire engine when leaving the project.
+ // If you need this, you're most likely doing something wrong.
+ "hardReset": false,
+
+ // Whether the project is hidden from the project selection.
+ "hidden": false,
+
+ // Whether the game window's title should be set to the project's name, and the icon to the image
+ // in the file `window_icon.png`.
+ // When your project is configured as the engine's target project, it's automatically done unless if
+ // this option is explicitly set to false; else, it's done if this is set to true.
+ "setWindowTitleAndIcon": null,
+
+ // Config values for the engine and any libraries you may have.
+ // These config values can control chapter-specific features as well.
+ "config": {
+ "kristal": {
+ // End of config
+ }
+ },
+
+ // Whether or not to enable dev mode. Dev mode enables debug keys and the console.
+ "dev": true
+}
\ No newline at end of file
diff --git a/mod.lua b/mod.lua
new file mode 100644
index 0000000..cc2ed5d
--- /dev/null
+++ b/mod.lua
@@ -0,0 +1,6 @@
+function Mod:init()
+ Game:registerEvent("squeak", function(data)
+ return Squeak(data.x, data.y, {data.width, data.height, data.polygon})
+ end)
+ print("Loaded " .. self.info.name .. "!")
+end
diff --git a/scripts/battle/bullets/arenahazard.lua b/scripts/battle/bullets/arenahazard.lua
new file mode 100644
index 0000000..a901f33
--- /dev/null
+++ b/scripts/battle/bullets/arenahazard.lua
@@ -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
diff --git a/scripts/battle/bullets/smallbullet.lua b/scripts/battle/bullets/smallbullet.lua
new file mode 100644
index 0000000..4eb9582
--- /dev/null
+++ b/scripts/battle/bullets/smallbullet.lua
@@ -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
diff --git a/scripts/battle/cutscenes/dummy.lua b/scripts/battle/cutscenes/dummy.lua
new file mode 100644
index 0000000..353c28d
--- /dev/null
+++ b/scripts/battle/cutscenes/dummy.lua
@@ -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
+}
\ No newline at end of file
diff --git a/scripts/battle/encounters/dummy.lua b/scripts/battle/encounters/dummy.lua
new file mode 100644
index 0000000..9ba7cae
--- /dev/null
+++ b/scripts/battle/encounters/dummy.lua
@@ -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
diff --git a/scripts/battle/enemies/dummy.lua b/scripts/battle/enemies/dummy.lua
new file mode 100644
index 0000000..f3d5fbc
--- /dev/null
+++ b/scripts/battle/enemies/dummy.lua
@@ -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
\ No newline at end of file
diff --git a/scripts/battle/waves/aiming.lua b/scripts/battle/waves/aiming.lua
new file mode 100644
index 0000000..df83723
--- /dev/null
+++ b/scripts/battle/waves/aiming.lua
@@ -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
diff --git a/scripts/battle/waves/basic.lua b/scripts/battle/waves/basic.lua
new file mode 100644
index 0000000..02db057
--- /dev/null
+++ b/scripts/battle/waves/basic.lua
@@ -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
diff --git a/scripts/battle/waves/movingarena.lua b/scripts/battle/waves/movingarena.lua
new file mode 100644
index 0000000..18d4f8d
--- /dev/null
+++ b/scripts/battle/waves/movingarena.lua
@@ -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
diff --git a/scripts/data/actors/dummy.lua b/scripts/data/actors/dummy.lua
new file mode 100644
index 0000000..c97eb8f
--- /dev/null
+++ b/scripts/data/actors/dummy.lua
@@ -0,0 +1,54 @@
+local actor, super = Class(Actor, "dummy")
+
+function actor:init()
+ super.init(self)
+
+ -- Display name (optional)
+ self.name = "Dummy"
+
+ -- Width and height for this actor, used to determine its center
+ self.width = 27
+ self.height = 45
+
+ -- Hitbox for this actor in the overworld (optional, uses width and height by default)
+ self.hitbox = { 0, 25, 19, 14 }
+
+ -- Color for this actor used in outline areas (optional, defaults to red)
+ self.color = { 1, 0, 0 }
+
+ -- Whether this actor flips horizontally (optional, values are "right" or "left", indicating the flip direction)
+ self.flip = nil
+
+ -- Path to this actor's sprites (defaults to "")
+ self.path = "enemies/dummy"
+ -- This actor's default sprite or animation, relative to the path (defaults to "")
+ self.default = "idle"
+
+ -- Sound to play when this actor speaks (optional)
+ self.voice = nil
+ -- Path to this actor's portrait for dialogue (optional)
+ self.portrait_path = nil
+ -- Offset position for this actor's portrait (optional)
+ self.portrait_offset = nil
+
+ -- Whether this actor as a follower will blush when close to the player
+ self.can_blush = false
+
+ -- Table of talk sprites and their talk speeds (default 0.25)
+ self.talk_sprites = {}
+
+ -- Table of sprite animations
+ self.animations = {
+ -- Looping animation with 0.25 seconds between each frame
+ -- (even though there's only 1 idle frame)
+ ["idle"] = { "idle", 0.25, true },
+ }
+
+ -- Table of sprite offsets (indexed by sprite name)
+ self.offsets = {
+ -- Since the width and height is the idle sprite size, the offset is 0,0
+ ["idle"] = { 0, 0 },
+ }
+end
+
+return actor
diff --git a/scripts/data/actors/starwalker.lua b/scripts/data/actors/starwalker.lua
new file mode 100644
index 0000000..d435adf
--- /dev/null
+++ b/scripts/data/actors/starwalker.lua
@@ -0,0 +1,47 @@
+local actor, super = Class(Actor, "starwalker")
+
+function actor:init()
+ super.init(self)
+
+ -- Display name (optional)
+ self.name = "Starwalker"
+
+ -- Width and height for this actor, used to determine its center
+ self.width = 37
+ self.height = 36
+
+ -- Hitbox for this actor in the overworld (optional, uses width and height by default)
+ self.hitbox = { 2, 26, 27, 10 }
+
+ -- Color for this actor used in outline areas (optional, defaults to red)
+ self.color = { 1, 1, 0 }
+
+ -- Whether this actor flips horizontally (optional, values are "right" or "left", indicating the flip direction)
+ self.flip = nil
+
+ -- Path to this actor's sprites (defaults to "")
+ self.path = "npcs/starwalker"
+ -- This actor's default sprite or animation, relative to the path (defaults to "")
+ self.default = ""
+
+ -- Sound to play when this actor speaks (optional)
+ self.voice = nil
+ -- Path to this actor's portrait for dialogue (optional)
+ self.portrait_path = nil
+ -- Offset position for this actor's portrait (optional)
+ self.portrait_offset = nil
+
+ -- Whether this actor as a follower will blush when close to the player
+ self.can_blush = false
+
+ -- Table of talk sprites and their talk speeds (default 0.25)
+ self.talk_sprites = {}
+
+ -- Table of sprite animations
+ self.animations = {}
+
+ -- Table of sprite offsets (indexed by sprite name)
+ self.offsets = {}
+end
+
+return actor
diff --git a/scripts/data/actors/wall.lua b/scripts/data/actors/wall.lua
new file mode 100644
index 0000000..5a66215
--- /dev/null
+++ b/scripts/data/actors/wall.lua
@@ -0,0 +1,49 @@
+local actor, super = Class(Actor, "wall")
+
+function actor:init()
+ super.init(self)
+
+ -- Display name (optional)
+ self.name = "Wall"
+
+ -- Width and height for this actor, used to determine its center
+ self.width = 60
+ self.height = 70
+
+ -- Hitbox for this actor in the overworld (optional, uses width and height by default)
+ self.hitbox = { 0, 50, 60, 20 }
+
+ -- Color for this actor used in outline areas (optional, defaults to red)
+ self.color = { 1, 0, 0 }
+
+ -- Whether this actor flips horizontally (optional, values are "right" or "left", indicating the flip direction)
+ self.flip = nil
+
+ -- Path to this actor's sprites (defaults to "")
+ self.path = "npcs/wall"
+ -- This actor's default sprite or animation, relative to the path (defaults to "")
+ self.default = ""
+
+ -- Sound to play when this actor speaks (optional)
+ self.voice = nil
+ -- Path to this actor's portrait for dialogue (optional)
+ self.portrait_path = nil
+ -- Offset position for this actor's portrait (optional)
+ self.portrait_offset = nil
+
+ -- Whether this actor as a follower will blush when close to the player
+ self.can_blush = false
+
+ -- Table of talk sprites and their talk speeds (default 0.25)
+ self.talk_sprites = {
+ [""] = 0.2
+ }
+
+ -- Table of sprite animations
+ self.animations = {}
+
+ -- Table of sprite offsets (indexed by sprite name)
+ self.offsets = {}
+end
+
+return actor
diff --git a/scripts/data/items/ultimate_candy.lua b/scripts/data/items/ultimate_candy.lua
new file mode 100644
index 0000000..2a8a0a0
--- /dev/null
+++ b/scripts/data/items/ultimate_candy.lua
@@ -0,0 +1,58 @@
+-- Instead of Item, create a HealItem, a convenient class for consumable healing items
+local item, super = Class(HealItem, "ultimate_candy")
+
+function item:init()
+ super.init(self)
+
+ -- Display name
+ self.name = "UltimatCandy"
+ -- Name displayed when used in battle (optional)
+ self.use_name = "ULTIMATE CANDY"
+
+ -- Item type (item, key, weapon, armor)
+ self.type = "item"
+ -- Item icon (for equipment)
+ self.icon = nil
+
+ -- Battle description
+ self.effect = "Best\nhealing"
+ -- Shop description
+ self.shop = "Perfection"
+ -- Menu description
+ self.description = "Sparkles with perfection.\nMust be shared with everyone. +??HP"
+
+ -- Amount healed (HealItem variable)
+ self.heal_amount = 1
+
+ -- Default shop price (sell price is halved)
+ self.price = 100
+ -- Whether the item can be sold
+ self.can_sell = true
+
+ -- Consumable target mode (ally, party, enemy, enemies, or none)
+ self.target = "party"
+ -- Where this item can be used (world, battle, all, or none)
+ self.usable_in = "all"
+ -- Item this item will get turned into when consumed
+ self.result_item = nil
+ -- Will this item be instantly consumed in battles?
+ self.instant = false
+
+ -- Equip bonuses (for weapons and armor)
+ self.bonuses = {}
+ -- Bonus name and icon (displayed in equip menu)
+ self.bonus_name = nil
+ self.bonus_icon = nil
+
+ -- Equippable characters (default true for armors, false for weapons)
+ self.can_equip = {}
+
+ -- Character reactions (key = party member id)
+ self.reactions = {
+ susie = "Hey! It's hollow inside!",
+ ralsei = "I like the texture!",
+ noelle = "That was underwhelming...",
+ }
+end
+
+return item
diff --git a/scripts/objects/Squeak.lua b/scripts/objects/Squeak.lua
new file mode 100644
index 0000000..820824c
--- /dev/null
+++ b/scripts/objects/Squeak.lua
@@ -0,0 +1,13 @@
+---@class Squeak : Event
+local Squeak, super = Class(Event)
+
+function Squeak:init(x, y, shape)
+ super.init(self, x, y, shape)
+end
+
+function Squeak:onInteract(player, dir)
+ Assets.playSound("squeak")
+ return true
+end
+
+return Squeak
diff --git a/scripts/world/cutscenes/room1.lua b/scripts/world/cutscenes/room1.lua
new file mode 100644
index 0000000..dd97b09
--- /dev/null
+++ b/scripts/world/cutscenes/room1.lua
@@ -0,0 +1,74 @@
+return {
+ -- The inclusion of the below line tells the language server that the first parameter of the cutscene is `WorldCutscene`.
+ -- This allows it to fetch us useful documentation that shows all of the available cutscene functions while writing our cutscenes!
+
+ ---@param cutscene WorldCutscene
+ wall = function(cutscene, event)
+ -- Open textbox and wait for completion
+ cutscene:text("* The wall seems cracked.")
+
+ -- If we have Susie, play a cutscene
+ local susie = cutscene:getCharacter("susie")
+ if susie then
+ -- Detach camera and followers (since characters will be moved)
+ cutscene:detachCamera()
+ cutscene:detachFollowers()
+
+ -- All text from now is spoken by Susie
+ cutscene:setSpeaker(susie)
+ cutscene:text("* Hey,[wait:5] think I can break\nthis wall?", "smile")
+
+ -- Get the bottom-center of the broken wall
+ local x = event.x + event.width / 2
+ local y = event.y + event.height / 2
+
+ -- Move Susie up to the wall over 0.75 seconds
+ cutscene:walkTo(susie, x, y + 40, 0.75, "up")
+ -- Move other party members behind Susie
+ cutscene:walkTo(Game.world.player, x, y + 100, 0.75, "up")
+ if cutscene:getCharacter("ralsei") then
+ cutscene:walkTo("ralsei", x + 60, y + 100, 0.75, "up")
+ end
+ if cutscene:getCharacter("noelle") then
+ cutscene:walkTo("noelle", x - 60, y + 100, 0.75, "up")
+ end
+
+ -- Wait 1.5 seconds
+ cutscene:wait(1.5)
+
+ -- Walk back,
+ cutscene:wait(cutscene:walkTo(susie, x, y + 60, 0.5, "up", true))
+ -- and run forward!
+ cutscene:wait(cutscene:walkTo(susie, x, y + 20, 0.2))
+
+ -- Slam!!
+ Assets.playSound("impact")
+ susie:shake(4)
+ susie:setSprite("shock_up")
+
+ -- Slide back a bit
+ cutscene:slideTo(susie, x, y + 40, 0.1)
+ cutscene:wait(1.5)
+
+ -- owie
+ susie:setAnimation({ "away_scratch", 0.25, true })
+ susie:shake(4)
+ Assets.playSound("wing")
+
+ cutscene:wait(1)
+ cutscene:text("* Guess not.", "nervous")
+
+ -- Reset Susie's sprite
+ susie:resetSprite()
+
+ -- Reattach the camera
+ cutscene:attachCamera()
+
+ -- Align the follower positions behind Kris's current position
+ cutscene:alignFollowers()
+ -- And reattach them, making them return to their target positions
+ cutscene:attachFollowers()
+ Game:setFlag("wall_hit", true)
+ end
+ end
+}
diff --git a/scripts/world/maps/room1.lua b/scripts/world/maps/room1.lua
new file mode 100644
index 0000000..29cb11f
--- /dev/null
+++ b/scripts/world/maps/room1.lua
@@ -0,0 +1,550 @@
+return {
+ version = "1.5",
+ luaversion = "5.1",
+ tiledversion = "1.8.4",
+ orientation = "orthogonal",
+ renderorder = "right-down",
+ width = 20,
+ height = 24,
+ tilewidth = 40,
+ tileheight = 40,
+ nextlayerid = 6,
+ nextobjectid = 37,
+ properties = {
+ ["name"] = "Test Map - Room 1"
+ },
+ tilesets = {
+ {
+ name = "castle",
+ firstgid = 1,
+ filename = "../tilesets/castle.tsx"
+ }
+ },
+ layers = {
+ {
+ type = "tilelayer",
+ x = 0,
+ y = 0,
+ width = 20,
+ height = 24,
+ id = 1,
+ name = "tiles",
+ visible = true,
+ opacity = 1,
+ offsetx = 0,
+ offsety = 0,
+ parallaxx = 1,
+ parallaxy = 1,
+ properties = {},
+ encoding = "lua",
+ data = {
+ 0, 22, 13, 23, 23, 13, 23, 23, 23, 13, 23, 23, 13, 24, 0, 0, 0, 0, 0, 0,
+ 0, 22, 23, 23, 23, 23, 23, 21, 23, 23, 23, 23, 23, 24, 0, 0, 0, 0, 0, 0,
+ 0, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 0, 0, 0, 0, 0, 0,
+ 0, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 0, 0, 0, 0, 0, 0,
+ 0, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 0, 0, 0, 0, 0, 0,
+ 0, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 7, 7, 7, 7, 7, 7,
+ 0, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 15, 15, 15, 15, 15, 15,
+ 0, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 0, 0, 0, 0, 0, 0,
+ 0, 14, 15, 15, 11, 11, 11, 11, 11, 11, 11, 15, 15, 16, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 14, 15, 11, 11, 11, 15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 18, 19, 20, 0,
+ 0, 0, 0, 0, 0, 0, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 22, 23, 24, 0,
+ 0, 0, 0, 0, 0, 0, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 22, 9, 24, 0,
+ 0, 0, 0, 0, 0, 0, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, 0,
+ 0, 0, 0, 0, 0, 0, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, 0,
+ 0, 6, 7, 7, 7, 7, 11, 11, 11, 7, 7, 7, 7, 7, 7, 7, 11, 11, 12, 0,
+ 0, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 15, 15, 15, 15, 16, 0,
+ 0, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 0, 0, 0, 0, 0, 0,
+ 0, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 0, 0, 0, 0, 0, 0,
+ 0, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ }
+ },
+ {
+ type = "tilelayer",
+ x = 0,
+ y = 0,
+ width = 20,
+ height = 24,
+ id = 2,
+ name = "decal",
+ visible = true,
+ opacity = 1,
+ offsetx = 0,
+ offsety = 0,
+ parallaxx = 1,
+ parallaxy = 1,
+ properties = {},
+ encoding = "lua",
+ data = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 30, 31, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 34, 35, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 38, 39, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ }
+ },
+ {
+ type = "objectgroup",
+ draworder = "topdown",
+ id = 3,
+ name = "collision",
+ visible = true,
+ opacity = 0.5,
+ offsetx = 0,
+ offsety = 0,
+ parallaxx = 1,
+ parallaxy = 1,
+ properties = {},
+ objects = {
+ {
+ id = 1,
+ name = "",
+ type = "",
+ shape = "rectangle",
+ x = 40,
+ y = 80,
+ width = 520,
+ height = 40,
+ rotation = 0,
+ visible = true,
+ properties = {}
+ },
+ {
+ id = 2,
+ name = "",
+ type = "",
+ shape = "rectangle",
+ x = 560,
+ y = 120,
+ width = 40,
+ height = 80,
+ rotation = 0,
+ visible = true,
+ properties = {}
+ },
+ {
+ id = 3,
+ name = "",
+ type = "",
+ shape = "rectangle",
+ x = 600,
+ y = 160,
+ width = 200,
+ height = 40,
+ rotation = 0,
+ visible = true,
+ properties = {}
+ },
+ {
+ id = 4,
+ name = "",
+ type = "",
+ shape = "rectangle",
+ x = 560,
+ y = 280,
+ width = 40,
+ height = 80,
+ rotation = 0,
+ visible = true,
+ properties = {}
+ },
+ {
+ id = 5,
+ name = "",
+ type = "",
+ shape = "rectangle",
+ x = 600,
+ y = 280,
+ width = 200,
+ height = 40,
+ rotation = 0,
+ visible = true,
+ properties = {}
+ },
+ {
+ id = 6,
+ name = "",
+ type = "",
+ shape = "rectangle",
+ x = 440,
+ y = 360,
+ width = 120,
+ height = 40,
+ rotation = 0,
+ visible = true,
+ properties = {}
+ },
+ {
+ id = 7,
+ name = "",
+ type = "",
+ shape = "rectangle",
+ x = 360,
+ y = 400,
+ width = 80,
+ height = 40,
+ rotation = 0,
+ visible = true,
+ properties = {}
+ },
+ {
+ id = 8,
+ name = "",
+ type = "",
+ shape = "rectangle",
+ x = 360,
+ y = 680,
+ width = 280,
+ height = 40,
+ rotation = 0,
+ visible = true,
+ properties = {}
+ },
+ {
+ id = 9,
+ name = "",
+ type = "",
+ shape = "rectangle",
+ x = 560,
+ y = 800,
+ width = 40,
+ height = 120,
+ rotation = 0,
+ visible = true,
+ properties = {}
+ },
+ {
+ id = 10,
+ name = "",
+ type = "",
+ shape = "rectangle",
+ x = 40,
+ y = 920,
+ width = 520,
+ height = 40,
+ rotation = 0,
+ visible = true,
+ properties = {}
+ },
+ {
+ id = 11,
+ name = "",
+ type = "",
+ shape = "rectangle",
+ x = 0,
+ y = 720,
+ width = 40,
+ height = 200,
+ rotation = 0,
+ visible = true,
+ properties = {}
+ },
+ {
+ id = 12,
+ name = "",
+ type = "",
+ shape = "rectangle",
+ x = 40,
+ y = 680,
+ width = 200,
+ height = 40,
+ rotation = 0,
+ visible = true,
+ properties = {}
+ },
+ {
+ id = 14,
+ name = "",
+ type = "",
+ shape = "rectangle",
+ x = 160,
+ y = 400,
+ width = 80,
+ height = 40,
+ rotation = 0,
+ visible = true,
+ properties = {}
+ },
+ {
+ id = 15,
+ name = "",
+ type = "",
+ shape = "rectangle",
+ x = 40,
+ y = 360,
+ width = 120,
+ height = 40,
+ rotation = 0,
+ visible = true,
+ properties = {}
+ },
+ {
+ id = 16,
+ name = "",
+ type = "",
+ shape = "rectangle",
+ x = 0,
+ y = 120,
+ width = 40,
+ height = 240,
+ rotation = 0,
+ visible = true,
+ properties = {}
+ },
+ {
+ id = 17,
+ name = "",
+ type = "",
+ shape = "rectangle",
+ x = 200,
+ y = 440,
+ width = 40,
+ height = 240,
+ rotation = 0,
+ visible = true,
+ properties = {}
+ },
+ {
+ id = 18,
+ name = "",
+ type = "",
+ shape = "rectangle",
+ x = 360,
+ y = 440,
+ width = 40,
+ height = 240,
+ rotation = 0,
+ visible = true,
+ properties = {}
+ },
+ {
+ id = 23,
+ name = "",
+ type = "",
+ shape = "rectangle",
+ x = 600,
+ y = 640,
+ width = 40,
+ height = 40,
+ rotation = 0,
+ visible = true,
+ properties = {}
+ },
+ {
+ id = 24,
+ name = "",
+ type = "",
+ shape = "rectangle",
+ x = 640,
+ y = 600,
+ width = 120,
+ height = 40,
+ rotation = 0,
+ visible = true,
+ properties = {}
+ },
+ {
+ id = 25,
+ name = "",
+ type = "",
+ shape = "rectangle",
+ x = 760,
+ y = 640,
+ width = 40,
+ height = 160,
+ rotation = 0,
+ visible = true,
+ properties = {}
+ },
+ {
+ id = 26,
+ name = "",
+ type = "",
+ shape = "rectangle",
+ x = 600,
+ y = 800,
+ width = 160,
+ height = 40,
+ rotation = 0,
+ visible = true,
+ properties = {}
+ }
+ }
+ },
+ {
+ type = "objectgroup",
+ draworder = "topdown",
+ id = 4,
+ name = "objects",
+ visible = true,
+ opacity = 1,
+ offsetx = 0,
+ offsety = 0,
+ parallaxx = 1,
+ parallaxy = 1,
+ properties = {},
+ objects = {
+ {
+ id = 19,
+ name = "npc",
+ type = "",
+ shape = "rectangle",
+ x = 480,
+ y = 840,
+ width = 40,
+ height = 40,
+ rotation = 0,
+ visible = true,
+ properties = {
+ ["actor"] = "starwalker",
+ ["text1"] = "* These [color:yellow]stairs[color:reset] are [color:yellow]Pissing[color:reset] me\noff...",
+ ["text2"] = "* I'm the original [color:yellow]Starwalker[color:reset]"
+ }
+ },
+ {
+ id = 27,
+ name = "savepoint",
+ type = "",
+ shape = "rectangle",
+ x = 80,
+ y = 210,
+ width = 40,
+ height = 40,
+ rotation = 0,
+ visible = true,
+ properties = {
+ ["text1"] = "* Silence echoes in the darkness\nof this familiar-yet-different\nscenery.",
+ ["text2"] = "* The power of avoiding copying\nofficial music shines within\nyou."
+ }
+ },
+ {
+ id = 28,
+ name = "squeak",
+ type = "",
+ shape = "rectangle",
+ x = 680,
+ y = 600,
+ width = 40,
+ height = 40,
+ rotation = 0,
+ visible = true,
+ properties = {}
+ },
+ {
+ id = 29,
+ name = "transition",
+ type = "",
+ shape = "rectangle",
+ x = 800,
+ y = 200,
+ width = 40,
+ height = 80,
+ rotation = 0,
+ visible = true,
+ properties = {
+ ["map"] = "room2",
+ ["marker"] = "entry"
+ }
+ },
+ {
+ id = 33,
+ name = "interactable",
+ type = "",
+ shape = "rectangle",
+ x = 280,
+ y = 80,
+ width = 40,
+ height = 40,
+ rotation = 0,
+ visible = true,
+ properties = {
+ ["cutscene"] = "room1.wall",
+ ["once"] = true
+ }
+ },
+ {
+ id = 35,
+ name = "npc",
+ type = "",
+ shape = "point",
+ x = 300,
+ y = 160,
+ width = 0,
+ height = 0,
+ rotation = 0,
+ visible = true,
+ properties = {
+ ["actor"] = "wall",
+ ["flagcheck"] = "wall_hit",
+ ["text1"] = "* I Am the Wall Guardian.[wait:5]\n* This Wall is Off Limits for you\nno-good wall slammers."
+ }
+ }
+ }
+ },
+ {
+ type = "objectgroup",
+ draworder = "topdown",
+ id = 5,
+ name = "markers",
+ visible = true,
+ opacity = 1,
+ offsetx = 0,
+ offsety = 0,
+ parallaxx = 1,
+ parallaxy = 1,
+ properties = {},
+ objects = {
+ {
+ id = 20,
+ name = "spawn",
+ type = "",
+ shape = "point",
+ x = 300,
+ y = 250,
+ width = 0,
+ height = 0,
+ rotation = 0,
+ visible = true,
+ properties = {}
+ },
+ {
+ id = 30,
+ name = "entry",
+ type = "",
+ shape = "point",
+ x = 760,
+ y = 240,
+ width = 0,
+ height = 0,
+ rotation = 0,
+ visible = true,
+ properties = {}
+ }
+ }
+ }
+ }
+}
diff --git a/scripts/world/maps/room1.tmx b/scripts/world/maps/room1.tmx
new file mode 100644
index 0000000..60ca6ca
--- /dev/null
+++ b/scripts/world/maps/room1.tmx
@@ -0,0 +1,140 @@
+
+
diff --git a/scripts/world/maps/room2.lua b/scripts/world/maps/room2.lua
new file mode 100644
index 0000000..ca1b109
--- /dev/null
+++ b/scripts/world/maps/room2.lua
@@ -0,0 +1,255 @@
+return {
+ version = "1.5",
+ luaversion = "5.1",
+ tiledversion = "1.8.4",
+ orientation = "orthogonal",
+ renderorder = "right-down",
+ width = 20,
+ height = 12,
+ tilewidth = 40,
+ tileheight = 40,
+ nextlayerid = 6,
+ nextobjectid = 12,
+ properties = {
+ ["name"] = "Test Map - Room 2"
+ },
+ tilesets = {
+ {
+ name = "castle",
+ firstgid = 1,
+ filename = "../tilesets/castle.tsx"
+ }
+ },
+ layers = {
+ {
+ type = "tilelayer",
+ x = 0,
+ y = 0,
+ width = 20,
+ height = 12,
+ id = 1,
+ name = "tiles",
+ visible = true,
+ opacity = 1,
+ offsetx = 0,
+ offsety = 0,
+ parallaxx = 1,
+ parallaxy = 1,
+ properties = {},
+ encoding = "lua",
+ data = {
+ 0, 0, 0, 0, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 0,
+ 0, 0, 0, 0, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 0,
+ 0, 0, 0, 0, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 0,
+ 0, 0, 0, 0, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 0,
+ 0, 0, 0, 0, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 0,
+ 0, 0, 0, 0, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 0,
+ 0, 0, 0, 0, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 0,
+ 0, 0, 0, 0, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 0,
+ 7, 7, 7, 7, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 0,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ }
+ },
+ {
+ type = "tilelayer",
+ x = 0,
+ y = 0,
+ width = 20,
+ height = 12,
+ id = 2,
+ name = "decal",
+ visible = true,
+ opacity = 1,
+ offsetx = 0,
+ offsety = 0,
+ parallaxx = 1,
+ parallaxy = 1,
+ properties = {},
+ encoding = "lua",
+ data = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 32, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 35, 36, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 39, 40, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ }
+ },
+ {
+ type = "objectgroup",
+ draworder = "topdown",
+ id = 3,
+ name = "collision",
+ visible = true,
+ opacity = 0.5,
+ offsetx = 0,
+ offsety = 0,
+ parallaxx = 1,
+ parallaxy = 1,
+ properties = {},
+ objects = {
+ {
+ id = 1,
+ name = "",
+ type = "",
+ shape = "rectangle",
+ x = 120,
+ y = 120,
+ width = 40,
+ height = 200,
+ rotation = 0,
+ visible = true,
+ properties = {}
+ },
+ {
+ id = 2,
+ name = "",
+ type = "",
+ shape = "rectangle",
+ x = 0,
+ y = 400,
+ width = 760,
+ height = 40,
+ rotation = 0,
+ visible = true,
+ properties = {}
+ },
+ {
+ id = 3,
+ name = "",
+ type = "",
+ shape = "rectangle",
+ x = 760,
+ y = 120,
+ width = 40,
+ height = 280,
+ rotation = 0,
+ visible = true,
+ properties = {}
+ },
+ {
+ id = 4,
+ name = "",
+ type = "",
+ shape = "rectangle",
+ x = 160,
+ y = 80,
+ width = 600,
+ height = 40,
+ rotation = 0,
+ visible = true,
+ properties = {}
+ },
+ {
+ id = 7,
+ name = "",
+ type = "",
+ shape = "rectangle",
+ x = 0,
+ y = 280,
+ width = 120,
+ height = 40,
+ rotation = 0,
+ visible = true,
+ properties = {}
+ }
+ }
+ },
+ {
+ type = "objectgroup",
+ draworder = "topdown",
+ id = 4,
+ name = "markers",
+ visible = true,
+ opacity = 1,
+ offsetx = 0,
+ offsety = 0,
+ parallaxx = 1,
+ parallaxy = 1,
+ properties = {},
+ objects = {
+ {
+ id = 5,
+ name = "spawn",
+ type = "",
+ shape = "point",
+ x = 360,
+ y = 240,
+ width = 0,
+ height = 0,
+ rotation = 0,
+ visible = true,
+ properties = {}
+ },
+ {
+ id = 8,
+ name = "entry",
+ type = "",
+ shape = "point",
+ x = 40,
+ y = 360,
+ width = 0,
+ height = 0,
+ rotation = 0,
+ visible = true,
+ properties = {}
+ }
+ }
+ },
+ {
+ type = "objectgroup",
+ draworder = "topdown",
+ id = 5,
+ name = "objects",
+ visible = true,
+ opacity = 1,
+ offsetx = 0,
+ offsety = 0,
+ parallaxx = 1,
+ parallaxy = 1,
+ properties = {},
+ objects = {
+ {
+ id = 6,
+ name = "enemy",
+ type = "",
+ shape = "rectangle",
+ x = 530,
+ y = 220,
+ width = 40,
+ height = 40,
+ rotation = 0,
+ visible = true,
+ properties = {
+ ["actor"] = "dummy",
+ ["encounter"] = "dummy"
+ }
+ },
+ {
+ id = 9,
+ name = "transition",
+ type = "",
+ shape = "rectangle",
+ x = -40,
+ y = 320,
+ width = 40,
+ height = 80,
+ rotation = 0,
+ visible = true,
+ properties = {
+ ["map"] = "room1",
+ ["marker"] = "entry"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/scripts/world/maps/room2.tmx b/scripts/world/maps/room2.tmx
new file mode 100644
index 0000000..482313b
--- /dev/null
+++ b/scripts/world/maps/room2.tmx
@@ -0,0 +1,71 @@
+
+
diff --git a/scripts/world/template.world b/scripts/world/template.world
new file mode 100644
index 0000000..1cc12c8
--- /dev/null
+++ b/scripts/world/template.world
@@ -0,0 +1,20 @@
+{
+ "maps": [
+ {
+ "fileName": "maps/room1.tmx",
+ "height": 960,
+ "width": 800,
+ "x": 0,
+ "y": 0
+ },
+ {
+ "fileName": "maps/room2.tmx",
+ "height": 480,
+ "width": 800,
+ "x": 880,
+ "y": -120
+ }
+ ],
+ "onlyShowAdjacentMaps": false,
+ "type": "world"
+}
diff --git a/scripts/world/tilesets/castle.lua b/scripts/world/tilesets/castle.lua
new file mode 100644
index 0000000..ff30464
--- /dev/null
+++ b/scripts/world/tilesets/castle.lua
@@ -0,0 +1,31 @@
+return {
+ version = "1.10",
+ luaversion = "5.1",
+ tiledversion = "1.10.2",
+ name = "castle",
+ class = "",
+ tilewidth = 40,
+ tileheight = 40,
+ spacing = 0,
+ margin = 0,
+ columns = 4,
+ image = "../../../assets/sprites/tilesets/castle.png",
+ imagewidth = 160,
+ imageheight = 400,
+ objectalignment = "unspecified",
+ tilerendersize = "tile",
+ fillmode = "stretch",
+ tileoffset = {
+ x = 0,
+ y = 0
+ },
+ grid = {
+ orientation = "orthogonal",
+ width = 40,
+ height = 40
+ },
+ properties = {},
+ wangsets = {},
+ tilecount = 40,
+ tiles = {}
+}
diff --git a/scripts/world/tilesets/castle.tsx b/scripts/world/tilesets/castle.tsx
new file mode 100644
index 0000000..2254cc7
--- /dev/null
+++ b/scripts/world/tilesets/castle.tsx
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+