├── .ctrlp ├── .gitignore ├── LICENSE ├── README.md ├── arena.lua ├── assets ├── fonts │ ├── FatPixelFont.ttf │ ├── PixulBrush-Mono.ttf │ ├── PixulBrush.ttf │ └── fonts_go_here.txt ├── images │ ├── amplify.png │ ├── annihilation.png │ ├── arrow.png │ ├── assassination.png │ ├── awakening.png │ ├── ballista.png │ ├── ballista_x.png │ ├── baneling_burst.png │ ├── berserking.png │ ├── blessing.png │ ├── blunt_arrow.png │ ├── burning_field.png │ ├── burning_strike.png │ ├── call_of_the_void.png │ ├── centipede.png │ ├── ceremonial_dagger.png │ ├── chronomancy.png │ ├── cluster_elite.png │ ├── conjurer.png │ ├── construct_instability.png │ ├── critical_strike.png │ ├── crucio.png │ ├── culling_strike.png │ ├── curser.png │ ├── damage_4.png │ ├── death_6.png │ ├── deceleration.png │ ├── defensive_stance.png │ ├── dividends.png │ ├── divine_barrage.png │ ├── divine_blessing.png │ ├── divine_machine_arrow.png │ ├── divine_punishment.png │ ├── echo_barrage.png │ ├── enchanted.png │ ├── enchanter.png │ ├── exploder_elite.png │ ├── explorer.png │ ├── explosive_arrow.png │ ├── flying_daggers.png │ ├── forcer.png │ ├── forcer_elite.png │ ├── fracture.png │ ├── freezing_field.png │ ├── gravity_field.png │ ├── hardening.png │ ├── haste.png │ ├── healer.png │ ├── healing_strike.png │ ├── heavy_impact.png │ ├── hextouch.png │ ├── hive.png │ ├── homing_barrage.png │ ├── icon.png │ ├── infesting_strike.png │ ├── insurance.png │ ├── intimidation.png │ ├── kinetic_bomb.png │ ├── kinetic_strike.png │ ├── last_stand.png │ ├── lasting_7.png │ ├── lightning_strike.png │ ├── lock.png │ ├── lucky_strike.png │ ├── mage.png │ ├── magnetism.png │ ├── magnify.png │ ├── malediction.png │ ├── meat_shield.png │ ├── mercenary.png │ ├── noxious_strike.png │ ├── nuker.png │ ├── offensive_stance.png │ ├── orbitism.png │ ├── ouroboros_technique_l.png │ ├── ouroboros_technique_r.png │ ├── payback.png │ ├── porcupine_technique.png │ ├── psycholeak.png │ ├── psychosense.png │ ├── psychoshot.png │ ├── psychosink.png │ ├── psyker.png │ ├── psyker_orbs.png │ ├── ranger.png │ ├── rearm.png │ ├── reinforce.png │ ├── resonance.png │ ├── rogue.png │ ├── seeping.png │ ├── shoot_5.png │ ├── silencing_strike.png │ ├── sorcerer.png │ ├── speed_3.png │ ├── speed_booster_elite.png │ ├── star.png │ ├── stunning_strike.png │ ├── swarmer.png │ ├── swarmer_elite.png │ ├── taunt.png │ ├── temporal_chains.png │ ├── tremor.png │ ├── ultimatum.png │ ├── unleash.png │ ├── unrelenting_stance.png │ ├── unwavering_stance.png │ ├── voider.png │ ├── vulnerability.png │ ├── warping_shots.png │ ├── warrior.png │ └── whispers_of_doom.png ├── maps │ └── maps_go_here.txt ├── media │ ├── achievement_ascension_1.png │ ├── achievement_ascension_5.png │ ├── achievement_cluster.png │ ├── achievement_conjurers_win.png │ ├── achievement_cursers_win.png │ ├── achievement_enchanters_win.png │ ├── achievement_exploder.png │ ├── achievement_forcer.png │ ├── achievement_forcers_win.png │ ├── achievement_forcers_win_gray.png │ ├── achievement_game_complete.png │ ├── achievement_healers_win.png │ ├── achievement_level_2_win.png │ ├── achievement_level_3_win.png │ ├── achievement_locked.png │ ├── achievement_mages_win.png │ ├── achievement_mercenaries_win.png │ ├── achievement_new_game_1.png │ ├── achievement_new_game_5.png │ ├── achievement_nukers_win.png │ ├── achievement_psykers_win.png │ ├── achievement_rangers_win.png │ ├── achievement_rogues_win.png │ ├── achievement_sorcerers_win.png │ ├── achievement_speed_booster.png │ ├── achievement_swarmer.png │ ├── achievement_swarmers_win.png │ ├── achievement_voiders_win.png │ ├── achievement_warriors_win.png │ ├── community_capsule.png │ ├── event_cover.png │ ├── event_cover_2.png │ ├── event_header.png │ ├── event_header_2.png │ ├── header_capsule.png │ ├── header_capsule_2.png │ ├── hero_capsule.png │ ├── hero_capsule_2.png │ ├── icon.ico │ ├── icon.png │ ├── item_cover.png │ ├── library_capsule.png │ ├── library_capsule_2.png │ ├── library_hero.png │ ├── library_hero_2.png │ ├── library_logo.png │ ├── loop_cover.png │ ├── main_capsule.png │ ├── main_capsule_2.png │ ├── mercenary_cover.png │ ├── orb_cover.png │ ├── screenshot1.png │ ├── screenshot10.png │ ├── screenshot11.png │ ├── screenshot11_text.png │ ├── screenshot12.png │ ├── screenshot12_text.png │ ├── screenshot13.png │ ├── screenshot13_text.png │ ├── screenshot14.png │ ├── screenshot15.png │ ├── screenshot16.png │ ├── screenshot16_text.png │ ├── screenshot17.png │ ├── screenshot2.png │ ├── screenshot4.png │ ├── screenshot5.png │ ├── screenshot7.png │ ├── small_capsule.png │ ├── small_capsule_2.png │ ├── sorcerer_cover.png │ ├── twitch_boxart.jpg │ ├── twitch_boxart.png │ └── youtube_thumbnail.png ├── shaders │ ├── combine.frag │ ├── default.vert │ ├── displacement.frag │ ├── full_combine.frag │ ├── replace.frag │ └── shadow.frag └── sounds │ ├── 258269__jcallison__mouth-pop.ogg │ ├── 321215__hybrid-v__sci-fi-weapons-deploy.ogg │ ├── 376532__womb-affliction__flute-trill.ogg │ ├── 399656__bajko__sfx-thunder-blast.ogg │ ├── 458586__inspectorj__ui-mechanical-notification-01-fx.ogg │ ├── 467951__benzix2__ui-button-click.ogg │ ├── 80921__justinbw__buttonchime02up.ogg │ ├── Alert sounds 3.ogg │ ├── Arrow Impact wood 1.ogg │ ├── Arrow Impact wood 3.ogg │ ├── Bloody punches 10.ogg │ ├── Bloody punches 7.ogg │ ├── Body Fall 18.ogg │ ├── Body Fall 2.ogg │ ├── Body Head (Headshot) 1.ogg │ ├── Bonus 2.ogg │ ├── Bonus.ogg │ ├── Buff 13.ogg │ ├── Buff 14.ogg │ ├── Buff 3.ogg │ ├── Buff 4.ogg │ ├── Buff 5.ogg │ ├── Buff 8.ogg │ ├── Cannon impact sounds (Hitting ship) 4.ogg │ ├── Cannon shots 1.ogg │ ├── Cannon shots 7.ogg │ ├── Click.ogg │ ├── Coins - Gears - Slot.ogg │ ├── Coins 7.ogg │ ├── Coins 8.ogg │ ├── Coins 9.ogg │ ├── Collect 2.ogg │ ├── Collect 4.ogg │ ├── Collect 5.ogg │ ├── Concrete 6.ogg │ ├── Concrete 7.ogg │ ├── Crickets Chirping 4.ogg │ ├── Critters eating 2.ogg │ ├── Dagger Stab (Flesh) 4.ogg │ ├── Damage 2.ogg │ ├── Damage 3.ogg │ ├── Damage 5.ogg │ ├── Debuff 15.ogg │ ├── Earth Bolt 1.ogg │ ├── Earth Bolt 14.ogg │ ├── Earth Bolt 20.ogg │ ├── Error 2.ogg │ ├── Explosion Fireworks_01.ogg │ ├── Explosion Flesh_01.ogg │ ├── Explosion Flesh_02.ogg │ ├── Explosion Grenade_04.ogg │ ├── Fire bolt 10.ogg │ ├── Fire bolt 3.ogg │ ├── Fire bolt 5.ogg │ ├── Frost Bolt 20.ogg │ ├── Glass item Breaks 2.ogg │ ├── Heavy sword woosh 1.ogg │ ├── Heavy sword woosh 19.ogg │ ├── Kick 16_1.ogg │ ├── Kick 16_2.ogg │ ├── Kubbi - Ember - 01 Pathfinder.ogg │ ├── Kubbi - Ember - 02 Ember.ogg │ ├── Kubbi - Ember - 03 Firelight.ogg │ ├── Kubbi - Ember - 04 Cascade.ogg │ ├── Kubbi - Ember - 05 Compass.ogg │ ├── Kubbi - Ember - 09 Formed by Glaciers.ogg │ ├── Magical Impact 12.ogg │ ├── Magical Impact 13.ogg │ ├── Magical Impact 18.ogg │ ├── Magical Impact 26.ogg │ ├── Magical Impact 27.ogg │ ├── Magical Swoosh 18.ogg │ ├── Male Jump 1.ogg │ ├── Male Jump 2.ogg │ ├── Male Jump 3.ogg │ ├── Mud footsteps 7.ogg │ ├── Mud footsteps 9.ogg │ ├── Pistol Shot_07.ogg │ ├── Pistol Shot_08.ogg │ ├── Player Takes Damage 17.ogg │ ├── Player Takes Damage 2.ogg │ ├── Pop sounds 10.ogg │ ├── Popping bloody Sac 1.ogg │ ├── Releasing Bow String 1.ogg │ ├── Revolver Shot_07.ogg │ ├── Revolver Shot_08.ogg │ ├── Sci Fi Machine Gun 7.ogg │ ├── Shadow Punch 1.ogg │ ├── Shadow Punch 2.ogg │ ├── Shield Impacts Sword 1.ogg │ ├── Shooting Projectile (Classic) 11.ogg │ ├── Sniper Shot_09.ogg │ ├── Spark 1.ogg │ ├── Spark 2.ogg │ ├── Spark 3.ogg │ ├── Spawn 10.ogg │ ├── Switch 3.ogg │ ├── Switch.ogg │ ├── Sword hits another sword 6.ogg │ ├── Sword impact (Flesh) 2.ogg │ ├── Throwing Knife (Thrown) 3.ogg │ ├── Throwing Knife (Thrown) 4.ogg │ ├── Trailer - Ember.ogg │ ├── Unlock 3.ogg │ ├── Weapon Swap 2.ogg │ ├── Whipping Horse 3.ogg │ ├── Wind Bolt 12.ogg │ ├── Wind Bolt 14.ogg │ ├── Wind Bolt 18.ogg │ ├── Wind Bolt 20.ogg │ ├── Wind Bolt 8.ogg │ ├── Wolf barks 5.ogg │ ├── Wood Heavy 5.ogg │ └── bamboo_hit_by_lord.ogg ├── build.sh ├── builds └── windows │ └── windows_build_goes_here.txt ├── buy_screen.lua ├── conf.lua ├── devlog.md ├── enemies.lua ├── engine ├── datastructures │ ├── graph.lua │ ├── grid.lua │ ├── string.lua │ └── table.lua ├── external │ ├── binser.lua │ ├── clipper.lua │ ├── init.lua │ ├── mlib.lua │ └── ripple.lua ├── game │ ├── flashes.lua │ ├── gameobject.lua │ ├── group.lua │ ├── hitfx.lua │ ├── input.lua │ ├── object.lua │ ├── parent.lua │ ├── physics.lua │ ├── springs.lua │ ├── state.lua │ ├── steering.lua │ └── trigger.lua ├── gamecontrollerdb.txt ├── graphics │ ├── animation.lua │ ├── camera.lua │ ├── canvas.lua │ ├── color.lua │ ├── font.lua │ ├── graphics.lua │ ├── image.lua │ ├── shader.lua │ ├── text.lua │ └── tileset.lua ├── init.lua ├── love │ ├── OpenAL32.dll │ ├── SDL2.dll │ ├── build_steam.bat │ ├── build_web.bat │ ├── build_windows.bat │ ├── changes.txt │ ├── game.ico │ ├── license.txt │ ├── love.dll │ ├── love.exe │ ├── love.ico │ ├── lovec.exe │ ├── lua51.dll │ ├── luasteam.dll │ ├── mpg123.dll │ ├── msvcp120.dll │ ├── msvcr120.dll │ ├── polyclipping.dll │ ├── readme.txt │ ├── steam_api64.dll │ ├── steam_appid.txt │ └── steam_rich_presence.txt ├── map │ ├── solid.lua │ └── tilemap.lua ├── math │ ├── chain.lua │ ├── circle.lua │ ├── line.lua │ ├── math.lua │ ├── polygon.lua │ ├── random.lua │ ├── rectangle.lua │ ├── spring.lua │ ├── triangle.lua │ └── vector.lua ├── sound.lua └── system.lua ├── main.lua ├── mainmenu.lua ├── media.lua ├── objects.lua ├── player.lua ├── run.sh ├── shared.lua └── todo /.ctrlp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/.ctrlp -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | steam/ 2 | builds/ 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 adn 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | # SNKRX 6 | 7 | [SNKRX](https://store.steampowered.com/app/915310/SNKRX/) is an arcade shooter roguelite where you control a snake of heroes that automatically attack nearby enemies. 8 | Combine different heroes to unlock class bonuses and create unique builds, and steer your unstoppable party as they ravage through endless waves of enemies. 9 | 10 | https://user-images.githubusercontent.com/409773/119258159-ea982b00-bb9e-11eb-8082-37e2c65591ea.mp4 11 | 12 | [**Check it out on Steam!**](https://store.steampowered.com/app/915310/SNKRX/) 13 | 14 | ### Running 15 | 16 | Download this repository, `cd` into it and then run `engine/love/love.exe --console .`. You need to have Steam up to run it successfully. 17 | 18 | ### LICENSE 19 | 20 | All assets have their specific licenses and they are linked to in the game's credits. All code is under the MIT license. 21 | -------------------------------------------------------------------------------- /assets/fonts/FatPixelFont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/fonts/FatPixelFont.ttf -------------------------------------------------------------------------------- /assets/fonts/PixulBrush-Mono.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/fonts/PixulBrush-Mono.ttf -------------------------------------------------------------------------------- /assets/fonts/PixulBrush.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/fonts/PixulBrush.ttf -------------------------------------------------------------------------------- /assets/fonts/fonts_go_here.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/fonts/fonts_go_here.txt -------------------------------------------------------------------------------- /assets/images/amplify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/amplify.png -------------------------------------------------------------------------------- /assets/images/annihilation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/annihilation.png -------------------------------------------------------------------------------- /assets/images/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/arrow.png -------------------------------------------------------------------------------- /assets/images/assassination.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/assassination.png -------------------------------------------------------------------------------- /assets/images/awakening.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/awakening.png -------------------------------------------------------------------------------- /assets/images/ballista.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/ballista.png -------------------------------------------------------------------------------- /assets/images/ballista_x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/ballista_x.png -------------------------------------------------------------------------------- /assets/images/baneling_burst.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/baneling_burst.png -------------------------------------------------------------------------------- /assets/images/berserking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/berserking.png -------------------------------------------------------------------------------- /assets/images/blessing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/blessing.png -------------------------------------------------------------------------------- /assets/images/blunt_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/blunt_arrow.png -------------------------------------------------------------------------------- /assets/images/burning_field.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/burning_field.png -------------------------------------------------------------------------------- /assets/images/burning_strike.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/burning_strike.png -------------------------------------------------------------------------------- /assets/images/call_of_the_void.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/call_of_the_void.png -------------------------------------------------------------------------------- /assets/images/centipede.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/centipede.png -------------------------------------------------------------------------------- /assets/images/ceremonial_dagger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/ceremonial_dagger.png -------------------------------------------------------------------------------- /assets/images/chronomancy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/chronomancy.png -------------------------------------------------------------------------------- /assets/images/cluster_elite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/cluster_elite.png -------------------------------------------------------------------------------- /assets/images/conjurer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/conjurer.png -------------------------------------------------------------------------------- /assets/images/construct_instability.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/construct_instability.png -------------------------------------------------------------------------------- /assets/images/critical_strike.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/critical_strike.png -------------------------------------------------------------------------------- /assets/images/crucio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/crucio.png -------------------------------------------------------------------------------- /assets/images/culling_strike.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/culling_strike.png -------------------------------------------------------------------------------- /assets/images/curser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/curser.png -------------------------------------------------------------------------------- /assets/images/damage_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/damage_4.png -------------------------------------------------------------------------------- /assets/images/death_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/death_6.png -------------------------------------------------------------------------------- /assets/images/deceleration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/deceleration.png -------------------------------------------------------------------------------- /assets/images/defensive_stance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/defensive_stance.png -------------------------------------------------------------------------------- /assets/images/dividends.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/dividends.png -------------------------------------------------------------------------------- /assets/images/divine_barrage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/divine_barrage.png -------------------------------------------------------------------------------- /assets/images/divine_blessing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/divine_blessing.png -------------------------------------------------------------------------------- /assets/images/divine_machine_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/divine_machine_arrow.png -------------------------------------------------------------------------------- /assets/images/divine_punishment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/divine_punishment.png -------------------------------------------------------------------------------- /assets/images/echo_barrage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/echo_barrage.png -------------------------------------------------------------------------------- /assets/images/enchanted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/enchanted.png -------------------------------------------------------------------------------- /assets/images/enchanter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/enchanter.png -------------------------------------------------------------------------------- /assets/images/exploder_elite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/exploder_elite.png -------------------------------------------------------------------------------- /assets/images/explorer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/explorer.png -------------------------------------------------------------------------------- /assets/images/explosive_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/explosive_arrow.png -------------------------------------------------------------------------------- /assets/images/flying_daggers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/flying_daggers.png -------------------------------------------------------------------------------- /assets/images/forcer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/forcer.png -------------------------------------------------------------------------------- /assets/images/forcer_elite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/forcer_elite.png -------------------------------------------------------------------------------- /assets/images/fracture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/fracture.png -------------------------------------------------------------------------------- /assets/images/freezing_field.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/freezing_field.png -------------------------------------------------------------------------------- /assets/images/gravity_field.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/gravity_field.png -------------------------------------------------------------------------------- /assets/images/hardening.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/hardening.png -------------------------------------------------------------------------------- /assets/images/haste.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/haste.png -------------------------------------------------------------------------------- /assets/images/healer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/healer.png -------------------------------------------------------------------------------- /assets/images/healing_strike.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/healing_strike.png -------------------------------------------------------------------------------- /assets/images/heavy_impact.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/heavy_impact.png -------------------------------------------------------------------------------- /assets/images/hextouch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/hextouch.png -------------------------------------------------------------------------------- /assets/images/hive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/hive.png -------------------------------------------------------------------------------- /assets/images/homing_barrage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/homing_barrage.png -------------------------------------------------------------------------------- /assets/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/icon.png -------------------------------------------------------------------------------- /assets/images/infesting_strike.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/infesting_strike.png -------------------------------------------------------------------------------- /assets/images/insurance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/insurance.png -------------------------------------------------------------------------------- /assets/images/intimidation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/intimidation.png -------------------------------------------------------------------------------- /assets/images/kinetic_bomb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/kinetic_bomb.png -------------------------------------------------------------------------------- /assets/images/kinetic_strike.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/kinetic_strike.png -------------------------------------------------------------------------------- /assets/images/last_stand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/last_stand.png -------------------------------------------------------------------------------- /assets/images/lasting_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/lasting_7.png -------------------------------------------------------------------------------- /assets/images/lightning_strike.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/lightning_strike.png -------------------------------------------------------------------------------- /assets/images/lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/lock.png -------------------------------------------------------------------------------- /assets/images/lucky_strike.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/lucky_strike.png -------------------------------------------------------------------------------- /assets/images/mage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/mage.png -------------------------------------------------------------------------------- /assets/images/magnetism.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/magnetism.png -------------------------------------------------------------------------------- /assets/images/magnify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/magnify.png -------------------------------------------------------------------------------- /assets/images/malediction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/malediction.png -------------------------------------------------------------------------------- /assets/images/meat_shield.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/meat_shield.png -------------------------------------------------------------------------------- /assets/images/mercenary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/mercenary.png -------------------------------------------------------------------------------- /assets/images/noxious_strike.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/noxious_strike.png -------------------------------------------------------------------------------- /assets/images/nuker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/nuker.png -------------------------------------------------------------------------------- /assets/images/offensive_stance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/offensive_stance.png -------------------------------------------------------------------------------- /assets/images/orbitism.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/orbitism.png -------------------------------------------------------------------------------- /assets/images/ouroboros_technique_l.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/ouroboros_technique_l.png -------------------------------------------------------------------------------- /assets/images/ouroboros_technique_r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/ouroboros_technique_r.png -------------------------------------------------------------------------------- /assets/images/payback.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/payback.png -------------------------------------------------------------------------------- /assets/images/porcupine_technique.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/porcupine_technique.png -------------------------------------------------------------------------------- /assets/images/psycholeak.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/psycholeak.png -------------------------------------------------------------------------------- /assets/images/psychosense.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/psychosense.png -------------------------------------------------------------------------------- /assets/images/psychoshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/psychoshot.png -------------------------------------------------------------------------------- /assets/images/psychosink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/psychosink.png -------------------------------------------------------------------------------- /assets/images/psyker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/psyker.png -------------------------------------------------------------------------------- /assets/images/psyker_orbs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/psyker_orbs.png -------------------------------------------------------------------------------- /assets/images/ranger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/ranger.png -------------------------------------------------------------------------------- /assets/images/rearm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/rearm.png -------------------------------------------------------------------------------- /assets/images/reinforce.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/reinforce.png -------------------------------------------------------------------------------- /assets/images/resonance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/resonance.png -------------------------------------------------------------------------------- /assets/images/rogue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/rogue.png -------------------------------------------------------------------------------- /assets/images/seeping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/seeping.png -------------------------------------------------------------------------------- /assets/images/shoot_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/shoot_5.png -------------------------------------------------------------------------------- /assets/images/silencing_strike.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/silencing_strike.png -------------------------------------------------------------------------------- /assets/images/sorcerer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/sorcerer.png -------------------------------------------------------------------------------- /assets/images/speed_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/speed_3.png -------------------------------------------------------------------------------- /assets/images/speed_booster_elite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/speed_booster_elite.png -------------------------------------------------------------------------------- /assets/images/star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/star.png -------------------------------------------------------------------------------- /assets/images/stunning_strike.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/stunning_strike.png -------------------------------------------------------------------------------- /assets/images/swarmer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/swarmer.png -------------------------------------------------------------------------------- /assets/images/swarmer_elite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/swarmer_elite.png -------------------------------------------------------------------------------- /assets/images/taunt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/taunt.png -------------------------------------------------------------------------------- /assets/images/temporal_chains.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/temporal_chains.png -------------------------------------------------------------------------------- /assets/images/tremor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/tremor.png -------------------------------------------------------------------------------- /assets/images/ultimatum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/ultimatum.png -------------------------------------------------------------------------------- /assets/images/unleash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/unleash.png -------------------------------------------------------------------------------- /assets/images/unrelenting_stance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/unrelenting_stance.png -------------------------------------------------------------------------------- /assets/images/unwavering_stance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/unwavering_stance.png -------------------------------------------------------------------------------- /assets/images/voider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/voider.png -------------------------------------------------------------------------------- /assets/images/vulnerability.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/vulnerability.png -------------------------------------------------------------------------------- /assets/images/warping_shots.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/warping_shots.png -------------------------------------------------------------------------------- /assets/images/warrior.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/warrior.png -------------------------------------------------------------------------------- /assets/images/whispers_of_doom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/images/whispers_of_doom.png -------------------------------------------------------------------------------- /assets/maps/maps_go_here.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/maps/maps_go_here.txt -------------------------------------------------------------------------------- /assets/media/achievement_ascension_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/achievement_ascension_1.png -------------------------------------------------------------------------------- /assets/media/achievement_ascension_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/achievement_ascension_5.png -------------------------------------------------------------------------------- /assets/media/achievement_cluster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/achievement_cluster.png -------------------------------------------------------------------------------- /assets/media/achievement_conjurers_win.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/achievement_conjurers_win.png -------------------------------------------------------------------------------- /assets/media/achievement_cursers_win.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/achievement_cursers_win.png -------------------------------------------------------------------------------- /assets/media/achievement_enchanters_win.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/achievement_enchanters_win.png -------------------------------------------------------------------------------- /assets/media/achievement_exploder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/achievement_exploder.png -------------------------------------------------------------------------------- /assets/media/achievement_forcer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/achievement_forcer.png -------------------------------------------------------------------------------- /assets/media/achievement_forcers_win.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/achievement_forcers_win.png -------------------------------------------------------------------------------- /assets/media/achievement_forcers_win_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/achievement_forcers_win_gray.png -------------------------------------------------------------------------------- /assets/media/achievement_game_complete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/achievement_game_complete.png -------------------------------------------------------------------------------- /assets/media/achievement_healers_win.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/achievement_healers_win.png -------------------------------------------------------------------------------- /assets/media/achievement_level_2_win.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/achievement_level_2_win.png -------------------------------------------------------------------------------- /assets/media/achievement_level_3_win.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/achievement_level_3_win.png -------------------------------------------------------------------------------- /assets/media/achievement_locked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/achievement_locked.png -------------------------------------------------------------------------------- /assets/media/achievement_mages_win.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/achievement_mages_win.png -------------------------------------------------------------------------------- /assets/media/achievement_mercenaries_win.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/achievement_mercenaries_win.png -------------------------------------------------------------------------------- /assets/media/achievement_new_game_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/achievement_new_game_1.png -------------------------------------------------------------------------------- /assets/media/achievement_new_game_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/achievement_new_game_5.png -------------------------------------------------------------------------------- /assets/media/achievement_nukers_win.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/achievement_nukers_win.png -------------------------------------------------------------------------------- /assets/media/achievement_psykers_win.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/achievement_psykers_win.png -------------------------------------------------------------------------------- /assets/media/achievement_rangers_win.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/achievement_rangers_win.png -------------------------------------------------------------------------------- /assets/media/achievement_rogues_win.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/achievement_rogues_win.png -------------------------------------------------------------------------------- /assets/media/achievement_sorcerers_win.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/achievement_sorcerers_win.png -------------------------------------------------------------------------------- /assets/media/achievement_speed_booster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/achievement_speed_booster.png -------------------------------------------------------------------------------- /assets/media/achievement_swarmer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/achievement_swarmer.png -------------------------------------------------------------------------------- /assets/media/achievement_swarmers_win.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/achievement_swarmers_win.png -------------------------------------------------------------------------------- /assets/media/achievement_voiders_win.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/achievement_voiders_win.png -------------------------------------------------------------------------------- /assets/media/achievement_warriors_win.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/achievement_warriors_win.png -------------------------------------------------------------------------------- /assets/media/community_capsule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/community_capsule.png -------------------------------------------------------------------------------- /assets/media/event_cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/event_cover.png -------------------------------------------------------------------------------- /assets/media/event_cover_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/event_cover_2.png -------------------------------------------------------------------------------- /assets/media/event_header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/event_header.png -------------------------------------------------------------------------------- /assets/media/event_header_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/event_header_2.png -------------------------------------------------------------------------------- /assets/media/header_capsule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/header_capsule.png -------------------------------------------------------------------------------- /assets/media/header_capsule_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/header_capsule_2.png -------------------------------------------------------------------------------- /assets/media/hero_capsule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/hero_capsule.png -------------------------------------------------------------------------------- /assets/media/hero_capsule_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/hero_capsule_2.png -------------------------------------------------------------------------------- /assets/media/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/icon.ico -------------------------------------------------------------------------------- /assets/media/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/icon.png -------------------------------------------------------------------------------- /assets/media/item_cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/item_cover.png -------------------------------------------------------------------------------- /assets/media/library_capsule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/library_capsule.png -------------------------------------------------------------------------------- /assets/media/library_capsule_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/library_capsule_2.png -------------------------------------------------------------------------------- /assets/media/library_hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/library_hero.png -------------------------------------------------------------------------------- /assets/media/library_hero_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/library_hero_2.png -------------------------------------------------------------------------------- /assets/media/library_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/library_logo.png -------------------------------------------------------------------------------- /assets/media/loop_cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/loop_cover.png -------------------------------------------------------------------------------- /assets/media/main_capsule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/main_capsule.png -------------------------------------------------------------------------------- /assets/media/main_capsule_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/main_capsule_2.png -------------------------------------------------------------------------------- /assets/media/mercenary_cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/mercenary_cover.png -------------------------------------------------------------------------------- /assets/media/orb_cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/orb_cover.png -------------------------------------------------------------------------------- /assets/media/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/screenshot1.png -------------------------------------------------------------------------------- /assets/media/screenshot10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/screenshot10.png -------------------------------------------------------------------------------- /assets/media/screenshot11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/screenshot11.png -------------------------------------------------------------------------------- /assets/media/screenshot11_text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/screenshot11_text.png -------------------------------------------------------------------------------- /assets/media/screenshot12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/screenshot12.png -------------------------------------------------------------------------------- /assets/media/screenshot12_text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/screenshot12_text.png -------------------------------------------------------------------------------- /assets/media/screenshot13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/screenshot13.png -------------------------------------------------------------------------------- /assets/media/screenshot13_text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/screenshot13_text.png -------------------------------------------------------------------------------- /assets/media/screenshot14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/screenshot14.png -------------------------------------------------------------------------------- /assets/media/screenshot15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/screenshot15.png -------------------------------------------------------------------------------- /assets/media/screenshot16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/screenshot16.png -------------------------------------------------------------------------------- /assets/media/screenshot16_text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/screenshot16_text.png -------------------------------------------------------------------------------- /assets/media/screenshot17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/screenshot17.png -------------------------------------------------------------------------------- /assets/media/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/screenshot2.png -------------------------------------------------------------------------------- /assets/media/screenshot4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/screenshot4.png -------------------------------------------------------------------------------- /assets/media/screenshot5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/screenshot5.png -------------------------------------------------------------------------------- /assets/media/screenshot7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/screenshot7.png -------------------------------------------------------------------------------- /assets/media/small_capsule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/small_capsule.png -------------------------------------------------------------------------------- /assets/media/small_capsule_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/small_capsule_2.png -------------------------------------------------------------------------------- /assets/media/sorcerer_cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/sorcerer_cover.png -------------------------------------------------------------------------------- /assets/media/twitch_boxart.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/twitch_boxart.jpg -------------------------------------------------------------------------------- /assets/media/twitch_boxart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/twitch_boxart.png -------------------------------------------------------------------------------- /assets/media/youtube_thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/media/youtube_thumbnail.png -------------------------------------------------------------------------------- /assets/shaders/combine.frag: -------------------------------------------------------------------------------- 1 | vec4 effect(vec4 vcolor, Image texture, vec2 tc, vec2 pc) { 2 | vec4 tex_color = Texel(texture, tc); 3 | return vec4(vcolor.rgb*tex_color.rgb, tex_color.a); 4 | } 5 | -------------------------------------------------------------------------------- /assets/shaders/default.vert: -------------------------------------------------------------------------------- 1 | vec4 position(mat4 transform_projection, vec4 vertex_position) { 2 | return transform_projection * vertex_position; 3 | } 4 | -------------------------------------------------------------------------------- /assets/shaders/displacement.frag: -------------------------------------------------------------------------------- 1 | extern Image displacement_map; 2 | vec4 effect(vec4 color, Image texture, vec2 tc, vec2 pc) { 3 | vec4 dp = Texel(displacement_map, tc); 4 | vec2 p = tc; 5 | p.x += (dp.r*2.0 - 1.0)*0.025*dp.a; 6 | p.y += (dp.g*2.0 - 1.0)*0.025*dp.a; 7 | return color*Texel(texture, p); 8 | } 9 | -------------------------------------------------------------------------------- /assets/shaders/full_combine.frag: -------------------------------------------------------------------------------- 1 | vec4 effect(vec4 vcolor, Image texture, vec2 tc, vec2 pc) { 2 | vec4 tex_color = Texel(texture, tc); 3 | return vec4(vcolor.rgb + tex_color.rgb, tex_color.a); 4 | } 5 | -------------------------------------------------------------------------------- /assets/shaders/replace.frag: -------------------------------------------------------------------------------- 1 | vec4 effect(vec4 vcolor, Image texture, vec2 tc, vec2 pc) { 2 | vec4 tex_color = Texel(texture, tc); 3 | return vec4(vcolor.rgb, tex_color.a); 4 | } 5 | -------------------------------------------------------------------------------- /assets/shaders/shadow.frag: -------------------------------------------------------------------------------- 1 | vec4 effect(vec4 vcolor, Image texture, vec2 tc, vec2 pc) { 2 | return vec4(0.1, 0.1, 0.1, Texel(texture, tc).a*0.5); 3 | } 4 | -------------------------------------------------------------------------------- /assets/sounds/258269__jcallison__mouth-pop.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/258269__jcallison__mouth-pop.ogg -------------------------------------------------------------------------------- /assets/sounds/321215__hybrid-v__sci-fi-weapons-deploy.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/321215__hybrid-v__sci-fi-weapons-deploy.ogg -------------------------------------------------------------------------------- /assets/sounds/376532__womb-affliction__flute-trill.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/376532__womb-affliction__flute-trill.ogg -------------------------------------------------------------------------------- /assets/sounds/399656__bajko__sfx-thunder-blast.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/399656__bajko__sfx-thunder-blast.ogg -------------------------------------------------------------------------------- /assets/sounds/458586__inspectorj__ui-mechanical-notification-01-fx.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/458586__inspectorj__ui-mechanical-notification-01-fx.ogg -------------------------------------------------------------------------------- /assets/sounds/467951__benzix2__ui-button-click.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/467951__benzix2__ui-button-click.ogg -------------------------------------------------------------------------------- /assets/sounds/80921__justinbw__buttonchime02up.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/80921__justinbw__buttonchime02up.ogg -------------------------------------------------------------------------------- /assets/sounds/Alert sounds 3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Alert sounds 3.ogg -------------------------------------------------------------------------------- /assets/sounds/Arrow Impact wood 1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Arrow Impact wood 1.ogg -------------------------------------------------------------------------------- /assets/sounds/Arrow Impact wood 3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Arrow Impact wood 3.ogg -------------------------------------------------------------------------------- /assets/sounds/Bloody punches 10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Bloody punches 10.ogg -------------------------------------------------------------------------------- /assets/sounds/Bloody punches 7.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Bloody punches 7.ogg -------------------------------------------------------------------------------- /assets/sounds/Body Fall 18.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Body Fall 18.ogg -------------------------------------------------------------------------------- /assets/sounds/Body Fall 2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Body Fall 2.ogg -------------------------------------------------------------------------------- /assets/sounds/Body Head (Headshot) 1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Body Head (Headshot) 1.ogg -------------------------------------------------------------------------------- /assets/sounds/Bonus 2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Bonus 2.ogg -------------------------------------------------------------------------------- /assets/sounds/Bonus.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Bonus.ogg -------------------------------------------------------------------------------- /assets/sounds/Buff 13.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Buff 13.ogg -------------------------------------------------------------------------------- /assets/sounds/Buff 14.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Buff 14.ogg -------------------------------------------------------------------------------- /assets/sounds/Buff 3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Buff 3.ogg -------------------------------------------------------------------------------- /assets/sounds/Buff 4.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Buff 4.ogg -------------------------------------------------------------------------------- /assets/sounds/Buff 5.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Buff 5.ogg -------------------------------------------------------------------------------- /assets/sounds/Buff 8.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Buff 8.ogg -------------------------------------------------------------------------------- /assets/sounds/Cannon impact sounds (Hitting ship) 4.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Cannon impact sounds (Hitting ship) 4.ogg -------------------------------------------------------------------------------- /assets/sounds/Cannon shots 1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Cannon shots 1.ogg -------------------------------------------------------------------------------- /assets/sounds/Cannon shots 7.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Cannon shots 7.ogg -------------------------------------------------------------------------------- /assets/sounds/Click.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Click.ogg -------------------------------------------------------------------------------- /assets/sounds/Coins - Gears - Slot.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Coins - Gears - Slot.ogg -------------------------------------------------------------------------------- /assets/sounds/Coins 7.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Coins 7.ogg -------------------------------------------------------------------------------- /assets/sounds/Coins 8.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Coins 8.ogg -------------------------------------------------------------------------------- /assets/sounds/Coins 9.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Coins 9.ogg -------------------------------------------------------------------------------- /assets/sounds/Collect 2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Collect 2.ogg -------------------------------------------------------------------------------- /assets/sounds/Collect 4.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Collect 4.ogg -------------------------------------------------------------------------------- /assets/sounds/Collect 5.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Collect 5.ogg -------------------------------------------------------------------------------- /assets/sounds/Concrete 6.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Concrete 6.ogg -------------------------------------------------------------------------------- /assets/sounds/Concrete 7.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Concrete 7.ogg -------------------------------------------------------------------------------- /assets/sounds/Crickets Chirping 4.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Crickets Chirping 4.ogg -------------------------------------------------------------------------------- /assets/sounds/Critters eating 2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Critters eating 2.ogg -------------------------------------------------------------------------------- /assets/sounds/Dagger Stab (Flesh) 4.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Dagger Stab (Flesh) 4.ogg -------------------------------------------------------------------------------- /assets/sounds/Damage 2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Damage 2.ogg -------------------------------------------------------------------------------- /assets/sounds/Damage 3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Damage 3.ogg -------------------------------------------------------------------------------- /assets/sounds/Damage 5.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Damage 5.ogg -------------------------------------------------------------------------------- /assets/sounds/Debuff 15.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Debuff 15.ogg -------------------------------------------------------------------------------- /assets/sounds/Earth Bolt 1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Earth Bolt 1.ogg -------------------------------------------------------------------------------- /assets/sounds/Earth Bolt 14.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Earth Bolt 14.ogg -------------------------------------------------------------------------------- /assets/sounds/Earth Bolt 20.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Earth Bolt 20.ogg -------------------------------------------------------------------------------- /assets/sounds/Error 2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Error 2.ogg -------------------------------------------------------------------------------- /assets/sounds/Explosion Fireworks_01.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Explosion Fireworks_01.ogg -------------------------------------------------------------------------------- /assets/sounds/Explosion Flesh_01.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Explosion Flesh_01.ogg -------------------------------------------------------------------------------- /assets/sounds/Explosion Flesh_02.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Explosion Flesh_02.ogg -------------------------------------------------------------------------------- /assets/sounds/Explosion Grenade_04.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Explosion Grenade_04.ogg -------------------------------------------------------------------------------- /assets/sounds/Fire bolt 10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Fire bolt 10.ogg -------------------------------------------------------------------------------- /assets/sounds/Fire bolt 3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Fire bolt 3.ogg -------------------------------------------------------------------------------- /assets/sounds/Fire bolt 5.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Fire bolt 5.ogg -------------------------------------------------------------------------------- /assets/sounds/Frost Bolt 20.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Frost Bolt 20.ogg -------------------------------------------------------------------------------- /assets/sounds/Glass item Breaks 2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Glass item Breaks 2.ogg -------------------------------------------------------------------------------- /assets/sounds/Heavy sword woosh 1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Heavy sword woosh 1.ogg -------------------------------------------------------------------------------- /assets/sounds/Heavy sword woosh 19.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Heavy sword woosh 19.ogg -------------------------------------------------------------------------------- /assets/sounds/Kick 16_1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Kick 16_1.ogg -------------------------------------------------------------------------------- /assets/sounds/Kick 16_2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Kick 16_2.ogg -------------------------------------------------------------------------------- /assets/sounds/Kubbi - Ember - 01 Pathfinder.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Kubbi - Ember - 01 Pathfinder.ogg -------------------------------------------------------------------------------- /assets/sounds/Kubbi - Ember - 02 Ember.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Kubbi - Ember - 02 Ember.ogg -------------------------------------------------------------------------------- /assets/sounds/Kubbi - Ember - 03 Firelight.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Kubbi - Ember - 03 Firelight.ogg -------------------------------------------------------------------------------- /assets/sounds/Kubbi - Ember - 04 Cascade.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Kubbi - Ember - 04 Cascade.ogg -------------------------------------------------------------------------------- /assets/sounds/Kubbi - Ember - 05 Compass.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Kubbi - Ember - 05 Compass.ogg -------------------------------------------------------------------------------- /assets/sounds/Kubbi - Ember - 09 Formed by Glaciers.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Kubbi - Ember - 09 Formed by Glaciers.ogg -------------------------------------------------------------------------------- /assets/sounds/Magical Impact 12.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Magical Impact 12.ogg -------------------------------------------------------------------------------- /assets/sounds/Magical Impact 13.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Magical Impact 13.ogg -------------------------------------------------------------------------------- /assets/sounds/Magical Impact 18.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Magical Impact 18.ogg -------------------------------------------------------------------------------- /assets/sounds/Magical Impact 26.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Magical Impact 26.ogg -------------------------------------------------------------------------------- /assets/sounds/Magical Impact 27.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Magical Impact 27.ogg -------------------------------------------------------------------------------- /assets/sounds/Magical Swoosh 18.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Magical Swoosh 18.ogg -------------------------------------------------------------------------------- /assets/sounds/Male Jump 1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Male Jump 1.ogg -------------------------------------------------------------------------------- /assets/sounds/Male Jump 2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Male Jump 2.ogg -------------------------------------------------------------------------------- /assets/sounds/Male Jump 3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Male Jump 3.ogg -------------------------------------------------------------------------------- /assets/sounds/Mud footsteps 7.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Mud footsteps 7.ogg -------------------------------------------------------------------------------- /assets/sounds/Mud footsteps 9.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Mud footsteps 9.ogg -------------------------------------------------------------------------------- /assets/sounds/Pistol Shot_07.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Pistol Shot_07.ogg -------------------------------------------------------------------------------- /assets/sounds/Pistol Shot_08.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Pistol Shot_08.ogg -------------------------------------------------------------------------------- /assets/sounds/Player Takes Damage 17.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Player Takes Damage 17.ogg -------------------------------------------------------------------------------- /assets/sounds/Player Takes Damage 2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Player Takes Damage 2.ogg -------------------------------------------------------------------------------- /assets/sounds/Pop sounds 10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Pop sounds 10.ogg -------------------------------------------------------------------------------- /assets/sounds/Popping bloody Sac 1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Popping bloody Sac 1.ogg -------------------------------------------------------------------------------- /assets/sounds/Releasing Bow String 1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Releasing Bow String 1.ogg -------------------------------------------------------------------------------- /assets/sounds/Revolver Shot_07.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Revolver Shot_07.ogg -------------------------------------------------------------------------------- /assets/sounds/Revolver Shot_08.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Revolver Shot_08.ogg -------------------------------------------------------------------------------- /assets/sounds/Sci Fi Machine Gun 7.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Sci Fi Machine Gun 7.ogg -------------------------------------------------------------------------------- /assets/sounds/Shadow Punch 1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Shadow Punch 1.ogg -------------------------------------------------------------------------------- /assets/sounds/Shadow Punch 2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Shadow Punch 2.ogg -------------------------------------------------------------------------------- /assets/sounds/Shield Impacts Sword 1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Shield Impacts Sword 1.ogg -------------------------------------------------------------------------------- /assets/sounds/Shooting Projectile (Classic) 11.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Shooting Projectile (Classic) 11.ogg -------------------------------------------------------------------------------- /assets/sounds/Sniper Shot_09.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Sniper Shot_09.ogg -------------------------------------------------------------------------------- /assets/sounds/Spark 1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Spark 1.ogg -------------------------------------------------------------------------------- /assets/sounds/Spark 2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Spark 2.ogg -------------------------------------------------------------------------------- /assets/sounds/Spark 3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Spark 3.ogg -------------------------------------------------------------------------------- /assets/sounds/Spawn 10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Spawn 10.ogg -------------------------------------------------------------------------------- /assets/sounds/Switch 3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Switch 3.ogg -------------------------------------------------------------------------------- /assets/sounds/Switch.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Switch.ogg -------------------------------------------------------------------------------- /assets/sounds/Sword hits another sword 6.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Sword hits another sword 6.ogg -------------------------------------------------------------------------------- /assets/sounds/Sword impact (Flesh) 2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Sword impact (Flesh) 2.ogg -------------------------------------------------------------------------------- /assets/sounds/Throwing Knife (Thrown) 3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Throwing Knife (Thrown) 3.ogg -------------------------------------------------------------------------------- /assets/sounds/Throwing Knife (Thrown) 4.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Throwing Knife (Thrown) 4.ogg -------------------------------------------------------------------------------- /assets/sounds/Trailer - Ember.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Trailer - Ember.ogg -------------------------------------------------------------------------------- /assets/sounds/Unlock 3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Unlock 3.ogg -------------------------------------------------------------------------------- /assets/sounds/Weapon Swap 2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Weapon Swap 2.ogg -------------------------------------------------------------------------------- /assets/sounds/Whipping Horse 3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Whipping Horse 3.ogg -------------------------------------------------------------------------------- /assets/sounds/Wind Bolt 12.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Wind Bolt 12.ogg -------------------------------------------------------------------------------- /assets/sounds/Wind Bolt 14.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Wind Bolt 14.ogg -------------------------------------------------------------------------------- /assets/sounds/Wind Bolt 18.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Wind Bolt 18.ogg -------------------------------------------------------------------------------- /assets/sounds/Wind Bolt 20.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Wind Bolt 20.ogg -------------------------------------------------------------------------------- /assets/sounds/Wind Bolt 8.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Wind Bolt 8.ogg -------------------------------------------------------------------------------- /assets/sounds/Wolf barks 5.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Wolf barks 5.ogg -------------------------------------------------------------------------------- /assets/sounds/Wood Heavy 5.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/Wood Heavy 5.ogg -------------------------------------------------------------------------------- /assets/sounds/bamboo_hit_by_lord.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/assets/sounds/bamboo_hit_by_lord.ogg -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd D:/code/SNKRX 4 | engine/love/build_steam.bat SNKRX 5 | -------------------------------------------------------------------------------- /builds/windows/windows_build_goes_here.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/builds/windows/windows_build_goes_here.txt -------------------------------------------------------------------------------- /conf.lua: -------------------------------------------------------------------------------- 1 | function love.conf(t) 2 | t.version = "11.3" 3 | t.window.width = 960 4 | t.window.height = 540 5 | t.window.vsync = 1 6 | t.window.msaa = 0 7 | end 8 | -------------------------------------------------------------------------------- /engine/datastructures/graph.lua: -------------------------------------------------------------------------------- 1 | Graph = Object:extend() 2 | function Graph:init() 3 | self.adjacency_list = {} 4 | self.nodes = {} 5 | self.edges = {} 6 | self.floyd_dists = {} 7 | end 8 | 9 | 10 | -- Nodes can be of any type but must be unique 11 | -- graph = Graph() 12 | -- graph:add_node(1) 13 | -- graph:add_node('node_2') 14 | function Graph:add_node(node) 15 | self.adjacency_list[node] = {} 16 | self:_set_nodes() 17 | end 18 | 19 | 20 | -- Returns a node after searching for it by property, the property value must be unique among all nodes 21 | -- graph = Graph() 22 | -- graph:add_node({id = 1}) 23 | -- graph:add_node({id = 2}) 24 | -- graph:get_node_by_property('id', 1) -> original {id = 1} table 25 | function Graph:get_node_by_property(key, value) 26 | for node, _ in pairs(self.adjacency_list) do 27 | if node[key] == value then 28 | return node 29 | end 30 | end 31 | end 32 | 33 | 34 | -- Runs function f for all nodes in the graph 35 | -- graph = Graph() 36 | -- graph:add_node(1) 37 | -- graph:add_node('node_2') 38 | -- graph:for_all_nodes(function(node) print(node) end) -> prints 1, 'node_2' 39 | function Graph:for_all_nodes(f) 40 | for _, node in ipairs(self.nodes) do 41 | f(node) 42 | end 43 | end 44 | 45 | 46 | -- Runs function f for all edges in the graph 47 | -- graph = Graph() 48 | -- graph:add_node(1) 49 | -- graph:add_node('node_2') 50 | -- graph:add_node(3) 51 | -- graph:add_edge(1, 'node_2') 52 | -- graph:add_edge('node_2', 3) 53 | -- graph:for_all_edges(function(node1, node2) print(node1, node2) end) -> prints 1, 'node_2'; prints 'node_2', 3 54 | function Graph:for_all_edges(f) 55 | for _, edge in ipairs(self.edges) do 56 | f(edge[1], edge[2]) 57 | end 58 | end 59 | 60 | 61 | -- Returns a table containing all neighbors of the given node. 62 | -- graph = Graph() 63 | -- graph:add_node(1) 64 | -- graph:add_node('node_2') 65 | -- graph:add_node(3) 66 | -- graph:add_edge(1, 'node_2') 67 | -- graph:add_edge('node_2', 3) 68 | -- graph:get_node_neighbors('node_2') -> {1, 3} 69 | function Graph:get_node_neighbors(node) 70 | return self.adjacency_list[node] 71 | end 72 | 73 | 74 | -- graph = Graph() 75 | -- graph:add_node(1) 76 | -- graph:remove_node(1) 77 | function Graph:remove_node(node) 78 | for _node, list in pairs(self.adjacency_list) do 79 | self:remove_edge(node, _node) 80 | end 81 | self.adjacency_list[node] = nil 82 | self:_set_nodes() 83 | end 84 | 85 | 86 | local function contains_edge(table, edge) 87 | for _, v in ipairs(table) do 88 | if (v[1] == edge[1] and v[2] == edge[2]) or (v[1] == edge[2] and v[2] == edge[1]) then 89 | return true 90 | end 91 | end 92 | return false 93 | end 94 | 95 | 96 | -- graph = Graph() 97 | -- graph:add_node(1) 98 | -- graph:add_node('node_2') 99 | -- graph:add_edge(1, 'node_2') 100 | function Graph:add_edge(node1, node2) 101 | if table.any(self.adjacency_list[node1], function(v) return v == node2 end) then return end 102 | table.insert(self.adjacency_list[node1], node2) 103 | table.insert(self.adjacency_list[node2], node1) 104 | self:_set_edges() 105 | end 106 | 107 | 108 | -- graph = Graph() 109 | -- graph:add_node(1) 110 | -- graph:add_node('node_2') 111 | -- graph:add_edge(1, 'node_2') 112 | -- graph:remove_edge(1, 'node_2') 113 | function Graph:remove_edge(node1, node2) 114 | for i, node in ipairs(self.adjacency_list[node1]) do 115 | if node == node2 then 116 | table.remove(self.adjacency_list[node1], i) 117 | break 118 | end 119 | end 120 | for i, node in ipairs(self.adjacency_list[node2]) do 121 | if node == node1 then 122 | table.remove(self.adjacency_list[node2], i) 123 | break 124 | end 125 | end 126 | self:_set_edges() 127 | end 128 | 129 | 130 | -- graph = Graph() 131 | -- graph:add_node(1) 132 | -- graph:add_node('node_2') 133 | -- graph:add_node('node_3') 134 | -- graph:add_edge(1, 'node_2') 135 | -- graph:add_edge('node_2', 'node_3') 136 | -- path = graph:shortest_path_bfs(1, 'node_3') 137 | -- print(path) -> {1, 'node_2', 'node_3'} 138 | function Graph:shortest_path_bfs(node1, node2) 139 | local path = {} 140 | local visited = {} 141 | local queue = {} 142 | table.insert(queue, node1) 143 | visited[node1] = true 144 | 145 | while #queue > 0 do 146 | local node = table.remove(queue, 1) 147 | if node == node2 then 148 | local linear_path = {} 149 | local current_node = node2 150 | table.insert(linear_path, 1, current_node) 151 | while current_node ~= node1 do 152 | current_node = path[current_node] 153 | table.insert(linear_path, 1, current_node) 154 | end 155 | return linear_path 156 | end 157 | 158 | for _, neighbor in ipairs(self.adjacency_list[node]) do 159 | if not visited[neighbor] then 160 | path[neighbor] = node 161 | visited[neighbor] = true 162 | table.insert(queue, neighbor) 163 | end 164 | end 165 | end 166 | end 167 | 168 | 169 | function Graph:get_distance_between_nodes(node1, node2) 170 | return self.floyd_dists[node1][node2] 171 | end 172 | 173 | 174 | -- Comments follow pseudocode from http://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm. 175 | -- graph = Graph() 176 | -- graph:add_node(1) 177 | -- graph:add_node('node_2') 178 | -- graph:add_edge(1, 'node_2') 179 | -- graph:floyd_warshall() 180 | -- print(graph.floyd_dists[1]['node_2']) -> 1 181 | function Graph:floyd_warshall() 182 | self:_set_nodes() 183 | self:_set_edges() 184 | 185 | -- initialize multidimensional to be array 186 | for _, node in ipairs(self.nodes) do 187 | self.floyd_dists[node] = {} 188 | end 189 | 190 | -- let floyd_dist be a |V|x|V| array of minimun distance initialized to infinity 191 | for _, node in ipairs(self.nodes) do 192 | for _, _node in ipairs(self.nodes) do 193 | self.floyd_dists[node][_node] = 10000 -- 10000 is big enough for an unweighted graph 194 | self.floyd_dists[_node][node] = 10000 195 | end 196 | end 197 | 198 | -- set dist[v][v] to 0 199 | for _, node in ipairs(self.nodes) do 200 | self.floyd_dists[node][node] = 0 201 | end 202 | 203 | -- set dist[u][v] to w(u, v) which is always 1 in the case of an unweighted graph 204 | for _, edge in ipairs(self.edges) do 205 | self.floyd_dists[edge[1]][edge[2]] = 1 206 | self.floyd_dists[edge[2]][edge[1]] = 1 207 | end 208 | 209 | -- main triple loop 210 | for _, nodek in ipairs(self.nodes) do 211 | for _, nodei in ipairs(self.nodes) do 212 | for _, nodej in ipairs(self.nodes) do 213 | if self.floyd_dists[nodei][nodek] + self.floyd_dists[nodek][nodej] < self.floyd_dists[nodei][nodej] then 214 | self.floyd_dists[nodei][nodej] = self.floyd_dists[nodei][nodek] + self.floyd_dists[nodek][nodej] 215 | end 216 | end 217 | end 218 | end 219 | end 220 | 221 | 222 | function Graph:_get_edge(node1, node2) 223 | for _, node in ipairs(self.adjacency_list[node1]) do 224 | if node == node2 then 225 | return true 226 | end 227 | end 228 | return false 229 | end 230 | 231 | 232 | function Graph:_set_nodes() 233 | self.nodes = {} 234 | for node, _ in pairs(self.adjacency_list) do 235 | table.insert(self.nodes, node) 236 | end 237 | end 238 | 239 | 240 | function Graph:_set_edges() 241 | self.edges = {} 242 | for node, list in pairs(self.adjacency_list) do 243 | for _, _node in ipairs(list) do 244 | if not contains_edge(self.edges, {node, _node}) then 245 | table.insert(self.edges, {node, _node}) 246 | end 247 | end 248 | end 249 | end 250 | 251 | 252 | function Graph:__tostring() 253 | local str = "----\nAdjacency List: \n" 254 | for node, list in pairs(self.adjacency_list) do 255 | str = str .. node .. " -> " 256 | for _, adj in ipairs(list) do 257 | str = str .. adj .. ", " 258 | end 259 | str = string.sub(str, 0, -3) 260 | str = str .. "\n" 261 | end 262 | str = str .. "\n" 263 | str = str .. "Nodes: \n" 264 | for _, node in ipairs(self.nodes) do 265 | str = str .. node .. "\n" 266 | end 267 | str = str .. "\n" 268 | str = str .. "Edges: \n" 269 | for _, edge in ipairs(self.edges) do 270 | str = str .. edge[1] .. ", " .. edge[2] 271 | str = str .. "\n" 272 | end 273 | str = str .. "\n" 274 | str = str .. "Floyd Warshall Distances: \n" 275 | for node, _ in pairs(self.floyd_dists) do 276 | for _node, _ in pairs(self.floyd_dists[node]) do 277 | str = str .. "(" .. node .. ", " .. _node .. ") = " .. self.floyd_dists[node][_node] 278 | str = str .. "\n" 279 | end 280 | end 281 | str = str .. "----\n" 282 | return str 283 | end 284 | -------------------------------------------------------------------------------- /engine/datastructures/grid.lua: -------------------------------------------------------------------------------- 1 | -- Starts a new grid with 10 width, 5 height and all values 0ed 2 | -- grid = Grid(10, 5, 0) 3 | -- Starts a new grid with 3 width, 2 height and values 1, 2, 3 in the first row and 3, 4, 5 in the second row. 4 | -- grid = Grid(3, 2, {1, 2, 3, 4, 5, 6}) 5 | Grid = Object:extend() 6 | function Grid:init(w, h, v) 7 | self.grid = {} 8 | self.w, self.h = w, h 9 | if type(v) ~= 'table' then 10 | for j = 1, h do 11 | for i = 1, w do 12 | self.grid[w*(j-1) + i] = v or 0 13 | end 14 | end 15 | else 16 | for j = 1, h do 17 | for i = 1, w do 18 | self.grid[w*(j-1) + i] = v[w*(j-1) + i] 19 | end 20 | end 21 | end 22 | end 23 | 24 | 25 | -- Creates a copy of a grid instance 26 | -- grid = Grid(10, 5, 0) 27 | -- grid_clone = grid:clone() 28 | function Grid:clone() 29 | local new_grid = Grid(self.w, self.h, 0) 30 | new_grid.grid = table.deep_copy(self.grid) 31 | return new_grid 32 | end 33 | 34 | 35 | -- grid = Grid(10, 5, 0) 36 | -- grid:get(2, 2) -> 0 37 | -- grid:set(2, 2, 1) 38 | -- grid:get(2, 2) -> 1 39 | -- grid:set(11, 1, 1) -> doesn't actually set because out of bounds, fails silently 40 | function Grid:set(x, y, v) 41 | if not self:_is_outside_bounds(x, y) then 42 | self.grid[self.w*(y-1) + x] = v 43 | end 44 | end 45 | 46 | 47 | -- Applies function f to all grid elements 48 | -- If i1,j1 and i2,j2 are passed then it applies only to the subgrid defined by those values. 49 | -- grid:apply(function(grid, i, j) grid:set(i, j, 0) end) -> sets all elements in the grid to 0 50 | -- grid:apply(function(grid, i, j) grid:set(i, j, 0) end, 2, 2, 4, 4) -> sets all elements in the subgrid 2,2x4,4 to 0 51 | function Grid:apply(f, i1, j1, i2, j2) 52 | if i1 and j1 and i2 and j2 then 53 | for i = i1, i2 do 54 | for j = j1, j2 do 55 | f(self, i, j) 56 | end 57 | end 58 | else 59 | for i = 1, self.w do 60 | for j = 1, self.h do 61 | f(self, i, j) 62 | end 63 | end 64 | end 65 | end 66 | 67 | 68 | -- grid = Grid(10, 5, 0) 69 | -- print(grid:get(2, 2)) -> 0 70 | -- grid:set(2, 2, 1) 71 | -- grid:get(2, 2) -> 1 72 | -- grid:get(11, 1) -> nil, out of bounds, fails silently 73 | function Grid:get(x, y) 74 | if not self:_is_outside_bounds(x, y) then 75 | return self.grid[self.w*(y-1) + x] 76 | end 77 | end 78 | 79 | 80 | -- Converts the 2D grid to a 1D array 81 | -- If i1,j1 and i2,j2 are passed then it applies only to the subgrid defined by those values. 82 | -- grid = Grid(3, 2, 1) 83 | -- grid:to_table() -> {1, 1, 1, 1, 1, 1} 84 | -- grid:to_table(1, 1, 2, 2) -> {1, 1, 1, 1} 85 | function Grid:to_table(i1, j1, i2, j2) 86 | local t = {} 87 | if i1 and j1 and i2 and j2 then 88 | for j = j1, j2 do 89 | for i = i1, i2 do 90 | if self:get(i, j) then 91 | table.insert(t, self:get(i, j)) 92 | end 93 | end 94 | end 95 | else 96 | for j = 1, self.h do 97 | for i = 1, self.w do 98 | table.insert(t, self:get(i, j)) 99 | end 100 | end 101 | end 102 | return t, self.w 103 | end 104 | 105 | 106 | -- Rotates the grid in an anti-clockwise direction 107 | -- grid = Grid(3, 2, {1, 2, 3, 4, 5, 6}) -> the grid looks like this: 108 | -- [1, 2, 3] 109 | -- [4, 5, 6] 110 | -- grid:rotate_anticlockwise() -> now the grid looks like this: 111 | -- [3, 6] 112 | -- [2, 5] 113 | -- [1, 4] 114 | -- grid:rotate_anticlockwise() -> now the grid looks like this: 115 | -- [6, 5, 4] 116 | -- [3, 2, 1] 117 | function Grid:rotate_anticlockwise() 118 | local new_grid = Grid(self.h, self.w, 0) 119 | for i = 1, self.w do 120 | for j = 1, self.h do 121 | new_grid:set(j, i, self:get(i, j)) 122 | end 123 | end 124 | 125 | for i = 1, new_grid.w do 126 | for k = 0, math.floor(new_grid.h/2) do 127 | local v1, v2 = new_grid:get(i, 1+k), new_grid:get(i, new_grid.h-k) 128 | new_grid:set(i, 1+k, v2) 129 | new_grid:set(i, new_grid.h-k, v1) 130 | end 131 | end 132 | 133 | return new_grid 134 | end 135 | 136 | 137 | -- Rotates the grid in a clockwise direction 138 | -- grid = Grid(3, 2, {1, 2, 3, 4, 5, 6}) -> the grid looks like this: 139 | -- [1, 2, 3] 140 | -- [4, 5, 6] 141 | -- grid:rotate_clockwise() -> now the grid looks like this: 142 | -- [4, 1] 143 | -- [5, 2] 144 | -- [6, 3] 145 | -- grid:rotate_clockwise() -> now the grid looks like this: 146 | -- [6, 5, 4] 147 | -- [3, 2, 1] 148 | function Grid:rotate_clockwise() 149 | local new_grid = Grid(self.h, self.w, 0) 150 | for i = 1, self.w do 151 | for j = 1, self.h do 152 | new_grid:set(j, i, self:get(i, j)) 153 | end 154 | end 155 | 156 | for j = 1, new_grid.h do 157 | for k = 0, math.floor(new_grid.w/2) do 158 | local v1, v2 = new_grid:get(1+k, j), new_grid:get(new_grid.w-k, j) 159 | new_grid:set(1+k, j, v2) 160 | new_grid:set(new_grid.w-k, j, v1) 161 | end 162 | end 163 | 164 | return new_grid 165 | end 166 | 167 | 168 | -- Assume the following grid: 169 | -- grid = Grid(10, 10, { 170 | -- 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 171 | -- 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 172 | -- 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 173 | -- 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 174 | -- 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 175 | -- 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 176 | -- 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 177 | -- 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 178 | -- 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 179 | -- 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 180 | -- }) 181 | -- In this grid you can see that there are multiple islands of solid positions formed. 182 | -- This function will go over the entire grid and find all the islands of solid values, mark them with different numbers, and return them. 183 | -- Essentially, it would do this: { 184 | -- 1, 1, 1, 0, 0, 0, 0, 2, 2, 0, 185 | -- 1, 1, 0, 0, 0, 0, 2, 2, 2, 2, 186 | -- 1, 0, 0, 0, 3, 0, 2, 0, 2, 0, 187 | -- 0, 0, 0, 3, 3, 3, 0, 0, 2, 0, 188 | -- 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 189 | -- 4, 0, 0, 0, 0, 0, 0, 5, 0, 0, 190 | -- 4, 4, 0, 0, 0, 0, 5, 5, 0, 0, 191 | -- 4, 4, 0, 6, 6, 0, 0, 5, 5, 5, 192 | -- 4, 4, 0, 6, 6, 0, 0, 0, 0, 5, 193 | -- 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 194 | -- } 195 | -- All values form islands that are connected, and each of those islands is identified by a different number. 196 | -- The function returns this information in two formats: an array of positions per island number, and the marked grid as shown above. 197 | -- islands, marked_grid = grid:flood_fill(1) -> (the value passed in is what the solid value should be, in the case of the array we're using as an example 1 is the proper value) 198 | -- islands is an array that looks like this: { 199 | -- [1] = {{1, 1}, {2, 1}, {3, 1}, {1, 2}, {2, 2}, {1, 3}}, 200 | -- [2] = {{8, 1}, {9, 1}, {7, 2}, {8, 2}, {9, 2}, {10, 2}, {7, 3}, {9, 3}, {9, 4}}, 201 | -- ... 202 | -- [7] = {{3, 10}} 203 | -- } 204 | -- It contains all the positions in each island, indexed by island number. 205 | -- And marked_grid is simply a Grid instance that looks exactly like the one shown above right after I said "Essentially, it would do this:" 206 | function Grid:flood_fill(v) 207 | local islands = {} 208 | local marked_grid = Grid(self.w, self.h, 0) 209 | 210 | local flood_fill = function(i, j, color) 211 | local queue = {} 212 | table.insert(queue, {i, j}) 213 | while #queue > 0 do 214 | local x, y = unpack(table.remove(queue, 1)) 215 | marked_grid:set(x, y, color) 216 | table.insert(islands[color], {x, y}) 217 | 218 | if self:get(x, y-1) == v and marked_grid:get(x, y-1) == 0 then table.insert(queue, {x, y-1}) end 219 | if self:get(x, y+1) == v and marked_grid:get(x, y+1) == 0 then table.insert(queue, {x, y+1}) end 220 | if self:get(x-1, y) == v and marked_grid:get(x-1, y) == 0 then table.insert(queue, {x-1, y}) end 221 | if self:get(x+1, y) == v and marked_grid:get(x+1, y) == 0 then table.insert(queue, {x+1, y}) end 222 | end 223 | end 224 | 225 | local color = 1 226 | islands[color] = {} 227 | for i = 1, self.w do 228 | for j = 1, self.h do 229 | if self:get(i, j) == v and marked_grid:get(i, j) == 0 then 230 | flood_fill(i, j, color) 231 | color = color + 1 232 | islands[color] = {} 233 | end 234 | end 235 | end 236 | 237 | islands[color] = nil 238 | return islands, marked_grid 239 | end 240 | 241 | 242 | function Grid:_is_outside_bounds(x, y) 243 | if x > self.w then return true end 244 | if x < 1 then return true end 245 | if y > self.h then return true end 246 | if y < 1 then return true end 247 | end 248 | 249 | 250 | function Grid:__tostring() 251 | local str = '' 252 | for j = 1, self.h do 253 | str = str .. '[' 254 | for i = 1, self.w do 255 | str = str .. self:get(i, j) .. ', ' 256 | end 257 | str = str:sub(1, -3) .. ']\n' 258 | end 259 | return str 260 | end 261 | -------------------------------------------------------------------------------- /engine/datastructures/string.lua: -------------------------------------------------------------------------------- 1 | -- Returns the substring to the left of the first instance of the pattern passed in 2 | -- a = 'assets/images/player_32.png' 3 | -- a:left('/') -> 'assets' 4 | function string:left(p) 5 | local i = self:find(p) 6 | if i then 7 | local out = self:sub(1, i-1) 8 | return out ~= "" and out 9 | end 10 | end 11 | 12 | 13 | -- Returns the substring to the right of the first instance of the pattern passed in 14 | -- a = 'assets/images/player_32.png' 15 | -- a:right('/') -> 'images/player_32.png' 16 | function string:right(p) 17 | local _, j = self:find(p) 18 | if j then 19 | local out = self:sub(j+1) 20 | return out ~= "" and out 21 | end 22 | end 23 | 24 | 25 | -- Splits the string into words in a table according to the separator pattern passed in 26 | -- paid_comment = 'This engine is really great!' 27 | -- paid_comment:split('%s') -> {'This', 'engine', 'is', 'really', 'great!'} 28 | function string:split(s) 29 | if not s then s = "%s" end 30 | local out = {} 31 | for str in self:gmatch("([^" .. s .. "]+)") do 32 | table.insert(out, str) 33 | end 34 | return out 35 | end 36 | 37 | 38 | -- Returns the character at a particular index 39 | -- a = 'engine' 40 | -- a:index(2) -> 'n' 41 | -- a:index(3) -> 'g' 42 | -- a:index(-1) -> 'e' 43 | function string:index(i) 44 | return self:sub(i, i) 45 | end 46 | 47 | 48 | -- Returns the capitalized string 49 | -- a = 'engine' 50 | -- a:capitalize() -> 'Engine' 51 | function string:capitalize() 52 | return self:gsub("^%l", string.upper) 53 | end 54 | -------------------------------------------------------------------------------- /engine/external/clipper.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2017 Laurent Zubiaur 2 | -- 3 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 4 | -- of this software and associated documentation files (the "Software"), to deal 5 | -- in the Software without restriction, including without limitation the rights 6 | -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | -- copies of the Software, and to permit persons to whom the Software is 8 | -- furnished to do so, subject to the following conditions: 9 | -- 10 | -- The above copyright notice and this permission notice shall be included in all 11 | -- copies or substantial portions of the Software. 12 | -- 13 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | -- SOFTWARE. 20 | 21 | local ffi = require 'ffi' 22 | -- NOTE ffi.load doesn't use package.cpath to search for libraries but rather the 23 | -- default OS default search path (e.g. LD_LIBRARY_PATH). 24 | 25 | -- Load Clipper from a shared library... 26 | local C = ffi.load 'polyclipping' 27 | -- or use the default namespace if Clipper's been compiled within the executable 28 | -- local C = ffi.C 29 | 30 | ffi.cdef[[ 31 | 32 | // Replace `int64_t` with `int32_t` if Clipper has been compiled with `use_int32` 33 | typedef struct __cl_int_point { int64_t x, y; } cl_int_point; 34 | typedef struct __cl_int_rect { int64_t left; int64_t top; int64_t right; int64_t bottom; } cl_int_rect; 35 | // Replace `long long` with `int` if compiled with `use_int32` 36 | typedef signed long long cInt; 37 | 38 | typedef struct __cl_path cl_path; 39 | typedef struct __cl_paths cl_paths; 40 | typedef struct __cl_offset cl_offset; 41 | typedef struct __cl_clipper cl_clipper; 42 | 43 | const char* cl_err_msg(); 44 | 45 | // Path 46 | cl_path* cl_path_new(); 47 | void cl_path_free(cl_path *self); 48 | cl_int_point* cl_path_get(cl_path *self, int i); 49 | bool cl_path_add(cl_path *self, cInt x, cInt y); 50 | int cl_path_size(cl_path *self); 51 | double cl_path_area(const cl_path *self); 52 | bool cl_path_orientation(const cl_path *self); 53 | void cl_path_reverse(cl_path *self); 54 | int cl_path_point_in_polygon(cl_path *self,cInt x, cInt y); 55 | cl_paths* cl_path_simplify(cl_path *self,int fillType); 56 | cl_path* cl_path_clean_polygon(const cl_path *in, double distance); 57 | 58 | // Paths 59 | cl_paths* cl_paths_new(); 60 | void cl_paths_free(cl_paths *self); 61 | cl_path* cl_paths_get(cl_paths *self, int i); 62 | bool cl_paths_add(cl_paths *self, cl_path *path); 63 | int cl_paths_size(cl_paths *self); 64 | 65 | // ClipperOffset 66 | cl_offset* cl_offset_new(double miterLimit,double roundPrecision); 67 | void cl_offset_free(cl_offset *self); 68 | cl_paths* cl_offset_path(cl_offset *self, cl_path *subj, double delta, int jointType, int endType); 69 | cl_paths* cl_offset_paths(cl_offset *self, cl_paths *subj, double delta, int jointType, int endType); 70 | void cl_offset_clear(cl_offset *self); 71 | 72 | // Clipper 73 | cl_clipper* cl_clipper_new(); 74 | void cl_clipper_free(cl_clipper *cl); 75 | void cl_clipper_clear(cl_clipper *cl); 76 | bool cl_clipper_add_path(cl_clipper *cl,cl_path *path, int pt, bool closed,const char *err); 77 | bool cl_clipper_add_paths(cl_clipper *cl,cl_paths *paths, int pt, bool closed,const char *err); 78 | void cl_clipper_reverse_solution(cl_clipper *cl, bool value); 79 | void cl_clipper_preserve_collinear(cl_clipper *cl, bool value); 80 | void cl_clipper_strictly_simple(cl_clipper *cl, bool value); 81 | cl_paths* cl_clipper_execute(cl_clipper *cl,int clipType,int subjFillType,int clipFillType); 82 | cl_int_rect cl_clipper_get_bounds(cl_clipper *cl); 83 | ]] 84 | 85 | local ClipType = { 86 | intersection = 0, union = 1, difference = 2, xor = 3 87 | } 88 | 89 | local JoinType = { 90 | square = 0, round = 1, miter = 2 91 | } 92 | 93 | local EndType = { 94 | closedPolygon = 0, closedLine = 1, openButt = 2, openSquare = 3, openRound = 4 95 | } 96 | 97 | -- Clipper constructor options 98 | local InitOptions = { 99 | reverseSolution = 1, strictlySimple = 2, preserveCollinear = 4 100 | } 101 | 102 | local PolyType = { 103 | subject = 0, clip = 1 104 | } 105 | 106 | local PolyFillType = { 107 | evenOdd = 1, nonZero = 2, positive = 2, negative = 3 108 | } 109 | 110 | local Path = {} 111 | 112 | function Path.new() 113 | return ffi.gc(C.cl_path_new(), C.cl_path_free) 114 | end 115 | 116 | function Path:add(x,y) 117 | return C.cl_path_add(self,x,y) 118 | end 119 | 120 | function Path:get(i) 121 | return C.cl_path_get(self,i-1) 122 | end 123 | 124 | function Path:size() 125 | return C.cl_path_size(self) 126 | end 127 | 128 | function Path:area() 129 | return C.cl_path_area(self) 130 | end 131 | 132 | function Path:reverse() 133 | return C.cl_path_reverse(self) 134 | end 135 | 136 | function Path:orientation() 137 | return C.cl_path_orientation(self) 138 | end 139 | 140 | function Path:contains(x,y) 141 | return C.cl_path_point_in_polygon(self,x,y) 142 | end 143 | 144 | function Path:simplify(fillType) 145 | fillType = fillType or 'evenOdd' 146 | fillType = assert(PolyFillType[fillType],'unknown fill type') 147 | return C.cl_path_simplify(self,fillType) 148 | end 149 | 150 | function Path:cleanPolygon(distance) 151 | distance = distance or 1.415 152 | return C.cl_path_clean_polygon(self,distance) 153 | end 154 | 155 | local Paths = {} 156 | 157 | function Paths.new() 158 | return ffi.gc(C.cl_paths_new(), C.cl_paths_free) 159 | end 160 | 161 | function Paths:add(path) 162 | return C.cl_paths_add(self,path) 163 | end 164 | 165 | function Paths:get(i) 166 | return C.cl_paths_get(self,i-1) 167 | end 168 | 169 | function Paths:size() 170 | return C.cl_paths_size(self) 171 | end 172 | 173 | local ClipperOffset = {} 174 | 175 | function ClipperOffset.new(miterLimit,roundPrecision) 176 | local co = C.cl_offset_new(miterLimit or 2,roundPrecision or 0.25) 177 | return ffi.gc(co, C.cl_offset_free) 178 | end 179 | 180 | function ClipperOffset:offsetPath(path,delta,jt,et) 181 | jt,et = jt or 'square', et or 'openButt' 182 | assert(JoinType[jt]) 183 | assert(EndType[et]) 184 | local out = C.cl_offset_path(self,path,delta,JoinType[jt],EndType[et]) 185 | if out == nil then 186 | error(ffi.string(C.cl_err_msg())) 187 | end 188 | return out 189 | end 190 | 191 | function ClipperOffset:offsetPaths(paths,delta,jt,et) 192 | jt,et = jt or 'square', et or 'openButt' 193 | assert(JoinType[jt]) 194 | assert(EndType[et]) 195 | local out = C.cl_offset_paths(self,paths,delta,JoinType[jt],EndType[et]) 196 | if out == nil then 197 | error(ffi.string(C.cl_err_msg())) 198 | end 199 | return out 200 | end 201 | 202 | function ClipperOffset:clear() 203 | C.cl_offset_clear(self) 204 | end 205 | 206 | -- Clipper 207 | 208 | local Clipper = {} 209 | 210 | function Clipper.new(...) 211 | local cl = C.cl_clipper_new() 212 | for _,opt in ipairs {...} do 213 | assert(InitOptions[opt]) 214 | if opt == 'strictlySimple' then 215 | C.cl_clipper_strictly_simple(true) 216 | elseif opt == 'reverseSolution' then 217 | C.cl_clipper_reverse_solution(true) 218 | else 219 | C.cl_clipper_preserve_collinear(true) 220 | end 221 | end 222 | return ffi.gc(cl, C.cl_clipper_free) 223 | end 224 | 225 | function Clipper:clear() 226 | C.cl_clipper_clear(self) 227 | end 228 | 229 | function Clipper:addPath(path,pt,closed) 230 | assert(path,'path is nil') 231 | assert(PolyType[pt],'unknown polygon type') 232 | if closed == nil then closed = true end 233 | C.cl_clipper_add_path(self,path,PolyType[pt],closed,err); 234 | end 235 | 236 | function Clipper:addPaths(paths,pt,closed) 237 | assert(paths,'paths is nil') 238 | assert(PolyType[pt],'unknown polygon type') 239 | if closed == nil then closed = true end 240 | if not C.cl_clipper_add_paths(self,paths,PolyType[pt],closed,err) then 241 | error(ffi.string(C.cl_err_msg())) 242 | end 243 | end 244 | 245 | function Clipper:execute(clipType,subjFillType,clipFillType) 246 | subjFillType = subjFillType or 'evenOdd' 247 | clipFillType = clipFillType or 'evenOdd' 248 | clipType = assert(ClipType[clipType],'unknown clip type') 249 | subjFillType = assert(PolyFillType[subjFillType],'unknown fill type') 250 | clipFillType = assert(PolyFillType[clipFillType],'unknown fill type') 251 | local out = C.cl_clipper_execute(self,clipType,subjFillType,clipFillType) 252 | -- XXX test `not nil` return false ?! 253 | if out == nil then 254 | error(ffi.string(C.cl_err_msg())) 255 | end 256 | return out 257 | end 258 | 259 | function Clipper:getBounds() 260 | local r = C.cl_clipper_get_bounds(self) 261 | return tonumber(r.left),tonumber(r.top),tonumber(r.right),tonumber(r.bottom) 262 | end 263 | 264 | ffi.metatype('cl_path', {__index = Path}) 265 | ffi.metatype('cl_paths', {__index = Paths}) 266 | ffi.metatype('cl_offset', {__index = ClipperOffset}) 267 | ffi.metatype('cl_clipper', {__index = Clipper}) 268 | 269 | return { 270 | Path = Path.new, 271 | Paths = Paths.new, 272 | ClipperOffset = ClipperOffset.new, 273 | Clipper = Clipper.new, 274 | } 275 | -------------------------------------------------------------------------------- /engine/external/init.lua: -------------------------------------------------------------------------------- 1 | local path = ... 2 | if not path:find("init") then 3 | binser = require(path .. ".binser") 4 | mlib = require(path .. ".mlib") 5 | -- if not web then clipper = require(path .. ".clipper") end 6 | ripple = require(path .. ".ripple") 7 | steam = require 'luasteam' 8 | end 9 | -------------------------------------------------------------------------------- /engine/game/flashes.lua: -------------------------------------------------------------------------------- 1 | -- The base Flashes class. 2 | -- This class is used to manage all flashes in an object. 3 | -- Add a new flash: 4 | -- self.flashes:add('hit', 0.15) 5 | -- Use it: 6 | -- self.flashes.hit:flash(0.1) 7 | -- self.flashes.hit.f -- a boolean that says if it's currently flashing or not 8 | -- Every GameObject has a .flashes attribute with a Flashes instance attached to it. 9 | Flashes = Object:extend() 10 | function Flashes:init() 11 | self.trigger = Trigger() 12 | end 13 | 14 | 15 | function Flashes:update(dt) 16 | self.trigger:update(dt) 17 | end 18 | 19 | 20 | -- Adds a new flash to the object. The name must be unique and the second argument is the default duration for the flash. 21 | -- self.flashes:add('hit', 0.15) 22 | function Flashes:add(name, default_duration) 23 | if name == 'parent' or name == 'names' or name == 'trigger' or name == 'add' or name == 'use' or name == 'update' or name == 'init' or name == 'pull' or name == 'flash' then 24 | error("Invalid name to be added to the Flashes object. 'add', 'flash', 'init', 'names', 'parent', 'pull', 'trigger', 'update' and 'use' are reserved names, choose another.") 25 | end 26 | self[name] = {f = false, default_duration = default_duration or 0.15, flash = function(_, duration) 27 | self[name].f = true 28 | self.trigger:after(duration or self[name].default_duration, function() self[name].f = false end, name) 29 | end} 30 | end 31 | -------------------------------------------------------------------------------- /engine/game/gameobject.lua: -------------------------------------------------------------------------------- 1 | -- The base GameObject class. 2 | -- The general way of creating an object that implements these functions goes like this: 3 | --[[ 4 | MyGameObject = Object:extend() 5 | MyGameObject:implement(GameObject) 6 | function MyGameObject:init(args) 7 | self:init_game_object(args) 8 | end 9 | 10 | function MyGameObject:update(dt) 11 | self:update_game_object(dt) 12 | end 13 | ]]-- 14 | 15 | -- This simply implements the GameObject class as a mixin into your own class, giving it the functions defined in this file as well as the attributes set from init_game_object. 16 | -- In general you'd create your own game object like this, for instance: 17 | -- group = Group() 18 | -- MyGameObject{group = group, x = 100, y = 100, v = 100, r = math.pi/4} 19 | -- And then this object would be automatically updated and drawn by the group. 20 | -- Alternatively you could add the object to the group manually: 21 | -- my_object = MyGameObject{x = 100, y = 100, v = 100, r = math.pi/4} 22 | -- group:add(my_object) 23 | -- One of the nice patterns I've found was a passing arguments as a key + value table. 24 | -- So in the case above, the object would automatically have its .x, .y and .v attributes set to 100 and its .r attribute set to math.pi/4. 25 | GameObject = Object:extend() 26 | function GameObject:init_game_object(args) 27 | for k, v in pairs(args or {}) do self[k] = v end 28 | if self.group then self.group:add(self) end 29 | self.x, self.y = self.x or 0, self.y or 0 30 | self.r = self.r or 0 31 | self.sx, self.sy = self.sx or 1, self.sy or 1 32 | self.id = self.id or random:uid() 33 | self.t = Trigger() 34 | self.springs = Springs() 35 | self.flashes = Flashes() 36 | self.hfx = HitFX(self) 37 | self.spring = Spring(1) 38 | return self 39 | end 40 | 41 | 42 | function GameObject:update_game_object(dt) 43 | self.t:update(dt) 44 | self.springs:update(dt) 45 | self.flashes:update(dt) 46 | self.hfx:update(dt) 47 | self.spring:update(dt) 48 | if self.body then self:update_physics(dt) end 49 | 50 | if self.shape then 51 | if self.shape.vertices and self.body then 52 | self.shape.vertices = {self.body:getWorldPoints(self.fixture:getShape():getPoints())} 53 | self.shape:get_centroid() 54 | end 55 | if self.body then 56 | self.shape:move_to(self:get_position()) 57 | end 58 | 59 | if self.interact_with_mouse then 60 | local colliding_with_mouse = self.shape:is_colliding_with_point(self.group:get_mouse_position()) 61 | if colliding_with_mouse and not self.colliding_with_mouse then 62 | self.colliding_with_mouse = true 63 | if self.on_mouse_enter then self:on_mouse_enter() end 64 | elseif not colliding_with_mouse and self.colliding_with_mouse then 65 | self.colliding_with_mouse = false 66 | if self.on_mouse_exit then self:on_mouse_exit() end 67 | end 68 | if self.colliding_with_mouse then 69 | if self.on_mouse_stay then self:on_mouse_stay() end 70 | end 71 | end 72 | end 73 | end 74 | 75 | 76 | function GameObject:draw_game_object() 77 | if self.body then self:draw_physics() end 78 | end 79 | -------------------------------------------------------------------------------- /engine/game/hitfx.lua: -------------------------------------------------------------------------------- 1 | -- The base HitFX class. 2 | -- Whenever an object is interacted with it's a good idea to either pull on a spring attached to its scale, or to flash it to signal that the interaction went through. 3 | -- This is a combination of both Springs and Flashes put together to create that effect. 4 | -- An instance of this called .hfx is added automatically to every game object. 5 | -- Add a new effect: 6 | -- self.hfx:add('hit') 7 | -- Subsequent arguments are defaults for flashes and springs respectively, so: 8 | -- self.hfx:add('hit', 0.15, 1, 200, 20) 9 | -- Will add a flash with default duration of 0.15 and a spring with parameters 1, 200, 20. 10 | -- Use the effect: 11 | -- self.hfx:use('hit') 12 | -- Subsequent arguments are the same as for the add function. This will call flash on the flash and pull on the spring. 13 | -- Access its values: 14 | -- self.hfx.hit.x -- the spring value 15 | -- self.hfx.hit.f -- the flash boolean 16 | HitFX = Object:extend() 17 | function HitFX:init(parent) 18 | self.parent = parent 19 | self.names = {} 20 | end 21 | 22 | 23 | function HitFX:update(dt) 24 | if not self.parent then return end 25 | if self.parent and self.parent.dead then self.parent = nil; return end 26 | 27 | for _, name in ipairs(self.names) do 28 | self[name].x = self.parent.springs[name].x 29 | self[name].f = self.parent.flashes[name].f 30 | end 31 | end 32 | 33 | 34 | function HitFX:add(name, x, k, d, default_flash_duration) 35 | if name == 'parent' or name == 'names' or name == 'trigger' or name == 'add' or name == 'use' or name == 'update' or name == 'init' or name == 'pull' or name == 'flash' then 36 | error("Invalid name to be added to the HitFX object. 'add', 'flash', 'init', 'names', 'parent', 'pull', 'trigger', 'update' and 'use' are reserved names, choose another.") 37 | end 38 | self.parent.flashes:add(name, default_flash_duration) 39 | self.parent.springs:add(name, x, k, d) 40 | table.insert(self.names, name) 41 | self[name] = {x = self.parent.springs[name].x, f = self.parent.flashes[name].f} 42 | end 43 | 44 | 45 | function HitFX:use(name, x, k, d, flash_duration) 46 | if not self.parent then return end 47 | self.parent.flashes[name]:flash(flash_duration) 48 | self.parent.springs[name]:pull(x, k, d) 49 | end 50 | 51 | 52 | function HitFX:pull(name, ...) 53 | self.parent.springs[name]:pull(...) 54 | end 55 | 56 | 57 | function HitFX:flash(name, ...) 58 | self.parent.flashes[name]:flash(...) 59 | end 60 | -------------------------------------------------------------------------------- /engine/game/input.lua: -------------------------------------------------------------------------------- 1 | Input = Object:extend() 2 | function Input:init(joystick_index) 3 | self.mouse_buttons = {"m1", "m2", "m3", "m4", "m5", "wheel_up", "wheel_down"} 4 | self.gamepad_buttons = {"fdown", "fup", "fleft", "fright", "dpdown", "dpup", "dpleft", "dpright", "start", "back", "guide", "leftstick", "rightstick", "rb", "lb"} 5 | self.index_to_gamepad_button = {["a"] = "fdown", ["b"] = "fright", ["x"] = "fleft", ["y"] = "fup", ["back"] = "back", ["start"] = "start", ["guide"] = "guide", ["leftstick"] = "leftstick", 6 | ["rightstick"] = "rightstick", ["leftshoulder"] = "lb", ["rightshoulder"] = "rb", ["dpdown"] = "dpdown", ["dpup"] = "dpup", ["dpleft"] = "dpleft", ["dpright"] = "dpright", 7 | } 8 | self.index_to_gamepad_axis = {["leftx"] = "leftx", ["rightx"] = "rightx", ["lefty"] = "lefty", ["righty"] = "righty", ["triggerleft"] = "lt", ["triggerright"] = "rt"} 9 | self.gamepad_axis = {} 10 | self.joystick_index = joystick_index or 1 11 | self.joystick = love.joystick.getJoysticks()[self.joystick_index] 12 | self.keyboard_state = {} 13 | self.previous_keyboard_state = {} 14 | self.mouse_state = {} 15 | self.previous_mouse_state = {} 16 | self.gamepad_state = {} 17 | self.previous_gamepad_state = {} 18 | self.actions = {} 19 | self.textinput_buffer = '' 20 | end 21 | 22 | 23 | function Input:update(dt) 24 | for _, action in ipairs(self.actions) do 25 | self[action].pressed = false 26 | self[action].down = false 27 | self[action].released = false 28 | end 29 | 30 | for _, action in ipairs(self.actions) do 31 | for _, key in ipairs(self[action].keys) do 32 | if table.contains(self.mouse_buttons, key) then 33 | self[action].pressed = self[action].pressed or (self.mouse_state[key] and not self.previous_mouse_state[key]) 34 | self[action].down = self[action].down or self.mouse_state[key] 35 | self[action].released = self[action].released or (not self.mouse_state[key] and self.previous_mouse_state[key]) 36 | elseif table.contains(self.gamepad_buttons, key) then 37 | self[action].pressed = self[action].pressed or (self.gamepad_state[key] and not self.previous_gamepad_state[key]) 38 | self[action].down = self[action].down or self.gamepad_state[key] 39 | self[action].released = self[action].released or (not self.gamepad_state[key] and self.previous_gamepad_state[key]) 40 | else 41 | self[action].pressed = self[action].pressed or (self.keyboard_state[key] and not self.previous_keyboard_state[key]) 42 | self[action].down = self[action].down or self.keyboard_state[key] 43 | self[action].released = self[action].released or (not self.keyboard_state[key] and self.previous_keyboard_state[key]) 44 | end 45 | end 46 | end 47 | 48 | self.previous_mouse_state = table.copy(self.mouse_state) 49 | self.previous_gamepad_state = table.copy(self.gamepad_state) 50 | self.previous_keyboard_state = table.copy(self.keyboard_state) 51 | self.mouse_state.wheel_up = false 52 | self.mouse_state.wheel_down = false 53 | end 54 | 55 | 56 | function Input:set_mouse_grabbed(v) 57 | love.mouse.setGrabbed(v) 58 | end 59 | 60 | 61 | function Input:set_mouse_visible(v) 62 | love.mouse.setVisible(v) 63 | end 64 | 65 | 66 | function Input:bind(action, keys) 67 | if not self[action] then self[action] = {} end 68 | if type(keys) == "string" then self[action].keys = {keys} 69 | elseif type(keys) == "table" then self[action].keys = keys end 70 | table.insert(self.actions, action) 71 | end 72 | 73 | 74 | function Input:unbind(action) 75 | self[action] = nil 76 | end 77 | 78 | 79 | function Input:axis(key) 80 | return self.gamepad_axis[key] 81 | end 82 | 83 | 84 | function Input:textinput(text) 85 | self.textinput_buffer = self.textinput_buffer .. text 86 | return self.textinput_buffer 87 | end 88 | 89 | 90 | function Input:get_and_clear_textinput_buffer() 91 | local buffer = self.textinput_buffer 92 | self.textinput_buffer = "" 93 | return buffer 94 | end 95 | 96 | 97 | function Input:bind_all() 98 | -- Set direct input binds for every keyboard and mouse key 99 | -- Mostly to be used if you want to skip the action system and refer to keys directly (i.e. for internal tools or menus that don't need their keys changed ever) 100 | local keyboard_binds = {['a'] = {'a'}, ['b'] = {'b'}, ['c'] = {'c'}, ['d'] = {'d'}, ['e'] = {'e'}, ['f'] = {'f'}, ['g'] = {'g'}, ['h'] = {'h'}, ['i'] = {'i'}, ['j'] = {'j'}, ['k'] = {'k'}, ['l'] = {'l'}, ['m'] = {'m'}, ['n'] = {'n'}, ['o'] = {'o'}, ['p'] = {'p'}, ['q'] = {'q'}, ['r'] = {'r'}, ['s'] = {'s'}, ['t'] = {'t'}, ['u'] = {'u'}, ['v'] = {'v'}, ['w'] = {'w'}, ['x'] = {'x'}, ['y'] = {'y'}, ['z'] = {'z'}, ['0'] = {'0'}, ['1'] = {'1'}, ['2'] = {'2'}, ['3'] = {'3'}, ['4'] = {'4'}, ['5'] = {'5'}, ['6'] = {'6'}, ['7'] = {'7'}, ['8'] = {'8'}, ['9'] = {'9'}, ['space'] = {'space'}, ['!'] = {'!'}, ['"'] = {'"'}, ['#'] = {'#'}, ['$'] = {'$'}, ['&'] = {'&'}, ["'"] = {"'"}, ['('] = {'('}, [')'] = {')'}, ['*'] = {'*'}, ['+'] = {'+'}, [','] = {','}, ['-'] = {'-'}, ['.'] = {'.'}, ['/'] = {'/'}, [':'] = {':'}, [';'] = {';'}, ['kp0'] = {'kp0'}, ['kp1'] = {'kp1'}, ['kp2'] = {'kp2'}, ['kp3'] = {'kp3'}, ['kp4'] = {'kp4'}, ['kp5'] = {'kp5'}, ['kp6'] = {'kp6'}, ['kp7'] = {'kp7'}, ['kp8'] = {'kp8'}, ['kp9'] = {'kp9'}, ['kp.'] = {'kp.'}, ['kp,'] = {'kp,'}, ['kp/'] = {'kp/'}, ['kp*'] = {'kp*'}, ['kp-'] = {'kp-'}, ['kp+'] = {'kp+'}, ['kpenter'] = {'kpenter'}, ['kp='] = {'kp='}, ['up'] = {'up'}, ['down'] = {'down'}, ['right'] = {'right'}, ['left'] = {'left'}, ['home'] = {'home'}, ['pageup'] = {'pageup'}, ['pagedown'] = {'pagedown'}, ['insert'] = {'insert'}, ['backspace'] = {'backspace'}, ['tab'] = {'tab'}, ['clear'] = {'clear'}, ['return'] = {'return'}, ['delete'] = {'delete'}, ['f1'] = {'f1'}, ['f2'] = {'f2'}, ['f3'] = {'f3'}, ['f4'] = {'f4'}, ['f5'] = {'f5'}, ['f6'] = {'f6'}, ['f7'] = {'f7'}, ['f8'] = {'f8'}, ['f9'] = {'f9'}, ['f10'] = {'f10'}, ['f11'] = {'f11'}, ['f12'] = {'f12'}, ['f13'] = {'f13'}, ['f14'] = {'f14'}, ['f15'] = {'f15'}, ['f16'] = {'f16'}, ['f17'] = {'f17'}, ['f18'] = {'f18'}, ['numlock'] = {'numlock'}, ['capslock'] = {'capslock'}, ['scrolllock'] = {'scrolllock'}, ['rshift'] = {'rshift'}, ['lshift'] = {'lshift'}, ['rctrl'] = {'rctrl'}, ['lctrl'] = {'lctrl'}, ['ralt'] = {'ralt'}, ['lalt'] = {'lalt'}, ['rgui'] = {'rgui'}, ['lgui'] = {'lgui'}, ['mode'] = {'mode'}, ['escape'] = {'escape'}} 101 | for k, v in pairs(keyboard_binds) do self:bind(k, v) end 102 | self:bind('m1', {'m1'}) 103 | self:bind('m2', {'m2'}) 104 | self:bind('m3', {'m3'}) 105 | self:bind('m4', {'m4'}) 106 | self:bind('m5', {'m5'}) 107 | self:bind('wheel_up', {'wheel_up'}) 108 | self:bind('wheel_down', {'wheel_down'}) 109 | end 110 | -------------------------------------------------------------------------------- /engine/game/object.lua: -------------------------------------------------------------------------------- 1 | Object = {} 2 | Object.__index = Object 3 | function Object:init() 4 | end 5 | 6 | 7 | function Object:extend() 8 | local cls = {} 9 | for k, v in pairs(self) do 10 | if k:find("__") == 1 then 11 | cls[k] = v 12 | end 13 | end 14 | cls.__index = cls 15 | cls.super = self 16 | setmetatable(cls, self) 17 | return cls 18 | end 19 | 20 | 21 | function Object:implement(...) 22 | for _, cls in pairs({...}) do 23 | for k, v in pairs(cls) do 24 | if self[k] == nil and type(v) == "function" then 25 | self[k] = v 26 | end 27 | end 28 | end 29 | end 30 | 31 | 32 | function Object:is(T) 33 | local mt = getmetatable(self) 34 | while mt do 35 | if mt == T then 36 | return true 37 | end 38 | mt = getmetatable(mt) 39 | end 40 | return false 41 | end 42 | 43 | 44 | function Object:__tostring() 45 | return "Object" 46 | end 47 | 48 | 49 | function Object:__call(...) 50 | local obj = setmetatable({}, self) 51 | obj:init(...) 52 | return obj 53 | end 54 | -------------------------------------------------------------------------------- /engine/game/parent.lua: -------------------------------------------------------------------------------- 1 | -- This useful to add to objects that need to have some kind of relationship with their parents. 2 | -- Call the appropriate function in the object's update function every frame. 3 | -- The game object must have a .parent attribute defined and pointing to another game object. 4 | Parent = Object:extend() 5 | 6 | 7 | -- Follows the parent's transform exclusively. 8 | -- This means that if the parent dies the entity also dies. 9 | -- The .parent attribute is niled on death. 10 | function Parent:follow_parent_exclusively() 11 | if self.parent and self.parent.dead then 12 | self.parent = nil 13 | self.dead = true 14 | return 15 | end 16 | self.x, self.y = self.parent.x, self.parent.y 17 | self.r = self.parent.r 18 | self.sx, self.sy = self.parent.sx, self.parent.sy 19 | end 20 | -------------------------------------------------------------------------------- /engine/game/springs.lua: -------------------------------------------------------------------------------- 1 | -- The base Springs class. 2 | -- This class is used to manage all springs in an object. 3 | -- Add a new spring: 4 | -- self.springs:add('hit', 1) 5 | -- Use it: 6 | -- self.springs.hit:pull(0.2) 7 | -- self.springs.hit.x 8 | -- Every GameObject has a .springs attribute with a Springs instance attached to it. 9 | -- See engine/game/hit 10 | Springs = Object:extend() 11 | function Springs:init() 12 | self.names = {} 13 | end 14 | 15 | 16 | function Springs:update(dt) 17 | for _, name in ipairs(self.names) do 18 | self[name]:update(dt) 19 | end 20 | end 21 | 22 | 23 | -- Adds a new spring to the object. The name must be unique and the next arguments are the same 24 | -- as the arguments for creating a spring, see engine/math/spring.lua. 25 | -- self.springs:add('hit', 1) 26 | function Springs:add(name, x, k, d) 27 | if name == 'parent' or name == 'names' or name == 'trigger' or name == 'add' or name == 'use' or name == 'update' or name == 'init' or name == 'pull' or name == 'flash' then 28 | error("Invalid name to be added to the Springs object. 'add', 'flash', 'init', 'names', 'parent', 'pull', 'trigger', 'update' and 'use' are reserved names, choose another.") 29 | end 30 | self[name] = Spring(x, k, d) 31 | table.insert(self.names, name) 32 | end 33 | -------------------------------------------------------------------------------- /engine/game/state.lua: -------------------------------------------------------------------------------- 1 | -- The base State class. 2 | -- The general way of creating an object that implements these functions goes like this: 3 | --[[ 4 | MyState = Object:extend() 5 | MyState:implement(State) 6 | function MyState:init(name) 7 | self:init_state(name) 8 | end 9 | 10 | function MyState:on_enter(from) 11 | 12 | end 13 | 14 | function MyState:update(dt) 15 | 16 | end 17 | 18 | 19 | function MyState:draw() 20 | 21 | end 22 | ]]-- 23 | 24 | -- This creates a new MyState class which you can then use to start writing your code. 25 | -- Use the init function for things you need to do when the state object is created. 26 | -- Use the on_enter function for things you need to do whenever the state gets activated. 27 | -- By default, whenever a state gets deactivated it's not deleted from memory, so if you want to restart a level, for instance, whenever you switch states, 28 | -- then you need to destroy everything that needs to be destroyed in an on_exit function and then recreate it again in the on_enter function. 29 | -- 30 | -- You'd add a state to the game like this: 31 | -- main:add(MyState'level_1') 32 | -- You'd move to that state like so: 33 | -- main:go_to'level_1' 34 | -- main:go_to automatically calls on_exit for the currently active state and on_enter for the new one. 35 | -- You can access the currently active state with main.current. 36 | State = Object:extend() 37 | function State:init_state(name) 38 | self.name = name or random:uid() 39 | self.active = false 40 | end 41 | 42 | 43 | function State:enter(from, ...) 44 | self.active = true 45 | if self.on_enter then self:on_enter(from, ...) end 46 | end 47 | 48 | 49 | function State:exit(to, ...) 50 | self.active = false 51 | if self.on_exit then self:on_exit(to, ...) end 52 | end 53 | 54 | 55 | 56 | -- The main state. This is a global state that is always active and contains all other states. 57 | Main = Object:extend() 58 | Main:implement(State) 59 | function Main:init(name) 60 | self:init_state(name) 61 | self.states = {} 62 | self.transitions = Group():no_camera() 63 | end 64 | 65 | 66 | function Main:update(dt) 67 | for _, state in pairs(self.states) do 68 | if state.active or state.persistent_update then 69 | state:update(dt) 70 | end 71 | end 72 | self.transitions:update(dt) 73 | end 74 | 75 | 76 | function Main:draw() 77 | for _, state in pairs(self.states) do 78 | if state.active or state.persistent_draw then 79 | state:draw() 80 | end 81 | end 82 | self.transitions:draw() 83 | end 84 | 85 | 86 | function Main:add(state) 87 | self.states[state.name] = state 88 | end 89 | 90 | 91 | function Main:get(state_name) 92 | return self.states[state_name] 93 | end 94 | 95 | 96 | -- Deactivates the current active state and activates the target one. 97 | -- Calls on_exit for the deactivated state and on_enter for the activated one. 98 | -- If on_exit returns true then the deactivated state will be removed from memory. 99 | -- You must handle the destruction of it yourself in its on_exit function. 100 | function Main:go_to(state, ...) 101 | if type(state) == 'string' then state = self:get(state) end 102 | 103 | if self.current then 104 | if self.current.active then 105 | if self.current:exit(state) then 106 | self.states[state.name] = nil 107 | end 108 | end 109 | end 110 | 111 | local last_state = self.current 112 | self.current = state 113 | state:enter(last_state, ...) 114 | end 115 | -------------------------------------------------------------------------------- /engine/graphics/animation.lua: -------------------------------------------------------------------------------- 1 | -- The class responsible for holding an animation's graphics. 2 | -- It takes in an already loaded image, the width and height of each frames, and a list of frames in terms of indexed position on the image. 3 | -- player_sheet = Image('player_sheet') 4 | -- player_idle_frames = AnimationFrames(player_sheet, 32, 32, {{1, 1}, {2, 1}}) 5 | -- player_run_frames = AnimationFrames(player_sheet, 32, 32, {{1, 2}, {2, 2}, {3, 2}}) 6 | -- player_attack_frames = AnimationFrames(player_sheet, 32, 32, {{1, 3}, {2, 3}, {3, 3}, {4, 3}}) 7 | -- 8 | -- In the example above we first load an image, and then load 3 player animations. 9 | -- Each animation comes from different rows in the same spritesheet, and that's reflected by the last argument in each call. 10 | -- If your animation comes from a single spritesheet that doesn't have multiple animations, then you can omit the last argument and it will automatically go through it. 11 | AnimationFrames = Object:extend() 12 | function AnimationFrames:init(image, frame_w, frame_h, frames_list) 13 | self.source = image 14 | self.frame_w, self.frame_h = frame_w, frame_h 15 | self.frames_list = frames_list 16 | 17 | if type(self.frames_list) == 'number' then -- the source is a single row spritesheet and number of frames are specified 18 | local frames_list = {} 19 | for i = 1, self.frames_list do table.insert(frames_list, {i, 1}) end 20 | self.frames_list = frames_list 21 | elseif not self.frames_list then 22 | local frames_list = {} 23 | for i = 1, math.floor(self.source.w/self.frame_w) do table.insert(frames_list, {i, 1}) end 24 | self.frames_list = frames_list 25 | end 26 | 27 | self.frames = {} 28 | for i, frame in ipairs(self.frames_list) do 29 | self.frames[i] = {quad = love.graphics.newQuad((frame[1]-1)*self.frame_w, (frame[2]-1)*self.frame_h, self.frame_w, self.frame_h, self.source.w, self.source.h), w = self.frame_w, h = self.frame_h} 30 | end 31 | self.size = #self.frames 32 | end 33 | 34 | 35 | function AnimationFrames:draw(frame, x, y, r, sx, sy, ox, oy, color) 36 | local _r, g, b, a 37 | if color then 38 | _r, g, b, a = love.graphics.getColor() 39 | graphics.set_color(color) 40 | end 41 | love.graphics.draw(self.source.image, self.frames[frame].quad, x, y, r or 0, sx or 1, sy or sx or 1, self.frames[frame].w/2 + (ox or 0), self.frames[frame].h/2 + (oy or 0)) 42 | if color then love.graphics.setColor(_r, g, b, a) end 43 | end 44 | 45 | 46 | 47 | 48 | -- The class that logically updates an animation. 49 | -- This being separated from the visual part of an animation is useful whenever you need animation-like behavior unrelated to graphics, like making your own animations with code only. For instance: 50 | --[[ 51 | self.animation = AnimationLogic(0.04, 6, 'loop', { 52 | [1] = function() 53 | if self.parent:is(Player) then 54 | for i = 1, random:int(1, 3) do DustParticle(game.current_state.floor, self.parent.x, self.parent.y)) end 55 | end 56 | self.z = 9 57 | end, 58 | [2] = function() self.parent.timer:tween(0.025, self, {z = 6}, math.linear, nil, 'move_2') end, 59 | [3] = function() self.parent.timer:tween(0.025, self, {z = 3}, math.linear, nil, 'move_3') end, 60 | [4] = function() 61 | self.parent.timer:tween(0.025, self, {z = 0}, math.linear, nil, 'move_4') 62 | self.sx = 0.1 63 | self.parent.timer:tween(0.05, self, {sx = 0}, math.linear, nil, 'move_5') 64 | end, 65 | }) 66 | ]]-- 67 | -- 68 | -- That was an example of a code-only movement animation for a game I'm making. 69 | -- The arguments that this takes are the delay between each frame, how many frames there are, the loop mode ('loop', 'once' or 'bounce') and a table of actions. 70 | -- The delay argument can either be a number or a table, if it's a table then the delay for each frame can be set individually: 71 | -- animation = AnimationLogic({0.02, 0.04, 0.06, 0.04}, ...) 72 | -- In the example above, it would take 0.02s to go from frame 1 to 2, 0.04s from 2 to 3, 0.06s from 3 to 4 and 0.04s from 4 to 5 (or 1 if there are only 4 frames). 73 | -- Loop can be either: 'loop', the animation will start once from frame 1 once it reaches the end; 'once', it will stop once it reaches the end; 'bounce', it will reverse once it reaches the end or start 74 | -- Finally, the actions table can contain a list of functions, as shown in the code-only animation example above, and each function will be performed when that frame is reached. 75 | -- In that example, once the second frame is reached, this function would be executed: 76 | -- function() self.parent.timer:tween(0.025, self, {z = 6}, math.linear, nil, 'move_2') end 77 | -- The index 0 can be used to perform an action once the animation reaches its end: 78 | -- self.animation = AnimationLogic(0.04, self.player_dead_frames.size, 'once', {[0] = function() self.dead = true end}) 79 | AnimationLogic = Object:extend() 80 | function AnimationLogic:init(delay, frames, loop_mode, actions) 81 | self.delay = delay 82 | self.frames = frames 83 | self.loop_mode = loop_mode or "once" 84 | self.actions = actions 85 | self.timer = 0 86 | self.frame = 1 87 | self.direction = 1 88 | end 89 | 90 | 91 | function AnimationLogic:update(dt) 92 | if self.dead then return end 93 | 94 | self.timer = self.timer + dt 95 | local delay = self.delay 96 | if type(self.delay) == "table" then delay = self.delay[self.frame] end 97 | 98 | if self.timer > delay then 99 | self.timer = 0 100 | self.frame = self.frame + self.direction 101 | if self.frame > self.frames or self.frame < 1 then 102 | if self.loop_mode == "once" then 103 | self.frame = self.frames 104 | self.dead = true 105 | elseif self.loop_mode == "loop" then 106 | self.frame = 1 107 | elseif self.loop_mode == "bounce" then 108 | self.direction = -self.direction 109 | self.frame = self.frame + 2*self.direction 110 | end 111 | if self.actions and self.actions[0] then self.actions[0]() end 112 | end 113 | if self.actions and self.actions[self.frame] then self.actions[self.frame]() end 114 | end 115 | end 116 | 117 | 118 | 119 | 120 | -- The Animation class, a mix of AnimationFrames and AnimationLogic. 121 | -- Takes in a delay, an AnimationFrames object, the loop mode and a table of actions. 122 | -- Read more about the AnimationFrames and AnimationLogic classes as everything there applies here. 123 | Animation = Object:extend() 124 | function Animation:init(delay, animation_frames, loop_mode, actions) 125 | self.delay = delay 126 | self.animation_frames = animation_frames 127 | self.size = self.animation_frames.size 128 | self.loop_mode = loop_mode 129 | self.actions = actions 130 | self.animation_logic = AnimationLogic(self.delay, self.animation_frames.size, self.loop_mode, self.actions) 131 | end 132 | 133 | 134 | function Animation:update(dt) 135 | self.animation_logic:update(dt) 136 | end 137 | 138 | 139 | function Animation:draw(x, y, r, sx, sy, ox, oy, color) 140 | self.animation_frames:draw(self.animation_logic.frame, x, y, r, sx, sy, ox, oy, color) 141 | end 142 | -------------------------------------------------------------------------------- /engine/graphics/canvas.lua: -------------------------------------------------------------------------------- 1 | -- A canvas object for offscreen rendering. 2 | Canvas = Object:extend() 3 | function Canvas:init(w, h, opts) 4 | local opts = opts or {} 5 | self.w, self.h = w, h 6 | self.canvas = love.graphics.newCanvas(self.w, self.h, {msaa = opts.msaa}) 7 | self.stencil = opts.stencil 8 | end 9 | 10 | 11 | -- Draws the canvas to the screen. 12 | --[[ 13 | function init() 14 | canvas = Canvas(gw, gh) 15 | end 16 | 17 | function draw() 18 | canvas:draw_to(function() 19 | -- draw your game 20 | end) 21 | canvas:draw(0, 0, 0, sx, sy) 22 | end 23 | ]]-- 24 | function Canvas:draw(x, y, r, sx, sy, ox, oy) 25 | love.graphics.setColor(1, 1, 1, 1) 26 | love.graphics.setBlendMode("alpha", "premultiplied") 27 | love.graphics.draw(self.canvas, x or 0, y or 0, r or 0, sx or 1, sy or 1, ox or 0, oy or 0) 28 | love.graphics.setBlendMode("alpha") 29 | end 30 | 31 | 32 | function Canvas:draw2(x, y, r, sx, sy, ox, oy) 33 | love.graphics.setColor(1, 1, 1, 1) 34 | love.graphics.draw(self.canvas, x or 0, y or 0, r or 0, sx or 1, sy or 1, ox or 0, oy or 0) 35 | end 36 | 37 | 38 | -- Takes in a function and uses it to draw to this canvas. 39 | -- canvas:draw_to(function() 40 | -- graphics.rectangle(gw/2, gh/2, 50, 50) 41 | -- end) 42 | function Canvas:draw_to(action) 43 | love.graphics.setCanvas({self.canvas, stencil = self.stencil}) 44 | love.graphics.clear() 45 | action() 46 | love.graphics.setCanvas() 47 | end 48 | 49 | 50 | -- Sets this canvas as the active one. 51 | function Canvas:set() 52 | current_canvas = self 53 | love.graphics.setCanvas(self.canvas) 54 | end 55 | 56 | 57 | -- Unsets this canvas as the active one. 58 | function Canvas:unset() 59 | current_canvas = nil 60 | love.graphics.setCanvas() 61 | end 62 | 63 | 64 | -- Clears this canvas' buffer. 65 | function Canvas:clear() 66 | love.graphics.clear() 67 | end 68 | -------------------------------------------------------------------------------- /engine/graphics/color.lua: -------------------------------------------------------------------------------- 1 | -- A basic color object. 2 | -- Colors can be created in 3 forms: 3 | -- color = Color('#ffffff') 4 | -- color = Color(255, 255, 255) 5 | -- color = Color(1, 1, 1) 6 | -- You can access the colors values via .r, .g, .b and .a. 7 | -- You can create a copy of a color by calling color:clone(). 8 | Color = Object:extend() 9 | function Color:init(r, g, b, a) 10 | if type(r) == "string" then 11 | local hex = r:gsub("#", "") 12 | self.r = tonumber("0x" .. hex:sub(1, 2))/255 13 | self.g = tonumber("0x" .. hex:sub(3, 4))/255 14 | self.b = tonumber("0x" .. hex:sub(5, 6))/255 15 | self.a = 1 16 | else 17 | if r > 1 or g > 1 or b > 1 then 18 | self.r = r/255 19 | self.g = g/255 20 | self.b = b/255 21 | self.a = (a or 255)/255 22 | else 23 | self.r = r 24 | self.g = g 25 | self.b = b 26 | self.a = a or 1 27 | end 28 | end 29 | end 30 | 31 | 32 | function Color:clone() 33 | return Color(self.r, self.g, self.b, self.a) 34 | end 35 | 36 | 37 | function Color:lighten(v) 38 | local h, s, l = self:_to_hsl() 39 | l = l + v 40 | self.r, self.g, self.b = self:_to_rgb(h, s, l) 41 | return self 42 | end 43 | 44 | 45 | function Color:darken(v) 46 | local h, s, l = self:_to_hsl() 47 | l = l - v 48 | self.r, self.g, self.b = self:_to_rgb(h, s, l) 49 | return self 50 | end 51 | 52 | 53 | function Color:fade(v) 54 | self.a = self.a - v 55 | return self 56 | end 57 | 58 | 59 | function Color:_to_hsl() 60 | local max, min = math.max(self.r, self.g, self.b), math.min(self.r, self.g, self.b) 61 | local h, s, l 62 | l = (max + min)/2 63 | if max == min then h, s = 0, 0 64 | else 65 | local d = max - min 66 | if l > 0.5 then s = d/(2 - max - min) else s = d/(max + min) end 67 | if max == self.r then 68 | h = (self.g - self.b)/d 69 | if self.g < self.b then h = h + 6 end 70 | elseif max == self.g then h = (self.b - self.r)/d + 2 71 | elseif max == self.b then h = (self.r - self.g)/d + 4 end 72 | h = h/6 73 | end 74 | return h, s, l 75 | end 76 | 77 | 78 | function Color:_to_rgb(h, s, l) 79 | if s == 0 then return math.clamp(l, 0, 1), math.clamp(l, 0, 1), math.clamp(l, 0, 1) end 80 | local function to(p, q, t) 81 | if t < 0 then t = t + 1 end 82 | if t > 1 then t = t - 1 end 83 | if t < .16667 then return p + (q - p)*6*t end 84 | if t < .5 then return q end 85 | if t < .66667 then return p + (q - p)*(.66667 - t)*6 end 86 | return p 87 | end 88 | local q = l < .5 and l*(1 + s) or l + s - l*s 89 | local p = 2*l - q 90 | return math.clamp(to(p, q, h + .33334), 0, 1), math.clamp(to(p, q, h), 0, 1), math.clamp(to(p, q, h - .33334), 0, 1) 91 | end 92 | -------------------------------------------------------------------------------- /engine/graphics/font.lua: -------------------------------------------------------------------------------- 1 | -- The base Font class. 2 | Font = Object:extend() 3 | function Font:init(asset_name, font_size) 4 | self.font = love.graphics.newFont("assets/fonts/" .. asset_name .. ".ttf", font_size) 5 | self.h = self.font:getHeight() 6 | end 7 | 8 | 9 | function Font:get_text_width(text) 10 | return self.font:getWidth(text) 11 | end 12 | 13 | 14 | function Font:get_height() 15 | return self.font:getHeight() 16 | end 17 | -------------------------------------------------------------------------------- /engine/graphics/image.lua: -------------------------------------------------------------------------------- 1 | -- The base Image class. 2 | Image = Object:extend() 3 | function Image:init(asset_name) 4 | self.image = love.graphics.newImage("assets/images/" .. asset_name .. ".png") 5 | self.w = self.image:getWidth() 6 | self.h = self.image:getHeight() 7 | end 8 | 9 | 10 | function Image:draw(x, y, r, sx, sy, ox, oy, color) 11 | local _r, g, b, a 12 | if color then 13 | _r, g, b, a = love.graphics.getColor() 14 | graphics.set_color(color) 15 | end 16 | love.graphics.draw(self.image, x, y, r or 0, sx or 1, sy or sx or 1, self.w/2 + (ox or 0), self.h/2 + (oy or 0)) 17 | if color then love.graphics.setColor(_r, g, b, a) end 18 | end 19 | 20 | 21 | 22 | 23 | -- The base Quad class. Useful for loading pieces of images as independent Image objects. Every function that takes in an Image also takes in a Quad. 24 | Quad = Object:extend() 25 | function Quad:init(image, tile_w, tile_h, tile_coordinates) 26 | self.image = image 27 | self.quad = love.graphics.newQuad((tile_coordinates[1]-1)*tile_w, (tile_coordinates[2]-1)*tile_h, tile_w, tile_h, self.image.w, self.image.h) 28 | self.w, self.h = tile_w, tile_h 29 | end 30 | 31 | 32 | function Quad:draw(x, y, r, sx, sy, ox, oy) 33 | love.graphics.draw(self.image.image, self.quad, x, y, r or 0, sx or 1, sy or sx or 1, self.w/2 + (ox or 0), self.h/2 + (oy or 0)) 34 | end 35 | 36 | 37 | 38 | 39 | 40 | -- A linear gradient image. 41 | -- The first argument is the direction of the gradient and can be either 'horizontal' or 'vertical'. 42 | GradientImage = Object:extend() 43 | function GradientImage:init(direction, ...) 44 | local colors = {...} 45 | local mesh_data = {} 46 | 47 | if direction == "horizontal" then 48 | for i = 1, #colors do 49 | local color = colors[i] 50 | local x = (i-1)/(#colors-1) 51 | table.insert(mesh_data, {x, 1, x, 1, color.r, color.g, color.b, color.a or 1}) 52 | table.insert(mesh_data, {x, 0, x, 0, color.r, color.g, color.b, color.a or 1}) 53 | end 54 | elseif direction == "vertical" then 55 | for i = 1, #colors do 56 | local color = colors[i] 57 | local y = (i-1)/(#colors-1) 58 | table.insert(mesh_data, {1, y, 1, y, color.r, color.g, color.b, color.a or 1}) 59 | table.insert(mesh_data, {0, y, 0, y, color.r, color.g, color.b, color.a or 1}) 60 | end 61 | end 62 | 63 | self.mesh = love.graphics.newMesh(mesh_data, "strip", "static") 64 | end 65 | 66 | 67 | -- Draws the gradient image with size w, h centered on x, y. 68 | function GradientImage:draw(x, y, w, h, r, sx, sy, ox, oy) 69 | graphics.push(x, y, r) 70 | love.graphics.draw(self.mesh, x - (sx or 1)*(w + (ox or 0))/2, y - (sy or 1)*(h + (oy or 0))/2, 0, w*(sx or 1), h*(sy or sx or 1)) 71 | graphics.pop() 72 | end 73 | -------------------------------------------------------------------------------- /engine/graphics/shader.lua: -------------------------------------------------------------------------------- 1 | -- The base Shader class. 2 | Shader = Object:extend() 3 | function Shader:init(vertex_name, fragment_name) 4 | self.shader = love.graphics.newShader("assets/shaders/" .. (vertex_name or "default.vert"), "assets/shaders/" .. fragment_name) 5 | end 6 | 7 | 8 | -- Sets this shader as the active one. 9 | function Shader:set() 10 | current_shader = self 11 | love.graphics.setShader(self.shader) 12 | end 13 | 14 | -- Unsets this shader as the active one. 15 | function Shader:unset() 16 | current_shader = nil 17 | love.graphics.setShader() 18 | end 19 | 20 | 21 | -- Takes in a parameter and the data that corresponds to it and sends it to the shader. 22 | -- shader:send('displacement_map', displacement_canvas) 23 | function Shader:send(value, data) 24 | if data:is(Canvas) then 25 | self.shader:send(value, data.canvas) 26 | elseif data:is(Image) then 27 | self.shader:send(value, data.image) 28 | else 29 | self.shader:send(value, data) 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /engine/graphics/tileset.lua: -------------------------------------------------------------------------------- 1 | -- The class responsible for loading and drawing tilesets. 2 | -- This is primarily used by the Tilemap class. 3 | Tileset = Object:extend() 4 | function Tileset:init(image, tile_w, tile_h) 5 | self.image = image 6 | self.tile_w, self.tile_h = tile_w, tile_h 7 | self.grid = Grid(math.floor(self.image.w/self.tile_w), math.floor(self.image.h/self.tile_h), 0) 8 | self.w, self.h = self.grid.w, self.grid.h 9 | for i = 1, self.grid.w do 10 | for j = 1, self.grid.h do 11 | self.grid:set(i, j, love.graphics.newQuad((i-1)*self.tile_w, (j-1)*self.tile_h, self.tile_w, self.tile_h, self.image.w, self.image.h)) 12 | end 13 | end 14 | end 15 | 16 | 17 | -- Draws a tile based on its tileset 1D index. 18 | -- If the tileset has width 10 (10 columns) then the index 11 corresponds to the first tile in the second row. 19 | function Tileset:draw_tile(index, x, y, r, sx, sy, ox, oy) 20 | love.graphics.draw(self.image.image, self:get_quad(index), x, y, r or 0, sx or 1, sy or sx or 1, ox or 0, oy or 0) 21 | end 22 | 23 | 24 | -- Returns the quad that represents the tile at the given index. 25 | function Tileset:get_quad(index) 26 | return self.grid:get(math.index_to_coordinates(index, self.w)) 27 | end 28 | -------------------------------------------------------------------------------- /engine/init.lua: -------------------------------------------------------------------------------- 1 | local path = ... 2 | if not path:find("init") then 3 | require(path .. ".datastructures.string") 4 | require(path .. ".datastructures.table") 5 | require(path .. ".external") 6 | require(path .. ".graphics.graphics") 7 | require(path .. ".game.object") 8 | require(path .. ".system") 9 | require(path .. ".datastructures.graph") 10 | require(path .. ".datastructures.grid") 11 | require(path .. ".game.gameobject") 12 | require(path .. ".game.group") 13 | require(path .. ".game.state") 14 | require(path .. ".game.physics") 15 | require(path .. ".game.steering") 16 | require(path .. ".graphics.animation") 17 | require(path .. ".graphics.camera") 18 | require(path .. ".graphics.canvas") 19 | require(path .. ".graphics.color") 20 | require(path .. ".graphics.font") 21 | require(path .. ".graphics.image") 22 | require(path .. ".graphics.shader") 23 | require(path .. ".graphics.text") 24 | require(path .. ".graphics.tileset") 25 | require(path .. ".map.solid") 26 | require(path .. ".map.tilemap") 27 | require(path .. ".math.polygon") 28 | require(path .. ".math.chain") 29 | require(path .. ".math.circle") 30 | require(path .. ".math.line") 31 | require(path .. ".math.math") 32 | require(path .. ".math.random") 33 | require(path .. ".math.rectangle") 34 | require(path .. ".math.spring") 35 | require(path .. ".math.triangle") 36 | require(path .. ".math.vector") 37 | require(path .. ".game.trigger") 38 | require(path .. ".game.input") 39 | require(path .. ".sound") 40 | require(path .. ".game.parent") 41 | require(path .. ".game.springs") 42 | require(path .. ".game.flashes") 43 | require(path .. ".game.hitfx") 44 | end 45 | 46 | function engine_run(config) 47 | if not web then 48 | love.filesystem.setIdentity(config.game_name) 49 | steam.init() 50 | system.load_state() 51 | 52 | local _, _, flags = love.window.getMode() 53 | local window_width, window_height = love.window.getDesktopDimensions(flags.display) 54 | if config.window_width ~= 'max' then window_width = config.window_width end 55 | if config.window_height ~= 'max' then window_height = config.window_height end 56 | 57 | local limits = love.graphics.getSystemLimits() 58 | local anisotropy = limits.anisotropy 59 | msaa = limits.canvasmsaa 60 | if config.msaa ~= 'max' then msaa = config.msaa end 61 | if config.anisotropy ~= 'max' then anisotropy = config.anisotropy end 62 | 63 | gw, gh = config.game_width or 480, config.game_height or 270 64 | sx, sy = window_width/(config.game_width or 480), window_height/(config.game_height or 270) 65 | ww, wh = window_width, window_height 66 | 67 | if state.sx and state.sy then 68 | sx, sy = state.sx, state.sy 69 | love.window.setMode(state.sx*gw, state.sy*gh, {vsync = config.vsync, msaa = msaa or 0, display = config.display}) 70 | else 71 | state.sx, state.sy = sx, sy 72 | love.window.setMode(window_width, window_height, {vsync = config.vsync, msaa = msaa or 0, display = config.display}) 73 | end 74 | love.window.setTitle(config.game_name) 75 | 76 | else 77 | gw, gh = config.game_width or 480, config.game_height or 270 78 | sx, sy = 2, 2 79 | ww, wh = 960, 540 80 | end 81 | 82 | love.window.setIcon(love.image.newImageData('assets/images/icon.png')) 83 | love.graphics.setBackgroundColor(0, 0, 0, 1) 84 | love.graphics.setColor(1, 1, 1, 1) 85 | love.joystick.loadGamepadMappings("engine/gamecontrollerdb.txt") 86 | graphics.set_line_style(config.line_style or "rough") 87 | graphics.set_default_filter(config.default_filter or "nearest", config.default_filter or "nearest", anisotropy or 0) 88 | 89 | combine = Shader("default.vert", "combine.frag") 90 | replace = Shader("default.vert", "replace.frag") 91 | full_combine = Shader("default.vert", "full_combine.frag") 92 | 93 | input = Input() 94 | input:bind_all() 95 | for k, v in pairs(config.input or {}) do input:bind(k, v) end 96 | random = Random() 97 | trigger = Trigger() 98 | camera = Camera(gw/2, gh/2) 99 | mouse = Vector(0, 0) 100 | last_mouse = Vector(0, 0) 101 | mouse_dt = Vector(0, 0) 102 | init() 103 | 104 | if love.timer then love.timer.step() end 105 | 106 | if not web then 107 | _, _, flags = love.window.getMode() 108 | fixed_dt = 1/flags.refreshrate 109 | else fixed_dt = 1/60 end 110 | 111 | local accumulator = fixed_dt 112 | local dt = 0 113 | frame, time = 0, 0 114 | 115 | if not web then refresh_rate = flags.refreshrate 116 | else refresh_rate = 60 end 117 | 118 | return function() 119 | if love.event then 120 | love.event.pump() 121 | for name, a, b, c, d, e, f in love.event.poll() do 122 | if name == "quit" then 123 | if not love.quit or not love.quit() then 124 | system.save_state() 125 | steam.shutdown() 126 | return a or 0 127 | end 128 | elseif name == "focus" then 129 | --[[] 130 | if main.current:is(Arena) then 131 | if not a then open_options(main.current) 132 | else close_options(main.current) end 133 | end 134 | ]]-- 135 | elseif name == "keypressed" then input.keyboard_state[a] = true; input.last_key_pressed = a 136 | elseif name == "keyreleased" then input.keyboard_state[a] = false 137 | elseif name == "mousepressed" then input.mouse_state[input.mouse_buttons[c]] = true; input.last_key_pressed = input.mouse_buttons[c] 138 | elseif name == "mousereleased" then input.mouse_state[input.mouse_buttons[c]] = false 139 | elseif name == "wheelmoved" then if b == 1 then input.mouse_state.wheel_up = true elseif b == -1 then input.mouse_state.wheel_down = true end 140 | elseif name == "gamepadpressed" then input.gamepad_state[input.index_to_gamepad_button[b]] = true; input.last_key_pressed = input.index_to_gamepad_button[b] 141 | elseif name == "gamepadreleased" then input.gamepad_state[input.index_to_gamepad_button[b]] = false 142 | elseif name == "gamepadaxis" then input.gamepad_axis[input.index_to_gamepad_axis[b]] = c 143 | elseif name == "textinput" then input:textinput(a) end 144 | end 145 | end 146 | 147 | if love.timer then dt = love.timer.step() end 148 | 149 | steam.runCallbacks() 150 | accumulator = accumulator + dt 151 | while accumulator >= fixed_dt do 152 | frame = frame + 1 153 | input:update(fixed_dt) 154 | trigger:update(fixed_dt) 155 | camera:update(fixed_dt) 156 | local mx, my = love.mouse.getPosition() 157 | mouse:set(mx/sx, my/sy) 158 | mouse_dt:set(mouse.x - last_mouse.x, mouse.y - last_mouse.y) 159 | update(fixed_dt) 160 | system.update() 161 | input.last_key_pressed = nil 162 | last_mouse:set(mouse.x, mouse.y) 163 | accumulator = accumulator - fixed_dt 164 | time = time + fixed_dt 165 | end 166 | 167 | if love.graphics and love.graphics.isActive() then 168 | love.graphics.origin() 169 | love.graphics.clear(love.graphics.getBackgroundColor()) 170 | draw() 171 | love.graphics.present() 172 | end 173 | 174 | if love.timer then love.timer.sleep(0.001) end 175 | end 176 | end 177 | -------------------------------------------------------------------------------- /engine/love/OpenAL32.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/engine/love/OpenAL32.dll -------------------------------------------------------------------------------- /engine/love/SDL2.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/engine/love/SDL2.dll -------------------------------------------------------------------------------- /engine/love/build_steam.bat: -------------------------------------------------------------------------------- 1 | call "C:\Program Files\7-Zip\7z.exe" a -r %1.zip -w ..\..\ -xr!engine/love -xr!builds -xr!steam -xr!.git -xr!*.moon -xr!conf.lua 2 | rename %1.zip %1.love 3 | copy /b "love.exe"+"%1.love" "%1.exe" 4 | del %1.love 5 | mkdir %1 6 | for %%I in (*.dll) do copy %%I %1\ 7 | for %%I in (*.txt) do copy %%I %1\ 8 | copy %1.exe %1\ 9 | del %1.exe 10 | copy %1\ ..\..\steam\ContentBuilder\content\ 11 | del /q %1\ 12 | rmdir /q %1\ 13 | -------------------------------------------------------------------------------- /engine/love/build_web.bat: -------------------------------------------------------------------------------- 1 | call "C:\Program Files\7-Zip\7z.exe" a -r %1.zip -w ..\..\ -xr!engine/love -xr!builds -xr!.git -xr!*.moon 2 | rename %1.zip %1.love 3 | call love-js -c -m 1073741824 -t %1 %2\engine\love\%1.love ..\..\builds\web 4 | del %1.love 5 | -------------------------------------------------------------------------------- /engine/love/build_windows.bat: -------------------------------------------------------------------------------- 1 | call "C:\Program Files\7-Zip\7z.exe" a -r %1.zip -w ..\..\ -xr!engine/love -xr!builds -xr!steam -xr!.git -xr!*.moon -xr!conf.lua 2 | rename %1.zip %1.love 3 | copy /b "love.exe"+"%1.love" "%1.exe" 4 | del %1.love 5 | mkdir %1 6 | for %%I in (*.dll) do copy %%I %1\ 7 | for %%I in (*.txt) do copy %%I %1\ 8 | copy %1.exe %1\ 9 | del %1.exe 10 | call "C:\Program Files\7-Zip\7z.exe" a %1.zip %1\ 11 | del /q %1\ 12 | rmdir /q %1\ 13 | copy %1.zip ..\..\builds\windows\ 14 | del %1.zip 15 | -------------------------------------------------------------------------------- /engine/love/game.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/engine/love/game.ico -------------------------------------------------------------------------------- /engine/love/love.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/engine/love/love.dll -------------------------------------------------------------------------------- /engine/love/love.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/engine/love/love.exe -------------------------------------------------------------------------------- /engine/love/love.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/engine/love/love.ico -------------------------------------------------------------------------------- /engine/love/lovec.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/engine/love/lovec.exe -------------------------------------------------------------------------------- /engine/love/lua51.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/engine/love/lua51.dll -------------------------------------------------------------------------------- /engine/love/luasteam.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/engine/love/luasteam.dll -------------------------------------------------------------------------------- /engine/love/mpg123.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/engine/love/mpg123.dll -------------------------------------------------------------------------------- /engine/love/msvcp120.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/engine/love/msvcp120.dll -------------------------------------------------------------------------------- /engine/love/msvcr120.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/engine/love/msvcr120.dll -------------------------------------------------------------------------------- /engine/love/polyclipping.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/engine/love/polyclipping.dll -------------------------------------------------------------------------------- /engine/love/readme.txt: -------------------------------------------------------------------------------- 1 | LÖVE is an *awesome* framework you can use to make 2D games in Lua. It's free, open-source, and works on Windows, Mac OS X, Linux, Android, and iOS. 2 | 3 | [![Build Status: Windows](https://ci.appveyor.com/api/projects/status/u1a69u5o5ej1pus4?svg=true)](https://ci.appveyor.com/project/AlexSzpakowski/love) 4 | 5 | Documentation 6 | ------------- 7 | 8 | We use our [wiki][wiki] for documentation. 9 | If you need further help, feel free to ask on our [forums][forums], our [Discord server][discord], or our IRC channel [#love on OFTC][irc]. 10 | 11 | Compilation 12 | ----------- 13 | 14 | ### Windows 15 | Follow the instructions at the [megasource][megasource] repository page. 16 | 17 | ### *nix 18 | Run `platform/unix/automagic` from the repository root, then run ./configure and make. 19 | 20 | $ platform/unix/automagic 21 | $ ./configure 22 | $ make 23 | 24 | When using a source release, automagic has already been run, and the first step can be skipped. 25 | 26 | ### macOS 27 | Download the required frameworks from [here][dependencies] and place them in `/Library/Frameworks/`. 28 | 29 | Then use the Xcode project found at `platform/xcode/love.xcodeproj` to build the `love-macosx` target. 30 | 31 | ### iOS 32 | Download the `ios-libraries` zip file corresponding to the LÖVE version being used from [here][dependencies-ios], 33 | unzip it, and place the `include` and `libraries` subfolders into LÖVE's `platform/xcode/ios` folder. 34 | 35 | Then use the Xcode project found at `platform/xcode/love.xcodeproj` to build the `love-ios` target. 36 | 37 | See `readme-iOS.rtf` for more information. 38 | 39 | ### Android 40 | Visit the [Android build repository][android-repository] for build instructions. 41 | 42 | Repository information 43 | ---------------------- 44 | 45 | We use the 'default' branch for development, and therefore it should not be considered stable. 46 | Also used is the 'minor' branch, which is used for features in the next minor version and it is 47 | not our development target (which would be the next revision - version numbers are formatted major.minor.revision.) 48 | 49 | We tag all our releases (since we started using mercurial), and have binary downloads available for them. 50 | 51 | Experimental changes are developed in the separate [love-experiments][love-experiments] repository. 52 | 53 | Contributing 54 | ------------ 55 | 56 | The best places to contribute are through the Bitbucket issue tracker and the official Discord server or IRC channel. 57 | For code contributions, pull requests and patches are welcome. Be sure to read the [source code style guide][codestyle]. 58 | 59 | Builds 60 | ------ 61 | 62 | Releases are found in the 'downloads' section on bitbucket, are linked on [the site][site], 63 | and there's a ppa for ubuntu, [ppa:bartbes/love-stable][stableppa]. 64 | 65 | There are also unstable/nightly builds: 66 | 67 | - Most can be found [here][builds]. 68 | - For ubuntu linux they are in [ppa:bartbes/love-unstable][unstableppa] 69 | - For arch linux there's [love-hg][aur] in the AUR. 70 | 71 | Dependencies 72 | ------------ 73 | 74 | - SDL2 75 | - OpenGL 2.1+ / OpenGL ES 2+ 76 | - OpenAL 77 | - Lua / LuaJIT / LLVM-lua 78 | - FreeType 79 | - ModPlug 80 | - mpg123 81 | - Vorbisfile 82 | - Theora 83 | 84 | [site]: http://love2d.org 85 | [wiki]: http://love2d.org/wiki 86 | [forums]: http://love2d.org/forums 87 | [discord]: https://discord.gg/rhUets9 88 | [irc]: irc://irc.oftc.net/love 89 | [dependencies]: http://love2d.org/sdk 90 | [dependencies-ios]: https://bitbucket.org/rude/love/downloads/ 91 | [megasource]: https://bitbucket.org/rude/megasource 92 | [builds]: http://love2d.org/builds 93 | [stableppa]: https://launchpad.net/~bartbes/+archive/love-stable 94 | [unstableppa]: https://launchpad.net/~bartbes/+archive/love-unstable 95 | [aur]: http://aur.archlinux.org/packages/love-hg 96 | [love-experiments]: https://bitbucket.org/bartbes/love-experiments 97 | [codestyle]: https://love2d.org/wiki/Code_Style 98 | [android-repository]: https://bitbucket.org/MartinFelis/love-android-sdl2 99 | -------------------------------------------------------------------------------- /engine/love/steam_api64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a327ex/SNKRX/6b93a64d694d59472375467648868ae4521d6706/engine/love/steam_api64.dll -------------------------------------------------------------------------------- /engine/love/steam_appid.txt: -------------------------------------------------------------------------------- 1 | 915310 2 | -------------------------------------------------------------------------------- /engine/love/steam_rich_presence.txt: -------------------------------------------------------------------------------- 1 | "lang" 2 | { 3 | "english" 4 | { 5 | "tokens" 6 | { 7 | "#StatusFull" "%text%" 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /engine/map/solid.lua: -------------------------------------------------------------------------------- 1 | Solid = Object:extend() 2 | Solid:implement(GameObject) 3 | Solid:implement(Physics) 4 | 5 | 6 | function Solid:init(args) 7 | self:init_game_object(args) 8 | self:set_as_chain(true, self.vertices, 'static', 'solid') 9 | end 10 | 11 | 12 | function Solid:update(dt) 13 | self:update_game_object(dt) 14 | end 15 | 16 | 17 | function Solid:draw() 18 | self:draw_game_object() 19 | end 20 | -------------------------------------------------------------------------------- /engine/map/tilemap.lua: -------------------------------------------------------------------------------- 1 | -- A tilemap class responsible for the loading and drawing of maps made of tiles. 2 | -- The x, y coordinates passed in represent the center of the map. 3 | -- A tileset object containing the tileset to be used by the map must be passed in, as well as a grid object containing the map itself. 4 | -- If tile_rules is also passed in, then the grid instance must contain its values in relation to what the tiling rules expect. 5 | -- If no tiling rules are present then the grid will contain the 1D index of each tile in the tileset, so if the tileset has 5 columns, the first element in the second row will be numbered 6 in the grid. 6 | -- If solid_rules is passed in, then the that function should receive a tile index and return true on those that are supposed to form a solid object, for instance: 7 | -- function(index) return index ~= 0 end would make every tile index that isn't 0 into a solid object. 8 | -- The created solids are stored in .solids after the tilemap instance is created. They should be added to the same group that you want gameplay objects to collide with the map's solid walls. 9 | -- Solids are evaluated before tile rules are applied to the tile grid. 10 | Tilemap = Object:extend() 11 | function Tilemap:init(x, y, tileset, tile_grid, tile_rules, solid_rules) 12 | self.tileset = tileset 13 | self.grid = tile_grid 14 | self.x, self.y = x, y 15 | self.w, self.h = self.grid.w*self.tileset.tile_w, self.grid.h*self.tileset.tile_h 16 | self.tile_rules = tile_rules 17 | self.solid_rules = solid_rules 18 | 19 | -- Create solids from the tile grid 20 | local tw, th = self.tileset.tile_w, self.tileset.tile_h 21 | if self.solid_rules then 22 | local ox, oy = self.x - self.w/2, self.y - self.h/2 23 | local tile_polygons = {} 24 | for i = 1, self.grid.w do 25 | for j = 1, self.grid.h do 26 | if self.solid_rules(self.grid:get(i, j)) then 27 | table.insert(tile_polygons, Polygon({ox + (i-1)*tw, oy + (j-1)*th, ox + i*tw, oy + (j-1)*th, ox + i*tw, oy + j*th, ox + (i-1)*tw, oy + j*th})) 28 | end 29 | end 30 | end 31 | self.solids_vertices = {} 32 | for _, solid in ipairs(Polygon.merge_polygons(tile_polygons)) do 33 | table.insert(self.solids_vertices, solid) 34 | end 35 | end 36 | 37 | if self.tile_rules:is(TilekitRules) then 38 | self.grid = Grid(self.grid.w, self.grid.h, self.tile_rules.process({w = self.grid.w, h = self.grid.h, data = self.grid.grid}).data) 39 | end 40 | 41 | -- Create spritebatch 42 | self.spritebatch = love.graphics.newSpriteBatch(self.tileset.image.image, 8192, 'static') 43 | for i = 1, self.grid.w do 44 | for j = 1, self.grid.h do 45 | local v = self.grid:get(i, j) 46 | if v ~= 0 then 47 | self.spritebatch:add(self.tileset:get_quad(v), (i-1)*tw, (j-1)*th) 48 | end 49 | end 50 | end 51 | end 52 | 53 | 54 | function Tilemap:draw(r, sx, sy, ox, oy) 55 | love.graphics.draw(self.spritebatch, self.x - self.w/2, self.y - self.h/2, r or 0, sx or 1, sy or sx or 1, ox or 0, oy or 0) 56 | end 57 | 58 | 59 | 60 | 61 | -- This class is responsible for loading tile rules exported by rxi's Tilekit https://rxi.itch.io/tilekit 62 | -- An instance of this class should be passed in as the third argument for a Tilemap instance 63 | TilekitRules = Object:extend() 64 | function TilekitRules:init(rules_filename) 65 | self.process = require('assets/maps/' .. rules_filename) 66 | end 67 | -------------------------------------------------------------------------------- /engine/math/chain.lua: -------------------------------------------------------------------------------- 1 | -- A chain class. If loop is true then this is the same as a polygon, otherwise its a collection of connected lines (an open polygon). 2 | -- Implements every function that Polygon does. 3 | Chain = Object:extend() 4 | Chain:implement(Polygon) 5 | function Chain:init(loop, vertices) 6 | self.loop = loop 7 | self.vertices = vertices 8 | self:get_size() 9 | self:get_bounds() 10 | self:get_centroid() 11 | end 12 | 13 | 14 | -- Draws the chain of lines with the given color and width. 15 | function Chain:draw(color, line_width) 16 | if self.loop and not line_width then 17 | graphics.polygon(self.vertices, color, line_width) 18 | else 19 | for i = 1, #self.vertices-2, 2 do 20 | graphics.line(self.vertices[i], self.vertices[i+1], self.vertices[i+2], self.vertices[i+3], color, line_width) 21 | if self.loop and i == #self.vertices-2 then 22 | graphics.line(self.vertices[i], self.vertices[i+1], self.vertices[1], self.vertices[2], color, line_width) 23 | end 24 | end 25 | end 26 | end 27 | 28 | 29 | -- If loop is true, returns true if the point is inside the polygon. 30 | -- If loop is false, returns true if the point lies on any of the chain's lines. 31 | -- colliding = chain:is_colliding_with_point(x, y) 32 | function Chain:is_colliding_with_point(x, y) 33 | if self.loop then 34 | return mlib.polygon.checkPoint(x, y, self.vertices) 35 | else 36 | for i = 1, #self.vertices-2, 2 do 37 | if mlib.segment.checkPoint(x, y, self.vertices[i], self.vertices[i+1], self.vertices[i+2], self.vertices[i+3]) then 38 | return true 39 | end 40 | end 41 | end 42 | end 43 | 44 | 45 | -- If loop is true, returns true if the line is colliding with the polygon. 46 | -- If loop is false, returns true if the line is colliding with any of the chain's lines. 47 | -- colliding = chain:is_colliding_with_line(line) 48 | function Chain:is_colliding_with_line(line) 49 | if self.loop then 50 | return mlib.polygon.isSegmentInside(line.x1, line.y1, line.x2, line.y2, self.vertices) 51 | else 52 | for i = 1, #self.vertices-2, 2 do 53 | if mlib.segment.getIntersection(self.vertices[i], self.vertices[i+1], self.vertices[i+2], self.vertices[i+3], line.x1, line.y1, line.x2, line.y2) then 54 | return true 55 | end 56 | end 57 | end 58 | end 59 | 60 | 61 | -- If loop is true for both chains, returns true if the polygon is colliding with the polygon. 62 | -- If loop is false for both chains, returns true if any of the lines of one chain are colliding with any of the lines of the other chain. 63 | -- If one is true and the other is false, returns true if the one that's a polygon is colliding with any of the lines of the one that is a chain. 64 | -- colliding = chain:is_colliding_with_chain(other_chain) 65 | function Chain:is_colliding_with_chain(chain) 66 | if self.loop and chain.loop then 67 | return mlib.polygon.isPolygonInside(self.vertices, chain.vertices) or mlib.polygon.isPolygonInside(chain.vertices, self.vertices) 68 | elseif self.loop and not chain.loop then 69 | return chain:is_colliding_with_polygon(self) 70 | elseif not self.loop and chain.loop then 71 | return self:is_colliding_with_polygon(chain) 72 | else 73 | for i = 1, #self.vertices-2, 2 do 74 | for j = 1, #chain.vertices-2, 2 do 75 | if mlib.segment.getIntersection(self.vertices[i], self.vertices[i+1], self.vertices[i+2], self.vertices[i+3], chain.vertices[j], chain.vertices[j+1], chain.vertices[j+2], chain.vertices[j+3]) then 76 | return true 77 | end 78 | end 79 | end 80 | end 81 | end 82 | 83 | 84 | -- If loop is true, returns true if the circle is colliding with the polygon. 85 | -- If loop is false, returns true if the circle is colliding with any of the chain's lines. 86 | -- colliding = chain:is_colliding_with_circle(circle) 87 | function Chain:is_colliding_with_circle(circle) 88 | if self.loop then 89 | return mlib.polygon.isCircleCompletelyInside(circle.x, circle.y, circle.rs, self.vertices) or 90 | mlib.circle.isPolygonCompletelyInside(circle.x, circle.y, circle.rs, self.vertices) or 91 | mlib.polygon.getCircleIntersection(circle.x, circle.y, circle.rs, self.vertices) 92 | else 93 | for i = 1, #self.vertices-2, 2 do 94 | if mlib.circle.getSegmentIntersection(circle.x, circle.y, circle.rs, self.vertices[i], self.vertices[i+1], self.vertices[i+2], self.vertices[i+3]) then 95 | return true 96 | end 97 | end 98 | end 99 | end 100 | 101 | 102 | -- If loop is true, returns true if the polygon is colliding with the polygon. 103 | -- If loop is false, returns true if the polygon is colliding with any of the chain's lines. 104 | -- colliding = chain:is_colliding_with_polygon(polygon) 105 | function Chain:is_colliding_with_polygon(polygon) 106 | if self.loop then 107 | return mlib.polygon.isPolygonInside(self.vertices, polygon.vertices) or mlib.polygon.isPolygonInside(polygon.vertices, self.vertices) 108 | else 109 | for i = 1, #self.vertices-2, 2 do 110 | if mlib.polygon.getSegmentIntersection(self.vertices[i], self.vertices[i+1], self.vertices[i+2], self.vertices[i+3], polygon.vertices) then 111 | return true 112 | end 113 | end 114 | end 115 | end 116 | -------------------------------------------------------------------------------- /engine/math/circle.lua: -------------------------------------------------------------------------------- 1 | -- A circle class. 2 | Circle = Object:extend() 3 | function Circle:init(x, y, rs) 4 | self.x, self.y = x, y 5 | self.rs = rs 6 | self.w, self.h = 2*self.rs, 2*self.rs 7 | self.x1, self.y1, self.x2, self.y2 = self.x - self.rs, self.y - self.rs, self.x + self.rs, self.y + self.rs 8 | end 9 | 10 | 11 | -- Draws the circle . 12 | -- If color is passed in then the circle will be filled with that color (color is a Color instance) 13 | -- If line_width is passed in then the circle will not be filled and will instead be drawn as a set of lines of the given width. 14 | function Circle:draw(color, line_width) 15 | graphics.circle(self.x, self.y, self.rs, color, line_width) 16 | end 17 | 18 | 19 | -- Moves the circle directly to the given position. 20 | -- circle:move_to(20, 20) -> moves the circle to position 20, 20 21 | function Circle:move_to(x, y) 22 | self.x, self.y = x, y 23 | end 24 | 25 | 26 | -- Returns true if this polygon is colliding with the given shape. 27 | -- colliding = polygon:is_colliding_with_shape(shape) 28 | function Circle:is_colliding_with_shape(shape) 29 | if shape:is(Line) then 30 | return self:is_colliding_with_line(shape) 31 | elseif shape:is(Chain) then 32 | return self:is_colliding_with_chain(shape) 33 | elseif shape:is(Circle) then 34 | return self:is_colliding_with_circle(shape) 35 | elseif shape:is(Polygon) then 36 | return self:is_colliding_with_polygon(shape) 37 | elseif shape:is(Rectangle) then 38 | return self:is_colliding_with_polygon(shape) 39 | elseif shape:is(EmeraldRectangle) then 40 | return self:is_colliding_with_polygon(shape) 41 | elseif shape:is(Triangle) then 42 | return self:is_colliding_with_polygon(shape) 43 | elseif shape:is(EquilateralTriangle) then 44 | return self:is_colliding_with_polygon(shape) 45 | end 46 | end 47 | 48 | 49 | -- Returns true if the point is inside the circle. 50 | -- colliding = polygon:is_colliding_with_point(x, y) 51 | function Circle:is_colliding_with_point(x, y) 52 | return mlib.circle.checkPoint(x, y, self.x, self.y, self.rs) 53 | end 54 | 55 | 56 | -- Returns true if the line is colliding with this circle. 57 | -- colliding = circle:is_colliding_with_line(line) 58 | function Circle:is_colliding_with_line(line) 59 | return mlib.circle.getSegmentIntersection(self.x, self.y, self.rs, line.x1, line.y1, line.x2, line.y2) 60 | end 61 | 62 | 63 | -- Returns true if the chain is colliding with this circle. 64 | -- colliding = circle:is_colliding_with_chain(chain) 65 | function Circle:is_colliding_with_chain(chain) 66 | return chain:is_colliding_with_circle(self) 67 | end 68 | 69 | 70 | -- Returns true if the circle is colliding with this circle. 71 | -- colliding = circle:is_colliding_with_circle(other_circle) 72 | function Circle:is_colliding_with_circle(circle) 73 | return mlib.circle.isCircleCompletelyInside(self.x, self.y, self.rs, circle.x, circle.y, circle.rs) or 74 | mlib.circle.isCircleCompletelyInside(circle.x, circle.y, circle.rs, self.x, self.y, self.rs) or 75 | mlib.circle.getCircleIntersection(self.x, self.y, self.rs, circle.x, circle.y, circle.rs) 76 | end 77 | 78 | 79 | -- Returns true if the polygon is colliding with this circle. 80 | -- colliding = circle:is_colliding_with_polygon(polygon) 81 | function Circle:is_colliding_with_polygon(polygon) 82 | return mlib.polygon.isCircleCompletelyInside(self.x, self.y, self.rs, polygon.vertices) or 83 | mlib.circle.isPolygonCompletelyInside(self.x, self.y, self.rs, polygon.vertices) or 84 | mlib.polygon.getCircleIntersection(self.x, self.y, self.rs, polygon.vertices) 85 | end 86 | -------------------------------------------------------------------------------- /engine/math/line.lua: -------------------------------------------------------------------------------- 1 | -- A line class. 2 | -- Implements every function that Polygon does. 3 | Line = Object:extend() 4 | Line:implement(Polygon) 5 | function Line:init(x1, y1, x2, y2) 6 | self.x1, self.y1, self.x2, self.y2 = x1, y1, x2, y2 7 | self.x, self.y = (self.x1 + self.x2)/2, (self.y1 + self.y2)/2 8 | self.vertices = {x1, y1, x2, y2} 9 | self:get_size() 10 | self:get_bounds() 11 | self:get_centroid() 12 | end 13 | 14 | 15 | -- Draws the line with the given color and width. 16 | function Line:draw(color, line_width) 17 | graphics.line(self.x1, self.y1, self.x2, self.y2, color, line_width) 18 | end 19 | 20 | 21 | -- Noisifies the line, returning a Chain instance with the results. 22 | -- offset corresponds to the maximum amount of perpendicular offseting each line will have 23 | -- generations corresponds to the number of times the line will be subdivided 24 | -- The higher the number of generations, the higher the number of final lines generates and the more granular the noisification will be 25 | -- line:noisify(15, 4) -> noisifies the line with 15 maximum units of offseting with 4 generations 26 | function Line:noisify(offset, generations) 27 | local lines = {} 28 | local generations = generations or 4 29 | local offset = offset or 8 30 | table.insert(lines, {x1 = self.x1, y1 = self.y1, x2 = self.x2, y2 = self.y2}) 31 | 32 | for i = 1, generations do 33 | for i = #lines, 1, -1 do 34 | local spx, spy = lines[i].x1, lines[i].y1 35 | local epx, epy = lines[i].x2, lines[i].y2 36 | table.remove(lines, i) 37 | 38 | local mpx, mpy = (spx + epx)/2, (spy + epy)/2 39 | local pnx, pny = Vector(epx - spx, epy - spy):normalize():perpendicular():unpack() 40 | mpx = mpx + pnx*random:float(-offset, offset) 41 | mpy = mpy + pny*random:float(-offset, offset) 42 | table.insert(lines, i, {x1 = spx, y1 = spy, x2 = mpx, y2 = mpy}) 43 | table.insert(lines, i+1, {x1 = mpx, y1 = mpy, x2 = epx, y2 = epy}) 44 | end 45 | offset = offset/2 46 | end 47 | 48 | local vertices = {} 49 | for i, line in ipairs(lines) do 50 | if i == #lines then 51 | table.insert(vertices, line.x1) 52 | table.insert(vertices, line.y1) 53 | table.insert(vertices, line.x2) 54 | table.insert(vertices, line.y2) 55 | else 56 | table.insert(vertices, line.x1) 57 | table.insert(vertices, line.y1) 58 | end 59 | end 60 | return Chain(false, vertices) 61 | end 62 | 63 | 64 | -- Returns true if the point lies on the line. 65 | -- colliding = line:is_colliding_with_point(x, y) 66 | function Line:is_colliding_with_point(x, y) 67 | return mlib.segment.checkPoint(x, y, self.x1, self.y1, self.x2, self.y2) 68 | end 69 | 70 | 71 | -- Returns true if the line is colliding with this line. 72 | -- colliding = line:is_colliding_with_line(other_line) 73 | function Line:is_colliding_with_line(line) 74 | return mlib.segment.getIntersection(self.x1, self.y1, self.x2, self.y2, line.x1, line.y1, line.x2, line.y2) 75 | end 76 | 77 | 78 | -- Returns true if the chain is colliding with this line. 79 | -- colliding = line:is_colliding_with_chain(chain) 80 | function Line:is_colliding_with_chain(chain) 81 | return chain:is_colliding_with_line(self) 82 | end 83 | 84 | 85 | -- Returns true if the circle is colliding with this line. 86 | -- colliding = line:is_colliding_with_circle(circle) 87 | function Line:is_colliding_with_circle(circle) 88 | return mlib.circle.getSegmentIntersection(circle.x, circle.y, circle.rs, self.x1, self.y1, self.x2, self.y2) 89 | end 90 | 91 | 92 | -- Returns true if the polygon is colliding with this line . 93 | -- colliding = line:is_colliding_with_polygon(polygon) 94 | function Line:is_colliding_with_polygon(polygon) 95 | return mlib.polygon.getSegmentIntersection(self.x1, self.y1, self.x2, self.y2, polygon.vertices) 96 | end 97 | -------------------------------------------------------------------------------- /engine/math/random.lua: -------------------------------------------------------------------------------- 1 | -- The base random class. 2 | -- You can create a new Random object with its own seed by passing it in on the constructor. 3 | -- A global instance of this called "random" is available by default. 4 | Random = Object:extend() 5 | function Random:init(seed) 6 | seed = seed or os.time() 7 | self.generator = love.math.newRandomGenerator(seed) 8 | end 9 | 10 | 11 | -- Returns true at the given chance. 12 | -- random:bool(50) -> returns true 50% of the time 13 | -- random:bool(25) -> returns true 25% of the time 14 | -- random:bool(3) -> returns true 3% of the time 15 | function Random:bool(chance) 16 | if self.generator:random(1, 1000) < 10*(chance or 50) then 17 | return true 18 | end 19 | end 20 | 21 | 22 | -- Returns a random real number between the range. 23 | -- random:float(0, 1) -> returns a random number between 0 and 1, like 0.432 24 | -- random:float(-100, 45.2) -> returns a random number between -100 and 45.2, like -99.7 25 | function Random:float(min, max) 26 | min = min or 0 27 | max = max or 1 28 | return (min > max and (self.generator:random()*(min - max) + max)) or (self.generator:random()*(max - min) + min) 29 | end 30 | 31 | 32 | -- Returns a random integer number between the range. 33 | -- random:int(1, 7) -> returns a random integer between 1 and 7, like 4 34 | -- random:int(-2, 0) -> returns a random integer between -2 and 0, like -2 35 | function Random:int(min, max) 36 | return self.generator:random(min or 0, max or 1) 37 | end 38 | 39 | 40 | -- Returns a random value of the table. 41 | -- a = {7, 6, 5, 4} 42 | -- random:table(a) -> returns either 7, 6, 5 or 4 randomly 43 | function Random:table(t) 44 | return t[self.generator:random(1, #t)] 45 | end 46 | 47 | -- Returns a random value of the table and also removes it. 48 | -- a = {7, 6, 5, 4} 49 | -- random:table_remove(a) -> returns either 7, 6, 5 or 4 randomly and removes it from the table as well 50 | function Random:table_remove(t) 51 | return table.remove(t, self.generator:random(1, #t)) 52 | end 53 | 54 | 55 | -- Returns a 1 at the given chance, otherwise returns -1. 56 | -- random:sign(65) -> returns 1 65% of the time and -1 35% of the time 57 | -- random:sign(20) -> returns 1 20% of the time and -1 80% of the time 58 | function Random:sign(chance) 59 | if self.generator:random(1, 1000) < 10*(chance or 50) then return 1 60 | else return -1 end 61 | end 62 | 63 | 64 | -- Returns a random index at the given weights. 65 | -- random:weighted_pick(50, 30, 20) -> will return 1 50% of the time, 2 30% of the time and 3 20% of the time 66 | -- random:weighted_pick(10, 8, 2) -> will return 1 50% of the time, 2 40% of the time and 3 10% of the time 67 | -- random:weighted_pick(2, 1) -> will return 1 66% of the time, will return 2 33% of the time 68 | function Random:weighted_pick(...) 69 | local weights = {...} 70 | local total_weight = 0 71 | local pick = 0 72 | for _, weight in ipairs(weights) do total_weight = total_weight + weight end 73 | 74 | total_weight = self:float(0, total_weight) 75 | for i = 1, #weights do 76 | if total_weight < weights[i] then 77 | pick = i 78 | break 79 | end 80 | total_weight = total_weight - weights[i] 81 | end 82 | return pick 83 | end 84 | 85 | 86 | -- Returns a unique identifier. 87 | function Random:uid() 88 | local fn = function(x) 89 | local r = self:int(1, 16) - 1 90 | r = (x == "x") and (r + 1) or (r % 4) + 9 91 | return ("0123456789abcdef"):sub(r, r) 92 | end 93 | return (("xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"):gsub("[xy]", fn)) 94 | end 95 | -------------------------------------------------------------------------------- /engine/math/rectangle.lua: -------------------------------------------------------------------------------- 1 | -- A rectangle class. 2 | -- Implements every function that Polygon does. 3 | Rectangle = Object:extend() 4 | Rectangle:implement(Polygon) 5 | function Rectangle:init(x, y, w, h, r) 6 | self.x, self.y, self.w, self.h, self.r = x, y, w, h, r 7 | local x1, y1 = math.rotate_point(x - w/2, y - h/2, r or 0, x, y) 8 | local x2, y2 = math.rotate_point(x + w/2, y - h/2, r or 0, x, y) 9 | local x3, y3 = math.rotate_point(x + w/2, y + h/2, r or 0, x, y) 10 | local x4, y4 = math.rotate_point(x - w/2, y + h/2, r or 0, x, y) 11 | self.vertices = {x1, y1, x2, y2, x3, y3, x4, y4} 12 | self:get_size() 13 | self:get_bounds() 14 | self:get_centroid() 15 | end 16 | 17 | 18 | 19 | 20 | -- An emerald rectangle class. This is a rectangle with its corners cut by the given rx, ry amount. 21 | -- Implements every function that Polygon does. 22 | EmeraldRectangle = Object:extend() 23 | EmeraldRectangle:implement(Polygon) 24 | function EmeraldRectangle:init(x, y, w, h, rx, ry, r) 25 | self.x, self.y, self.w, self.h, self.r = x, y, w, h, r 26 | self.rx, self.ry = rx, ry 27 | local x1, y1 = math.rotate_scale_point(x - w/2, y - h/2 + ry, r or 0, x, y) 28 | local x2, y2 = math.rotate_scale_point(x - w/2 + rx, y - h/2, r or 0, x, y) 29 | local x3, y3 = math.rotate_scale_point(x + w/2 - rx, y - h/2, r or 0, x, y) 30 | local x4, y4 = math.rotate_scale_point(x + w/2, y - h/2 + ry, r or 0, x, y) 31 | local x5, y5 = math.rotate_scale_point(x + w/2, y + h/2 - ry, r or 0, x, y) 32 | local x6, y6 = math.rotate_scale_point(x + w/2 - rx, y + h/2, r or 0, x, y) 33 | local x7, y7 = math.rotate_scale_point(x - w/2 + rx, y + h/2, r or 0, x, y) 34 | local x8, y8 = math.rotate_scale_point(x - w/2, y + h/2 - ry, r or 0, x, y) 35 | self.vertices = {x1, y1, x2, y2, x3, y3, x4, y4, x5, y5, x6, y6, x7, y7, x8, y8} 36 | self:get_size() 37 | self:get_bounds() 38 | self:get_centroid() 39 | end 40 | -------------------------------------------------------------------------------- /engine/math/spring.lua: -------------------------------------------------------------------------------- 1 | -- A Spring class. This is extremely useful for juicing things up. 2 | -- See this article https://github.com/a327ex/blog/issues/60 for more details. 3 | -- The argument passed in are: the initial value of the spring, its stiffness and damping. 4 | Spring = Object:extend() 5 | function Spring:init(x, k, d) 6 | self.x = x or 0 7 | self.k = k or 100 8 | self.d = d or 10 9 | self.target_x = self.x 10 | self.v = 0 11 | end 12 | 13 | 14 | function Spring:update(dt) 15 | local a = -self.k*(self.x - self.target_x) - self.d*self.v 16 | self.v = self.v + a*dt 17 | self.x = self.x + self.v*dt 18 | end 19 | 20 | 21 | -- Pull the spring with a certain amount of force. This force should be related to the initial value you set to the spring. 22 | function Spring:pull(f, k, d) 23 | if k then self.k = k end 24 | if d then self.d = d end 25 | self.x = self.x + f 26 | end 27 | 28 | 29 | -- Animates the spring such that it reaches the target value in a smoothy springy motion. 30 | -- Unlike pull, which tugs on the spring so that it bounces around the anchor, this changes that anchor itself. 31 | function Spring:animate(x, k, d) 32 | if k then self.k = k end 33 | if d then self.d = d end 34 | self.target_x = x 35 | end 36 | 37 | 38 | 39 | 40 | --[[ 41 | NSpring = Object:extend() 42 | 43 | 44 | function NSpring:new(x, z, o) 45 | self.x = x or 0 46 | self.z = z or 0.5 47 | self.o = o or 2*math.pi 48 | self.target_x = self.x 49 | self.v = 0 50 | end 51 | 52 | 53 | function NSpring:update(dt) 54 | local f = 1 + 2*dt*self.z*self.o 55 | local oo = self.o*self.o 56 | local hoo = dt*oo 57 | local hhoo = dt*hoo 58 | local det_inv = 1/(f+hhoo) 59 | local det_x = f*self.x + dt*self.v + hhoo*self.target_x 60 | local det_v = self.v + hoo*(self.target_x - self.x) 61 | self.x = det_x*det_inv 62 | self.v = det_v*det_inv 63 | end 64 | 65 | 66 | function NSpring:animate(target_x, pd, td) 67 | self.z = math.log(pd)/(self.o*td) 68 | self.target_x = target_x 69 | end 70 | ]]-- 71 | -------------------------------------------------------------------------------- /engine/math/triangle.lua: -------------------------------------------------------------------------------- 1 | -- An isosceles triangle class. This is a triangle with size w, h centered on x, y pointed to the right (angle 0). 2 | -- Implements every function that Polygon does. 3 | Triangle = Object:extend() 4 | Triangle:implement(Polygon) 5 | function Triangle:init(x, y, w, h) 6 | self.x, self.y, self.w, self.h = x, y, w, h 7 | local x1, y1 = x + h/2, y 8 | local x2, y2 = x - h/2, y - w/2 9 | local x3, y3 = x - h/2, y + w/2 10 | self.vertices = {x1, y1, x2, y2, x3, y3} 11 | self:get_size() 12 | self:get_bounds() 13 | self:get_centroid() 14 | end 15 | 16 | 17 | -- An equilateral triangle class. This is a tringle with size w centered on x, y pointed to the right (angle 0). 18 | -- Implements every function that Polygon does. 19 | EquilateralTriangle = Object:extend() 20 | EquilateralTriangle:implement(Polygon) 21 | function EquilateralTriangle:init(x, y, w) 22 | self.x, self.y, self.w = x, y, w 23 | local h = math.sqrt(math.pow(w, 2) - math.pow(w/2, 2)) 24 | local x1, y1 = x + h/2, y 25 | local x2, y2 = x - h/2, y - w/2 26 | local x3, y3 = x - h/2, y + w/2 27 | self.vertices = {x1, y1, x2, y2, x3, y3} 28 | self:get_size() 29 | self:get_bounds() 30 | self:get_centroid() 31 | end 32 | -------------------------------------------------------------------------------- /engine/math/vector.lua: -------------------------------------------------------------------------------- 1 | -- The base Vector class. 2 | local EPSILON = 0.0001 3 | local EPSILON_SQUARED = EPSILON*EPSILON 4 | Vector = Object:extend() 5 | function Vector:init(x, y) 6 | self.x = x or 0 7 | self.y = y or x or 0 8 | end 9 | 10 | 11 | function Vector:clone() 12 | return Vector(self.x, self.y) 13 | end 14 | 15 | 16 | function Vector:unpack() 17 | return self.x, self.y 18 | end 19 | 20 | 21 | function Vector.__tostring(self) 22 | return "(" .. tonumber(self.x) .. ", " .. tonumber(self.y) .. ")" 23 | end 24 | 25 | 26 | function Vector:set(x, y) 27 | if not y then 28 | self.x = x.x 29 | self.y = x.y 30 | else 31 | self.x = x 32 | self.y = y 33 | end 34 | return self 35 | end 36 | 37 | 38 | function Vector:add(x, y) 39 | if not y then 40 | self.x = self.x + x.x 41 | self.y = self.y + x.y 42 | else 43 | self.x = self.x + x 44 | self.y = self.y + y 45 | end 46 | return self 47 | end 48 | 49 | 50 | function Vector:sub(x, y) 51 | if not y then 52 | self.x = self.x - x.x 53 | self.y = self.y - x.y 54 | else 55 | self.x = self.x - x 56 | self.y = self.y - y 57 | end 58 | return self 59 | end 60 | 61 | 62 | function Vector:mul(s) 63 | if type(s) == "table" then 64 | self.x = self.x*s.x 65 | self.y = self.y*s.y 66 | else 67 | self.x = self.x*s 68 | self.y = self.y*s 69 | end 70 | return self 71 | end 72 | 73 | 74 | function Vector:div(s) 75 | if type(s) == "table" then 76 | self.x = self.x*s.x 77 | self.y = self.y*s.y 78 | else 79 | self.x = self.x/s 80 | self.y = self.y/s 81 | end 82 | return self 83 | end 84 | 85 | 86 | function Vector:scale(k) 87 | self.x = self.x*k 88 | self.y = self.y*k 89 | return self 90 | end 91 | 92 | 93 | function Vector:rotate(p, r) 94 | local cos = math.cos(r) 95 | local sin = math.sin(r) 96 | local dx = self.x - p.x 97 | local dy = self.y - p.y 98 | self.x = dx*cos - sin*dy + p.x 99 | self.y = sin*dx + cos*dy + p.y 100 | return self 101 | end 102 | 103 | 104 | function Vector:floor() 105 | self.x = math.floor(self.x) 106 | self.y = math.floor(self.y) 107 | return self 108 | end 109 | 110 | 111 | function Vector:ceil() 112 | self.x = math.ceil(self.x) 113 | self.y = math.ceil(self.y) 114 | return self 115 | end 116 | 117 | 118 | function Vector:round(p) 119 | self.x = math.round(self.x, p) 120 | self.y = math.round(self.y, p) 121 | return self 122 | end 123 | 124 | 125 | function Vector:dot(v) 126 | return self.x*v.x + self.y*v.y 127 | end 128 | 129 | 130 | function Vector:is_perpendicular(v) 131 | return math.abs(self:dot(v)) < EPSILON_SQUARED 132 | end 133 | 134 | 135 | function Vector:cross(v) 136 | return self.x*v.y - self.y*v.x 137 | end 138 | 139 | 140 | function Vector:is_parallel(v) 141 | return math.abs(self:cross(v)) < EPSILON_SQUARED 142 | end 143 | 144 | 145 | function Vector:is_set() 146 | return self.x or self.y 147 | end 148 | 149 | 150 | function Vector:is_zero() 151 | return math.abs(self.x) < EPSILON and math.abs(self.y) < EPSILON 152 | end 153 | 154 | 155 | function Vector:zero() 156 | self.x = 0 157 | self.y = 0 158 | return self 159 | end 160 | 161 | 162 | function Vector:length() 163 | return math.sqrt(self.x*self.x + self.y*self.y) 164 | end 165 | 166 | 167 | function Vector:length_squared() 168 | return self.x*self.x + self.y*self.y 169 | end 170 | 171 | 172 | function Vector:normalize() 173 | if self:is_zero() then return self end 174 | return self:scale(1/self:length()) 175 | end 176 | 177 | 178 | function Vector:perpendicular() 179 | return Vector(-self.y, self.x) 180 | end 181 | 182 | 183 | function Vector:left_normal() 184 | return Vector(self.y, -self.x) 185 | end 186 | 187 | 188 | function Vector:invert() 189 | self.x = self.x*-1 190 | self.y = self.y*-1 191 | return self 192 | end 193 | 194 | 195 | function Vector:project_to(v) 196 | local lsq = v:length_squared() 197 | local dp = self:dot(v) 198 | return Vector(dp*v.x/lsq, dp*v.y/lsq) 199 | end 200 | 201 | 202 | function Vector:truncate(max) 203 | local s = max*max/self:length_squared() 204 | s = (s > 1 and 1) or math.sqrt(s) 205 | self.x = self.x*s 206 | self.y = self.y*s 207 | return self 208 | end 209 | 210 | 211 | function Vector:angle_to(v) 212 | return math.atan2(v.y - self.y, v.x - self.x) 213 | end 214 | 215 | 216 | function Vector:angle() 217 | return math.atan2(self.y, self.x) 218 | end 219 | 220 | 221 | function Vector:distance_squared(v) 222 | local dx = v.x - self.x 223 | local dy = v.y - self.y 224 | return dx*dx + dy*dy 225 | end 226 | 227 | 228 | function Vector:distance(v) 229 | return math.sqrt(self:distance_squared(v)) 230 | end 231 | 232 | 233 | function Vector:bounce(normal, bounce_coefficient) 234 | local d = (1 + (bounce_coefficient or 1))*self:dot(normal) 235 | self.x = self.x - d*normal.x 236 | self.y = self.y - d*normal.y 237 | return self 238 | end 239 | 240 | 241 | function Vector:overlaps_with_circle(cx, cy, r) 242 | return mlib.circle.checkPoint(self.x, self.y, cx, cy, r) 243 | end 244 | 245 | 246 | function Vector:overlaps_with_polygon(vs) 247 | return mlib.polygon.checkPoint(self.x, self.y, vs) 248 | end 249 | 250 | 251 | function Vector:overlaps_with_rectangle(x, y, w, h) 252 | return mlib.polygon.checkPoint(self.x, self.y, x - w/2, y - h/2, x + w/2, y - h/2, x + w/2, y + h/2, x - w/2, y + h/2) 253 | end 254 | -------------------------------------------------------------------------------- /engine/sound.lua: -------------------------------------------------------------------------------- 1 | -- TODO: actually implement this later, for now ripple works fine with just name swaps on top of it for naming consistency. 2 | Sound = function(asset_name, options) return ripple.newSound(love.audio.newSource('assets/sounds/' .. asset_name, 'static'), options) end 3 | SoundTag = ripple.newTag 4 | Effect = love.audio.setEffect 5 | -------------------------------------------------------------------------------- /engine/system.lua: -------------------------------------------------------------------------------- 1 | system = {} 2 | 3 | 4 | function system.update(dt) 5 | if input.f12.pressed then 6 | for k, v in pairs(system.type_count()) do 7 | print(k, v) 8 | end 9 | print("-- " .. math.round(tonumber(collectgarbage("count"))/1024, 3) .. "MB --") 10 | print() 11 | end 12 | end 13 | 14 | 15 | global_type_table = nil 16 | function system.type_name(o) 17 | if global_type_table == nil then 18 | global_type_table = {} 19 | for k, v in pairs(_G) do 20 | global_type_table[v] = k 21 | end 22 | global_type_table[0] = "table" 23 | end 24 | return global_type_table[getmetatable(o) or 0] or "Unknown" 25 | end 26 | 27 | 28 | function system.count_all(f) 29 | local seen = {} 30 | local count_table 31 | count_table = function(t) 32 | if seen[t] then return end 33 | f(t) 34 | seen[t] = true 35 | for k, v in pairs(t) do 36 | if type(v) == "table" then count_table(v) 37 | elseif type(v) == "userdata" then f(v) 38 | end 39 | end 40 | end 41 | count_table(_G) 42 | end 43 | 44 | 45 | function system.type_count() 46 | local counts = {} 47 | local enumerate = function(o) 48 | if type(o) == 'function' then 49 | local upvalues = {} 50 | 51 | else 52 | local t = system.type_name(o) 53 | counts[t] = (counts[t] or 0) + 1 54 | end 55 | end 56 | system.count_all(enumerate) 57 | return counts 58 | end 59 | 60 | 61 | function system.enumerate_files(path, filter) 62 | local function recursive_enumerate(path, files) 63 | local items = love.filesystem.getDirectoryItems(path) 64 | for _, item in ipairs(items) do 65 | local file = path .. "/" .. item 66 | local info = love.filesystem.getInfo(file) 67 | if info.type == "file" then 68 | table.insert(files, file) 69 | elseif info.type == "directory" then 70 | recursive_enumerate(file, files) 71 | end 72 | end 73 | end 74 | 75 | local files = {} 76 | recursive_enumerate(path, files) 77 | if filter then 78 | local filtered_files = {} 79 | for _, file in ipairs(files) do 80 | if file:find(filter) then 81 | table.insert(filtered_files, file:left(filter):right(path .. "/")) 82 | end 83 | end 84 | return filtered_files 85 | else 86 | return files 87 | end 88 | end 89 | 90 | 91 | function system.does_file_exist(path) 92 | local file = io.open(path, "r") 93 | if file then 94 | file:close() 95 | return true 96 | end 97 | end 98 | 99 | 100 | function system.load_files(path, filter, exclude_table) 101 | local exclude_table = {unpack(exclude_table or {})} 102 | for _, file in ipairs(system.enumerate_files(path, filter)) do 103 | if not table.contains(exclude_table, file) then 104 | require(path .. "." .. file) 105 | end 106 | end 107 | end 108 | 109 | 110 | function system.save_file(filename, data) 111 | binser.w(filename, data) 112 | end 113 | 114 | 115 | function system.load_file(filename) 116 | return binser.r(filename)[1] 117 | end 118 | 119 | 120 | function system.save_state() 121 | love.filesystem.createDirectory("") 122 | local str = "return " .. table.tostring(state or {}) 123 | love.filesystem.write("state.txt", str) 124 | end 125 | 126 | 127 | function system.load_state() 128 | if love.filesystem.getInfo("state") then 129 | state = binser.r(love.filesystem.getSaveDirectory() .. '/state')[1] 130 | love.filesystem.createDirectory("old_save_backup") 131 | os.rename(love.filesystem.getSaveDirectory() .. '/state', love.filesystem.getSaveDirectory() .. '/old_save_backup/state') 132 | system.save_state() 133 | end 134 | local chunk = love.filesystem.load("state.txt") 135 | if chunk then state = chunk() 136 | else state = {} end 137 | end 138 | 139 | 140 | function system.save_run(level, loop, gold, units, passives, shop_level, shop_xp, run_passive_pool, locked_state) 141 | local run = {level = level, loop = loop, gold = gold, units = units, passives = passives, shop_level = shop_level, shop_xp = shop_xp, run_passive_pool = run_passive_pool, locked_state = locked_state, 142 | current_new_game_plus = current_new_game_plus, run_time = run_time} 143 | local str = "return " .. table.tostring(run) 144 | love.filesystem.write("run_v4.txt", str) 145 | end 146 | 147 | 148 | function system.load_run() 149 | local chunk = love.filesystem.load("run_v4.txt") 150 | if chunk then return chunk() 151 | else return {} end 152 | end 153 | 154 | 155 | function system.get_main_directory() 156 | return love.filesystem.getSource() 157 | end 158 | 159 | 160 | function system.get_save_directory() 161 | return love.filesystem.getSaveDirectory() 162 | end 163 | 164 | 165 | function system.filedropped(file) 166 | game:filedropped(love.filesystem.newFileData(file:read())) 167 | end 168 | 169 | 170 | function system.rename(old_path, new_path) 171 | os.rename(old_path, new_path) 172 | end 173 | 174 | 175 | function system.execute(cmd) 176 | os.execute(cmd) 177 | end 178 | 179 | 180 | function system.remove(path) 181 | os.remove(path) 182 | end 183 | 184 | 185 | function system.open_url(url) 186 | love.system.openURL(url) 187 | end 188 | -------------------------------------------------------------------------------- /media.lua: -------------------------------------------------------------------------------- 1 | Media = Object:extend() 2 | Media:implement(State) 3 | function Media:init(name) 4 | self:init_state(name) 5 | end 6 | 7 | 8 | function Media:on_enter(from) 9 | camera.x, camera.y = gw/2, gh/2 10 | self.main = Group() 11 | self.effects = Group() 12 | self.ui = Group() 13 | 14 | graphics.set_background_color(blue[0]) 15 | Text2{group = self.ui, x = gw/2, y = gh/2, lines = { 16 | {text = '[fg]SNKRX', font = fat_font, alignment = 'center', height_offset = -15}, 17 | {text = '[fg]loop update', font = pixul_font, alignment = 'center'}, 18 | }} 19 | end 20 | 21 | 22 | function Media:update(dt) 23 | self.main:update(dt*slow_amount) 24 | self.effects:update(dt*slow_amount) 25 | self.ui:update(dt*slow_amount) 26 | end 27 | 28 | 29 | function Media:draw() 30 | self.main:draw() 31 | self.effects:draw() 32 | self.ui:draw() 33 | 34 | mercenary:draw(30, 30, 0, 1, 1, 0, 0, yellow2[-5]) 35 | end 36 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd D:/code/SNKRX 4 | engine/love/love.exe --console . 5 | -------------------------------------------------------------------------------- /todo: -------------------------------------------------------------------------------- 1 | Weekly maintenance updates: 2 | 3 | #3 4 | 5 | * Fixed a bug that would cause incorrect party size after loading a looped run (thanks ArnaudOechslin https://github.com/a327ex/SNKRX/pull/15) 6 | * Fixed a rare crash due to enemy critters being spawned during a level transition 7 | * Improved descriptions for engineer and sentry to avoid confusion 8 | * Fixed engineer's and artificer's cooldown not being displayed properly 9 | * Changed freezing field's color to blue for better visual clarity 10 | * Increased cryomancer's area of effect by 50% 11 | * Increased bane's void rift's size by 50% 12 | * Beastmaster now has 10% crit chance by default 13 | * The fairy will no longer buff non-attacking units 14 | * Awakening and enchanted items will no longer buff non-attacking units 15 | * Changed magician's Lv.3 effect to "+50% attack speed every 12 seconds for 6 seconds" 16 | Added the following keyboard shortcuts: 17 | While in the shop: 18 | * R rerolls units 19 | * 1, 2, 3 buys units 20 | * Enter/space starts the round 21 | While in the item selection screen: 22 | * R rerolls items 23 | * 1, 2, 3, 4 buys items 24 | 25 | #2 26 | 27 | * Fixed a bug where NG+5 difficulty would go down to NG+4 after looping 28 | * Capped enemy movement speed after level 150 29 | * Warden's bubble is now affected by magnify 30 | * Changed all text instances of "active set" to "active class" to avoid confusion 31 | * Added a run timer option - note that the timer will be off for saved runs that started before the patch 32 | -- * Alt tabbing now automatically pauses the game while in the arena 33 | * Shop level can now be reduced 34 | 35 | #1 36 | 37 | * Fixed several blue screen crashes due to broken looping state 38 | * Fixed several blue screen crashes due to broken physics state 39 | * Fixed a bug where double clicking the loop button would lead to broken looping state and crashes 40 | * Fixed sold items not being restored to the passive pool 41 | * Fixed gambler's volume being too loud with high amounts of gold 42 | * Fixed soundtrack button not working on the win screen 43 | * Fixed volume text bug when decreasing it from 1 to 0 44 | * Fixed volume buttons not looping 45 | * Fixed a bug where the first run would not have certain items in the item pool 46 | * Fixed psyker orbs item saying "+1/2/3" in the shop when it is "+1/2/4" 47 | * Fixed kinetic strike not being in the passive pool 48 | * Fixed a bug where sometimes restarting the game from a looped run would let you have more units than normal on the next run 49 | * Limited critters to 100 due to performance issues 50 | * Limited health orbs on the arena to 30 due to performance issues 51 | * Limited gold coins on the arena to 30 due to performance issues 52 | 53 | --- 54 | 55 | 30Hz 56 | 57 | Invoker - casts attacks and spells from other units 58 | having a unit like this from the start will help ensure that attacks are behind function calls that can be accessed by another unit easily rather than mostly hidden like they are now 59 | 60 | Future ideas: 61 | Chaos related classes 62 | Cartographer - https://i.imgur.com/Bz6glry.png 63 | Trappers: 64 | Emitters: +projectile damage, projectile mods 65 | warping 66 | homing/barrage 67 | wavy, 90, 45 degree 68 | splitting tears 69 | Traps - map modifier 70 | turrets attached to walls shoot single, slow moving projectiles in a predictable pattern that deal lots of damage 71 | Triangler - drops a trap and the 3rd trap will trigger the area, dealing X AoE damage 2-3 times 72 | Brawlers: units focused on crashing on enemies 73 | https://i.imgur.com/5YubukS.png - unit idea 74 | Bodyguard - https://i.imgur.com/Y2pP20v.png 75 | Conjurer unit that creates an unit that actively protects you from enemy projectiles 76 | Guardians - https://i.imgur.com/Ynu5Cdw.png 77 | Cultists - https://i.imgur.com/GsfoZBd.png 78 | psyker + builder - https://i.imgur.com/VjY6r1d.png 79 | Assists (2/4) - 80 | Ringmaster (tier 4 assist, nuker) - +15% to all stats to adjacent units, Lv.3 effect - create a cross that deals AoE damage 5 times for 10 seconds 81 | Absorber (tier 2 assist, warrior) - absorbs 50% damage from adjacent units, Lv.3 effect - absorbs 75% damage from adjacent units and gives the absorber +25% defense 82 | Pardoner (tier 3 assist, mercenary) - 83 | Oracle (tier 1 assist) - +10% dodge chance to adjacent units, Lv.3 effect - +20% dodge chance to adjacent units 84 | Seraph (tier 2 assist, healer) - periodically chooses 1 random unit and gives it +100% defense for 6 seconds, Lv.3 - choose 2 units instead 85 | Add a few builder units that create walls/solids the player can hide behind (https://www.youtube.com/watch?v=KqwBZ_2f7QU&t=2331s) 86 | Hexblaster? - curser that consumes curses to deal damage 87 | Bench? - https://i.imgur.com/B1gNVKk.png 88 | Balance option for when there are more sets - https://i.imgur.com/JMynwbL.png 89 | Negative effect: colliding with yourself kills one of your units 90 | https://i.imgur.com/bxfvA7g.png 91 | https://steamcommunity.com/app/915310/discussions/0/4658391921156086711/ - general feedback 92 | https://steamcommunity.com/app/915310/discussions/0/4658391921156325745/ - math on gold, rerolls and units 93 | https://steamcommunity.com/app/915310/discussions/0/3069747783686815659/ - general feedback 94 | https://steamcommunity.com/app/915310/discussions/0/3069747783688708231/ - general feedback 95 | https://steamcommunity.com/app/915310/discussions/0/3046104862443040220/ - general feedback 96 | Challenge mode 97 | Units die permanently when they die 98 | Slower scaling with less individually threatening units 99 | Max snake size goes up every 10 levels 100 | Draft mode 101 | Enemy ideas - https://steamcommunity.com/app/915310/discussions/0/3069747783691890511/ 102 | Unit ideas - https://i.imgur.com/VNMS2YV.png 103 | Unit ideas - https://steamcommunity.com/app/915310/discussions/0/3069747783693969554/ 104 | Unit ideas - https://steamcommunity.com/app/915310/discussions/0/3046104336668792953/ 105 | Achievement ideas - https://i.imgur.com/Q7jHWB2.png, https://i.imgur.com/2l5eist.png 106 | general ideas - https://i.imgur.com/W8EYUU1.png 107 | room types - https://i.imgur.com/u2AY1ea.png 108 | 109 | Draft system 110 | Ban system 111 | Class select 112 | Random select 113 | 114 | 115 | -- 116 | 117 | 118 | Roguelite update: 119 | Technical improvements: 120 | Spawn tech: spawn every entity in a grid, before spawning check to see if grid position is clear, this will prevent any issues due to entities spawning inside one another 121 | Battle stats: DPS, damage taken, etc (check Underlords) 122 | Tag system: similar to PoE 123 | Keyword explanations: similar to StS or Artifact, simply create additional text windows for keywords and what they mean 124 | Key rebinding (for non-QWERTY keyboards) 125 | StS-like map with nodes, node types: 126 | Arena 127 | Elite 128 | Boss 129 | Map (map of bigger size than arena with fixed spawns) 130 | Unit shop 131 | Item shop 132 | Once there are enough items there can be item tiers 133 | The item shop should work similarly to the normal shop, where it can be upgraded and then have a higher chance for higher tier items 134 | Text + reward 135 | Training grounds (upgrade unit) 136 | Tavern (heal units) 137 | Challenge + reward 138 | Go through the labyrinth without hitting any walls 139 | Go through the traps without getting hit 140 | Room ideas - https://i.imgur.com/ajqtTOc.png 141 | Units die permanently when they die (dead units can be stored in bench to be revived later) 142 | Units can have items attached to them like in Underlords 143 | Unit item ideas: 144 | This unit's projectiles pierce/chain/fork/seek/split/stun/etc 145 | This unit is a [class] 146 | New stat system: 147 | Most stats are values from 1 to 10 (can be lower than 1 or higher than 10 due to debuffs/buffs) that represent consistent internal values between all units 148 | i.e. 3 attack speed means the same internal attack rate value (like say 6 seconds) for the entire game 149 | In general it's better if units don't have hidden internal multipliers on these stats, although sometimes that may be inevitable 150 | Damage: 151 | Hit: 152 | Everything hits except DoT 153 | Damage type: 154 | Attack - physical attacks, decreased by the enemy's armor 155 | Spell - magical attacks, decreased by the enemy's magic resistance 156 | Attack type: 157 | Crash - damage dealt when bumping into enemies 158 | Projectile - damage dealt by projectiles 159 | AoE - damage dealt in an area 160 | DoT - damage dealt over time 161 | Attack speed: 162 | Defense: 163 | Armor - decreases damage taken from attacks 164 | Magic Resistance - decreases damage taken from spells 165 | Movement speed: 166 | --------------------------------------------------------------------------------