diff --git a/Language/english.json b/Language/english.json index 3593f47..2ca4b09 100644 --- a/Language/english.json +++ b/Language/english.json @@ -10,6 +10,12 @@ "nameUpper" : "EXECUTIONER", "description": "The Executioner is a mobile fighter who specializes in counting heads. Using Ion projectors, the Executioner fabricates illusions which cause foes to run away in fear, while also projecting an axe to take out the strongest enemies. Make sure to chain kills with Ion Burst and Execution to keep the damage pouring.", "endQuote": "..and so he left, bloodlust unfulfilled.", + }, + "chirr" : { + "name" : "Chirr", + "nameUpper" : "CHIRR", + "description" : "Chirr is a mystical creature who holds a pure connection with the planet. Her innate healing properties give her a highly supportive role, being also able to withdraw from any battle easily thanks to her high ability to hover. Chirr can befriend one creature at a time through her Natural Link.", + "endQuote" : "..and so she left, carrying new life in her spirit." } }, @@ -34,6 +40,31 @@ "name": "Crowd Execution", "description": "Launch into the air, and slam down with a projected axe for 1500% damage, fearing enemies.\nEach successful execution reduces skill cooldowns by one second." }, + + "chirrZ" : { + "name" : "Life Thorns", + "description" : "Shoot 3 projectiles for 270% total damage." + }, + "chirrX" : { + "name" : "Headbutt", + "description" : "Headbutt the enemies in front of you for 300% damage, stunning them for 3 seconds." + }, + "chirrC" : { + "name" : "Sanative Aura", + "description" : "Heal yourself and nearby allies for 25% of their total health. Healed allies earn increased health regeneration for 6 seconds." + }, + "chirrV" : { + "name" : "Natural Link", + "description" : "Befriend the closest nearby creature if it's below 50% of its total health. Only one creature may be tamed at a time." + }, + "chirrVBoosted" : { + "name" : "Prime Natural Link", + "description" : "Befriend the closest nearby creature if it's below 50% of its total health. Multiple creatures may be tamed at a time." + }, + "chirrTether" : { + "name" : "Unbreakable Bond", + "description" : "Press to connect yourself to a friend, redirecting all damage to them. Hold to teleport all nearby friends to Chirr." + }, }, "item" : { diff --git a/Sounds/Survivors/Chirr/skill1.ogg b/Sounds/Survivors/Chirr/skill1.ogg new file mode 100644 index 0000000..c095b54 Binary files /dev/null and b/Sounds/Survivors/Chirr/skill1.ogg differ diff --git a/Sounds/Survivors/Chirr/skill2.ogg b/Sounds/Survivors/Chirr/skill2.ogg new file mode 100644 index 0000000..33c3010 Binary files /dev/null and b/Sounds/Survivors/Chirr/skill2.ogg differ diff --git a/Sounds/Survivors/Chirr/skill3A.ogg b/Sounds/Survivors/Chirr/skill3A.ogg new file mode 100644 index 0000000..11ad153 Binary files /dev/null and b/Sounds/Survivors/Chirr/skill3A.ogg differ diff --git a/Sounds/Survivors/Chirr/skill3B.ogg b/Sounds/Survivors/Chirr/skill3B.ogg new file mode 100644 index 0000000..0e44964 Binary files /dev/null and b/Sounds/Survivors/Chirr/skill3B.ogg differ diff --git a/Sounds/Survivors/Chirr/skill4.ogg b/Sounds/Survivors/Chirr/skill4.ogg new file mode 100644 index 0000000..c3b4b7f Binary files /dev/null and b/Sounds/Survivors/Chirr/skill4.ogg differ diff --git a/Sprites/Survivors/Chirr/climb.png b/Sprites/Survivors/Chirr/climb.png new file mode 100644 index 0000000..d3c1378 Binary files /dev/null and b/Sprites/Survivors/Chirr/climb.png differ diff --git a/Sprites/Survivors/Chirr/death.png b/Sprites/Survivors/Chirr/death.png new file mode 100644 index 0000000..339027e Binary files /dev/null and b/Sprites/Survivors/Chirr/death.png differ diff --git a/Sprites/Survivors/Chirr/decoy.png b/Sprites/Survivors/Chirr/decoy.png new file mode 100644 index 0000000..0a7934a Binary files /dev/null and b/Sprites/Survivors/Chirr/decoy.png differ diff --git a/Sprites/Survivors/Chirr/flight.png b/Sprites/Survivors/Chirr/flight.png new file mode 100644 index 0000000..766ca11 Binary files /dev/null and b/Sprites/Survivors/Chirr/flight.png differ diff --git a/Sprites/Survivors/Chirr/idle.png b/Sprites/Survivors/Chirr/idle.png new file mode 100644 index 0000000..748439d Binary files /dev/null and b/Sprites/Survivors/Chirr/idle.png differ diff --git a/Sprites/Survivors/Chirr/idleHalf.png b/Sprites/Survivors/Chirr/idleHalf.png new file mode 100644 index 0000000..26758bc Binary files /dev/null and b/Sprites/Survivors/Chirr/idleHalf.png differ diff --git a/Sprites/Survivors/Chirr/jump.png b/Sprites/Survivors/Chirr/jump.png new file mode 100644 index 0000000..703981b Binary files /dev/null and b/Sprites/Survivors/Chirr/jump.png differ diff --git a/Sprites/Survivors/Chirr/jumpHalf.png b/Sprites/Survivors/Chirr/jumpHalf.png new file mode 100644 index 0000000..3b9ed50 Binary files /dev/null and b/Sprites/Survivors/Chirr/jumpHalf.png differ diff --git a/Sprites/Survivors/Chirr/palette.png b/Sprites/Survivors/Chirr/palette.png new file mode 100644 index 0000000..e569d82 Binary files /dev/null and b/Sprites/Survivors/Chirr/palette.png differ diff --git a/Sprites/Survivors/Chirr/portrait.png b/Sprites/Survivors/Chirr/portrait.png new file mode 100644 index 0000000..6674006 Binary files /dev/null and b/Sprites/Survivors/Chirr/portrait.png differ diff --git a/Sprites/Survivors/Chirr/portraitSmall.png b/Sprites/Survivors/Chirr/portraitSmall.png new file mode 100644 index 0000000..c7a9937 Binary files /dev/null and b/Sprites/Survivors/Chirr/portraitSmall.png differ diff --git a/Sprites/Survivors/Chirr/select.png b/Sprites/Survivors/Chirr/select.png new file mode 100644 index 0000000..549d74d Binary files /dev/null and b/Sprites/Survivors/Chirr/select.png differ diff --git a/Sprites/Survivors/Chirr/shoot1.png b/Sprites/Survivors/Chirr/shoot1.png new file mode 100644 index 0000000..504a2b9 Binary files /dev/null and b/Sprites/Survivors/Chirr/shoot1.png differ diff --git a/Sprites/Survivors/Chirr/shoot1Half.png b/Sprites/Survivors/Chirr/shoot1Half.png new file mode 100644 index 0000000..2803076 Binary files /dev/null and b/Sprites/Survivors/Chirr/shoot1Half.png differ diff --git a/Sprites/Survivors/Chirr/shoot2.png b/Sprites/Survivors/Chirr/shoot2.png new file mode 100644 index 0000000..0a5b6c5 Binary files /dev/null and b/Sprites/Survivors/Chirr/shoot2.png differ diff --git a/Sprites/Survivors/Chirr/shoot3.png b/Sprites/Survivors/Chirr/shoot3.png new file mode 100644 index 0000000..278f9c1 Binary files /dev/null and b/Sprites/Survivors/Chirr/shoot3.png differ diff --git a/Sprites/Survivors/Chirr/shoot4.png b/Sprites/Survivors/Chirr/shoot4.png new file mode 100644 index 0000000..ffc0192 Binary files /dev/null and b/Sprites/Survivors/Chirr/shoot4.png differ diff --git a/Sprites/Survivors/Chirr/skills.png b/Sprites/Survivors/Chirr/skills.png new file mode 100644 index 0000000..79aa7ed Binary files /dev/null and b/Sprites/Survivors/Chirr/skills.png differ diff --git a/Sprites/Survivors/Chirr/sparks.png b/Sprites/Survivors/Chirr/sparks.png new file mode 100644 index 0000000..1b53615 Binary files /dev/null and b/Sprites/Survivors/Chirr/sparks.png differ diff --git a/Sprites/Survivors/Chirr/subportrait.jpg b/Sprites/Survivors/Chirr/subportrait.jpg new file mode 100644 index 0000000..bbf13a6 Binary files /dev/null and b/Sprites/Survivors/Chirr/subportrait.jpg differ diff --git a/Sprites/Survivors/Chirr/tameIcon.png b/Sprites/Survivors/Chirr/tameIcon.png new file mode 100644 index 0000000..02f9b7b Binary files /dev/null and b/Sprites/Survivors/Chirr/tameIcon.png differ diff --git a/Sprites/Survivors/Chirr/tracer.png b/Sprites/Survivors/Chirr/tracer.png new file mode 100644 index 0000000..bd276fe Binary files /dev/null and b/Sprites/Survivors/Chirr/tracer.png differ diff --git a/Sprites/Survivors/Chirr/walk.png b/Sprites/Survivors/Chirr/walk.png new file mode 100644 index 0000000..02f99b0 Binary files /dev/null and b/Sprites/Survivors/Chirr/walk.png differ diff --git a/Sprites/Survivors/Chirr/walkHalf.png b/Sprites/Survivors/Chirr/walkHalf.png new file mode 100644 index 0000000..1f2e17a Binary files /dev/null and b/Sprites/Survivors/Chirr/walkHalf.png differ diff --git a/Sprites/Survivors/Chirr/wings.png b/Sprites/Survivors/Chirr/wings.png new file mode 100644 index 0000000..6185b5b Binary files /dev/null and b/Sprites/Survivors/Chirr/wings.png differ diff --git a/Survivors/chirr.lua b/Survivors/chirr.lua new file mode 100644 index 0000000..5a5814f --- /dev/null +++ b/Survivors/chirr.lua @@ -0,0 +1,604 @@ +local SPRITE_PATH = path.combine(PATH, "Sprites/Survivors/Chirr") +local SOUND_PATH = path.combine(PATH, "Sounds/Survivors/Chirr") + + +-- assets +-- icon sprites and stuff +local sprite_loadout = Resources.sprite_load(NAMESPACE, "ChirrSelect", path.combine(SPRITE_PATH, "select.png"), 15, 14, 0) +local sprite_portrait = Resources.sprite_load(NAMESPACE, "ChirrPortrait", path.combine(SPRITE_PATH, "portrait.png"), 2) +local sprite_portrait_small = Resources.sprite_load(NAMESPACE, "ChirrPortraitSmall", path.combine(SPRITE_PATH, "portraitSmall.png")) +local sprite_skills = Resources.sprite_load(NAMESPACE, "ChirrSkills", path.combine(SPRITE_PATH, "skills.png"), 6) + +-- non-skill sprites +local sprite_idle = Resources.sprite_load(NAMESPACE, "ChirrIdle", path.combine(SPRITE_PATH, "idle.png"), 1, 12, 11) +local sprite_idle_half = Resources.sprite_load(NAMESPACE, "ChirrIdleHalf", path.combine(SPRITE_PATH, "idleHalf.png"), 1, 12, 11) +local sprite_walk = Resources.sprite_load(NAMESPACE, "ChirrWalk", path.combine(SPRITE_PATH, "walk.png"), 8, 13, 13) +local sprite_walk_half = Resources.sprite_load(NAMESPACE, "ChirrWalkHalf", path.combine(SPRITE_PATH, "walkHalf.png"), 8, 13, 13) +local sprite_jump = Resources.sprite_load(NAMESPACE, "ChirrJump", path.combine(SPRITE_PATH, "jump.png"), 1, 11, 13) +local sprite_jump_half = Resources.sprite_load(NAMESPACE, "ChirrJumpHalf", path.combine(SPRITE_PATH, "jumpHalf.png"), 1, 11, 13) +local sprite_flight = Resources.sprite_load(NAMESPACE, "ChirrFlight", path.combine(SPRITE_PATH, "flight.png"), 1, 11, 15) +local sprite_wings = Resources.sprite_load(NAMESPACE, "ChirrWings", path.combine(SPRITE_PATH, "wings.png"), 3, 11, 15) +local sprite_climb = Resources.sprite_load(NAMESPACE, "ChirrClimb", path.combine(SPRITE_PATH, "climb.png"), 2, 11, 10) +local sprite_death = Resources.sprite_load(NAMESPACE, "ChirrDeath", path.combine(SPRITE_PATH, "death.png"), 8, 16, 11) +local sprite_decoy = Resources.sprite_load(NAMESPACE, "ChirrDecoy", path.combine(SPRITE_PATH, "decoy.png"), 1, 9, 10) + +-- skill sprites +local sprite_shoot1 = Resources.sprite_load(NAMESPACE, "ChirrShoot1", path.combine(SPRITE_PATH, "shoot1.png"), 5, 17, 11) +local sprite_shoot1_half = Resources.sprite_load(NAMESPACE, "ChirrShoot1Half", path.combine(SPRITE_PATH, "shoot1Half.png"), 5, 17, 12) +local sprite_shoot2 = Resources.sprite_load(NAMESPACE, "ChirrShoot2", path.combine(SPRITE_PATH, "shoot2.png"), 7, 23, 11) +local sprite_shoot3 = Resources.sprite_load(NAMESPACE, "ChirrShoot3", path.combine(SPRITE_PATH, "shoot3.png"), 13, 19, 27) +local sprite_shoot4 = Resources.sprite_load(NAMESPACE, "ChirrShoot4", path.combine(SPRITE_PATH, "shoot4.png"), 10, 3, 4) + +-- effect sprites, entirely used for the primary because i was having fun and bc the rest of her kit didnt really use any +local sprite_sparks = Resources.sprite_load(NAMESPACE, "ChirrSparks", path.combine(SPRITE_PATH, "sparks.png"), 3, 11, 3) +local sprite_tracer = Resources.sprite_load(NAMESPACE, "ChirrTracer", path.combine(SPRITE_PATH, "tracer.png"), 5, 0, 2) + +-- sprites for the elite type given to tamed monsters +local sprite_palette = Resources.sprite_load(NAMESPACE, "ChirrPalette", path.combine(SPRITE_PATH, "palette.png")) +local sprite_tamed_icon = Resources.sprite_load(NAMESPACE, "ChirrTameIcon", path.combine(SPRITE_PATH, "tameIcon.png")) + +-- sounds +local sound_shoot1 = Resources.sfx_load(NAMESPACE, "ChirrShoot1", path.combine(SOUND_PATH, "skill1.ogg")) +local sound_shoot2 = Resources.sfx_load(NAMESPACE, "ChirrShoot2", path.combine(SOUND_PATH, "skill2.ogg")) +local sound_shoot3a = Resources.sfx_load(NAMESPACE, "ChirrShoot3A", path.combine(SOUND_PATH, "skill3A.ogg")) +local sound_shoot3b = Resources.sfx_load(NAMESPACE, "ChirrShoot3B", path.combine(SOUND_PATH, "skill3B.ogg")) +local sound_shoot4 = Resources.sfx_load(NAMESPACE, "ChirrShoot4", path.combine(SOUND_PATH, "skill4.ogg")) + + +-------- the creachirr +local chirr = Survivor.new(NAMESPACE, "chirr") +local chirr_id = chirr.value +local player_actor + +local tamed = {} +local has_full_party = 0 + +local tethered +local time_since_tether_override + +chirr:set_stats_base({ -- setting base stats + maxhp = 104, + damage = 11, + regen = 0.014 +}) +chirr:set_stats_level({ -- setting stats for leveling up + maxhp = 29, + damage = 3, + regen = 0.0024, + armor = 2 +}) + +local base_physics = { -- chirrs default different physics + gravity1 = 0.4, + gravity2 = 0.16 +} +chirr:set_physics_base(base_physics) + +chirr:set_animations({ -- setting the animations, a lot of this is modeled after executioner + idle = sprite_idle, + walk = sprite_walk, + jump = sprite_jump, + jump_peak = sprite_jump, + fall = sprite_fall, + climb = sprite_climb, + death = sprite_death, + decoy = sprite_decoy, +}) + +chirr:set_cape_offset(0,0,0,0) +chirr:set_primary_color(Color.from_rgb(129,167,98)) + +-- setting some misc sprites +chirr.sprite_loadout = sprite_loadout +chirr.sprite_portrait = sprite_portrait +chirr.sprite_portrait_small = sprite_portrait_small +chirr.sprite_title = sprite_walk + +chirr:clear_callbacks() +chirr:onInit(function(actor) -- setting up the beasts half sprite stuff + -- shoutouts to kris for the awesome code i can entirely copy!! + local idle_half = Array.new() + local walk_half = Array.new() + local jump_half = Array.new() + idle_half:push(sprite_idle, sprite_idle_half, 0) + walk_half:push(sprite_walk, sprite_walk_half, 0, sprite_walk) + jump_half:push(sprite_jump, sprite_jump_half, 0) + + actor.sprite_idle_half = idle_half + actor.sprite_walk_half = walk_half + actor.sprite_jump_half = jump_half + actor.sprite_jump_peak_half = jump_half + actor.sprite_fall_half = jump_half + + actor:survivor_util_init_half_sprites() + +end) + +-- her wing sprites +local obj_wings = Object.new(NAMESPACE, "chirrWings") +obj_wings.obj_sprite = sprite_wings +obj_wings.obj_depth = 1 + +-- setting up the default skills +local chirrPrimary = chirr:get_primary() +local chirrSecondary = chirr:get_secondary() +local chirrUtility = chirr:get_utility() +local chirrSpecial = chirr:get_special() + +local chirrSpecialScepter = Skill.new(NAMESPACE, "chirrSpecialBoosted") +chirrSpecial:set_skill_upgrade(chirrSpecialScepter) + +local chirrTether = Skill.new(NAMESPACE, "chirrTether") + + +-- her movement control stuff +local flying = false -- hover stuff +local wings +chirr:onStep(function( actor ) + if actor.moveUpHold == 1.0 and actor.pVspeed > 0.35 then -- her hover + flying = true + actor.pVspeed = 0.35 + + if actor.actor_state_current_id == -1 then -- setting her sprite to the flight idle if nothing else is happening + actor.sprite_index = sprite_flight + end + + if not wings then -- displaying her wings + wings = obj_wings:create(actor.ghost_x, actor.ghost_y) + wingsData = wings:get_data() + wingsData.parent = actor + wings.image_xscale = gm.cos(gm.degtorad(actor:skill_util_facing_direction())) + else + wings.x = actor.x + wings.y = actor.y + wings.image_xscale = gm.cos(gm.degtorad(actor:skill_util_facing_direction())) + end + else + flying = false + + if wings then + wings:destroy() + wings = nil + end + end + + if player_actor ~= actor then -- updating the actor value for my own convenience + player_actor = actor + end + + if #tamed < 1 + player_actor:item_stack_count(Item.find("ror", "ancientScepter")) and has_full_party == 1 then -- if your party has newly been made empty + has_full_party = 0 + time_since_tether_override = os.clock() + GM._mod_ActorSkillSlot_removeOverride(player_actor:actor_get_skill_slot(Skill.SLOT.special), chirrTether , Skill.OVERRIDE_PRIORITY.cancel) -- sets the override skill to tether + + elseif #tamed >= 1 + player_actor:item_stack_count(Item.find("ror", "ancientScepter")) and has_full_party == 0 then -- if your party has newly been made full + has_full_party = 1 + GM._mod_ActorSkillSlot_addOverride(player_actor:actor_get_skill_slot(Skill.SLOT.special), chirrTether, Skill.OVERRIDE_PRIORITY.cancel) -- removes the tether override skill + time_since_tether_override = os.clock() + end +end) + + +-------- get this girl her thorns +-- this is all basic skill info +chirrPrimary.sprite = sprite_skills +chirrPrimary.subimage = 0 +chirrPrimary.cooldown = 5 +chirrPrimary.damage = 0.9 +chirrPrimary.require_key_press = false +chirrPrimary.is_primary = true +chirrPrimary.does_change_activity_state = true +chirrPrimary.hold_facing_direction = true +chirrPrimary.required_interrupt_priority = State.ACTOR_STATE_INTERRUPT_PRIORITY.any + +local tracer_particle = Particle.find("ror", "WispGTracer") +local tracer_color = Color.from_rgb(129,167,98) +local thorn_tracer, thorn_tracer_info = CustomTracer.new(function(x1, y1, x2, y2) -- i just kinda copied this tracer code and tweaked it for some fun visuals + if x1 < x2 then x1 = x1 + 16 else x1 = x1 - 16 end + + y1 = y1 - 5 + y2 = y2 - 5 + + local tracer = gm.instance_create(x1, y1, gm.constants.oEfLineTracer) + + tracer.xend = x2 + tracer.yend = y2 + tracer.sprite_index = sprite_tracer + tracer.image_speed = 1 + tracer.rate = 1 + tracer.blend_1 = Color.from_rgb(129,167,98) + tracer.blend_2 = tracer_color + tracer.blend_rate = 0.2 + tracer.image_alpha = 1.5 + tracer.bm = 1 + tracer.width = 1 + + local dist = gm.point_distance(x1, y1, x2, y2) + local dir = gm.point_direction(x1, y1, x2, y2) + + tracer_particle:set_direction(dir, dir, 0, 0) + + local px = x1 + local i = 0 + while i < dist do + tracer_particle:create_colour(px, y1 + gm.random_range(-8, 8), tracer_color, 1) + px = px + gm.lengthdir_x(20, dir) + i = i + 20 + end +end) +thorn_tracer_info.sparks_offset_y = 0 + +local stateChirrPrimary = State.new(NAMESPACE, "chirrPrimary") -- making a primary state + +chirrPrimary:clear_callbacks() +chirrPrimary:onActivate(function( actor ) + actor:enter_state(stateChirrPrimary) +end) + +stateChirrPrimary:clear_callbacks() -- using the skill, setting up the important data and stuff +stateChirrPrimary:onEnter(function( actor, data ) + actor.image_index2 = 0 + data.fired = 0 + data.count = 3 + + actor:skill_util_strafe_init() + actor:skill_util_strafe_turn_init() +end) + +stateChirrPrimary:onStep(function( actor, data ) -- actually using the skill + actor.sprite_index2 = sprite_shoot1_half + + actor:skill_util_strafe_update(.12 * actor.attack_speed, 0.5) + actor:skill_util_step_strafe_sprites() + actor:skill_util_strafe_turn_update() + if data.fired == 0 and data.count > 0 and actor.image_index2 > 1 then -- this if else is to make sure the skill fires at the right time and also does a proper barrage of 3 thorns + data.fired = 1 + data.count = data.count - 1 + + actor:sound_play(sound_shoot1, .5, 0.9 + math.random() * 0.4) + + if actor:is_authority() then -- if local player + local damage = actor:skill_get_damage(chirrPrimary) + local dir = actor:skill_util_facing_direction() + + if not GM.skill_util_update_heaven_cracker(actor, damage, actor.image_xscale) then -- if not using heaven cracker + local buff_shadow_clone = Buff.find("ror", "shadowClone") + for i=0, actor:buff_stack_count(buff_shadow_clone) do -- stuff for shattered mirror + local attack = actor:fire_bullet(actor.x, actor.y, 400, dir, damage, nil, sprite_sparks, thorn_tracer, true) + attack.climb = i * 8 + end + end + end + else + data.fired = 0 + end + + if actor.image_index2 >= gm.sprite_get_number(actor.sprite_index2) then + actor:skill_util_reset_activity_state() + end +end) + + +-------- headbutt +-- just skill info +chirrSecondary.sprite = sprite_skills +chirrSecondary.subimage = 1 +chirrSecondary.cooldown = 4*60 +chirrSecondary.damage = 3.0 +chirrSecondary.require_key_press = true +chirrSecondary.required_interrupt_priority = State.ACTOR_STATE_INTERRUPT_PRIORITY.skill + +local stateChirrSecondary = State.new(NAMESPACE, "chirrSecondary") -- setting up states again + +chirrSecondary:clear_callbacks() +chirrSecondary:onActivate(function( actor ) + actor:enter_state(stateChirrSecondary) +end) + +stateChirrSecondary:clear_callbacks() +stateChirrSecondary:onEnter(function( actor, data ) -- skill setup again + actor.image_index = 0 + data.fired = 0 +end) + +stateChirrSecondary:onStep(function( actor, data ) -- using the skill + actor.sprite_index = sprite_shoot2 + actor.image_speed = 0.25 + + actor:skill_util_fix_hspeed() + + if data.fired == 0 then + data.fired = 1 + actor:sound_play(sound_shoot2, .8, 0.9 + math.random() * 0.2) + + if actor:is_authority() then + local damage = actor:skill_get_damage(chirrSecondary) + local dir = gm.cos(gm.degtorad(actor:skill_util_facing_direction())) -- explosions dont use directions, this is just used to make the x offset + + if not GM.skill_util_update_heaven_cracker(actor, damage, actor.image_xscale) then -- if not using heaven cracker + local buff_shadow_clone = Buff.find("ror", "shadowClone") + for i=0, actor:buff_stack_count(buff_shadow_clone) do -- stuff for shattered mirror + attack = actor:fire_explosion(actor.x + dir * 40, actor.y, 100, 50, damage, nil, nil, true) + attack.attack_info:allow_stun() + attack.attack_info:set_stun(3, dir, standard) + end + end + end + end + + actor:skill_util_exit_state_on_anim_end() +end) + + +-------- heal pulse +-- more info yayyyyyyy +chirrUtility.sprite = sprite_skills +chirrUtility.subimage = 2 +chirrUtility.cooldown = 15 * 60 +chirrUtility.is_utility = true +chirrUtility.require_key_press = true +chirrUtility.required_interrupt_priority = State.ACTOR_STATE_INTERRUPT_PRIORITY.skill + +local stateChirrUtility = State.new(NAMESPACE, "chirrUtility") -- guess what this is, you get three tries + +chirrUtility:clear_callbacks() +chirrUtility:onActivate(function(actor) + actor:enter_state(stateChirrUtility) +end) + +stateChirrUtility:clear_callbacks() +stateChirrUtility:onEnter(function(actor, data) + actor.image_index = 0 + actor:sound_play(sound_shoot3a, .8, 1.4) +end) + +local utilRegenBuff = Buff.new(NAMESPACE, "chirrRegenBuff") +utilRegenBuff.show_icon = false + +utilRegenBuff:clear_callbacks() +utilRegenBuff:onStatRecalc(function( actor ) + actor.hp_regen = actor.hp_regen + actor.maxhp * .002 +end) + +stateChirrUtility:onStep(function( actor, data ) + actor.sprite_index = sprite_shoot3 + actor.image_speed = .25 + + if actor.image_index == 12 then -- when the animation reaches the final frame + actor:sound_play(sound_shoot3b, 1.2, 0.9 + math.random() * 0.2) + + local healArea = GM.instance_create(actor.x, actor.y, gm.constants.oEfCircle) -- cool circle object ty kris + healArea.radius = 240 + healArea.rate = 60 + healArea.image_blend = Color.from_rgb(179,217,148) + + if actor:is_authority() then + local buff_shadow_clone = Buff.find("ror", "shadowClone") + for i = 0, actor:buff_stack_count(buff_shadow_clone) do + local friends = List.new() -- list to hold our friends + + actor:collision_circle_list(actor.x, actor.y, 320, gm.constants.pActor, false, false, friends, false) -- this grabs all the actors in a radius + + for _, friend in ipairs(friends) do + if friend.team == actor.team then -- checking to see who is actually our friend + friend:heal(friend.maxhp * .25) + friend:buff_apply(utilRegenBuff, 6 * 60) + end + end + + friends:destroy() + end + end + end + + actor:skill_util_exit_state_on_anim_end() +end) + + +-------- taming +-- getting a tame +chirrSpecial.sprite = sprite_skills +chirrSpecial.subimage = 3 +chirrSpecial.cooldown = 4 * 60 +chirrSpecial.require_key_press = true +chirrSpecial.required_interrupt_priority = State.ACTOR_STATE_INTERRUPT_PRIORITY.skill + +-- getting a SUUUUUPPPERRR tame +chirrSpecialScepter.sprite = sprite_skills +chirrSpecialScepter.subimage = 5 +chirrSpecialScepter.cooldown = 4 * 60 +chirrSpecialScepter.required_interrupt_priority = State.ACTOR_STATE_INTERRUPT_PRIORITY.skill + +-- girl and her tether +chirrTether.sprite = sprite_skills +chirrTether.subimage = 4 +chirrTether.cooldown = 4 * 60 +chirrTether.require_key_press = false +chirrTether.required_interrupt_priority = State.ACTOR_STATE_INTERRUPT_PRIORITY.skill + +local stateChirrSpecial = State.new(NAMESPACE, "chirrSpecial") +local stateChirrTether = State.new(NAMESPACE, "chirrTether") + +chirrSpecial:clear_callbacks() +chirrSpecial:onActivate(function( actor, data ) + actor:enter_state(stateChirrSpecial) +end) +chirrSpecialScepter:clear_callbacks() +chirrSpecialScepter:onActivate(function( actor, data ) + actor:enter_state(stateChirrSpecial) +end) + +local obj_tame_heart = Object.new(NAMESPACE, "chirrTameHeart") -- making the little heart object +obj_tame_heart.obj_sprite = sprite_shoot4 +obj_tame_heart.obj_depth = 1 + +local function tameHeartStep( inst ) -- heart animation and lifetime stuff + inst.image_speed = 0.25 + if inst.image_index == 9 then + inst:destroy() + end +end + +obj_tame_heart:onStep(tameHeartStep) + +local elite = Elite.new(NAMESPACE, "tamed") -- setup for the elite type +local elite_id = gm.elite_type_find("tamed") +elite:set_healthbar_icon(sprite_tamed_icon) +elite.palette = sprite_palette +gm._mod_elite_generate_palette_all() + +Callback.add(Callback.TYPE.onGameStart, "SSChirrResetTameTable", function( ) -- resets the friend list each game + tamed = {} +end) + +Callback.add(Callback.TYPE.onDeath, "SSChirrTameUpdate", function( victim, fell_out_of_bounds ) -- removes friends from the list once they die + for index, friend in ipairs(tamed) do + if friend.value == victim.value then + table.remove(tamed, index) + end + end + + if tethered then + if tethered.value == victim.value then + tethered = nil + end + end +end) + +Callback.add(Callback.TYPE.onPickupCollected, "SSChirrFriendItemSync", function( pickup, player ) -- copies your new items over to your friend + if pickup.equipment_id == -1 then + for _, friend in ipairs(tamed) do + friend:item_give(pickup.item_id, 1, pickup.item_stack_kind) + end + end +end) + +Callback.add(Callback.TYPE.onStageStart, "SSChirrFriendGroupup", function( ) -- brings your friends to you on each stage + for _, friend in ipairs(tamed) do + gm.teleport_nearby(friend.value, player_actor.x, player_actor.y) + end +end) + +Callback.add(Callback.TYPE.onDamagedProc, "SSChirrTetherSiphon", function( actor, hit_info ) -- soul siphon ACTIVATE!! + if actor.value == player_actor.value and tethered then + local damage_to_siphon = hit_info.damage + player_actor:heal(damage_to_siphon) + GM.damage_inflict(tethered, damage_to_siphon) + end +end) + +Callback.add(Callback.TYPE.onDraw, "SSChirrTetherDraw", function( ) + if tethered then + gm.draw_set_alpha(0.5) + + gm.draw_set_colour(Color.TEXT_GREEN) + gm.draw_line_width(player_actor.x, player_actor.y, tethered.x, tethered.y, 5 + math.random() * 2) + gm.draw_circle(tethered.x, tethered.y, 4 + math.random() * 8, false) + gm.draw_circle(player_actor.x, player_actor.y, 2 + math.random() * 4, false) + + gm.draw_set_colour(Color.WHITE) + gm.draw_line_width(player_actor.x, player_actor.y, tethered.x, tethered.y, 2) + gm.draw_circle(tethered.x, tethered.y, 8, true) + gm.draw_circle(player_actor.x, player_actor.y, 4, true) + + gm.draw_set_alpha(1) + end +end) + +stateChirrSpecial:clear_callbacks() +stateChirrSpecial:onEnter(function( actor, data ) + actor:sound_play(sound_shoot4, 1, 1) + + obj_tame_heart:create(actor.x + 14 * gm.cos(gm.degtorad(actor:skill_util_facing_direction())), actor.y - 10) -- make the little heart + + local potential_tames = List.new() + actor:collision_circle_list(actor.x, actor.y, 240, gm.constants.pActor, true, false, potential_tames, false) -- grab all nearby actors like the util + + for _, tame_option in ipairs(potential_tames) do + if tame_option.team ~= actor.team and not tame_option.enemy_party and tame_option.hp <= tame_option.maxhp * 0.5 then -- makes sure they arent on our team, arent a boss, and have half health or less + if #tamed < 1 + actor:item_stack_count(Item.find("ror", "ancientScepter")) then -- sets the limit based off our scepter count + actor:actor_team_set(tame_option, 1) -- setting the enemies team to our team (1 is player team, 2 is enemy) + tame_option.persistent = true -- this stops our friends from going away each stage + GM.elite_set(tame_option, elite) -- sets the elite type of your friend + table.insert(tamed, tame_option) -- adds them to our list of total friends + tame_option.hp = tame_option.maxhp -- heals them for their troubles + actor:inventory_items_copy(actor, tame_option, 0) -- gives our friend our items + tame_option.y_range = 100 + end + end + end + + potential_tames:destroy() + actor:skill_util_reset_activity_state() +end) + +chirrTether:clear_callbacks() +chirrTether:onActivate(function( actor, state ) + actor:enter_state(stateChirrTether) +end) + +stateChirrTether:clear_callbacks() +stateChirrTether:onEnter(function( actor, data ) + data.step = 0 + data.exit = 0 +end) + +local tether_tp_charge_time = .75*60 + +stateChirrTether:onStep(function( actor, data ) + if os.clock() - time_since_tether_override > .1 then + actor:skill_util_fix_hspeed() + + data.step = data.step + 1 + + local holding = gm.bool(actor.v_skill) + if not holding or data.step >= tether_tp_charge_time then + actor:skill_util_reset_activity_state() + end + else + data.exit = 1 + actor:skill_util_reset_activity_state() + end +end) + +stateChirrTether:onExit(function( actor, data ) + if data.exit == 0 then + if data.step >= tether_tp_charge_time then + for _, friend in ipairs(tamed) do + gm.teleport_nearby(friend.value, player_actor.x, player_actor.y) + end + else + if not tethered then + local nearby_friends = {} + local nearby_allies = List.new() + actor:collision_circle_list(actor.x, actor.y, 240, gm.constants.pActor, true, false, nearby_allies, false) + + for _,ally in ipairs(nearby_allies) do + for _,friend in ipairs(tamed) do + if ally.value == friend.value then + table.insert(nearby_friends, ally) + end + end + end + + local shortest_distance + local closest_friend + if #nearby_friends > 0 then + for _,friend in ipairs(nearby_friends) do + local current_distance = math.sqrt((friend.x - actor.x)^2+(friend.y - actor.y)^2) + + if not shortest_distance or shortest_distance > current_distance then + shortest_distance = current_distance + closest_friend = friend + print(shortest_distance) + end + end + end + + tethered = closest_friend + else + tethered = nil + end + end + end +end) \ No newline at end of file