diff --git a/.gitignore b/.gitignore
index 16ba4891..fcf34915 100644
--- a/.gitignore
+++ b/.gitignore
@@ -400,4 +400,5 @@ test.cpp
 /x64/Release/Adventures in Lestoria.zip
 /x64/Release/Adventures in Lestoria_web.zip
 /x64/Release/AdventuresInLestoria_web.zip
-packkey.cpp
\ No newline at end of file
+packkey.cpp
+desktop.ini
\ No newline at end of file
diff --git a/Adventures in Lestoria/Adventures in Lestoria.vcxproj b/Adventures in Lestoria/Adventures in Lestoria.vcxproj
index 98950e71..f9203f87 100644
--- a/Adventures in Lestoria/Adventures in Lestoria.vcxproj	
+++ b/Adventures in Lestoria/Adventures in Lestoria.vcxproj	
@@ -712,6 +712,10 @@
       
     
     
+    
+      
+      
+    
     
     
       
diff --git a/Adventures in Lestoria/Arrow.cpp b/Adventures in Lestoria/Arrow.cpp
index 8f196431..02fe9d65 100644
--- a/Adventures in Lestoria/Arrow.cpp	
+++ b/Adventures in Lestoria/Arrow.cpp	
@@ -44,11 +44,15 @@ All rights reserved.
 
 INCLUDE_game
 
-Arrow::Arrow(vf2d pos,vf2d targetPos,vf2d vel,float radius,int damage,bool upperLevel,bool friendly,Pixel col)
-	:finalDistance(geom2d::line(pos,targetPos).length()*1.2f),acc(PI/2*"Ranger.Auto Attack.ArrowSpd"_F),
+Arrow::Arrow(vf2d pos,vf2d targetPos,vf2d vel,float acc,float radius,int damage,bool upperLevel,bool friendly,Pixel col)
+	:finalDistance(geom2d::line(pos,targetPos).length()*1.2f),acc(acc),targetPos(targetPos),
 	Bullet(pos,vel,radius,damage,
 		"arrow.png",upperLevel,false,INFINITE,true,friendly,col){}
 
+Arrow::Arrow(vf2d pos,vf2d targetPos,vf2d vel,const std::string_view gfx,float acc,float radius,int damage,bool upperLevel,bool friendly,Pixel col)
+	:finalDistance(geom2d::line(pos,targetPos).length()*1.2f),acc(acc),
+	Bullet(pos,vel,radius,damage,std::string(gfx),upperLevel,false,INFINITE,true,friendly,col){}
+
 void Arrow::Update(float fElapsedTime){
 	float speed=vel.mag();
 	travelDistance+=speed*fElapsedTime;
@@ -59,6 +63,10 @@ void Arrow::Update(float fElapsedTime){
 	}
 }
 
