public bool IsUnlocked(int accountID)
        {
            if (StartsUnlocked)
            {
                return(true);
            }

            if (CampaignPlanet != null)
            {
                if (CampaignPlanet.IsCompleted(accountID) && UnlockOnPlanetCompletion)
                {
                    return(true);
                }
                if (CampaignPlanet.IsUnlocked(accountID) && UnlockOnPlanetUnlock)
                {
                    return(true);
                }
            }

            var db = new ZkDataContext();
            AccountCampaignJournalProgress progress = db.AccountCampaignJournalProgress.FirstOrDefault(x => x.AccountID == accountID && x.CampaignID == CampaignID && x.JournalID == JournalID);

            return(progress != null && progress.IsUnlocked);
        }
        public static void ProgressCampaign(int accountID, int missionID, string missionVars = "")
        {
            ZkDataContext db = new ZkDataContext();
            CampaignPlanet planet = db.CampaignPlanets.FirstOrDefault(p => p.MissionID == missionID);
            if (planet != null)
            {
                Account acc = db.Accounts.First(x => x.AccountID == accountID);
                bool alreadyCompleted = false;
                int campID = planet.CampaignID;
                Campaign camp = planet.Campaign;
                List<int> unlockedPlanetIDs = new List<int>();
                List<int> unlockedJournalIDs = new List<int>();
                //List<CampaignPlanet> unlockedPlanets = new List<CampaignPlanet>();
                //List<CampaignJournal> unlockedJournals = new List<CampaignJournal>();

                // start with processing the mission vars, if there are any
                byte[] missionVarsAsByteArray = System.Convert.FromBase64String(missionVars);
                string missionVarsDecoded = System.Text.Encoding.UTF8.GetString(missionVarsAsByteArray);
                foreach (string kvpRaw in missionVarsDecoded.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
                {
                    string kvpRaw2 = kvpRaw.Trim();
                    string key = "", value = "";
                    string[] kvpSplit = kvpRaw2.Split(new[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
                    if (kvpSplit.Length == 2)
                    {
                        key = kvpSplit[0].Trim();
                        value = kvpSplit[1].Trim();
                    }
                    else
                    {
                        throw new Exception("Invalid key-value pair in decoded mission vars: " + missionVarsDecoded);
                    }
                    if (!(string.IsNullOrEmpty(key) || string.IsNullOrEmpty(value)))
                    {
                        CampaignVar cv = camp.CampaignVars.First(x => x.KeyString == key);
                        AccountCampaignVar acv = acc.AccountCampaignVars.FirstOrDefault(x => x.CampaignID == campID && x.VarID == cv.VarID);
                        if (acv == null)
                        {
                            db.AccountCampaignVars.InsertOnSubmit(new AccountCampaignVar() { AccountID = accountID, CampaignID = campID, VarID = cv.VarID, Value = value });
                        }
                        else acv.Value = value;
                    }
                }

                //reload DB - this allows the vars submitted this session to be used by the following code
                db.SubmitChanges();
                db = new ZkDataContext();
                acc = db.Accounts.First(x => x.AccountID == accountID);
                planet = db.CampaignPlanets.FirstOrDefault(p => p.MissionID == missionID);
                camp = planet.Campaign;

                // now we unlock planets and journal entries
                // first mark this planet as completed - but only if it's already unlocked
                AccountCampaignProgress progress = acc.AccountCampaignProgress.FirstOrDefault(x => x.PlanetID == planet.PlanetID && x.CampaignID == planet.CampaignID);
                if (progress != null)
                {
                    alreadyCompleted = progress.IsCompleted;
                }
                else if (planet.StartsUnlocked)
                {
                    progress = new AccountCampaignProgress() { AccountID = accountID, CampaignID = campID, PlanetID = planet.PlanetID, IsCompleted = false, IsUnlocked = true };
                    db.AccountCampaignProgress.InsertOnSubmit(progress);
                }

                if (progress != null && planet.IsUnlocked(accountID))
                {
                    progress.IsCompleted = true;


                    // unlock planets made available by completing this one
                    var links = camp.CampaignLinks.Where(x => x.UnlockingPlanetID == planet.PlanetID);
                    foreach (CampaignLink link in links)
                    {
                        CampaignPlanet toUnlock = link.PlanetToUnlock;
                        bool proceed = true;
                        var requiredVars = toUnlock.CampaignPlanetVars;
                        if (requiredVars.Count() == 0) proceed = true;
                        else
                        {
                            foreach (CampaignPlanetVar variable in requiredVars)
                            {
                                AccountCampaignVar accountVar = acc.AccountCampaignVars.FirstOrDefault(x => x.CampaignID == campID && x.VarID == variable.RequiredVarID);
                                if (!(accountVar != null && accountVar.Value == variable.RequiredValue))
                                {
                                    proceed = false;
                                    break;  // failed to meet var requirement, stop here
                                }
                            }
                        }

                        if (proceed)    // met requirements for unlocking planet
                        {
                            AccountCampaignProgress progress2 = toUnlock.AccountCampaignProgress.FirstOrDefault(x => x.CampaignID == campID && x.AccountID == accountID);
                            if (progress2 == null)
                            {
                                progress2 = new AccountCampaignProgress() { AccountID = accountID, CampaignID = campID, PlanetID = toUnlock.PlanetID, IsCompleted = false, IsUnlocked = true };
                                db.AccountCampaignProgress.InsertOnSubmit(progress2);
                                unlockedPlanetIDs.Add(toUnlock.PlanetID);
                            }
                            else if (!progress2.IsUnlocked)
                            {
                                progress2.IsUnlocked = true;
                                unlockedPlanetIDs.Add(toUnlock.PlanetID);
                            }
                        }
                    }
                }
                // unlock journals
                var journalsWithVars = db.CampaignJournals.Where(x => x.CampaignID == campID && x.CampaignJournalVars.Any());
                foreach (CampaignJournal journal in journalsWithVars)
                {
                    bool proceed = true;

                    var requiredVars = journal.CampaignJournalVars.Where(x => x.CampaignID == campID).ToList();
                    foreach (CampaignJournalVar variable in requiredVars)
                    {
                        AccountCampaignVar accountVar = acc.AccountCampaignVars.FirstOrDefault(x => x.CampaignID == campID && x.VarID == variable.RequiredVarID);
                        if (!(accountVar != null && accountVar.Value == variable.RequiredValue))
                        {
                            proceed = false;
                            break;  // failed to meet var requirement, stop here
                        }
                    }

                    if (proceed)    // met requirements for unlocking journal
                    {
                        System.Console.WriteLine(journal.Title);
                        AccountCampaignJournalProgress jp = journal.AccountCampaignJournalProgress.FirstOrDefault(x => x.AccountID == accountID);
                        if (jp == null)
                        {
                            jp = new AccountCampaignJournalProgress() { AccountID = accountID, CampaignID = campID, JournalID = journal.JournalID, IsUnlocked = true };
                            db.AccountCampaignJournalProgress.InsertOnSubmit(jp);
                            unlockedJournalIDs.Add(journal.JournalID);
                        }
                        else if (!jp.IsUnlocked)
                        {
                            jp.IsUnlocked = true;
                            unlockedJournalIDs.Add(journal.JournalID);
                        }
                    }
                }
                db.SubmitChanges();
                db = new ZkDataContext();
                planet = db.CampaignPlanets.FirstOrDefault(p => p.MissionID == missionID);

                //throw new Exception("DEBUG: Campaign " + campID + ", planet " + planet.PlanetID);
                if (!alreadyCompleted)
                {
                    db.CampaignEvents.InsertOnSubmit(Global.CreateCampaignEvent(accountID, campID, "Planet completed: {0}", planet));
                    foreach (CampaignJournal journal in db.CampaignJournals.Where(x => x.CampaignID == campID && x.PlanetID == planet.PlanetID && x.UnlockOnPlanetCompletion))
                    {
                        unlockedJournalIDs.Add(journal.JournalID);
                    }
                }
                foreach (int unlockedID in unlockedPlanetIDs)
                {
                    CampaignPlanet unlocked = db.CampaignPlanets.FirstOrDefault(x => x.PlanetID == unlockedID && x.CampaignID == campID);
                    db.CampaignEvents.InsertOnSubmit(Global.CreateCampaignEvent(accountID, campID, "Planet unlocked: {0}", unlocked));
                    foreach (CampaignJournal journal in db.CampaignJournals.Where(x => x.CampaignID == campID && x.PlanetID == unlocked.PlanetID && x.UnlockOnPlanetUnlock))
                    {
                        unlockedJournalIDs.Add(journal.JournalID);
                    }
                }
                foreach (int ujid in unlockedJournalIDs)
                {
                    CampaignJournal uj = db.CampaignJournals.FirstOrDefault(x => x.JournalID == ujid && x.CampaignID == campID);
                    db.CampaignEvents.InsertOnSubmit(Global.CreateCampaignEvent(accountID, campID, "{1} - Journal entry unlocked: {0}", uj, uj.CampaignPlanet));
                }
                db.SubmitChanges();
            }
        }