From 714a07cbb221f5caaad7e3f664211b711880e155 Mon Sep 17 00:00:00 2001 From: jss2a98aj Date: Sat, 30 Nov 2019 15:16:42 -0500 Subject: [PATCH] New ship BG and pet --- animations/emotes/avalirecharged.animation | 7 + animations/emotes/avalirecharged.png | Bin 0 -> 3025 bytes animations/emotes/avalisleepy.animation | 7 + animations/emotes/avalisleepy.png | Bin 0 -> 3538 bytes .../avalipetstartingbag.activeitem | 24 + .../avalipetstartingbag.animation | 210 ++++++++ .../avalipetstartingbag.lua | 88 ++++ .../avalipetstartingbag.png | Bin 0 -> 192 bytes .../avalipetstartingbagicon.png | Bin 0 -> 178 bytes items/generic/crafting/avalibattery.item | 10 + items/generic/crafting/avalibattery.png | Bin 0 -> 228 bytes items/generic/crafting/avalismallbattery.item | 10 + items/generic/crafting/avalismallbattery.png | Bin 0 -> 174 bytes monsters/pets/avaliGroundPet.lua | 369 ++++++++++++++ monsters/pets/avaliPetBehavior.lua | 209 ++++++++ monsters/pets/avaliactions/eatAction.lua | 116 +++++ monsters/pets/avaliactions/inspectAction.lua | 120 +++++ monsters/pets/avaliactions/starvingAction.lua | 25 + .../avalicleaningbot.animation | 475 ++++++++++++++++++ .../avalicleaningbot/avalicleaningbot.frames | 26 + .../avalicleaningbot.monstertype | 210 ++++++++ .../avalicleaningbot/avalicleaningbot.png | Bin 0 -> 16243 bytes .../pets/avalicleaningbot/body.monsterpart | 9 + .../avalipetchargingstation.animation | 48 ++ .../avalipetchargingstation.frames | 15 + .../avalipetchargingstation.lua | 14 + .../avalipetchargingstation.object | 65 +++ .../avalipetchargingstation.png | Bin 0 -> 227 bytes .../avalipetchargingstationicon.png | Bin 0 -> 189 bytes .../avalitechstation/avalitechstation.object | 2 +- .../avaliteleportertier0.object | 2 +- .../D Feedstocks/avalismallbattery.recipe | 9 + ships/avali/avalit0blocks.png | Bin 255 -> 310 bytes ships/avali/avalit1blocks.png | Bin 259 -> 294 bytes ships/avali/avalit2blocks.png | Bin 267 -> 293 bytes ships/avali/avalit3blocks.png | Bin 283 -> 306 bytes ships/avali/avalit4blocks.png | Bin 307 -> 355 bytes ships/avali/avalit5blocks.png | Bin 357 -> 425 bytes ships/avali/avalit6blocks.png | Bin 455 -> 531 bytes ships/avali/avalit7blocks.png | Bin 577 -> 656 bytes ships/avali/avalit8blocks.png | Bin 683 -> 837 bytes ships/avali/aviant0blocks.png | Bin 242 -> 305 bytes ships/avali/aviant1blocks.png | Bin 231 -> 287 bytes ships/avali/aviant2blocks.png | Bin 232 -> 279 bytes ships/avali/aviant3blocks.png | Bin 258 -> 297 bytes ships/avali/aviant4blocks.png | Bin 293 -> 343 bytes ships/avali/aviant5blocks.png | Bin 350 -> 448 bytes ships/avali/aviant6blocks.png | Bin 420 -> 497 bytes ships/avali/aviant7blocks.png | Bin 470 -> 546 bytes ships/avali/aviant8blocks.png | Bin 500 -> 635 bytes ships/avali/blockkey.config | 129 ++++- tiles/materials/camptapestry1.material | 4 +- tiles/materials/camptapestry2.material | 4 +- treasure/avali.treasurepools | 8 + treasure/shiplocker.treasurepools.patch | 5 + 55 files changed, 2193 insertions(+), 27 deletions(-) create mode 100644 animations/emotes/avalirecharged.animation create mode 100644 animations/emotes/avalirecharged.png create mode 100644 animations/emotes/avalisleepy.animation create mode 100644 animations/emotes/avalisleepy.png create mode 100644 items/active/unsorted/avalipetstartingbag/avalipetstartingbag.activeitem create mode 100644 items/active/unsorted/avalipetstartingbag/avalipetstartingbag.animation create mode 100644 items/active/unsorted/avalipetstartingbag/avalipetstartingbag.lua create mode 100644 items/active/unsorted/avalipetstartingbag/avalipetstartingbag.png create mode 100644 items/active/unsorted/avalipetstartingbag/avalipetstartingbagicon.png create mode 100644 items/generic/crafting/avalibattery.item create mode 100644 items/generic/crafting/avalibattery.png create mode 100644 items/generic/crafting/avalismallbattery.item create mode 100644 items/generic/crafting/avalismallbattery.png create mode 100644 monsters/pets/avaliGroundPet.lua create mode 100644 monsters/pets/avaliPetBehavior.lua create mode 100644 monsters/pets/avaliactions/eatAction.lua create mode 100644 monsters/pets/avaliactions/inspectAction.lua create mode 100644 monsters/pets/avaliactions/starvingAction.lua create mode 100644 monsters/pets/avalicleaningbot/avalicleaningbot.animation create mode 100644 monsters/pets/avalicleaningbot/avalicleaningbot.frames create mode 100644 monsters/pets/avalicleaningbot/avalicleaningbot.monstertype create mode 100644 monsters/pets/avalicleaningbot/avalicleaningbot.png create mode 100644 monsters/pets/avalicleaningbot/body.monsterpart create mode 100644 objects/ship/avalipetchargingstation/avalipetchargingstation.animation create mode 100644 objects/ship/avalipetchargingstation/avalipetchargingstation.frames create mode 100644 objects/ship/avalipetchargingstation/avalipetchargingstation.lua create mode 100644 objects/ship/avalipetchargingstation/avalipetchargingstation.object create mode 100644 objects/ship/avalipetchargingstation/avalipetchargingstation.png create mode 100644 objects/ship/avalipetchargingstation/avalipetchargingstationicon.png create mode 100644 recipes/crafting/lathe/D Feedstocks/avalismallbattery.recipe diff --git a/animations/emotes/avalirecharged.animation b/animations/emotes/avalirecharged.animation new file mode 100644 index 00000000..32d7b317 --- /dev/null +++ b/animations/emotes/avalirecharged.animation @@ -0,0 +1,7 @@ +{ + "frames" : "avalirecharged.png", + "variants" : 1, + "frameNumber" : 8, + "animationCycle" : 1, + "offset" : [0, 0] +} diff --git a/animations/emotes/avalirecharged.png b/animations/emotes/avalirecharged.png new file mode 100644 index 0000000000000000000000000000000000000000..e399b6da15840225869e13460d50f9901e62560f GIT binary patch literal 3025 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 T@0Z7f00000NkvXXu0mjf#lyx( literal 0 HcmV?d00001 diff --git a/animations/emotes/avalisleepy.animation b/animations/emotes/avalisleepy.animation new file mode 100644 index 00000000..99672323 --- /dev/null +++ b/animations/emotes/avalisleepy.animation @@ -0,0 +1,7 @@ +{ + "frames" : "avalisleepy.png", + "variants" : 1, + "frameNumber" : 8, + "animationCycle" : 1, + "offset" : [0, 0] +} diff --git a/animations/emotes/avalisleepy.png b/animations/emotes/avalisleepy.png new file mode 100644 index 0000000000000000000000000000000000000000..232bf869958a9543408cd341690c23c4d2b757cd GIT binary patch literal 3538 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 Tcc#@q!x%hW{an^LB{Ts5ZxK23 literal 0 HcmV?d00001 diff --git a/monsters/pets/avaliGroundPet.lua b/monsters/pets/avaliGroundPet.lua new file mode 100644 index 00000000..0c6743be --- /dev/null +++ b/monsters/pets/avaliGroundPet.lua @@ -0,0 +1,369 @@ +require "/scripts/util.lua" + +function init() + self.pathing = {} + self.pathing.stuckTimer = 0 + self.pathing.maxStuckTime = 2 + + self.jumpCooldown = 0 + self.jumpMaxCooldown = 1 + + self.movementParameters = mcontroller.baseParameters() + self.jumpHoldTime = self.movementParameters.airJumpProfile.jumpHoldTime + self.jumpSpeed = self.movementParameters.airJumpProfile.jumpSpeed + self.runSpeed = self.movementParameters.runSpeed + + self.stuckPosition = mcontroller.position() + self.stuckCount = 0 + + self.scriptDelta = 5 + + storage.petResources = storage.petResources or config.getParameter("petResources") + self.petResourceDeltas = config.getParameter("petResourceDeltas") + setPetResources(storage.petResources) + + local states = stateMachine.scanScripts(config.getParameter("scripts"), "(%a+State)%.lua") + self.state = stateMachine.create(states) + + local actionStates = {} + local actions = stateMachine.scanScripts(config.getParameter("scripts"), "(%a+Action)%.lua") + for _, action in pairs(actions) do + table.insert(actionStates, 1, action) + end + self.actionState = stateMachine.create(actionStates) + self.autoPickState = false + + self.actionState.leavingState = function(stateName) + self.querySurroundingsTimer = 0 + end + + self.followTarget = 0 + + self.behaviorName = config.getParameter("petBehavior") + self.behavior = _ENV[self.behaviorName] + + storage.knownPlayers = storage.knownPlayers or config.getParameter("knownPlayers", {}) + storage.foodLikings = storage.foodLikings or config.getParameter("foodLikings", {}) + + self.querySurroundingsCooldown = config.getParameter("querySurroundingsCooldown", 3) + self.querySurroundingsTimer = 1 + self.querySurroundingsRange = config.getParameter("querySurroundingsRange", 50) + + self.standTimer = 0 + + self.updateAnchorTimer = 0 + + self.debug = false + + if self.behavior and self.behavior.init then + self.behavior.init() + end + + self.lastInteract = 0 + monster.setInteractive(true) +end + +function receiveNotification(notification) + return self.state.pickState({ notification = notification }) +end + + +function interact() + if world.time() - self.lastInteract > config.getParameter("interactCooldown", 3.0) then + emote("happy") + self.lastInteract = world.time() + end +end + +function update(dt) + self.actionState.update(dt) + + if self.actionState.stateDesc() == "" and not self.state.update(dt) then + self.state.pickState() + end + + if self.querySurroundingsTimer <= 0 then + querySurroundings() + self.querySurroundingsTimer = self.querySurroundingsTimer + self.querySurroundingsCooldown + end + + tickResources(dt) + decrementTimers(dt) + + updateAnchor() + + if not self.moved then script.setUpdateDelta(self.scriptDelta) end + + if self.actionState.stateDesc() ~= "" then + util.debugText(self.actionState.stateDesc(), mcontroller.position(), "blue") + else + util.debugText(self.state.stateDesc(), mcontroller.position(), "blue") + end + drawDebugResources() +end + +function setAnchor(entityId) + if not self.anchorId or self.anchorId == entityId or not world.entityExists(self.anchorId) then + storage.anchorPosition = world.entityPosition(entityId) + self.anchorId = entityId + world.callScriptedEntity(entityId, "setPet", entity.id(), { + foodLikings = storage.foodLikings, + knownPlayers = storage.knownPlayers, + petResources = petResources(), + seed = monster.seed() + }) + return true + else + return false + end +end + +function updateAnchor() + if self.anchorId and world.entityExists(self.anchorId) then + if self.updateAnchorTimer <= 0 then + setAnchor(self.anchorId) + self.updateAnchorTimer = 1 + end + else + findAnchor() + end +end + +function findAnchor() + local anchorName = config.getParameter("anchorName", "humantechstation") + local nearObjects + if storage.anchorPosition then + local nearObjects = world.entityQuery(storage.anchorPosition, 5, { + includedTypes = { "object" }, + boundMode = "Position" + }) + + for _,objectId in ipairs(nearObjects) do + local objectPosition = world.entityPosition(objectId) + if world.entityName(objectId) == anchorName and not world.callScriptedEntity(objectId, "hasPet") then + setAnchor(objectId) + return true + end + end + end + + if not storage.home then + status.setResource("health", 0) + else + -- Pet spawned by a colony deed -- allow the pet to continue living until + -- tenant.despawn kills it. + end + return false +end + +function querySurroundings() + local nearEntities = world.entityQuery(mcontroller.position(), self.querySurroundingsRange, { + includedTypes = { "player", "itemDrop", "monster", "object" }, + withoutEntityId = entity.id() + }) + + --Queue up reactions + for _,entityId in ipairs(nearEntities) do + self.behavior.reactTo(entityId) + end + + --Run actions + self.behavior.run() + + return false +end + +function emote(emoteType) + animator.burstParticleEmitter("emote"..emoteType) +end + +function itemFoodLiking(entityName) + if not entityName or (entityName ~= "avalibattery" and entityName ~= "avalismallbattery") then + return false + end + + local foodLiking = storage.foodLikings[entityName] + if foodLiking == nil then + return nil + end + + --How much does it like the food + if foodLiking == true or foodLiking == false then + foodLiking = math.random(50, 100) + storage.foodLikings[entityName] = foodLiking + end + + return foodLiking +end + +function petResources() + local resources = {} + for resourceName, resourceValue in pairs(storage.petResources) do + resources[resourceName] = status.resource(resourceName) + end + return resources +end + +function setPetResources(resources) + for resourceName, resourceValue in pairs(resources) do + status.setResource(resourceName, resourceValue) + end +end + +function tickResources(dt) + for resourceName, resourceDelta in pairs(self.petResourceDeltas) do + status.modifyResource(resourceName, resourceDelta * dt) + end +end + +function drawDebugResources() + if not self.debug then return end + + local resources = storage.petResources + local position = mcontroller.position() + + local y = 2 + for resourceName, resourceValue in pairs(storage.petResources) do + --Border + world.debugLine(vec2.add(position, {-2, y+0.125}), vec2.add(position, {-2, y + 0.75}), "black") + world.debugLine(vec2.add(position, {-2, y + 0.75}), vec2.add(position, {2, y + 0.75}), "black") + world.debugLine(vec2.add(position, {2, y + 0.75}), vec2.add(position, {2, y+0.125}), "black") + world.debugLine(vec2.add(position, {2, y+0.125}), vec2.add(position, {-2, y+0.125}), "black") + + local width = 3.75 * status.resource(resourceName) / 100 + world.debugLine(vec2.add(position, {-1.875, y + 0.25}), vec2.add(position, {-1.875 + width, y + 0.25}), "green") + world.debugLine(vec2.add(position, {-1.875, y + 0.375}), vec2.add(position, {-1.875 + width, y + 0.375}), "green") + world.debugLine(vec2.add(position, {-1.875, y + 0.5}), vec2.add(position, {-1.875 + width, y + 0.5}), "green") + world.debugLine(vec2.add(position, {-1.875, y + 0.625}), vec2.add(position, {-1.875 + width, y + 0.625}), "green") + + world.debugText(resourceName, vec2.add(position, {2.25, y - 0.125}), "blue") + y = y + 1 + end +end + +function decrementTimers(dt) + self.querySurroundingsTimer = self.querySurroundingsTimer - dt + self.jumpCooldown = self.jumpCooldown - dt + self.standTimer = self.standTimer - dt + self.updateAnchorTimer = self.updateAnchorTimer - dt +end + +function setMovementState(running) + if not mcontroller.onGround() then + if mcontroller.liquidMovement() then + animator.setAnimationState("movement", "swim") + else + setJumpState() + end + else + if running then + animator.setAnimationState("movement", "run") + else + animator.setAnimationState("movement", "walk") + end + end +end + +function setIdleState() + local currentState = animator.animationState("movement") + if currentState ~= "idle" and currentState ~= "stand" then + self.standTimer = config.getParameter("idle.standTime", 2) + end + + if not mcontroller.onGround() then + setJumpState() + elseif self.standTimer < 0 then + animator.setAnimationState("movement", "idle") + else + animator.setAnimationState("movement", "stand") + end +end + +function setJumpState() + if mcontroller.yVelocity() > 0 then + animator.setAnimationState("movement", "jumping") + else + animator.setAnimationState("movement", "falling") + end +end + +function boundingBox(force) + if self.boundingBox and not force then return self.boundingBox end + + local collisionPoly = mcontroller.collisionPoly() + local bounds = {0, 0, 0, 0} + + for _,point in pairs(collisionPoly) do + if point[1] < bounds[1] then bounds[1] = point[1] end + if point[2] < bounds[2] then bounds[2] = point[2] end + if point[1] > bounds[3] then bounds[3] = point[1] end + if point[2] > bounds[4] then bounds[4] = point[2] end + end + self.boundingBox = bounds + + return bounds +end + +-------------------------------------------------------------------------------- +--MOVEMENT--------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +function approachPoint(dt, targetPosition, stopDistance, running) + local toTarget = world.distance(targetPosition, mcontroller.position()) + local targetDistance = world.magnitude(targetPosition, mcontroller.position()) + local groundPosition = findGroundPosition(targetPosition, -20, 1, util.toDirection(-toTarget[1])) + + if groundPosition then + self.approachPosition = groundPosition + end + + self.pather = self.pather or PathMover:new({run = running}) + self.pather.options.run = running + if self.approachPosition and (targetDistance > stopDistance or not mcontroller.onGround()) then + if self.pather:move(self.approachPosition, dt) == "running" then + mcontroller.controlFace(self.pather.deltaX or toTarget[1]) + setMovementState(running) + else + setIdleState() + end + + return false + elseif targetDistance <= stopDistance then + return true + end +end + +-------------------------------------------------------------------------------- +function move(direction, options) + if options == nil then options = {} end + if options.run == nil then options.run = false end + direction = util.toDirection(direction) + + local position = mcontroller.position() + local boundsEdge = 0 + local bounds = boundingBox() + local tilePosition + if direction > 0 then + tilePosition = {math.ceil(position[1]), position[2]} + boundsEdge = bounds[3] + else + tilePosition = {math.floor(position[1]), position[2]} + boundsEdge = bounds[1] + end + + --Stop at walls + if world.lineTileCollision({position[1], position[2] + bounds[2] + 1.5}, { position[1] + boundsEdge + direction, position[2] + bounds[2] + 1.5}, {"Null", "Block", "Dynamic", "Slippery"}) then + return false, "wall" + end + + --Check if the position ahead is valid, including slopes + local yDirs = {0, 1, -1} + for _,yDir in ipairs(yDirs) do + if validStandingPosition({tilePosition[1] + direction, tilePosition[2] + yDir}) then + moveX(direction, options.run) + return true + end + end + + return false, "ledge" +end diff --git a/monsters/pets/avaliPetBehavior.lua b/monsters/pets/avaliPetBehavior.lua new file mode 100644 index 00000000..806eac0f --- /dev/null +++ b/monsters/pets/avaliPetBehavior.lua @@ -0,0 +1,209 @@ +petBehavior = { + actionQueue = {} +} + +function petBehavior.init() + petBehavior.entityTypeReactions = { + ["player"] = petBehavior.reactToPlayer, + ["itemDrop"] = petBehavior.reactToItemDrop, + ["monster"] = petBehavior.reactToMonster, + ["object"] = petBehavior.reactToObject + } + + petBehavior.actions = { + ["emote"] = petBehavior.emote + } + + petBehavior.actionStates = { + ["inspect"] = "inspectAction", + ["follow"] = "followAction", + ["eat"] = "eatAction", + ["beg"] = "begAction", + ["play"] = "pounceAction", + ["sleep"] = "sleepAction", + ["starving"] = "starvingAction" + } + + self.currentActionScore = 0 + self.actionParams = config.getParameter("actionParams") + self.actionInterruptThreshold = config.getParameter("actionParams.interruptThreshold", 15) + self.inspected = {} +end + +--Queue an action to later be sorted by score +--score is optional +function petBehavior.queueAction(type, args, score) + table.insert(petBehavior.actionQueue, {type = type, args = args, score = score}) +end + +function petBehavior.performAction(action) + if petBehavior.actions[action.type] and self.actionCooldowns[action.type] <= 0 and self.actionState.stateDesc() == "" then + return petBehavior.actions[action.type](args) + end + + return false +end + +--Score all actions in the queue, then try them until one returns a success +function petBehavior.run() + if self.actionState.stateDesc() == "" then + self.currentActionScore = 0 + end + + --Add state actions to the reaction queue + for actionName,_ in pairs(petBehavior.actionStates) do + petBehavior.queueAction(actionName) + end + + --Sort actions based on score + for _,queuedAction in pairs(petBehavior.actionQueue) do + queuedAction.score = queuedAction.score or petBehavior.scoreAction(queuedAction.type) + end + table.sort(petBehavior.actionQueue, function(a, b) return a.score > b.score end) + + --Pick the first valid option + for _,action in pairs(petBehavior.actionQueue) do + if action.score <= 0 or action.score <= self.currentActionScore + self.actionInterruptThreshold then break end + + local picked = false + if not self.actionParams[action.type] or action.score > self.actionParams[action.type].minScore then + if petBehavior.actionStates[action.type] and self.actionState.stateDesc() ~= petBehavior.actionStates[action.type] then + if (action.args and self.actionState.pickState(action.args)) then + picked = true + elseif(petBehavior.actionStates[action.type] and self.actionState.pickState({[petBehavior.actionStates[action.type]] = true})) then + picked = true + end + elseif petBehavior.actions[action.type] and petBehavior.performAction(action.type) then + picked = true + end + end + + if picked then + self.currentActionScore = action.score + break + end + end + + petBehavior.actionQueue = {} +end + +--Evaluate an action and set a weight +function petBehavior.scoreAction(action) + if action == "eat" or action == "beg" then + return status.resource("hunger") + + elseif action == "follow" then + return status.resource("curiosity") - petBehavior.starvingLevel() + + elseif action == "inspect" then + return status.resource("curiosity") - petBehavior.starvingLevel() + + elseif action == "play" then + return status.resource("playful") - petBehavior.starvingLevel() + + elseif action == "sleep" then + return status.resource("sleepy") + + elseif action == "starving" then + return petBehavior.starvingLevel() - 20 + + elseif action == "emote" then + return 100 - petBehavior.starvingLevel() + + else + return 0 + end +end + +function petBehavior.starvingLevel() + local hunger = status.resource("hunger") + if hunger > config.getParameter("actionParams.hungerStarvingLevel") then + return hunger + else + return 0 + end +end + +---------------------------------------- +--ENTITY REACTIONS +---------------------------------------- + +function petBehavior.reactTo(entityId) + local entityType = world.entityType(entityId) + + if petBehavior.entityTypeReactions[entityType] then + petBehavior.entityTypeReactions[entityType](entityId) + end +end + +function petBehavior.reactToPlayer(entityId) + local playerUuid = world.entityUniqueId(entityId) + + --Check hands for goodies + local primaryItem = world.entityHandItem(entityId, "primary") + local altItem = world.entityHandItem(entityId, "alt") + local foodLiking = itemFoodLiking(primaryItem) or itemFoodLiking(altItem) + if foodLiking then + local score = status.resource("hunger") - (100 - foodLiking) + petBehavior.queueAction("beg", {begTarget = entityId}, score) + end + + if storage.knownPlayers[tostring(playerUuid)] then + petBehavior.queueAction("follow", {followTarget = entityId}) + else + petBehavior.queueAction("inspect", {inspectTarget = entityId, approachDistance = 4}) + end +end + +function petBehavior.reactToItemDrop(entityId) +-- local entityName = world.entityName(entityId) +-- local foodLiking = itemFoodLiking(entityName) +-- if foodLiking then +-- local score = math.max(status.resource("hunger") - (100 - foodLiking), petBehavior.starvingLevel()) +-- petBehavior.queueAction("eat", {eatTarget = entityId}, score) +-- elseif foodLiking == nil then +-- petBehavior.queueAction("inspect", {inspectTarget = entityId, approachDistance = 2}, status.resource("hunger")) +-- end +end + +function petBehavior.reactToMonster(entityId) + local entityName = world.monsterType(entityId) + if entityName == "petball" then + petBehavior.queueAction("play", {pounceTarget = entityId}) + end +end + +function petBehavior.reactToObject(entityId) + local entityName = world.entityName(entityId) + if entityName == "avalipetchargingstation" then + local item = world.containerItemAt(entityId, 0) + if item then + local foodLiking = itemFoodLiking(item.name) + if foodLiking then + local score = math.max(status.resource("hunger") - (100 - foodLiking), petBehavior.starvingLevel()) + petBehavior.queueAction("eat", {eatTarget = entityId}, score) + elseif foodLiking == nil then + self.inspected[entityId] = false + petBehavior.queueAction("inspect", {inspectTarget = entityId, approachDistance = 2}, status.resource("hunger")) + end + end + end + + if entityName == "pethouse" then + petBehavior.queueAction("sleep", {sleepTarget = entityId}) + end +--PURCHASEABLE PETS COMPAT + if entityName == "petTrap" then + status.setResource("health",0) + end +--END PURCHASEABLE PETS COMPAT +end + +---------------------------------------- +--ACTIONS +---------------------------------------- + +function petBehavior.emote(emoteName) + emote(emoteName) + return false +end diff --git a/monsters/pets/avaliactions/eatAction.lua b/monsters/pets/avaliactions/eatAction.lua new file mode 100644 index 00000000..ac34f550 --- /dev/null +++ b/monsters/pets/avaliactions/eatAction.lua @@ -0,0 +1,116 @@ +eatAction = { +} + +function eatAction.enterWith(args) + if not args.eatTarget then return nil end + + if status.resource("hunger") < config.getParameter("actionParams.eat.minHunger", 40) then + return nil + end + + --Make sure the target is valid + local entityType = world.entityType(args.eatTarget) + if not world.entityExists(args.eatTarget) or (entityType ~= "itemDrop" and entityType ~= "object") then + return nil + end + + return { + targetId = args.eatTarget, + approachDistance = config.getParameter("actionParams.eat.distance", 2), + runDistance = 5, + eatTimer = 2, + approachTimer = 5, + eating = false + } +end + +function eatAction.update(dt, stateData) + if not world.entityExists(stateData.targetId) then return true end + + local targetPosition = world.entityPosition(stateData.targetId) + local targetDistance = world.magnitude(targetPosition, mcontroller.position()) + + local running = targetDistance > stateData.runDistance + + --Approach the target + if not approachPoint(dt, targetPosition, stateData.approachDistance, running) then + stateData.approachTimer = stateData.approachTimer - dt + + if stateData.approachTimer < 0 or self.pathing.stuck then + return true, 5 + end + + return false + end + + if stateData.eating == false then + animator.setAnimationState("movement", "eat") + stateData.eating = true + end + + stateData.eatTimer = stateData.eatTimer - dt + + if stateData.eatTimer < 0 then + local targetType = world.entityType(stateData.targetId) + if (targetType == "itemDrop" and eatAction.consumeItemDrop(stateData)) or + (targetType == "object" and not eatAction.foodInBowl(stateData.targetId)) or + (targetType == "object" and eatAction.consumeFromObject(stateData)) then + return true, config.getParameter("actionParams.eat.cooldown") + end + end + + return false +end + +function eatAction.consumeItemDrop(stateData) + local oldDropPosition = world.entityPosition(stateData.targetId) + local itemDrop = world.takeItemDrop(stateData.targetId) + if itemDrop then + local foodLiking = itemFoodLiking(itemDrop.name) + + if foodLiking > 50 then + emote("happy") + else + emote("sad") + end + + local numEaten = math.min(itemDrop.count, math.ceil(status.resource("hunger") / 40)) + status.modifyResource("hunger", -40 * numEaten) + + if numEaten < itemDrop.count then + world.spawnItem(itemDrop.name, oldDropPosition, itemDrop.count - numEaten, itemDrop.parameters) + end + + return true + end +end + +function eatAction.foodInBowl(objectId) + local item = world.containerItemAt(objectId, 0) + if item then + local foodLiking = itemFoodLiking(item.name) + if foodLiking then + return foodLiking + end + end + return false +end + +function eatAction.consumeFromObject(stateData) + local foodLiking = eatAction.foodInBowl(stateData.targetId) + + local item = world.containerItemAt(stateData.targetId, 0) + if item then + if foodLiking and item.name == "avalibattery" and world.containerConsumeAt(stateData.targetId, 0, 0) then + emote("avalirecharged") + status.modifyResource("hunger", -30) + return true + elseif foodLiking and world.containerConsumeAt(stateData.targetId, 0, 1) then + emote("avalirecharged") + status.modifyResource("hunger", -40) + return true + end + end + + return false +end diff --git a/monsters/pets/avaliactions/inspectAction.lua b/monsters/pets/avaliactions/inspectAction.lua new file mode 100644 index 00000000..13342e92 --- /dev/null +++ b/monsters/pets/avaliactions/inspectAction.lua @@ -0,0 +1,120 @@ +inspectAction = { +} + +function inspectAction.enterWith(args) + if not args.inspectTarget or not args.approachDistance or self.inspected[args.inspectTarget] then return nil end + + return { + inspectTarget = args.inspectTarget, + approachDistance = args.approachDistance, + followUpAction = args.followUpAction, + inspected = false, + runDistance = 5, + inspectTimer = 1, + approachTimer = 5, + targetType = world.entityType(args.inspectTarget), + targetName = world.entityName(args.inspectTarget) + } +end + +function inspectAction.update(dt, stateData) + if not world.entityExists(stateData.inspectTarget) then return true end + + local targetPosition = world.entityPosition(stateData.inspectTarget) + local targetDistance = world.magnitude(targetPosition, mcontroller.position()) + + local running = targetDistance > stateData.runDistance + + if stateData.didEmote then + stateData.inspectTimer = stateData.inspectTimer - dt + end + + --Approach the target + if not approachPoint(dt, targetPosition, stateData.approachDistance, running) then + stateData.approachTimer = stateData.approachTimer - dt + + if stateData.approachTimer < 0 or self.pathing.stuck then + return true, config.getParameter("actionParams.inspect.cooldown", 15) + end + + return false + elseif not stateData.didEmote then + --emote("confused") + stateData.didEmote = true + animator.setAnimationState("movement", "inspect") + end + + local toTarget = world.distance(targetPosition, mcontroller.position()) + mcontroller.controlFace(toTarget[1]) + + --Form an opinion about the target + if stateData.inspectTimer < 0 then + + if stateData.targetType == "itemDrop" or (stateData.targetType == "object" and stateData.targetName == "avalipetchargingstation") then + inspectAction.inspectFood(stateData) + end + + if stateData.targetType == "monster" then + inspectAction.inspectMonster(stateData) + end + + if stateData.targetType == "player" then + inspectAction.inspectPlayer(stateData) + end + + self.inspected[stateData.inspectTarget] = true + stateData.inspected = true + return true, config.getParameter("actionParams.inspect.cooldown", 15) + end + + return false +end + +function inspectAction.inspectFood(stateData) + local itemName + if stateData.targetType == "object" then + itemName = world.containerItemAt(stateData.inspectTarget, 0).name + elseif stateData.targetType == "itemDrop" then + itemName = world.entityName(stateData.inspectTarget) + end + + local foodLiking = itemFoodLiking(itemName) + + if foodLiking == nil then + foodLiking = math.random(100) + storage.foodLikings[itemName] = foodLiking + end + + if foodLiking > 50 then + emote("happy") + else + emote("sad") + end +end + +function inspectAction.inspectMonster(stateData) + local monsterType = world.monsterType(stateData.inspectTarget) + + if monsterLiking == nil then + monsterLiking = math.random(100) > 50 + end + + if monsterLiking then + emote("happy") + else + emote("sad") + end +end + +function inspectAction.inspectPlayer(stateData) + local uuid = world.entityUniqueId(stateData.inspectTarget) + if not storage.knownPlayers[tostring(uuid)] then + storage.knownPlayers[tostring(uuid)] = true + end + + emote("happy") +end + +function inspectAction.leavingState(stateData) + status.modifyResource("curiosity", -20) +end diff --git a/monsters/pets/avaliactions/starvingAction.lua b/monsters/pets/avaliactions/starvingAction.lua new file mode 100644 index 00000000..82da3606 --- /dev/null +++ b/monsters/pets/avaliactions/starvingAction.lua @@ -0,0 +1,25 @@ +starvingAction = {} + +function starvingAction.enterWith(args) + if not args.starvingAction then return nil end + + return { + timer = 2, + didEmote = false + } +end + +function starvingAction.update(dt, stateData) + animator.setAnimationState("movement", "idle") + + stateData.timer = stateData.timer - dt + + if stateData.timer < 1 and not stateData.didEmote then + emote("sad") + stateData.didEmote = true + elseif stateData.timer < 0 then + return true, config.getParameter("actionParams.starving.cooldown", 3) + else + return false + end +end diff --git a/monsters/pets/avalicleaningbot/avalicleaningbot.animation b/monsters/pets/avalicleaningbot/avalicleaningbot.animation new file mode 100644 index 00000000..9419cc5f --- /dev/null +++ b/monsters/pets/avalicleaningbot/avalicleaningbot.animation @@ -0,0 +1,475 @@ +{ + "animatedParts" : { + "stateTypes" : { + "movement" : { + "priority" : 0, + "default" : "idle", + "states" : { + "idle" : { + "frames" : 4 + }, + "stand" : { + "frames" : 4 + }, + "run" : { + "frames" : 4, + "cycle" : 0.5, + "mode" : "loop" + }, + "blink" : { + "frames" : 4 + }, + "walk" : { + "frames" : 4, + "cycle" : 1, + "mode" : "loop" + }, + "jumping" : { + "frames" : 3, + "cycle" : 0.25, + "mode" : "transition", + "transition" : "falling" + }, + "falling" : { + "frames" : 2, + "cycle" : 0.25, + "mode" : "loop" + }, + "eat" : { + "frames" : 4, + "cycle" : 0.4, + "mode" : "loop" + }, + "inspect" : { + "frames" : 5, + "cycle" : 2 + }, + "swim" : { + "frames" : 8, + "cycle" : 1 + }, + "sound" : { + "frames" : 4, + "cycle" : 0.5, + "mode" : "transition", + "transition" : "stand" + }, + "sleep" : { + "frames" : 1 + }, + "invisible" : { + "frames" : 1 + } + } + } + }, + + "parts" : { + "body" : { + "properties" : { + "centered" : true, + "offset" : [0, 1] + }, + "partStates" : { + "movement" : { + "idle" : { + "properties" : { + "image" : ":idle." + } + }, + "stand" : { + "properties" : { + "image" : ":stand." + } + }, + "run" : { + "properties" : { + "image" : ":run." + } + }, + "blink" : { + "properties" : { + "image" : ":blink." + } + }, + "walk" : { + "properties" : { + "image" : ":walk." + } + }, + "jumping" : { + "properties" : { + "image" : ":jump." + } + }, + "falling" : { + "properties" : { + "image" : ":fall." + } + }, + "eat" : { + "properties" : { + "image" : ":eat." + } + }, + "eatloop" : { + "properties" : { + "image" : ":eatloop." + } + }, + "inspect" : { + "properties" : { + "image" : ":inspect." + } + }, + "swim" : { + "properties" : { + "image" : ":swim." + } + }, + "sound" : { + "properties" : { + "image" : ":sound." + } + }, + "sleep" : { + "properties" : { + "image" : ":sleep." + } + }, + "invisible" : { + "properties" : { + "image" : ":invisible." + } + } + } + } + } + } + }, + + "particleEmitters" : { + "damage" : { + "emissionRate" : 0.7, + "particles" : [ + ] + }, + "deathPoof" : { + "particles" : [ + { + "particle" : { + "type" : "animated", + "animation" : "/animations/puff2c/puff2c.animation", + "size" : 1, + "angularVelocity" : 35, + "fade" : 1, + "destructionTime" : 1, + "position" : [0, 0], + "initialVelocity" : [0, 0], + "finalVelocity" : [0, 0], + "approach" : [1, 1], + "timeToLive" : 0.4, + "layer" : "middle" + } + }, + { + "particle" : { + "type" : "animated", + "animation" : "/animations/fizz1/fizz1.animation", + "size" : 1, + "angularVelocity" : 20, + "fade" : 1, + "destructionTime" : 1, + "position" : [0, 0], + "initialVelocity" : [-8, 8], + "finalVelocity" : [-3, -4], + "approach" : [15, 15], + "timeToLive" : 3.45, + "layer" : "middle", + "variance" : { + "initialVelocity" : [-4, 2], + "finalVelocity" : [-3, -4] + } + } + }, + { + "particle" : { + "type" : "animated", + "animation" : "/animations/fizz1/fizz1.animation", + "size" : 1, + "angularVelocity" : 20, + "fade" : 1, + "destructionTime" : 1, + "position" : [0, 0], + "initialVelocity" : [8, 8], + "finalVelocity" : [3, -4], + "approach" : [15, 15], + "timeToLive" : 3.45, + "layer" : "middle", + "variance" : { + "initialVelocity" : [4, 2], + "finalVelocity" : [3, -4] + } + } + }, + { + "particle" : { + "type" : "animated", + "animation" : "/animations/fizz2/fizz2.animation", + "size" : 1, + "angularVelocity" : 20, + "fade" : 1, + "destructionTime" : 1, + "position" : [0, 0], + "initialVelocity" : [-8, 8], + "finalVelocity" : [-3, -4], + "approach" : [15, 15], + "timeToLive" : 3.45, + "layer" : "middle", + "variance" : { + "initialVelocity" : [-4, 2], + "finalVelocity" : [-3, -4] + } + } + }, + { + "particle" : { + "type" : "animated", + "animation" : "/animations/fizz2/fizz2.animation", + "size" : 1, + "angularVelocity" : 20, + "fade" : 1, + "destructionTime" : 1, + "position" : [0, 0], + "initialVelocity" : [8, 8], + "finalVelocity" : [3, -4], + "approach" : [15, 15], + "timeToLive" : 3.45, + "layer" : "middle", + "variance" : { + "initialVelocity" : [4, 2], + "finalVelocity" : [3, -4] + } + } + }, + { + "particle" : { + "type" : "animated", + "animation" : "/animations/fizz3/fizz3.animation", + "size" : 1, + "angularVelocity" : 20, + "fade" : 1, + "destructionTime" : 1, + "position" : [0, 0], + "initialVelocity" : [-8, 8], + "finalVelocity" : [-3, -4], + "approach" : [15, 15], + "timeToLive" : 3.45, + "layer" : "middle", + "variance" : { + "initialVelocity" : [-4, 2], + "finalVelocity" : [-3, -4] + } + } + }, + { + "particle" : { + "type" : "animated", + "animation" : "/animations/fizz3/fizz3.animation", + "size" : 1, + "angularVelocity" : 20, + "fade" : 1, + "destructionTime" : 1, + "position" : [0, 0], + "initialVelocity" : [8, 8], + "finalVelocity" : [3, -4], + "approach" : [15, 15], + "timeToLive" : 3.45, + "layer" : "middle", + "variance" : { + "initialVelocity" : [4, 2], + "finalVelocity" : [3, -4] + } + } + }, + { + "particle" : { + "type" : "animated", + "animation" : "/animations/fizz4/fizz4.animation", + "size" : 1, + "angularVelocity" : 20, + "fade" : 1, + "destructionTime" : 1, + "position" : [0, 0], + "initialVelocity" : [-8, 8], + "finalVelocity" : [-3, -4], + "approach" : [15, 15], + "timeToLive" : 3.45, + "layer" : "middle", + "variance" : { + "initialVelocity" : [-4, 2], + "finalVelocity" : [-3, -4] + } + } + }, + { + "particle" : { + "type" : "animated", + "animation" : "/animations/fizz4/fizz4.animation", + "size" : 1, + "angularVelocity" : 20, + "fade" : 1, + "destructionTime" : 1, + "position" : [0, 0], + "initialVelocity" : [8, 8], + "finalVelocity" : [3, -4], + "approach" : [15, 15], + "timeToLive" : 3.45, + "layer" : "middle", + "variance" : { + "initialVelocity" : [4, 2], + "finalVelocity" : [3, -4] + } + } + } + ] + }, + + "emotehappy" : { + "emissionRate" : 1, + "particles" : [ + { + "particle" : { + "type" : "animated", + "animation" : "/animations/emotes/happy.animation", + "position" : [0.5, 2], + "finalVelocity" : [0, 0], + "initialVelocity" : [0, 0], + "destructionTime" : 0.2, + "destructionAction" : "shrink", + "layer" : "front", + "timeToLive" : 0.8, + "flippable" : false + } + } + ] + }, + + "emotesad" : { + "emissionRate" : 1, + "particles" : [ + { + "particle" : { + "type" : "animated", + "animation" : "/animations/emotes/sad.animation", + "position" : [0.5, 2], + "finalVelocity" : [0, 0], + "initialVelocity" : [0, 0], + "destructionTime" : 0.2, + "destructionAction" : "shrink", + "layer" : "front", + "timeToLive" : 0.8, + "flippable" : false + } + } + ] + }, + + "emoteconfused" : { + "emissionRate" : 1, + "particles" : [ + { + "particle" : { + "type" : "animated", + "animation" : "/animations/emotes/confused.animation", + "position" : [0.5, 2], + "finalVelocity" : [0, 0], + "initialVelocity" : [0, 0], + "destructionTime" : 0.2, + "destructionAction" : "shrink", + "layer" : "front", + "timeToLive" : 0.8, + "flippable" : false + } + } + ] + }, + + "emotesleepy" : { + "emissionRate" : 1, + "particles" : [ + { + "particle" : { + "type" : "animated", + "animation" : "/animations/emotes/avalisleepy.animation", + "position" : [0.5, 2], + "finalVelocity" : [0, 0], + "initialVelocity" : [0, 0], + "destructionTime" : 0.2, + "destructionAction" : "shrink", + "layer" : "front", + "timeToLive" : 0.8, + "flippable" : false + } + } + ] + }, + + "emoteavalirecharged" : { + "emissionRate" : 1, + "particles" : [ + { + "particle" : { + "type" : "animated", + "animation" : "/animations/emotes/avalirecharged.animation", + "position" : [0.5, 2], + "finalVelocity" : [0, 0], + "initialVelocity" : [0, 0], + "destructionTime" : 0.2, + "destructionAction" : "shrink", + "layer" : "front", + "timeToLive" : 0.8, + "flippable" : false + } + } + ] + }, + + "sleep" : { + "emissionRate" : 2, + "particles" : [ + { + "particle" : { + "type" : "animated", + "animation" : "/animations/statuseffects/sleep/sleep.animation", + "position" : [0, 0], + "initialVelocity" : [0, 3], + "finalVelocity" : [10, 1], + "approach" : [2, 50], + "size" : 1, + "layer" : "middle", + "timeToLive" : 9, + "flippable" : false, + "variance" : { + "size" : 0.3 + } + } + } + ] + } + }, + + "effects" : { + "blink" : { + "type" : "flash", + "time" : 0.25, + "directives" : "fade=ffffff;0.5" + } + }, + + "sounds" : { + "turnHostile" : [ ], + "deathPuff" : [ "/sfx/npc/enemydeathpuff.ogg" ], + "petsound" : [] + } +} diff --git a/monsters/pets/avalicleaningbot/avalicleaningbot.frames b/monsters/pets/avalicleaningbot/avalicleaningbot.frames new file mode 100644 index 00000000..d3a1db78 --- /dev/null +++ b/monsters/pets/avalicleaningbot/avalicleaningbot.frames @@ -0,0 +1,26 @@ +{ + "frameGrid" : { + "size" : [32, 32], + "dimensions" : [10, 6], + + "names" : [ + [ null, "idle.1", "idle.2", "idle.3", "idle.4", null, "eat.1", "eat.2", "eat.3", "eat.4" ], + [ null, null, null, "invisible.1", null, null, "inspect.1", "inspect.2", "inspect.3", "inspect.4" ], + [ null, "run.1", "run.2", "run.3", "run.4", null, "blink.1", "blink.2", "blink.3", "blink.4" ], + [ null, "jump.1", "jump.2", "jump.3", null, "fall.1", "fall.2" ], + [ null, "swim.1", "swim.2", "swim.3", "swim.4", "swim.5", "swim.6", "swim.7", "swim.8" ], + [ null, "sound.1", "sound.2", "sound.3", "sound.4", null, "sleep.1" ] + ] + }, + "aliases" : { + "walk.1" : "idle.1", + "walk.2" : "idle.2", + "walk.3" : "idle.3", + "walk.4" : "idle.4", + "stand.1" : "idle.1", + "stand.2" : "idle.2", + "stand.3" : "idle.3", + "stand.4" : "idle.4", + "inspect.5" : "inspect.1" + } +} diff --git a/monsters/pets/avalicleaningbot/avalicleaningbot.monstertype b/monsters/pets/avalicleaningbot/avalicleaningbot.monstertype new file mode 100644 index 00000000..604b5624 --- /dev/null +++ b/monsters/pets/avalicleaningbot/avalicleaningbot.monstertype @@ -0,0 +1,210 @@ +{ + "type" : "avalicleaningbot", + + "categories" : [ "avalicleaningbot" ], + "parts" : [ "body" ], + + "animation" : "avalicleaningbot.animation", + + "dropPools" : [ ], + + "baseParameters" : { + "persistent" : true, + "damageTeamType" : "ghostly", + + "scripts" : [ + "/monsters/pets/avaliGroundPet.lua", + "/scripts/pathing.lua", + "/scripts/stateMachine.lua", + "/scripts/util.lua", + "/scripts/vec2.lua", + + "/monsters/pets/idleState.lua", + "/monsters/pets/wanderState.lua", + + "/monsters/pets/avaliPetBehavior.lua", + "/monsters/pets/actions/followAction.lua", + "/monsters/pets/avaliactions/inspectAction.lua", + "/monsters/pets/avaliactions/eatAction.lua", + "/monsters/pets/actions/sleepAction.lua", + "/monsters/pets/actions/begAction.lua", + "/monsters/pets/actions/pounceAction.lua", + "/monsters/pets/avaliactions/starvingAction.lua" + ], + + "petBehavior" : "petBehavior", + + "anchorName" : "avalitechstation", + "petResources" : { + "sleepy" : 10, + "hunger" : 60, + "playful" : 10, + "curiosity" : 60 + }, + "petResourceDeltas" : { + "sleepy" : 1, + "hunger" : 0.3, + "playful" : 1, + "curiosity" : 1 + }, + + "actionParams" : { + "hungerStarvingLevel" : 80, + "beg" : { + "minScore" : 50, + "cooldown" : 5, + "distance" : 3, + "emoteCooldown" : 2 + }, + "follow" : { + "minScore" : 35, + "cooldown" : 5, + "curiosityDelta" : -5, + "boredTime" : 3 + }, + "inspect" : { + "minScore" : 20, + "cooldown" : 2 + }, + "eat" : { + "minScore" : 0, + "minHunger" : 40, + "cooldown" : 0, + "distance" : 2 + }, + "play" : { + "minScore" : 20, + "cooldown" : 2 + }, + "sleep" : { + "minScore" : 50, + "minSleepy" : 75, + "cooldown" : 10 + }, + "starving" : { + "minScore" : 60, + "cooldown" : 3 + } + }, + + "pathing" : { + "canOpenDoors" : false + }, + + "metaBoundBox" : [-1.625, -2.375, 1.75, 2.0], + "scale" : 1.0, + + "querySurroundingsCooldown" : 1, + + "wander" : { + "wanderTime" : [10, 20], + "changeDirectionTime" : [10, 15], + "changeDirectionWait" : [1, 3] + }, + + "follow" : { + }, + + "pounce" : { + "maxRange" : 10, + "minRange" : 5 + }, + + "idle" : { + "idleTime" : [1, 3], + "standTime" : 2 + }, + + "movementSettings" : { + "collisionPoly" : [ [-0.75, -0.65], [-0.4, -1], [0.4, -1], [0.75, -0.65], [0.75, 0.15], [0.4, 0.5], [-0.4, 0.5], [-0.75, 0.15] ], + + "mass" : 1.0, + "walkSpeed" : 2, + "runSpeed" : 6, + "flySpeed" : 15, + "airForce" : 50.0 + }, + + "bodyMaterialKind" : "organic", + + "knockoutTime" : 0.3, + "knockoutEffect" : "blink", + "deathParticles" : "deathPoof", + + "touchDamage" : { + "poly" : [ [-1.625, -2.375], [1.75, -2.375], [1.75, 2.0], [-1.625, 2.0] ], + "damage" : 15, + + "teamType" : "enemy", + "damageSourceKind" : "lash", + "statusEffects" : [ ] + }, + + "statusSettings" : { + "statusProperties" : { + "targetMaterialKind" : "organic" + }, + + "appliesEnvironmentStatusEffects" : false, + "appliesWeatherStatusEffects" : false, + "minimumLiquidStatusEffectPercentage" : 0.1, + + "primaryScriptSources" : [ + "/stats/monster_primary.lua" + ], + "primaryScriptDelta" : 5, + + "stats" : { + "knockbackStunTime" : { + "baseValue" : 0.25 + }, + "knockbackThreshold" : { + "baseValue" : 9 + }, + "maxHealth" : { + "baseValue" : 72 + }, + "protection" : { + "baseValue" : 0.0 + }, + "healthRegen" : { + "baseValue" : 0.0 + }, + "powerMultiplier" : { + "baseValue" : 1.0 + } + }, + + "resources" : { + "stunned" : { + "deltaValue" : -1.0, + "initialValue" : 0.0 + }, + "health" : { + "maxStat" : "maxHealth", + "deltaStat" : "healthRegen", + "defaultPercentage" : 100 + }, + "sleepy" : { + "maxValue" : 100, + "defaultPercentage" : 10 + }, + "hunger" : { + "maxValue" : 100, + "defaultPercentage" : 10 + }, + "playful" : { + "maxValue" : 100, + "defaultPercentage" : 10 + }, + "curiosity" : { + "maxValue" : 100, + "defaultPercentage" : 10 + } + } + }, + + "mouthOffset" : [0, 0], + "feetOffset" : [0, -8] + } +} diff --git a/monsters/pets/avalicleaningbot/avalicleaningbot.png b/monsters/pets/avalicleaningbot/avalicleaningbot.png new file mode 100644 index 0000000000000000000000000000000000000000..e70d71a225be6349b4802ce2ca75f16cbcda1125 GIT binary patch literal 16243 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:empty" + } + }, + "full" : { + "properties" : { + "image" : ":full" + } + }, + "infinite" : { + "properties" : { + "image" : ":infinite" + } + } + } + } + } + } + } +} diff --git a/objects/ship/avalipetchargingstation/avalipetchargingstation.frames b/objects/ship/avalipetchargingstation/avalipetchargingstation.frames new file mode 100644 index 00000000..e3e0c233 --- /dev/null +++ b/objects/ship/avalipetchargingstation/avalipetchargingstation.frames @@ -0,0 +1,15 @@ +{ + "frameGrid" : { + "size" : [24, 8], + "dimensions" : [3, 1], + "names" : [ + [ "default.0", "default.1", "infinite" ] + ] + }, + + "aliases" : { + "default.default" : "default.0", + "empty" : "default.0", + "full" : "default.1" + } +} diff --git a/objects/ship/avalipetchargingstation/avalipetchargingstation.lua b/objects/ship/avalipetchargingstation/avalipetchargingstation.lua new file mode 100644 index 00000000..e2620fad --- /dev/null +++ b/objects/ship/avalipetchargingstation/avalipetchargingstation.lua @@ -0,0 +1,14 @@ +function update(dt) + local item = world.containerItemAt(entity.id(), 0) + if item then + if item.name == "avalismallbattery" then + animator.setAnimationState("bowl", "full") + return + elseif item.name == "avalibattery" then + animator.setAnimationState("bowl", "infinite") + return + end + end + + animator.setAnimationState("bowl", "empty") +end diff --git a/objects/ship/avalipetchargingstation/avalipetchargingstation.object b/objects/ship/avalipetchargingstation/avalipetchargingstation.object new file mode 100644 index 00000000..4e2cfe2a --- /dev/null +++ b/objects/ship/avalipetchargingstation/avalipetchargingstation.object @@ -0,0 +1,65 @@ +{ + "objectName" : "avalipetchargingstation", + "colonyTags" : ["misc"], + "rarity" : "Common", + "objectType" : "container", + "tooltipKind" : "container", + + "category" : "storage", + "price" : 50, + "description" : "This charging station can hot swap or recharge cleaning drone batteries.", + "shortdescription" : "Cleaning Drone Charger", + "race" : "generic", + "printable" : false, + "health" : 1.5, + + "apexDescription" : "The Miniknog charge some of their drones with stations like this.", + "avianDescription" : "This charges a cleaning drone, but I wonder how long its battery lasts?", + "floranDescription" : "Floran thinkss this is strange way to feed petss.", + "glitchDescription" : "Enticed. Does this recharge that small cleaning robot?", + "humanDescription" : "There were vacuums with chargers like this on Earth, but not as fancy.", + "hylotlDescription" : "Some of these might not be a bad addition to underwater Hylotl cities.", + "novakidDescription" : "What strange pet uses this here thing?", + + "inventoryIcon" : "avalipetchargingstationicon.png", + "orientations" : [ + { + "image" : "avalipetchargingstation.png:.", + "imagePosition" : [0, 0], + "animationPosition" : [12, 4], + "direction" : "left", + "flipImages" : true, + + "spaces" : [ [0, 0], [1, 0], [2, 0] ], + "anchors" : [ "bottom" ], + "directions" : ["right"] + }, + { + "image" : "avalipetchargingstation.png:.", + "imagePosition" : [0, 0], + "animationPosition" : [12, 4], + "direction" : "right", + + "spaces" : [ [0, 0], [1, 0], [2, 0] ], + "anchors" : [ "bottom" ], + "directions" : ["right"] + } + ], + + "animation" : "avalipetchargingstation.animation", + "animationParts" : { + "foodbowl" : "avalipetchargingstation.png" + }, + + "scripts" : [ + "avalipetchargingstation.lua" + ], + "scriptDelta" : 5, + + "openSounds" : [ "/sfx/objects/chest_small_open.ogg" ], + "closeSounds" : [ "/sfx/objects/chest_small_close.ogg" ], + "slotCount" : 1, + "uiConfig" : "/interface/chests/chest.config", + "frameCooldown" : 5, + "autoCloseCooldown" : 3600 +} diff --git a/objects/ship/avalipetchargingstation/avalipetchargingstation.png b/objects/ship/avalipetchargingstation/avalipetchargingstation.png new file mode 100644 index 0000000000000000000000000000000000000000..72fd591b1db124bb66c48c2161b5a1c8e13b7281 GIT binary patch literal 227 zcmeAS@N?(olHy`uVBq!ia0vp^9ze{&!VDx=%w8e^q!a^uLR^(L`iP5*mzI|H`!h_P zI(7N-@ZrO6vo-$z|L^`n;W$t+V@Z%-FoVOh8)-mJfv1aONJU(7!iN_x zG7=LW%sFtNq19IS)JLJ19W9NCOzKUxy28hv`OG%BDRky#1Y=lBg7Oi|vqqN{WHcQ+ zl$J4QR7a`xoXxaJj8dC$_QBPwj2G8Nbsc2e&3PiNfRTw!fT5^hqp8({1)Sou9XmM~ Z7_R?!H~Jvn&TU?b@{mvKWpXJ9gv7jc>CxX8%572~^5h666=m;PC858j$1Y>Eakt z5tppMz_QTfaV#T;bJzopr0L}74ApigX literal 0 HcmV?d00001 diff --git a/objects/ship/avalitechstation/avalitechstation.object b/objects/ship/avalitechstation/avalitechstation.object index b5f8f384..c293c14d 100644 --- a/objects/ship/avalitechstation/avalitechstation.object +++ b/objects/ship/avalitechstation/avalitechstation.object @@ -44,7 +44,7 @@ "animation" : "/objects/ship/techstation.animation", "scripts" : [ "/objects/spawner/techstation.lua" ], "scriptDelta" : 20, - "shipPetType" : "petcat", + "shipPetType" : "avalicleaningbot", "spawnOffset" : [2, -1], "chatPortrait" : "/ai/portraits/avaliportrait.png:yell.1", diff --git a/objects/ship/avaliteleportertier0/avaliteleportertier0.object b/objects/ship/avaliteleportertier0/avaliteleportertier0.object index f9f4ce17..10d54bad 100755 --- a/objects/ship/avaliteleportertier0/avaliteleportertier0.object +++ b/objects/ship/avaliteleportertier0/avaliteleportertier0.object @@ -36,7 +36,7 @@ "spaceScan" : 0.1, "anchors" : [ "bottom" ], "collision" : "solid", - "collisionSpaces" : [ [-2, 0], [-1, 0], [0, 0], [1, 0], [-2, 9], [-1, 9], [0, 9], [1, 9] ], + "collisionSpaces" : [ [-2, 0], [-1, 0], [0, 0], [1, 0] ], "sitPosition" : [-8, 8] } diff --git a/recipes/crafting/lathe/D Feedstocks/avalismallbattery.recipe b/recipes/crafting/lathe/D Feedstocks/avalismallbattery.recipe new file mode 100644 index 00000000..63682101 --- /dev/null +++ b/recipes/crafting/lathe/D Feedstocks/avalismallbattery.recipe @@ -0,0 +1,9 @@ +{ + "input" : [ + { "item" : "avaligraphene", "count" : 10 }, + { "item" : "copperbar", "count" : 1 } + ], + "output" : { "item" : "avalismallbattery", "count" : 10 }, + "duration" : 0.1, + "groups" : [ "ingredients", "avalilathe", "all"] +} \ No newline at end of file diff --git a/ships/avali/avalit0blocks.png b/ships/avali/avalit0blocks.png index a0d565da0ded6482830cd6d8e6a9e822950c18ee..5964bb79ed8b93f3a4531a2a3c42c4f27434ca8e 100644 GIT binary patch delta 295 zcmV+?0oeZk0k#5=7=H)@0000tuYYR*002TzOjJbx0RObt@c;ji|NsC0b&v`KY%##R zTC{C|fB^q>|NrQK7}Zn1zW_>K3r_%m{{R60S5a2a!~p;QUhz!;fB=sFj(NiNg8%>k zuSrBfRCr$P)!PcfFbsg<=FoNr&+7ZXsxh%RM23@qGV()734id>T`ldM3-Db521w)$ zO1_}x3R-?(I*{cCCTjgDXq|k)2K)qMsgu)g+Qvrr9G`#}Z%}h-hFzykMDvw!D_I2-N&u$kI-Z tfULcMeAMOY{vdh*4(t2m00Rs#z#Cz>+p-&0G64Vp002ovPDHLkV1nEZi{bzP delta 239 zcmVe7(4g5D;V?F$5v|12dP8o!bVfvS_MhJ1(D@>y>KIa*oRBKm&!=GYf$i1P pKmx)>X#`OI(+BYU^=S`1TmU8qaO|@WzkW&UFWl+)s z-Jz0dprh8Gf~BJ_Sm)n>k-PADOs}uc!GvL5116br3H#deEw~JTBQFev2$2Ku05Y`Y zHz4(mgGBluhzC7bmjK&9!~%N6EBYQR%aiw;KuPh1kvsEYFy@}XxXa7w{(1&3^L?^` d1{!GK9ebVwdJsh|WxW6Z002ovPDHLkV1nBrf@J^z delta 243 zcmZ3+)XX$NqMn7Bfq~&u{G==(r5@lD;`$%RVE7M&4FCT(=xELXQb1C@Av54u!_T29eAylO-B_jj@hN#-04 zE6qY$XI6+jGTkV{qs5_Z!_21sLCeN@$|2k3ooZC9T@efaeGDa;2Y4J_h-z-$s% iL*|!lRUr>oI58Byi0@9Av~vN_Sqz@8elF{r5}E)^?PcQt diff --git a/ships/avali/avalit2blocks.png b/ships/avali/avalit2blocks.png index 7974acddc93a5c75df146dbcdc6fd24171277e59..39458044ba1b142cbb33c9ff83812c97f5636947 100644 GIT binary patch delta 278 zcmV+x0qOpW0;K|w7=H)@0000tuYYR*0022qOjJbx0RObt@c;ji|NsC0b&xT@yb1(t z|8@Tu)l>iIfB^sh{{R601O)$AQ2_q{R?ozGdU}rkj+_AhT>Uta0001|Nkl zf6)Ns|NlSX|Ns9+q5T{{wTvY}e!;)9IzN8`a{4`8978JN-d-`}YIYE4P1JwHYZJhy z5dDNN^C|1^kd?)3>jfgX1)G}Q>qV-WIo&APv*5;#dc(^#Z%SsLwA?s1p{Zmk$IB28 z$)KjYtxH&n+qSi+R15Aq6vN!eW)_efcQw4?`@hV??RKxXwLItO*~jtvm-Hp6#S?^b wmhVs4csKI_i;_z=LxAi{=88A9RtH(`Kfay!<^IpsRUlt^y85}Sb4q9e0GRY}ssI20 diff --git a/ships/avali/avalit3blocks.png b/ships/avali/avalit3blocks.png index 2459326dadd6107f0b77c8da0be77d6fd6f4d3a2..706c868d59a317f6474001973b0460ae4a9e7732 100644 GIT binary patch delta 291 zcmV+;0o?wZ0-7=H)@0000tuYYR*002BtOjJbx0RObt@c;ji|NsC0b&xT@yb1(t z|8@Tu)l>ff0H>$_sHms_|Nj900RIF8|5{pB&%}CqdXE2&oB;nilG+OZ006N`L_t(| zUhUNj2SYIohT$f?o!xK$|G(~&wNz1C7b}yOLj;c~9CudE?SGW}3(!CVy$5j91d;BA zk~WC+KzAvm8t8oOH=sikZ+O%N3$O+ZB}}8<2Q#N_OPn#Wl)0@ghu~ff9AB7NN&yi? zqJZl18_>7SgWl_d#E4q3EQvk?nO|UJK5^_oEblS4;5dNN{Dpl1J@5exQU`{sEZ6>| p40K2tJSc-`^&A@-XrO@xJ^?>;g?w#OaVG!(002ovPDHLkV1g7sfc^jg delta 268 zcmdnQG@EIHL_G^L0|UdS_(@qnN;kkK#PvUr!SEjl8UFvTs;Zj9@PGO8|0`Ck00~4z z{V$#Hzx4lqp#1*{K)Q>^EL(T6RPb+>)G1tFCIDkd0somp??Q`J{|PiB)n7Qn{tH0 zWv><=ttC8~2S3kR!jk^wn8W6K(%FJ$8;?6bXpp$^XydBs1@k`}x_x_>{hepI))qdO zEs||L>+%&c7!afeR+(Ddk PFFjrTSY6gRB{Ts5Xh(b! diff --git a/ships/avali/avalit4blocks.png b/ships/avali/avalit4blocks.png index 848f2076495c95d931f4a0054a322b792f9affde..a741b260062c200c07a78f114b1cc8a57228fe02 100644 GIT binary patch delta 340 zcmV-a0jvJA0^$2e|9|SzmOurM!b%X92Y(~wMfng1Xs)7B?zbqX zoN|aXK7w2VB{_;LN0H*pjX@dCAZG`hq4a-uZ%K}e{c(}!1>nz@I|DTBjh zTHA57bXvb~jXTM5lWMr<46zQEHD?Q8(;grIqyv}D9N&O-xSZp#PG8P@<`AVhNEJ-D zQUdhkXl5=rcTI7j=1O@8VUgpsb19oo*UrV9DaWPvX&mW?xNzYsmw)*srktT{tow3j mv$5XU)9GJDIpvg7PPs3miYB(LePSN~0000_ z{{R60T3Y{gkpFf6|Nj90|B(Ox-2eaol4*!O0002NNklxzBg8xvSLfT!j^Y1GIr+dClu@ZtlI`$))W`v3QBL zP3p!5m+splBy~Dl{&IT`NuAE_nXU=M;!ck?Zuse9Ea!}=0IbzP(f}ibJ9Rez^&fvf qKVK~8GF|V@O&vPkSN>zg754;0OM-=k0f`_00000Fn_T3Sk93s%p>0RR7bdV2r=+>ZZ_h}-Eu z0003WNklksf41nP#z0CW$`~Ht=8XRe%%(I>vhJPnNNTe%#s9OQuQ8nZK zmT|@zhe+dFkz-Je<1{%=i*xS+YH$wcHo!S*{BQTA$qBGE&MzgGA8hXw0}5h702~_x;E#znv=ymPV^$Kz46miCD+kU?%31zIZy5ACGVPJ4A(xm zWK4K(9cSF(gFC2j^0>bk2cXGuTAb&~bJCZM=A`WGhMs-IIWey12?Rg{ z8J(PQ01Xb{NgUT6<2k?7cK)O?&NxS@a%WYJ`p37*4HQ_D_GoGjQd|H4002ovPDHLk FV1kp&$S?o^ delta 342 zcmV-c0jd6}1LXpc7=Hu<0000u`!hcP001yhOjJbx0RI3LfB^ph0RR7bdU~7y|EH(_ zsHmv_00945TK{#B|8@WW{{a90kpKVO|Ns92-jp){008hwL_t(|UhUN}3c@fH$Kh^v z^8_Bj3wZ*M;ouPJAzaH}p=%L}yH}9V)hP%rMJX}#K|&zd@_(wRr9bec1U_0qz)0{K z-RQcc#hT}|U^>lpn&Ylp137N0P5s>}io2f6jpr&e-{%tS0h~Y!gdJ`_4Y`e!vM@hd z&cY69q9ym;lIh(04ruZnns%?;Q8)RH_8uJ_4UQ}8K5ia#6yrNx34n|oOlB-VPfd3K zsdeF$x`<#OaZll!+3vg>R?uNpLv!jc+>GU5Qm|$1&c(Pdl>N?T$$#5vS(Nn<9sgY5 om3ofR=-l5G&rO&viobgO1Cs=h7=H)@0002?Za6mp002c$OjJbx0RR90kpKVxwAb+eb&v`KZ2$k< zF~Ge4b^jREQ~&<}r>Fm%0RO0{r~v=}{{R60T3P`A07_pAR?oy*v~7BNdXE2&%&F*U z0004rNkl?sX3_j89FEL_LpE^f)!oys)U{8C=?hXF~P=eov|%1H1IxTJh~G58m!gK=I&~ z<@~lkojgoe9$fV<%3ikfKzWz8ETfk{Xzgfi`rvjC)7CsptLz~z9-_KuA!JjJm)2JG z!X$45c!e?ZXn*-Nj+rOh9H&aRP-D-3~sGz-t~sL1A91E?0#L8J##OQ@8>V$%$sT+olOsu zywbcRR?(YqZttG#zTU?hwT$MT2S9{4ntC9;05|hO!lRmaj{4!bpH=nF!i)EXDaG&e zAYL5D@*L&mSUSY-f8Fa|_e@CFd(`zpEBj)v{BxIk1)uL5+xEwr(@f6L00000NkvXX Hu0mjfb=3@K delta 441 zcmV;q0Y?6l1jhrA7=Hu<0000pp4B!0001yhOjJbx0RI3LfB^ph0RR7bdU~7y|EH(_ zsHmv_00945TK{#B|8@WW{{a90kpKVO|Ns92-jp){00B}-L_t(|UhUVxYJ)%&1<Af*Tz)t3U}U70^lzys}aQ#aP(|6!E^tYbCUxO9(807+ere%CWcK4AY{> zi?48*-<2cfrv3W>jO07XTn!1DLJx zUyi z00068Nklub20K)HOmu?$pFt5`Rl<@dw32>fsPmG#Tiy zh1Kah>vX3(-RVwuy3?HoKtw^b*desqA+*{dwA$|U2Fsh?0%@~}IQ5g6z39#85z^>J zZ#hlmN^gPm7IokjYXNlaFI)!CpC)wez=;G8p`{pFi=c5B z7PLO8jeNxq4Wt6;vBgtLrzat8xt+Ezxci#XM9`%Ttw*^x3XK8{c+ja2wA7kr>51m& z+-XxA(92Ri@Y`vUoN1s)+tQ5AgD1^TEelIiVbS`3UUj`Y*?hihJz6U4sAs002ovPDHLkV1hjVOO5~l delta 564 zcmV-40?Yl71;GT67=Hu<0001-q-gX2001yhOjJbx0RI3LfB^ph0RR7bdU~7y|EH(_ zsHmv_00945TK{#B|8@WW{{a90kpKVO|Ns92-jp){00GQNL_t(|UhUYyYQjJe2H-t; z?GyA7`a*q*K12^8!Dr|-jH1^d#NP7=m1XY=rO=BgVLBN~Mt@Uuv$I+$&5!KH;KvTj zYyz4jCVx&3deDO&v~?JxEpB79!x+tr>$O9*9@{2bov5`e{ti%qMklOGYgZ~gX68)LTQX3PhiGGR>-?c4)z8WYV z%^ih1m_nY?(0_dUo|1Nu`?+YT5BJvEBBPEYqzPZhA|nH&(F-T)%>D&X02dks@q zKsD*FQxZTb&*XUZ*MvC5Ep_^RMCUs>p&`0zHTRlOzg}*-mG{<&cmWLn3xG+W=E|kl z`f7Rq&Mnr^$?*T$pt~_9NmsHhm#Q~&<}R?o!$00030|9X0Qj{lCF0RR75S^)n5MwkS$ z0008JNklvEeg5QbsC6(`$C&+YwRYK0=_Q>YB!!GF@G-uFkBVQ}XeAP8sD zeAAQ;jcH6{8q=7@G^R0)X-s1p)0jryQlbGMvPLouO!Kj}Z7)7m=f-n%Xvoo^AxDFT z937g-Y1{gm7EFPr)uaJA5OJ|;8gd}gA=G(5PSj|Cm___*3h#@!~YooH#_vNue~3P zJGW@)(V(G6fu{LQj{;3YS_zHFA)3awMFV6sz)X`bLcSlPp^FjA&n#*P*r7S?j!(bi zH7+K6Z8@FXQmMu*b%*A9Z;Hruvu#(d6z}K71Gj0;s(&===cY%fcWKw1sU|&e7{*~p z4?J2UX-gVOJ5~cMYRDE*rWxQHQcc`B40mdPcMY&b!_V|1@CV~eGXg)yk(#~hRMdpF zXhH{^80c}=*Vd8KAwi=R;AVmdVc zewzYBz)?jb^4yxnx2OR!8la9QlpDn~eCWER3Tf7psb|ofKkd0OjoVZS4d0Tx>{T7_khNGnBLzOj-eRw(V=YuV29@%9odw^+V>T_yLV;a*mKBbz)Km8-Ugodvc z`EM>k^P)wg>)aH&Z_c|kO3saGfQ$yHp&>&g9lmGOFly3KQR9{>tI4lZr0BU)ooP&C znlodcH+wV{GEL*tqGXMYp delta 671 zcmV;Q0$}~c2CD^-7=Hu<0000sZbt?H001yhOjJdBdU^mAfB*ph0RI30|Ns900RLKA zoB;p-b&&sc|Ns90sHmu?r~m)|kpKVO|Ns9fJK~}M00K5iL_t(|UhUe!YQjJe2H;-$ zB7yeOzDtV`NP)Ib6FTP7li)>i&AIR31C&xAK*$^PCMY1&&3|-tjO&7#3{8;z)eS`X z$b{Kl5wBvEJYKRBoZti}IKc_dD@Fn8lWTZ01HDZF6L$P@r+}OPc7Fg0^YF~94h~I$@^D6!wi(z7 zPDvfu)2X+kak#F}o(|a1^xDU9x*i$~p6UW; zQBlg#Ie)v25MdvglUavut7LV>Np+p^7F$d z26f!U^it{y_t{MpsX6Y~Z`#!aj`Ov7a;M^GU617c+$%ZJ#XGcD4CQd-b-78P6_Ml2 zuQb-FYc_xr9C`nw7x=$|C{l7h#&JEIu{psBP8e{4^9xf9bko*G@Oc0L002ovPDHLk FV1iVFMi2l1 diff --git a/ships/avali/aviant0blocks.png b/ships/avali/aviant0blocks.png index 90bc31a422d06eaea7f7a455b608fa91bbdb257b..70c53f9eaead779d5d4ba78db485afb0a46eb710 100644 GIT binary patch delta 290 zcmV+-0p0%c0kHy*7=H)@0001{8J!FO002TzOjJbx0RObt@c;ji3IuHbb&xT@y#MHc z|8@WW|NmOFZGeCP7}Zmb|Bgyu3%|br{{R60S5Z#@fL71M0RR7hGXMa909+1MI{*Lx zs!2paRCr$P)QJiLF$@IICO!7_JpTVzO;h4Zp->e;a0W`k0Dq5C*mZ}m1vKbZK!XOI z{`^6LWR_!_W?#m2n7*Y|JEb%Epq=%`jtv6IIV4&68=;WJZG z`a#%R4Gj$@HI_yOC(D4?t%eda1Xf0I@`Mzuj@D~f73LKBmSsudOx-0NE3LRz#4+~2 z>Mgih=5X1cV5JBniwSWda3V&$Opu?YEh)ubr)u;E{ z3LV94n~|8Ok|;W@{8`#m#n=>P$DD^&eunlsD|Fb=-j8TqE}Xj1oDtUatK?GBvHA0~ zNTr>~S)8V8d3B{EYUMPYGj(d}?fjFur delta 215 zcmbQw^qg^mL_G^L0|P_y%K}~?r5@lD;`*O~p~0A;K}Yj{>4g8K|G{9w{}&Daff67D z!gCn@A1x9Q1Zt`Fba4!+h)b6ElpxZ>RMytWcI?>MV+n_D9Fp*od=U0_+O%m*YL^-q zoGb%kw;D>!5Ljs?!sAr1TCZUhpKIt_u7D+lGv`XRt*jCbsAZ75dO0BEJ>Stpn`Pby zRuxL*<<#7;xFW&fbWq_25QLbRm~i=i)zC|chSls{s diff --git a/ships/avali/aviant2blocks.png b/ships/avali/aviant2blocks.png index de697689e4d0a4e88016c35eabcec892ed2c1656..f00698fc122d4def1a72fd524b787744d5766984 100644 GIT binary patch delta 264 zcmV+j0r&ps0ha=h7=H)@0001{8J!FO001^nOjJbx0RObt@c;jiF~Ge4b&v`KZ2$lN z|8@Tu)l-}R{{a90{{R60S5W}}09Mb$|LA~vdU}rkj^IjCb^rhXoJmAMRCr$P)QbiK zF%X5}%;dJcx9|V5%_JG7t9FxCDbAscAD_V1oqjgZpx+NPXn)Y4L!Vq5`-ILMmnCwR zt1h#h#=oUwQ(`D1@@D5m!`JAX#$j4E@M=S+@lWPZenkm&D>__X;&GqG-xFxiph1HM{RAh(u1{7_WaR(= O002ovPDHLkU;%OfRqY3Xdj(xfR|xSv zHjHHo>pfhM{5UCDHCZ8mi8(bnIeD?CmzPz-)n*Z}RSYKE6w{>Fr=|g2!QkoY=d#Wz Gp$P!8J64+j diff --git a/ships/avali/aviant3blocks.png b/ships/avali/aviant3blocks.png index a402fb5dc2c8f0c6e69a9eeb991320004d3127df..d293d35034c6b9e41e549ee5f6ee874288828090 100644 GIT binary patch delta 282 zcmV+#0pUl@sHms_|Nj900RLKAR?ozGdU}rkj+_AhHdwxh00021Nkl69p?#wmi}dr%0MatJ^v?Y=h9pnsPK8Z>Coep)`Q>;syJ zY8b?gqmv<@$_8nt@n>mQ73b5K19LuTtj2XC9ouQ#a_bn^XpL5yGr}!BMJ|!f>i5$k zl@1~&2`ydYEtE*4v7658x;4%99lf7FnR|MY;>%uWe|d_>E?R?Oe?=Q!GhLGCV4}^p g#t#fMXwaY^*%NYpy_H1z5&!@I07*qoM6N<$g08TH;{X5v delta 242 zcmZ3<)WkGFqMn7Bfq^0UWdSdc(hcwlasAK0@IQ^=e^k`}(g`b8tf;E0`u~5z|NsA& zFaQ6)^#2?NkUYbGAOaEBj6Z|)Pw;ec45^4qULeua6llQJ(Qwk|+b z0002ZNkl_z;}2@@vj6}907*qoM6N<$f@D>Lk^lez diff --git a/ships/avali/aviant5blocks.png b/ships/avali/aviant5blocks.png index 3b656f68ce12c3960ea3f62905e1b1bc3abc1911..af7eb51a149bf9eeb2b7c3127f98b45ee95d3afc 100644 GIT binary patch delta 434 zcmV;j0Zsni0>A^17=H)@000214aju>002c$OjJbx0RR90kpFd%3IuGl*YGjGy#ICo zsHmt~v~3vGQ~&?mr>Fl)Ukd>L|5ne${{R60T3P`A0D5|Q|Nj7v|Bjph|NsC0cp-m9 z0003tNkl1xC<3G%h{eMoW231${r5xVN#Bi*W%U>!zhj^{brH>^&=5A9ZbElFyDHrA^UbHfm zuS+b6N;$3+9j%P%QO5M_C4IKt%KD^KYFXStDKS=AO9^=s5!KnjdsE(RsNBPw+p(0) z4JGrwlrdI0_U1+*LnGr#$uY`O_9{=*F*4!nZ2Df#4=cXMDdY&s6JalLdZ2_7%GrSu cN+_ZH0A1DW5UxNDe*gdg07*qoM6N<$g3^-GF8}}l delta 335 zcmV-V0kHnS1Kt9V7=Hu<0001P{Uvk&001peOjJex00960|NnK600960|B(Ox-2eXo z|8@VUsHmI(|9X0Q|5{oA{{W|_{{a90>qnxFaYJAgLYT)gvwx7~F+U{gCyJsdilI3X z9jHn_amLJCGViJs?8~xur#i7H7jUpUnwdJdBbM3G`3(F3=@=v61;CrWI0-C-^F*L> zUVKhNf|t{Rsc;q+wAQ(8{6{%-(pgkNI9(chdzWMKIBnB8xw>1hb1$81QL1pt(Rc41 zPU~@sr#U$+0!T=RC{1WdDQ*_jpKslqQ8>eKVnUKpwrY18Ir?wT^QUtIX8@i$2f)9( h!t{GlXE=(YD0iOLr;}MNq5%K^002ovPDHLkV1hu@n$7?K diff --git a/ships/avali/aviant6blocks.png b/ships/avali/aviant6blocks.png index 725fe07058fcb10fb539a701011a839ae7f9b386..91397dbdd7edb86c582b84bd4f86ab6df21501c2 100644 GIT binary patch delta 483 zcmV<90UZ9M1Mvfp7=H)@00001wC2(P002BtOjJbx0RR90kpKVxwAb+eb&v`KY%##R z7}Zn%b^reW009600RI41&&2;)TBxX~j{lDT{{Wl-|NsBor>Fm28lNNp00D4GL_t(| zUhUZHYQr!L1z=0I)9!wI|94$05V4qNInCTTaLymYLC}vFm47zj;Ru1UFHk}WC6rJ? z2_=;2lJ}JA$27__<F-SE;IJDHm-xyJ%caGCD*ffsj8SHi zWW&?)D9ZIBQ-3SngJYCGzqYB2mfeGgQkq{=f2q_imGK5+Tw=6?61MS`Nn$@-%bJqQ zXz32NmHfN%%U3e1lDVbaGFXp$PM$6lj@mfU-k}^seUTVy}N+MTj2N$9zT8vW4 zw?)bjxRx$uDpM*YJxYo5EN@k=H&tXU)y=FMWtCdW;9N&*@L5Zn$NkC~-V+^5NmNl1 z&83uBrIrrBZlXW3{~v`>YT2uN4Le30rJqTj<@n&ey57>PN`EQvEJp@PD4~QBN+_X( Z@*B&6N&wLN1;qdW002ovPDHLkV1gBt^9BF_ delta 406 zcmV;H0crm61Ed3x7=Hu<0002QL<`dZ001gbOjJex00945TK{#BsHmu?r~m(T|NsC0 z|Nq?o|BwLx{{a60|Nj600RNl-|ESktLjV8)HAzH4RCr$P)4@suF%W>^B~ZP1_FM++ zMSKGvLIf6%rO-h^ptn3hCHOc#fuMNGLy0rPE;F^RO;%Xy{C_}l2_Kte)BMXk&+`IY z0>BH%G>SP(Dcz;Cm1$hlO_ckRX ziM!_Gd1o%m&1Y?XV3#q?(GTqXm!?_zh4VnI_`P58qhFA~Yov!^v#IZx%$yBymS2B1 zgFgE2%xi1Dky+5dcOF~g?@CU;x=EbA=Xu^IFQ4ch;t$B}cmMzZ07*qoM6N<$f(Spy A0{{R3 diff --git a/ships/avali/aviant7blocks.png b/ships/avali/aviant7blocks.png index fdccd7f5aaad287e32e8661c648bf04a251a5462..8b12164c3136cc7e181cf55269c40794ed626ef2 100644 GIT binary patch delta 533 zcmV+w0_y$N1EK_w7=H)@0001*C8Wdv002BtOjJbx0RR90kpKVx|8dt@0RR60{{a75T2{}*j{lDT{{Wl-|NsBor>Fl>I~Z6100E&%L_t(| zUhSFNj>8}fM6nG?yZ6ig|LYbSE6}o-G)WYQi&Fph``5eDU|DpmH-(`fW9mP*+aIZiKbt5 z(UOB_ZWQQQRIVN@<#KVbk0q6;E7yLO=7QXrMdk9ewUo=nK}$=yT+BX}4gxOG<0Y@<$<}5LWLvgTd ziG}vamWXVLSh8F)m`Ad_YB-4D@G(p4BbyF}(1y^wV}`Ys0HS3&H~_rCkPL?Ka|od^ zn4`*)Vq!~Am8C~|mycR=(D=}9c}OU$b6%v{)FqreyrH`~sPd0AsA*Q4GNQFxayqo(=Vc9(BEvw#Gr}gO2a&>!XU}E8 zy@>B39*W1Zu!DjJZ+(K5;N$oNg5s$UC7Hwylh6{AKo`wVNq-7kKA2>~r1_Vrsi~<^ za18)&;HDzxMr(bq^~p_T&1xd=mzmmh^X1{B(VFa=>GrHlb^=qSEPw(L-`pfGK(j@3 zZ_@0J#Z06nwkDF9c+|7aaSXO+uJ(KrMD_;&Xmd;sVlRNRGR;PrfgX3>)z4ra-ppuo z8Z?Ff^X@|}3V$kYDk|bPCQz<~c?T3{ZqY`XpTSXQ6Ddbwb{6r>Ue+F*BP33m9S>|G z2Yu69rWq4M^XYOj`*ibFnqN4@H|5eV)*f%lPYrGd%E6xZ8`#sECHQB=M>1uC?c@x@ za}$@fOz!V|-gC|69=99cmrW&?KBmX*KFEy9#V|B8CP4~wENZ4VPn@Ye|8B#*n0j1E yv-};boCzZB>UZz(PP>A+YVBe3{41%csrd$B4sG;HH{T)v0000E=n=sq6`$wnjcL5r6mQp+`W75<>fUxRVXs z$(`KEo!rTt+{vBX$(>@ydyMNtS4$k#>5R?g(%>0;c;HZ}-KhX~awj6O28a>|Z~{Oc zl9N~ih{}69AugRbBogW)I}NN4L{hCg#fgobq6Cpxt4V)jaRN;_8o$*E-GI)zT-Q#F{wp?_Ro=~VSml{-ygHH6^>R@l}F zQFB^MtPwx3&Yi~avmU|_V2x{>Vh)E&&D*$tIrV7o)192sha!VRrB2&8fb<nVJAcPga8GfJtKED0Lu9u%bRi%&-apmDJS88$*fb9FnLed z%GkRn)Sc|z6Y5R|?+FW>4BZp@f2iEaoowSy?&MDHbePo!Y-hfsvv;-;G(iy`KML= zsVV#LwZhSnKz|oFKl4;O3E~zsa}7z5^w(8Mh6|{kNI(?{_p0K7qp5ly9ppejc~wOg zP{l%xDz>w_H7#V)#u`-`K&6i%ExUk%(Sm5J&dpf$##feCy@INPv}_f&9J?JS6>z!= zTaI<9h-fc?1)YH(H0*@ovGO7+gOREQ)r`Tkb(Q^ta9>f`69rp7i2_^RVz4`Po9;B)3s6^nW(!IN?BEa#|a|npjRc=!%{{s5l8q4s1 c$Ye5^Pg-bi