diff --git a/assets/sprites/bullets/example_bullet.png b/assets/sprites/bullets/example_bullet.png new file mode 100644 index 0000000..633b32f Binary files /dev/null and b/assets/sprites/bullets/example_bullet.png differ diff --git a/assets/sprites/bullets/hatbullet.png b/assets/sprites/bullets/hatbullet.png new file mode 100644 index 0000000..a80938d Binary files /dev/null and b/assets/sprites/bullets/hatbullet.png differ diff --git a/assets/sprites/enemies/woody/idle.png b/assets/sprites/enemies/woody/idle.png new file mode 100644 index 0000000..3ead41f Binary files /dev/null and b/assets/sprites/enemies/woody/idle.png differ diff --git a/scripts/battle/bullets/hatbulllet.lua b/scripts/battle/bullets/hatbulllet.lua new file mode 100644 index 0000000..81eb8f9 --- /dev/null +++ b/scripts/battle/bullets/hatbulllet.lua @@ -0,0 +1,27 @@ +---@class HatBullet : Bullet +local HatBullet, 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 HatBullet:init(x, y, dir, speed) + -- Last argument = sprite path + super.init(self, x, y, "bullets/hatbullet") + + -- 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 + self.collision = false + + +end + +function HatBullet:update() + -- For more complicated bullet behaviours, code here gets called every update + + super.update(self) +end + +return HatBullet diff --git a/scripts/battle/encounters/woody.lua b/scripts/battle/encounters/woody.lua new file mode 100644 index 0000000..79b232c --- /dev/null +++ b/scripts/battle/encounters/woody.lua @@ -0,0 +1,21 @@ +local Woody, super = Class(Encounter) + +function Woody: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 woody enemy to the encounter + self:addEnemy("woody") + + --- Uncomment this line to add another! + --self:addEnemy("woody") +end + +return Woody diff --git a/scripts/battle/enemies/woody.lua b/scripts/battle/enemies/woody.lua new file mode 100644 index 0000000..3f98f1c --- /dev/null +++ b/scripts/battle/enemies/woody.lua @@ -0,0 +1,96 @@ +local Woody, super = Class(EnemyBattler) + +function Woody:init() + super.init(self) + + -- Enemy name + self.name = "Woody" + -- Sets the actor, which handles the enemy's sprites (see scripts/data/actors/Woody.lua) + self:setActor("woody") + + -- 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 Woody 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 Woody 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 Woody: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 Woody smiles back.", + "* It seems the Woody 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 Woody\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 Woody spiritually bowed\nin return." + elseif battler.chara.id == "susie" then + -- S-Action: start a cutscene (see scripts/battle/cutscenes/Woody.lua) + Game.battle:startActCutscene("Woody", "susie_punch") + return + else + -- Text for any other character (like Noelle) + return "* "..battler.chara:getName().." straightened the\nWoody'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 Woody \ No newline at end of file diff --git a/scripts/battle/waves/stackinghats.lua b/scripts/battle/waves/stackinghats.lua new file mode 100644 index 0000000..baec4bd --- /dev/null +++ b/scripts/battle/waves/stackinghats.lua @@ -0,0 +1,50 @@ +local StackingHats, super = Class(Wave) + +function StackingHats:init() + super.init(self) + + -- The duration of our wave, in seconds. (Defaults to `5`) + self.time = 10 + self.count = 0 + +end + +function StackingHats:onStart() + -- code here gets called at the start of the wave + -- create a bullet in the center of the screen + arena = Game.battle.arena + arenaX, arenaY = arena:getBottomLeft() + arenaX2, arenaY2 = arena:getBottomRight() + self.collider = LineCollider(self, arenaX, arenaY, arenaX2, arenaY2) + self.timer:everyInstant(1, function() + + local x = SCREEN_WIDTH / 2 + local y = arena:getTop() * 0.5 + self.bullets[self.count] = self:spawnBullet("hatbulllet", x, y) + -- set the bullet's speed to 4 (pixels per 1/30th second) + self.bullets[self.count].physics.speed = 4 + self.count = self.count + 1 + end) + super.onStart(self) +end + +function StackingHats:update() + -- code here gets called every frame + + for i = 0, self.count -1, 1 do + for j = 0, i-1, 1 do + if (i ~= j) then + if (self.bullets[i].collider:collidesWith(self.bullets[j])) then + self.bullets[i].physics.speed = 0 + end + end + end + if (self.bullets[i].collider:collidesWith(self)) then + self.bullets[i].physics.speed = 0 + end + end + + super.update(self) +end + +return StackingHats \ No newline at end of file diff --git a/scripts/data/actors/woody.lua b/scripts/data/actors/woody.lua new file mode 100644 index 0000000..e5288ac --- /dev/null +++ b/scripts/data/actors/woody.lua @@ -0,0 +1,54 @@ +local actor, super = Class(Actor, "woody") + +function actor:init() + super.init(self) + + -- Display name (optional) + self.name = "Woody" + + -- 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/woody" + -- 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