+void Arrow::PointToBestTargetPath(const int perceptionLevel){
+	//TODO: Figure out arrow target and spawn an arrow.
+}
+
 bool Arrow::PlayerHit(Player*player)
 {
 	deactivated=true;
diff --git a/Adventures in Lestoria/Bear.cpp b/Adventures in Lestoria/Bear.cpp
index 46303ec4..013cd344 100644
--- a/Adventures in Lestoria/Bear.cpp	
+++ b/Adventures in Lestoria/Bear.cpp	
@@ -76,7 +76,7 @@ void Monster::STRATEGY::BEAR(Monster&m,float fElapsedTime,std::string strategy){
 			if(m.F(A::CASTING_TIMER)==0.f){
 				m.I(A::PHASE)=2;
 				m.F(A::CASTING_TIMER)=ConfigFloat("Attack Animation Wait Time");
-				m.PerformOtherAnimation(0);
+				m.PerformAnimation("SLAM");
 			}
 		}break;
 		case 2:{
diff --git a/Adventures in Lestoria/Boar.cpp b/Adventures in Lestoria/Boar.cpp
index 295ddd6a..d7a0a8bf 100644
--- a/Adventures in Lestoria/Boar.cpp	
+++ b/Adventures in Lestoria/Boar.cpp	
@@ -73,7 +73,7 @@ void Monster::STRATEGY::BOAR(Monster&m,float fElapsedTime,std::string strategy){
 				m.UpdateFacingDirection(game->GetPlayer()->GetPos());
 			}else{
 			ScratchPhaseTransition:
-				m.PerformOtherAnimation(0);
+				m.PerformAnimation("SCRATCH");
 				m.F(A::CASTING_TIMER)=ConfigInt("Ground Scratch Count")*m.GetCurrentAnimation().GetTotalAnimationDuration();
 				m.phase=PhaseName::SCRATCH;
 			}
diff --git a/Adventures in Lestoria/BulletTypes.h b/Adventures in Lestoria/BulletTypes.h
index d41436d0..f3020b60 100644
--- a/Adventures in Lestoria/BulletTypes.h	
+++ b/Adventures in Lestoria/BulletTypes.h	
@@ -67,8 +67,11 @@ struct Arrow:public Bullet{
 	float travelDistance=0;
 	float finalDistance=0;
 	float acc=PI/2*250;
-	Arrow(vf2d pos,vf2d targetPos,vf2d vel,float radius,int damage,bool upperLevel,bool friendly=false,Pixel col=WHITE);
+	vf2d targetPos;
+	Arrow(vf2d pos,vf2d targetPos,vf2d vel,float acc,float radius,int damage,bool upperLevel,bool friendly=false,Pixel col=WHITE);
+	Arrow(vf2d pos,vf2d targetPos,vf2d vel,const std::string_view gfx,float acc,float radius,int damage,bool upperLevel,bool friendly=false,Pixel col=WHITE);
 	void Update(float fElapsedTime)override;
+	void PointToBestTargetPath(const int perceptionLevel);
 	bool PlayerHit(Player*player)override;
 	bool MonsterHit(Monster&monster)override;
 };
diff --git a/Adventures in Lestoria/Goblin_Bow.cpp b/Adventures in Lestoria/Goblin_Bow.cpp
new file mode 100644
index 00000000..c6b7149c
--- /dev/null
+++ b/Adventures in Lestoria/Goblin_Bow.cpp	
@@ -0,0 +1,130 @@
+#pragma region License
+/*
+License (OLC-3)
+~~~~~~~~~~~~~~~
+
+Copyright 2024 Joshua Sigona 
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+1. Redistributions or derivations of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+2. Redistributions or derivative works in binary form must reproduce the above
+copyright notice. This list of conditions and the following	disclaimer must be
+reproduced in the documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors may
+be used to endorse or promote products derived from this software without specific
+prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS	"AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+SHALL THE COPYRIGHT	HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL,	SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT	(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+Portions of this software are copyright © 2024 The FreeType
+Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
+All rights reserved.
+*/
+#pragma endregion
+#include "AdventuresInLestoria.h"
+#include "DEFINES.h"
+#include "Monster.h"
+#include "MonsterStrategyHelpers.h"
+#include "BulletTypes.h"
+#include "util.h"
+/*
+Attack Strategie: 
+if range > 1000 move to a 850 range.
+if range 1000-700. Stand still and shoot. (reload twice as fast)
+if range between 500 and 700. Shoot and run in random direction while reloading without getting out of the 500 - 700 range
+range < 500 while reloading try to get distance. still shoot if reload finishes
+After every attack: reload takes 2 seconds. 
+before shooting takes 1 second.
+*/
+
+INCLUDE_game
+INCLUDE_ANIMATION_DATA
+INCLUDE_BULLET_LIST
+using A=Attribute;
+
+void Monster::STRATEGY::GOBLIN_BOW(Monster&m,float fElapsedTime,std::string strategy){
+	#pragma region Phase, Animation, and Helper function setup
+		enum PhaseName{
+			MOVE,
+			WINDUP,
+		};
+
+		enum class ANIMATION_OFFSET{
+			IDLE_ANIMATION=0,
+			SHOOT_ANIMATION=4,
+		};
+
+		auto SetFacingAnimation=[&](ANIMATION_OFFSET animation,vf2d target){
+			m.PerformAnimation("");
+		};
+		auto IsSpriteFlipped=[&](){return m.GetFacingDirection()==RIGHT;};
+	#pragma endregion
+
+	using enum ANIMATION_OFFSET;
+	
+	m.F(A::ATTACK_COOLDOWN)+=fElapsedTime;
+
+	switch(m.phase){
+		case MOVE:{
+			float distToPlayer=m.GetDistanceFrom(game->GetPlayer()->GetPos());
+
+			const bool outsideMaxShootingRange=distToPlayer>=ConfigPixelsArr("Stand Still and Shoot Range",1);
+
+			auto PrepareToShoot=[&](){
+				m.phase=WINDUP;
+				m.F(A::SHOOT_TIMER)=ConfigFloat("Attack Windup Time");
+				SetFacingAnimation(SHOOT_ANIMATION,game->GetPlayer()->GetPos());
+			};
+
+			if(outsideMaxShootingRange){
+				m.target=game->GetPlayer()->GetPos();
+				RUN_TOWARDS(m,fElapsedTime,"Run Towards");
+			}else
+			if(m.F(A::ATTACK_COOLDOWN)>=ConfigFloat("Attack Reload Time")){
+				PrepareToShoot();
+			}else
+			if(distToPlayer(m.GetPos(),game->GetPlayer()->GetPos()).upoint(-1);
+				RUN_TOWARDS(m,fElapsedTime,"Run Towards");
+			}else
+			if(distToPlayer>=ConfigPixelsArr("Random Direction Range",0)&&distToPlayerGetPlayer()->GetPos(),m.GetPos());
+				float targetDir=m.B(A::RANDOM_DIRECTION)==CW?dirFromPlayer+PI/4:dirFromPlayer-PI/4;
+				m.target=game->GetPlayer()->GetPos()+vf2d{m.F(A::RANDOM_RANGE),targetDir}.cart();
+				RUN_TOWARDS(m,fElapsedTime,"Run Towards");
+			}else{ //Only the stand still and shoot range remains...
+				PrepareToShoot();
+			}
+		}break;
+		case WINDUP:{
+			m.F(A::SHOOT_TIMER)-=fElapsedTime;
+			if(m.F(A::SHOOT_TIMER)<=0){	
+				geom2d::line pointTowardsPlayer(m.GetPos(),game->GetPlayer()->GetPos());
+				vf2d extendedLine=pointTowardsPlayer.upoint(1.1f);
+				CreateBullet(Arrow)(m.GetPos(),extendedLine,pointTowardsPlayer.vector().norm()*ConfigFloat("Arrow Spd"),"goblin_arrow.png",PI/2*ConfigFloat("Arrow Spd"),ConfigFloat("Arrow Hitbox Radius"),m.GetAttack(),m.OnUpperLevel())EndBullet;
+				Arrow&arrow=static_cast(*BULLET_LIST.back());
+				arrow.PointToBestTargetPath(0);
+				m.phase=MOVE;
+			}
+			m.B(A::RANDOM_DIRECTION)=util::random()%2;
+			m.F(A::RANDOM_RANGE)=util::random_range(ConfigPixelsArr("Random Direction Range",0),ConfigPixelsArr("Random Direction Range",1));
+		}break;
+	}
+}
\ No newline at end of file
diff --git a/Adventures in Lestoria/Goblin_Dagger.cpp b/Adventures in Lestoria/Goblin_Dagger.cpp
index 613930d2..22294ef5 100644
--- a/Adventures in Lestoria/Goblin_Dagger.cpp	
+++ b/Adventures in Lestoria/Goblin_Dagger.cpp	
@@ -75,7 +75,7 @@ void Monster::STRATEGY::GOBLIN_DAGGER(Monster&m,float fElapsedTime,std::string s
 	};
 
 	auto SetFacingAnimation=[&](ANIMATION_OFFSET animation,vf2d target){
-		m.PerformOtherAnimation(int(animation)+int(m.GetFacingDirectionToTarget(target)));
+		m.PerformAnimation("");
 	};
 	auto IsSpriteFlipped=[&](){return m.GetFacingDirection()==RIGHT;};
 
diff --git a/Adventures in Lestoria/Monster.cpp b/Adventures in Lestoria/Monster.cpp
index 1a1f80c3..5e18365d 100644
--- a/Adventures in Lestoria/Monster.cpp	
+++ b/Adventures in Lestoria/Monster.cpp	
@@ -66,14 +66,10 @@ std::mapMonsterData::imgs;
 
 Monster::Monster(vf2d pos,MonsterData data,bool upperLevel,bool bossMob):
 	pos(pos),spawnPos(pos),hp(data.GetHealth()),size(data.GetSizeMult()),targetSize(data.GetSizeMult()),strategy(data.GetAIStrategy()),name(data.GetDisplayName()),upperLevel(upperLevel),isBoss(bossMob),facingDirection(DOWN){
-	bool firstAnimation=true;
-	for(std::string&anim:data.GetAnimations()){
+	for(const std::string&anim:data.GetAnimations()){
 		animation.AddState(anim,ANIMATION_DATA[anim]);
-		if(firstAnimation){
-			animation.ChangeState(internal_animState,anim);
-			firstAnimation=false;
-		}
 	}
+	PerformIdleAnimation();
 	stats.A("Health")=data.GetHealth();
 	stats.A("Attack")=data.GetAttack();
 	stats.A("Move Spd %")=data.GetMoveSpdMult();
@@ -125,28 +121,28 @@ Animate2D::Frame Monster::GetFrame()const{
 	return animation.GetFrame(internal_animState);
 }
 void Monster::PerformJumpAnimation(){
-	animation.ChangeState(internal_animState,MONSTER_DATA[name].GetJumpAnimation());
+	PerformAnimation(MONSTER_DATA[name].GetJumpAnimation());
 }
 void Monster::PerformShootAnimation(){
-	animation.ChangeState(internal_animState,MONSTER_DATA[name].GetShootAnimation());
+	PerformAnimation(MONSTER_DATA[name].GetShootAnimation());
 }
 void Monster::PerformIdleAnimation(){
-	animation.ChangeState(internal_animState,MONSTER_DATA[name].GetIdleAnimation());
+	PerformAnimation(MONSTER_DATA[name].GetIdleAnimation());
 }
 void Monster::PerformNPCDownAnimation(){
-	animation.ChangeState(internal_animState,MONSTER_DATA[name].GetIdleAnimation());
+	PerformAnimation(MONSTER_DATA[name].GetIdleAnimation());
 }
 void Monster::PerformNPCUpAnimation(){
-	animation.ChangeState(internal_animState,MONSTER_DATA[name].GetJumpAnimation());
+	PerformAnimation(MONSTER_DATA[name].GetJumpAnimation());
 }
 void Monster::PerformNPCLeftAnimation(){
-	animation.ChangeState(internal_animState,MONSTER_DATA[name].GetShootAnimation());
+	PerformAnimation(MONSTER_DATA[name].GetShootAnimation());
 }
 void Monster::PerformNPCRightAnimation(){
-	animation.ChangeState(internal_animState,MONSTER_DATA[name].GetDeathAnimation());
+	PerformAnimation(MONSTER_DATA[name].GetDeathAnimation());
 }
-void Monster::PerformOtherAnimation(const uint8_t otherInd){
-	animation.ChangeState(internal_animState,MONSTER_DATA[name].GetAnimations()[4+otherInd]);
+void Monster::PerformAnimation(const std::string_view animationName){
+	animation.ChangeState(internal_animState,std::string(animationName));
 }
 bool Monster::_SetX(float x,const bool monsterInvoked){
 	vf2d newPos={x,pos.y};
@@ -879,7 +875,6 @@ const float Monster::GetDistanceFrom(vf2d target)const{
 
 const Direction Monster::GetFacingDirectionToTarget(vf2d target)const{
 	float targetDirection=util::angleTo(GetPos(),target);
-	LOG(targetDirection<-PI/4)return Direction::EAST;
 	else if(targetDirection>=3*PI/4||targetDirection<-3*PI/4)return Direction::WEST;
 	else if(targetDirection<=3*PI/4&&targetDirection>PI/4)return Direction::SOUTH;
diff --git a/Adventures in Lestoria/Monster.h b/Adventures in Lestoria/Monster.h
index d47af050..e932567f 100644
--- a/Adventures in Lestoria/Monster.h	
+++ b/Adventures in Lestoria/Monster.h	
@@ -56,13 +56,6 @@ class AiL;
 
 enum class Attribute;
 
-enum class MonsterAnimation{
-	IDLE,
-	JUMP,
-	SHOOT,
-	DEATH
-};
-
 class GameEvent;
 
 class Monster:IAttributable{
@@ -109,7 +102,7 @@ public:
 	void PerformNPCUpAnimation();
 	void PerformNPCLeftAnimation();
 	void PerformNPCRightAnimation();
-	void PerformOtherAnimation(const uint8_t otherInd);
+	void PerformAnimation(const std::string_view animationName);
 	const Animate2D::FrameSequence&GetCurrentAnimation()const;
 	const bool OnUpperLevel()const;
 	void Moved();
@@ -219,6 +212,8 @@ private:
 		static int _GetInt(Monster&m,std::string param,std::string strategy,int index=0);
 		static float _GetFloat(Monster&m,std::string param,std::string strategy,int index=0);
 		static Pixel _GetPixel(Monster&m,std::string param,std::string strategy,int index=0);
+		//Converts unit distances to pixels. (Every 100 units = 24 pixels)
+		static double _GetPixels(Monster&m,std::string param,std::string strategy,int index=0);
 		static vf2d _GetVec(Monster&m,std::string param,std::string strategy,int index=0);
 		static const std::string&_GetString(Monster&m,std::string param,std::string strategy,int index=0);
 		static datafile _Get(Monster&m,std::string param,std::string strategy);
@@ -235,6 +230,7 @@ private:
 		static void NPC(Monster&m,float fElapsedTime,std::string strategy);
 		static void BOAR(Monster&m,float fElapsedTime,std::string strategy);
 		static void GOBLIN_DAGGER(Monster&m,float fElapsedTime,std::string strategy);
+		static void GOBLIN_BOW(Monster&m,float fElapsedTime,std::string strategy);
 	};
 	bool bumpedIntoTerrain=false; //Gets set to true before a strategy executes if the monster runs into some terrain on this frame.
 	bool attackedByPlayer=false; //Gets set to true before a strategy executes if the monster has been attacked by the player.
diff --git a/Adventures in Lestoria/MonsterAttribute.h b/Adventures in Lestoria/MonsterAttribute.h
index 4bb674aa..6c0c4949 100644
--- a/Adventures in Lestoria/MonsterAttribute.h	
+++ b/Adventures in Lestoria/MonsterAttribute.h	
@@ -108,4 +108,7 @@ enum class Attribute{
     INITIALIZED,
     JUMP_MOVE_TO_TARGET_TIMER,
     ATTACK_TYPE,
+    ATTACK_COOLDOWN,
+    RANDOM_DIRECTION,
+    RANDOM_RANGE
 };
\ No newline at end of file
diff --git a/Adventures in Lestoria/MonsterData.cpp b/Adventures in Lestoria/MonsterData.cpp
index c758a82f..777a166a 100644
--- a/Adventures in Lestoria/MonsterData.cpp	
+++ b/Adventures in Lestoria/MonsterData.cpp	
@@ -51,7 +51,7 @@ std::mapMONSTER_DATA;
 
 MonsterData::MonsterData()
 :atk(0),collisionDmg(0),hp(0),moveSpd(0),size(0),strategy("Run Towards"){}
-MonsterData::MonsterData(std::string name,int hp,int atk,const uint32_t xp,std::vectoranimations,std::vectordrops,float moveSpd,float size,std::string strategy,int collisionDmg):
+MonsterData::MonsterData(std::string name,int hp,int atk,const uint32_t xp,std::vectordrops,float moveSpd,float size,std::string strategy,int collisionDmg):
 	name(name),hp(hp),atk(atk),xp(xp),moveSpd(moveSpd),size(size),strategy(strategy),animations(animations),dropData(drops),collisionDmg(collisionDmg){}
 
 void MonsterData::InitializeMonsterData(){
@@ -60,12 +60,7 @@ void MonsterData::InitializeMonsterData(){
 		if(MONSTER_DATA.count(key)){
 			ERR("WARNING! A monster with the name "<animations{
-			MonsterName+"_IDLE",
-			MonsterName+"_JUMP",
-			MonsterName+"_SPIT",
-			MonsterName+"_DIE",
-		};
+		std::vectoranimations;
 
 		MonsterData::imgs[MonsterName]=NEW Renderable();
 		const rcode imgLoadResult=MonsterData::imgs[MonsterName]->Load("assets/monsters/"+MonsterName+".png");
@@ -94,78 +89,32 @@ void MonsterData::InitializeMonsterData(){
 			ANIMATION_DATA[state]=anim;
 		};
 
-		for(int i=0;idrops;
@@ -191,7 +140,6 @@ void MonsterData::InitializeMonsterData(){
 			DATA["Monsters"][MonsterName]["Health"].GetInt(),
 			DATA["Monsters"][MonsterName]["Attack"].GetInt(),
 			DATA["Monsters"][MonsterName]["XP"].GetInt(),
-			animations,
 			drops,
 			float(DATA["Monsters"][MonsterName]["MoveSpd"].GetReal()),
 			float(DATA["Monsters"][MonsterName]["Size"].GetReal())/100,
@@ -199,6 +147,19 @@ void MonsterData::InitializeMonsterData(){
 			DATA["Monsters"][MonsterName]["CollisionDmg"].GetInt()
 		);
 
+		for(size_t animationRow=0;const std::string&animationName:animations){
+			if(!monster.animations.insert(animationName).second)ERR(std::format("WARNING! The Animation {} for Monster {} already exists! Animations should have unique names!",animationName,MonsterName));
+
+			switch(animationRow){
+				case 0:monster.idleAnimation=animationName;break;
+				case 1:monster.jumpAnimation=animationName;break;
+				case 2:monster.shootAnimation=animationName;break;
+				case 3:monster.deathAnimation=animationName;break;
+			}
+
+			animationRow++;
+		}
+
 		monster.hurtSound=hurtSound;
 		monster.deathSound=deathSound;
 		monster.walkSound=walkSound;
@@ -260,78 +221,32 @@ void MonsterData::InitializeNPCData(){
 			ANIMATION_DATA[state]=anim;
 		};
 
-		for(int i=0;idrops;
@@ -352,7 +267,20 @@ void MonsterData::InitializeNPCData(){
 			ERR("WARNING! Strategy for "<&MonsterData::GetDropData(){
 	return dropData;
diff --git a/Adventures in Lestoria/MonsterData.h b/Adventures in Lestoria/MonsterData.h
index ab6ef7b5..59436f4f 100644
--- a/Adventures in Lestoria/MonsterData.h	
+++ b/Adventures in Lestoria/MonsterData.h	
@@ -38,6 +38,7 @@ All rights reserved.
 #pragma once
 #include "DEFINES.h"
 #include "Item.h"
+#include 
 
 INCLUDE_ITEM_DATA
 
@@ -64,20 +65,22 @@ private:
 	uint32_t xp;
 	float moveSpd;//1.0=100%
 	float size;
-	std::vector animations;
+	std::unordered_setanimations;
+	std::string idleAnimation="WARRIOR_IDLE_S"; //Represents the basic animation name, not the full animation name that ANIMATION_DATA indexes into!! (Ex. Not GREEN_SLIME_IDLE, but IDLE)
+	std::string jumpAnimation="WARRIOR_IDLE_S"; //Represents the basic animation name, not the full animation name that ANIMATION_DATA indexes into!! (Ex. Not GREEN_SLIME_JUMP, but JUMP)
+	std::string shootAnimation="WARRIOR_IDLE_S"; //Represents the basic animation name, not the full animation name that ANIMATION_DATA indexes into!! (Ex. Not GREEN_SLIME_SHOOT, but SHOOT)
+	std::string deathAnimation="WARRIOR_IDLE_S"; //Represents the basic animation name, not the full animation name that ANIMATION_DATA indexes into!! (Ex. Not GREEN_SLIME_DEATH, but DEATH)
 	std::string strategy;
 	int collisionDmg;
-	std::string jumpAnimation="WARRIOR_IDLE_S";
-	std::string shootAnimation="WARRIOR_IDLE_S";
-	std::string deathAnimation="WARRIOR_IDLE_S";
 	EventName hurtSound="";
 	EventName deathSound="";
 	EventName walkSound="";
 	std::vector dropData;
 	bool isNPC=false;
+	bool fourWayDirectionalSprites=false; //When this flag is set, a monster has 4-way animations instead of the default 1-direction animation.
 public:
 	MonsterData();
-	MonsterData(std::string name,int hp,int atk,const uint32_t xp,std::vectoranimations,std::vectordrops,float moveSpd=1.0f,float size=1.0f,std::string strategy="Run Towards",int collisionDmg=0);
+	MonsterData(std::string name,int hp,int atk,const uint32_t xp,std::vectordrops,float moveSpd=1.0f,float size=1.0f,std::string strategy="Run Towards",int collisionDmg=0);
 	int GetHealth();
 	int GetAttack();
 	const uint32_t GetXP()const;
@@ -93,7 +96,7 @@ public:
 	const EventName&GetDeathSound();
 	const EventName&GetWalkSound();
 	const bool IsNPC()const;
-	std::vectorGetAnimations(){
+	std::unordered_setGetAnimations(){
 		return animations;
 	}
 	const std::vector&GetDropData();
@@ -101,4 +104,6 @@ public:
 	static void InitializeMonsterData();
 	static void InitializeNPCData();
 	static std::mapimgs;
+	const bool HasFourWaySprites()const;
+	void SetUsesFourWaySprites();
 };
\ No newline at end of file
diff --git a/Adventures in Lestoria/MonsterStrategyHelpers.h b/Adventures in Lestoria/MonsterStrategyHelpers.h
index 47288b61..9f9bf876 100644
--- a/Adventures in Lestoria/MonsterStrategyHelpers.h	
+++ b/Adventures in Lestoria/MonsterStrategyHelpers.h	
@@ -39,9 +39,13 @@ All rights reserved.
 #define ConfigInt(param) _GetInt(m,param,strategy)
 #define ConfigFloat(param) _GetFloat(m,param,strategy)
 #define ConfigPixel(param) _GetPixel(m,param,strategy)
+//Converts unit distances to pixels. (Every 100 units = 24 pixels)
+#define ConfigPixels(param) _GetPixels(m,param,strategy)
 #define ConfigString(param) _GetString(m,param,strategy)
 #define ConfigVec(param) _GetVec(m,param,strategy)
 #define Config(param) _Get(m,param,strategy)
+//Converts unit distances to pixels. (Every 100 units = 24 pixels)
+#define ConfigPixelsArr(param,ind) _GetPixels(m,param,strategy,ind)
 #define ConfigIntArr(param,ind) _GetInt(m,param,strategy,ind)
 #define ConfigFloatArr(param,ind) _GetFloat(m,param,strategy,ind)
 #define ConfigStringArr(param,ind) _GetString(m,param,strategy,ind)
diff --git a/Adventures in Lestoria/RUN_STRATEGY.cpp b/Adventures in Lestoria/RUN_STRATEGY.cpp
index 88c21311..b5a5e788 100644
--- a/Adventures in Lestoria/RUN_STRATEGY.cpp	
+++ b/Adventures in Lestoria/RUN_STRATEGY.cpp	
@@ -55,6 +55,7 @@ void Monster::InitializeStrategies(){
 	STRATEGY_DATA.insert("NPC",Monster::STRATEGY::NPC);
 	STRATEGY_DATA.insert("Boar",Monster::STRATEGY::BOAR);
 	STRATEGY_DATA.insert("Goblin Dagger",Monster::STRATEGY::GOBLIN_DAGGER);
+	STRATEGY_DATA.insert("Goblin Bow",Monster::STRATEGY::GOBLIN_BOW);
 
 	STRATEGY_DATA.SetInitialized();
 }
@@ -120,6 +121,17 @@ Pixel Monster::STRATEGY::_GetPixel(Monster&m,std::string param,std::string strat
 	}
 }
 
+double Monster::STRATEGY::_GetPixels(Monster&m,std::string param,std::string strategy,int index){
+	if(m.IsNPC()&&DATA["NPCs"][m.name].HasProperty(param)){
+		return DATA["NPCs"][m.name].GetProperty(param).GetReal(index)/100.f*24;
+	}else
+	if(!m.IsNPC()&&DATA["Monsters"][m.name].HasProperty(param)){
+		return DATA["Monsters"][m.name].GetProperty(param).GetReal(index)/100.f*24;
+	} else {
+		return DATA["MonsterStrategy"][strategy].GetProperty(param).GetReal(index)/100.f*24;
+	}
+}
+
 void Monster::STRATEGY::RUN_STRATEGY(Monster&m,float fElapsedTime){
 	m.GetStrategy()(m,fElapsedTime,m.strategy);
 }
\ No newline at end of file
diff --git a/Adventures in Lestoria/Ranger.cpp b/Adventures in Lestoria/Ranger.cpp
index 6493331d..95a6d18f 100644
--- a/Adventures in Lestoria/Ranger.cpp	
+++ b/Adventures in Lestoria/Ranger.cpp	
@@ -71,7 +71,7 @@ bool Ranger::AutoAttack(){
     vf2d extendedLine=pointTowardsCursor.upoint(1.1f);
     float angleToCursor=atan2(extendedLine.y-GetPos().y,extendedLine.x-GetPos().x);
     attack_cooldown_timer=ARROW_ATTACK_COOLDOWN-GetAttackRecoveryRateReduction();
-    BULLET_LIST.push_back(std::make_unique(Arrow(GetPos(),extendedLine,vf2d{cos(angleToCursor)*"Ranger.Auto Attack.ArrowSpd"_F,float(sin(angleToCursor)*"Ranger.Auto Attack.ArrowSpd"_F-PI/8*"Ranger.Auto Attack.ArrowSpd"_F)}+movementVelocity/1.5f,"Ranger.Auto Attack.Radius"_F/100*12,int(GetAttack()*"Ranger.Auto Attack.DamageMult"_F),OnUpperLevel(),true)));
+    BULLET_LIST.push_back(std::make_unique(Arrow(GetPos(),extendedLine,vf2d{cos(angleToCursor)*"Ranger.Auto Attack.ArrowSpd"_F,float(sin(angleToCursor)*"Ranger.Auto Attack.ArrowSpd"_F-PI/8*"Ranger.Auto Attack.ArrowSpd"_F)}+movementVelocity/1.5f,PI/2*"Ranger.Auto Attack.ArrowSpd"_F,"Ranger.Auto Attack.Radius"_F,int(GetAttack()*"Ranger.Auto Attack.DamageMult"_F),OnUpperLevel(),true)));
     SetState(State::SHOOT_ARROW);
     SetAnimationBasedOnTargetingDirection(angleToCursor);
     SoundEffect::PlaySFX("Ranger.Auto Attack.Sound"_S,SoundEffect::CENTERED);
diff --git a/Adventures in Lestoria/SlimeKing.cpp b/Adventures in Lestoria/SlimeKing.cpp
index 7d24883b..dd693f07 100644
--- a/Adventures in Lestoria/SlimeKing.cpp	
+++ b/Adventures in Lestoria/SlimeKing.cpp	
@@ -229,7 +229,7 @@ void Monster::STRATEGY::SLIMEKING(Monster&m,float fElapsedTime,std::string strat
 	}
 
 	if(m.GetState()==State::CASTING){
-		m.PerformOtherAnimation(0);
+		m.PerformAnimation("CHARGEUP");
 		if(m.F(A::CASTING_TIMER)==0){
 			m.SetState(State::NORMAL);
 			m.I(A::JUMP_COUNT)++;
diff --git a/Adventures in Lestoria/Ursule.cpp b/Adventures in Lestoria/Ursule.cpp
index 26233cd8..d47d8338 100644
--- a/Adventures in Lestoria/Ursule.cpp	
+++ b/Adventures in Lestoria/Ursule.cpp	
@@ -96,7 +96,7 @@ void Monster::STRATEGY::URSULE(Monster&m,float fElapsedTime,std::string strategy
         case 1:{ //Run bear strategy in phase 1.
             auto TransitionToPhase2=[&](){
                 m.phase=2;
-                m.PerformOtherAnimation(1);
+                m.PerformAnimation("SIT");
                 m.AddBuff(BARRIER_DAMAGE_REDUCTION,INFINITE,ConfigFloat("Phase 2.Barrier Damage Reduction")/100.f);
                 m.I(A::PHASE_REPEAT_COUNT)=ConfigInt("Phase 2.Wisp Pattern Spawn Count");
                 SoundEffect::PlaySFX("Ursule Phase Transition",SoundEffect::CENTERED);
@@ -153,7 +153,7 @@ void Monster::STRATEGY::URSULE(Monster&m,float fElapsedTime,std::string strategy
             BEAR(m,fElapsedTime,"Bear");
         }break;
         case 2:{
-            m.PerformOtherAnimation(2);
+            m.PerformAnimation("GLARE");
             m.F(A::SHOOT_TIMER)=std::max(0.f,m.F(A::SHOOT_TIMER)-fElapsedTime);
             
             #pragma region Environment Color Change Handling
@@ -272,7 +272,7 @@ void Monster::STRATEGY::URSULE(Monster&m,float fElapsedTime,std::string strategy
             if(m.GetRemainingHPPct()<=ConfigFloat("Phase 4.Change")/100.f){
                 auto TransitionToPhase5=[&](){
                     m.phase=5;
-                    m.PerformOtherAnimation(1);
+                    m.PerformAnimation("SIT");
                     SoundEffect::PlaySFX("Ursule Phase Transition",SoundEffect::CENTERED);
                     m.F(A::ENVIRONMENT_TIMER)=ConfigFloat("Phase 4.Environment Fade-out Time");
                     m.I(A::ENVIRONMENT_PHASE)=0;
@@ -305,7 +305,7 @@ void Monster::STRATEGY::URSULE(Monster&m,float fElapsedTime,std::string strategy
                     if(m.F(A::CASTING_TIMER)==0.f){
                         m.F(A::CASTING_TIMER)=ConfigFloat("Phase 3.Charge Cast Time");
                         m.UpdateFacingDirection(geom2d::line(m.GetPos(),game->GetPlayer()->GetPos()).vector());
-                        m.PerformOtherAnimation(4);
+                        m.PerformAnimation("CHARGE");
                         m.B(A::COLLIDED_WITH_PLAYER)=false;
                     }
                 }
@@ -322,7 +322,7 @@ void Monster::STRATEGY::URSULE(Monster&m,float fElapsedTime,std::string strategy
                         m.target.y=std::clamp(m.target.y,0.f,float(game->GetCurrentMap().GetMapData().height*game->GetCurrentMap().GetMapData().tileheight));
                         m.F(A::TARGET_TIMER)=ConfigFloat("Phase 3.Charge Max Run Time");
                         m.F(A::CHARGE_COOLDOWN)=ConfigFloat("Phase 3.Charge Attack Cooldown");
-                        m.PerformOtherAnimation(3);
+                        m.PerformAnimation("CHARGING");
                     }
                     break;
                 }
@@ -360,7 +360,7 @@ void Monster::STRATEGY::URSULE(Monster&m,float fElapsedTime,std::string strategy
             }
         }break;
         case 5:{ //Final boss phase.
-            m.PerformOtherAnimation(2);
+            m.PerformAnimation("GLARE");
             m.F(A::SHOOT_TIMER)=std::max(0.f,m.F(A::SHOOT_TIMER)-fElapsedTime);
 
             #pragma region Environment Color Change Handling
diff --git a/Adventures in Lestoria/Version.h b/Adventures in Lestoria/Version.h
index 5e02614f..ae3c74eb 100644
--- a/Adventures in Lestoria/Version.h	
+++ b/Adventures in Lestoria/Version.h	
@@ -39,7 +39,7 @@ All rights reserved.
 #define VERSION_MAJOR 1
 #define VERSION_MINOR 2
 #define VERSION_PATCH 0
-#define VERSION_BUILD 9091
+#define VERSION_BUILD 9115
 
 #define stringify(a) stringify_(a)
 #define stringify_(a) #a
diff --git a/Adventures in Lestoria/assets.zip b/Adventures in Lestoria/assets.zip
new file mode 100644
index 00000000..0e4b83d6
Binary files /dev/null and b/Adventures in Lestoria/assets.zip differ
diff --git a/Adventures in Lestoria/assets/Campaigns/2_1.tmx b/Adventures in Lestoria/assets/Campaigns/2_1.tmx
index 877e16d5..4fc148e3 100644
--- a/Adventures in Lestoria/assets/Campaigns/2_1.tmx	
+++ b/Adventures in Lestoria/assets/Campaigns/2_1.tmx	
@@ -1,5 +1,5 @@
 
-