#retrofit multipart
Explore tagged Tumblr posts
probelalkhan · 8 years ago
Text
New Post has been published on Simplified Coding
New Post has been published on https://www.simplifiedcoding.net/retrofit-upload-file-tutorial/
Retrofit Upload File Tutorial - Uploading and Downloading Images
Tumblr media
(adsbygoogle = window.adsbygoogle || []).push();
Struggling to upload images to your server? Here is the Retrofit Upload File Tutorial. The retrofit library also gives an option to send the Multipart http requests, and we can use it for uploading files. So lets learn how we do this in our android project. Here I am going to use PHP and MySQL on the server side.
Contents
1 Building APIs
1.1 Creating MySQL Database
1.2 Creating PHP Project
1.3 Defining Constants
1.4 Connecting to Database
1.5 The Uploads Directory
1.6 Handling File Upload and Download
1.7 Handling API Calls
2 Retrofit Upload File Tutorial
2.1 Creating a new Project
2.2 Adding Libraries
2.3 Creating the Model Class for Response
2.4 Creating API Interface
2.5 Uploading the File
2.5.1 Creating Interface
2.5.2 Checking the Read Storage Permission
2.5.3 Choosing a File
2.5.4 Getting Absolute Path from Uri
2.5.5 Uploading the File
3 Downloading Images Back
4 Retrofit Upload File Source Code
Building APIs
We will send the file to our server from the android application. So at the server side we need to catch the file and then we need to store it on the server. To do all these tasks we create API. So lets start. Here I am using XAMPP server, you can use any other server as well.
Creating MySQL Database
The first step is creating the database. And we need a database shown as below.
Tumblr media
Database
You can use the following SQL for creating the above database.
CREATE TABLE `images` ( `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT, `description` varchar(1000) NOT NULL, `image` varchar(500) NOT NULL );
Creating PHP Project
For PHP Project I am using PHP Storm. But you can use notepad++, sublime or any other IDE as well.
Create a new project inside htdocs (c:/xampp/htdocs). I have created a project named ImageUploadApi.
Defining Constants
First we will create a file named Constants.php to define all the required constants.
<?php /** * Created by PhpStorm. * User: Belal * Date: 10/5/2017 * Time: 11:31 AM */ define('DB_HOST', 'localhost'); define('DB_USER', 'root'); define('DB_PASS', 'password'); define('DB_NAME', 'simplifiedcoding'); define('UPLOAD_PATH', '/uploads/');
Connecting to Database
Now we will connect to our database. For this create a php class named DbConnect.php and write the following code.
<?php /** * Created by PhpStorm. * User: Belal * Date: 10/5/2017 * Time: 11:31 AM */ class DbConnect private $con; public function connect() require_once dirname(__FILE__) . '/Constants.php'; $this->con = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME); if (mysqli_connect_errno()) echo 'Failed to connect ' . mysqli_connect_error(); return null; return $this->con;
The Uploads Directory
Create a directory inside your project named uploads. In this folder we will store all the images uploaded by the user.
Tumblr media
Handling File Upload and Download
Now we will create one more class named FileHandler.php to save and retrieve the images.
<?php /** * Created by PhpStorm. * User: Belal * Date: 10/5/2017 * Time: 11:30 AM */ class FileHandler private $con; public function __construct() require_once dirname(__FILE__) . '/DbConnect.php'; $db = new DbConnect(); $this->con = $db->connect(); public function saveFile($file, $extension, $desc) $name = round(microtime(true) * 1000) . '.' . $extension; $filedest = dirname(__FILE__) . UPLOAD_PATH . $name; move_uploaded_file($file, $filedest); $url = $server_ip = gethostbyname(gethostname()); $stmt = $this->con->prepare("INSERT INTO images (description, url) VALUES (?, ?)"); $stmt->bind_param("ss", $desc, $name); if ($stmt->execute()) return true; return false; public function getAllFiles() $stmt = $this->con->prepare("SELECT id, description, url FROM images ORDER BY id DESC"); $stmt->execute(); $stmt->bind_result($id, $desc, $url); $images = array(); while ($stmt->fetch()) $temp = array(); $absurl = 'http://' . gethostbyname(gethostname()) . '/ImageUploadApi' . UPLOAD_PATH . $url; $temp['id'] = $id; $temp['desc'] = $desc; $temp['url'] = $absurl; array_push($images, $temp); return $images;
Handling API Calls
Lastly we need to handle the calls of our Android App. So for this we will create a php file named Api.php and in this file we will handle all the api calls.
<?php /** * Created by PhpStorm. * User: Belal * Date: 10/5/2017 * Time: 11:53 AM */ require_once dirname(__FILE__) . '/FileHandler.php'; $response = array(); if (isset($_GET['apicall'])) switch ($_GET['apicall']) case 'upload': if (isset($_POST['desc']) && strlen($_POST['desc']) > 0 && $_FILES['image']['error'] === UPLOAD_ERR_OK) $upload = new FileHandler(); $file = $_FILES['image']['tmp_name']; $desc = $_POST['desc']; if ($upload->saveFile($file, getFileExtension($_FILES['image']['name']), $desc)) $response['error'] = false; $response['message'] = 'File Uploaded Successfullly'; else $response['error'] = true; $response['message'] = 'Required parameters are not available'; break; case 'getallimages': $upload = new FileHandler(); $response['error'] = false; $response['images'] = $upload->getAllFiles(); break; echo json_encode($response); function getFileExtension($file) $path_parts = pathinfo($file); return $path_parts['extension'];
Now lets test our API and for this we can use any REST Client. I am using POSTMAN.
Tumblr media
Testing API
So our API is working fine. Now lets move into the Android Part.
Retrofit Upload File Tutorial
Here begins the main part. The first step always is creating a new Android Studio Project.
Creating a new Project
I have created a project named RetrofitFileUpload.
Now once the project is completely loaded we will add the Retrofit and Gson to it.
Adding Libraries
Come inside app level build.gradle file and add the following given lines to it inside the dependencies block.
dependencies compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', exclude group: 'com.android.support', module: 'support-annotations' ) compile 'com.android.support:appcompat-v7:26.+' compile 'com.android.support.constraint:constraint-layout:1.0.2' //these two lines are added compile 'com.squareup.retrofit2:retrofit:2.3.0' compile 'com.squareup.retrofit2:converter-gson:2.2.0' testCompile 'junit:junit:4.12'
Now sync your project.
Creating the Model Class for Response
The response we are getting from the server is.
"error": false, "message": "File Uploaded Successfullly"
So to parse it we will create a model class. So create a class named MyResponse.java and write the following code.
package net.simplifiedlearning.retrofitfileupload; /** * Created by Belal on 10/5/2017. */ public class MyResponse boolean error; String message;
Creating API Interface
Now we need an interface to define all the API calls. So create an interface named Api.java and write the following code.
package net.simplifiedlearning.retrofitfileupload; import okhttp3.RequestBody; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.http.Multipart; import retrofit2.http.POST; import retrofit2.http.Part; /** * Created by Belal on 10/5/2017. */ public interface Api //the base URL for our API //make sure you are not using localhost //find the ip usinc ipconfig command String BASE_URL = "http://192.168.43.124/ImageUploadApi/"; //this is our multipart request //we have two parameters on is name and other one is description @Multipart @POST("Api.php?apicall=upload") Call<MyResponse> uploadImage(@Part("image\"; filename=\"myfile.jpg\" ") RequestBody file, @Part("desc") RequestBody desc);
Uploading the File
Creating Interface
To upload a file we need a file, and to get the file we will create a file chooser. So we will create a button and by tapping this button we will open a file chooser activity.
So first create a button inside activity_main.xml.
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="net.simplifiedlearning.retrofitfileupload.MainActivity"> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:text="Upload Image" /> </RelativeLayout>
Checking the Read Storage Permission
So if the android is running the version greater than Lollipop then we need to ask for the permission on run time. But we also need to define the permission in the manifest file. The permission here is the Read Storage Permission as to upload a file first we need to read the file from the storage and without storage permission we cannot do it.
So first open AndroidManifest.xml and define the read permission.
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="net.simplifiedlearning.retrofitfileupload"> <!-- read and internet permission --> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
We also defined the internet permission as this is also needed. But we don’t need to ask internet permission on runtime.  So on onCreate() we will add the following code.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:" + getPackageName())); finish(); startActivity(intent); return;
The above code will stop the application and open the settings page, if the app is not having the read storage permission.
Choosing a File
We will add the following code on Button Click Event to open the file chooser.
Intent i = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); startActivityForResult(i, 100);
Now we also need to track the result of this file chooser intent. And to do this we need to override the method onActivityResult().
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) super.onActivityResult(requestCode, resultCode, data); if (requestCode == 100 && resultCode == RESULT_OK && data != null) //the image URI Uri selectedImage = data.getData();
We got the image Uri but it is not enough we need to actual image path. For this we will create one more method.
Getting Absolute Path from Uri
For getting the absolute path we can use the following method.
/* * This method is fetching the absolute path of the image file * if you want to upload other kind of files like .pdf, .docx * you need to make changes on this method only * Rest part will be the same * */ private String getRealPathFromURI(Uri contentUri) String[] proj = MediaStore.Images.Media.DATA; CursorLoader loader = new CursorLoader(this, contentUri, proj, null, null, null); Cursor cursor = loader.loadInBackground(); int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); cursor.moveToFirst(); String result = cursor.getString(column_index); cursor.close(); return result;
Uploading the File
Now to upload the file we will create the following method.
private void uploadFile(Uri fileUri, String desc) //creating a file File file = new File(getRealPathFromURI(fileUri)); //creating request body for file RequestBody requestFile = RequestBody.create(MediaType.parse(getContentResolver().getType(fileUri)), file); RequestBody descBody = RequestBody.create(MediaType.parse("text/plain"), desc); //The gson builder Gson gson = new GsonBuilder() .setLenient() .create(); //creating retrofit object Retrofit retrofit = new Retrofit.Builder() .baseUrl(Api.BASE_URL) .addConverterFactory(GsonConverterFactory.create(gson)) .build(); //creating our api Api api = retrofit.create(Api.class); //creating a call and calling the upload image method Call<MyResponse> call = api.uploadImage(requestFile, descBody); //finally performing the call call.enqueue(new Callback<MyResponse>() @Override public void onResponse(Call<MyResponse> call, Response<MyResponse> response) if (!response.body().error) Toast.makeText(getApplicationContext(), "File Uploaded Successfully...", Toast.LENGTH_LONG).show(); else Toast.makeText(getApplicationContext(), "Some error occurred...", Toast.LENGTH_LONG).show(); @Override public void onFailure(Call<MyResponse> call, Throwable t) Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_LONG).show(); );
Now we just need to club everything inside our MainActivity.java.
package net.simplifiedlearning.retrofitfileupload; import android.Manifest; import android.content.Intent; import android.content.pm.PackageManager; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.provider.MediaStore; import android.provider.Settings; import android.support.v4.content.ContextCompat; import android.support.v4.content.CursorLoader; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Toast; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import java.io.File; import java.io.IOException; import okhttp3.MediaType; import okhttp3.RequestBody; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; public class MainActivity extends AppCompatActivity @Override protected void onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //checking the permission if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:" + getPackageName())); finish(); startActivity(intent); return; //adding click listener to button findViewById(R.id.button).setOnClickListener(new View.OnClickListener() @Override public void onClick(View view) //opening file chooser Intent i = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); startActivityForResult(i, 100); ); @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) super.onActivityResult(requestCode, resultCode, data); if (requestCode == 100 && resultCode == RESULT_OK && data != null) //the image URI Uri selectedImage = data.getData(); //calling the upload file method after choosing the file uploadFile(selectedImage, "My Image"); private void uploadFile(Uri fileUri, String desc) //creating a file File file = new File(getRealPathFromURI(fileUri)); //creating request body for file RequestBody requestFile = RequestBody.create(MediaType.parse(getContentResolver().getType(fileUri)), file); RequestBody descBody = RequestBody.create(MediaType.parse("text/plain"), desc); //The gson builder Gson gson = new GsonBuilder() .setLenient() .create(); //creating retrofit object Retrofit retrofit = new Retrofit.Builder() .baseUrl(Api.BASE_URL) .addConverterFactory(GsonConverterFactory.create(gson)) .build(); //creating our api Api api = retrofit.create(Api.class); //creating a call and calling the upload image method Call<MyResponse> call = api.uploadImage(requestFile, descBody); //finally performing the call call.enqueue(new Callback<MyResponse>() @Override public void onResponse(Call<MyResponse> call, Response<MyResponse> response) if (!response.body().error) Toast.makeText(getApplicationContext(), "File Uploaded Successfully...", Toast.LENGTH_LONG).show(); else Toast.makeText(getApplicationContext(), "Some error occurred...", Toast.LENGTH_LONG).show(); @Override public void onFailure(Call<MyResponse> call, Throwable t) Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_LONG).show(); ); /* * This method is fetching the absolute path of the image file * if you want to upload other kind of files like .pdf, .docx * you need to make changes on this method only * Rest part will be the same * */ private String getRealPathFromURI(Uri contentUri) String[] proj = MediaStore.Images.Media.DATA; CursorLoader loader = new CursorLoader(this, contentUri, proj, null, null, null); Cursor cursor = loader.loadInBackground(); int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); cursor.moveToFirst(); String result = cursor.getString(column_index); cursor.close(); return result;
Now try running your application you will see something like this.
Tumblr media
Retrofit Upload File Tutorial
You can see it is working absolutely fine.
Downloading Images Back
We have already created an API call to fetch the images.
Tumblr media
Now we will use this API Call to fetch the images. And the best thing is I already posted a tutorial of Retrofit where you can learn fetching JSON from server. And I also posted a tutorial about creating RecyclerView using JSON data. So visit the posts from the below link to complete the fetching part of this tutorial.
Retrofit Android Example – Fetching JSON from URL
Expandable RecyclerView Android – RecyclerView with Expandable Items
Retrofit Upload File Source Code
In case you are having any troubles you can get my source code from here. The source code contains all the php scripts and the android project.
Retrofit Upload File Source Code Download
So thats all for this Retrofit Upload File Tutorial guys. If you are having any questions then please leave your comments. And if you found this post helpful then please SHARE it. Thank You 🙂
0 notes
wirewitchviolet · 2 years ago
Text
The Entire Plot of Final Fantasy 14, with all the expansions, and some serious analysis of how good it actually is. (Part 1 - ARR)
Are you like me? Have you had people talking your ear off for years now about this supposedly amazingly good story Final Fantasy 14 has? And then you noticed that absurdly good deal free trial where you get the whole base game plus the first expansion for free and don’t have to pay a dime until you hit the later expansions? And you played through all of that because you had a serious medical issue and it was the only game you could play while forced to lay in bed for the better part of a year where you could make out enough of the screen to play the grindy bits since you happened to have a wireless controller? And then someone was super nice and gifted you the expansions and like half a year’s worth of time cards, and you decided to just marathon through so you can talk about it on your blog? No? Good because then you wouldn’t have a reason to read this.
Also just to get this right out of the way up front- Does FF14 have a great plot? No. No absolutely not. It is TERRIBLY paced and none of the characters have any personality until you’re into the expansions and it’s really awful with women in places and absolutely terrible at having any deaths stick and it tries to do this Game of Thrones sort of thing where people use “whoreson” as an insult and say “anyroad” instead of “anyway” and there’s some pretty sloppy retcons and TERRIBLE redemption arcs after a point. It does have a lot of well-written bits scattered through it though, it makes some interest choices structurally, and bends over backwards for some references that pander nicely to me, so, it’s worthy of discussion. This is going to be long, by the way. May have to multipart it.
Before I get into anything I have to talk about the structure here. FF14 is straight up written like it’s a single player JRPG. You are controlling a specific named character with a ton of personal backstory, you have a party of named characters who you end up going everywhere with after a while, there are full on perspective shifts later on where you control one of them instead of the main character for bits, there’s honestly probably more dialog than combat, and narratively speaking all those other players straight up do not even exist (with a couple interesting exceptions). In fact, as of when I’m writing this (patch 6.3) it is a legit major design goal to legitimately retrofit the whole thing into a true single player game where you can go into all the dungeons and boss fights with a party of plot siginficant NPCs instead of human party members and they have custom dialog and abilities and quirky little explorative stuff they do.
Also, there is a single specific main questline that must be followed from the very start of the game through to the end credits, then continuing through all the free content patches between the base game and first expansion, the next set of free patches, next expansion, etc. and you are just straight up hard locked out of everything until you reach it in the order it was added to the game. Which... I rather dig from the angle of seeing everything grow and mature sequentially, but then they do totally screw that up by doing total rewrites of class abilities without changing class specific quests so you get stuff like the summoner quest line talking about being a tactician and ordering your pets around the battlefield, but at present you don’t really have pets just pet shaped damage spells and it’s weird. Oh and that’s another thing. It goes all FF5 with the classes, so rather than make an alt for every class you make you just kinda have one character, and level up in every class you’re inclined to pick up seperately, which is nice honestly.
Finally there’s the whole “1.0″ thing. When FF14 first came out, it was generally agreed to be Not Great, and concerns with its quality were met in a way I honestly quite dig. Things played up to a big huge climax, everyone’s fighting the main antagonists in this big protracted battle, and then there is a full on apocalypse, absolute trashing the entire setting, killing a ton of major NPCs and possibly all the player characters. Then they made... really a sequel to that game, set like 5 years later I think it was, and that’s the FF14 we have today. All the “1.0″ stuff is backstory that honestly gets really heavily referenced and called back to, but can no longer actually be played through, and... look I tried watching a longplay but it was really boring so this is the one big hole in my research. The intro to the game shows the whole apocalypse bit, fun times.
That out of the way, let’s get into the actual base game, AKA 2.0 AKA
A Realm Reborn
Despite ostensibly being “the whole game” this is the part of the game everyone agrees is kinda just total garbage you just want to get over with as quickly as you can, and it gets much better once you do. I really figured going in that was just stockholm syndrome but no it actually is hiliarious how sharply things turn upward the instant you hit the official endpoint of this and get into the first batch of content patches. They’re just responding to feedback and trying to make characters more likeable, adding the first batch of dungeons and bosses with actual interesting mechanics, etc. It really is night and day. But slog through it I did.
Starting things off, you are Some Person, hailing from an Unspecified Place, to one of the three (really six) major cities in the continent of Eorzea based on what you picked as a starting class, either riding in a covered wagon or a ship. Oh and there’s a bit of an abstract intro before that where you’re floating around in space while a bit crystal says some stuff to you but we’ll get to that. One thing I do really like is that these three major cities remain relevant through the whole game. Most MMOs start you off in Newbie Town and from there you just kinda journey out through an endless parade of new towns until maybe hitting the pre-expansions Main Faction Hub or whatever but here, no, there’s basically just these three cities, you are constantly touching base with them, so low level and high level characters are constantly mingling and passing each other.
There’s a unique quest string in each city up to a certain point where you start regularly traveling between them, but they all kinda boil down to the same thing. Go to the local inn, get asked to do some basic errands by the proprietor, eventually one of these leads into some robed guy siccing a big monster on you, one of the eventual party members you get hooked up with showing up and going “hey nice job” with that, getting the first of these 6 crystals that summons you to almost literally the room with the sages from Ocarina of Time, and introducing the concept that you have this special rare power called The Echo where at usually inconvenient times you pass out and have a flashback to some event involving a person/object/location that’s at hand, which basically just exists as an excuse to exposition dump at you, but also comes with the handy side effect that you’re largely immune to mind control, which will come up.
After that you go back to town, the game starts a rather terrible habit of having every NPC there is talk you up as just the best person who ever lived, and sends you off to meet the other two heads of state to plan a big commemorative event for the whole apocalypse thing. After that these two basically identical little elf twins tag along as all the leaders give speeches, and badmouth them, after which you pick one of the three to formally swear allegiance to which ulitimately effects basically nothing except maybe what team you’re on in some “training exercise” PVP content you’re probably going to totally ignore, and which town you come back to for this one optional questline where you get to put together a little squad of people and send them off on missions. That said though, a quick rundown of things:
We have Limsa Lominsa, a city where a big ol’ fleet of pirate ships ran aground on some rocks and a nice fertile island, and decided to just kinda extend the rigging into the rocks and make this half island half ship and keep calling the leader of the city the Admiral. Said leader is Merlwyb, a huge woman with the sort of sideburns I’ve never seen on a cis woman and despite being told she’s bloodthirsty and racist she spends the whole game taking in refugees negotiating peace accords with “monster” races, and at one point straight up tags along with you to just shoot some cult leader to hopefully avoid a boss fight. She’s honestly great and one of like two likeable NPCs in the whole base game.
East of there we have Ul’dah, the desert city, which is just kind of this ultra-capitalist hellscape where a big mostly-markets city is surrounded by a shanty town full of refugees from the nearby country that’s been occupied by the evil empire for the past 20 years. Nominally the leader is Sultana Nanamo, a member of the race who all look like toddlers and are almost to the last sleazy capitalist scumbags. She’s relatively OK but also she’s a total figurehead while a corrupt merchant council controls most things and her big bruiser former arena champ bodyguard/boyfriend Rauban is the actual functional leader. He’s the other one likeable NPC.
Then finally northeast of that there’s Gridania, the chill hippie town in the peaceful/creepy giant semi-haunted forest where the leadership is a bunch of people born with big ol’ horns who can communicate with primeval elemental spirits at least in theory, and specifically by a woman named Kan-E-Senna who for the life of me doesn’t ever do one single thing I can think of worth mentioning except I think she’s maybe dating Merlwyb. Also the whole communing with ancient forest gods thing, given the main gimmick of the setting I’ll get to shortly seems like kind of just this gigantic red flag and it’s damn weird these people never get called out on this.
Anyway once you’ve settled in and you’ve run through the real early game basically tutorial dungeon set, you find yourself constantly getting called back to this one little nothing town on the edge of the map to have meetings with those eventual party members I mentioned, The Scions of the Seventh Dawn. I say “eventual party members” because once you’re a couple expansions deep they’re actually nicely fleshed out and there’s bits where you run through dungeons and stuff with them but at this point they all just sit around in a boardroom giving you mission briefings and having no personality. Real quick rundown though why not?
Tumblr media
From right to left here, we have Thancred, sleazy sketchy scumbag rogue type, Y’shtola, nerdy white mage, Minfilia, leader and soggy piece of white bread who never gets any character development at all (but apparently had a good bit in 1.0), the broken staff of Louisoix, the old wise guy who used to lead these people in 1.0 and nobly sacrificed himself in the apocalypse, the youtuber I stole this screenshot from, Papalymo a stick in the mud dad type, Yda who wears a weird mask and kicks people and Urianger, a gigantic nerd in a hoodie with ear socks who does have some characterization but it’s just constantly throwing out thee and thou and perchange and generally managing to be the sort of creepy nerd who calls you “m’lady” within a fantasy setting which is an impressive trick. I absolutely hated Urianger until I didn’t. Oh and weirdly not featured here are the elf twins, grandchildren of Louisoix, Alphinaud, who’s kind of this dorky blueblood idealistic loser, and Alisaie who.... actually isn’t with these people at all and is kind of off doing her own thing until the lead up to the second expansion.
Anyway these people primarily exist as a little think tank/independent squandron here to do the big damn hero thing, particularly regarding The Most Interesting Idea In FF14, which I’m about to get to, but functionally they literally all just sit around and have you do everything.
So yeah, The Most Interesting Idea In FF14- We’ve got this big buzzword, aether, which is... kind of all matter and energy but mostly found in big honking crystals that’s kinda the elementally tinged lifeblood of the planet and also the basis for any crafting and factors into any and all technobabble. Most importantly though, if you have a giant pile of aether crystals handy, and you pray to your personal conception of a savior, you summon a god (AKA eikon AKA primal AKA recognizable classic FF summon). Said god will maybe try to help you with your problem, but will mostly suck all aether out of the local area, which is bad for the environment, and odd human sacrifice, and also just permanently mess with the minds of anyone in their immediate vicinity to turn them into an absolutely fanatical worshiper. Plus they’re all big set piece-y boss fights, usually with really cool soundtracks.
What’s nice about this is, hey, first off, excuse for a cool boss fight, and there’s even a baked in excuse to have harder refights against them later, because people can always just summon them again after you kill them. But in addition to boiling a lot of problems down to “how about you the video game protagonist go punch this big monster until it explodes?” this also sets up a nice set of long term problem with no easy answer. There’s a conflict between the humans (by which I mean the somewhere between 4 and 7 or 8 races generally agreed to be people/playable character options) and one of what are referred to as “beast tribes” (although later we drop the beast because one of them in a later expansion is kinda literally one of the playable races and people realized it’s messed up) over who gets to live in a plot of land or whatever. Humanity comes in with superior weapons, the kobolds or whatever get desperate and pray up a god, they go fanatic, start doing really depraved stuff to the humans, things escalate more, there’s retaliation, they pray again, etc. etc. It’s an idea that gets taken pretty seriously, really examined from a lot of angles, and sets up what I think are consistently the strongest parts of the game, where you end up befriending various monster-folk who aren’t constantly summoning gods and help them out with their personal self-contained questlines.
Anyway, you waste a bit of time, eventually go off to deal with a violent lizard people problem which ends in them summoning Ifrit, hey cool, you didn’t get mind controlled, and got another of those Zelda room crystals. You sit through a TON of absolute shameless filler, like finding out Ramuh’s been summoned but ultimately not fighting him because he’s honestly being reasonably chill, and which includes a string of like 6 quests where you’re trying to get hints on fighting Titan out of someone who insists on having a big huge wine and cheese party you need to get supplies for and ultimately is a fraud anyway. But eventually yeah, kobolds summoned Titan, you go smack him down.
Then we have more filler, but also a bit of a nice unexpected twist where the evil empire finds out where your base is and one of these four big named Imperial general types with the really over the top helmets just kinda walks in with a gun and kills everyone.
Tumblr media
Things go a bit grim for a bit. There’s literally a quest where you have to gather up a bunch of bodies and cart them off to a cemetery on the other side of the country, where you just kinda hang out aimlessly until noticing that wait, one of these people in this church is Cid in disguise. This is kind of a weird bit of writing because while presumably Cid was established a bit in 1.0, in the game we actually have today he is literally never mentioned one single time before you run into him here. But, you know, it’s an FF game. We’ve got a guy named Cid, he’s a super talented engineer, he’s got an airship. We also have a Wedge and a Biggs, they work for him in this case. And oh hey it turns out the massive pile of corpses in your HQ somehow didn’t include any of those party members who suck and who cares but will get development later, which kinda ticks me off but again, get used to nobody staying dead. Oh and there’s also a side trek up to Coerthas, the huge frozen sprawling wasteland north of Gridania that mostly serves to start teasing a bit of how the first expansion is going to be about a bunch of classist Catholic elves having a big ol’ war with dragons but honestly I mostly just remember having to talk to NPCs behind enough doors and stairways to constantly get lost looking for them.
Eventually though you get back to your job as designated god slayer, go fight summon number 3, Garuda, courtesy of some weird bird people, and then there’s a scene that’s just plain fun, where turns out the locals gathered up enough crystals to immediately summon her again and also have some of the other monster folk who summoned the first two bosses and there’s enough crystals around for them to also resummon their gods and... eh it’s worth a youtube link to like the most amusing scene in the base game, sure.
youtube
So yeah, we’re getting all set for our big cheesy summoned god Mexican Standoff and then the (local) leader of the evil empire just shows up out of nowhere and airdrops in the Ultima Weapon, which... has always looked like this for all prior FF appearances but only watching this did it really sink in for me what an absolutely ridiculous robot-dragon-centaur it is. Said goofy robot kills and eats all 3 gods, stealing their power, and then gets brought home to the main imperial fortress in the religion to hang out until it’s time to be the final boss.
And then we have more filler for a bit. Not nearly so much as usual, but we do have this 5th country between Ul’dah and Coerthas with the main imperial base to poke around in a bit before the endgame. A bit of this involves Cid and Pals commandeering a fun bit of FF6 fan service you get to tool around with a little before planning your big assault.
Tumblr media
So yeah, end of the base game, the pacing stops being so terrible. We get a quick fight against Imperial General #1 which when I first started playing was an absolutely hilarious pushover because it’s the first time you get a party of 8 people instead of 4 and it’s just the one guy, but has since been revamped into a surprisingly tense thing you do solo, then an assault on an outpost where Cid’s playing with the toy shown above blowing down walls for you and you kill the general who killed uh... whoever was in your HQ that wasn’t important earlier, then the proper stronghold where there’s a fun bit where everyone gets to hop in a mech and fire off machine guns and missiles everywhere before general #3, the head Imperial in the region (who gives this real long I am a nazi piece of trash and I love to hear myself talk speech that I have completely committed to memory because it’s unskippable and the game frequently bribes people to come back and fill party slots for first-timers), and that goofy Ultima Weapon. Oh and Lahabrea.
I haven’t mentioned Lahabrea because he sucks and also matters so little to anything I genuinely didn’t have cause to mention him. He’s in that cutscene I linked though. So I should get this out of the way I guess.
So OK, there are three main antagonist factions in FF14. There’s random monster men summoning their gods, usually because frankly humans are doing colonialisms and threatening to wipe them out. There’s the evil empire which really doesn’t do a whole lot on camera? I feel like they were more active in 1.0 but really we just get a couple shots of generals plotting, the one bit where your base gets gunned down, the Ultima Weapon drop-in, and then the series of assault stuff at the end, but they are established pretty well as properly hate-worthy nazis. And then we have the Ascians.
The Ascians suck so bad. They are these cloaked hooded masked mystery weirdos who just kind of watch from the shadows and play evil vizier to... really literally anyone who ends up being an antagonist anywhere in the game. They taught monster men how to summon gods, they encourage the evil empire to be evil and dig up ancient high tech ruins. The... only one who I think gets named properly in the base game here possesses Thancred for a while but not only is he fine after this happens before any real effort is made to establish Thancred as a character so who even cares? And their goals and motivations are shrouded in mystery. By which I definitely mean the writers just through in some generically evil mystery weirdos and hoped nobody would notice they don’t do a single damn thing of substance with the vague plan to maybe do something interesting somewhere down the road. The 3rd expansion eventually gets around to developing one of them as an actual character and giving them a whole backstory and agenda that’s kind of interesting but really none of what gets done with them before, or for that matter after that point makes any sense. We just have a few random scenes where some guy in a black cloak goes all “yes, yes, just as planned, we’ve been watching you” and it’s transparently freaking nothing. And then you kill one as a cutsceney final boss.
And that’s it. That’s the whole base game of FF14. There is a TON of filler, a couple pointless speed bump dungeons, 3 boss fights (that are all total cakewalks), and a big assault on an imperial stronghold with a 4th proper boss fight towards the end. And there’s such obvious blatant signs of cut content too. Again there’s this whole Zelda medallion chamber to track your progress, you fill it with these 6 elemental crystals, 3 of those come from summons you kill, and the other 3 are kinda just... sitting there. We tease the hell out of fights with Ramuh and Leviathan that don’t happen. We don’t really mention Shiva but you always have Shiva early on in an FF game. Those three boss fights do totally make it in the first free patch wave along with a few others, but... you can see where they were cut out and replaced by finding fancy cheese for some random doofus.
It’s MISERABLY paced (count the main story quests on this list! Most of these are just wandering around talking to people as you slowly discuss plans for boss fights), the main villains barely get introduced before you fight them, the supporting cast isn’t fleshed out at all, and the gameplay for this leg of thing is dull as dirt too.
Side content is a bit better. There’s... well two little quest lines actually for every class in the game, a couple of which are a bit interesting. Black mages make a bunch of monster race friends and team up to close portals to hell. Scholars get into this bit learning how Tonberries (the green cloaked little monsters who slowly walk up and stab you with knives) are actually normal people suffering from a horrible plague. The rogue/ninja class wasn’t actually added in until the patches but it’s the one later edition you can access almost right away and has a fun bit with a charming thieves-cant-talking pirate-country-protecting assassin squad, and then there’s those “beast tribe” quests which again, I really like.
We’ve got the Proud Warrior Race guy lizard people, one of whom is a cat girl, and she has a big ol’ chip on her shoulder about it, while you help her resolve issues about her birth mother. We’ve got a fish guy who used to be a badass murderous warrior but became a dad and mellowed out and is dealing with some of his kids growing up to be jerks. We’ve got flightless bird people with a religion that’s basically just Laputa, wanting help building a high altitude airship to search for their promised land. This weird thing with mischievous fairy type plant people pranking each other and fighting for custody of like... plant fairy baby Jesus, and... OK the kobold quests are this dumb romantic rivalry but the mount you get for it is great.
Tumblr media
Next time- “It gets good after level 50 I swear!” (and time allowing “The Award-Winning Heavensward Expansion”)
18 notes · View notes