var PARAMETERS = {
    "timedMemories": 0, 
    "focusPanelKey": "focusPanel", 
    "dayTicks": 150, 
    "currencyBoost": "currencyBoost", 
    "startEntities": [
        "gameStateEntity", 
        "self", 
        "day", 
        "controlPanel", 
        "chapter1", 
        "autosaver", 
        "trainPathMakingPanel", 
        "utilityPanel", 
        "dayPanel", 
        "focusPanel", 
        "buildPanel", 
        "cursor", 
        "currencyRenderEntity", 
        "songCounter", 
        "pointCounter", 
        "difficultySetting"
    ], 
    "damageIndicator": "damageIndicator", 
    "trainPathMakingPanel": "trainPathMakingPanel", 
    "lostControl": "lostControl", 
    "baseButtonName": "baseButton", 
    "buildingConnect": 1, 
    "path": "path", 
    "trainPath": "trainPath", 
    "creepHit": [
        "lostControl", 
        "selfDamageEffect", 
        "disableMemories"
    ], 
    "interactionKeys": [
        "self"
    ], 
    "currencyRenderEntity": "currencyRenderEntity", 
    "baseConnectKey": "willMine", 
    "size": [
        13, 
        23
    ], 
    "buildPanel": "buildPanel", 
    "settingsPanel": "settingsPanel", 
    "chapterPanel": "chapterPanel", 
    "selfExtension": "selfExtension", 
    "qualityMine": "qualityMine", 
    "memoryConstruction": "memoryConstruction", 
    "wishesToBeMaking": 0, 
    "buildablePaths": 0, 
    "memoryStopGoal": "memoryStopGoal", 
    "ensureExistence": [
        "difficultySetting"
    ], 
    "randomMenuMarker": "randomMenuMarker", 
    "memory": "memory", 
    "carriage": "carriage", 
    "ticksPerTick": [
        2, 
        5
    ], 
    "towerPanel": "entityPanel", 
    "selfDeadMenu": "breakdownMenu", 
    "utilityPanel": "utilityPanel", 
    "difficultySetting": "difficultySetting", 
    "junction": "junction", 
    "extensionMonitorEnabled": 0, 
    "trainPanel": "trainPanel", 
    "trainPathMaker": "trainPathMaker", 
    "newParticle": "newParticle", 
    "openMenu": "openMenu", 
    "gameStageStartMenu": "gameStageStartMenu", 
    "towerFlash": "towerFlash", 
    "train": "train", 
    "emotionSource": "emotionSource", 
    "goalComplete": "goalComplete", 
    "allTrainPanel": "allTrainPanel", 
    "maxExperience": 8, 
    "startEntityGroups": [
        "emotionState", 
        "emotionCurrencyTicker", 
        "emotionEntityMaker", 
        "notificationCurrency", 
        "emotionResourceMap", 
        "setPanel", 
        "selfExtension", 
        "currencyOverload", 
        "checkQuality", 
        "currencyOverload", 
        "currencyAction", 
        "personCurrencyAction", 
        "steamAchievement"
    ], 
    "day": "day", 
    "gameStateEntity": "gameStateEntity", 
    "addPoints": "addPoints", 
    "bullet": "bullet", 
    "sectors": [
        5, 
        8
    ], 
    "currencyRequest": "currencyRequest", 
    "clickText": "clickText", 
    "resourceModifiers": 0, 
    "traitPanel": "traitPanel", 
    "toast": "toast", 
    "pointNotification": "pointNotification", 
    "carryTrain": "carryTrain", 
    "allGoalPanel": "allGoalPanel", 
    "construction": "construction", 
    "allBuildingPanel": "allBuildingPanel", 
    "notification": "notification", 
    "autosaveKey": "autosave_songs", 
    "singleUseTrainPath": "singleUseTrainPath", 
    "defaultHeldOpinions": [
        "acquaintance"
    ], 
    "self": "self", 
    "creepSelfDamage": 10
};

var allArgs = {
    "lackAccomplishmentAction": {
        "currenciesRequired": [
            "selfLoathing"
        ], 
        "towerKey": "lackAccomplishmentTower", 
        "name": "Lack of Accomplishment", 
        "key": "lackAccomplishmentAction"
    }, 
    "indulgeCuriosityMenu": {
        "image": "goal_puzzle-000000", 
        "completeText": "Being able to do this is half the reason to make music.", 
        "name": "COMPLETED - Indulge Curiosity", 
        "key": "indulgeCuriosityMenu"
    }, 
    "callousnessAction": {
        "currenciesRequired": [
            "strength"
        ], 
        "towerKey": "callousnessTower", 
        "name": "Callousness", 
        "key": "callousnessAction"
    }, 
    "talkAboutSelfGoal": {
        "currencyEntities": {
            "talkEmotion": 25
        }, 
        "randomPosition": 0, 
        "name": "Talk About Yourself", 
        "image": "goal_goalConvinceWife-000000-000000", 
        "key": "talkAboutSelfGoal", 
        "currencyRequested": {
            "need_lover": 3
        }, 
        "entitiesToMake": "workEmotionDeposit", 
        "goalMenuKey": "talkAboutSelfMenu", 
        "description": "There's so much you want to say."
    }, 
    "deadInsideAction": {
        "currenciesRequired": [
            "pain"
        ], 
        "towerKey": "deadInsideTower", 
        "name": "Feel Dead Inside", 
        "key": "deadInsideAction"
    }, 
    "newProblem": {
        "goodEntities": {
            "techProblemDeposit": 2, 
            "coolThingDeposit": 1, 
            "easyProblemDeposit": 1, 
            "emoProblemDeposit": 2
        }, 
        "addReplaceMarker": 0, 
        "key": "newProblem"
    }, 
    "socialMuseMineButton": {
        "constructionKey": "socialMuseMineConstruction", 
        "name": "Spend Time With Muse", 
        "entityCost": {
            "will": 2
        }, 
        "image": "tower_mineSocial", 
        "currenciesMade": [
            "social_muse"
        ], 
        "entityKey": "socialMuseMine", 
        "limitingKeys": [
            "socialMuseMine", 
            "socialMuseMineConstruction"
        ], 
        "key": "socialMuseMineButton", 
        "description": "makes social"
    }, 
    "workThroughButton": {
        "constructionKey": "workThroughGoalConstruction", 
        "name": "Work Through Emotion", 
        "image": "towerThinkAbout", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "workThroughGoal", 
        "key": "workThroughButton", 
        "description": "Requires work on emotion."
    }, 
    "flagQuitButton": {
        "flagToCheck": "quit", 
        "entitiesToMake": "tellQuitButton", 
        "key": "flagQuitButton"
    }, 
    "integrateButton": {
        "constructionKey": "integrateGoalConstruction", 
        "name": "Integrate Hard Work", 
        "image": "goal_towerThinkAbout-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "integrateGoal", 
        "key": "integrateButton", 
        "description": [
            "Requires hard work.", 
            "Makes satisfaction."
        ]
    }, 
    "careDeposit": {
        "image": "tower_heart", 
        "activeImage": "tower_heart_activeOutline-0099ff", 
        "name": "Care", 
        "key": "careDeposit", 
        "deposit": {
            "care_lover": 5
        }
    }, 
    "applyTower": {
        "image": "tower_motivation", 
        "activeImage": "tower_motivation_activeOutline-0099ff", 
        "name": "Fuel Yourself", 
        "key": "applyTower", 
        "description": "Converts frustration -> will"
    }, 
    "loverFleshTowerConstruction": {
        "key": "loverFleshTowerConstruction", 
        "constructionIdentifier": "loverFleshTower", 
        "name": "Constructing - Flesh Out With Lover", 
        "image": "trait-aaaaaa99_tower_mineSocial", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Flesh Out With Lover", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "ignoreDifferencesGoal": {
        "key": "ignoreDifferencesGoal", 
        "randomPosition": 1, 
        "name": "Ignore Differences", 
        "makeOnTimeOut": [
            "alienationDeposit", 
            "aloneSad", 
            "yourAloneTime"
        ], 
        "image": "goal_social-000000", 
        "description": [
            "You and the lover really are two different people. You can't let that", 
            "get to you."
        ], 
        "sourceTrait": "hangingOut", 
        "currencyRequested": {
            "strength": 3
        }, 
        "goalMenuKey": "ignoreDifferencesMenu", 
        "timeToDeath": 240
    }, 
    "selfEsteem": {
        "name": "self-esteem", 
        "key": "selfEsteem", 
        "tags": [
            "selfEsteem"
        ]
    }, 
    "techWorkAction": {
        "currenciesRequired": [
            "techProblem", 
            "techSolution"
        ], 
        "towerKey": "techWorkTower", 
        "currenciesToMake": [
            "hardWork"
        ], 
        "name": "Complete Tech Work", 
        "key": "techWorkAction"
    }, 
    "dependenceDeposit": {
        "image": "tower_dependence", 
        "activeImage": "tower_dependence_activeOutline-0099ff", 
        "name": "Dependence", 
        "key": "dependenceDeposit", 
        "deposit": {
            "dependence": 3
        }
    }, 
    "fatigueMenu": {
        "image": "goal_droop-000000", 
        "name": "COMPLETED - Fatigue", 
        "key": "fatigueMenu"
    }, 
    "largeStrengthDeposit": {
        "image": "tower_weight", 
        "activeImage": "tower_weight_activeOutline-0099ff", 
        "name": "Strength", 
        "key": "largeStrengthDeposit", 
        "deposit": {
            "strength": 5
        }
    }, 
    "tryForMeaningMenu": {
        "image": "goal_social-000000", 
        "name": "COMPLETED - Try For Deeper Meaning", 
        "key": "tryForMeaningMenu"
    }, 
    "quietDemons": {
        "personHeld": "demon", 
        "isaacEntities": [
            "listenDemonsButton", 
            "fadeQuietDemons"
        ], 
        "image": "trait-662222", 
        "name": "Quieted Demons", 
        "key": "quietDemons"
    }, 
    "talkAboutSelfButton": {
        "constructionKey": "talkAboutSelfGoalConstruction", 
        "name": "Talk About Yourself", 
        "image": "goal_goalConvinceWife-000000-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "talkAboutSelfGoal", 
        "key": "talkAboutSelfButton", 
        "description": [
            "Requires need.", 
            "Makes work on emotion."
        ]
    }, 
    "acceptAlienationGoalConstruction": {
        "key": "acceptAlienationGoalConstruction", 
        "constructionIdentifier": "acceptAlienationGoal", 
        "name": "Constructing - Accept Alienation", 
        "image": "trait-aaaaaa99_goal_social-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Accept Alienation", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "easyWorkTower": {
        "image": "tower_towerDrive", 
        "activeImage": "tower_towerDrive_activeOutline-0099ff", 
        "name": "Easy Work", 
        "key": "easyWorkTower", 
        "description": "Converts will + easy problem + music -> satisfaction"
    }, 
    "reflectFailureMenu": {
        "image": "goal_eyeTear-000000", 
        "completeText": "It's painful every time, but it is what you are.", 
        "name": "COMPLETED - Reflect on Deficiencies", 
        "key": "reflectFailureMenu"
    }, 
    "indifferenceTowerConstruction": {
        "key": "indifferenceTowerConstruction", 
        "constructionIdentifier": "indifferenceTower", 
        "name": "Constructing - Aloofness", 
        "image": "trait-aaaaaa99_tower_goalConfession-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Aloofness", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "proveSuccessMenu": {
        "image": "goal_trophy-000000", 
        "name": "COMPLETED - Prove Success", 
        "key": "proveSuccessMenu"
    }, 
    "sustainEmoInspirationMenu": {
        "image": "goal_mineCaution-000000", 
        "name": "COMPLETED - Sustain Emotional Inspiration", 
        "key": "sustainEmoInspirationMenu"
    }, 
    "tryForMeaningGoalConstruction": {
        "key": "tryForMeaningGoalConstruction", 
        "constructionIdentifier": "tryForMeaningGoal", 
        "name": "Constructing - Try For Deeper Meaning", 
        "image": "trait-aaaaaa99_goal_social-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Try For Deeper Meaning", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "deriveHumanInsightButton": {
        "constructionKey": "deriveHumanInsightGoalConstruction", 
        "name": "Derive Human Insight", 
        "image": "goal_spark-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "deriveHumanInsightGoal", 
        "key": "deriveHumanInsightButton", 
        "description": [
            "Requires reflect.", 
            "Makes understanding."
        ]
    }, 
    "loveStrengthAction": {
        "currenciesRequired": [
            "love_lover"
        ], 
        "towerKey": "loveStrengthTower", 
        "currenciesToMake": [
            "strength"
        ], 
        "name": "Derive Strength", 
        "key": "loveStrengthAction"
    }, 
    "majorIssueMenu": {
        "image": "goal_eyeTear-000000", 
        "completeText": "Things are okay for now.", 
        "name": "COMPLETED - Defuse Major Issue", 
        "key": "majorIssueMenu"
    }, 
    "fullStuckProblem": {
        "entitiesToMake": [
            "stuckProblem", 
            "explainStuck"
        ], 
        "key": "fullStuckProblem"
    }, 
    "makePlaythingButton": {
        "constructionKey": "makePlaythingGoalConstruction", 
        "name": "Make Playthings", 
        "image": "goal_dice-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "makePlaythingGoal", 
        "key": "makePlaythingButton", 
        "description": [
            "Requires music.", 
            "Makes plaything."
        ]
    }, 
    "sadRideGoal": {
        "key": "sadRideGoal", 
        "randomPosition": 0, 
        "currencyRequested": {
            "pain": 3
        }, 
        "image": "goal_goalFightProgramming-000000-000000", 
        "killMemory": 0, 
        "flareMemory": 1, 
        "entitiesToMake": "rideResult", 
        "goalMenuKey": "sadRideMenu", 
        "name": "Explore Sadness"
    }, 
    "prideStartGoalConstruction": {
        "key": "prideStartGoalConstruction", 
        "constructionIdentifier": "prideStartGoal", 
        "name": "Constructing - Be Proud Of This", 
        "image": "trait-aaaaaa99_goal_trophy-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Be Proud Of This", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "song": {
        "name": "Poor Song", 
        "aliveEntities": {
            "ignoreIssuesGoal": 5
        }, 
        "image": "trait-aa33aa", 
        "personHeld": "song", 
        "isaacEntities": [
            "easyWorkButton", 
            "thinkSongBadButton", 
            "songSatisfiedButton", 
            "integrateButton", 
            "sinkWorkButton"
        ], 
        "health": 450, 
        "key": "song", 
        "description": "What will this be?"
    }, 
    "aloneSadMine": {
        "resourceCount": 2, 
        "name": "Feeling Alone", 
        "image": "tower_social", 
        "key": "aloneSadMine", 
        "emotionTags": [
            "lover", 
            "sadness"
        ], 
        "resourceKeys": [
            "sadness"
        ], 
        "activeImage": "tower_social_activeOutline-0099ff", 
        "description": "There really isn't anyone else who cares. - makes sadness"
    }, 
    "tryOldJokesGoalConstruction": {
        "key": "tryOldJokesGoalConstruction", 
        "constructionIdentifier": "tryOldJokesGoal", 
        "name": "Constructing - Try Old Jokes", 
        "image": "trait-aaaaaa99_goal_social-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Try Old Jokes", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "endRealTalkGoalConstruction": {
        "key": "endRealTalkGoalConstruction", 
        "constructionIdentifier": "endRealTalkGoal", 
        "name": "Constructing - You've Said Enough", 
        "image": "trait-aaaaaa99_goal_social-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "You've Said Enough", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "easyProblemDeposit": {
        "randomPosition": 1, 
        "name": "Easy Problem", 
        "aliveEntities": {
            "stuckProblem": 5
        }, 
        "image": "tower_puzzle", 
        "deposit": {
            "easyProblem": 3
        }, 
        "key": "easyProblemDeposit", 
        "activeImage": "tower_puzzle_activeOutline-0099ff"
    }, 
    "fleshResult": {
        "goodEntities": {
            "makeSadSong": 3
        }, 
        "badEntities": "explainFleshFail", 
        "key": "fleshResult"
    }, 
    "selfReliance": {
        "name": "Self-Reliance", 
        "image": "trait-99cc00", 
        "personHeld": "selfCore", 
        "isaacEntities": [
            "dreamFutureButton", 
            "strengthButton", 
            "reflectPastButton"
        ], 
        "key": "selfReliance", 
        "maxLevel": 12
    }, 
    "fleshSongButton": {
        "constructionKey": "fleshSongGoalConstruction", 
        "name": "Flesh Out Song", 
        "image": "goal_coolNote-000000", 
        "description": "Requires satisfaction.", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "fleshSongGoal", 
        "key": "fleshSongButton", 
        "displayRiskIcon": 1
    }, 
    "shareAction": {
        "towerKey": "shareTower", 
        "name": "Share Experiences", 
        "entitiesOnConvert": {
            "condCaptureInspirationGoal": 10
        }, 
        "currenciesRequired": [
            "social_lover"
        ], 
        "currenciesToMake": [
            "sharing_lover"
        ], 
        "key": "shareAction"
    }, 
    "understandTower": {
        "image": "tower_understand", 
        "activeImage": "tower_understand_activeOutline-0099ff", 
        "name": "Understand", 
        "key": "understandTower", 
        "description": "Converts pain -> understanding"
    }, 
    "steamAlienation": {
        "steamAchievementKey": "alienation", 
        "keyToCheck": "alienationMineConstruction", 
        "key": "steamAlienation"
    }, 
    "museFishGoalConstruction": {
        "key": "museFishGoalConstruction", 
        "constructionIdentifier": "museFishGoal", 
        "name": "Constructing - Fish For Compliments", 
        "image": "trait-aaaaaa99_goal_hook-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Fish For Compliments", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "findNewMusicButton": {
        "constructionKey": "findNewMusicGoalConstruction", 
        "name": "Listen To Music Together", 
        "image": "goal_cd-000000", 
        "description": "Requires music, social.", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "findNewMusicGoal", 
        "key": "findNewMusicButton", 
        "displayRiskIcon": 1
    }, 
    "loveMine": {
        "name": "Love", 
        "image": "tower_heart", 
        "key": "loveMine", 
        "resourceKeys": [
            "love_lover"
        ], 
        "activeImage": "tower_heart_activeOutline-0099ff", 
        "description": "Just because it hurts, doesn't mean it's not love."
    }, 
    "indulgeHappyGoal": {
        "key": "indulgeHappyGoal", 
        "randomPosition": 0, 
        "name": "Indulge In Satisfaction", 
        "image": "goal_mineCheer-000000", 
        "currencyRequested": {
            "will": 3
        }, 
        "entitiesToMake": "prideBurst", 
        "goalMenuKey": "indulgeHappyMenu"
    }, 
    "techWorkTowerConstruction": {
        "key": "techWorkTowerConstruction", 
        "constructionIdentifier": "techWorkTower", 
        "name": "Constructing - Complete Tech Work", 
        "image": "trait-aaaaaa99_tower_thinkProblem", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Complete Tech Work", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "selfRelianceToAloneness": {
        "sourceTrait": "selfReliance", 
        "newTrait": "aloneness", 
        "key": "selfRelianceToAloneness"
    }, 
    "lovingCareButton": {
        "constructionKey": "lovingCareTowerConstruction", 
        "name": "Loving Care", 
        "image": "tower_minePain-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "lovingCareTower", 
        "limitingKeys": [
            "lovingCareTower", 
            "lovingCareTowerConstruction"
        ], 
        "key": "lovingCareButton", 
        "subPanel": "Towers", 
        "description": "Converts love + sharing -> care"
    }, 
    "endAlienationGoal": {
        "key": "endAlienationGoal", 
        "randomPosition": 0, 
        "name": "End Alienation", 
        "image": "goal_heart-000000", 
        "currencyRequested": {
            "love_lover": 3
        }, 
        "entitiesToMake": [
            "alienationToFriend", 
            "killLoveDeposit"
        ], 
        "goalMenuKey": "endAlienationMenu"
    }, 
    "strengthButton": {
        "constructionKey": "strengthTowerConstruction", 
        "name": "Gain Strength", 
        "image": "tower_weight", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "strengthTower", 
        "limitingKeys": [
            "strengthTower", 
            "strengthTowerConstruction"
        ], 
        "key": "strengthButton", 
        "subPanel": "Towers", 
        "description": "Converts will -> strength"
    }, 
    "quietDemonsStrengthGoalConstruction": {
        "key": "quietDemonsStrengthGoalConstruction", 
        "constructionIdentifier": "quietDemonsStrengthGoal", 
        "name": "Constructing - Quiet Demons", 
        "image": "trait-aaaaaa99_goal_weight-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Quiet Demons", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "goalHelpInfoPanel": {
        "components": [
            "worldGoalComponent"
        ], 
        "helpLines": [
            "These are things that your character wants.", 
            "They are completed by connecting it to the rest of the mind and generating the currencies that it requests."
        ], 
        "name": "Goals", 
        "key": "goalHelpInfoPanel"
    }, 
    "ignoreInsult2Goal": {
        "currencyEntities": "loverInsult", 
        "randomPosition": 1, 
        "name": "Ignore Insult", 
        "makeOnTimeOut": [
            "selfWorthless", 
            "killPride"
        ], 
        "image": "goal_goalCalmDown-000000", 
        "description": "The lover didn't need to say that about your work.", 
        "key": "ignoreInsult2Goal", 
        "sourceTrait": "loverBase", 
        "currencyRequested": {
            "alienation_lover": 3
        }, 
        "goalMenuKey": "ignoreInsult2Menu", 
        "timeToDeath": 240
    }, 
    "suspicionGoal": {
        "key": "suspicionGoal", 
        "randomPosition": 1, 
        "name": "Defuse Suspicion", 
        "makeOnTimeOut": "unhappy", 
        "image": "goal_towerConfess-000000", 
        "description": [
            "You are spending a lot of time with the muse. Is it surprising that", 
            "the lover is suspicious?"
        ], 
        "currencyRequested": {
            "love_lover": 6
        }, 
        "entitiesToMake": "suspicionResult", 
        "goalMenuKey": "suspicionMenu", 
        "timeToDeath": 240
    }, 
    "apologizeTowerConstruction": {
        "key": "apologizeTowerConstruction", 
        "constructionIdentifier": "apologizeTower", 
        "name": "Constructing - Apologize", 
        "image": "trait-aaaaaa99_tower_towerConfess", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Apologize", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "understandingDeposit": {
        "image": "tower_understand", 
        "activeImage": "tower_understand_activeOutline-0099ff", 
        "name": "Understanding", 
        "key": "understandingDeposit", 
        "deposit": {
            "understanding": 3
        }
    }, 
    "desireImprovementMenu": {
        "image": "goal_drive-000000", 
        "name": "COMPLETED - Desire Self-Improvement", 
        "key": "desireImprovementMenu"
    }, 
    "failureMineButton": {
        "constructionKey": "failureMineConstruction", 
        "name": "Failures", 
        "entityCost": {
            "will": 2
        }, 
        "image": "tower_pain", 
        "currenciesMade": [
            "failure"
        ], 
        "entityKey": "failureMine", 
        "limitingKeys": [
            "failureMine", 
            "failureMineConstruction"
        ], 
        "key": "failureMineButton", 
        "description": "makes personal failure"
    }, 
    "solveMusicGoalPathTechInspiration": {
        "startKey": "solveMusicGoal", 
        "endKey": "techInspiration", 
        "key": "solveMusicGoalPathTechInspiration"
    }, 
    "loverJoy": {
        "emotionsToAdd": {
            "joy": 1
        }, 
        "name": "Lover Happiness", 
        "image": "tower_social", 
        "key": "loverJoy", 
        "memoryKey": "memory_loverJoy", 
        "emotionTags": "lover", 
        "resourceKey": "loverJoyMineConstruction", 
        "activeImage": "tower_social_activeOutline-0099ff", 
        "description": "It feels good when the lover is happy"
    }, 
    "fearTickModifier": {
        "timeToDeath": 15
    }, 
    "work": {
        "name": "work", 
        "key": "work", 
        "tags": [
            "work"
        ]
    }, 
    "completeEmoMine": {
        "image": "tower_mineWill", 
        "resourceKeys": [
            "will"
        ], 
        "name": "Completion Desire", 
        "key": "completeEmoMine", 
        "activeImage": "tower_mineWill_activeOutline-0099ff"
    }, 
    "suppressDemonsMenu": {
        "image": "goal_goalActivation-000000", 
        "name": "COMPLETED - Suppress Inner Demons", 
        "key": "suppressDemonsMenu"
    }, 
    "unimpressed": {
        "flag": "unimpressed", 
        "key": "unimpressed"
    }, 
    "hurtLoverMine": {
        "resourceCount": 2, 
        "name": "Hurt Lover", 
        "image": "tower_social", 
        "key": "hurtLoverMine", 
        "emotionTags": [
            "lover", 
            "sadness"
        ], 
        "resourceKeys": [
            "sadness"
        ], 
        "activeImage": "tower_social_activeOutline-0099ff", 
        "description": "You had no call to say that to the lover - makes sadness"
    }, 
    "personGoal": {
        "minSelfDist": 4
    }, 
    "workSatisfactionMineConstruction": {
        "key": "workSatisfactionMineConstruction", 
        "constructionIdentifier": "workSatisfactionMine", 
        "name": "Satisfaction With Work", 
        "image": "tower_puzzle-666666aa", 
        "currencyRequested": {
            "impulse": 1
        }, 
        "constructionName": "Satisfaction With Work"
    }, 
    "relaxGoal": {
        "key": "relaxGoal", 
        "randomPosition": 0, 
        "name": "Relax", 
        "image": "goal_goalPark-000000", 
        "currencyRequested": {
            "cheer": 3
        }, 
        "entitiesToMake": "relaxDeposit", 
        "goalMenuKey": "relaxMenu"
    }, 
    "explainStuck": {
        "completeText": "There may be something here, but you can't quite figure it out yet.", 
        "key": "explainStuck"
    }, 
    "killTellQuitButton": {
        "entitiesToKill": [
            "tellQuitButton"
        ], 
        "key": "killTellQuitButton"
    }, 
    "indifferenceButton": {
        "constructionKey": "indifferenceTowerConstruction", 
        "name": "Aloofness", 
        "image": "tower_goalConfession-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "indifferenceTower", 
        "limitingKeys": [
            "indifferenceTower", 
            "indifferenceTowerConstruction"
        ], 
        "key": "indifferenceButton", 
        "subPanel": "Towers", 
        "description": "Converts strength -> alienation"
    }, 
    "aloneSadMineConstruction": {
        "key": "aloneSadMineConstruction", 
        "constructionIdentifier": "aloneSadMine", 
        "name": "Feeling Alone", 
        "image": "tower_social-666666aa", 
        "currencyRequested": {
            "impulse": 1
        }, 
        "constructionName": "Feeling Alone"
    }, 
    "shareJoyAction": {
        "currenciesRequired": [
            "sharing_lover"
        ], 
        "towerKey": "shareJoyTower", 
        "name": "Share Joy", 
        "key": "shareJoyAction"
    }, 
    "deriveStrengthButton": {
        "constructionKey": "deriveStrengthGoalConstruction", 
        "name": "Derive Strength", 
        "image": "goal_weightPlus-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "deriveStrengthGoal", 
        "key": "deriveStrengthButton", 
        "description": [
            "Requires will.", 
            "Makes strength."
        ]
    }, 
    "identifyProblemGoalConstruction": {
        "key": "identifyProblemGoalConstruction", 
        "constructionIdentifier": "identifyProblemGoal", 
        "name": "Constructing - Identify Issue", 
        "image": "trait-aaaaaa99_goal_thinkProblem-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Identify Issue", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "indulgeCuriosityButton": {
        "constructionKey": "indulgeCuriosityGoalConstruction", 
        "name": "Indulge Curiosity", 
        "image": "goal_puzzle-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "indulgeCuriosityGoal", 
        "key": "indulgeCuriosityButton", 
        "description": "Requires intrigue, desire self-improvement."
    }, 
    "nextDayMuse": {
        "entitiesToMake": [
            "meetMuseGoal"
        ], 
        "key": "nextDayMuse"
    }, 
    "fadeTechInspiration": {
        "timeToDeath": 300, 
        "key": "fadeTechInspiration", 
        "makeOnTimeOut": "sustainTechInspirationGoal"
    }, 
    "endHangOutGoalConstruction": {
        "key": "endHangOutGoalConstruction", 
        "constructionIdentifier": "endHangOutGoal", 
        "name": "Constructing - Call It A Day", 
        "image": "trait-aaaaaa99_goal_goalPark-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Call It A Day", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "sustainTechInspirationMenu": {
        "image": "goal_mineCaution-000000", 
        "name": "COMPLETED - Sustain Tech Inspiration", 
        "key": "sustainTechInspirationMenu"
    }, 
    "intrigueTower": {
        "image": "tower_openMind", 
        "activeImage": "tower_openMind_activeOutline-0099ff", 
        "name": "Intrigued By", 
        "key": "intrigueTower", 
        "description": [
            "How do you come up with these?", 
            "Converts thought -> intrigue"
        ]
    }, 
    "explainSuspicionSuccess": {
        "completeText": "You've allayed the lover's suspicions for now.", 
        "key": "explainSuspicionSuccess"
    }, 
    "applyTowerConstruction": {
        "key": "applyTowerConstruction", 
        "constructionIdentifier": "applyTower", 
        "name": "Constructing - Fuel Yourself", 
        "image": "trait-aaaaaa99_tower_motivation", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Fuel Yourself", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "strengthMineButton": {
        "constructionKey": "strengthMineConstruction", 
        "name": "Derive Strength", 
        "entityCost": {
            "will": 2
        }, 
        "image": "tower_weight", 
        "currenciesMade": [
            "strength"
        ], 
        "entityKey": "strengthMine", 
        "limitingKeys": [
            "strengthMine", 
            "strengthMineConstruction"
        ], 
        "key": "strengthMineButton", 
        "description": "makes strength"
    }, 
    "dismissSuccessButton": {
        "constructionKey": "dismissSuccessGoalConstruction", 
        "name": "Dismiss your Success", 
        "image": "goal_eyeTear-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "dismissSuccessGoal", 
        "key": "dismissSuccessButton", 
        "description": "Requires will."
    }, 
    "museVentTower": {
        "name": "Vent To", 
        "image": "tower_mineFrustration", 
        "damageMultipliers": {
            "anger": 1, 
            "sadness": 1
        }, 
        "key": "museVentTower", 
        "activeImage": "tower_mineFrustration_activeOutline-0099ff", 
        "description": "Uses social to calm anger, sadness"
    }, 
    "selfWorthless": {
        "emotionsToAdd": {
            "sadness": 1
        }, 
        "name": "Feeling Worthless", 
        "image": "tower_trophy", 
        "key": "selfWorthless", 
        "memoryKey": "memory_selfWorthless", 
        "emotionTags": "self", 
        "resourceKey": "selfWorthlessMineConstruction", 
        "activeImage": "tower_trophy_activeOutline-0099ff"
    }, 
    "releaseButton": {
        "constructionKey": "releaseGoalConstruction", 
        "name": "Release Song", 
        "image": "goal_cd-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "releaseGoal", 
        "key": "releaseButton", 
        "description": [
            "Requires resolve.", 
            "Makes success."
        ]
    }, 
    "cheerMineButton": {
        "constructionKey": "cheerMineConstruction", 
        "name": "Cheer Mine", 
        "entityCost": {
            "will": 2
        }, 
        "image": "tower_mineCheer", 
        "currenciesMade": [
            "cheer"
        ], 
        "entityKey": "cheerMine", 
        "limitingKeys": [
            "cheerMine", 
            "cheerMineConstruction"
        ], 
        "key": "cheerMineButton", 
        "description": "makes cheer"
    }, 
    "sustainEmoInspirationGoal": {
        "currencyEntities": "drainedSad", 
        "randomPosition": 1, 
        "name": "Sustain Emotional Inspiration", 
        "makeOnTimeOut": "killEmoInspiration", 
        "image": "goal_mineCaution-000000", 
        "key": "sustainEmoInspirationGoal", 
        "sourceTrait": "emoInspiration", 
        "currencyRequested": {
            "music": 5
        }, 
        "entitiesToMake": "fadeEmoInspiration", 
        "goalMenuKey": "sustainEmoInspirationMenu", 
        "timeToDeath": 150
    }, 
    "deriveStrengthGoalConstruction": {
        "key": "deriveStrengthGoalConstruction", 
        "constructionIdentifier": "deriveStrengthGoal", 
        "name": "Constructing - Derive Strength", 
        "image": "trait-aaaaaa99_goal_weightPlus-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Derive Strength", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "museBaseTraitConnectMusic": {
        "startKey": "museBase", 
        "endKey": "music", 
        "key": "museBaseTraitConnectMusic"
    }, 
    "joyRideGoalConstruction": {
        "key": "joyRideGoalConstruction", 
        "constructionIdentifier": "joyRideGoal", 
        "name": "Constructing - Explore Joy", 
        "image": "trait-aaaaaa99_goalFightProgramming-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Explore Joy", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "shareCoolMenu": {
        "image": "goal_coolNote-000000", 
        "name": "COMPLETED - Share Something Cool", 
        "key": "shareCoolMenu"
    }, 
    "destinyButton": {
        "constructionKey": "destinyGoalConstruction", 
        "name": "Believe In Future", 
        "image": "goal_daydream-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "destinyGoal", 
        "key": "destinyButton", 
        "description": "Requires self-esteem."
    }, 
    "freshMusicToMusicLove": {
        "sourceTrait": "freshMusic", 
        "newTrait": "musicLove", 
        "key": "freshMusicToMusicLove"
    }, 
    "steamThought": {
        "steamAchievementKey": "thought", 
        "keyToCheck": "thoughtTowerConstruction", 
        "key": "steamThought"
    }, 
    "stopDemonStrengthGoal": {
        "key": "stopDemonStrengthGoal", 
        "randomPosition": 1, 
        "name": "Stop Demon", 
        "makeOnTimeOut": "selfWorthless", 
        "image": "goal_memoryProgramming-000000", 
        "sourceTrait": "demons", 
        "currencyRequested": {
            "strength": 3
        }, 
        "goalMenuKey": "stopDemonStrengthMenu", 
        "timeToDeath": 240
    }, 
    "settingTower": {
        "image": "tower_towerPaint", 
        "activeImage": "tower_towerPaint_activeOutline-0099ff", 
        "name": "Add Life", 
        "key": "settingTower", 
        "description": "Converts understanding + music -> emotional solution"
    }, 
    "deriveCoolThingButton": {
        "constructionKey": "deriveCoolThingGoalConstruction", 
        "name": "Derive Cool Thing", 
        "image": "goal_coolNote-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "deriveCoolThingGoal", 
        "key": "deriveCoolThingButton", 
        "description": [
            "Requires thought.", 
            "Makes cool thing."
        ]
    }, 
    "begLoverGoal": {
        "currencyEntities": "shameBurst", 
        "randomPosition": 1, 
        "name": "Beg Lover To Stay", 
        "makeOnTimeOut": [
            "loverLeaveMenu", 
            "gameEnd"
        ], 
        "image": "goal_towerConfess-000000", 
        "description": [
            "Failing this will end the game. Succeeding will destroy your", 
            "self-esteem."
        ], 
        "key": "begLoverGoal", 
        "sourceTrait": "unhappy", 
        "currencyRequested": {
            "need_lover": 3
        }, 
        "entitiesToMake": [
            "killUnhappy", 
            "killPride"
        ], 
        "goalMenuKey": "begLoverMenu", 
        "timeToDeath": 180
    }, 
    "loverBasePathHateWork": {
        "startKey": "loverBase", 
        "endKey": "hateWork", 
        "key": "loverBasePathHateWork"
    }, 
    "understanding": {
        "name": "understanding", 
        "key": "understanding", 
        "tags": [
            "understanding"
        ]
    }, 
    "shareJoyTower": {
        "name": "Share Joy", 
        "image": "tower_towerTalk", 
        "damageMultipliers": {
            "joy": 1
        }, 
        "key": "shareJoyTower", 
        "activeImage": "tower_towerTalk_activeOutline-0099ff", 
        "description": "Uses sharing to calm joy"
    }, 
    "fireUpGoalConstruction": {
        "key": "fireUpGoalConstruction", 
        "constructionIdentifier": "fireUpGoal", 
        "name": "Constructing - Fire Yourself Up", 
        "image": "trait-aaaaaa99_weight", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Fire Yourself Up", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "fadeFreshMusic": {
        "timeToDeath": 180, 
        "key": "fadeFreshMusic", 
        "makeOnTimeOut": "sustainFreshMusicGoal"
    }, 
    "memory_hurtLover": {
        "image": "trait-013d70", 
        "memoryEmotions": [
            "sadness"
        ], 
        "key": "memory_hurtLover", 
        "name": "Hurt Lover"
    }, 
    "captureLoveGoal": {
        "key": "captureLoveGoal", 
        "randomPosition": 0, 
        "name": "Capture Inspiration", 
        "image": "goal_butterfly-000000", 
        "currencyRequested": {
            "love_lover": 1
        }, 
        "entitiesToMake": "captureInspirationResult", 
        "goalMenuKey": "captureLoveMenu", 
        "timeToDeath": 60
    }, 
    "path": {
        "planeOfExistence": "path"
    }, 
    "solveMusicButton": {
        "constructionKey": "solveMusicGoalConstruction", 
        "name": "Solve Fresh Music", 
        "image": "goal_towerThinkAbout-000000", 
        "description": "Requires thought.", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "solveMusicGoal", 
        "disablingEntities": [
            "techInspiration"
        ], 
        "key": "solveMusicButton", 
        "displayRiskIcon": 1
    }, 
    "makeEmoInspirationGoal": {
        "key": "makeEmoInspirationGoal", 
        "randomPosition": 0, 
        "currencyRequested": {
            "thought": 3
        }, 
        "image": "goal_goalFightProgramming-000000-000000", 
        "killMemory": 0, 
        "sourceTrait": 0, 
        "flareMemory": 0, 
        "entitiesToMake": "convinceSelfGoal", 
        "goalMenuKey": "makeEmoInspirationMenu", 
        "name": "Get Inspiration From"
    }, 
    "refineAccomplishmentButton": {
        "constructionKey": "refineAccomplishmentTowerConstruction", 
        "name": "Refine Accomplishment", 
        "image": "tower_towerThinkAbout", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "refineAccomplishmentTower", 
        "limitingKeys": [
            "refineAccomplishmentTower", 
            "refineAccomplishmentTowerConstruction"
        ], 
        "key": "refineAccomplishmentButton", 
        "subPanel": "Towers", 
        "description": [
            "Surely accomplishments imply worth?", 
            "Converts accomplishment + will -> self-esteem"
        ]
    }, 
    "ignoreIssuesGoal": {
        "key": "ignoreIssuesGoal", 
        "randomPosition": 1, 
        "name": "Ignore Issues With Song", 
        "makeOnTimeOut": "shameBurst", 
        "image": "goal_headphones-000000", 
        "description": "This is not what you wanted it to be.", 
        "sourceTrait": "song", 
        "currencyRequested": {
            "will": 3
        }, 
        "goalMenuKey": "ignoreIssuesMenu", 
        "timeToDeath": 120
    }, 
    "apologizeAction": {
        "currenciesRequired": [
            "need_lover", 
            "mistake"
        ], 
        "towerKey": "apologizeTower", 
        "currenciesToMake": [
            "apology_lover"
        ], 
        "name": "Apologize", 
        "key": "apologizeAction"
    }, 
    "reflectSuccessGoalConstruction": {
        "key": "reflectSuccessGoalConstruction", 
        "constructionIdentifier": "reflectSuccessGoal", 
        "name": "Constructing - Reflect on Success", 
        "image": "trait-aaaaaa99_goal_trophy-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Reflect on Success", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "discussProblem": {
        "name": "discuss problem", 
        "key": "discussProblem", 
        "tags": [
            "discussProblem"
        ]
    }, 
    "musicLove_ext": {
        "personHeld": "music", 
        "image": [
            "trait-00aaaa", 
            "trait-009999", 
            "trait-00BBBB"
        ], 
        "name": "The Music", 
        "key": "musicLove_ext", 
        "sourceKey": "musicLove"
    }, 
    "deriveStrengthMenu": {
        "image": "goal_weightPlus-000000", 
        "completeText": "It just takes willpower", 
        "name": "COMPLETED - Derive Strength", 
        "key": "deriveStrengthMenu"
    }, 
    "releasedSong_ext": {
        "image": [
            "trait-00aaaa", 
            "trait-009999", 
            "trait-00BBBB"
        ], 
        "name": "Released Song", 
        "key": "releasedSong_ext", 
        "sourceKey": "releasedSong"
    }, 
    "strengthMine": {
        "name": "Derive Strength", 
        "image": "tower_weight", 
        "key": "strengthMine", 
        "resourceKeys": [
            "strength"
        ], 
        "activeImage": "tower_weight_activeOutline-0099ff", 
        "description": "You cannot be both alone and weak."
    }, 
    "reflectMineButton": {
        "constructionKey": "reflectMineConstruction", 
        "name": "Reflection", 
        "entityCost": {
            "will": 2
        }, 
        "image": "tower_mineThought", 
        "currenciesMade": [
            "reflect"
        ], 
        "entityKey": "reflectMine", 
        "limitingKeys": [
            "reflectMine", 
            "reflectMineConstruction"
        ], 
        "key": "reflectMineButton", 
        "description": "makes reflect"
    }, 
    "emoInspiration": {
        "sector": [
            3, 
            3
        ], 
        "name": "Emotional Inspiration", 
        "image": "trait-9900cc", 
        "isaacEntities": [
            "fadeEmoInspiration", 
            "settingButton", 
            "deriveHumanInsightButton", 
            "reflectMineButton", 
            "completeEmoMineButton", 
            "emoWorkButton"
        ], 
        "health": 450, 
        "key": "emoInspiration", 
        "startManagedEntities": [
            "connectSource"
        ]
    }, 
    "thinkSongBadMenu": {
        "image": "goal_eyeTear-000000", 
        "completeText": [
            "It took will to admit the song's failures, but my goodness do they", 
            "exist."
        ], 
        "name": "COMPLETED - Face Song Failings", 
        "key": "thinkSongBadMenu"
    }, 
    "intrigue": {
        "name": "intrigue", 
        "key": "intrigue", 
        "tags": [
            "intrigue"
        ]
    }, 
    "sadnessMap": {
        "startManagedEntities": [
            "painMine"
        ], 
        "emotionToMap": "sadness", 
        "emotionMappedEntities": [
            "understandButton", 
            "sadRideButton", 
            "deadInsideButton", 
            "sadMemoryFade", 
            "workThroughButton"
        ], 
        "key": "sadnessMap"
    }, 
    "toldWorthGoal": {
        "currencyEntities": {
            "prideBurst": 25
        }, 
        "randomPosition": 0, 
        "name": "Be Told Of Your Worth", 
        "image": "goal_trophy-000000", 
        "key": "toldWorthGoal", 
        "currencyRequested": {
            "love_lover": 3
        }, 
        "entitiesToMake": "selfEsteemDeposit", 
        "goalMenuKey": "toldWorthMenu"
    }, 
    "completeEmoMineButton": {
        "constructionKey": "completeEmoMineConstruction", 
        "name": "Completion Desire", 
        "entityCost": {
            "will": 2
        }, 
        "image": "tower_mineWill", 
        "currenciesMade": [
            "will"
        ], 
        "entityKey": "completeEmoMine", 
        "limitingKeys": [
            "completeEmoMine", 
            "completeEmoMineConstruction"
        ], 
        "key": "completeEmoMineButton", 
        "description": "makes will"
    }, 
    "loveInsult": {
        "badEntities": [
            "insultingMake", 
            "majorIssueGoal"
        ], 
        "key": "loveInsult"
    }, 
    "socialMuseMine": {
        "name": "Spend Time With Muse", 
        "image": "tower_mineSocial", 
        "key": "socialMuseMine", 
        "resourceKeys": [
            "social_muse"
        ], 
        "resourceEntities": {
            "condCaptureInspirationGoal": 10
        }, 
        "activeImage": "tower_mineSocial_activeOutline-0099ff"
    }, 
    "desireImprovementGoalConstruction": {
        "key": "desireImprovementGoalConstruction", 
        "constructionIdentifier": "desireImprovementGoal", 
        "name": "Constructing - Desire Self-Improvement", 
        "image": "trait-aaaaaa99_goal_drive-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Desire Self-Improvement", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "selfLoathing": {
        "name": "Self-Loathing", 
        "aliveEntities": {
            "shameBurst": 10
        }, 
        "image": "trait-66ff00", 
        "personHeld": "selfCore", 
        "isaacEntities": [
            "selfLoathingMineButton", 
            "lackAccomplishmentButton", 
            "dismissFailureButton", 
            "shameBurst", 
            "indulgeBitternessButton", 
            "desireImprovementButton"
        ], 
        "key": "selfLoathing", 
        "maxLevel": 12, 
        "description": "Why do you pretend that you're worth something?"
    }, 
    "flashback_angerConstruction": {
        "key": "flashback_angerConstruction", 
        "constructionIdentifier": "flashback_anger", 
        "name": "Flashback", 
        "image": "tower_flashback-000000-666666aa", 
        "currencyRequested": {
            "impulse": 1
        }, 
        "constructionName": "Flashback"
    }, 
    "nascentSongToSong": {
        "sourceTrait": "nascentSong", 
        "newTrait": "song", 
        "key": "nascentSongToSong"
    }, 
    "resolveStrengthAction": {
        "currenciesRequired": [
            "strength"
        ], 
        "towerKey": "resolveStrengthTower", 
        "currenciesToMake": [
            "resolve"
        ], 
        "name": "Gain Resolve", 
        "key": "resolveStrengthAction"
    }, 
    "reflectPastButton": {
        "constructionKey": "reflectPastGoalConstruction", 
        "name": "Reflect On Past", 
        "image": "goal_drive-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "reflectPastGoal", 
        "key": "reflectPastButton", 
        "description": [
            "Requires strength, alienation.", 
            "Makes resolve."
        ]
    }, 
    "unhappy": {
        "name": "Unhappy", 
        "aliveEntities": {
            "begLoverGoal": 10, 
            "loverSad": 10
        }, 
        "image": "trait-aa00cc", 
        "personHeld": "lover", 
        "isaacEntities": [
            "apologizeButton", 
            "findMistakeButton", 
            "unhappyDisable", 
            "loverSad", 
            "selfHate"
        ], 
        "key": "unhappy"
    }, 
    "disableMemories": {
        "timeToDeath": 150, 
        "key": "disableMemories"
    }, 
    "identifyProblemGoal": {
        "key": "identifyProblemGoal", 
        "randomPosition": 0, 
        "name": "Identify Issue", 
        "image": "goal_thinkProblem-000000", 
        "currencyRequested": {
            "thought": 1
        }, 
        "entitiesToMake": "newProblem", 
        "goalMenuKey": "identifyProblemMenu", 
        "description": [
            "Now, it's just a question of stating problems in a way that lets you", 
            "find solutions."
        ]
    }, 
    "endHangOutGoal": {
        "key": "endHangOutGoal", 
        "randomPosition": 0, 
        "name": "Call It A Day", 
        "image": "goal_goalPark-000000", 
        "currencyRequested": {
            "will": 1
        }, 
        "entitiesToMake": "hangingOutToFriend", 
        "goalMenuKey": "endHangOutMenu"
    }, 
    "apologizeTower": {
        "image": "tower_towerConfess", 
        "activeImage": "tower_towerConfess_activeOutline-0099ff", 
        "name": "Apologize", 
        "key": "apologizeTower", 
        "description": "Converts need + mistake -> apology"
    }, 
    "loverInsultMineConstruction": {
        "key": "loverInsultMineConstruction", 
        "constructionIdentifier": "loverInsultMine", 
        "name": "You Don't Need This", 
        "image": "tower_memoryNastyComment-000000-666666aa", 
        "currencyRequested": {
            "impulse": 1
        }, 
        "constructionName": "You Don't Need This"
    }, 
    "stuckProblem": {
        "emotionsToAdd": {
            "anger": 1
        }, 
        "name": "Stuck With Problem", 
        "image": "tower_puzzle", 
        "key": "stuckProblem", 
        "memoryKey": "memory_stuckProblem", 
        "emotionTags": "self", 
        "resourceKey": "stuckProblemMineConstruction", 
        "activeImage": "tower_puzzle_activeOutline-0099ff", 
        "description": "Will you never solve this problem?"
    }, 
    "cheerLoverResult": {
        "goodEntities": {
            "hangingOutToRealTalk": 3
        }, 
        "badEntities": "explainCheerLoverFail", 
        "key": "cheerLoverResult"
    }, 
    "solveMusicGoalConstruction": {
        "key": "solveMusicGoalConstruction", 
        "constructionIdentifier": "solveMusicGoal", 
        "name": "Constructing - Solve Fresh Music", 
        "image": "trait-aaaaaa99_goal_towerThinkAbout-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Solve Fresh Music", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "killSongProblems": {
        "entitiesToKill": [
            "emoInspiration", 
            "techInspiration", 
            "easyProblemDeposit", 
            "emoProblemDeposit", 
            "techProblemDeposit", 
            "easyWorkTower", 
            "songSatisfactionDeposit", 
            "largeSongSatisfactionDeposit"
        ], 
        "key": "killSongProblems"
    }, 
    "restartWorkGoal": {
        "key": "restartWorkGoal", 
        "randomPosition": 0, 
        "name": "Restart Work", 
        "image": "goal_towerThinkAbout-000000", 
        "currencyRequested": {
            "resolve": 3
        }, 
        "entitiesToMake": "sadSongToSong", 
        "goalMenuKey": "restartWorkMenu"
    }, 
    "personCallousnessAction": {
        "currenciesRequired": [
            "strength"
        ], 
        "towerKey": "personCallousnessTower", 
        "name": "Callousness", 
        "key": "personCallousnessAction"
    }, 
    "captureLoveMenu": {
        "image": "goal_butterfly-000000", 
        "name": "COMPLETED - Capture Inspiration", 
        "key": "captureLoveMenu"
    }, 
    "beCaredMenu": {
        "image": "goal_heart-000000", 
        "name": "COMPLETED - Be Cared For", 
        "key": "beCaredMenu"
    }, 
    "restartWorkGoalConstruction": {
        "key": "restartWorkGoalConstruction", 
        "constructionIdentifier": "restartWorkGoal", 
        "name": "Constructing - Restart Work", 
        "image": "trait-aaaaaa99_goal_towerThinkAbout-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Restart Work", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "music": {
        "name": "music", 
        "key": "music", 
        "tags": [
            "music"
        ]
    }, 
    "fadeQuietDemons": {
        "timeToDeath": 180, 
        "key": "fadeQuietDemons", 
        "makeOnTimeOut": "quietDemonsToDemons"
    }, 
    "memory": {
        "mapSpawnKey": "memoryExtension", 
        "maxLevel": 6, 
        "playerKillable": "true", 
        "timeToDeath": 150
    }, 
    "understandAction": {
        "currenciesRequired": [
            "pain"
        ], 
        "towerKey": "understandTower", 
        "currenciesToMake": [
            "understanding"
        ], 
        "name": "Understand", 
        "key": "understandAction"
    }, 
    "loverInsultMine": {
        "resourceCount": 1, 
        "name": "You Don't Need This", 
        "image": "tower_memoryNastyComment-000000", 
        "key": "loverInsultMine", 
        "emotionTags": [
            "lover", 
            "anger"
        ], 
        "resourceKeys": [
            "anger"
        ], 
        "activeImage": "tower_memoryNastyComment-000000_activeOutline-0099ff", 
        "description": "makes anger"
    }, 
    "killPrideStartButton": {
        "entitiesToKill": [
            "prideStartButton"
        ], 
        "key": "killPrideStartButton"
    }, 
    "firstParty": {
        "timeToDeath": 150, 
        "key": "firstParty", 
        "makeOnTimeOut": "party"
    }, 
    "fatigueGoal": {
        "key": "fatigueGoal", 
        "randomPosition": 1, 
        "name": "Fatigue", 
        "makeOnTimeOut": [
            "exhaustion"
        ], 
        "image": "goal_droop-000000", 
        "currencyRequested": {
            "relax": 3
        }, 
        "goalMenuKey": "fatigueMenu", 
        "timeToDeath": 300
    }, 
    "plaything": {
        "name": "plaything", 
        "key": "plaything", 
        "tags": [
            "plaything"
        ]
    }, 
    "relax": {
        "name": "relaxation", 
        "key": "relax", 
        "tags": [
            "relax"
        ]
    }, 
    "trait": {
        "playerKillable": "true"
    }, 
    "findMistakeGoalConstruction": {
        "key": "findMistakeGoalConstruction", 
        "constructionIdentifier": "findMistakeGoal", 
        "name": "Constructing - Learn Mistakes", 
        "image": "trait-aaaaaa99_goal_towerConfess-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Learn Mistakes", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "quietDemonsLoveMenu": {
        "image": "goal_heart-000000", 
        "name": "COMPLETED - Quiet Demons", 
        "key": "quietDemonsLoveMenu"
    }, 
    "explainRideFail": {
        "completeText": [
            "Not all emotion can fit into the song. You need more than what you", 
            "just got."
        ], 
        "key": "explainRideFail"
    }, 
    "stopDemonLoveMenu": {
        "image": "goal_memoryProgramming-000000", 
        "name": "COMPLETED - Stop Demon", 
        "key": "stopDemonLoveMenu"
    }, 
    "talkTowerConstruction": {
        "key": "talkTowerConstruction", 
        "constructionIdentifier": "talkTower", 
        "name": "Constructing - Vent To", 
        "image": "trait-aaaaaa99_tower_mineFrustration", 
        "currencyRequested": {
            "care_lover": 5
        }, 
        "constructionName": "Vent To", 
        "killOnTransform": 0
    }, 
    "impulse": {
        "tags": [
            "impulse"
        ], 
        "name": "impulse", 
        "key": "impulse", 
        "checkDayEnd": 1
    }, 
    "endMuseGoalConstruction": {
        "key": "endMuseGoalConstruction", 
        "constructionIdentifier": "endMuseGoal", 
        "name": "Constructing - Cut Off From The Muse", 
        "image": "trait-aaaaaa99_goal_towerConfess-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Cut Off From The Muse", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "towerHelpInfoPanel": {
        "components": [
            "towerComponent", 
            "resourceComponent", 
            "constructionComponent"
        ], 
        "helpLines": [
            "These are the activities that you can perform.", 
            "Activating them will generate the appropriate currency and may require a currency of its own."
        ], 
        "name": "Towers", 
        "key": "towerHelpInfoPanel"
    }, 
    "discardFreshMusicMenu": {
        "image": "goal_discard-000000", 
        "name": "COMPLETED - Discard Fresh Music", 
        "key": "discardFreshMusicMenu"
    }, 
    "relaxGoalConstruction": {
        "key": "relaxGoalConstruction", 
        "constructionIdentifier": "relaxGoal", 
        "name": "Constructing - Relax", 
        "image": "trait-aaaaaa99_goal_goalPark-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Relax", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "stopDemonSelfEsteemMenu": {
        "image": "goal_memoryProgramming-000000", 
        "name": "COMPLETED - Stop Demon", 
        "key": "stopDemonSelfEsteemMenu"
    }, 
    "cheerUpMenu": {
        "image": "trait-ff0066_goal_mineCheer-000000", 
        "name": "COMPLETED - Cheer Up", 
        "key": "cheerUpMenu"
    }, 
    "thoughtButton": {
        "constructionKey": "thoughtTowerConstruction", 
        "name": "Thinking About Music", 
        "baseKeys": [
            "bruteForceTower"
        ], 
        "image": "tower_doubleNote", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "thoughtTower", 
        "limitingKeys": [
            "thoughtTower", 
            "thoughtTowerConstruction"
        ], 
        "key": "thoughtButton", 
        "subPanel": "Towers", 
        "description": "Converts music -> thought"
    }, 
    "quietDemonsToDemons": {
        "sourceTrait": "quietDemons", 
        "newTrait": "demons", 
        "key": "quietDemonsToDemons"
    }, 
    "swallowBitternessMenu": {
        "image": "goal_daydream-000000", 
        "name": "COMPLETED - Swallow Bitterness", 
        "key": "swallowBitternessMenu"
    }, 
    "accomplishment": {
        "name": "accomplishment", 
        "key": "accomplishment", 
        "tags": [
            "accomplishment"
        ]
    }, 
    "curious_ext": {
        "personHeld": "music", 
        "image": [
            "trait-00aaff", 
            "trait-0099E5", 
            "trait-00BBFF"
        ], 
        "name": "Curiosity", 
        "key": "curious_ext", 
        "sourceKey": "curious"
    }, 
    "personCallousnessTowerConstruction": {
        "key": "personCallousnessTowerConstruction", 
        "constructionIdentifier": "personCallousnessTower", 
        "name": "Constructing - Callousness", 
        "image": "trait-aaaaaa99_tower_numb", 
        "currencyRequested": {
            "strength": 5
        }, 
        "constructionName": "Callousness", 
        "killOnTransform": 0
    }, 
    "angerRideButton": {
        "constructionKey": "angerRideGoalConstruction", 
        "name": "Explore Anger", 
        "image": "goalFightProgramming-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "angerRideGoal", 
        "disablingEntities": [
            "makeEmoInspirationGoal", 
            "emoInspiration", 
            "faceEmotionGoal", 
            "makeEmoInspirationGoal", 
            "convinceSelfGoal"
        ], 
        "key": "angerRideButton", 
        "description": "Requires frustration."
    }, 
    "hangingOutToFriend": {
        "sourceTrait": "hangingOut", 
        "newTrait": "friend", 
        "key": "hangingOutToFriend"
    }, 
    "understandFailingsMenu": {
        "image": "goal_eyeTear-000000", 
        "name": "COMPLETED - Understand Song Failings", 
        "key": "understandFailingsMenu"
    }, 
    "hateWorkToAmbition": {
        "sourceTrait": "hateWork", 
        "newTrait": "ambition", 
        "key": "hateWorkToAmbition"
    }, 
    "selfWorthlessMineConstruction": {
        "key": "selfWorthlessMineConstruction", 
        "constructionIdentifier": "selfWorthlessMine", 
        "name": "Feeling Worthless", 
        "image": "tower_trophy-666666aa", 
        "currencyRequested": {
            "impulse": 1
        }, 
        "constructionName": "Feeling Worthless"
    }, 
    "killFlesh": {
        "entitiesToKill": [
            "angerFleshTower", 
            "joyFleshTower", 
            "sadFleshTower", 
            "loverFleshTower", 
            "museFleshTower"
        ], 
        "key": "killFlesh"
    }, 
    "lovingToMakingWork": {
        "sourceTrait": "loving", 
        "newTrait": "makingWork", 
        "key": "lovingToMakingWork"
    }, 
    "towerFocusPathStopComponent": {
        "particleKey": "starParticleEffect"
    }, 
    "memory_loverInsult": {
        "image": "trait-aa1717", 
        "memoryEmotions": [
            "anger"
        ], 
        "key": "memory_loverInsult", 
        "name": "You Don't Need This"
    }, 
    "interestWorkGoal": {
        "key": "interestWorkGoal", 
        "randomPosition": 0, 
        "name": "Interest In Work", 
        "image": "goal_coolNote-000000", 
        "currencyRequested": {
            "coolThing": 3
        }, 
        "entitiesToMake": "curiosityToCaresWork", 
        "goalMenuKey": "interestWorkMenu"
    }, 
    "struggleComplimentsGoalConstruction": {
        "key": "struggleComplimentsGoalConstruction", 
        "constructionIdentifier": "struggleComplimentsGoal", 
        "name": "Constructing - Hope For Compliments", 
        "image": "trait-aaaaaa99_goal_hook-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Hope For Compliments", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "demonsToQuietDemons": {
        "sourceTrait": "demons", 
        "newTrait": "quietDemons", 
        "key": "demonsToQuietDemons"
    }, 
    "care_lover": {
        "name": "care", 
        "key": "care_lover", 
        "tags": [
            "care_lover"
        ]
    }, 
    "hundredHealthComponent": {
        "health": 100
    }, 
    "indulgeCuriosityGoal": {
        "key": "indulgeCuriosityGoal", 
        "randomPosition": 0, 
        "name": "Indulge Curiosity", 
        "image": "goal_puzzle-000000", 
        "currencyRequested": {
            "intrigue": 3, 
            "desireImprovement": 3
        }, 
        "entitiesToMake": "freshMusicToCurious", 
        "goalMenuKey": "indulgeCuriosityMenu"
    }, 
    "rememberGoalConstruction": {
        "key": "rememberGoalConstruction", 
        "constructionIdentifier": "rememberGoal", 
        "name": "Constructing - Remember Being Alone", 
        "image": "trait-aaaaaa99_goal_social-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Remember Being Alone", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "yourAloneTime": {
        "timeToDeath": 30, 
        "name": "You just need some time alone.", 
        "key": "yourAloneTime", 
        "traitsToDisable": [
            "loverBase"
        ]
    }, 
    "currency": {
        "planeOfExistence": "path"
    }, 
    "museFleshTowerConstruction": {
        "key": "museFleshTowerConstruction", 
        "constructionIdentifier": "museFleshTower", 
        "name": "Constructing - Flesh Out With Muse", 
        "image": "trait-aaaaaa99_tower_mineSocial", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Flesh Out With Muse", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "reflectAloneMenu": {
        "image": "goal_social-000000", 
        "name": "COMPLETED - Reflect on Being Alone", 
        "key": "reflectAloneMenu"
    }, 
    "quietDemonsLoveButton": {
        "constructionKey": "quietDemonsLoveGoalConstruction", 
        "name": "Quiet Demons", 
        "image": "goal_heart-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "quietDemonsLoveGoal", 
        "key": "quietDemonsLoveButton", 
        "description": "Requires love."
    }, 
    "releasedSongTraitConnectSelfCore": {
        "startKey": "releasedSong", 
        "endKey": "selfCore", 
        "key": "releasedSongTraitConnectSelfCore"
    }, 
    "explainFleshFail": {
        "completeText": "That just was not good enough for your song.", 
        "key": "explainFleshFail"
    }, 
    "daydreamingToAchievement": {
        "sourceTrait": "daydreaming", 
        "newTrait": "achievement", 
        "key": "daydreamingToAchievement"
    }, 
    "interestWorkMenu": {
        "image": "goal_coolNote-000000", 
        "name": "COMPLETED - Interest In Work", 
        "key": "interestWorkMenu"
    }, 
    "endRealTalkGoal": {
        "key": "endRealTalkGoal", 
        "randomPosition": 0, 
        "name": "You've Said Enough", 
        "image": "goal_social-000000", 
        "currencyRequested": {
            "social_lover": 1
        }, 
        "entitiesToMake": "realTalkToFriend", 
        "goalMenuKey": "endRealTalkMenu"
    }, 
    "selfReliance_ext": {
        "personHeld": "selfCore", 
        "image": [
            "trait-99cc00", 
            "trait-89B700", 
            "trait-A8E000"
        ], 
        "name": "Self-Reliance", 
        "key": "selfReliance_ext", 
        "sourceKey": "selfReliance"
    }, 
    "struggleComplimentsMenu": {
        "image": "goal_hook-000000", 
        "completeText": "This is just not as easy as it used to be.", 
        "name": "COMPLETED - Hope For Compliments", 
        "key": "struggleComplimentsMenu"
    }, 
    "dismissFailureMenu": {
        "image": "goal_goalCleaner-000000", 
        "completeText": "You have better things to do than let this consume you.", 
        "name": "COMPLETED - Dismiss Failures", 
        "key": "dismissFailureMenu"
    }, 
    "museFishButton": {
        "constructionKey": "museFishGoalConstruction", 
        "name": "Fish For Compliments", 
        "image": "goal_hook-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "museFishGoal", 
        "key": "museFishButton", 
        "description": [
            "Requires social, released music.", 
            "Makes self-esteem."
        ]
    }, 
    "loving_ext": {
        "personHeld": "lover", 
        "image": [
            "trait-cc3366", 
            "trait-B72D5B", 
            "trait-E03870"
        ], 
        "name": "Slightly Distant", 
        "key": "loving_ext", 
        "sourceKey": "loving"
    }, 
    "memory_prideBurst": {
        "image": "trait-e6e617", 
        "memoryEmotions": [
            "joy"
        ], 
        "key": "memory_prideBurst", 
        "name": "Burst of Pride"
    }, 
    "reflectFailureButton": {
        "constructionKey": "reflectFailureGoalConstruction", 
        "name": "Reflect on Deficiencies", 
        "image": "goal_eyeTear-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "reflectFailureGoal", 
        "key": "reflectFailureButton", 
        "description": "Requires will."
    }, 
    "holdOn": {
        "name": "holding on", 
        "key": "holdOn", 
        "tags": [
            "holdOn"
        ]
    }, 
    "need_lover": {
        "name": "need", 
        "key": "need_lover", 
        "tags": [
            "need_lover"
        ]
    }, 
    "releaseGoalConstruction": {
        "key": "releaseGoalConstruction", 
        "constructionIdentifier": "releaseGoal", 
        "name": "Constructing - Release Song", 
        "image": "trait-aaaaaa99_goal_cd-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Release Song", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "talkAboutSelfGoalConstruction": {
        "key": "talkAboutSelfGoalConstruction", 
        "constructionIdentifier": "talkAboutSelfGoal", 
        "name": "Constructing - Talk About Yourself", 
        "image": "trait-aaaaaa99_goal_goalConvinceWife-000000-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Talk About Yourself", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "releasedSong": {
        "sector": [
            0, 
            4
        ], 
        "name": "Released Song", 
        "image": "trait-00aaaa", 
        "isaacEntities": [
            "playMusicButton", 
            "releaseSatisfyButton", 
            "indulgeHappyButton"
        ], 
        "health": 300, 
        "key": "releasedSong", 
        "startManagedEntities": [
            "connectSource", 
            "releasedSongTraitConnectSelfCore"
        ]
    }, 
    "alienation": {
        "personHeld": "lover", 
        "isaacEntities": [
            "endAlienationButton", 
            "endAlienationStrengthButton", 
            "strengthMineButton", 
            "personCallousnessButton", 
            "indifferenceButton", 
            "overcomeDistanceGoal", 
            "resolveStrengthButton", 
            "reflectAloneButton"
        ], 
        "image": "trait-cc33ff", 
        "name": "Alienation", 
        "key": "alienation"
    }, 
    "reflectPastGoalConstruction": {
        "key": "reflectPastGoalConstruction", 
        "constructionIdentifier": "reflectPastGoal", 
        "name": "Constructing - Reflect On Past", 
        "image": "trait-aaaaaa99_goal_drive-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Reflect On Past", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "fadeAchievement": {
        "timeToDeath": 180, 
        "key": "fadeAchievement", 
        "makeOnTimeOut": "sustainAchievementGoal"
    }, 
    "discardButton": {
        "constructionKey": "discardGoalConstruction", 
        "name": "Discard Song", 
        "image": "goal_goalDisavowal-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "discardGoal", 
        "key": "discardButton", 
        "description": "Requires will."
    }, 
    "loverJoyMineConstruction": {
        "key": "loverJoyMineConstruction", 
        "constructionIdentifier": "loverJoyMine", 
        "name": "Lover Happiness", 
        "image": "tower_social-666666aa", 
        "currencyRequested": {
            "impulse": 1
        }, 
        "constructionName": "Lover Happiness"
    }, 
    "resolveAction": {
        "towerKey": "resolveTower", 
        "name": "Gain Resolve", 
        "entitiesOnConvert": {
            "fatigueGoal": 5
        }, 
        "currenciesRequired": [
            "will", 
            "selfEsteem"
        ], 
        "currenciesToMake": [
            "resolve"
        ], 
        "key": "resolveAction"
    }, 
    "resolveTowerConstruction": {
        "key": "resolveTowerConstruction", 
        "constructionIdentifier": "resolveTower", 
        "name": "Constructing - Gain Resolve", 
        "image": "trait-aaaaaa99_tower_drive", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Gain Resolve", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "selfHate": {
        "emotionsToAdd": {
            "anger": 1
        }, 
        "name": "Hating Yourself", 
        "image": "tower_memoryDefineSelf-000000", 
        "key": "selfHate", 
        "memoryKey": "memory_selfHate", 
        "emotionTags": "self", 
        "resourceKey": "selfHateMineConstruction", 
        "activeImage": "tower_memoryDefineSelf-000000_activeOutline-0099ff"
    }, 
    "settingAction": {
        "currenciesRequired": [
            "understanding", 
            "music"
        ], 
        "towerKey": "settingTower", 
        "currenciesToMake": [
            "emoSolution"
        ], 
        "name": "Setting", 
        "key": "settingAction"
    }, 
    "makingWork_ext": {
        "personHeld": "lover", 
        "image": [
            "trait-993366", 
            "trait-892D5B", 
            "trait-A83870"
        ], 
        "name": "Making Things Work", 
        "key": "makingWork_ext", 
        "sourceKey": "makingWork"
    }, 
    "reflect": {
        "name": "reflect", 
        "key": "reflect", 
        "tags": [
            "reflect"
        ]
    }, 
    "tryForMeaningButton": {
        "constructionKey": "tryForMeaningGoalConstruction", 
        "name": "Try For Deeper Meaning", 
        "image": "goal_social-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "tryForMeaningGoal", 
        "key": "tryForMeaningButton", 
        "description": [
            "Requires work on emotion, thought.", 
            "Makes alienation."
        ]
    }, 
    "nascentSong": {
        "sector": [
            4, 
            4
        ], 
        "name": "Nascent Song", 
        "image": "trait-aa3399", 
        "personHeld": "song", 
        "isaacEntities": [
            "fleshSongButton", 
            "angerFleshButton", 
            "joyFleshButton", 
            "sadFleshButton", 
            "loverFleshButton", 
            "museFleshButton"
        ], 
        "key": "nascentSong", 
        "startManagedEntities": [
            "nascentSongTraitConnectMusic", 
            "nascentSongTraitConnectLoverBase"
        ]
    }, 
    "reflectSuccessButton": {
        "constructionKey": "reflectSuccessGoalConstruction", 
        "name": "Reflect on Success", 
        "image": "goal_trophy-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "reflectSuccessGoal", 
        "key": "reflectSuccessButton", 
        "description": "Requires self-esteem."
    }, 
    "memory_drainedSad": {
        "image": "trait-013d70", 
        "memoryEmotions": [
            "sadness"
        ], 
        "key": "memory_drainedSad", 
        "name": "Feeling Drained"
    }, 
    "intrigueButton": {
        "constructionKey": "intrigueTowerConstruction", 
        "name": "Intrigued By", 
        "image": "tower_openMind", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "intrigueTower", 
        "limitingKeys": [
            "intrigueTower", 
            "intrigueTowerConstruction"
        ], 
        "key": "intrigueButton", 
        "subPanel": "Towers", 
        "description": [
            "How do you come up with these?", 
            "Converts thought -> intrigue"
        ]
    }, 
    "deriveCoolThingGoal": {
        "key": "deriveCoolThingGoal", 
        "randomPosition": 0, 
        "name": "Derive Cool Thing", 
        "image": "goal_coolNote-000000", 
        "currencyRequested": {
            "thought": 3
        }, 
        "entitiesToMake": [
            "coolThingDeposit", 
            "killDeriveCoolThingButton"
        ], 
        "goalMenuKey": "deriveCoolThingMenu"
    }, 
    "fleshSongGoalConstruction": {
        "key": "fleshSongGoalConstruction", 
        "constructionIdentifier": "fleshSongGoal", 
        "name": "Constructing - Flesh Out Song", 
        "image": "trait-aaaaaa99_goal_coolNote-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Flesh Out Song", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "releasedMusic": {
        "name": "released music", 
        "key": "releasedMusic", 
        "tags": [
            "releasedMusic"
        ]
    }, 
    "makingWork": {
        "name": "Making Things Work", 
        "aliveEntities": {
            "insultingMake": 5
        }, 
        "image": "trait-993366", 
        "personHeld": "lover", 
        "isaacEntities": [
            "tryComplimentsButton", 
            "romanceReplace", 
            "tryOldJokesButton"
        ], 
        "key": "makingWork"
    }, 
    "hateWork_ext": {
        "personHeld": "selfCore", 
        "image": [
            "trait-00cc00", 
            "trait-00B700", 
            "trait-00E000"
        ], 
        "name": "Hating Your Job", 
        "key": "hateWork_ext", 
        "sourceKey": "hateWork"
    }, 
    "alienationToFriend": {
        "sourceTrait": "alienation", 
        "newTrait": "friend", 
        "key": "alienationToFriend"
    }, 
    "breakdownMenu": {
        "endGame": 1, 
        "completeText": [
            "This kind of life is just too much for you. You just cannot handle", 
            "this amount of constant emotion."
        ], 
        "key": "breakdownMenu"
    }, 
    "endAlienationMenu": {
        "image": "goal_heart-000000", 
        "name": "COMPLETED - End Alienation", 
        "key": "endAlienationMenu"
    }, 
    "minorInsultMenu": {
        "image": "goal_towerConfess-000000", 
        "name": "COMPLETED - Smooth Over Misunderstanding", 
        "key": "minorInsultMenu"
    }, 
    "explainCheerFail": {
        "completeText": "The lover meant well, but saying the wrong thing still hurts.", 
        "key": "explainCheerFail"
    }, 
    "angerMap": {
        "startManagedEntities": [
            "frustrationMine"
        ], 
        "emotionToMap": "anger", 
        "emotionMappedEntities": [
            "applyButton", 
            "angerRideButton", 
            "rageButton", 
            "angryMemoryFade", 
            "workThroughButton", 
            "fireUpButton"
        ], 
        "key": "angerMap"
    }, 
    "sustainFreshMusicGoal": {
        "key": "sustainFreshMusicGoal", 
        "randomPosition": 1, 
        "name": "Keep Fresh Music Alive", 
        "makeOnTimeOut": "freshMusicToMusicLove", 
        "image": "goal_mineCaution-000000", 
        "sourceTrait": "freshMusic", 
        "currencyRequested": {
            "music": 3
        }, 
        "entitiesToMake": "fadeFreshMusic", 
        "goalMenuKey": "sustainFreshMusicMenu", 
        "timeToDeath": 120
    }, 
    "sadRideButton": {
        "constructionKey": "sadRideGoalConstruction", 
        "name": "Explore Sadness", 
        "image": "goalFightProgramming-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "sadRideGoal", 
        "disablingEntities": [
            "makeEmoInspirationGoal", 
            "emoInspiration", 
            "faceEmotionGoal", 
            "makeEmoInspirationGoal", 
            "convinceSelfGoal"
        ], 
        "key": "sadRideButton", 
        "description": "Requires pain."
    }, 
    "aloneSad": {
        "emotionsToAdd": {
            "sadness": 2
        }, 
        "name": "Feeling Alone", 
        "image": "tower_social", 
        "key": "aloneSad", 
        "memoryKey": "memory_aloneSad", 
        "emotionTags": "lover", 
        "resourceKey": "aloneSadMineConstruction", 
        "activeImage": "tower_social_activeOutline-0099ff", 
        "description": "There really isn't anyone else who cares."
    }, 
    "rideResult": {
        "goodEntities": {
            "faceEmotionGoal": 3
        }, 
        "badEntities": "explainRideFail", 
        "key": "rideResult"
    }, 
    "playMusicTower": {
        "image": "tower_coolNote", 
        "activeImage": "tower_coolNote_activeOutline-0099ff", 
        "name": "Play Released Music", 
        "key": "playMusicTower", 
        "description": "Converts music -> released music"
    }, 
    "joyRideButton": {
        "constructionKey": "joyRideGoalConstruction", 
        "name": "Explore Joy", 
        "image": "goalFightProgramming-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "joyRideGoal", 
        "disablingEntities": [
            "makeEmoInspirationGoal", 
            "emoInspiration", 
            "faceEmotionGoal", 
            "makeEmoInspirationGoal", 
            "convinceSelfGoal"
        ], 
        "key": "joyRideButton", 
        "description": "Requires cheer."
    }, 
    "makeSadSong": {
        "entitiesToMake": [
            "nascentSongToSong", 
            "killFlesh"
        ], 
        "key": "makeSadSong"
    }, 
    "coolThing": {
        "name": "cool thing", 
        "key": "coolThing", 
        "tags": [
            "coolThing"
        ]
    }, 
    "personHealAction": {
        "health": 0
    }, 
    "indulgeHappyMenu": {
        "image": "goal_mineCheer-000000", 
        "name": "COMPLETED - Indulge In Satisfaction", 
        "key": "indulgeHappyMenu"
    }, 
    "cheerLoverMenu": {
        "image": "goal_mineCheer-000000", 
        "completeText": [
            "It's often hard to get through, but the lover never did have enough", 
            "defense against joy."
        ], 
        "name": "COMPLETED - Cheer Up Lover", 
        "key": "cheerLoverMenu"
    }, 
    "failureMine": {
        "image": "tower_pain", 
        "resourceKeys": [
            "failure"
        ], 
        "name": "Failures", 
        "key": "failureMine", 
        "activeImage": "tower_pain_activeOutline-0099ff"
    }, 
    "insight": {
        "name": "insight", 
        "key": "insight", 
        "tags": [
            "insight"
        ]
    }, 
    "fireUpGoal": {
        "key": "fireUpGoal", 
        "randomPosition": 0, 
        "currencyRequested": {
            "frustration": 6
        }, 
        "image": "goal_weight-000000", 
        "flareMemory": 1, 
        "entitiesToMake": "strengthDeposit", 
        "goalMenuKey": "fireUpMenu", 
        "name": "Fire Yourself Up"
    }, 
    "desireImprovementGoal": {
        "key": "desireImprovementGoal", 
        "randomPosition": 0, 
        "name": "Desire Self-Improvement", 
        "image": "goal_drive-000000", 
        "currencyRequested": {
            "failure": 3, 
            "dreamGreatness": 3
        }, 
        "entitiesToMake": [
            "desireImprovementDeposit"
        ], 
        "goalMenuKey": "desireImprovementMenu"
    }, 
    "tellQuitButton": {
        "constructionKey": "tellQuitGoalConstruction", 
        "name": "Speak of New Start", 
        "image": "goal_trophy-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "tellQuitGoal", 
        "key": "tellQuitButton", 
        "description": "Requires social."
    }, 
    "angerFleshButton": {
        "constructionKey": "angerFleshTowerConstruction", 
        "name": "Flesh Out With Anger", 
        "image": "tower_mineFrustration", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "angerFleshTower", 
        "limitingKeys": [
            "angerFleshTower", 
            "angerFleshTowerConstruction"
        ], 
        "key": "angerFleshButton", 
        "subPanel": "Towers", 
        "description": "Converts frustration -> satisfaction"
    }, 
    "musicLoveToFreshMusic": {
        "sourceTrait": "musicLove", 
        "newTrait": "freshMusic", 
        "key": "musicLoveToFreshMusic"
    }, 
    "hurtRelationship": {
        "entitiesToMake": {
            "addUnhappy": 25
        }, 
        "key": "hurtRelationship"
    }, 
    "freshMusic": {
        "name": "New Music", 
        "image": "trait-00aacc", 
        "personHeld": "music", 
        "isaacEntities": [
            "solveMusicButton", 
            "fadeFreshMusic", 
            "indulgeCuriosityButton", 
            "bruteForceButton", 
            "envyButton", 
            "discardFreshMusicButton"
        ], 
        "key": "freshMusic", 
        "description": "You won't listen to this forever, but for now it's everything."
    }, 
    "selfLoathingMine": {
        "name": "Self-Loathing", 
        "image": "tower_eyeTear-000000", 
        "key": "selfLoathingMine", 
        "resourceKeys": [
            "selfLoathe"
        ], 
        "activeImage": "tower_eyeTear-000000_activeOutline-0099ff", 
        "description": "What is there to like about someone like you?"
    }, 
    "sayRight_lover": {
        "name": "right words", 
        "key": "sayRight_lover", 
        "tags": [
            "sayRight_lover"
        ]
    }, 
    "drownTower": {
        "image": "tower_wave", 
        "activeImage": "tower_wave_activeOutline-0099ff", 
        "name": "Drowning In Muse", 
        "key": "drownTower", 
        "description": "Converts music + social -> drowning"
    }, 
    "destinyGoal": {
        "key": "destinyGoal", 
        "randomPosition": 0, 
        "name": "Believe In Future", 
        "image": "goal_daydream-000000", 
        "currencyRequested": {
            "selfEsteem": 3
        }, 
        "entitiesToMake": "alonenessToAmbition", 
        "goalMenuKey": "destinyMenu", 
        "description": [
            "It doesn't matter that you have to do it alone. You will do great", 
            "things."
        ]
    }, 
    "angerRideGoal": {
        "key": "angerRideGoal", 
        "randomPosition": 0, 
        "currencyRequested": {
            "frustration": 3
        }, 
        "image": "goal_goalFightProgramming-000000-000000", 
        "killMemory": 0, 
        "flareMemory": 1, 
        "entitiesToMake": "rideResult", 
        "goalMenuKey": "angerRideMenu", 
        "name": "Explore Anger"
    }, 
    "songSatisfiedGoalConstruction": {
        "key": "songSatisfiedGoalConstruction", 
        "constructionIdentifier": "songSatisfiedGoal", 
        "name": "Constructing - Check Song Quality", 
        "image": "trait-aaaaaa99_goal_coolNote-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Check Song Quality", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "loverJoyMine": {
        "resourceCount": 1, 
        "name": "Lover Happiness", 
        "image": "tower_social", 
        "key": "loverJoyMine", 
        "emotionTags": [
            "lover", 
            "joy"
        ], 
        "resourceKeys": [
            "joy"
        ], 
        "activeImage": "tower_social_activeOutline-0099ff", 
        "description": "It feels good when the lover is happy - makes joy"
    }, 
    "quietDemonsStrengthButton": {
        "constructionKey": "quietDemonsStrengthGoalConstruction", 
        "name": "Quiet Demons", 
        "image": "goal_weight-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "quietDemonsStrengthGoal", 
        "key": "quietDemonsStrengthButton", 
        "description": "Requires strength."
    }, 
    "pushYourselfMine": {
        "image": "tower_mineWill", 
        "resourceKeys": [
            "will"
        ], 
        "name": "Push Yourself", 
        "key": "pushYourselfMine", 
        "activeImage": "tower_mineWill_activeOutline-0099ff"
    }, 
    "loverBase_ext": {
        "personHeld": "lover", 
        "image": [
            "trait-ff3366", 
            "trait-E52D5B", 
            "trait-FF3870"
        ], 
        "name": "The Lover", 
        "key": "loverBase_ext", 
        "sourceKey": "loverBase"
    }, 
    "friendToMakingWork": {
        "sourceTrait": "friend", 
        "newTrait": "makingWork", 
        "key": "friendToMakingWork"
    }, 
    "joyMemoryFade": {
        "newTrait": "fadingJoyMemory", 
        "key": "joyMemoryFade", 
        "timeToDeath": 450
    }, 
    "romance_ext": {
        "personHeld": "lover", 
        "image": [
            "trait-aa0066", 
            "trait-99005B", 
            "trait-BB0070"
        ], 
        "name": "Romance", 
        "key": "romance_ext", 
        "sourceKey": "romance"
    }, 
    "solutionAction": {
        "currenciesRequired": [
            "insight", 
            "will"
        ], 
        "towerKey": "solutionTower", 
        "currenciesToMake": [
            "techSolution"
        ], 
        "name": "Solve", 
        "key": "solutionAction"
    }, 
    "impressGoalConstruction": {
        "key": "impressGoalConstruction", 
        "constructionIdentifier": "impressGoal", 
        "name": "Constructing - Impress Lover", 
        "image": "trait-aaaaaa99_goal_trophy-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Impress Lover", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "dreamOfGreatnessGoalConstruction": {
        "key": "dreamOfGreatnessGoalConstruction", 
        "constructionIdentifier": "dreamOfGreatnessGoal", 
        "name": "Constructing - Dream of Greatness", 
        "image": "trait-aaaaaa99_goal_daydream-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Dream of Greatness", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "sinkWorkTowerConstruction": {
        "key": "sinkWorkTowerConstruction", 
        "constructionIdentifier": "sinkWorkTower", 
        "name": "Constructing - Sink Into Work", 
        "image": "trait-aaaaaa99_tower_lifePreserver", 
        "currencyRequested": {
            "thought": 5
        }, 
        "constructionName": "Sink Into Work", 
        "killOnTransform": 0
    }, 
    "makingWorkToFriend": {
        "sourceTrait": "makingWork", 
        "newTrait": "friend", 
        "key": "makingWorkToFriend"
    }, 
    "thinkSongBadButton": {
        "constructionKey": "thinkSongBadGoalConstruction", 
        "name": "Face Song Failings", 
        "image": "goal_eyeTear-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "thinkSongBadGoal", 
        "disablingEntities": [
            "easyProblemDeposit", 
            "emoProblemDeposit", 
            "techProblemDeposit", 
            "understandFailingsGoal"
        ], 
        "key": "thinkSongBadButton", 
        "description": "Requires resolve."
    }, 
    "shameBurstMine": {
        "resourceCount": 1, 
        "name": "Burst of Shame", 
        "image": "tower_memoryShame-000000", 
        "key": "shameBurstMine", 
        "emotionTags": [
            "self", 
            "sadness"
        ], 
        "resourceKeys": [
            "sadness"
        ], 
        "activeImage": "tower_memoryShame-000000_activeOutline-0099ff", 
        "description": "makes sadness"
    }, 
    "musicLove": {
        "sector": [
            2, 
            0
        ], 
        "name": "The Music", 
        "image": "trait-00aaaa", 
        "personHeld": "music", 
        "isaacEntities": [
            "musicMineButton", 
            "findNewMusicButton", 
            "startNewSongButton", 
            "refineInspirationButton"
        ], 
        "key": "musicLove", 
        "startManagedEntities": [
            "musicLovePathHateWork"
        ], 
        "description": "It's just important"
    }, 
    "steamPlay": {
        "steamAchievementKey": "play", 
        "keyToCheck": "playWithMusicTowerConstruction", 
        "key": "steamPlay"
    }, 
    "faceEmotionGoal": {
        "key": "faceEmotionGoal", 
        "randomPosition": 0, 
        "currencyRequested": {
            "strength": 3
        }, 
        "image": "goal_weight-000000", 
        "killMemory": 0, 
        "sourceTrait": 0, 
        "flareMemory": 0, 
        "entitiesToMake": "makeEmoInspirationGoal", 
        "goalMenuKey": "faceEmotionMenu", 
        "name": "Face Emotion"
    }, 
    "obnoxiousMenu": {
        "image": "goal_pain-000000", 
        "name": "COMPLETED - Suppress Obnoxious Happiness", 
        "key": "obnoxiousMenu"
    }, 
    "tryOldJokesButton": {
        "constructionKey": "tryOldJokesGoalConstruction", 
        "name": "Try Old Jokes", 
        "image": "goal_social-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "tryOldJokesGoal", 
        "key": "tryOldJokesButton", 
        "description": [
            "Requires social, cheer.", 
            "Makes alienation."
        ]
    }, 
    "rageButton": {
        "constructionKey": "rageTowerConstruction", 
        "name": "Rage", 
        "image": "tower_towerDisavowal", 
        "entityCost": {
            "frustration": 5
        }, 
        "entityKey": "rageTower", 
        "limitingKeys": [
            "rageTower", 
            "rageTowerConstruction"
        ], 
        "key": "rageButton", 
        "description": "Uses frustration to calm sadness, joy"
    }, 
    "memory_shameBurst": {
        "image": "trait-013d70", 
        "memoryEmotions": [
            "sadness"
        ], 
        "key": "memory_shameBurst", 
        "name": "Burst of Shame"
    }, 
    "achievementGoalConstruction": {
        "key": "achievementGoalConstruction", 
        "constructionIdentifier": "achievementGoal", 
        "name": "Constructing - Note Achievement", 
        "image": "trait-aaaaaa99_goal_trophy-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Note Achievement", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "captureMusicGoal": {
        "key": "captureMusicGoal", 
        "randomPosition": 0, 
        "name": "Capture Inspiration", 
        "image": "goal_butterfly-000000", 
        "currencyRequested": {
            "music": 1
        }, 
        "entitiesToMake": "captureInspirationResult", 
        "goalMenuKey": "captureMusicMenu", 
        "timeToDeath": 60
    }, 
    "ambition": {
        "sector": [
            0, 
            0
        ], 
        "name": "Ambition", 
        "image": "trait-99ff00", 
        "personHeld": "selfCore", 
        "isaacEntities": [
            "willMineButton", 
            "callousnessButton", 
            "rollSleevesButton", 
            "deriveStrengthButton", 
            "daydreamButton", 
            "flagQuitPrideGoal"
        ], 
        "health": 1000, 
        "key": "ambition", 
        "maxLevel": 12, 
        "description": "You know that you can be great"
    }, 
    "settingTowerConstruction": {
        "key": "settingTowerConstruction", 
        "constructionIdentifier": "settingTower", 
        "name": "Constructing - Add Life", 
        "image": "trait-aaaaaa99_tower_towerPaint", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Add Life", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "museBase_ext": {
        "personHeld": "muse", 
        "image": [
            "trait-ff6699", 
            "trait-E55B89", 
            "trait-FF70A8"
        ], 
        "name": "The Muse", 
        "key": "museBase_ext", 
        "sourceKey": "museBase"
    }, 
    "frustrationMine": {
        "image": "tower_mineFrustration", 
        "resourceKeys": [
            "frustration"
        ], 
        "name": "Frustration Mine", 
        "key": "frustrationMine", 
        "activeImage": "tower_mineFrustration_activeOutline-0099ff"
    }, 
    "gameStateEntity": {
        "name": "gameStateEntity", 
        "key": "gameStateEntity"
    }, 
    "museFleshTower": {
        "image": "tower_mineSocial", 
        "activeImage": "tower_mineSocial_activeOutline-0099ff", 
        "name": "Flesh Out With Muse", 
        "key": "museFleshTower", 
        "description": "Converts drowning -> satisfaction"
    }, 
    "tellQuitGoalConstruction": {
        "key": "tellQuitGoalConstruction", 
        "constructionIdentifier": "tellQuitGoal", 
        "name": "Constructing - Speak of New Start", 
        "image": "trait-aaaaaa99_goal_trophy-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Speak of New Start", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "memory_loverJoy": {
        "image": "trait-e6e617", 
        "memoryEmotions": [
            "joy"
        ], 
        "key": "memory_loverJoy", 
        "name": "Lover Happiness"
    }, 
    "angerFleshTowerConstruction": {
        "key": "angerFleshTowerConstruction", 
        "constructionIdentifier": "angerFleshTower", 
        "name": "Constructing - Flesh Out With Anger", 
        "image": "trait-aaaaaa99_tower_mineFrustration", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Flesh Out With Anger", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "selfLoathingToBitterness": {
        "sourceTrait": "selfLoathing", 
        "newTrait": "bitterness", 
        "key": "selfLoathingToBitterness"
    }, 
    "hangOutGoal": {
        "currencyEntities": {
            "condCaptureInspirationGoal": 10
        }, 
        "randomPosition": 0, 
        "name": "Hang Out", 
        "image": "goal_goalPark-000000", 
        "key": "hangOutGoal", 
        "currencyRequested": {
            "cheer": 3
        }, 
        "entitiesToMake": "friendToHangingOut", 
        "goalMenuKey": "hangOutMenu"
    }, 
    "sustainAchievementMenu": {
        "image": "goal_mineCaution-000000", 
        "name": "COMPLETED - Sustain Achievement", 
        "key": "sustainAchievementMenu"
    }, 
    "endAlienationStrengthMenu": {
        "image": "goal_weight-000000", 
        "name": "COMPLETED - End Alienation", 
        "key": "endAlienationStrengthMenu"
    }, 
    "selfLoathingMineButton": {
        "constructionKey": "selfLoathingMineConstruction", 
        "name": "Self-Loathing", 
        "entityCost": {
            "will": 2
        }, 
        "image": "tower_eyeTear-000000", 
        "currenciesMade": [
            "selfLoathe"
        ], 
        "entityKey": "selfLoathingMine", 
        "limitingKeys": [
            "selfLoathingMine", 
            "selfLoathingMineConstruction"
        ], 
        "key": "selfLoathingMineButton", 
        "description": "makes self-loathing"
    }, 
    "beCaredButton": {
        "constructionKey": "beCaredGoalConstruction", 
        "name": "Be Cared For", 
        "image": "goal_heart-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "beCaredGoal", 
        "key": "beCaredButton", 
        "description": [
            "Requires need.", 
            "Makes care."
        ]
    }, 
    "intrigueTowerConstruction": {
        "key": "intrigueTowerConstruction", 
        "constructionIdentifier": "intrigueTower", 
        "name": "Constructing - Intrigued By", 
        "image": "trait-aaaaaa99_tower_openMind", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Intrigued By", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "newTraitParticle": {
        "effectRadius": 30, 
        "image": "dot-ffd700", 
        "key": "newTraitParticle"
    }, 
    "dreamOfGreatnessGoal": {
        "key": "dreamOfGreatnessGoal", 
        "randomPosition": 0, 
        "name": "Dream of Greatness", 
        "image": "goal_daydream-000000", 
        "currencyRequested": {
            "love_lover": 3
        }, 
        "entitiesToMake": [
            "greatnessDreamDeposit"
        ], 
        "goalMenuKey": "dreamOfGreatnessMenu", 
        "description": "You want to admit it. You just need some encouragement."
    }, 
    "understandFailingsGoal": {
        "key": "understandFailingsGoal", 
        "randomPosition": 0, 
        "name": "Understand Song Failings", 
        "image": "goal_eyeTear-000000", 
        "currencyRequested": {
            "thought": 3
        }, 
        "entitiesToMake": "songToSadSong", 
        "goalMenuKey": "understandFailingsMenu", 
        "description": [
            "It's not enough to accept failures. You need to understand them as", 
            "well."
        ]
    }, 
    "cheerPrideMenu": {
        "image": "goal_trophy-000000", 
        "name": "COMPLETED - Feel Good About Self", 
        "key": "cheerPrideMenu"
    }, 
    "intrigueAction": {
        "currenciesRequired": [
            "thought"
        ], 
        "towerKey": "intrigueTower", 
        "currenciesToMake": [
            "intrigue"
        ], 
        "name": "Intrigued By", 
        "key": "intrigueAction"
    }, 
    "singleUseTrainPath": {
        "focusCost": 0, 
        "requiresFullPath": "false"
    }, 
    "personCurrency": {
        "planeOfExistence": "path"
    }, 
    "meetMuseGoal": {
        "key": "meetMuseGoal", 
        "randomPosition": 1, 
        "name": "Meet Fan!", 
        "image": "trait-ede9e6_goal_towerFlirt-000000", 
        "description": "You have a fan! No one's ever been this excited to meet you before.", 
        "currencyRequested": {
            "releasedMusic": 3
        }, 
        "entitiesToMake": "museBase", 
        "goalMenuKey": "meetMuseMenu", 
        "timeToDeath": 300
    }, 
    "bruteForceTower": {
        "image": "tower_energy", 
        "activeImage": "tower_energy_activeOutline-0099ff", 
        "name": "Brute Force Problem", 
        "key": "bruteForceTower", 
        "description": "Converts resolve + music -> thought"
    }, 
    "sayRightAction": {
        "currenciesRequired": [
            "understanding", 
            "social_lover"
        ], 
        "towerKey": "sayRightTower", 
        "currenciesToMake": [
            "sayRight_lover"
        ], 
        "name": "Saying The Right Things", 
        "key": "sayRightAction"
    }, 
    "dreamFutureGoal": {
        "key": "dreamFutureGoal", 
        "randomPosition": 0, 
        "name": "Dream of Future", 
        "image": "goal_daydream-000000", 
        "currencyRequested": {
            "cheer": 3
        }, 
        "entitiesToMake": "selfRelianceToAmbition", 
        "goalMenuKey": "dreamFutureMenu"
    }, 
    "cynicismAction": {
        "currenciesRequired": [
            "will"
        ], 
        "towerKey": "cynicismTower", 
        "name": "Cynicism", 
        "key": "cynicismAction"
    }, 
    "sinkWorkButton": {
        "constructionKey": "sinkWorkTowerConstruction", 
        "name": "Sink Into Work", 
        "image": "tower_lifePreserver", 
        "entityCost": {
            "thought": 5
        }, 
        "entityKey": "sinkWorkTower", 
        "limitingKeys": [
            "sinkWorkTower", 
            "sinkWorkTowerConstruction"
        ], 
        "key": "sinkWorkButton", 
        "subPanel": "Defenses", 
        "description": "Uses thought to calm anger, joy, sadness"
    }, 
    "deriveStrengthGoal": {
        "key": "deriveStrengthGoal", 
        "randomPosition": 0, 
        "name": "Derive Strength", 
        "image": "goal_weightPlus-000000", 
        "currencyRequested": {
            "will": 6
        }, 
        "entitiesToMake": [
            "strengthDeposit"
        ], 
        "goalMenuKey": "deriveStrengthMenu"
    }, 
    "lovingToFriend": {
        "sourceTrait": "loving", 
        "newTrait": "friend", 
        "key": "lovingToFriend"
    }, 
    "makeEmoInspiration": {
        "entitiesToMake": [
            "emoInspiration", 
            "emoInspirationTraitConnectSong"
        ], 
        "key": "makeEmoInspiration"
    }, 
    "shareJoyMuseButton": {
        "constructionKey": "shareJoyMuseTowerConstruction", 
        "name": "Share Joy", 
        "image": "tower_towerTalk", 
        "entityCost": {
            "social_muse": 5
        }, 
        "entityKey": "shareJoyMuseTower", 
        "limitingKeys": [
            "shareJoyMuseTower", 
            "shareJoyMuseTowerConstruction"
        ], 
        "key": "shareJoyMuseButton", 
        "subPanel": "Defenses", 
        "description": "Uses social to calm joy"
    }, 
    "loseImpressedMenu": {
        "image": "trait-ff0066_goal_droop-000000", 
        "name": "COMPLETED - Be Letdown", 
        "key": "loseImpressedMenu"
    }, 
    "shareTower": {
        "image": "tower_towerDate", 
        "activeImage": "tower_towerDate_activeOutline-0099ff", 
        "name": "Share Experiences", 
        "key": "shareTower", 
        "description": "Converts social -> sharing"
    }, 
    "explainCheerLoverFail": {
        "completeText": "You made the attempt, but it just wasn't enough this time.", 
        "key": "explainCheerLoverFail"
    }, 
    "captureInspirationMenu": {
        "image": "goal_butterfly-000000", 
        "name": "COMPLETED - Capture Inspiration", 
        "key": "captureInspirationMenu"
    }, 
    "needLoverAction": {
        "currenciesRequired": [
            "social_lover", 
            "selfEsteem"
        ], 
        "towerKey": "needLoverTower", 
        "currenciesToMake": [
            "need_lover"
        ], 
        "name": "Express Need For Lover", 
        "key": "needLoverAction"
    }, 
    "loverLeaveMenu": {
        "endGame": 1, 
        "completeText": [
            "You cannot do this without the lover.", 
            "-----", 
            "Failing the Beg Lover To Stay goal has ended the game."
        ], 
        "key": "loverLeaveMenu"
    }, 
    "showMusicDisable": {
        "name": "You need to show the lover your music first.", 
        "key": "showMusicDisable", 
        "traitsToDisable": [
            "romance", 
            "loving", 
            "makingWork", 
            "friend", 
            "hangingOut", 
            "alienation", 
            "realTalk"
        ]
    }, 
    "envyGoal": {
        "key": "envyGoal", 
        "randomPosition": 0, 
        "name": "Give In To Envy", 
        "image": "goal_depositSin-000000", 
        "currencyRequested": {
            "will": 1
        }, 
        "entitiesToMake": "selfHate", 
        "goalMenuKey": "envyMenu", 
        "description": "How are other people successful and not you?"
    }, 
    "memory_aloneSad": {
        "image": "trait-013d70", 
        "memoryEmotions": [
            "sadness"
        ], 
        "key": "memory_aloneSad", 
        "name": "Feeling Alone"
    }, 
    "fishComplimentsGoal": {
        "currencyEntities": {
            "loverJoy": 50
        }, 
        "randomPosition": 0, 
        "name": "Fish For Compliments", 
        "image": "goal_hook-000000", 
        "key": "fishComplimentsGoal", 
        "currencyRequested": {
            "social_lover": 3, 
            "music": 3
        }, 
        "entitiesToMake": "selfEsteemDeposit", 
        "goalMenuKey": "fishComplimentsMenu"
    }, 
    "identifyProblemButton": {
        "constructionKey": "identifyProblemGoalConstruction", 
        "name": "Identify Issue", 
        "image": "goal_thinkProblem-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "identifyProblemGoal", 
        "key": "identifyProblemButton", 
        "description": "Requires thought."
    }, 
    "memory_loverSad": {
        "image": "trait-013d70", 
        "memoryEmotions": [
            "sadness"
        ], 
        "key": "memory_loverSad", 
        "name": "Lover Sadness"
    }, 
    "talkAboutLoverButton": {
        "constructionKey": "talkAboutLoverGoalConstruction", 
        "name": "Talk About Lover", 
        "image": "goal_goalChat-000000-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "talkAboutLoverGoal", 
        "disablingEntities": [
            "overcomeDistanceGoal", 
            "cheerLoverGoal"
        ], 
        "key": "talkAboutLoverButton", 
        "description": "Requires social, cheer."
    }, 
    "aloneness": {
        "name": "Aloneness", 
        "aliveEntities": {
            "aloneSad": 10
        }, 
        "image": "trait-6600aa", 
        "personHeld": "selfCore", 
        "isaacEntities": [
            "alienationMineButton", 
            "rememberButton", 
            "destinyButton"
        ], 
        "key": "aloneness", 
        "maxLevel": 12
    }, 
    "callousnessTowerConstruction": {
        "key": "callousnessTowerConstruction", 
        "constructionIdentifier": "callousnessTower", 
        "name": "Constructing - Callousness", 
        "image": "trait-aaaaaa99_tower_numb", 
        "currencyRequested": {
            "strength": 5
        }, 
        "constructionName": "Callousness", 
        "killOnTransform": 0
    }, 
    "museFleshButton": {
        "constructionKey": "museFleshTowerConstruction", 
        "name": "Flesh Out With Muse", 
        "image": "tower_mineSocial", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "museFleshTower", 
        "limitingKeys": [
            "museFleshTower", 
            "museFleshTowerConstruction"
        ], 
        "key": "museFleshButton", 
        "subPanel": "Towers", 
        "description": "Converts drowning -> satisfaction"
    }, 
    "stopDemonSelfEsteemGoal": {
        "key": "stopDemonSelfEsteemGoal", 
        "randomPosition": 1, 
        "name": "Stop Demon", 
        "makeOnTimeOut": "selfWorthless", 
        "image": "goal_memoryProgramming-000000", 
        "sourceTrait": "demons", 
        "currencyRequested": {
            "selfEsteem": 3
        }, 
        "goalMenuKey": "stopDemonSelfEsteemMenu", 
        "timeToDeath": 240
    }, 
    "acceptAlienationMenu": {
        "image": "goal_social-000000", 
        "completeText": "There's only one of you.", 
        "name": "COMPLETED - Accept Alienation", 
        "key": "acceptAlienationMenu"
    }, 
    "resolveTower": {
        "image": "tower_drive", 
        "activeImage": "tower_drive_activeOutline-0099ff", 
        "name": "Gain Resolve", 
        "key": "resolveTower", 
        "description": [
            "You need to work yourself up for some things.", 
            "Converts will + self-esteem -> resolve"
        ]
    }, 
    "dismissFailureGoal": {
        "key": "dismissFailureGoal", 
        "randomPosition": 0, 
        "name": "Dismiss Failures", 
        "image": "goal_goalCleaner-000000", 
        "currencyRequested": {
            "selfEsteem": 6
        }, 
        "entitiesToMake": "selfLoathingToAmbition", 
        "goalMenuKey": "dismissFailureMenu", 
        "description": "True or not, you can't let this define you."
    }, 
    "steamRelease": {
        "steamAchievementKey": "release", 
        "keyToCheck": "releaseGoal", 
        "key": "steamRelease"
    }, 
    "museInspirationGoal": {
        "key": "museInspirationGoal", 
        "randomPosition": 0, 
        "name": "Be Inspired!", 
        "image": "goal_towerThinkAbout-000000", 
        "currencyRequested": {
            "drown_muse": 3
        }, 
        "entitiesToMake": "museInspireResult", 
        "goalMenuKey": "museInspirationMenu"
    }, 
    "acceptCheerResult": {
        "goodEntities": {
            "loverJoy": 2
        }, 
        "badEntities": "cheerFail", 
        "key": "acceptCheerResult"
    }, 
    "prideBurst": {
        "emotionsToAdd": {
            "joy": 1
        }, 
        "name": "Burst of Pride", 
        "image": "tower_memoryRing", 
        "key": "prideBurst", 
        "memoryKey": "memory_prideBurst", 
        "emotionTags": "work", 
        "resourceKey": "prideBurstMineConstruction", 
        "activeImage": "tower_memoryRing_activeOutline-0099ff"
    }, 
    "willMineButton": {
        "constructionKey": "willMineConstruction", 
        "name": "Drive", 
        "entityCost": {
            "will": 2
        }, 
        "image": "tower_mineWill", 
        "currenciesMade": [
            "will"
        ], 
        "entityKey": "willMine", 
        "limitingKeys": [
            "willMine", 
            "willMineConstruction"
        ], 
        "key": "willMineButton", 
        "description": "makes will"
    }, 
    "alienation_ext": {
        "personHeld": "lover", 
        "image": [
            "trait-cc33ff", 
            "trait-B72DE5", 
            "trait-E038FF"
        ], 
        "name": "Alienation", 
        "key": "alienation_ext", 
        "sourceKey": "alienation"
    }, 
    "refineInspirationButton": {
        "constructionKey": "refineInspirationGoalConstruction", 
        "name": "Refine Raw Inspiration", 
        "image": "goal_towerThinkAbout-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "refineInspirationGoal", 
        "key": "refineInspirationButton", 
        "description": "Requires raw inspiration, thought."
    }, 
    "quitWorkMenu": {
        "image": "goal_heart-000000", 
        "name": "COMPLETED - Quit Worthless Job", 
        "key": "quitWorkMenu"
    }, 
    "rechargeTower": {
        "image": "tower_energy", 
        "activeImage": "tower_energy_activeOutline-0099ff", 
        "name": "Recharge", 
        "key": "rechargeTower", 
        "description": "Converts cheer -> will"
    }, 
    "failureMineConstruction": {
        "key": "failureMineConstruction", 
        "constructionIdentifier": "failureMine", 
        "name": "Constructing - Failures", 
        "image": "trait-aaaaaa99_tower_pain", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Failures", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "pushYourselfMineConstruction": {
        "key": "pushYourselfMineConstruction", 
        "constructionIdentifier": "pushYourselfMine", 
        "name": "Constructing - Push Yourself", 
        "image": "trait-aaaaaa99_tower_mineWill", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Push Yourself", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "deriveInsightGoal": {
        "key": "deriveInsightGoal", 
        "randomPosition": 0, 
        "name": "Derive Insight", 
        "image": "goal_towerThinkAbout-000000", 
        "currencyRequested": {
            "thought": 5
        }, 
        "entitiesToMake": "insightDeposit", 
        "goalMenuKey": "deriveInsightMenu"
    }, 
    "goodSong": {
        "personHeld": "song", 
        "isaacEntities": [
            "releaseButton", 
            "deriveCoolThingButton", 
            "achievementButton"
        ], 
        "image": "trait-cc33cc", 
        "name": "Great Song!", 
        "key": "goodSong"
    }, 
    "techProblem": {
        "name": "technical issue", 
        "key": "techProblem", 
        "tags": [
            "techProblem"
        ]
    }, 
    "loverSadMine": {
        "resourceCount": 1, 
        "name": "Lover Sadness", 
        "image": "tower_social", 
        "key": "loverSadMine", 
        "emotionTags": [
            "lover", 
            "sadness"
        ], 
        "resourceKeys": [
            "sadness"
        ], 
        "activeImage": "tower_social_activeOutline-0099ff", 
        "description": "It hurts when the lover hurts - makes sadness"
    }, 
    "socialMineConstruction": {
        "key": "socialMineConstruction", 
        "constructionIdentifier": "socialMine", 
        "name": "Constructing - Spend Time With Lover", 
        "image": "trait-aaaaaa99_tower_mineSocial", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Spend Time With Lover", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "steamCynicism": {
        "steamAchievementKey": "cynicism", 
        "keyToCheck": "cynicismTowerConstruction", 
        "key": "steamCynicism"
    }, 
    "stuckProblemMineConstruction": {
        "key": "stuckProblemMineConstruction", 
        "constructionIdentifier": "stuckProblemMine", 
        "name": "Stuck With Problem", 
        "image": "tower_puzzle-666666aa", 
        "currencyRequested": {
            "impulse": 1
        }, 
        "constructionName": "Stuck With Problem"
    }, 
    "ambitionToDaydreaming": {
        "sourceTrait": "ambition", 
        "newTrait": "daydreaming", 
        "key": "ambitionToDaydreaming"
    }, 
    "museInspirationGoalConstruction": {
        "key": "museInspirationGoalConstruction", 
        "constructionIdentifier": "museInspirationGoal", 
        "name": "Constructing - Be Inspired!", 
        "image": "trait-aaaaaa99_goal_towerThinkAbout-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Be Inspired!", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "alonenessToAmbition": {
        "sourceTrait": "aloneness", 
        "newTrait": "ambition", 
        "key": "alonenessToAmbition"
    }, 
    "dismissSuccessMenu": {
        "image": "goal_eyeTear-000000", 
        "name": "COMPLETED - Dismiss your Success", 
        "key": "dismissSuccessMenu"
    }, 
    "rechargeButton": {
        "constructionKey": "rechargeTowerConstruction", 
        "name": "Recharge", 
        "image": "tower_energy", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "rechargeTower", 
        "limitingKeys": [
            "rechargeTower", 
            "rechargeTowerConstruction"
        ], 
        "key": "rechargeButton", 
        "description": "Converts cheer -> will"
    }, 
    "releaseGoal": {
        "currencyEntities": {
            "drainedSad": 25
        }, 
        "randomPosition": 0, 
        "name": "Release Song", 
        "image": "goal_cd-000000", 
        "key": "releaseGoal", 
        "currencyRequested": {
            "resolve": 6
        }, 
        "entitiesToMake": [
            "showMusic", 
            "releasedSong", 
            "successDeposit", 
            "killSong"
        ], 
        "goalMenuKey": "releaseMenu", 
        "description": "It's hard to let go."
    }, 
    "swallowBitternessGoal": {
        "key": "swallowBitternessGoal", 
        "randomPosition": 0, 
        "name": "Swallow Bitterness", 
        "image": "goal_daydream-000000", 
        "currencyRequested": {
            "will": 10
        }, 
        "entitiesToMake": "bitternessToAmbition", 
        "goalMenuKey": "swallowBitternessMenu"
    }, 
    "resolveButton": {
        "constructionKey": "resolveTowerConstruction", 
        "name": "Gain Resolve", 
        "image": "tower_drive", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "resolveTower", 
        "limitingKeys": [
            "resolveTower", 
            "resolveTowerConstruction"
        ], 
        "key": "resolveButton", 
        "subPanel": "Towers", 
        "description": [
            "You need to work yourself up for some things.", 
            "Converts will + self-esteem -> resolve"
        ]
    }, 
    "loverFleshTower": {
        "image": "tower_mineSocial", 
        "activeImage": "tower_mineSocial_activeOutline-0099ff", 
        "name": "Flesh Out With Lover", 
        "key": "loverFleshTower", 
        "description": "Converts need -> satisfaction"
    }, 
    "showMusic": {
        "name": "Show Music", 
        "image": "trait-ffcc00", 
        "personHeld": "lover", 
        "isaacEntities": [
            "showMusicButton", 
            "showMusicDisable"
        ], 
        "health": 100, 
        "key": "showMusic"
    }, 
    "fadeHangingOut": {
        "timeToDeath": 180, 
        "key": "fadeHangingOut", 
        "makeOnTimeOut": "keepGoingGoal"
    }, 
    "beCaredGoalConstruction": {
        "key": "beCaredGoalConstruction", 
        "constructionIdentifier": "beCaredGoal", 
        "name": "Constructing - Be Cared For", 
        "image": "trait-aaaaaa99_goal_heart-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Be Cared For", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "flashback_sadness": {
        "resourceCount": 1, 
        "name": "Flashback", 
        "image": "tower_flashback-000000", 
        "key": "flashback_sadness", 
        "emotionTags": "sadness", 
        "resourceKeys": [
            "sadness"
        ], 
        "activeImage": "tower_flashback-000000_activeOutline-0099ff", 
        "description": "makes sadness"
    }, 
    "will": {
        "name": "will", 
        "key": "will", 
        "tags": [
            "will"
        ]
    }, 
    "endHangOutMenu": {
        "image": "goal_goalPark-000000", 
        "name": "COMPLETED - Call It A Day", 
        "key": "endHangOutMenu"
    }, 
    "overcomeExhaustGoal": {
        "key": "overcomeExhaustGoal", 
        "randomPosition": 0, 
        "name": "Overcome Exhaustion", 
        "image": "goal_goalPark-000000", 
        "currencyRequested": {
            "relax": 6
        }, 
        "entitiesToMake": "killExhaustion", 
        "goalMenuKey": "overcomeExhaustMenu"
    }, 
    "cheerPrideButton": {
        "constructionKey": "cheerPrideGoalConstruction", 
        "name": "Feel Good About Self", 
        "image": "trophy", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "cheerPrideGoal", 
        "key": "cheerPrideButton", 
        "description": [
            "Requires cheer.", 
            "Makes self-esteem."
        ]
    }, 
    "strengthTower": {
        "image": "tower_weight", 
        "activeImage": "tower_weight_activeOutline-0099ff", 
        "name": "Gain Strength", 
        "key": "strengthTower", 
        "description": "Converts will -> strength"
    }, 
    "techWorkButton": {
        "constructionKey": "techWorkTowerConstruction", 
        "name": "Complete Tech Work", 
        "image": "tower_thinkProblem", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "techWorkTower", 
        "limitingKeys": [
            "techWorkTower", 
            "techWorkTowerConstruction"
        ], 
        "key": "techWorkButton", 
        "subPanel": "Towers", 
        "description": "Converts technical issue + tech solution -> hard work"
    }, 
    "needTimeMenu": {
        "image": "goal_goalAskAbout-000000", 
        "name": "COMPLETED - Needs Time With You", 
        "key": "needTimeMenu"
    }, 
    "acceptDemonMenu": {
        "image": "goal_trophy-000000", 
        "name": "COMPLETED - Accept Truth", 
        "key": "acceptDemonMenu"
    }, 
    "killTechInspiration": {
        "entitiesToKill": [
            "techInspiration"
        ], 
        "key": "killTechInspiration"
    }, 
    "cheerLoverGoal": {
        "currencyEntities": {
            "condCaptureInspirationGoal": 10
        }, 
        "randomPosition": 0, 
        "name": "Cheer Up Lover", 
        "makeOnTimeOut": "loverSad", 
        "image": "goal_mineCheer-000000", 
        "key": "cheerLoverGoal", 
        "currencyRequested": {
            "cheer": 3, 
            "sayRight_lover": 3
        }, 
        "entitiesToMake": [
            "cheerLoverResult", 
            "loverJoy"
        ], 
        "goalMenuKey": "cheerLoverMenu", 
        "timeToDeath": 300
    }, 
    "steamInspiration": {
        "steamAchievementKey": "inspiration", 
        "keyToCheck": "captureInspirationGoal", 
        "key": "steamInspiration"
    }, 
    "releaseMenu": {
        "image": "goal_cd-000000", 
        "completeText": "This was always the hardest part of any song.", 
        "name": "COMPLETED - Release Song", 
        "key": "releaseMenu"
    }, 
    "rageAction": {
        "currenciesRequired": [
            "frustration"
        ], 
        "towerKey": "rageTower", 
        "name": "Rage", 
        "key": "rageAction"
    }, 
    "willDrainGoal": {
        "minSelfDist": 4
    }, 
    "selfPityMenu": {
        "image": "goal_lifePreserver-000000", 
        "name": "COMPLETED - Don't Drown In Self-Pity", 
        "key": "selfPityMenu"
    }, 
    "resolveStrengthTower": {
        "image": "tower_drive", 
        "activeImage": "tower_drive_activeOutline-0099ff", 
        "name": "Gain Resolve", 
        "key": "resolveStrengthTower", 
        "description": [
            "If it has to just be you, then it will be just you.", 
            "Converts strength -> resolve"
        ]
    }, 
    "rageTower": {
        "name": "Rage", 
        "image": "tower_towerDisavowal", 
        "damageMultipliers": {
            "joy": 1, 
            "sadness": 1
        }, 
        "key": "rageTower", 
        "activeImage": "tower_towerDisavowal_activeOutline-0099ff", 
        "description": "Uses frustration to calm sadness, joy"
    }, 
    "rollSleevesGoalConstruction": {
        "key": "rollSleevesGoalConstruction", 
        "constructionIdentifier": "rollSleevesGoal", 
        "name": "Constructing - Work On Yourself", 
        "image": "trait-aaaaaa99_goal_drive-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Work On Yourself", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "toldWorthMenu": {
        "image": "goal_trophy-000000", 
        "completeText": "It feels sincere when the lover says it.", 
        "name": "COMPLETED - Be Told Of Your Worth", 
        "key": "toldWorthMenu"
    }, 
    "techInspirationTraitConnectSong": {
        "startKey": "techInspiration", 
        "endKey": "song", 
        "key": "techInspirationTraitConnectSong"
    }, 
    "bruteForceAction": {
        "towerKey": "bruteForceTower", 
        "name": "Brute Force Problem", 
        "entitiesOnConvert": {
            "stuckBrute": 25
        }, 
        "currenciesRequired": [
            "resolve", 
            "music"
        ], 
        "currenciesToMake": [
            "thought"
        ], 
        "key": "bruteForceAction"
    }, 
    "friend": {
        "sector": [
            0, 
            0
        ], 
        "name": "Friend", 
        "image": "trait-ff3399", 
        "personHeld": "lover", 
        "isaacEntities": [
            "shareJoyButton", 
            "hangOutButton", 
            "beCaredButton", 
            "talkButton"
        ], 
        "health": 1000, 
        "key": "friend", 
        "startManagedEntities": [
            "friendTraitConnectSelfCore"
        ]
    }, 
    "ignoreInsult1Menu": {
        "image": "goal_goalCalmDown-000000", 
        "completeText": "You can be the bigger person.", 
        "name": "COMPLETED - Ignore Insult", 
        "key": "ignoreInsult1Menu"
    }, 
    "prideStartGoal": {
        "key": "prideStartGoal", 
        "randomPosition": 0, 
        "name": "Be Proud Of This", 
        "image": "goal_trophy-000000", 
        "currencyRequested": {
            "will": 3, 
            "social_lover": 3
        }, 
        "entitiesToMake": [
            "killPrideStartButton", 
            "largePrideDeposit"
        ], 
        "goalMenuKey": "prideStartMenu", 
        "description": "It's a little hard to accept that you did something to be proud of."
    }, 
    "talkAboutSelfMenu": {
        "image": "goal_goalConvinceWife-000000-000000", 
        "name": "COMPLETED - Talk About Yourself", 
        "key": "talkAboutSelfMenu"
    }, 
    "daydreamingToSelfLoathing": {
        "sourceTrait": "daydreaming", 
        "newTrait": "selfLoathing", 
        "key": "daydreamingToSelfLoathing"
    }, 
    "endAlienationStrengthGoalConstruction": {
        "key": "endAlienationStrengthGoalConstruction", 
        "constructionIdentifier": "endAlienationStrengthGoal", 
        "name": "Constructing - End Alienation", 
        "image": "trait-aaaaaa99_goal_weight-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "End Alienation", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "worldGoal": {
        "minSelfDist": 4
    }, 
    "applyButton": {
        "constructionKey": "applyTowerConstruction", 
        "name": "Fuel Yourself", 
        "image": "tower_motivation", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "applyTower", 
        "limitingKeys": [
            "applyTower", 
            "applyTowerConstruction"
        ], 
        "key": "applyButton", 
        "description": "Converts frustration -> will"
    }, 
    "convinceSelfGoal": {
        "key": "convinceSelfGoal", 
        "randomPosition": 0, 
        "currencyRequested": {
            "selfEsteem": 3
        }, 
        "image": "goal_trophy-000000", 
        "killMemory": 0, 
        "sourceTrait": 0, 
        "flareMemory": 0, 
        "description": "Just because it's about you, doesn't mean that it's trite.", 
        "entitiesToMake": "emoInspiration", 
        "goalMenuKey": "convinceSelfMenu", 
        "name": "Convince Self Of Value"
    }, 
    "joyFleshTower": {
        "image": "tower_mineCheer", 
        "activeImage": "tower_mineCheer_activeOutline-0099ff", 
        "name": "Flesh Out With Joy", 
        "key": "joyFleshTower", 
        "description": "Converts cheer -> satisfaction"
    }, 
    "revertFreshMusic": {
        "entitiesToMake": [
            "freshMusicToMusicLove", 
            "coolThingDeposit", 
            "explainRevert"
        ], 
        "key": "revertFreshMusic"
    }, 
    "explainSadMenu": {
        "completeText": [
            "You just cannot believe in yourself right now.", 
            "-----", 
            "Failing the Don't Drown In Self-Pity goal has killed all your self-esteem."
        ], 
        "name": "Drowned In Self Pity", 
        "key": "explainSadMenu"
    }, 
    "fleshSongMenu": {
        "image": "goal_coolNote-000000", 
        "name": "COMPLETED - Flesh Out Song", 
        "key": "fleshSongMenu"
    }, 
    "captureWillMenu": {
        "image": "goal_butterfly-000000", 
        "name": "COMPLETED - Capture Inspiration", 
        "key": "captureWillMenu"
    }, 
    "impressMenu": {
        "image": "goal_trophy-000000", 
        "completeText": "You live for these moments. This pays for so much of the hurt.", 
        "name": "COMPLETED - Impress Lover", 
        "key": "impressMenu"
    }, 
    "deriveInsightGoalConstruction": {
        "key": "deriveInsightGoalConstruction", 
        "constructionIdentifier": "deriveInsightGoal", 
        "name": "Constructing - Derive Insight", 
        "image": "trait-aaaaaa99_goal_towerThinkAbout-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Derive Insight", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "museVentButton": {
        "constructionKey": "museVentTowerConstruction", 
        "name": "Vent To", 
        "image": "tower_mineFrustration", 
        "entityCost": {
            "social_muse": 5
        }, 
        "entityKey": "museVentTower", 
        "limitingKeys": [
            "museVentTower", 
            "museVentTowerConstruction"
        ], 
        "key": "museVentButton", 
        "subPanel": "Defenses", 
        "description": "Uses social to calm anger, sadness"
    }, 
    "begLoverMenu": {
        "image": "goal_towerConfess-000000", 
        "name": "COMPLETED - Beg Lover To Stay", 
        "key": "begLoverMenu"
    }, 
    "explainFindMusicFail": {
        "completeText": [
            "You may not have found anything new, but you can't complain about", 
            "spending time with the lover like this."
        ], 
        "key": "explainFindMusicFail"
    }, 
    "fishComplimentsGoalConstruction": {
        "key": "fishComplimentsGoalConstruction", 
        "constructionIdentifier": "fishComplimentsGoal", 
        "name": "Constructing - Fish For Compliments", 
        "image": "trait-aaaaaa99_goal_hook-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Fish For Compliments", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "talkAboutLoverGoal": {
        "currencyEntities": {
            "condCaptureInspirationGoal": 10
        }, 
        "randomPosition": 0, 
        "name": "Talk About Lover", 
        "image": "goal_goalChat-000000-000000", 
        "key": "talkAboutLoverGoal", 
        "currencyRequested": {
            "social_lover": 3, 
            "cheer": 3
        }, 
        "entitiesToMake": "cheerLoverGoal", 
        "goalMenuKey": "talkAboutLoverMenu"
    }, 
    "understandTowerConstruction": {
        "key": "understandTowerConstruction", 
        "constructionIdentifier": "understandTower", 
        "name": "Constructing - Understand", 
        "image": "trait-aaaaaa99_tower_understand", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Understand", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "quitWorkButton": {
        "constructionKey": "quitWorkGoalConstruction", 
        "name": "Quit Worthless Job", 
        "image": "goal_heart-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "quitWorkGoal", 
        "key": "quitWorkButton", 
        "description": "Requires will."
    }, 
    "cheerUpGoal": {
        "key": "cheerUpGoal", 
        "randomPosition": 1, 
        "name": "Cheer Up", 
        "makeOnTimeOut": [
            "shameBurst", 
            "hurtRelationship"
        ], 
        "image": "trait-ff0066_goal_mineCheer-000000", 
        "description": "Everyday is hard and it helps when you're here to help cheer back up.", 
        "currencyRequested": {
            "cheer": 3
        }, 
        "entitiesToMake": "helpRelationship", 
        "goalMenuKey": "cheerUpMenu", 
        "timeToDeath": 150
    }, 
    "bitterness": {
        "name": "Bitterness", 
        "image": "trait-33cc00", 
        "personHeld": "selfCore", 
        "isaacEntities": [
            "cynicismButton", 
            "swallowBitternessButton", 
            "failureButton"
        ], 
        "key": "bitterness", 
        "maxLevel": 12
    }, 
    "workEmotionDeposit": {
        "timeToDeath": 150, 
        "image": "tower_towerThinkAbout", 
        "deposit": {
            "workEmotion": 3
        }, 
        "key": "workEmotionDeposit", 
        "activeImage": "tower_towerThinkAbout_activeOutline-0099ff", 
        "name": "Work Though Emotion"
    }, 
    "endMuseMenu": {
        "image": "goal_towerConfess-000000", 
        "name": "COMPLETED - Cut Off From The Muse", 
        "key": "endMuseMenu"
    }, 
    "project": {
        "playerKillable": "true"
    }, 
    "hangingOutToAlienation": {
        "sourceTrait": "hangingOut", 
        "newTrait": "alienation", 
        "key": "hangingOutToAlienation"
    }, 
    "explainMuseFail": {
        "completeText": [
            "Inspiration didn't strike this time, but time with the muse never", 
            "feels wasted."
        ], 
        "key": "explainMuseFail"
    }, 
    "alienationMine": {
        "name": "Alienation", 
        "image": "tower_pain", 
        "key": "alienationMine", 
        "resourceKeys": [
            "alienation_lover"
        ], 
        "activeImage": "tower_pain_activeOutline-0099ff", 
        "description": "You really are the only person to understand you."
    }, 
    "daydreamMenu": {
        "image": "goal_memoryDefineSelf-000000-000000", 
        "name": "COMPLETED - Daydream", 
        "key": "daydreamMenu"
    }, 
    "struggleCompliment": {
        "goodEntities": "selfEsteemDeposit", 
        "badEntities": "selfWorthless", 
        "key": "struggleCompliment"
    }, 
    "rechargeTowerConstruction": {
        "key": "rechargeTowerConstruction", 
        "constructionIdentifier": "rechargeTower", 
        "name": "Constructing - Recharge", 
        "image": "trait-aaaaaa99_tower_energy", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Recharge", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "emoWorkAction": {
        "currenciesRequired": [
            "emoSolution", 
            "emoProblem"
        ], 
        "towerKey": "emoWorkTower", 
        "currenciesToMake": [
            "hardWork"
        ], 
        "name": "Add Emotion", 
        "key": "emoWorkAction"
    }, 
    "gainControl_sadness": {
        "barColor": "9999cc", 
        "text": "You need some time to push through this sadness", 
        "textColor": 66, 
        "key": "gainControl_sadness"
    }, 
    "solutionButton": {
        "constructionKey": "solutionTowerConstruction", 
        "name": "Solve Problem", 
        "image": "tower_techPuzzle", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "solutionTower", 
        "limitingKeys": [
            "solutionTower", 
            "solutionTowerConstruction"
        ], 
        "key": "solutionButton", 
        "subPanel": "Towers", 
        "description": "Converts insight + will -> tech solution"
    }, 
    "selfLoathingMineConstruction": {
        "key": "selfLoathingMineConstruction", 
        "constructionIdentifier": "selfLoathingMine", 
        "name": "Constructing - Self-Loathing", 
        "image": "trait-aaaaaa99_tower_eyeTear-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Self-Loathing", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "hurtLover": {
        "emotionsToAdd": {
            "sadness": 2
        }, 
        "name": "Hurt Lover", 
        "image": "tower_social", 
        "key": "hurtLover", 
        "memoryKey": "memory_hurtLover", 
        "emotionTags": "lover", 
        "resourceKey": "hurtLoverMineConstruction", 
        "activeImage": "tower_social_activeOutline-0099ff", 
        "description": "You had no call to say that to the lover"
    }, 
    "flagQuitPrideGoal": {
        "flagToCheck": "quitPride", 
        "entitiesToMake": "quitPrideGoal", 
        "key": "flagQuitPrideGoal"
    }, 
    "drown_muse": {
        "name": "drowning", 
        "key": "drown_muse", 
        "tags": [
            "drown_muse"
        ]
    }, 
    "showMusicGoal": {
        "key": "showMusicGoal", 
        "randomPosition": 0, 
        "name": "Show Lover The Song", 
        "image": "goal_coolNote-000000", 
        "currencyRequested": {
            "releasedMusic": 3
        }, 
        "entitiesToMake": [
            "killShowMusic", 
            "unimpressed", 
            "romanceToLoving"
        ], 
        "goalMenuKey": "showMusicMenu"
    }, 
    "loseImpressedGoal": {
        "key": "loseImpressedGoal", 
        "randomPosition": 1, 
        "name": "Be Letdown", 
        "image": "trait-ff0066_goal_droop-000000", 
        "personHeld": "lover", 
        "currencyRequested": {
            "letdown": 3
        }, 
        "goalMenuKey": "loseImpressedMenu"
    }, 
    "captureMusicMenu": {
        "image": "goal_butterfly-000000", 
        "name": "COMPLETED - Capture Inspiration", 
        "key": "captureMusicMenu"
    }, 
    "startNewSongGoalPathNascentGoal": {
        "startKey": "startNewSongGoal", 
        "endKey": "nascentGoal", 
        "key": "startNewSongGoalPathNascentGoal"
    }, 
    "completeTechMineConstruction": {
        "key": "completeTechMineConstruction", 
        "constructionIdentifier": "completeTechMine", 
        "name": "Constructing - Completion Desire", 
        "image": "trait-aaaaaa99_tower_mineWill", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Completion Desire", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "anger": {
        "emotion": "anger", 
        "name": "anger", 
        "image": "creep-ff0000", 
        "damage": 0, 
        "memoryImage": "memoryAnger", 
        "health": 10, 
        "key": "anger"
    }, 
    "realTalk_ext": {
        "personHeld": "lover", 
        "image": [
            "trait-ffaa33", 
            "trait-E5992D", 
            "trait-FFBB38"
        ], 
        "name": "Real Talk", 
        "key": "realTalk_ext", 
        "sourceKey": "realTalk"
    }, 
    "newParticle": {
        "effectRadius": 25, 
        "image": "dot-ffd700", 
        "key": "newParticle"
    }, 
    "winMenu": {
        "endGame": 1, 
        "completeText": "It's not major success, but it's enough. You're a musician now!", 
        "key": "winMenu"
    }, 
    "overcomeDistanceMenu": {
        "image": "goal_towerConfess-000000", 
        "name": "COMPLETED - Overcome Distance", 
        "key": "overcomeDistanceMenu"
    }, 
    "thought": {
        "name": "thought", 
        "key": "thought", 
        "tags": [
            "thought"
        ]
    }, 
    "strength": {
        "name": "strength", 
        "key": "strength", 
        "tags": [
            "strength"
        ]
    }, 
    "museVentTowerConstruction": {
        "key": "museVentTowerConstruction", 
        "constructionIdentifier": "museVentTower", 
        "name": "Constructing - Vent To", 
        "image": "trait-aaaaaa99_tower_mineFrustration", 
        "currencyRequested": {
            "social_muse": 5
        }, 
        "constructionName": "Vent To", 
        "killOnTransform": 0
    }, 
    "talkAction": {
        "currenciesRequired": [
            "care_lover"
        ], 
        "towerKey": "talkTower", 
        "name": "Talk", 
        "key": "talkAction"
    }, 
    "playWithMusicButton": {
        "constructionKey": "playWithMusicTowerConstruction", 
        "name": "Play With Music", 
        "image": "tower_dice", 
        "entityCost": {
            "plaything": 5
        }, 
        "entityKey": "playWithMusicTower", 
        "limitingKeys": [
            "playWithMusicTower", 
            "playWithMusicTowerConstruction"
        ], 
        "key": "playWithMusicButton", 
        "subPanel": "Defenses", 
        "description": "Uses plaything to calm anger, sadness, joy"
    }, 
    "songSatisfactionDeposit": {
        "image": "tower_note", 
        "activeImage": "tower_note_activeOutline-0099ff", 
        "name": "Song Satisfaction", 
        "key": "songSatisfactionDeposit", 
        "deposit": {
            "songSatisfaction": 3
        }
    }, 
    "self": {
        "name": "Self", 
        "image": "trait-eeeeee_self", 
        "health": 200, 
        "key": "self", 
        "focusCount": 5, 
        "y": 0, 
        "x": 0
    }, 
    "swallowBitternessGoalConstruction": {
        "key": "swallowBitternessGoalConstruction", 
        "constructionIdentifier": "swallowBitternessGoal", 
        "name": "Constructing - Swallow Bitterness", 
        "image": "trait-aaaaaa99_goal_daydream-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Swallow Bitterness", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "killUnhappy": {
        "entitiesToKill": [
            "unhappy", 
            "begLoverGoal"
        ], 
        "key": "killUnhappy"
    }, 
    "resolveStrengthTowerConstruction": {
        "key": "resolveStrengthTowerConstruction", 
        "constructionIdentifier": "resolveStrengthTower", 
        "name": "Constructing - Gain Resolve", 
        "image": "trait-aaaaaa99_tower_drive", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Gain Resolve", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "optimismTowerConstruction": {
        "key": "optimismTowerConstruction", 
        "constructionIdentifier": "optimismTower", 
        "name": "Constructing - Optimism", 
        "image": "trait-aaaaaa99_tower_sun", 
        "currencyRequested": {
            "cheer": 5
        }, 
        "constructionName": "Optimism", 
        "killOnTransform": 0
    }, 
    "releaseSatisfyMenu": {
        "image": "goal_coolNote-000000", 
        "name": "COMPLETED - Satisfaction at Release", 
        "key": "releaseSatisfyMenu"
    }, 
    "bruteForceButton": {
        "constructionKey": "bruteForceTowerConstruction", 
        "name": "Brute Force Problem", 
        "image": "tower_energy", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "bruteForceTower", 
        "limitingKeys": [
            "bruteForceTower", 
            "bruteForceTowerConstruction"
        ], 
        "key": "bruteForceButton", 
        "subPanel": "Towers", 
        "description": "Converts resolve + music -> thought"
    }, 
    "sinkWorkAction": {
        "currenciesRequired": [
            "thought"
        ], 
        "towerKey": "sinkWorkTower", 
        "name": "Sink Into Work", 
        "key": "sinkWorkAction"
    }, 
    "fireUpButton": {
        "constructionKey": "fireUpGoalConstruction", 
        "name": "Fire Yourself Up", 
        "image": "weight", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "fireUpGoal", 
        "key": "fireUpButton", 
        "description": [
            "Requires frustration.", 
            "Makes strength."
        ]
    }, 
    "pain": {
        "name": "pain", 
        "key": "pain", 
        "tags": [
            "pain"
        ]
    }, 
    "irritabilityGoal": {
        "key": "irritabilityGoal", 
        "randomPosition": 1, 
        "name": "Scratch Music Itch", 
        "makeOnTimeOut": "selfHate", 
        "image": "goal_note-000000", 
        "sourceTrait": "musicLove", 
        "currencyRequested": {
            "music": 3
        }, 
        "goalMenuKey": "irritabilityMenu", 
        "timeToDeath": 120
    }, 
    "explainRevert": {
        "completeText": "This was interesting, but not interesting enough to build off", 
        "key": "explainRevert"
    }, 
    "quietDemonsLoveGoalConstruction": {
        "key": "quietDemonsLoveGoalConstruction", 
        "constructionIdentifier": "quietDemonsLoveGoal", 
        "name": "Constructing - Quiet Demons", 
        "image": "trait-aaaaaa99_goal_heart-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Quiet Demons", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "captureThoughtMenu": {
        "image": "goal_butterfly-000000", 
        "name": "COMPLETED - Capture Inspiration", 
        "key": "captureThoughtMenu"
    }, 
    "releaseSatisfyGoal": {
        "key": "releaseSatisfyGoal", 
        "randomPosition": 0, 
        "name": "Satisfaction at Release", 
        "image": "goal_coolNote-000000", 
        "currencyRequested": {
            "will": 3, 
            "cheer": 3
        }, 
        "entitiesToMake": [
            "accomplishmentDeposit"
        ], 
        "goalMenuKey": "releaseSatisfyMenu"
    }, 
    "discardFreshMusicButton": {
        "constructionKey": "discardFreshMusicGoalConstruction", 
        "name": "Discard Fresh Music", 
        "image": "goal_discard-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "discardFreshMusicGoal", 
        "key": "discardFreshMusicButton", 
        "description": "Requires will."
    }, 
    "songSatisfaction": {
        "name": "satisfaction", 
        "key": "songSatisfaction", 
        "tags": [
            "songSatisfaction"
        ]
    }, 
    "tellQuitMenu": {
        "image": "goal_trophy-000000", 
        "completeText": "This is exactly what the lover always wanted to hear.", 
        "name": "COMPLETED - Speak of New Start", 
        "key": "tellQuitMenu"
    }, 
    "drownTowerConstruction": {
        "key": "drownTowerConstruction", 
        "constructionIdentifier": "drownTower", 
        "name": "Constructing - Drowning In Muse", 
        "image": "trait-aaaaaa99_tower_wave", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Drowning In Muse", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "achievement": {
        "name": "Achievement", 
        "image": "trait-aaff00", 
        "personHeld": "selfCore", 
        "isaacEntities": [
            "fadeAchievement", 
            "dismissSuccessButton", 
            "prideBurst", 
            "dreamOfGreatnessButton", 
            "refineAccomplishmentButton"
        ], 
        "key": "achievement", 
        "maxLevel": 12, 
        "description": "You should be proud of what you've done"
    }, 
    "frustrationMineConstruction": {
        "key": "frustrationMineConstruction", 
        "constructionIdentifier": "frustrationMine", 
        "name": "Constructing - Frustration Mine", 
        "image": "trait-aaaaaa99_tower_mineFrustration", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Frustration Mine", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "endAlienationStrengthGoal": {
        "key": "endAlienationStrengthGoal", 
        "randomPosition": 0, 
        "name": "End Alienation", 
        "image": "goal_weight-000000", 
        "currencyRequested": {
            "strength": 5
        }, 
        "entitiesToMake": [
            "alienationToFriend", 
            "killLoveDeposit"
        ], 
        "goalMenuKey": "endAlienationStrengthMenu"
    }, 
    "thinkSongBadGoal": {
        "currencyEntities": "drainedSad", 
        "randomPosition": 0, 
        "name": "Face Song Failings", 
        "image": "goal_eyeTear-000000", 
        "key": "thinkSongBadGoal", 
        "currencyRequested": {
            "resolve": 3
        }, 
        "entitiesToMake": "understandFailingsGoal", 
        "goalMenuKey": "thinkSongBadMenu", 
        "description": "It takes effort to see the failings of your baby."
    }, 
    "memory_stuckProblem": {
        "image": "trait-aa1717", 
        "memoryEmotions": [
            "anger"
        ], 
        "key": "memory_stuckProblem", 
        "name": "Stuck With Problem"
    }, 
    "alienationDeposit": {
        "name": "Alienation", 
        "image": "tower_social", 
        "sourceTrait": "loverBase", 
        "deposit": {
            "alienation_lover": 3
        }, 
        "key": "alienationDeposit", 
        "activeImage": "tower_social_activeOutline-0099ff"
    }, 
    "fishComplimentsButton": {
        "constructionKey": "fishComplimentsGoalConstruction", 
        "name": "Fish For Compliments", 
        "image": "goal_hook-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "fishComplimentsGoal", 
        "key": "fishComplimentsButton", 
        "description": [
            "Requires social, music.", 
            "Makes self-esteem."
        ]
    }, 
    "sustainAchievementGoal": {
        "key": "sustainAchievementGoal", 
        "randomPosition": 1, 
        "name": "Sustain Achievement", 
        "makeOnTimeOut": "achievementToAmbition", 
        "image": "goal_mineCaution-000000", 
        "sourceTrait": "achievement", 
        "currencyRequested": {
            "selfEsteem": 3
        }, 
        "entitiesToMake": "fadeAchievement", 
        "goalMenuKey": "sustainAchievementMenu", 
        "timeToDeath": 120
    }, 
    "listenDemonsGoal": {
        "key": "listenDemonsGoal", 
        "randomPosition": 0, 
        "name": "Listen To Demons", 
        "image": "goal_memoryProgramming-000000", 
        "currencyRequested": {
            "will": 1
        }, 
        "entitiesToMake": [
            "quietDemonsToDemons", 
            "selfWorthless"
        ], 
        "goalMenuKey": "listenDemonsMenu"
    }, 
    "killLoveDeposit": {
        "entitiesToKill": [
            "loveDeposit"
        ], 
        "key": "killLoveDeposit"
    }, 
    "shareCoolGoal": {
        "key": "shareCoolGoal", 
        "randomPosition": 0, 
        "name": "Share Something Cool", 
        "image": "goal_coolNote-000000", 
        "currencyRequested": {
            "coolThing": 3
        }, 
        "entitiesToMake": [
            "loverJoy", 
            "largePrideDeposit"
        ], 
        "goalMenuKey": "shareCoolMenu"
    }, 
    "emoWorkTower": {
        "image": "tower_heart", 
        "activeImage": "tower_heart_activeOutline-0099ff", 
        "name": "Complete Add Emotion", 
        "key": "emoWorkTower", 
        "description": "Converts emotional solution + emotional issue -> hard work"
    }, 
    "strengthTowerConstruction": {
        "key": "strengthTowerConstruction", 
        "constructionIdentifier": "strengthTower", 
        "name": "Constructing - Gain Strength", 
        "image": "trait-aaaaaa99_tower_weight", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Gain Strength", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "indulgeCuriosityGoalConstruction": {
        "key": "indulgeCuriosityGoalConstruction", 
        "constructionIdentifier": "indulgeCuriosityGoal", 
        "name": "Constructing - Indulge Curiosity", 
        "image": "trait-aaaaaa99_goal_puzzle-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Indulge Curiosity", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "cheerPrideGoal": {
        "key": "cheerPrideGoal", 
        "randomPosition": 0, 
        "currencyRequested": {
            "cheer": 6
        }, 
        "image": "goal_trophy-000000", 
        "flareMemory": 1, 
        "entitiesToMake": "selfEsteemDeposit", 
        "goalMenuKey": "cheerPrideMenu", 
        "name": "Feel Good About Self"
    }, 
    "settingButton": {
        "constructionKey": "settingTowerConstruction", 
        "name": "Add Life", 
        "image": "tower_towerPaint", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "settingTower", 
        "limitingKeys": [
            "settingTower", 
            "settingTowerConstruction"
        ], 
        "key": "settingButton", 
        "subPanel": "Towers", 
        "description": "Converts understanding + music -> emotional solution"
    }, 
    "workThroughMenu": {
        "image": "goal_towerThinkAbout-000000", 
        "name": "COMPLETED - Work Through Emotion", 
        "key": "workThroughMenu"
    }, 
    "applyAction": {
        "currenciesRequired": [
            "frustration"
        ], 
        "towerKey": "applyTower", 
        "currenciesToMake": [
            "will"
        ], 
        "name": "Apply", 
        "key": "applyAction"
    }, 
    "indulgeHappyGoalConstruction": {
        "key": "indulgeHappyGoalConstruction", 
        "constructionIdentifier": "indulgeHappyGoal", 
        "name": "Constructing - Indulge In Satisfaction", 
        "image": "trait-aaaaaa99_goal_mineCheer-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Indulge In Satisfaction", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "workThroughGoal": {
        "key": "workThroughGoal", 
        "randomPosition": 0, 
        "currencyRequested": {
            "workEmotion": 3
        }, 
        "image": "goal_towerThinkAbout-000000", 
        "killMemory": 1, 
        "flareMemory": 0, 
        "goalMenuKey": "workThroughMenu", 
        "name": "Work Through Emotion"
    }, 
    "showMusicMenu": {
        "image": "goal_coolNote-000000", 
        "completeText": "Nothing the lover said was critical, so why doesn't this feel right?", 
        "name": "COMPLETED - Show Lover The Song", 
        "key": "showMusicMenu"
    }, 
    "discardFreshMusicGoal": {
        "key": "discardFreshMusicGoal", 
        "randomPosition": 0, 
        "name": "Discard Fresh Music", 
        "image": "goal_discard-000000", 
        "currencyRequested": {
            "will": 3
        }, 
        "entitiesToMake": "freshMusicToMusicLove", 
        "goalMenuKey": "discardFreshMusicMenu", 
        "description": [
            "The music is still interesting, but you need to obsess about", 
            "something else."
        ]
    }, 
    "dismissSuccessGoal": {
        "key": "dismissSuccessGoal", 
        "randomPosition": 0, 
        "name": "Dismiss your Success", 
        "image": "goal_eyeTear-000000", 
        "currencyRequested": {
            "will": 1
        }, 
        "entitiesToMake": "achievementToAmbition", 
        "goalMenuKey": "dismissSuccessMenu"
    }, 
    "completeTechMine": {
        "image": "tower_mineWill", 
        "resourceKeys": [
            "will"
        ], 
        "name": "Completion Desire", 
        "key": "completeTechMine", 
        "activeImage": "tower_mineWill_activeOutline-0099ff"
    }, 
    "coolThingDeposit": {
        "image": "tower_coolNote", 
        "activeImage": "tower_coolNote_activeOutline-0099ff", 
        "name": "Cool Thing", 
        "key": "coolThingDeposit", 
        "deposit": {
            "coolThing": 3
        }
    }, 
    "exhaustion_ext": {
        "personHeld": "music", 
        "image": [
            "trait-6600ff", 
            "trait-5B00E5", 
            "trait-7000FF"
        ], 
        "name": "Exhaustion", 
        "key": "exhaustion_ext", 
        "sourceKey": "exhaustion"
    }, 
    "stuckProblemMine": {
        "resourceCount": 1, 
        "name": "Stuck With Problem", 
        "image": "tower_puzzle", 
        "key": "stuckProblemMine", 
        "emotionTags": [
            "self", 
            "anger"
        ], 
        "resourceKeys": [
            "anger"
        ], 
        "activeImage": "tower_puzzle_activeOutline-0099ff", 
        "description": "Will you never solve this problem? - makes anger"
    }, 
    "daydreaming_ext": {
        "personHeld": "selfCore", 
        "image": [
            "trait-99ff66", 
            "trait-89E55B", 
            "trait-A8FF70"
        ], 
        "name": "Daydreaming", 
        "key": "daydreaming_ext", 
        "sourceKey": "daydreaming"
    }, 
    "freshMusic_ext": {
        "personHeld": "music", 
        "image": [
            "trait-00aacc", 
            "trait-0099B7", 
            "trait-00BBE0"
        ], 
        "name": "New Music", 
        "key": "freshMusic_ext", 
        "sourceKey": "freshMusic"
    }, 
    "toast": {
        "timeToDeath": 30, 
        "key": "toast"
    }, 
    "failureTower": {
        "image": "tower_mineCaution", 
        "activeImage": "tower_mineCaution_activeOutline-0099ff", 
        "name": "Think Of Failure", 
        "key": "failureTower", 
        "description": "Converts frustration + pain -> personal failure"
    }, 
    "techProblemDeposit": {
        "randomPosition": 1, 
        "name": "Technical Issues", 
        "image": "tower_techPuzzle", 
        "deposit": {
            "techProblem": 3
        }, 
        "key": "techProblemDeposit", 
        "activeImage": "tower_techPuzzle_activeOutline-0099ff"
    }, 
    "selfLoathe": {
        "name": "self-loathing", 
        "key": "selfLoathe", 
        "tags": [
            "selfLoathe"
        ]
    }, 
    "curiousToMusicLove": {
        "sourceTrait": "curious", 
        "newTrait": "musicLove", 
        "key": "curiousToMusicLove"
    }, 
    "reflectMineConstruction": {
        "key": "reflectMineConstruction", 
        "constructionIdentifier": "reflectMine", 
        "name": "Constructing - Reflection", 
        "image": "trait-aaaaaa99_tower_mineThought", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Reflection", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "optimismTower": {
        "name": "Optimism", 
        "image": "tower_sun", 
        "damageMultipliers": {
            "anger": 1, 
            "sadness": 1
        }, 
        "key": "optimismTower", 
        "activeImage": "tower_sun_activeOutline-0099ff", 
        "description": "Uses cheer to calm sadness, anger"
    }, 
    "freshMusicToCurious": {
        "sourceTrait": "freshMusic", 
        "newTrait": "curious", 
        "key": "freshMusicToCurious"
    }, 
    "convinceSelfMenu": {
        "image": "goal_trophy-000000", 
        "completeText": "It took a fight, but this is something you can work with.", 
        "name": "COMPLETED - Convince Self Of Value", 
        "key": "convinceSelfMenu"
    }, 
    "loverSadMineConstruction": {
        "key": "loverSadMineConstruction", 
        "constructionIdentifier": "loverSadMine", 
        "name": "Lover Sadness", 
        "image": "tower_social-666666aa", 
        "currencyRequested": {
            "impulse": 1
        }, 
        "constructionName": "Lover Sadness"
    }, 
    "desireImprovementDeposit": {
        "image": "tower_drive", 
        "activeImage": "tower_drive_activeOutline-0099ff", 
        "name": "Desire Self-Improvement", 
        "key": "desireImprovementDeposit", 
        "deposit": {
            "desireImprovement": 3
        }
    }, 
    "apologizeGoal": {
        "currencyEntities": "shameBurst", 
        "randomPosition": 0, 
        "name": "Apologize", 
        "image": "goal_towerConfess-000000", 
        "key": "apologizeGoal", 
        "currencyRequested": {
            "apology_lover": 3
        }, 
        "entitiesToMake": "apologizeResult", 
        "goalMenuKey": "apologizeMenu", 
        "description": [
            "It hurts to crawl, but you need the lover too much to do anything", 
            "else."
        ]
    }, 
    "selfHateMine": {
        "resourceCount": 1, 
        "name": "Hating Yourself", 
        "image": "tower_memoryDefineSelf-000000", 
        "key": "selfHateMine", 
        "emotionTags": [
            "self", 
            "anger"
        ], 
        "resourceKeys": [
            "anger"
        ], 
        "activeImage": "tower_memoryDefineSelf-000000_activeOutline-0099ff", 
        "description": "makes anger"
    }, 
    "selfLoathingToAmbition": {
        "sourceTrait": "selfLoathing", 
        "newTrait": "ambition", 
        "key": "selfLoathingToAmbition"
    }, 
    "romanceToFriend": {
        "sourceTrait": "romance", 
        "newTrait": "friend", 
        "key": "romanceToFriend"
    }, 
    "findProblem": {
        "count": 3, 
        "minCount": 3, 
        "entitiesToMake": "newProblem", 
        "key": "findProblem"
    }, 
    "callousnessTower": {
        "name": "Callousness", 
        "image": "tower_numb", 
        "damageMultipliers": {
            "anger": 1, 
            "joy": 1, 
            "sadness": 1
        }, 
        "key": "callousnessTower", 
        "activeImage": "tower_numb_activeOutline-0099ff", 
        "description": "Uses strength to calm anger, joy, sadness"
    }, 
    "releaseSatisfyGoalConstruction": {
        "key": "releaseSatisfyGoalConstruction", 
        "constructionIdentifier": "releaseSatisfyGoal", 
        "name": "Constructing - Satisfaction at Release", 
        "image": "trait-aaaaaa99_goal_coolNote-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Satisfaction at Release", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "worldGoalFocusPathStopComponent": {
        "currencyEffect": "starParticleEffect"
    }, 
    "impressGoal": {
        "currencyEntities": "loverJoy", 
        "randomPosition": 0, 
        "name": "Impress Lover", 
        "image": "goal_trophy-000000", 
        "key": "impressGoal", 
        "currencyRequested": {
            "coolThing": 3
        }, 
        "entitiesToMake": "largePrideDeposit", 
        "goalMenuKey": "impressMenu", 
        "timeToDeath": 240
    }, 
    "chapter1": {
        "name": "Chapter 1 - Starting Out", 
        "makeOnCreateEntities": [
            "hateWork", 
            "musicLove", 
            "loverBase", 
            "gameStartMenu"
        ], 
        "chapterCount": 1, 
        "key": "chapter1", 
        "entitiesToMake": [
            "chapter2"
        ], 
        "goalsToCheck": {
            "releaseGoal": 1
        }
    }, 
    "chapter3": {
        "chapterCount": 3, 
        "name": "Chapter 3 - Every Single Day", 
        "key": "chapter3", 
        "goalsToCheck": {
            "nullGoal": 1
        }
    }, 
    "angerFleshTower": {
        "image": "tower_mineFrustration", 
        "activeImage": "tower_mineFrustration_activeOutline-0099ff", 
        "name": "Flesh Out With Anger", 
        "key": "angerFleshTower", 
        "description": "Converts frustration -> satisfaction"
    }, 
    "thoughtAction": {
        "currenciesRequired": [
            "music"
        ], 
        "towerKey": "thoughtTower", 
        "currenciesToMake": [
            "thought"
        ], 
        "name": "Thinking About Music", 
        "key": "thoughtAction"
    }, 
    "deriveHumanInsightMenu": {
        "image": "goal_spark-000000", 
        "completeText": "If this was true for one, it is likely true for more.", 
        "name": "COMPLETED - Derive Human Insight", 
        "key": "deriveHumanInsightMenu"
    }, 
    "loveStrengthTowerConstruction": {
        "key": "loveStrengthTowerConstruction", 
        "constructionIdentifier": "loveStrengthTower", 
        "name": "Constructing - Derive Strength", 
        "image": "trait-aaaaaa99_tower_weight", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Derive Strength", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "ignoreDifferencesMenu": {
        "image": "goal_social-000000", 
        "name": "COMPLETED - Ignore Differences", 
        "key": "ignoreDifferencesMenu"
    }, 
    "sadFleshAction": {
        "currenciesRequired": [
            "pain"
        ], 
        "towerKey": "sadFleshTower", 
        "currenciesToMake": [
            "songSatisfaction"
        ], 
        "name": "Flesh Out With Sadness", 
        "key": "sadFleshAction"
    }, 
    "capturedInspiration": {
        "entitiesToMake": [
            "rawInspirationDeposit", 
            "explainCapturedInspiration"
        ], 
        "key": "capturedInspiration"
    }, 
    "museInspireResult": {
        "goodEntities": {
            "rawInspirationDeposit": 3
        }, 
        "badEntities": "explainMuseFail", 
        "key": "museInspireResult"
    }, 
    "releaseSatisfyButton": {
        "constructionKey": "releaseSatisfyGoalConstruction", 
        "name": "Satisfaction at Release", 
        "image": "goal_coolNote-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "releaseSatisfyGoal", 
        "key": "releaseSatisfyButton", 
        "description": [
            "Requires will, cheer.", 
            "Makes accomplishment."
        ]
    }, 
    "shareJoyMuseTower": {
        "name": "Share Joy", 
        "image": "tower_towerTalk", 
        "damageMultipliers": {
            "joy": 1
        }, 
        "key": "shareJoyMuseTower", 
        "activeImage": "tower_towerTalk_activeOutline-0099ff", 
        "description": "Uses social to calm joy"
    }, 
    "discardGoal": {
        "key": "discardGoal", 
        "randomPosition": 0, 
        "name": "Discard Song", 
        "image": "goal_goalDisavowal-000000", 
        "currencyRequested": {
            "will": 1
        }, 
        "entitiesToMake": "killSong", 
        "goalMenuKey": "discardMenu", 
        "description": "We can do better than this."
    }, 
    "thoughtTowerConstruction": {
        "key": "thoughtTowerConstruction", 
        "constructionIdentifier": "thoughtTower", 
        "name": "Constructing - Thinking About Music", 
        "image": "trait-aaaaaa99_tower_doubleNote", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Thinking About Music", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "talkButton": {
        "constructionKey": "talkTowerConstruction", 
        "name": "Vent To", 
        "image": "tower_mineFrustration", 
        "entityCost": {
            "care_lover": 5
        }, 
        "entityKey": "talkTower", 
        "limitingKeys": [
            "talkTower", 
            "talkTowerConstruction"
        ], 
        "key": "talkButton", 
        "subPanel": "Defenses", 
        "description": "Uses care to calm anger, sadness"
    }, 
    "resolve": {
        "name": "resolve", 
        "key": "resolve", 
        "tags": [
            "resolve"
        ]
    }, 
    "fadingJoyMemory": {
        "image": "trait-cccc99", 
        "name": "Fading Happy Memory", 
        "key": "fadingJoyMemory"
    }, 
    "social_lover": {
        "name": "social", 
        "key": "social_lover", 
        "tags": [
            "social_lover"
        ]
    }, 
    "cheerPrideGoalConstruction": {
        "key": "cheerPrideGoalConstruction", 
        "constructionIdentifier": "cheerPrideGoal", 
        "name": "Constructing - Feel Good About Self", 
        "image": "trait-aaaaaa99_trophy", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Feel Good About Self", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "reflectPastGoal": {
        "currencyEntities": {
            "aloneSad": 25
        }, 
        "randomPosition": 0, 
        "name": "Reflect On Past", 
        "image": "goal_drive-000000", 
        "key": "reflectPastGoal", 
        "currencyRequested": {
            "strength": 3, 
            "alienation_lover": 3
        }, 
        "entitiesToMake": [
            "resolveDeposit", 
            "selfRelianceToAloneness"
        ], 
        "goalMenuKey": "reflectPastMenu", 
        "description": "It would be nice if it didn't come to this quite so often."
    }, 
    "findMistakeButton": {
        "constructionKey": "findMistakeGoalConstruction", 
        "name": "Learn Mistakes", 
        "image": "goal_towerConfess-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "findMistakeGoal", 
        "key": "findMistakeButton", 
        "description": [
            "Requires will, social.", 
            "Makes mistake."
        ]
    }, 
    "fadingAngryMemory": {
        "image": "trait-cc9999", 
        "name": "Fading Angry Memory", 
        "key": "fadingAngryMemory"
    }, 
    "sadRideMenu": {
        "image": "goal_goalFightProgramming-000000-000000", 
        "completeText": "Somewhere in all of this sadness is the answer you need.", 
        "name": "COMPLETED - Explore Sadness", 
        "key": "sadRideMenu"
    }, 
    "lovingCareAction": {
        "currenciesRequired": [
            "love_lover", 
            "sharing_lover"
        ], 
        "towerKey": "lovingCareTower", 
        "currenciesToMake": [
            "care_lover"
        ], 
        "name": "Loving Care", 
        "key": "lovingCareAction"
    }, 
    "rollSleevesMenu": {
        "image": "goal_drive-000000", 
        "completeText": "There's plenty of work to do. Best to start now.", 
        "name": "COMPLETED - Work On Yourself", 
        "key": "rollSleevesMenu"
    }, 
    "callousnessButton": {
        "constructionKey": "callousnessTowerConstruction", 
        "name": "Callousness", 
        "image": "tower_numb", 
        "entityCost": {
            "strength": 5
        }, 
        "entityKey": "callousnessTower", 
        "limitingKeys": [
            "callousnessTower", 
            "callousnessTowerConstruction", 
            "cynicismTower"
        ], 
        "key": "callousnessButton", 
        "subPanel": "Defenses", 
        "description": "Uses strength to calm anger, joy, sadness"
    }, 
    "explainObnoxiousMenu": {
        "completeText": [
            "You just get carried away sometimes and it's always the lover who", 
            "suffers from it."
        ], 
        "name": "Burst of Obnoxiousness", 
        "key": "explainObnoxiousMenu"
    }, 
    "demon2Goal": {
        "key": "demon2Goal", 
        "randomPosition": 1, 
        "name": "Quiet Inner Demon", 
        "makeOnTimeOut": "selfWorthless", 
        "image": "goal_goalActivation-000000", 
        "description": "Sometimes you just feel sad.", 
        "sourceTrait": "demons", 
        "currencyRequested": {
            "cheer": 3
        }, 
        "goalMenuKey": "demon2Menu", 
        "timeToDeath": 120
    }, 
    "prideStartToDemons": {
        "sourceTrait": "prideStart", 
        "newTrait": "demons", 
        "key": "prideStartToDemons"
    }, 
    "drownAction": {
        "currenciesRequired": [
            "music", 
            "social_muse"
        ], 
        "towerKey": "drownTower", 
        "currenciesToMake": [
            "drown_muse"
        ], 
        "name": "Drowning In Muse", 
        "key": "drownAction"
    }, 
    "cynicismButton": {
        "constructionKey": "cynicismTowerConstruction", 
        "name": "Cynicism", 
        "baseKeys": [
            "callousnessTower"
        ], 
        "image": "tower_goalSpy-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "cynicismTower", 
        "limitingKeys": [
            "cynicismTower", 
            "cynicismTowerConstruction"
        ], 
        "key": "cynicismButton", 
        "subPanel": "Defenses", 
        "description": "Uses will to calm anger, joy, sadness"
    }, 
    "lostControl": {
        "name": "lostControl", 
        "key": "lostControl"
    }, 
    "successDeposit": {
        "randomPosition": 1, 
        "name": "Success", 
        "image": "tower_trait-ede9e6_coolNote", 
        "sourceTrait": 0, 
        "deposit": {
            "success": 5
        }, 
        "key": "successDeposit", 
        "activeImage": "tower_trait-ede9e6_coolNote_activeOutline-0099ff"
    }, 
    "insightDeposit": {
        "image": "tower_spark", 
        "activeImage": "tower_spark_activeOutline-0099ff", 
        "name": "Insight", 
        "key": "insightDeposit", 
        "deposit": {
            "insight": 3
        }
    }, 
    "endAlienationButton": {
        "constructionKey": "endAlienationGoalConstruction", 
        "name": "End Alienation", 
        "image": "goal_heart-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "endAlienationGoal", 
        "key": "endAlienationButton", 
        "description": "Requires love."
    }, 
    "willMine": {
        "image": "tower_mineWill", 
        "resourceKeys": [
            "will"
        ], 
        "name": "Drive", 
        "key": "willMine", 
        "activeImage": "tower_mineWill_activeOutline-0099ff"
    }, 
    "rollSleevesButton": {
        "constructionKey": "rollSleevesGoalConstruction", 
        "name": "Work On Yourself", 
        "image": "goal_drive-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "rollSleevesGoal", 
        "key": "rollSleevesButton", 
        "description": "Requires strength, frustration."
    }, 
    "acceptDemonGoalConstruction": {
        "key": "acceptDemonGoalConstruction", 
        "constructionIdentifier": "acceptDemonGoal", 
        "name": "Constructing - Accept Truth", 
        "image": "trait-aaaaaa99_goal_trophy-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Accept Truth", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "apologizeMenu": {
        "image": "goal_towerConfess-000000", 
        "completeText": "At least now it's over.", 
        "name": "COMPLETED - Apologize", 
        "key": "apologizeMenu"
    }, 
    "needLoverTower": {
        "image": "tower_dependence", 
        "activeImage": "tower_dependence_activeOutline-0099ff", 
        "name": "Express Need For Lover", 
        "key": "needLoverTower", 
        "description": [
            "It is embarrassing how much you need the lover.", 
            "Converts social + self-esteem -> need"
        ]
    }, 
    "refineInspirationMenu": {
        "image": "goal_towerThinkAbout-000000", 
        "name": "COMPLETED - Refine Raw Inspiration", 
        "key": "refineInspirationMenu"
    }, 
    "stopDemonStrengthMenu": {
        "image": "goal_memoryProgramming-000000", 
        "name": "COMPLETED - Stop Demon", 
        "key": "stopDemonStrengthMenu"
    }, 
    "endMuseGoal": {
        "key": "endMuseGoal", 
        "randomPosition": 0, 
        "name": "Cut Off From The Muse", 
        "image": "goal_towerConfess-000000", 
        "currencyRequested": {
            "strength": 5
        }, 
        "entitiesToMake": "killMuse", 
        "goalMenuKey": "endMuseMenu", 
        "description": "This is what the lover wants."
    }, 
    "reflectPastMenu": {
        "image": "goal_drive-000000", 
        "name": "COMPLETED - Reflect On Past", 
        "key": "reflectPastMenu"
    }, 
    "gainControl_joy": {
        "barColor": "cccc99", 
        "text": "It's hard to control yourself when you're this happy", 
        "textColor": "dddd00", 
        "key": "gainControl_joy"
    }, 
    "sadSong": {
        "name": "Dissatisfaction With Song", 
        "aliveEntities": {
            "dontGiveUpGoal": 5
        }, 
        "image": "trait-993399", 
        "personHeld": "song", 
        "isaacEntities": [
            "identifyProblemButton", 
            "discardButton", 
            "dontGiveUpGoal", 
            "restartWorkButton", 
            "killIgnoreIssuesGoal", 
            "pushYourselfMineButton", 
            "failureMineButton"
        ], 
        "key": "sadSong"
    }, 
    "findNewMusicMenu": {
        "image": "goal_cd-000000", 
        "completeText": "This is the kind of thing you can get your teeth into.", 
        "name": "COMPLETED - Listen To Music Together", 
        "key": "findNewMusicMenu"
    }, 
    "selfLoathingToAloneness": {
        "sourceTrait": "selfLoathing", 
        "newTrait": "aloneness", 
        "key": "selfLoathingToAloneness"
    }, 
    "quietDemonsStrengthMenu": {
        "image": "goal_weight-000000", 
        "name": "COMPLETED - Quiet Demons", 
        "key": "quietDemonsStrengthMenu"
    }, 
    "insulting": {
        "flag": "insulting", 
        "key": "insulting"
    }, 
    "captureSocialGoal": {
        "key": "captureSocialGoal", 
        "randomPosition": 0, 
        "name": "Capture Inspiration", 
        "image": "goal_butterfly-000000", 
        "currencyRequested": {
            "social_lover": 1
        }, 
        "entitiesToMake": "captureInspirationResult", 
        "goalMenuKey": "captureSocialMenu", 
        "timeToDeath": 60
    }, 
    "helpRelationship": {
        "entitiesToMake": {
            "killUnhappy": 25, 
            "happy": 50
        }, 
        "key": "helpRelationship"
    }, 
    "demons": {
        "sector": [
            3, 
            6
        ], 
        "name": "Inner Demons", 
        "aliveEntities": {
            "randomDemon": 5
        }, 
        "image": "trait-993333", 
        "personHeld": "demon", 
        "isaacEntities": [
            "proveSuccessGoal", 
            "quietDemonsLoveButton", 
            "quietDemonsStrengthButton", 
            "acceptDemonButton"
        ], 
        "key": "demons", 
        "startManagedEntities": [
            "demonsTraitConnectLoverBase", 
            "demonsTraitConnectMusic"
        ]
    }, 
    "overcomeDistanceGoal": {
        "randomPosition": 1, 
        "name": "Overcome Distance", 
        "currencyRequested": {
            "cheer": 3, 
            "strength": 3
        }, 
        "image": "goal_towerConfess-000000", 
        "minSelfDist": 4, 
        "sourceTrait": "loverBase", 
        "key": "overcomeDistanceGoal", 
        "goalMenuKey": "overcomeDistanceMenu", 
        "description": [
            "You just feel a little distant from the lover right now.", 
            [
                "Disables real talk while alive."
            ]
        ]
    }, 
    "ignoreIssuesMenu": {
        "image": "goal_headphones-000000", 
        "name": "COMPLETED - Ignore Issues With Song", 
        "key": "ignoreIssuesMenu"
    }, 
    "discardMenu": {
        "image": "goal_goalDisavowal-000000", 
        "name": "COMPLETED - Discard Song", 
        "key": "discardMenu"
    }, 
    "joy": {
        "emotion": "joy", 
        "planeOfExistence": "path", 
        "name": "joy", 
        "image": "creep-ffff00", 
        "damage": 0, 
        "memoryImage": "memoryJoy", 
        "health": 10, 
        "key": "joy"
    }, 
    "emoInspiration_ext": {
        "image": [
            "trait-9900cc", 
            "trait-8900B7", 
            "trait-A800E0"
        ], 
        "name": "Emotional Inspiration", 
        "key": "emoInspiration_ext", 
        "sourceKey": "emoInspiration"
    }, 
    "sayRightTowerConstruction": {
        "key": "sayRightTowerConstruction", 
        "constructionIdentifier": "sayRightTower", 
        "name": "Constructing - Saying The Right Things", 
        "image": "trait-aaaaaa99_tower_understand", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Saying The Right Things", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "reflectSuccessMenu": {
        "image": "goal_trophy-000000", 
        "name": "COMPLETED - Reflect on Success", 
        "key": "reflectSuccessMenu"
    }, 
    "drainedSad": {
        "emotionsToAdd": {
            "sadness": 2
        }, 
        "name": "Feeling Drained", 
        "image": "tower_energy", 
        "key": "drainedSad", 
        "memoryKey": "memory_drainedSad", 
        "emotionTags": "work", 
        "resourceKey": "drainedSadMineConstruction", 
        "activeImage": "tower_energy_activeOutline-0099ff", 
        "description": "It takes energy not to be sad and you don't have that right now."
    }, 
    "integrateGoalConstruction": {
        "key": "integrateGoalConstruction", 
        "constructionIdentifier": "integrateGoal", 
        "name": "Constructing - Integrate Hard Work", 
        "image": "trait-aaaaaa99_goal_towerThinkAbout-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Integrate Hard Work", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "solveMusicMenu": {
        "image": "goal_towerThinkAbout-000000", 
        "completeText": "That was interesting. Now it's time to build on top of what they did.", 
        "name": "COMPLETED - Solve Fresh Music", 
        "key": "solveMusicMenu"
    }, 
    "painMine": {
        "name": "Pain Mine", 
        "image": "tower_pain", 
        "key": "painMine", 
        "resourceKeys": [
            "pain"
        ], 
        "resourceEntities": {
            "selfPityGoal": 5
        }, 
        "activeImage": "tower_pain_activeOutline-0099ff"
    }, 
    "museFishMenu": {
        "image": "goal_hook-000000", 
        "name": "COMPLETED - Fish For Compliments", 
        "key": "museFishMenu"
    }, 
    "socialMine": {
        "image": "tower_mineSocial", 
        "resourceKeys": [
            "social_lover"
        ], 
        "name": "Spend Time With Lover", 
        "key": "socialMine", 
        "activeImage": "tower_mineSocial_activeOutline-0099ff"
    }, 
    "friendToLoving": {
        "sourceTrait": "friend", 
        "newTrait": "loving", 
        "key": "friendToLoving"
    }, 
    "sustainTechInspirationGoal": {
        "key": "sustainTechInspirationGoal", 
        "randomPosition": 1, 
        "name": "Sustain Tech Inspiration", 
        "makeOnTimeOut": "killTechInspiration", 
        "image": "goal_mineCaution-000000", 
        "sourceTrait": "techInspiration", 
        "currencyRequested": {
            "music": 5
        }, 
        "entitiesToMake": "fadeTechInspiration", 
        "goalMenuKey": "sustainTechInspirationMenu", 
        "timeToDeath": 150
    }, 
    "loveStrengthTower": {
        "image": "tower_weight", 
        "activeImage": "tower_weight_activeOutline-0099ff", 
        "name": "Derive Strength", 
        "key": "loveStrengthTower", 
        "description": "Converts love -> strength"
    }, 
    "listenDemonsMenu": {
        "image": "goal_memoryProgramming-000000", 
        "name": "COMPLETED - Listen To Demons", 
        "key": "listenDemonsMenu"
    }, 
    "suspicionMenu": {
        "image": "goal_towerConfess-000000", 
        "completeText": "The lover is not at all convinced by your explanation.", 
        "name": "COMPLETED - Defuse Suspicion", 
        "key": "suspicionMenu"
    }, 
    "stuckBrute": {
        "entitiesToMake": [
            "stuckProblem"
        ], 
        "key": "stuckBrute"
    }, 
    "museBaseTraitConnectSelfCore": {
        "startKey": "museBase", 
        "endKey": "selfCore", 
        "key": "museBaseTraitConnectSelfCore"
    }, 
    "impressButton": {
        "constructionKey": "impressGoalConstruction", 
        "name": "Impress Lover", 
        "image": "goal_trophy-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "impressGoal", 
        "key": "impressButton", 
        "description": [
            "Requires cool thing.", 
            "Makes self-esteem."
        ]
    }, 
    "indifferenceTower": {
        "image": "tower_goalConfession-000000", 
        "activeImage": "tower_goalConfession-000000_activeOutline-0099ff", 
        "name": "Aloofness", 
        "key": "indifferenceTower", 
        "description": "Converts strength -> alienation"
    }, 
    "selfHateMineConstruction": {
        "key": "selfHateMineConstruction", 
        "constructionIdentifier": "selfHateMine", 
        "name": "Hating Yourself", 
        "image": "tower_memoryDefineSelf-000000-666666aa", 
        "currencyRequested": {
            "impulse": 1
        }, 
        "constructionName": "Hating Yourself"
    }, 
    "romanceToLoving": {
        "sourceTrait": "romance", 
        "newTrait": "loving", 
        "key": "romanceToLoving"
    }, 
    "startNewSongGoal": {
        "key": "startNewSongGoal", 
        "randomPosition": 0, 
        "name": "Start New Song", 
        "image": "goal_newNote-000000", 
        "currencyRequested": {
            "will": 3, 
            "music": 3
        }, 
        "entitiesToMake": [
            "nascentSong", 
            "startNewSongGoalPathNascentGoal"
        ], 
        "goalMenuKey": "startNewSongMenu"
    }, 
    "unhappyDisable": {
        "name": "The lover is too unhappy with you right now for this.", 
        "key": "unhappyDisable", 
        "traitsToDisable": [
            "romance", 
            "loving", 
            "makingWork", 
            "friend", 
            "hangingOut", 
            "alienation", 
            "realTalk"
        ]
    }, 
    "indulgeBitternessGoalConstruction": {
        "key": "indulgeBitternessGoalConstruction", 
        "constructionIdentifier": "indulgeBitternessGoal", 
        "name": "Constructing - Indulge In Bitterness", 
        "image": "trait-aaaaaa99_goal_pain-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Indulge In Bitterness", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "makePlaythingMenu": {
        "image": "goal_dice-000000", 
        "name": "COMPLETED - Make Playthings", 
        "key": "makePlaythingMenu"
    }, 
    "endRealTalkMenu": {
        "image": "goal_social-000000", 
        "name": "COMPLETED - You've Said Enough", 
        "key": "endRealTalkMenu"
    }, 
    "indulgeHappyButton": {
        "constructionKey": "indulgeHappyGoalConstruction", 
        "name": "Indulge In Satisfaction", 
        "image": "goal_mineCheer-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "indulgeHappyGoal", 
        "key": "indulgeHappyButton", 
        "description": "Requires will."
    }, 
    "flashback_anger": {
        "resourceCount": 1, 
        "name": "Flashback", 
        "image": "tower_flashback-000000", 
        "key": "flashback_anger", 
        "emotionTags": "anger", 
        "resourceKeys": [
            "anger"
        ], 
        "activeImage": "tower_flashback-000000_activeOutline-0099ff", 
        "description": "makes anger"
    }, 
    "daydreamGoal": {
        "key": "daydreamGoal", 
        "randomPosition": 0, 
        "name": "Daydream", 
        "image": "goal_memoryDefineSelf-000000-000000", 
        "currencyRequested": {
            "relax": 3
        }, 
        "entitiesToMake": "ambitionToDaydreaming", 
        "goalMenuKey": "daydreamMenu"
    }, 
    "loverBasePathMusicLove": {
        "startKey": "loverBase", 
        "endKey": "musicLove", 
        "key": "loverBasePathMusicLove"
    }, 
    "condLoverJoy": {
        "entitiesToMake": "loverJoy", 
        "stopEntities": [
            "unhappy"
        ], 
        "key": "condLoverJoy"
    }, 
    "apology_lover": {
        "name": "apology", 
        "key": "apology_lover", 
        "tags": [
            "apology_lover"
        ]
    }, 
    "shareJoyMuseTowerConstruction": {
        "key": "shareJoyMuseTowerConstruction", 
        "constructionIdentifier": "shareJoyMuseTower", 
        "name": "Constructing - Share Joy", 
        "image": "trait-aaaaaa99_tower_towerTalk", 
        "currencyRequested": {
            "social_muse": 5
        }, 
        "constructionName": "Share Joy", 
        "killOnTransform": 0
    }, 
    "daydreaming": {
        "name": "Daydreaming", 
        "image": "trait-99ff66", 
        "personHeld": "selfCore", 
        "isaacEntities": [
            "reflectFailureButton", 
            "reflectSuccessButton"
        ], 
        "key": "daydreaming", 
        "maxLevel": 12
    }, 
    "rawInspiration": {
        "name": "raw inspiration", 
        "key": "rawInspiration", 
        "tags": [
            "rawInspiration"
        ]
    }, 
    "flashback_sadnessConstruction": {
        "key": "flashback_sadnessConstruction", 
        "constructionIdentifier": "flashback_sadness", 
        "name": "Flashback", 
        "image": "tower_flashback-000000-666666aa", 
        "currencyRequested": {
            "impulse": 1
        }, 
        "constructionName": "Flashback"
    }, 
    "cynicismTowerConstruction": {
        "key": "cynicismTowerConstruction", 
        "constructionIdentifier": "cynicismTower", 
        "name": "Constructing - Cynicism", 
        "image": "trait-aaaaaa99_tower_goalSpy-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Cynicism", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "deadInsideButton": {
        "constructionKey": "deadInsideTowerConstruction", 
        "name": "Numbness", 
        "image": "tower_numb", 
        "entityCost": {
            "pain": 5
        }, 
        "entityKey": "deadInsideTower", 
        "limitingKeys": [
            "deadInsideTower", 
            "deadInsideTowerConstruction"
        ], 
        "key": "deadInsideButton", 
        "description": "Uses pain to calm anger, joy"
    }, 
    "integrateMenu": {
        "image": "goal_towerThinkAbout-000000", 
        "name": "COMPLETED - Integrate Hard Work", 
        "key": "integrateMenu"
    }, 
    "musicMine": {
        "image": "tower_note", 
        "resourceKeys": [
            "music"
        ], 
        "name": "Music", 
        "key": "musicMine", 
        "activeImage": "tower_note_activeOutline-0099ff"
    }, 
    "acceptDemonGoal": {
        "key": "acceptDemonGoal", 
        "randomPosition": 0, 
        "name": "Accept Truth", 
        "image": "goal_trophy-000000", 
        "currencyRequested": {
            "will": 1
        }, 
        "entitiesToMake": "selfWorthless", 
        "goalMenuKey": "acceptDemonMenu", 
        "description": "It's hard to fight the demons when you know they don't lie."
    }, 
    "fadeExhaustion": {
        "timeToDeath": 450, 
        "key": "fadeExhaustion", 
        "makeOnTimeOut": "killExhaustion"
    }, 
    "captureInspirationGoal": {
        "key": "captureInspirationGoal", 
        "randomPosition": 1, 
        "name": "Capture Inspiration", 
        "image": "goal_butterfly-000000", 
        "currencyRequested": {
            "thought": 1
        }, 
        "entitiesToMake": "captureInspirationResult", 
        "goalMenuKey": "captureInspirationMenu", 
        "timeToDeath": 60
    }, 
    "lackAccomplishmentButton": {
        "constructionKey": "lackAccomplishmentTowerConstruction", 
        "name": "Lack of Accomplishment", 
        "image": "tower_lackAccomplishment", 
        "entityCost": {
            "selfLoathing": 5
        }, 
        "entityKey": "lackAccomplishmentTower", 
        "limitingKeys": [
            "lackAccomplishmentTower", 
            "lackAccomplishmentTowerConstruction"
        ], 
        "key": "lackAccomplishmentButton", 
        "subPanel": "Defenses", 
        "description": "Uses selfLoathing to calm joy"
    }, 
    "deriveHumanInsightGoal": {
        "currencyEntities": "drainedSad", 
        "randomPosition": 0, 
        "name": "Derive Human Insight", 
        "image": "goal_spark-000000", 
        "key": "deriveHumanInsightGoal", 
        "currencyRequested": {
            "reflect": 3
        }, 
        "entitiesToMake": "understandingDeposit", 
        "goalMenuKey": "deriveHumanInsightMenu"
    }, 
    "deriveInsightMenu": {
        "image": "goal_towerThinkAbout-000000", 
        "name": "COMPLETED - Derive Insight", 
        "key": "deriveInsightMenu"
    }, 
    "swallowBitternessButton": {
        "constructionKey": "swallowBitternessGoalConstruction", 
        "name": "Swallow Bitterness", 
        "image": "goal_daydream-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "swallowBitternessGoal", 
        "key": "swallowBitternessButton", 
        "description": "Requires will."
    }, 
    "irritabilityMenu": {
        "image": "goal_note-000000", 
        "name": "COMPLETED - Scratch Music Itch", 
        "key": "irritabilityMenu"
    }, 
    "thinkSongBadGoalConstruction": {
        "key": "thinkSongBadGoalConstruction", 
        "constructionIdentifier": "thinkSongBadGoal", 
        "name": "Constructing - Face Song Failings", 
        "image": "trait-aaaaaa99_goal_eyeTear-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Face Song Failings", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "museInspirationMenu": {
        "image": "goal_towerThinkAbout-000000", 
        "completeText": "The muse did what muses do. Inspire.", 
        "name": "COMPLETED - Be Inspired!", 
        "key": "museInspirationMenu"
    }, 
    "emoWorkButton": {
        "constructionKey": "emoWorkTowerConstruction", 
        "name": "Complete Add Emotion", 
        "image": "tower_heart", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "emoWorkTower", 
        "limitingKeys": [
            "emoWorkTower", 
            "emoWorkTowerConstruction"
        ], 
        "key": "emoWorkButton", 
        "subPanel": "Towers", 
        "description": "Converts emotional solution + emotional issue -> hard work"
    }, 
    "easyWorkAction": {
        "towerKey": "easyWorkTower", 
        "name": "Easy Work", 
        "entitiesOnConvert": {
            "workSatisfaction": 25
        }, 
        "currenciesRequired": [
            "will", 
            "easyProblem", 
            "music"
        ], 
        "currenciesToMake": [
            "songSatisfaction"
        ], 
        "key": "easyWorkAction"
    }, 
    "dismissSuccessGoalConstruction": {
        "key": "dismissSuccessGoalConstruction", 
        "constructionIdentifier": "dismissSuccessGoal", 
        "name": "Constructing - Dismiss your Success", 
        "image": "trait-aaaaaa99_goal_eyeTear-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Dismiss your Success", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "apologizeButton": {
        "constructionKey": "apologizeTowerConstruction", 
        "name": "Apologize", 
        "image": "tower_towerConfess", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "apologizeTower", 
        "limitingKeys": [
            "apologizeTower", 
            "apologizeTowerConstruction"
        ], 
        "key": "apologizeButton", 
        "subPanel": "Towers", 
        "description": "Converts need + mistake -> apology"
    }, 
    "chapter2": {
        "name": "Chapter 2 - Where Next?", 
        "makeOnCreateEntities": [
            "nextDayMuse"
        ], 
        "chapterCount": 2, 
        "key": "chapter2", 
        "entitiesToMake": [
            "chapter3"
        ], 
        "goalsToCheck": {
            "releaseGoal": 1
        }
    }, 
    "loveMineButton": {
        "constructionKey": "loveMineConstruction", 
        "name": "Love", 
        "entityCost": {
            "will": 2
        }, 
        "image": "tower_heart", 
        "currenciesMade": [
            "love_lover"
        ], 
        "entityKey": "loveMine", 
        "limitingKeys": [
            "loveMine", 
            "loveMineConstruction"
        ], 
        "key": "loveMineButton", 
        "description": "makes love"
    }, 
    "demonsTraitConnectMusic": {
        "startKey": "demons", 
        "endKey": "music", 
        "key": "demonsTraitConnectMusic"
    }, 
    "dreamGreatness": {
        "name": "dream of greatness", 
        "key": "dreamGreatness", 
        "tags": [
            "dreamGreatness"
        ]
    }, 
    "deriveCoolThingGoalConstruction": {
        "key": "deriveCoolThingGoalConstruction", 
        "constructionIdentifier": "deriveCoolThingGoal", 
        "name": "Constructing - Derive Cool Thing", 
        "image": "trait-aaaaaa99_goal_coolNote-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Derive Cool Thing", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "rollSleevesGoal": {
        "key": "rollSleevesGoal", 
        "randomPosition": 0, 
        "name": "Work On Yourself", 
        "image": "goal_drive-000000", 
        "currencyRequested": {
            "strength": 3, 
            "frustration": 3
        }, 
        "entitiesToMake": "ambitionToSelfReliance", 
        "goalMenuKey": "rollSleevesMenu"
    }, 
    "tryOldJokesMenu": {
        "image": "goal_social-000000", 
        "completeText": "They're not funny anymore.", 
        "name": "COMPLETED - Try Old Jokes", 
        "key": "tryOldJokesMenu"
    }, 
    "endHangOutButton": {
        "constructionKey": "endHangOutGoalConstruction", 
        "name": "Call It A Day", 
        "image": "goal_goalPark-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "endHangOutGoal", 
        "key": "endHangOutButton", 
        "description": "Requires will."
    }, 
    "endMuseButton": {
        "constructionKey": "endMuseGoalConstruction", 
        "name": "Cut Off From The Muse", 
        "image": "goal_towerConfess-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "endMuseGoal", 
        "key": "endMuseButton", 
        "description": "Requires strength."
    }, 
    "musicLovePathHateWork": {
        "startKey": "musicLove", 
        "endKey": "hateWork", 
        "key": "musicLovePathHateWork"
    }, 
    "exhaustion": {
        "name": "Exhaustion", 
        "aliveEntities": {
            "drainedSad": 5
        }, 
        "image": "trait-6600ff", 
        "personHeld": "music", 
        "isaacEntities": [
            "exhaustionDisable", 
            "overcomeExhaustButton", 
            "fadeExhaustion"
        ], 
        "health": 100, 
        "key": "exhaustion"
    }, 
    "fireUpMenu": {
        "image": "goal_weight-000000", 
        "name": "COMPLETED - Fire Yourself Up", 
        "key": "fireUpMenu"
    }, 
    "tryComplimentsMenu": {
        "image": "goal_hook-000000", 
        "completeText": "This doesn't feel like it's going to happen.", 
        "name": "COMPLETED - Try For Compliments", 
        "key": "tryComplimentsMenu"
    }, 
    "prideBurstMine": {
        "resourceCount": 1, 
        "name": "Burst of Pride", 
        "image": "tower_memoryRing", 
        "key": "prideBurstMine", 
        "emotionTags": [
            "work", 
            "joy"
        ], 
        "resourceKeys": [
            "joy"
        ], 
        "activeImage": "tower_memoryRing_activeOutline-0099ff", 
        "description": "makes joy"
    }, 
    "sadSongToSong": {
        "sourceTrait": "sadSong", 
        "newTrait": "song", 
        "key": "sadSongToSong"
    }, 
    "prideBurstMineConstruction": {
        "key": "prideBurstMineConstruction", 
        "constructionIdentifier": "prideBurstMine", 
        "name": "Burst of Pride", 
        "image": "tower_memoryRing-666666aa", 
        "currencyRequested": {
            "impulse": 1
        }, 
        "constructionName": "Burst of Pride"
    }, 
    "suppressDemonsGoal": {
        "key": "suppressDemonsGoal", 
        "randomPosition": 1, 
        "name": "Suppress Inner Demons", 
        "makeOnTimeOut": "prideStartToDemons", 
        "image": "goal_goalActivation-000000", 
        "sourceTrait": "prideStart", 
        "currencyRequested": {
            "will": 3
        }, 
        "goalMenuKey": "suppressDemonsMenu", 
        "timeToDeath": 120
    }, 
    "demon2Menu": {
        "image": "goal_goalActivation-000000", 
        "name": "COMPLETED - Quiet Inner Demon", 
        "key": "demon2Menu"
    }, 
    "bitternessToAmbition": {
        "sourceTrait": "bitterness", 
        "newTrait": "ambition", 
        "key": "bitternessToAmbition"
    }, 
    "stopDemonLoveGoal": {
        "key": "stopDemonLoveGoal", 
        "randomPosition": 1, 
        "name": "Stop Demon", 
        "makeOnTimeOut": "selfWorthless", 
        "image": "goal_memoryProgramming-000000", 
        "sourceTrait": "demons", 
        "currencyRequested": {
            "love_lover": 3
        }, 
        "goalMenuKey": "stopDemonLoveMenu", 
        "timeToDeath": 240
    }, 
    "easyProblem": {
        "name": "easy problem", 
        "key": "easyProblem", 
        "tags": [
            "easyProblem"
        ]
    }, 
    "frustration": {
        "name": "frustration", 
        "key": "frustration", 
        "tags": [
            "frustration"
        ]
    }, 
    "memory_selfHate": {
        "image": "trait-aa1717", 
        "memoryEmotions": [
            "anger"
        ], 
        "key": "memory_selfHate", 
        "name": "Hating Yourself"
    }, 
    "singleHealthComponent": {
        "health": 1
    }, 
    "indulgeBitternessGoal": {
        "key": "indulgeBitternessGoal", 
        "randomPosition": 0, 
        "name": "Indulge In Bitterness", 
        "image": "goal_pain-000000", 
        "currencyRequested": {
            "failure": 3, 
            "pain": 3
        }, 
        "entitiesToMake": "selfLoathingToBitterness", 
        "goalMenuKey": "indulgeBitternessMenu"
    }, 
    "endAlienationGoalConstruction": {
        "key": "endAlienationGoalConstruction", 
        "constructionIdentifier": "endAlienationGoal", 
        "name": "Constructing - End Alienation", 
        "image": "trait-aaaaaa99_goal_heart-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "End Alienation", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "songSatisfiedGoal": {
        "key": "songSatisfiedGoal", 
        "randomPosition": 0, 
        "name": "Check Song Quality", 
        "image": "goal_coolNote-000000", 
        "currencyRequested": {
            "songSatisfaction": 3
        }, 
        "entitiesToMake": "satisfyResult", 
        "goalMenuKey": "songSatisfiedMenu", 
        "description": "It's a lot to ask of yourself."
    }, 
    "relaxMenu": {
        "image": "goal_goalPark-000000", 
        "name": "COMPLETED - Relax", 
        "key": "relaxMenu"
    }, 
    "songSatisfiedButton": {
        "constructionKey": "songSatisfiedGoalConstruction", 
        "name": "Check Song Quality", 
        "image": "goal_coolNote-000000", 
        "description": "Requires satisfaction.", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "songSatisfiedGoal", 
        "key": "songSatisfiedButton", 
        "displayRiskIcon": 1
    }, 
    "captureWillGoal": {
        "key": "captureWillGoal", 
        "randomPosition": 0, 
        "name": "Capture Inspiration", 
        "image": "goal_butterfly-000000", 
        "currencyRequested": {
            "will": 1
        }, 
        "entitiesToMake": "captureInspirationResult", 
        "goalMenuKey": "captureWillMenu", 
        "timeToDeath": 60
    }, 
    "angerRideMenu": {
        "image": "goal_goalFightProgramming-000000-000000", 
        "completeText": "Somewhere in all of this anger is the answer you need.", 
        "name": "COMPLETED - Explore Anger", 
        "key": "angerRideMenu"
    }, 
    "rememberMenu": {
        "image": "goal_social-000000", 
        "name": "COMPLETED - Remember Being Alone", 
        "key": "rememberMenu"
    }, 
    "reflectMine": {
        "image": "tower_mineThought", 
        "resourceKeys": [
            "reflect"
        ], 
        "name": "Reflection", 
        "key": "reflectMine", 
        "activeImage": "tower_mineThought_activeOutline-0099ff"
    }, 
    "satisfyResult": {
        "goodEntities": "makeGoodSong", 
        "badEntities": {
            "fullStuckProblem": 2
        }, 
        "key": "satisfyResult"
    }, 
    "listenEvent": {
        "goodEntities": {
            "condCaptureInspirationGoal": 1, 
            "minorFlirtGoal": 3
        }, 
        "key": "listenEvent"
    }, 
    "daydreamGoalConstruction": {
        "key": "daydreamGoalConstruction", 
        "constructionIdentifier": "daydreamGoal", 
        "name": "Constructing - Daydream", 
        "image": "trait-aaaaaa99_goal_memoryDefineSelf-000000-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Daydream", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "killExhaustion": {
        "entitiesToKill": [
            "exhaustion"
        ], 
        "key": "killExhaustion"
    }, 
    "showMusicGoalConstruction": {
        "key": "showMusicGoalConstruction", 
        "constructionIdentifier": "showMusicGoal", 
        "name": "Constructing - Show Lover The Song", 
        "image": "trait-aaaaaa99_goal_coolNote-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Show Lover The Song", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "fadingMemory": {
        "mapSpawnKey": "memoryExtension", 
        "playerKillable": "true", 
        "timeToDeath": 300
    }, 
    "beCaredGoal": {
        "key": "beCaredGoal", 
        "randomPosition": 0, 
        "name": "Be Cared For", 
        "image": "goal_heart-000000", 
        "currencyRequested": {
            "need_lover": 3
        }, 
        "entitiesToMake": "careDeposit", 
        "goalMenuKey": "beCaredMenu", 
        "description": "You just miss being able to handle stuff yourself."
    }, 
    "realTalk": {
        "name": "Real Talk", 
        "image": "trait-ffaa33", 
        "personHeld": "lover", 
        "isaacEntities": [
            "loveMineButton", 
            "endRealTalkButton", 
            "toldWorthButton", 
            "tryForMeaningButton", 
            "fadeRealTalk"
        ], 
        "key": "realTalk", 
        "description": "It's easy to forget sometimes, but you really do love each other"
    }, 
    "killAchievementButton": {
        "entitiesToKill": [
            "achievementButton"
        ], 
        "key": "killAchievementButton"
    }, 
    "bandCheck": {
        "qualityKey": "bandEnthusiasmMine", 
        "minQualityEntities": "gameEnd", 
        "minQuality": 2, 
        "key": "bandCheck"
    }, 
    "selfRelianceToAmbition": {
        "sourceTrait": "selfReliance", 
        "newTrait": "ambition", 
        "key": "selfRelianceToAmbition"
    }, 
    "workThroughGoalConstruction": {
        "key": "workThroughGoalConstruction", 
        "constructionIdentifier": "workThroughGoal", 
        "name": "Constructing - Work Through Emotion", 
        "image": "trait-aaaaaa99_towerThinkAbout", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Work Through Emotion", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "shareButton": {
        "constructionKey": "shareTowerConstruction", 
        "name": "Share Experiences", 
        "image": "tower_towerDate", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "shareTower", 
        "limitingKeys": [
            "shareTower", 
            "shareTowerConstruction"
        ], 
        "key": "shareButton", 
        "subPanel": "Towers", 
        "description": "Converts social -> sharing"
    }, 
    "quitWorkGoal": {
        "key": "quitWorkGoal", 
        "randomPosition": 0, 
        "name": "Quit Worthless Job", 
        "image": "goal_heart-000000", 
        "currencyRequested": {
            "will": 3
        }, 
        "entitiesToMake": "hateWorkToAmbition", 
        "goalMenuKey": "quitWorkMenu", 
        "description": "You've been on the verge of this for a while now."
    }, 
    "mistake": {
        "name": "mistake", 
        "key": "mistake", 
        "tags": [
            "mistake"
        ]
    }, 
    "talkEmotion": {
        "badEntities": [
            "hurtLover", 
            "selfHate"
        ], 
        "key": "talkEmotion"
    }, 
    "sadFleshTowerConstruction": {
        "key": "sadFleshTowerConstruction", 
        "constructionIdentifier": "sadFleshTower", 
        "name": "Constructing - Flesh Out With Sadness", 
        "image": "trait-aaaaaa99_tower_pain", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Flesh Out With Sadness", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "reflectAloneGoal": {
        "key": "reflectAloneGoal", 
        "randomPosition": 0, 
        "name": "Reflect on Being Alone", 
        "image": "goal_social-000000", 
        "currencyRequested": {
            "will": 1
        }, 
        "entitiesToMake": "aloneSad", 
        "goalMenuKey": "reflectAloneMenu"
    }, 
    "keepTalkGoingMenu": {
        "image": "goal_goalPark-000000", 
        "name": "COMPLETED - Keep Things Going", 
        "key": "keepTalkGoingMenu"
    }, 
    "needLoverTowerConstruction": {
        "key": "needLoverTowerConstruction", 
        "constructionIdentifier": "needLoverTower", 
        "name": "Constructing - Express Need For Lover", 
        "image": "trait-aaaaaa99_tower_dependence", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Express Need For Lover", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "showMusic_ext": {
        "personHeld": "lover", 
        "image": [
            "trait-ffcc00", 
            "trait-E5B700", 
            "trait-FFE000"
        ], 
        "name": "Show Music", 
        "key": "showMusic_ext", 
        "sourceKey": "showMusic"
    }, 
    "shameBurstMineConstruction": {
        "key": "shameBurstMineConstruction", 
        "constructionIdentifier": "shameBurstMine", 
        "name": "Burst of Shame", 
        "image": "tower_memoryShame-000000-666666aa", 
        "currencyRequested": {
            "impulse": 1
        }, 
        "constructionName": "Burst of Shame"
    }, 
    "demon": {
        "badEntities": [
            "demon1Goal", 
            "demon2Goal"
        ], 
        "key": "demon"
    }, 
    "hurtBand": {
        "qualityDiff": -1, 
        "key": "hurtBand", 
        "qualityEntity": "bandEnthusiasmMine"
    }, 
    "hateWork": {
        "sector": [
            0, 
            0
        ], 
        "name": "Hating Your Job", 
        "image": "trait-00cc00", 
        "playerKillable": "true", 
        "personHeld": "selfCore", 
        "isaacEntities": [
            "willMineButton", 
            "quitWorkButton", 
            "exhaustionDisable", 
            "hateWorkLoveDisable"
        ], 
        "health": 1000, 
        "key": "hateWork", 
        "maxLevel": 12, 
        "startManagedEntities": [
            "willMine", 
            "hateWorkPathSelf"
        ], 
        "description": "Is this all that you were meant for?"
    }, 
    "letdown": {
        "name": "letdown", 
        "key": "letdown", 
        "tags": [
            "letdown"
        ]
    }, 
    "minorInsultGoal": {
        "key": "minorInsultGoal", 
        "randomPosition": 1, 
        "name": "Smooth Over Misunderstanding", 
        "makeOnTimeOut": [
            "unhappy", 
            "hurtLover"
        ], 
        "image": "goal_towerConfess-000000", 
        "sourceTrait": "loverBase", 
        "currencyRequested": {
            "social_lover": 3
        }, 
        "goalMenuKey": "minorInsultMenu", 
        "timeToDeath": 240
    }, 
    "museFishGoal": {
        "key": "museFishGoal", 
        "randomPosition": 0, 
        "name": "Fish For Compliments", 
        "image": "goal_hook-000000", 
        "currencyRequested": {
            "social_muse": 1, 
            "releasedMusic": 1
        }, 
        "entitiesToMake": {
            "largePrideDeposit": 100, 
            "suspicionGoal": 25
        }, 
        "goalMenuKey": "museFishMenu", 
        "description": "You barely have to try with the muse."
    }, 
    "hateWorkLoveDisable": {
        "name": "The lover is not talking to you until you follow through and quit.", 
        "key": "hateWorkLoveDisable", 
        "traitsToDisable": [
            "loverBase"
        ]
    }, 
    "killIgnoreIssuesGoal": {
        "entitiesToKill": [
            "ignoreIssuesGoal"
        ], 
        "key": "killIgnoreIssuesGoal"
    }, 
    "findMistakeMenu": {
        "image": "goal_towerConfess-000000", 
        "name": "COMPLETED - Learn Mistakes", 
        "key": "findMistakeMenu"
    }, 
    "killShowMusic": {
        "entitiesToKill": [
            "showMusic"
        ], 
        "key": "killShowMusic"
    }, 
    "obnoxiousGoal": {
        "key": "obnoxiousGoal", 
        "randomPosition": 1, 
        "name": "Suppress Obnoxious Happiness", 
        "makeOnTimeOut": [
            "shameBurst", 
            "aloneTime", 
            "explainObnoxiousMenu"
        ], 
        "image": "goal_pain-000000", 
        "currencyRequested": {
            "pain": 3
        }, 
        "goalMenuKey": "obnoxiousMenu", 
        "timeToDeath": 240
    }, 
    "realTalkToFriend": {
        "sourceTrait": "realTalk", 
        "newTrait": "friend", 
        "key": "realTalkToFriend"
    }, 
    "loveMineConstruction": {
        "key": "loveMineConstruction", 
        "constructionIdentifier": "loveMine", 
        "name": "Constructing - Love", 
        "image": "trait-aaaaaa99_tower_heart", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Love", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "endAlienationStrengthButton": {
        "constructionKey": "endAlienationStrengthGoalConstruction", 
        "name": "End Alienation", 
        "image": "goal_weight-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "endAlienationStrengthGoal", 
        "key": "endAlienationStrengthButton", 
        "description": "Requires strength."
    }, 
    "demonsTraitConnectLoverBase": {
        "startKey": "demons", 
        "endKey": "loverBase", 
        "key": "demonsTraitConnectLoverBase"
    }, 
    "socialMineButton": {
        "constructionKey": "socialMineConstruction", 
        "name": "Spend Time With Lover", 
        "entityCost": {
            "will": 2
        }, 
        "image": "tower_mineSocial", 
        "currenciesMade": [
            "social_lover"
        ], 
        "entityKey": "socialMine", 
        "limitingKeys": [
            "socialMine", 
            "socialMineConstruction"
        ], 
        "key": "socialMineButton", 
        "description": "makes social"
    }, 
    "holdOnAction": {
        "currenciesRequired": [
            "will"
        ], 
        "towerKey": "holdOnTower", 
        "currenciesToMake": [
            "holdOn"
        ], 
        "name": "Holding On", 
        "key": "holdOnAction"
    }, 
    "friendTraitConnectSelfCore": {
        "startKey": "friend", 
        "endKey": "selfCore", 
        "key": "friendTraitConnectSelfCore"
    }, 
    "apologizeResult": {
        "goodEntities": {
            "killUnhappy": 3
        }, 
        "badEntities": "explainApologyFail", 
        "key": "apologizeResult"
    }, 
    "findNewMusicResult": {
        "goodEntities": {
            "musicLoveToFreshMusic": 3
        }, 
        "badEntities": "explainFindMusicFail", 
        "key": "findNewMusicResult"
    }, 
    "romance": {
        "sector": [
            2, 
            3
        ], 
        "name": "Romance", 
        "image": "trait-aa0066", 
        "personHeld": "lover", 
        "isaacEntities": [
            "fishComplimentsButton", 
            "romanceReplace"
        ], 
        "health": 1000, 
        "key": "romance"
    }, 
    "demon1Menu": {
        "image": "goal_goalActivation-000000", 
        "name": "COMPLETED - Quiet Inner Demon", 
        "key": "demon1Menu"
    }, 
    "songCounter": {
        "key": "songCounter"
    }, 
    "selfPityGoal": {
        "key": "selfPityGoal", 
        "randomPosition": 1, 
        "name": "Don't Drown In Self-Pity", 
        "makeOnTimeOut": [
            "killPride", 
            "explainSadMenu"
        ], 
        "image": "goal_lifePreserver-000000", 
        "description": "Failing this will kill all of your self-esteem", 
        "currencyRequested": {
            "cheer": 3
        }, 
        "goalMenuKey": "selfPityMenu", 
        "timeToDeath": 240
    }, 
    "quietDemonsStrengthGoal": {
        "key": "quietDemonsStrengthGoal", 
        "randomPosition": 0, 
        "name": "Quiet Demons", 
        "image": "goal_weight-000000", 
        "currencyRequested": {
            "strength": 5
        }, 
        "entitiesToMake": "demonsToQuietDemons", 
        "goalMenuKey": "quietDemonsStrengthMenu"
    }, 
    "lovingCareTowerConstruction": {
        "key": "lovingCareTowerConstruction", 
        "constructionIdentifier": "lovingCareTower", 
        "name": "Constructing - Loving Care", 
        "image": "trait-aaaaaa99_tower_minePain-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Loving Care", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "fadeRealTalk": {
        "timeToDeath": 180, 
        "key": "fadeRealTalk", 
        "makeOnTimeOut": "keepTalkGoingGoal"
    }, 
    "makeTechInspiration": {
        "entitiesToMake": [
            "techInspiration", 
            "techInspirationTraitConnectSong"
        ], 
        "key": "makeTechInspiration"
    }, 
    "failure": {
        "name": "personal failure", 
        "key": "failure", 
        "tags": [
            "failure"
        ]
    }, 
    "mistakeDesposit": {
        "randomPosition": 1, 
        "name": "Mistakes", 
        "image": "tower_mistake-000000", 
        "deposit": {
            "mistake": 3
        }, 
        "key": "mistakeDesposit", 
        "activeImage": "tower_mistake-000000_activeOutline-0099ff"
    }, 
    "showMusicButton": {
        "constructionKey": "showMusicGoalConstruction", 
        "name": "Show Lover The Song", 
        "image": "goal_coolNote-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "showMusicGoal", 
        "key": "showMusicButton", 
        "description": "Requires released music."
    }, 
    "workEmotion": {
        "name": "work on emotion", 
        "key": "workEmotion", 
        "tags": [
            "workEmotion"
        ]
    }, 
    "captureThoughtGoal": {
        "key": "captureThoughtGoal", 
        "randomPosition": 0, 
        "name": "Capture Inspiration", 
        "image": "goal_butterfly-000000", 
        "currencyRequested": {
            "thought": 1
        }, 
        "entitiesToMake": "captureInspirationResult", 
        "goalMenuKey": "captureThoughtMenu", 
        "timeToDeath": 60
    }, 
    "killPride": {
        "currencyToKill": "selfEsteem", 
        "key": "killPride"
    }, 
    "selfWorthlessMine": {
        "resourceCount": 1, 
        "name": "Feeling Worthless", 
        "image": "tower_trophy", 
        "key": "selfWorthlessMine", 
        "emotionTags": [
            "self", 
            "sadness"
        ], 
        "resourceKeys": [
            "sadness"
        ], 
        "activeImage": "tower_trophy_activeOutline-0099ff", 
        "description": "makes sadness"
    }, 
    "indulgeBitternessMenu": {
        "image": "goal_pain-000000", 
        "completeText": "You've gotten used to the taste of it on your tongue.", 
        "name": "COMPLETED - Indulge In Bitterness", 
        "key": "indulgeBitternessMenu"
    }, 
    "frustrationMineButton": {
        "constructionKey": "frustrationMineConstruction", 
        "name": "Frustration Mine", 
        "entityCost": {
            "will": 2
        }, 
        "image": "tower_mineFrustration", 
        "currenciesMade": [
            "frustration"
        ], 
        "entityKey": "frustrationMine", 
        "limitingKeys": [
            "frustrationMine", 
            "frustrationMineConstruction"
        ], 
        "key": "frustrationMineButton", 
        "description": "makes frustration"
    }, 
    "dreamFutureMenu": {
        "image": "goal_daydream-000000", 
        "name": "COMPLETED - Dream of Future", 
        "key": "dreamFutureMenu"
    }, 
    "condCaptureInspirationGoal": {
        "entitiesToMake": "captureInspirationGoal", 
        "stopEntities": [
            "emoInspiration"
        ], 
        "key": "condCaptureInspirationGoal"
    }, 
    "restartWorkButton": {
        "constructionKey": "restartWorkGoalConstruction", 
        "name": "Restart Work", 
        "image": "goal_towerThinkAbout-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "restartWorkGoal", 
        "key": "restartWorkButton", 
        "description": "Requires resolve."
    }, 
    "bruteForceTowerConstruction": {
        "key": "bruteForceTowerConstruction", 
        "constructionIdentifier": "bruteForceTower", 
        "name": "Constructing - Brute Force Problem", 
        "image": "trait-aaaaaa99_tower_energy", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Brute Force Problem", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "gainControlMenu": {
        "y": 164, 
        "x": 237
    }, 
    "dreamFutureButton": {
        "constructionKey": "dreamFutureGoalConstruction", 
        "name": "Dream of Future", 
        "image": "goal_daydream-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "dreamFutureGoal", 
        "key": "dreamFutureButton", 
        "description": "Requires cheer."
    }, 
    "acceptAlienationGoal": {
        "key": "acceptAlienationGoal", 
        "randomPosition": 0, 
        "name": "Accept Alienation", 
        "image": "goal_social-000000", 
        "currencyRequested": {
            "strength": 6, 
            "alienation_lover": 6
        }, 
        "entitiesToMake": "hangingOutToAlienation", 
        "goalMenuKey": "acceptAlienationMenu", 
        "description": "You're not the same."
    }, 
    "shareJoyMuseAction": {
        "currenciesRequired": [
            "social_muse"
        ], 
        "towerKey": "shareJoyMuseTower", 
        "name": "Share Joy", 
        "key": "shareJoyMuseAction"
    }, 
    "achievement_ext": {
        "personHeld": "selfCore", 
        "image": [
            "trait-aaff00", 
            "trait-99E500", 
            "trait-BBFF00"
        ], 
        "name": "Achievement", 
        "key": "achievement_ext", 
        "sourceKey": "achievement"
    }, 
    "relaxDeposit": {
        "timeToDeath": 150, 
        "image": "tower_goalPark-000000", 
        "deposit": {
            "relax": 3
        }, 
        "key": "relaxDeposit", 
        "activeImage": "tower_goalPark-000000_activeOutline-0099ff", 
        "name": "Relaxation"
    }, 
    "hangingOut": {
        "name": "Hanging Out", 
        "aliveEntities": {
            "ignoreDifferencesGoal": 5
        }, 
        "image": "trait-ff9933", 
        "personHeld": "lover", 
        "isaacEntities": [
            "endHangOutButton", 
            "talkAboutSelfButton", 
            "talkAboutLoverButton", 
            "relaxButton", 
            "acceptAlienationButton", 
            "fadeHangingOut"
        ], 
        "key": "hangingOut"
    }, 
    "dontGiveUpMenu": {
        "image": "goal_motivation-000000", 
        "completeText": "Bad now doesn't mean bad forever.", 
        "name": "COMPLETED - Don't Give Up", 
        "key": "dontGiveUpMenu"
    }, 
    "easyWorkButton": {
        "constructionKey": "easyWorkTowerConstruction", 
        "name": "Easy Work", 
        "image": "tower_towerDrive", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "easyWorkTower", 
        "limitingKeys": [
            "easyWorkTower", 
            "easyWorkTowerConstruction"
        ], 
        "key": "easyWorkButton", 
        "subPanel": "Towers", 
        "description": "Converts will + easy problem + music -> satisfaction"
    }, 
    "explainCaptureFail": {
        "completeText": "It looks like there was nothing there after all.", 
        "key": "explainCaptureFail"
    }, 
    "startNewSongMenu": {
        "image": "goal_newNote-000000", 
        "name": "COMPLETED - Start New Song", 
        "key": "startNewSongMenu"
    }, 
    "friendToRomance": {
        "sourceTrait": "friend", 
        "newTrait": "romance", 
        "key": "friendToRomance"
    }, 
    "ignoreInsult2Menu": {
        "image": "goal_goalCalmDown-000000", 
        "completeText": "You can be the bigger person.", 
        "name": "COMPLETED - Ignore Insult", 
        "key": "ignoreInsult2Menu"
    }, 
    "insultingMake": {
        "badEntities": [
            "aloneTime", 
            "minorInsultGoal", 
            "ignoreInsult1Goal", 
            "ignoreInsult2Goal"
        ], 
        "key": "insultingMake"
    }, 
    "person": {
        "focusPanelKey": "focusPanel2", 
        "playerKillable": "true"
    }, 
    "cheer": {
        "name": "cheer", 
        "key": "cheer", 
        "tags": [
            "cheer"
        ]
    }, 
    "failureButton": {
        "constructionKey": "failureTowerConstruction", 
        "name": "Think Of Failure", 
        "image": "tower_mineCaution", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "failureTower", 
        "limitingKeys": [
            "failureTower", 
            "failureTowerConstruction"
        ], 
        "key": "failureButton", 
        "subPanel": "Towers", 
        "description": "Converts frustration + pain -> personal failure"
    }, 
    "quitPrideMenu": {
        "image": "goal_weight-000000", 
        "name": "COMPLETED - Strength In Quitting Job", 
        "key": "quitPrideMenu"
    }, 
    "workSatisfactionMine": {
        "resourceCount": 1, 
        "name": "Satisfaction With Work", 
        "image": "tower_puzzle", 
        "key": "workSatisfactionMine", 
        "emotionTags": [
            "work", 
            "joy"
        ], 
        "resourceKeys": [
            "joy"
        ], 
        "activeImage": "tower_puzzle_activeOutline-0099ff", 
        "description": "makes joy"
    }, 
    "dreamOfGreatnessMenu": {
        "image": "goal_daydream-000000", 
        "name": "COMPLETED - Dream of Greatness", 
        "key": "dreamOfGreatnessMenu"
    }, 
    "restartWorkMenu": {
        "image": "goal_towerThinkAbout-000000", 
        "name": "COMPLETED - Restart Work", 
        "key": "restartWorkMenu"
    }, 
    "joyFleshAction": {
        "currenciesRequired": [
            "cheer"
        ], 
        "towerKey": "joyFleshTower", 
        "currenciesToMake": [
            "songSatisfaction"
        ], 
        "name": "Flesh Out With Joy", 
        "key": "joyFleshAction"
    }, 
    "nascentSong_ext": {
        "personHeld": "song", 
        "image": [
            "trait-aa3399", 
            "trait-992D89", 
            "trait-BB38A8"
        ], 
        "name": "Nascent Song", 
        "key": "nascentSong_ext", 
        "sourceKey": "nascentSong"
    }, 
    "damageIndicator": {
        "timeToDeath": 5
    }, 
    "aloneTime": {
        "timeToDeath": 30, 
        "name": "The lover just needs some time alone.", 
        "key": "aloneTime", 
        "traitsToDisable": [
            "loverBase"
        ]
    }, 
    "chapter2Menu": {
        "image": "activeHex", 
        "name": "Chapter 2 - Where Next?", 
        "key": "chapter2Menu"
    }, 
    "envyMenu": {
        "image": "goal_depositSin-000000", 
        "name": "COMPLETED - Give In To Envy", 
        "key": "envyMenu"
    }, 
    "cynicismTower": {
        "name": "Cynicism", 
        "image": "tower_goalSpy-000000", 
        "damageMultipliers": {
            "anger": 1, 
            "joy": 1, 
            "sadness": 1
        }, 
        "key": "cynicismTower", 
        "activeImage": "tower_goalSpy-000000_activeOutline-0099ff", 
        "description": "Uses will to calm anger, joy, sadness"
    }, 
    "tryComplimentsButton": {
        "constructionKey": "tryComplimentsGoalConstruction", 
        "name": "Try For Compliments", 
        "image": "goal_hook-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "tryComplimentsGoal", 
        "key": "tryComplimentsButton", 
        "description": "Requires social, music."
    }, 
    "unhappy_ext": {
        "personHeld": "lover", 
        "image": [
            "trait-aa00cc", 
            "trait-9900B7", 
            "trait-BB00E0"
        ], 
        "name": "Unhappy", 
        "key": "unhappy_ext", 
        "sourceKey": "unhappy"
    }, 
    "fadeEmoInspiration": {
        "timeToDeath": 300, 
        "key": "fadeEmoInspiration", 
        "makeOnTimeOut": "sustainEmoInspirationGoal"
    }, 
    "alienationMineConstruction": {
        "key": "alienationMineConstruction", 
        "constructionIdentifier": "alienationMine", 
        "name": "Constructing - Alienation", 
        "image": "trait-aaaaaa99_tower_pain", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Alienation", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "techSolution": {
        "name": "tech solution", 
        "key": "techSolution", 
        "tags": [
            "techSolution"
        ]
    }, 
    "startNewSongButton": {
        "constructionKey": "startNewSongGoalConstruction", 
        "name": "Start New Song", 
        "image": "goal_newNote-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "startNewSongGoal", 
        "disablingEntities": [
            "nascentSong", 
            "song", 
            "sadSong", 
            "goodSong"
        ], 
        "key": "startNewSongButton", 
        "description": "Requires will, music."
    }, 
    "faceEmotionMenu": {
        "image": "goal_weight-000000", 
        "name": "COMPLETED - Face Emotion", 
        "key": "faceEmotionMenu"
    }, 
    "solveMusicResult": {
        "goodEntities": {
            "techInspiration": 4
        }, 
        "badEntities": [
            "fullStuckProblem", 
            "revertFreshMusic"
        ], 
        "key": "solveMusicResult"
    }, 
    "achievementMenu": {
        "image": "goal_trophy-000000", 
        "name": "COMPLETED - Note Achievement", 
        "key": "achievementMenu"
    }, 
    "hateWorkPathSelf": {
        "startKey": "hateWork", 
        "endKey": "self", 
        "key": "hateWorkPathSelf"
    }, 
    "keepTalkGoingGoal": {
        "key": "keepTalkGoingGoal", 
        "randomPosition": 1, 
        "name": "Keep Things Going", 
        "makeOnTimeOut": "realTalkToFriend", 
        "image": "goal_goalPark-000000", 
        "sourceTrait": "realTalk", 
        "currencyRequested": {
            "social_lover": 3
        }, 
        "entitiesToMake": "fadeRealTalk", 
        "goalMenuKey": "keepTalkGoingMenu", 
        "timeToDeath": 120
    }, 
    "loverFleshButton": {
        "constructionKey": "loverFleshTowerConstruction", 
        "name": "Flesh Out With Lover", 
        "image": "tower_mineSocial", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "loverFleshTower", 
        "limitingKeys": [
            "loverFleshTower", 
            "loverFleshTowerConstruction"
        ], 
        "key": "loverFleshButton", 
        "subPanel": "Towers", 
        "description": "Converts need -> satisfaction"
    }, 
    "refineInspirationGoal": {
        "currencyEntities": {
            "workSatisfaction": 50
        }, 
        "randomPosition": 0, 
        "name": "Refine Raw Inspiration", 
        "image": "goal_towerThinkAbout-000000", 
        "key": "refineInspirationGoal", 
        "currencyRequested": {
            "rawInspiration": 3, 
            "thought": 3
        }, 
        "entitiesToMake": "makeRandomInspiration", 
        "goalMenuKey": "refineInspirationMenu"
    }, 
    "alienationMineButton": {
        "constructionKey": "alienationMineConstruction", 
        "name": "Alienation", 
        "entityCost": {
            "will": 2
        }, 
        "image": "tower_pain", 
        "currenciesMade": [
            "alienation_lover"
        ], 
        "entityKey": "alienationMine", 
        "limitingKeys": [
            "alienationMine", 
            "alienationMineConstruction"
        ], 
        "key": "alienationMineButton", 
        "description": "makes alienation"
    }, 
    "musicMineConstruction": {
        "key": "musicMineConstruction", 
        "constructionIdentifier": "musicMine", 
        "name": "Constructing - Music", 
        "image": "trait-aaaaaa99_tower_note", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Music", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "captureSocialMenu": {
        "image": "goal_butterfly-000000", 
        "name": "COMPLETED - Capture Inspiration", 
        "key": "captureSocialMenu"
    }, 
    "songToSadSong": {
        "sourceTrait": "song", 
        "newTrait": "sadSong", 
        "key": "songToSadSong"
    }, 
    "dismissFailureButton": {
        "constructionKey": "dismissFailureGoalConstruction", 
        "name": "Dismiss Failures", 
        "image": "goal_goalCleaner-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "dismissFailureGoal", 
        "key": "dismissFailureButton", 
        "description": "Requires self-esteem."
    }, 
    "largeSongSatisfactionDeposit": {
        "image": "tower_note", 
        "activeImage": "tower_note_activeOutline-0099ff", 
        "name": "Song Satisfaction", 
        "key": "largeSongSatisfactionDeposit", 
        "deposit": {
            "songSatisfaction": 6
        }
    }, 
    "gameStartMenu": {
        "completeText": [
            "Things weren't what you wanted them to be. It's time to start fresh!", 
            "Go show everyone that you can be a success! Good luck!"
        ], 
        "name": "START!", 
        "key": "gameStartMenu", 
        "height": 675
    }, 
    "techWorkTower": {
        "image": "tower_thinkProblem", 
        "activeImage": "tower_thinkProblem_activeOutline-0099ff", 
        "name": "Complete Tech Work", 
        "key": "techWorkTower", 
        "description": "Converts technical issue + tech solution -> hard work"
    }, 
    "selfEsteemDeposit": {
        "image": "tower_medal", 
        "activeImage": "tower_medal_activeOutline-0099ff", 
        "name": "Self-Esteem", 
        "key": "selfEsteemDeposit", 
        "deposit": {
            "selfEsteem": 6
        }
    }, 
    "indulgeBitternessButton": {
        "constructionKey": "indulgeBitternessGoalConstruction", 
        "name": "Indulge In Bitterness", 
        "image": "goal_pain-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "indulgeBitternessGoal", 
        "key": "indulgeBitternessButton", 
        "description": "Requires pain, personal failure."
    }, 
    "joyMap": {
        "startManagedEntities": [
            "cheerMine"
        ], 
        "emotionToMap": "joy", 
        "emotionMappedEntities": [
            "rechargeButton", 
            "joyRideButton", 
            "optimismButton", 
            "joyMemoryFade", 
            "workThroughButton", 
            "cheerPrideButton"
        ], 
        "key": "joyMap"
    }, 
    "deadInsideTowerConstruction": {
        "key": "deadInsideTowerConstruction", 
        "constructionIdentifier": "deadInsideTower", 
        "name": "Constructing - Numbness", 
        "image": "trait-aaaaaa99_tower_numb", 
        "currencyRequested": {
            "pain": 5
        }, 
        "constructionName": "Numbness", 
        "killOnTransform": 0
    }, 
    "minorFlirtMenu": {
        "image": "goal_heart-000000", 
        "completeText": "You can't help but smile from this.", 
        "name": "COMPLETED - Minor Flirtation", 
        "key": "minorFlirtMenu"
    }, 
    "meetMuseMenu": {
        "image": "trait-ede9e6_goal_towerFlirt-000000", 
        "name": "COMPLETED - Meet Fan!", 
        "key": "meetMuseMenu"
    }, 
    "startNewSongGoalConstruction": {
        "key": "startNewSongGoalConstruction", 
        "constructionIdentifier": "startNewSongGoal", 
        "name": "Constructing - Start New Song", 
        "image": "trait-aaaaaa99_goal_newNote-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Start New Song", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "dreamFutureGoalConstruction": {
        "key": "dreamFutureGoalConstruction", 
        "constructionIdentifier": "dreamFutureGoal", 
        "name": "Constructing - Dream of Future", 
        "image": "trait-aaaaaa99_goal_daydream-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Dream of Future", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "cheerMineConstruction": {
        "key": "cheerMineConstruction", 
        "constructionIdentifier": "cheerMine", 
        "name": "Constructing - Cheer Mine", 
        "image": "trait-aaaaaa99_tower_mineCheer", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Cheer Mine", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "prideStartButton": {
        "constructionKey": "prideStartGoalConstruction", 
        "name": "Be Proud Of This", 
        "image": "goal_trophy-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "prideStartGoal", 
        "key": "prideStartButton", 
        "description": [
            "Requires will, social.", 
            "Makes self-esteem."
        ]
    }, 
    "alienation_lover": {
        "name": "alienation", 
        "key": "alienation_lover", 
        "tags": [
            "alienation_lover"
        ]
    }, 
    "explainApologyFail": {
        "completeText": "That wasn't anything like enough to placate the lover.", 
        "key": "explainApologyFail"
    }, 
    "rememberButton": {
        "constructionKey": "rememberGoalConstruction", 
        "name": "Remember Being Alone", 
        "image": "goal_social-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "rememberGoal", 
        "key": "rememberButton", 
        "description": "Requires will."
    }, 
    "hangingOut_ext": {
        "personHeld": "lover", 
        "image": [
            "trait-ff9933", 
            "trait-E5892D", 
            "trait-FFA838"
        ], 
        "name": "Hanging Out", 
        "key": "hangingOut_ext", 
        "sourceKey": "hangingOut"
    }, 
    "sadFleshButton": {
        "constructionKey": "sadFleshTowerConstruction", 
        "name": "Flesh Out With Sadness", 
        "image": "tower_pain", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "sadFleshTower", 
        "limitingKeys": [
            "sadFleshTower", 
            "sadFleshTowerConstruction"
        ], 
        "key": "sadFleshButton", 
        "subPanel": "Towers", 
        "description": "Converts pain -> satisfaction"
    }, 
    "songSatisfiedMenu": {
        "image": "goal_coolNote-000000", 
        "completeText": "Now, this is a song that you can show people.", 
        "name": "COMPLETED - Check Song Quality", 
        "key": "songSatisfiedMenu"
    }, 
    "angryMemoryFade": {
        "newTrait": "fadingAngryMemory", 
        "key": "angryMemoryFade", 
        "timeToDeath": 450
    }, 
    "painMineConstruction": {
        "key": "painMineConstruction", 
        "constructionIdentifier": "painMine", 
        "name": "Constructing - Pain Mine", 
        "image": "trait-aaaaaa99_tower_pain", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Pain Mine", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "museVentAction": {
        "currenciesRequired": [
            "social_muse"
        ], 
        "towerKey": "museVentTower", 
        "name": "Vent To", 
        "key": "museVentAction"
    }, 
    "envyButton": {
        "constructionKey": "envyGoalConstruction", 
        "name": "Give In To Envy", 
        "image": "goal_depositSin-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "envyGoal", 
        "key": "envyButton", 
        "description": "Requires will."
    }, 
    "demons_ext": {
        "personHeld": "demon", 
        "image": [
            "trait-993333", 
            "trait-892D2D", 
            "trait-A83838"
        ], 
        "name": "Inner Demons", 
        "key": "demons_ext", 
        "sourceKey": "demons"
    }, 
    "success": {
        "name": "success", 
        "key": "success", 
        "tags": [
            "success"
        ]
    }, 
    "desireImprovementButton": {
        "constructionKey": "desireImprovementGoalConstruction", 
        "name": "Desire Self-Improvement", 
        "image": "goal_drive-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "desireImprovementGoal", 
        "key": "desireImprovementButton", 
        "description": [
            "Requires dream of greatness, personal failure.", 
            "Makes desire self-improvement."
        ]
    }, 
    "loverCheck": {
        "qualityKey": "relationshipMine", 
        "minQualityEntities": "gameEnd", 
        "minQuality": 2, 
        "key": "loverCheck"
    }, 
    "memory_workSatisfaction": {
        "image": "trait-e6e617", 
        "memoryEmotions": [
            "joy"
        ], 
        "key": "memory_workSatisfaction", 
        "name": "Satisfaction With Work"
    }, 
    "resolveStrengthButton": {
        "constructionKey": "resolveStrengthTowerConstruction", 
        "name": "Gain Resolve", 
        "image": "tower_drive", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "resolveStrengthTower", 
        "limitingKeys": [
            "resolveStrengthTower", 
            "resolveStrengthTowerConstruction"
        ], 
        "key": "resolveStrengthButton", 
        "subPanel": "Towers", 
        "description": [
            "If it has to just be you, then it will be just you.", 
            "Converts strength -> resolve"
        ]
    }, 
    "shareJoyButton": {
        "constructionKey": "shareJoyTowerConstruction", 
        "name": "Share Joy", 
        "image": "tower_towerTalk", 
        "entityCost": {
            "sharing_lover": 5
        }, 
        "entityKey": "shareJoyTower", 
        "limitingKeys": [
            "shareJoyTower", 
            "shareJoyTowerConstruction"
        ], 
        "key": "shareJoyButton", 
        "subPanel": "Defenses", 
        "description": "Uses sharing to calm joy"
    }, 
    "keepGoingGoal": {
        "key": "keepGoingGoal", 
        "randomPosition": 1, 
        "name": "Keep Things Going", 
        "makeOnTimeOut": "hangingOutToFriend", 
        "image": "goal_goalPark-000000", 
        "sourceTrait": "hangingOut", 
        "currencyRequested": {
            "social_lover": 3
        }, 
        "entitiesToMake": "fadeHangingOut", 
        "goalMenuKey": "keepGoingMenu", 
        "timeToDeath": 120
    }, 
    "workSatisfaction": {
        "emotionsToAdd": {
            "joy": 1
        }, 
        "name": "Satisfaction With Work", 
        "image": "tower_puzzle", 
        "key": "workSatisfaction", 
        "memoryKey": "memory_workSatisfaction", 
        "emotionTags": "work", 
        "resourceKey": "workSatisfactionMineConstruction", 
        "activeImage": "tower_puzzle_activeOutline-0099ff"
    }, 
    "personCallousnessButton": {
        "constructionKey": "personCallousnessTowerConstruction", 
        "name": "Callousness", 
        "image": "tower_numb", 
        "entityCost": {
            "strength": 5
        }, 
        "entityKey": "personCallousnessTower", 
        "limitingKeys": [
            "personCallousnessTower", 
            "personCallousnessTowerConstruction"
        ], 
        "key": "personCallousnessButton", 
        "subPanel": "Defenses", 
        "description": "Uses strength to calm anger, joy, sadness"
    }, 
    "lackAccomplishmentTowerConstruction": {
        "key": "lackAccomplishmentTowerConstruction", 
        "constructionIdentifier": "lackAccomplishmentTower", 
        "name": "Constructing - Lack of Accomplishment", 
        "image": "trait-aaaaaa99_tower_lackAccomplishment", 
        "currencyRequested": {
            "selfLoathing": 5
        }, 
        "constructionName": "Lack of Accomplishment", 
        "killOnTransform": 0
    }, 
    "drainedSadMine": {
        "resourceCount": 2, 
        "name": "Feeling Drained", 
        "image": "tower_energy", 
        "key": "drainedSadMine", 
        "emotionTags": [
            "work", 
            "sadness"
        ], 
        "resourceKeys": [
            "sadness"
        ], 
        "activeImage": "tower_energy_activeOutline-0099ff", 
        "description": [
            "It takes energy not to be sad and you don't have that right now. -", 
            "makes sadness"
        ]
    }, 
    "joyFleshTowerConstruction": {
        "key": "joyFleshTowerConstruction", 
        "constructionIdentifier": "joyFleshTower", 
        "name": "Constructing - Flesh Out With Joy", 
        "image": "trait-aaaaaa99_tower_mineCheer", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Flesh Out With Joy", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "listenDemonsButton": {
        "constructionKey": "listenDemonsGoalConstruction", 
        "name": "Listen To Demons", 
        "image": "goal_memoryProgramming-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "listenDemonsGoal", 
        "key": "listenDemonsButton", 
        "description": "Requires will."
    }, 
    "optimismAction": {
        "currenciesRequired": [
            "cheer"
        ], 
        "towerKey": "optimismTower", 
        "name": "Optimism", 
        "key": "optimismAction"
    }, 
    "ambition_ext": {
        "personHeld": "selfCore", 
        "image": [
            "trait-99ff00", 
            "trait-89E500", 
            "trait-A8FF00"
        ], 
        "name": "Ambition", 
        "key": "ambition_ext", 
        "sourceKey": "ambition"
    }, 
    "talkTower": {
        "name": "Vent To", 
        "image": "tower_mineFrustration", 
        "damageMultipliers": {
            "anger": 1, 
            "sadness": 1
        }, 
        "key": "talkTower", 
        "activeImage": "tower_mineFrustration_activeOutline-0099ff", 
        "description": "Uses care to calm anger, sadness"
    }, 
    "tryComplimentsGoalConstruction": {
        "key": "tryComplimentsGoalConstruction", 
        "constructionIdentifier": "tryComplimentsGoal", 
        "name": "Constructing - Try For Compliments", 
        "image": "trait-aaaaaa99_goal_hook-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Try For Compliments", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "goodSong_ext": {
        "personHeld": "song", 
        "image": [
            "trait-cc33cc", 
            "trait-B72DB7", 
            "trait-E038E0"
        ], 
        "name": "Great Song!", 
        "key": "goodSong_ext", 
        "sourceKey": "goodSong"
    }, 
    "curious": {
        "personHeld": "music", 
        "isaacEntities": [
            "thoughtButton", 
            "makePlaythingButton", 
            "playWithMusicButton", 
            "sinkWorkButton"
        ], 
        "image": "trait-00aaff", 
        "name": "Curiosity", 
        "key": "curious"
    }, 
    "flashback_joyConstruction": {
        "key": "flashback_joyConstruction", 
        "constructionIdentifier": "flashback_joy", 
        "name": "Flashback", 
        "image": "tower_flashback-000000-666666aa", 
        "currencyRequested": {
            "impulse": 1
        }, 
        "constructionName": "Flashback"
    }, 
    "creep": {
        "planeOfExistence": "path"
    }, 
    "optimismButton": {
        "constructionKey": "optimismTowerConstruction", 
        "name": "Optimism", 
        "image": "tower_sun", 
        "entityCost": {
            "cheer": 5
        }, 
        "entityKey": "optimismTower", 
        "limitingKeys": [
            "optimismTower", 
            "optimismTowerConstruction"
        ], 
        "key": "optimismButton", 
        "description": "Uses cheer to calm sadness, anger"
    }, 
    "demon1Goal": {
        "key": "demon1Goal", 
        "randomPosition": 1, 
        "name": "Quiet Inner Demon", 
        "makeOnTimeOut": "selfWorthless", 
        "image": "goal_goalActivation-000000", 
        "description": "Of course you feel worthless. You are worthless.", 
        "sourceTrait": "demons", 
        "currencyRequested": {
            "love_lover": 3
        }, 
        "goalMenuKey": "demon1Menu", 
        "timeToDeath": 120
    }, 
    "overcomeExhaustMenu": {
        "image": "goal_goalPark-000000", 
        "name": "COMPLETED - Overcome Exhaustion", 
        "key": "overcomeExhaustMenu"
    }, 
    "completeEmoMineConstruction": {
        "key": "completeEmoMineConstruction", 
        "constructionIdentifier": "completeEmoMine", 
        "name": "Constructing - Completion Desire", 
        "image": "trait-aaaaaa99_tower_mineWill", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Completion Desire", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "rageTowerConstruction": {
        "key": "rageTowerConstruction", 
        "constructionIdentifier": "rageTower", 
        "name": "Constructing - Rage", 
        "image": "trait-aaaaaa99_tower_towerDisavowal", 
        "currencyRequested": {
            "frustration": 5
        }, 
        "constructionName": "Rage", 
        "killOnTransform": 0
    }, 
    "memory_selfWorthless": {
        "image": "trait-013d70", 
        "memoryEmotions": [
            "sadness"
        ], 
        "key": "memory_selfWorthless", 
        "name": "Feeling Worthless"
    }, 
    "sustainFreshMusicMenu": {
        "image": "goal_mineCaution-000000", 
        "name": "COMPLETED - Keep Fresh Music Alive", 
        "key": "sustainFreshMusicMenu"
    }, 
    "acceptDemonButton": {
        "constructionKey": "acceptDemonGoalConstruction", 
        "name": "Accept Truth", 
        "image": "goal_trophy-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "acceptDemonGoal", 
        "key": "acceptDemonButton", 
        "description": "Requires will."
    }, 
    "failureTowerConstruction": {
        "key": "failureTowerConstruction", 
        "constructionIdentifier": "failureTower", 
        "name": "Constructing - Think Of Failure", 
        "image": "trait-aaaaaa99_tower_mineCaution", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Think Of Failure", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "desireImprovement": {
        "name": "desire self-improvement", 
        "key": "desireImprovement", 
        "tags": [
            "desireImprovement"
        ]
    }, 
    "lackAccomplishmentTower": {
        "name": "Lack of Accomplishment", 
        "image": "tower_lackAccomplishment", 
        "damageMultipliers": {
            "joy": 1
        }, 
        "key": "lackAccomplishmentTower", 
        "activeImage": "tower_lackAccomplishment_activeOutline-0099ff", 
        "description": "Uses selfLoathing to calm joy"
    }, 
    "refineAccomplishmentTowerConstruction": {
        "key": "refineAccomplishmentTowerConstruction", 
        "constructionIdentifier": "refineAccomplishmentTower", 
        "name": "Constructing - Refine Accomplishment", 
        "image": "trait-aaaaaa99_tower_towerThinkAbout", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Refine Accomplishment", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "reflectAloneGoalConstruction": {
        "key": "reflectAloneGoalConstruction", 
        "constructionIdentifier": "reflectAloneGoal", 
        "name": "Constructing - Reflect on Being Alone", 
        "image": "trait-aaaaaa99_goal_social-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Reflect on Being Alone", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "sadRideGoalConstruction": {
        "key": "sadRideGoalConstruction", 
        "constructionIdentifier": "sadRideGoal", 
        "name": "Constructing - Explore Sadness", 
        "image": "trait-aaaaaa99_goalFightProgramming-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Explore Sadness", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "loverFleshAction": {
        "currenciesRequired": [
            "need_lover"
        ], 
        "towerKey": "loverFleshTower", 
        "currenciesToMake": [
            "songSatisfaction"
        ], 
        "name": "Flesh Out With Lover", 
        "key": "loverFleshAction"
    }, 
    "dreamOfGreatnessButton": {
        "constructionKey": "dreamOfGreatnessGoalConstruction", 
        "name": "Dream of Greatness", 
        "image": "goal_daydream-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "dreamOfGreatnessGoal", 
        "key": "dreamOfGreatnessButton", 
        "description": [
            "Requires love.", 
            "Makes dream of greatness."
        ]
    }, 
    "cheerMine": {
        "name": "Cheer Mine", 
        "image": "tower_mineCheer", 
        "key": "cheerMine", 
        "resourceKeys": [
            "cheer"
        ], 
        "resourceEntities": {
            "obnoxiousGoal": 5
        }, 
        "activeImage": "tower_mineCheer_activeOutline-0099ff"
    }, 
    "difficultySetting": {
        "key": "difficultySetting"
    }, 
    "overcomeExhaustGoalConstruction": {
        "key": "overcomeExhaustGoalConstruction", 
        "constructionIdentifier": "overcomeExhaustGoal", 
        "name": "Constructing - Overcome Exhaustion", 
        "image": "trait-aaaaaa99_goal_goalPark-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Overcome Exhaustion", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "refineInspirationGoalConstruction": {
        "key": "refineInspirationGoalConstruction", 
        "constructionIdentifier": "refineInspirationGoal", 
        "name": "Constructing - Refine Raw Inspiration", 
        "image": "trait-aaaaaa99_goal_towerThinkAbout-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Refine Raw Inspiration", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "nascentSongTraitConnectMusic": {
        "startKey": "nascentSong", 
        "endKey": "music", 
        "key": "nascentSongTraitConnectMusic"
    }, 
    "fadingSadMemory": {
        "image": "trait-9999cc", 
        "name": "Fading Sad Memory", 
        "key": "fadingSadMemory"
    }, 
    "gainControl_anger": {
        "barColor": "cc9999", 
        "text": "You're just too angry to think right now.", 
        "textColor": 660000, 
        "key": "gainControl_anger"
    }, 
    "killDeriveCoolThingButton": {
        "entitiesToKill": [
            "deriveCoolThingButton"
        ], 
        "key": "killDeriveCoolThingButton"
    }, 
    "ignoreInsult1Goal": {
        "currencyEntities": "loverInsult", 
        "randomPosition": 1, 
        "name": "Ignore Insult", 
        "makeOnTimeOut": [
            "selfWorthless", 
            "killPride"
        ], 
        "image": "goal_goalCalmDown-000000", 
        "description": "Your lover is listening to other people's music.", 
        "key": "ignoreInsult1Goal", 
        "sourceTrait": "loverBase", 
        "currencyRequested": {
            "alienation_lover": 3
        }, 
        "goalMenuKey": "ignoreInsult1Menu", 
        "timeToDeath": 240
    }, 
    "sayRightButton": {
        "constructionKey": "sayRightTowerConstruction", 
        "name": "Saying The Right Things", 
        "image": "tower_understand", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "sayRightTower", 
        "limitingKeys": [
            "sayRightTower", 
            "sayRightTowerConstruction"
        ], 
        "key": "sayRightButton", 
        "subPanel": "Towers", 
        "description": "Converts understanding + social -> right words"
    }, 
    "explainMajorMenu": {
        "completeText": [
            "You let a problem fester for too long and it doesn't look like you", 
            "can walk it back now."
        ], 
        "name": "Relationship Problems", 
        "key": "explainMajorMenu"
    }, 
    "discardFreshMusicGoalConstruction": {
        "key": "discardFreshMusicGoalConstruction", 
        "constructionIdentifier": "discardFreshMusicGoal", 
        "name": "Constructing - Discard Fresh Music", 
        "image": "trait-aaaaaa99_goal_discard-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Discard Fresh Music", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "toldWorthGoalConstruction": {
        "key": "toldWorthGoalConstruction", 
        "constructionIdentifier": "toldWorthGoal", 
        "name": "Constructing - Be Told Of Your Worth", 
        "image": "trait-aaaaaa99_goal_trophy-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Be Told Of Your Worth", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "quietDemons_ext": {
        "personHeld": "demon", 
        "image": [
            "trait-662222", 
            "trait-5B1E1E", 
            "trait-702525"
        ], 
        "name": "Quieted Demons", 
        "key": "quietDemons_ext", 
        "sourceKey": "quietDemons"
    }, 
    "selfLoathing_ext": {
        "personHeld": "selfCore", 
        "image": [
            "trait-66ff00", 
            "trait-5BE500", 
            "trait-70FF00"
        ], 
        "name": "Self-Loathing", 
        "key": "selfLoathing_ext", 
        "sourceKey": "selfLoathing"
    }, 
    "struggleComplimentsButton": {
        "constructionKey": "struggleComplimentsGoalConstruction", 
        "name": "Hope For Compliments", 
        "image": "goal_hook-000000", 
        "description": "Requires social, music.", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "struggleComplimentsGoal", 
        "key": "struggleComplimentsButton", 
        "displayRiskIcon": 1
    }, 
    "integrateGoal": {
        "currencyEntities": {
            "workSatisfaction": 25
        }, 
        "randomPosition": 0, 
        "name": "Integrate Hard Work", 
        "image": "goal_towerThinkAbout-000000", 
        "key": "integrateGoal", 
        "currencyRequested": {
            "hardWork": 3
        }, 
        "entitiesToMake": "largeSongSatisfactionDeposit", 
        "goalMenuKey": "integrateMenu"
    }, 
    "playthingDeposit": {
        "image": "tower_dice", 
        "activeImage": "tower_dice_activeOutline-0099ff", 
        "name": "Playthings", 
        "key": "playthingDeposit", 
        "deposit": {
            "plaything": 3
        }
    }, 
    "refineAccomplishmentAction": {
        "currenciesRequired": [
            "accomplishment", 
            "will"
        ], 
        "towerKey": "refineAccomplishmentTower", 
        "currenciesToMake": [
            "selfEsteem"
        ], 
        "name": "Refine Accomplishment", 
        "key": "refineAccomplishmentAction"
    }, 
    "timeOutMenu": {
        "endGame": 1, 
        "completeText": [
            "If you still are not a success, it doesn't look like you will ever be", 
            "one. It's time to try something else."
        ], 
        "key": "timeOutMenu"
    }, 
    "accomplishmentDeposit": {
        "image": "tower_trophy", 
        "activeImage": "tower_trophy_activeOutline-0099ff", 
        "name": "Accomplishment", 
        "key": "accomplishmentDeposit", 
        "deposit": {
            "accomplishment": 3
        }
    }, 
    "chapter3Menu": {
        "image": "activeHex", 
        "name": "Chapter 3 - Every Single Day", 
        "key": "chapter3Menu"
    }, 
    "achievementToAmbition": {
        "sourceTrait": "achievement", 
        "newTrait": "ambition", 
        "key": "achievementToAmbition"
    }, 
    "dependence": {
        "name": "dependence", 
        "key": "dependence", 
        "tags": [
            "dependence"
        ]
    }, 
    "talkAboutLoverMenu": {
        "image": "goal_goalChat-000000-000000", 
        "name": "COMPLETED - Talk About Lover", 
        "key": "talkAboutLoverMenu"
    }, 
    "keepGoingMenu": {
        "image": "goal_goalPark-000000", 
        "name": "COMPLETED - Keep Things Going", 
        "key": "keepGoingMenu"
    }, 
    "hardWork": {
        "name": "hard work", 
        "key": "hardWork", 
        "tags": [
            "hardWork"
        ]
    }, 
    "deriveInsightButton": {
        "constructionKey": "deriveInsightGoalConstruction", 
        "name": "Derive Insight", 
        "image": "goal_towerThinkAbout-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "deriveInsightGoal", 
        "key": "deriveInsightButton", 
        "description": [
            "Requires thought.", 
            "Makes insight."
        ]
    }, 
    "findNewMusicGoalConstruction": {
        "key": "findNewMusicGoalConstruction", 
        "constructionIdentifier": "findNewMusicGoal", 
        "name": "Constructing - Listen To Music Together", 
        "image": "trait-aaaaaa99_goal_cd-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Listen To Music Together", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "fleshSongGoal": {
        "currencyEntities": {
            "workSatisfaction": 25
        }, 
        "randomPosition": 0, 
        "name": "Flesh Out Song", 
        "image": "goal_coolNote-000000", 
        "key": "fleshSongGoal", 
        "currencyRequested": {
            "songSatisfaction": 3
        }, 
        "entitiesToMake": "fleshResult", 
        "goalMenuKey": "fleshSongMenu"
    }, 
    "explainCapturedInspiration": {
        "completeText": "It was tough to pin down, but you think you've got the core of it.", 
        "key": "explainCapturedInspiration"
    }, 
    "destinyMenu": {
        "image": "goal_daydream-000000", 
        "name": "COMPLETED - Believe In Future", 
        "key": "destinyMenu"
    }, 
    "shareJoyTowerConstruction": {
        "key": "shareJoyTowerConstruction", 
        "constructionIdentifier": "shareJoyTower", 
        "name": "Constructing - Share Joy", 
        "image": "trait-aaaaaa99_tower_towerTalk", 
        "currencyRequested": {
            "sharing_lover": 5
        }, 
        "constructionName": "Share Joy", 
        "killOnTransform": 0
    }, 
    "needTimeGoal": {
        "key": "needTimeGoal", 
        "randomPosition": 1, 
        "name": "Needs Time With You", 
        "makeOnTimeOut": "loverSad", 
        "image": "goal_goalAskAbout-000000", 
        "description": "The lover just wants a little time with you.", 
        "sourceTrait": "friend", 
        "currencyRequested": {
            "social_lover": 3
        }, 
        "entitiesToMake": "friendToHangingOut", 
        "goalMenuKey": "needTimeMenu", 
        "timeToDeath": 120
    }, 
    "achievementGoal": {
        "key": "achievementGoal", 
        "randomPosition": 0, 
        "name": "Note Achievement", 
        "image": "goal_trophy-000000", 
        "currencyRequested": {
            "will": 3
        }, 
        "entitiesToMake": [
            "accomplishmentDeposit", 
            "killAchievementButton"
        ], 
        "goalMenuKey": "achievementMenu", 
        "description": "It's abnormally hard to get yourself to accept your achievements."
    }, 
    "largePrideDeposit": {
        "image": "tower_medal", 
        "activeImage": "tower_medal_activeOutline-0099ff", 
        "name": "Self-Esteem", 
        "key": "largePrideDeposit", 
        "deposit": {
            "selfEsteem": 12
        }
    }, 
    "bitterness_ext": {
        "personHeld": "selfCore", 
        "image": [
            "trait-33cc00", 
            "trait-2DB700", 
            "trait-38E000"
        ], 
        "name": "Bitterness", 
        "key": "bitterness_ext", 
        "sourceKey": "bitterness"
    }, 
    "drainedSadMineConstruction": {
        "key": "drainedSadMineConstruction", 
        "constructionIdentifier": "drainedSadMine", 
        "name": "Feeling Drained", 
        "image": "tower_energy-666666aa", 
        "currencyRequested": {
            "impulse": 1
        }, 
        "constructionName": "Feeling Drained"
    }, 
    "emoProblem": {
        "name": "emotional issue", 
        "key": "emoProblem", 
        "tags": [
            "emoProblem"
        ]
    }, 
    "loveStrengthButton": {
        "constructionKey": "loveStrengthTowerConstruction", 
        "name": "Derive Strength", 
        "image": "tower_weight", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "loveStrengthTower", 
        "limitingKeys": [
            "loveStrengthTower", 
            "loveStrengthTowerConstruction"
        ], 
        "key": "loveStrengthButton", 
        "subPanel": "Towers", 
        "description": "Converts love -> strength"
    }, 
    "resourceModifier": {
        "timeToDeath": 15
    }, 
    "tellQuitGoal": {
        "key": "tellQuitGoal", 
        "randomPosition": 0, 
        "name": "Speak of New Start", 
        "image": "goal_trophy-000000", 
        "currencyRequested": {
            "social_lover": 3
        }, 
        "entitiesToMake": [
            "killTellQuitButton", 
            "romance", 
            "friend"
        ], 
        "goalMenuKey": "tellQuitMenu", 
        "description": [
            "The lover always wanted you to follow your dreams", 
            "and now you finally are."
        ]
    }, 
    "ambitionToSelfReliance": {
        "sourceTrait": "ambition", 
        "newTrait": "selfReliance", 
        "key": "ambitionToSelfReliance"
    }, 
    "personCallousnessTower": {
        "name": "Callousness", 
        "image": "tower_numb", 
        "damageMultipliers": {
            "anger": 1, 
            "joy": 1, 
            "sadness": 1
        }, 
        "key": "personCallousnessTower", 
        "activeImage": "tower_numb_activeOutline-0099ff", 
        "description": "Uses strength to calm anger, joy, sadness"
    }, 
    "makeEmoInspirationMenu": {
        "image": "goal_goalFightProgramming-000000-000000", 
        "name": "COMPLETED - Get Inspiration From", 
        "key": "makeEmoInspirationMenu"
    }, 
    "pushYourselfMineButton": {
        "constructionKey": "pushYourselfMineConstruction", 
        "name": "Push Yourself", 
        "entityCost": {
            "will": 2
        }, 
        "image": "tower_mineWill", 
        "currenciesMade": [
            "will"
        ], 
        "entityKey": "pushYourselfMine", 
        "limitingKeys": [
            "pushYourselfMine", 
            "pushYourselfMineConstruction"
        ], 
        "key": "pushYourselfMineButton", 
        "description": "makes will"
    }, 
    "needLoverButton": {
        "constructionKey": "needLoverTowerConstruction", 
        "name": "Express Need For Lover", 
        "image": "tower_dependence", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "needLoverTower", 
        "limitingKeys": [
            "needLoverTower", 
            "needLoverTowerConstruction"
        ], 
        "key": "needLoverButton", 
        "subPanel": "Towers", 
        "description": [
            "It is embarrassing how much you need the lover.", 
            "Converts social + self-esteem -> need"
        ]
    }, 
    "playMusicTowerConstruction": {
        "key": "playMusicTowerConstruction", 
        "constructionIdentifier": "playMusicTower", 
        "name": "Constructing - Play Released Music", 
        "image": "trait-aaaaaa99_tower_coolNote", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Play Released Music", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "findMistakeGoal": {
        "currencyEntities": "shameBurst", 
        "randomPosition": 0, 
        "name": "Learn Mistakes", 
        "image": "goal_towerConfess-000000", 
        "key": "findMistakeGoal", 
        "currencyRequested": {
            "will": 3, 
            "social_lover": 3
        }, 
        "entitiesToMake": [
            "mistakeDesposit", 
            "apologizeGoal"
        ], 
        "goalMenuKey": "findMistakeMenu"
    }, 
    "emoWorkTowerConstruction": {
        "key": "emoWorkTowerConstruction", 
        "constructionIdentifier": "emoWorkTower", 
        "name": "Constructing - Complete Add Emotion", 
        "image": "trait-aaaaaa99_tower_heart", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Complete Add Emotion", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "envyGoalConstruction": {
        "key": "envyGoalConstruction", 
        "constructionIdentifier": "envyGoal", 
        "name": "Constructing - Give In To Envy", 
        "image": "trait-aaaaaa99_goal_depositSin-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Give In To Envy", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "relaxButton": {
        "constructionKey": "relaxGoalConstruction", 
        "name": "Relax", 
        "image": "goal_goalPark-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "relaxGoal", 
        "key": "relaxButton", 
        "description": [
            "Requires cheer.", 
            "Makes relaxation."
        ]
    }, 
    "sharing_lover": {
        "name": "sharing", 
        "key": "sharing_lover", 
        "tags": [
            "sharing_lover"
        ]
    }, 
    "playWithMusicTowerConstruction": {
        "key": "playWithMusicTowerConstruction", 
        "constructionIdentifier": "playWithMusicTower", 
        "name": "Constructing - Play With Music", 
        "image": "trait-aaaaaa99_tower_dice", 
        "currencyRequested": {
            "plaything": 5
        }, 
        "constructionName": "Play With Music", 
        "killOnTransform": 0
    }, 
    "greatnessDreamDeposit": {
        "image": "tower_daydream", 
        "activeImage": "tower_daydream_activeOutline-0099ff", 
        "name": "Dreams of Greatness", 
        "key": "greatnessDreamDeposit", 
        "deposit": {
            "dreamGreatness": 3
        }
    }, 
    "dailyHelpInfoPanel": {
        "components": [
            "dailyEntityMakerComponent"
        ], 
        "helpLines": [
            "These are the sources of the recurring goals in your life."
        ], 
        "name": "Recurring Chores", 
        "key": "dailyHelpInfoPanel"
    }, 
    "strengthAction": {
        "currenciesRequired": [
            "will"
        ], 
        "towerKey": "strengthTower", 
        "currenciesToMake": [
            "strength"
        ], 
        "name": "Strength", 
        "key": "strengthAction"
    }, 
    "sayRightTower": {
        "image": "tower_understand", 
        "activeImage": "tower_understand_activeOutline-0099ff", 
        "name": "Saying The Right Things", 
        "key": "sayRightTower", 
        "description": "Converts understanding + social -> right words"
    }, 
    "solutionTowerConstruction": {
        "key": "solutionTowerConstruction", 
        "constructionIdentifier": "solutionTower", 
        "name": "Constructing - Solve Problem", 
        "image": "trait-aaaaaa99_tower_techPuzzle", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Solve Problem", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "techInspiration_ext": {
        "image": [
            "trait-cc0099", 
            "trait-B70089", 
            "trait-E000A8"
        ], 
        "name": "Inspiration", 
        "key": "techInspiration_ext", 
        "sourceKey": "techInspiration"
    }, 
    "playWithMusicTower": {
        "name": "Play With Music", 
        "image": "tower_dice", 
        "damageMultipliers": {
            "anger": 1, 
            "joy": 1, 
            "sadness": 1
        }, 
        "key": "playWithMusicTower", 
        "activeImage": "tower_dice_activeOutline-0099ff", 
        "description": "Uses plaything to calm anger, sadness, joy"
    }, 
    "strengthMineConstruction": {
        "key": "strengthMineConstruction", 
        "constructionIdentifier": "strengthMine", 
        "name": "Constructing - Derive Strength", 
        "image": "trait-aaaaaa99_tower_weight", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Derive Strength", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "exhaustionDisable": {
        "name": "You're too tired to do anything with this.", 
        "key": "exhaustionDisable", 
        "traitsToDisable": [
            "musicLove", 
            "freshMusic", 
            "curious"
        ]
    }, 
    "joyFleshButton": {
        "constructionKey": "joyFleshTowerConstruction", 
        "name": "Flesh Out With Joy", 
        "image": "tower_mineCheer", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "joyFleshTower", 
        "limitingKeys": [
            "joyFleshTower", 
            "joyFleshTowerConstruction"
        ], 
        "key": "joyFleshButton", 
        "subPanel": "Towers", 
        "description": "Converts cheer -> satisfaction"
    }, 
    "towerTickModifier": {
        "timeToDeath": 15
    }, 
    "killMuse": {
        "entitiesToKill": [
            "muse"
        ], 
        "key": "killMuse"
    }, 
    "shameBurst": {
        "emotionsToAdd": {
            "sadness": 1
        }, 
        "name": "Burst of Shame", 
        "image": "tower_memoryShame-000000", 
        "key": "shameBurst", 
        "memoryKey": "memory_shameBurst", 
        "emotionTags": "self", 
        "resourceKey": "shameBurstMineConstruction", 
        "activeImage": "tower_memoryShame-000000_activeOutline-0099ff"
    }, 
    "suspicionResult": {
        "goodEntities": {
            "explainSuspicionSuccess": 2
        }, 
        "badEntities": "unhappy", 
        "key": "suspicionResult"
    }, 
    "hurtLoverMineConstruction": {
        "key": "hurtLoverMineConstruction", 
        "constructionIdentifier": "hurtLoverMine", 
        "name": "Hurt Lover", 
        "image": "tower_social-666666aa", 
        "currencyRequested": {
            "impulse": 1
        }, 
        "constructionName": "Hurt Lover"
    }, 
    "sinkWorkTower": {
        "name": "Sink Into Work", 
        "image": "tower_lifePreserver", 
        "damageMultipliers": {
            "anger": 1, 
            "joy": 1, 
            "sadness": 1
        }, 
        "key": "sinkWorkTower", 
        "activeImage": "tower_lifePreserver_activeOutline-0099ff", 
        "description": "Uses thought to calm anger, joy, sadness"
    }, 
    "traitHelpInfoPanel": {
        "components": [
            "entityManagerComponent"
        ], 
        "helpLines": [
            "Traits are the things that define you as a person.", 
            "They range from the city you live in to your maturity to the people you know.", 
            "They grow with use and you can build towers on them."
        ], 
        "name": "Traits", 
        "key": "traitHelpInfoPanel"
    }, 
    "endRealTalkButton": {
        "constructionKey": "endRealTalkGoalConstruction", 
        "name": "You've Said Enough", 
        "image": "goal_social-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "endRealTalkGoal", 
        "key": "endRealTalkButton", 
        "description": "Requires social."
    }, 
    "sadMemoryFade": {
        "newTrait": "fadingSadMemory", 
        "key": "sadMemoryFade", 
        "timeToDeath": 450
    }, 
    "identifyProblemMenu": {
        "image": "goal_thinkProblem-000000", 
        "completeText": "Bit by bit, the picture becomes clearer.", 
        "name": "COMPLETED - Identify Issue", 
        "key": "identifyProblemMenu"
    }, 
    "tryOldJokesGoal": {
        "key": "tryOldJokesGoal", 
        "randomPosition": 0, 
        "name": "Try Old Jokes", 
        "image": "goal_social-000000", 
        "currencyRequested": {
            "social_lover": 3, 
            "cheer": 3
        }, 
        "entitiesToMake": [
            "alienationDeposit", 
            "aloneSad"
        ], 
        "goalMenuKey": "tryOldJokesMenu", 
        "description": "Shared jokes are the bones of every relationship."
    }, 
    "flashback_joy": {
        "resourceCount": 1, 
        "name": "Flashback", 
        "image": "tower_flashback-000000", 
        "key": "flashback_joy", 
        "emotionTags": "joy", 
        "resourceKeys": [
            "joy"
        ], 
        "activeImage": "tower_flashback-000000_activeOutline-0099ff", 
        "description": "makes joy"
    }, 
    "struggleComplimentsGoal": {
        "key": "struggleComplimentsGoal", 
        "randomPosition": 0, 
        "name": "Hope For Compliments", 
        "image": "goal_hook-000000", 
        "currencyRequested": {
            "social_lover": 3, 
            "music": 3
        }, 
        "entitiesToMake": "struggleCompliment", 
        "goalMenuKey": "struggleComplimentsMenu"
    }, 
    "talkAboutLoverGoalConstruction": {
        "key": "talkAboutLoverGoalConstruction", 
        "constructionIdentifier": "talkAboutLoverGoal", 
        "name": "Constructing - Talk About Lover", 
        "image": "trait-aaaaaa99_goal_goalChat-000000-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Talk About Lover", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "hangOutMenu": {
        "image": "goal_goalPark-000000", 
        "name": "COMPLETED - Hang Out", 
        "key": "hangOutMenu"
    }, 
    "friendToHangingOut": {
        "sourceTrait": "friend", 
        "newTrait": "hangingOut", 
        "key": "friendToHangingOut"
    }, 
    "toldWorthButton": {
        "constructionKey": "toldWorthGoalConstruction", 
        "name": "Be Told Of Your Worth", 
        "image": "goal_trophy-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "toldWorthGoal", 
        "key": "toldWorthButton", 
        "description": [
            "Requires love.", 
            "Makes self-esteem."
        ]
    }, 
    "aloneness_ext": {
        "personHeld": "selfCore", 
        "image": [
            "trait-6600aa", 
            "trait-5B0099", 
            "trait-7000BB"
        ], 
        "name": "Aloneness", 
        "key": "aloneness_ext", 
        "sourceKey": "aloneness"
    }, 
    "quietDemonsLoveGoal": {
        "key": "quietDemonsLoveGoal", 
        "randomPosition": 0, 
        "name": "Quiet Demons", 
        "image": "goal_heart-000000", 
        "currencyRequested": {
            "love_lover": 5
        }, 
        "entitiesToMake": "demonsToQuietDemons", 
        "goalMenuKey": "quietDemonsLoveMenu"
    }, 
    "solveMusicGoal": {
        "key": "solveMusicGoal", 
        "randomPosition": 0, 
        "name": "Solve Fresh Music", 
        "image": "goal_towerThinkAbout-000000", 
        "currencyRequested": {
            "thought": 5
        }, 
        "entitiesToMake": "solveMusicResult", 
        "goalMenuKey": "solveMusicMenu"
    }, 
    "prideStartMenu": {
        "image": "goal_trophy-000000", 
        "name": "COMPLETED - Be Proud Of This", 
        "key": "prideStartMenu"
    }, 
    "achievementButton": {
        "constructionKey": "achievementGoalConstruction", 
        "name": "Note Achievement", 
        "image": "goal_trophy-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "achievementGoal", 
        "key": "achievementButton", 
        "description": [
            "Requires will.", 
            "Makes accomplishment."
        ]
    }, 
    "nascentSongTraitConnectLoverBase": {
        "startKey": "nascentSong", 
        "endKey": "loverBase", 
        "key": "nascentSongTraitConnectLoverBase"
    }, 
    "painMineButton": {
        "constructionKey": "painMineConstruction", 
        "name": "Pain Mine", 
        "entityCost": {
            "will": 2
        }, 
        "image": "tower_pain", 
        "currenciesMade": [
            "pain"
        ], 
        "entityKey": "painMine", 
        "limitingKeys": [
            "painMine", 
            "painMineConstruction"
        ], 
        "key": "painMineButton", 
        "description": "makes pain"
    }, 
    "love_lover": {
        "name": "love", 
        "key": "love_lover", 
        "tags": [
            "love_lover"
        ]
    }, 
    "rememberGoal": {
        "key": "rememberGoal", 
        "randomPosition": 0, 
        "name": "Remember Being Alone", 
        "image": "goal_social-000000", 
        "currencyRequested": {
            "will": 1
        }, 
        "entitiesToMake": "aloneSad", 
        "goalMenuKey": "rememberMenu", 
        "description": "There's no dearth of things to remember here."
    }, 
    "loverSad": {
        "emotionsToAdd": {
            "sadness": 1
        }, 
        "name": "Lover Sadness", 
        "image": "tower_social", 
        "key": "loverSad", 
        "memoryKey": "memory_loverSad", 
        "emotionTags": "lover", 
        "resourceKey": "loverSadMineConstruction", 
        "activeImage": "tower_social_activeOutline-0099ff", 
        "description": "It hurts when the lover hurts"
    }, 
    "earworm": {
        "name": "Earworm", 
        "key": "earworm", 
        "timeToDeath": 300
    }, 
    "destinyGoalConstruction": {
        "key": "destinyGoalConstruction", 
        "constructionIdentifier": "destinyGoal", 
        "name": "Constructing - Believe In Future", 
        "image": "trait-aaaaaa99_goal_daydream-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Believe In Future", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "museBase": {
        "sector": [
            2, 
            4
        ], 
        "name": "The Muse", 
        "aliveEntities": {
            "prideBurst": 10
        }, 
        "image": "trait-ff6699", 
        "personHeld": "muse", 
        "isaacEntities": [
            "socialMuseMineButton", 
            "shareJoyMuseButton", 
            "museVentButton", 
            "museFishButton", 
            "endMuseButton", 
            "museInspirationButton", 
            "drownButton"
        ], 
        "key": "museBase", 
        "startManagedEntities": [
            "museBaseTraitConnectSelfCore", 
            "museBaseTraitConnectMusic"
        ], 
        "description": "Everything about the muse inspires you. The words, the looks, everything."
    }, 
    "trainPathHelpInfoPanel": {
        "components": [
            "trainPathComponent"
        ], 
        "helpLines": [
            "These are the trains of thought that let you complete tasks by moving things from one building to another."
        ], 
        "name": "Trains", 
        "key": "trainPathHelpInfoPanel"
    }, 
    "overcomeExhaustButton": {
        "constructionKey": "overcomeExhaustGoalConstruction", 
        "name": "Overcome Exhaustion", 
        "image": "goal_goalPark-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "overcomeExhaustGoal", 
        "key": "overcomeExhaustButton", 
        "description": "Requires relaxation."
    }, 
    "strengthDeposit": {
        "image": "tower_weight", 
        "activeImage": "tower_weight_activeOutline-0099ff", 
        "name": "Strength", 
        "key": "strengthDeposit", 
        "deposit": {
            "strength": 3
        }
    }, 
    "resolveDeposit": {
        "image": "tower_drive", 
        "activeImage": "tower_drive_activeOutline-0099ff", 
        "name": "Resolve", 
        "key": "resolveDeposit", 
        "deposit": {
            "resolve": 3
        }
    }, 
    "memoryGoal": {
        "minSelfDist": 4
    }, 
    "reflectFailureGoalConstruction": {
        "key": "reflectFailureGoalConstruction", 
        "constructionIdentifier": "reflectFailureGoal", 
        "name": "Constructing - Reflect on Deficiencies", 
        "image": "trait-aaaaaa99_goal_eyeTear-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Reflect on Deficiencies", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "sadness": {
        "emotion": "sadness", 
        "name": "sadness", 
        "image": "creep-0000ff", 
        "damage": 0, 
        "memoryImage": "memorySadness", 
        "health": 10, 
        "key": "sadness"
    }, 
    "discardGoalConstruction": {
        "key": "discardGoalConstruction", 
        "constructionIdentifier": "discardGoal", 
        "name": "Constructing - Discard Song", 
        "image": "trait-aaaaaa99_goal_goalDisavowal-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Discard Song", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "acceptAlienationButton": {
        "constructionKey": "acceptAlienationGoalConstruction", 
        "name": "Accept Alienation", 
        "image": "goal_social-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "acceptAlienationGoal", 
        "key": "acceptAlienationButton", 
        "description": "Requires alienation, strength."
    }, 
    "understandButton": {
        "constructionKey": "understandTowerConstruction", 
        "name": "Understand", 
        "image": "tower_understand", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "understandTower", 
        "limitingKeys": [
            "understandTower", 
            "understandTowerConstruction"
        ], 
        "key": "understandButton", 
        "description": "Converts pain -> understanding"
    }, 
    "randomDemon": {
        "keys": [
            "stopDemonLoveGoal", 
            "stopDemonStrengthGoal", 
            "stopDemonSelfEsteemGoal"
        ], 
        "key": "randomDemon"
    }, 
    "thoughtTower": {
        "image": "tower_doubleNote", 
        "activeImage": "tower_doubleNote_activeOutline-0099ff", 
        "name": "Thinking About Music", 
        "key": "thoughtTower", 
        "description": "Converts music -> thought"
    }, 
    "loving": {
        "name": "Slightly Distant", 
        "aliveEntities": {
            "loveInsult": 5
        }, 
        "image": "trait-cc3366", 
        "personHeld": "lover", 
        "isaacEntities": [
            "struggleComplimentsButton", 
            "romanceReplace", 
            "tryOldJokesButton"
        ], 
        "key": "loving", 
        "description": "All the embers of the romance are still there. They've just dimmed."
    }, 
    "indifferenceAction": {
        "currenciesRequired": [
            "strength"
        ], 
        "towerKey": "indifferenceTower", 
        "currenciesToMake": [
            "alienation_lover"
        ], 
        "name": "Aloofness", 
        "key": "indifferenceAction"
    }, 
    "tryComplimentsGoal": {
        "key": "tryComplimentsGoal", 
        "randomPosition": 0, 
        "name": "Try For Compliments", 
        "image": "goal_hook-000000", 
        "currencyRequested": {
            "social_lover": 3, 
            "music": 3
        }, 
        "entitiesToMake": "loverInsult", 
        "goalMenuKey": "tryComplimentsMenu"
    }, 
    "completeTechMineButton": {
        "constructionKey": "completeTechMineConstruction", 
        "name": "Completion Desire", 
        "entityCost": {
            "will": 2
        }, 
        "image": "tower_mineWill", 
        "currenciesMade": [
            "will"
        ], 
        "entityKey": "completeTechMine", 
        "limitingKeys": [
            "completeTechMine", 
            "completeTechMineConstruction"
        ], 
        "key": "completeTechMineButton", 
        "description": "makes will"
    }, 
    "romanceReplace": {
        "entitiesToMake": [
            "impressButton", 
            "shareButton", 
            "sayRightButton", 
            "loveStrengthButton", 
            "lovingCareButton"
        ], 
        "key": "romanceReplace"
    }, 
    "loverBase": {
        "sector": [
            2, 
            2
        ], 
        "name": "The Lover", 
        "image": "trait-ff3366", 
        "personHeld": "lover", 
        "isaacEntities": [
            "socialMineButton", 
            "needLoverButton", 
            "flagQuitButton", 
            "resolveButton"
        ], 
        "health": 450, 
        "key": "loverBase", 
        "startManagedEntities": [
            "loverBasePathHateWork", 
            "loverBasePathMusicLove"
        ]
    }, 
    "museFleshAction": {
        "currenciesRequired": [
            "drown_muse"
        ], 
        "towerKey": "museFleshTower", 
        "currenciesToMake": [
            "songSatisfaction"
        ], 
        "name": "Flesh Out With Muse", 
        "key": "museFleshAction"
    }, 
    "joyRideMenu": {
        "image": "goal_goalFightProgramming-000000-000000", 
        "completeText": "Somewhere in all of this joy is the answer you need.", 
        "name": "COMPLETED - Explore Joy", 
        "key": "joyRideMenu"
    }, 
    "makePlaythingGoalConstruction": {
        "key": "makePlaythingGoalConstruction", 
        "constructionIdentifier": "makePlaythingGoal", 
        "name": "Constructing - Make Playthings", 
        "image": "trait-aaaaaa99_goal_dice-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Make Playthings", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "emoSolution": {
        "name": "emotional solution", 
        "key": "emoSolution", 
        "tags": [
            "emoSolution"
        ]
    }, 
    "lovingCareTower": {
        "image": "tower_minePain-000000", 
        "activeImage": "tower_minePain-000000_activeOutline-0099ff", 
        "name": "Loving Care", 
        "key": "lovingCareTower", 
        "description": "Converts love + sharing -> care"
    }, 
    "daydreamButton": {
        "constructionKey": "daydreamGoalConstruction", 
        "name": "Daydream", 
        "image": "goal_memoryDefineSelf-000000-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "daydreamGoal", 
        "key": "daydreamButton", 
        "description": "Requires relaxation."
    }, 
    "refineAccomplishmentTower": {
        "image": "tower_towerThinkAbout", 
        "activeImage": "tower_towerThinkAbout_activeOutline-0099ff", 
        "name": "Refine Accomplishment", 
        "key": "refineAccomplishmentTower", 
        "description": [
            "Surely accomplishments imply worth?", 
            "Converts accomplishment + will -> self-esteem"
        ]
    }, 
    "reflectSuccessGoal": {
        "key": "reflectSuccessGoal", 
        "randomPosition": 0, 
        "name": "Reflect on Success", 
        "image": "goal_trophy-000000", 
        "currencyRequested": {
            "selfEsteem": 6
        }, 
        "entitiesToMake": "daydreamingToAchievement", 
        "goalMenuKey": "reflectSuccessMenu", 
        "description": "It's takes searching, but there are things here too."
    }, 
    "quitPrideGoal": {
        "key": "quitPrideGoal", 
        "randomPosition": 1, 
        "name": "Strength In Quitting Job", 
        "makeOnTimeOut": "proveSuccessGoal", 
        "image": "goal_weight-000000", 
        "description": [
            "Quitting your job to pursue music is impressive, but will you let", 
            "yourself accept that?"
        ], 
        "sourceTrait": "ambition", 
        "currencyRequested": {
            "will": 2
        }, 
        "entitiesToMake": [
            "largeStrengthDeposit", 
            "demons"
        ], 
        "goalMenuKey": "quitPrideMenu", 
        "timeToDeath": 120
    }, 
    "solutionTower": {
        "image": "tower_techPuzzle", 
        "activeImage": "tower_techPuzzle_activeOutline-0099ff", 
        "name": "Solve Problem", 
        "key": "solutionTower", 
        "description": "Converts insight + will -> tech solution"
    }, 
    "song_ext": {
        "personHeld": "song", 
        "image": [
            "trait-aa33aa", 
            "trait-992D99", 
            "trait-BB38BB"
        ], 
        "name": "Poor Song", 
        "key": "song_ext", 
        "sourceKey": "song"
    }, 
    "tryForMeaningGoal": {
        "currencyEntities": {
            "condCaptureInspirationGoal": 10
        }, 
        "randomPosition": 0, 
        "name": "Try For Deeper Meaning", 
        "image": "goal_social-000000", 
        "key": "tryForMeaningGoal", 
        "currencyRequested": {
            "thought": 3, 
            "workEmotion": 3
        }, 
        "entitiesToMake": "alienationDeposit", 
        "goalMenuKey": "tryForMeaningMenu", 
        "description": [
            "The lover is good at addressing the surface, but you want a much", 
            "deeper understanding."
        ]
    }, 
    "joyRideGoal": {
        "key": "joyRideGoal", 
        "randomPosition": 0, 
        "currencyRequested": {
            "cheer": 3
        }, 
        "image": "goal_goalFightProgramming-000000-000000", 
        "killMemory": 0, 
        "flareMemory": 1, 
        "entitiesToMake": "rideResult", 
        "goalMenuKey": "joyRideMenu", 
        "name": "Explore Joy"
    }, 
    "social_muse": {
        "name": "social", 
        "key": "social_muse", 
        "tags": [
            "social_muse"
        ]
    }, 
    "rawInspirationDeposit": {
        "image": "tower_towerThinkAbout", 
        "activeImage": "tower_towerThinkAbout_activeOutline-0099ff", 
        "name": "Raw Inspiration", 
        "key": "rawInspirationDeposit", 
        "deposit": {
            "rawInspiration": 3
        }
    }, 
    "rechargeAction": {
        "currenciesRequired": [
            "cheer"
        ], 
        "towerKey": "rechargeTower", 
        "currenciesToMake": [
            "will"
        ], 
        "name": "Recharge", 
        "key": "rechargeAction"
    }, 
    "deriveCoolThingMenu": {
        "image": "goal_coolNote-000000", 
        "completeText": "That is cool. You should show someone.", 
        "name": "COMPLETED - Derive Cool Thing", 
        "key": "deriveCoolThingMenu"
    }, 
    "cheerFail": {
        "entitiesToMake": [
            "selfWorthless", 
            "explainCheerFail"
        ], 
        "key": "cheerFail"
    }, 
    "playMusicButton": {
        "constructionKey": "playMusicTowerConstruction", 
        "name": "Play Released Music", 
        "image": "tower_coolNote", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "playMusicTower", 
        "limitingKeys": [
            "playMusicTower", 
            "playMusicTowerConstruction"
        ], 
        "key": "playMusicButton", 
        "subPanel": "Towers", 
        "description": "Converts music -> released music"
    }, 
    "letdownPath": {
        "startKey": "letdownDeposit", 
        "endKey": "loseImpressedGoal", 
        "key": "letdownPath"
    }, 
    "museInspirationButton": {
        "constructionKey": "museInspirationGoalConstruction", 
        "name": "Be Inspired!", 
        "image": "goal_towerThinkAbout-000000", 
        "description": "Requires drowning.", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "museInspirationGoal", 
        "key": "museInspirationButton", 
        "displayRiskIcon": 1
    }, 
    "reflectFailureGoal": {
        "key": "reflectFailureGoal", 
        "randomPosition": 0, 
        "name": "Reflect on Deficiencies", 
        "image": "goal_eyeTear-000000", 
        "currencyRequested": {
            "will": 1
        }, 
        "entitiesToMake": "daydreamingToSelfLoathing", 
        "goalMenuKey": "reflectFailureMenu", 
        "description": "There's plenty here to reflect upon"
    }, 
    "emoProblemDeposit": {
        "randomPosition": 1, 
        "name": "Emotional Issues", 
        "image": "tower_emoPuzzle", 
        "deposit": {
            "emoProblem": 3
        }, 
        "key": "emoProblemDeposit", 
        "activeImage": "tower_emoPuzzle_activeOutline-0099ff"
    }, 
    "proveSuccessGoal": {
        "currencyEntities": "prideBurst", 
        "randomPosition": 1, 
        "name": "Prove Success", 
        "makeOnTimeOut": [
            "gameEnd", 
            "timeOutMenu"
        ], 
        "currencyRequested": {
            "success": 15
        }, 
        "image": "goal_trophy-000000", 
        "timeToDeath": 10000, 
        "minSelfDist": 4, 
        "sourceTrait": "demons", 
        "startEntityKey": "loverBase", 
        "key": "proveSuccessGoal", 
        "entitiesToMake": [
            "gameEnd", 
            "winMenu"
        ], 
        "goalMenuKey": "proveSuccessMenu", 
        "description": "You'll show them all."
    }, 
    "makeGoodSong": {
        "entitiesToMake": [
            "songToGoodSong", 
            "killSongProblems"
        ], 
        "key": "makeGoodSong"
    }, 
    "angerFleshAction": {
        "currenciesRequired": [
            "frustration"
        ], 
        "towerKey": "angerFleshTower", 
        "currenciesToMake": [
            "songSatisfaction"
        ], 
        "name": "Flesh Out With Anger", 
        "key": "angerFleshAction"
    }, 
    "listenDemonsGoalConstruction": {
        "key": "listenDemonsGoalConstruction", 
        "constructionIdentifier": "listenDemonsGoal", 
        "name": "Constructing - Listen To Demons", 
        "image": "trait-aaaaaa99_goal_memoryProgramming-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Listen To Demons", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "sadSong_ext": {
        "personHeld": "song", 
        "image": [
            "trait-993399", 
            "trait-892D89", 
            "trait-A838A8"
        ], 
        "name": "Dissatisfaction With Song", 
        "key": "sadSong_ext", 
        "sourceKey": "sadSong"
    }, 
    "socialMuseMineConstruction": {
        "key": "socialMuseMineConstruction", 
        "constructionIdentifier": "socialMuseMine", 
        "name": "Constructing - Spend Time With Muse", 
        "image": "trait-aaaaaa99_tower_mineSocial", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Spend Time With Muse", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "playWithMusicAction": {
        "currenciesRequired": [
            "plaything"
        ], 
        "towerKey": "playWithMusicTower", 
        "name": "Play With Music", 
        "key": "playWithMusicAction"
    }, 
    "clickText": {
        "touchable": "true", 
        "timeToDeath": 30, 
        "key": "clickText"
    }, 
    "angerRideGoalConstruction": {
        "key": "angerRideGoalConstruction", 
        "constructionIdentifier": "angerRideGoal", 
        "name": "Constructing - Explore Anger", 
        "image": "trait-aaaaaa99_goalFightProgramming-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Explore Anger", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "musicMineButton": {
        "constructionKey": "musicMineConstruction", 
        "name": "Music", 
        "entityCost": {
            "will": 2
        }, 
        "image": "tower_note", 
        "currenciesMade": [
            "music"
        ], 
        "entityKey": "musicMine", 
        "limitingKeys": [
            "musicMine", 
            "musicMineConstruction"
        ], 
        "key": "musicMineButton", 
        "description": "makes music"
    }, 
    "easyWorkTowerConstruction": {
        "key": "easyWorkTowerConstruction", 
        "constructionIdentifier": "easyWorkTower", 
        "name": "Constructing - Easy Work", 
        "image": "trait-aaaaaa99_tower_towerDrive", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Easy Work", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "hangOutButton": {
        "constructionKey": "hangOutGoalConstruction", 
        "name": "Hang Out", 
        "image": "goal_goalPark-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "hangOutGoal", 
        "key": "hangOutButton", 
        "description": "Requires cheer."
    }, 
    "worker": {
        "planeOfExistence": "path"
    }, 
    "playMusicAction": {
        "currenciesRequired": [
            "music"
        ], 
        "towerKey": "playMusicTower", 
        "currenciesToMake": [
            "releasedMusic"
        ], 
        "name": "Play Released Music", 
        "key": "playMusicAction"
    }, 
    "fishComplimentsMenu": {
        "image": "goal_hook-000000", 
        "completeText": "This may be shallow, but this is still fun.", 
        "name": "COMPLETED - Fish For Compliments", 
        "key": "fishComplimentsMenu"
    }, 
    "techInspiration": {
        "sector": [
            4, 
            2
        ], 
        "name": "Inspiration", 
        "image": "trait-cc0099", 
        "isaacEntities": [
            "fadeTechInspiration", 
            "solutionButton", 
            "deriveInsightButton", 
            "completeTechMineButton", 
            "intrigueButton", 
            "techWorkButton"
        ], 
        "health": 450, 
        "key": "techInspiration", 
        "startManagedEntities": [
            "connectSource"
        ], 
        "description": "That seems like a really cool thing to try"
    }, 
    "captureInspirationResult": {
        "goodEntities": {
            "capturedInspiration": 3
        }, 
        "badEntities": {
            "captureWillGoal": 1, 
            "captureThoughtGoal": 1, 
            "captureSocialGoal": 2, 
            "explainCaptureFail": 1, 
            "captureMusicGoal": 2, 
            "captureLoveGoal": 1
        }, 
        "key": "captureInspirationResult"
    }, 
    "friend_ext": {
        "personHeld": "lover", 
        "image": [
            "trait-ff3399", 
            "trait-E52D89", 
            "trait-FF38A8"
        ], 
        "name": "Friend", 
        "key": "friend_ext", 
        "sourceKey": "friend"
    }, 
    "minorFlirtGoal": {
        "key": "minorFlirtGoal", 
        "randomPosition": 1, 
        "name": "Minor Flirtation", 
        "image": "goal_heart-000000", 
        "description": "Are you going to let this chance slip by?", 
        "sourceTrait": "musicLove", 
        "currencyRequested": {
            "social_lover": 1
        }, 
        "entitiesToMake": "condLoverJoy", 
        "goalMenuKey": "minorFlirtMenu", 
        "timeToDeath": 60
    }, 
    "loverInsult": {
        "emotionsToAdd": {
            "anger": 1
        }, 
        "name": "You Don't Need This", 
        "image": "tower_memoryNastyComment-000000", 
        "key": "loverInsult", 
        "memoryKey": "memory_loverInsult", 
        "emotionTags": "lover", 
        "resourceKey": "loverInsultMineConstruction", 
        "activeImage": "tower_memoryNastyComment-000000_activeOutline-0099ff"
    }, 
    "quitWorkGoalConstruction": {
        "key": "quitWorkGoalConstruction", 
        "constructionIdentifier": "quitWorkGoal", 
        "name": "Constructing - Quit Worthless Job", 
        "image": "trait-aaaaaa99_goal_heart-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Quit Worthless Job", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "majorIssueGoal": {
        "key": "majorIssueGoal", 
        "randomPosition": 1, 
        "name": "Defuse Major Issue", 
        "makeOnTimeOut": [
            "lovingToMakingWork", 
            "insulting", 
            "explainMajorMenu"
        ], 
        "image": "goal_eyeTear-000000", 
        "sourceTrait": "loverBase", 
        "currencyRequested": {
            "love_lover": 5
        }, 
        "goalMenuKey": "majorIssueMenu", 
        "timeToDeath": 240
    }, 
    "killSong": {
        "entitiesToKill": [
            "song", 
            "sadSong", 
            "goodSong"
        ], 
        "key": "killSong"
    }, 
    "hangingOutToRealTalk": {
        "sourceTrait": "hangingOut", 
        "newTrait": "realTalk", 
        "key": "hangingOutToRealTalk"
    }, 
    "reflectAloneButton": {
        "constructionKey": "reflectAloneGoalConstruction", 
        "name": "Reflect on Being Alone", 
        "image": "goal_social-000000", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "reflectAloneGoal", 
        "key": "reflectAloneButton", 
        "description": "Requires will."
    }, 
    "deriveHumanInsightGoalConstruction": {
        "key": "deriveHumanInsightGoalConstruction", 
        "constructionIdentifier": "deriveHumanInsightGoal", 
        "name": "Constructing - Derive Human Insight", 
        "image": "trait-aaaaaa99_goal_spark-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Derive Human Insight", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "hangOutGoalConstruction": {
        "key": "hangOutGoalConstruction", 
        "constructionIdentifier": "hangOutGoal", 
        "name": "Constructing - Hang Out", 
        "image": "trait-aaaaaa99_goal_goalPark-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Hang Out", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "dontGiveUpGoal": {
        "key": "dontGiveUpGoal", 
        "randomPosition": 1, 
        "name": "Don't Give Up", 
        "makeOnTimeOut": "killSong", 
        "image": "goal_motivation-000000", 
        "description": "Are you really going to waste more time on this garbage?", 
        "sourceTrait": "sadSong", 
        "currencyRequested": {
            "resolve": 1
        }, 
        "goalMenuKey": "dontGiveUpMenu", 
        "timeToDeath": 180
    }, 
    "sadFleshTower": {
        "image": "tower_pain", 
        "activeImage": "tower_pain_activeOutline-0099ff", 
        "name": "Flesh Out With Sadness", 
        "key": "sadFleshTower", 
        "description": "Converts pain -> satisfaction"
    }, 
    "songToGoodSong": {
        "sourceTrait": "song", 
        "newTrait": "goodSong", 
        "key": "songToGoodSong"
    }, 
    "willMineConstruction": {
        "key": "willMineConstruction", 
        "constructionIdentifier": "willMine", 
        "name": "Constructing - Drive", 
        "image": "trait-aaaaaa99_tower_mineWill", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Drive", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "makePlaythingGoal": {
        "key": "makePlaythingGoal", 
        "randomPosition": 0, 
        "name": "Make Playthings", 
        "image": "goal_dice-000000", 
        "currencyRequested": {
            "music": 3
        }, 
        "entitiesToMake": [
            "playthingDeposit", 
            "curiousToMusicLove"
        ], 
        "goalMenuKey": "makePlaythingMenu"
    }, 
    "findNewMusicGoal": {
        "currencyEntities": {
            "listenEvent": 25
        }, 
        "randomPosition": 0, 
        "name": "Listen To Music Together", 
        "image": "goal_cd-000000", 
        "key": "findNewMusicGoal", 
        "currencyRequested": {
            "social_lover": 3, 
            "music": 3
        }, 
        "entitiesToMake": "findNewMusicResult", 
        "goalMenuKey": "findNewMusicMenu", 
        "description": "This is your favorite thing to do together"
    }, 
    "drownButton": {
        "constructionKey": "drownTowerConstruction", 
        "name": "Drowning In Muse", 
        "image": "tower_wave", 
        "entityCost": {
            "will": 2
        }, 
        "entityKey": "drownTower", 
        "limitingKeys": [
            "drownTower", 
            "drownTowerConstruction"
        ], 
        "key": "drownButton", 
        "subPanel": "Towers", 
        "description": "Converts music + social -> drowning"
    }, 
    "shareTowerConstruction": {
        "key": "shareTowerConstruction", 
        "constructionIdentifier": "shareTower", 
        "name": "Constructing - Share Experiences", 
        "image": "trait-aaaaaa99_tower_towerDate", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Share Experiences", 
        "killOnTransform": 0, 
        "description": "It takes will to start something new."
    }, 
    "killEmoInspiration": {
        "entitiesToKill": [
            "emoInspiration"
        ], 
        "key": "killEmoInspiration"
    }, 
    "deadInsideTower": {
        "name": "Numbness", 
        "image": "tower_numb", 
        "damageMultipliers": {
            "anger": 1, 
            "joy": 1
        }, 
        "key": "deadInsideTower", 
        "activeImage": "tower_numb_activeOutline-0099ff", 
        "description": "Uses pain to calm anger, joy"
    }, 
    "dismissFailureGoalConstruction": {
        "key": "dismissFailureGoalConstruction", 
        "constructionIdentifier": "dismissFailureGoal", 
        "name": "Constructing - Dismiss Failures", 
        "image": "trait-aaaaaa99_goal_goalCleaner-000000", 
        "currencyRequested": {
            "will": 2
        }, 
        "constructionName": "Dismiss Failures", 
        "killOnTransform": 1, 
        "description": "It takes will to start something new."
    }, 
    "failureAction": {
        "currenciesRequired": [
            "frustration", 
            "pain"
        ], 
        "towerKey": "failureTower", 
        "currenciesToMake": [
            "failure"
        ], 
        "name": "Think of Failure", 
        "key": "failureAction"
    }
};

var ecs = {
    memoryComponent: function(args){
        if (args.memoryEmotions === undefined){ throw "ECS fail " + args.key + " missing memoryEmotions"; }
        if (args.sourceKey === undefined){ throw "ECS fail " + args.key + " missing sourceKey"; }
        if (args.source2Key === undefined){ throw "ECS fail " + args.key + " missing source2Key"; }
        return {
            coreMemory: false,
            source2Key: args['source2Key'],
            ticksToCoreMemory: 180,
            flashbackFunction: flashbackFunction,
            sourceKey: args['sourceKey'],
            flashbackCount: 1,
            memoryEmotions: args['memoryEmotions']
        };
    },
    talkAboutSelfGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.talkAboutSelfGoal)));
        return ecs.worldGoal(args);
    },
    endAlienationGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.endAlienationGoalConstruction)));
        return ecs.construction(args);
    },
    singleUseTrainPathComponent: function(args){
        return {
            makeTrain: singleUseMakeTrain,
            trainId: undefined,
            trainStops: args['trainStops'],
            killTrainPath: undefined,
            stoppable: false,
            trainKey: PARAMETERS.carryTrain,
            canMakeTrain: canMakeTrain,
            requiresFullPath: (args["requiresFullPath"] === undefined) ? true : args["requiresFullPath"],
            getKillTrainPath: getKillTrainPath,
            trainsToMake: 1,
            trainStartId: args['trainStartId'],
            paused: false,
            baseMakeTrain: makeTrain,
            allPaths: [],
            trainColor: undefined
        };
    },
    utilityEntityRenderComponent: function(args){
        return {
            insetColor: "#000000",
            fullAnimationTime: 0.1,
            yTextOffset: 5,
            backImage: "opaqueBack",
            drawImageButton: drawImageButton,
            animatingTime: 0.1,
            priority: 19,
            render: utilityEntityRender,
            drawPanelButton: drawPanelButton,
            snapPanelPos: snapPanelPos,
            showAllBuildingPanel: undefined,
            renderTime: 0,
            textColor: "#000000",
            renderState: "default",
            specificRender: args['specificRender'],
            drawHelpButton: drawHelpButton
        };
    },
    ignoreDifferencesGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.ignoreDifferencesGoal)));
        return ecs.worldGoal(args);
    },
    callousnessAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.callousnessAction)));
        return ecs.currencyAction(args);
    },
    timedRenderComponent: function(args){
        if (args.nextRenderComponent === undefined){ throw "ECS fail " + args.key + " missing nextRenderComponent"; }
        if (args.renderWhileTimed === undefined){ throw "ECS fail " + args.key + " missing renderWhileTimed"; }
        return {
            renderWhileTimed: args['renderWhileTimed'],
            render: timedRender,
            nextRenderComponent: args['nextRenderComponent'],
            timeDrawn: 0,
            renderTime: (args["renderTime"] === undefined) ? 0.15 : args["renderTime"],
            renderState: "default",
            priority: args['priority']
        };
    },
    callousnessTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.callousnessTowerConstruction)));
        return ecs.construction(args);
    },
    reflectFailureMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.reflectFailureMenu)));
        return ecs.goalCompleteMenu(args);
    },
    dailyEmotionMakerDayResponseComponent: function(args){
        return {
            daysPerStep: 3,
            entitiesToMake: "impatience",
            dayCount: 0,
            sourceEntityKeys: ["ambition", "selfLoathing", "achievement"],
            dayStart: dailyEmotionMakerDayStart
        };
    },
    holdOn: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.holdOn)));
        return ecs.currency(args);
    },
    trainPathMaker: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            rendererComponents: [ecs.trainPathMakerHighlightRenderComponent(args)],
            killExecutables: [ecs.trainPathMakerKill(args)],
            trainPathMakerComponent: ecs.trainPathMakerComponent(args),
            tickComponent: ecs.trainPathMakerTickComponent(args)
        };
    },
    makeRandomMultiple: function(args){
        return {
            replaceMarker: ecs.makeRandomMultipleReplaceMarker(args)
        };
    },
    fullStuckProblem: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.fullStuckProblem)));
        return ecs.entityReplace(args);
    },
    sadRideGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.sadRideGoal)));
        return ecs.memoryGoal(args);
    },
    aloneSadMine: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.aloneSadMine)));
        return ecs.emotionResource(args);
    },
    resolveTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.resolveTower)));
        return ecs.tower(args);
    },
    fleshResult: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.fleshResult)));
        return ecs.randomReplace(args);
    },
    gameEnd: function(args){
        return {
            renderComponents: [ecs.gameEndRenderComponent(args)],
            pauseComponent: ecs.pauseComponent(args),
            gameEndComponent: ecs.gameEndComponent(args)
        };
    },
    memoryConstruction: function(args){
        args["touchable"] = true;
        args["canExpandInto"] = false;
        return {
            junctionComponent: ecs.junctionComponent(args),
            rendererComponents: [ecs.constructionRenderComponent(args), ecs.progressRenderComponent(args)],
            personHeldComponent: ecs.personHeldComponent(args),
            managedComponent: ecs.managedComponent(args),
            constructionComponent: ecs.memoryConstructionComponent(args),
            createExecutables: [ecs.makeExtraPathsExec(args)].concat([ecs.openMenu(args)]),
            focusPathStopComponent: ecs.constructionFocusPathStopComponent(args),
            clickComponent: ecs.fullMenuClickComponent(args),
            uiDataComponent: ecs.uiDataComponent(args),
            tickComponent: ecs.constructionTickComponent(args),
            descriptionComponent: ecs.descriptionComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            interactionComponent: ecs.interactionComponent(args),
            identityComponent: ecs.identityComponent(args),
            positionComponent: ecs.positionComponent(args),
            playerMadeComponent: ecs.playerMadeComponent(args)
        };
    },
    lovingCareButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.lovingCareButton)));
        return ecs.towerButton(args);
    },
    explainCheerLoverFail: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.explainCheerLoverFail)));
        return ecs.menuModifier(args);
    },
    ignoreInsult2Goal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.ignoreInsult2Goal)));
        return ecs.worldGoal(args);
    },
    traitParticleEffect: function(args){
        args["impulseTimeToCheck"] = (args["impulseTimeToCheck"] === undefined) ? 0.1 : args["impulseTimeToCheck"];
        args["startParticles"] = (args["startParticles"] === undefined) ? 10 : args["startParticles"];
        return {
            healthComponent: ecs.singleHealthComponent(args),
            lifeSentinelComponent: ecs.traitParticleEffectLifeSentinelComponent(args),
            rendererComponents: [ecs.particleEffectRenderComponent(args)]
        };
    },
    explainStuck: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.explainStuck)));
        return ecs.menuModifier(args);
    },
    shareJoyAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.shareJoyAction)));
        return ecs.currencyAction(args);
    },
    towerComponent: function(args){
        return {
            possibleCurrencyActionForTower: possibleCurrencyActionForTower,
            storeCount: 3,
            canDamageTower: canDamageTower,
            currencyActionForTower: currencyActionForTower
        };
    },
    currencyRenderEntity: function(args){
        args["key"] = PARAMETERS.currencyRenderEntity;
        return {
            currencyRenderEntityComponent: ecs.currencyRenderEntityComponent(args),
            identityComponent: ecs.identityComponent(args),
            rendererComponents: [ecs.allCurrencyRenderClear(args), ecs.allCurrencyRenderComponent(args)]
        };
    },
    intrigueTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.intrigueTower)));
        return ecs.tower(args);
    },
    applyTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.applyTowerConstruction)));
        return ecs.memoryConstruction(args);
    },
    selfWorthless: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.selfWorthless)));
        return ecs.emotionSource(args);
    },
    settingTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.settingTower)));
        return ecs.tower(args);
    },
    currencyRenderComponent: function(args){
        return {
            priority: 12,
            image: "currency",
            renderState: "default",
            render: currencyRender,
            renderPos: undefined
        };
    },
    begLoverGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.begLoverGoal)));
        return ecs.worldGoal(args);
    },
    shareJoyTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.shareJoyTower)));
        return ecs.tower(args);
    },
    fadeFreshMusic: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.fadeFreshMusic)));
        return ecs.timedCreateEntities(args);
    },
    path: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.path)));
        args["y"] = args['start'][1];
        args["x"] = args['start'][0];
        args["name"] = 'path(' + args.x + ',' + args.y + ')';;
        args["key"] = 'path_' + args.x + '_' + args.y + '_' + args.end[0] + '_' + args.end[1];;
        return {
            rendererComponents: [ecs.pathConnectRenderComponent(args)],
            pathMoveComponent: ecs.pathMoveComponent(args),
            createExecutables: [ecs.duplicatePathExec(args)],
            healthComponent: ecs.hundredHealthComponent(args),
            identityComponent: ecs.identityComponent(args),
            positionComponent: ecs.positionComponent(args),
            playerMadeComponent: ecs.playerMadeComponent(args)
        };
    },
    solveMusicButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.solveMusicButton)));
        return ecs.goalButton(args);
    },
    ignoreIssuesGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.ignoreIssuesGoal)));
        return ecs.worldGoal(args);
    },
    fadingMemoryComponent: function(args){
        if (args.sourceKey === undefined){ throw "ECS fail " + args.key + " missing sourceKey"; }
        if (args.source2Key === undefined){ throw "ECS fail " + args.key + " missing source2Key"; }
        return {
            oldTrait: undefined,
            sourceKey: args['sourceKey'],
            source2Key: args['source2Key']
        };
    },
    creepCreateExec: function(args){
        if (args.sourceId === undefined){ throw "ECS fail " + args.key + " missing sourceId"; }
        return {
            firstTick: (args["firstTick"] === undefined) ? true : args["firstTick"],
            makeTrain: (args["makeTrain"] === undefined) ? true : args["makeTrain"],
            execute: creepCreateExec,
            creepTrainPath: PARAMETERS.singleUseTrainPath,
            sourceId: args['sourceId'],
            destination: (args["destination"] === undefined) ? PARAMETERS.self : args["destination"]
        };
    },
    thinkSongBadMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.thinkSongBadMenu)));
        return ecs.goalCompleteMenu(args);
    },
    intrigue: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.intrigue)));
        return ecs.currency(args);
    },
    checkManagedComponentExec: function(args){
        return {
            execute: checkManagedComponent
        };
    },
    sadnessMap: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.sadnessMap)));
        return ecs.emotionResourceMap(args);
    },
    desireImprovementGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.desireImprovementGoalConstruction)));
        return ecs.construction(args);
    },
    currencyOverload: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            currencyOverloadComponent: ecs.currencyOverloadComponent(args)
        };
    },
    selfLoathing: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.selfLoathing)));
        return ecs.trait(args);
    },
    flashback_angerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.flashback_angerConstruction)));
        return ecs.emotionSourceConstruction(args);
    },
    starParticleEffectRenderComponent: function(args){
        if (args.effectPos === undefined){ throw "ECS fail " + args.key + " missing effectPos"; }
        return {
            priority: 14,
            render: starParticleEffectRender,
            image: "star",
            effectPos: args['effectPos'],
            timeDrawn: 0,
            scaleTime: 0.15,
            renderState: "default"
        };
    },
    identifyProblemGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.identifyProblemGoal)));
        return ecs.worldGoal(args);
    },
    flashback_joyConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.flashback_joyConstruction)));
        return ecs.emotionSourceConstruction(args);
    },
    killSongProblems: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.killSongProblems)));
        return ecs.entityKill(args);
    },
    personCallousnessAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.personCallousnessAction)));
        return ecs.currencyAction(args);
    },
    aliveMakeComponent: function(args){
        return {
            focusTick: aliveMakeFocusTick,
            aliveEntities: (args["aliveEntities"] === undefined) ? [] : args["aliveEntities"]
        };
    },
    restartWorkGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.restartWorkGoalConstruction)));
        return ecs.construction(args);
    },
    music: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.music)));
        return ecs.currency(args);
    },
    fadeQuietDemons: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.fadeQuietDemons)));
        return ecs.timedCreateEntities(args);
    },
    loverInsultMine: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.loverInsultMine)));
        return ecs.emotionResource(args);
    },
    killPrideStartButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.killPrideStartButton)));
        return ecs.entityKill(args);
    },
    tutorialText: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            tutorialTextComponent: ecs.tutorialTextComponent(args),
            absolutePositionComponent: ecs.absolutePositionComponent(args),
            rendererComponents: [ecs.tutorialTextRenderComponent(args)]
        };
    },
    sourceExtensionRenderComponent: function(args){
        return {
            outlineImage: "activeOutline",
            render: sourceExtensionRender,
            wobble: 0,
            image: args['image'],
            priority: 5,
            timeToBlink: 1,
            newImage: "newOutline",
            getWobble: getWobble,
            renderState: "default",
            disabledImage: "trait-aaaaaa"
        };
    },
    relax: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.relax)));
        return ecs.currency(args);
    },
    relaxGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.relaxGoalConstruction)));
        return ecs.construction(args);
    },
    thoughtButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.thoughtButton)));
        return ecs.upgradeButton(args);
    },
    work: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.work)));
        return ecs.currency(args);
    },
    utilityPanel: function(args){
        return {
            panelComponent: ecs.setPanelComponent(args),
            absolutePositionComponent: ecs.utilityPanelAbsolutePositionComponent(args),
            rendererComponents: [ecs.utilityEntityRenderComponent(args)],
            uiClickComponent: ecs.panelUiClickComponent(args)
        };
    },
    carryTrain: function(args){
        return {
            rendererComponents: [ecs.trainRenderComponent(args)],
            trainComponent: ecs.trainComponent(args),
            trainVelocityComponent: ecs.trainVelocityComponent(args),
            createExecutables: [ecs.trainCreateExec(args)],
            killExecutables: [ecs.trainKillExec(args)],
            tickComponent: ecs.carryTrainTickComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            positionComponent: ecs.positionComponent(args)
        };
    },
    currencyRenderEntityComponent: function(args){
        return {
            addCurrencyToRender: addCurrencyToRender,
            currenciesToRender: []
        };
    },
    nextDayEntity: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            dayResponseComponent: ecs.nextDayEntityDayResponseComponent(args)
        };
    },
    currencyBoost: function(args){
        return {
            rendererComponents: [ecs.boostRenderComponent(args)],
            boostSlotComponent: ecs.boostSlotComponent(args),
            createExecutables: [ecs.randomOpenPositionExec(args)],
            currencyBoostComponent: ecs.currencyBoostComponent(args),
            identityComponent: ecs.identityComponent(args),
            positionComponent: ecs.positionComponent(args)
        };
    },
    activateRenderComponent: function(args){
        return {
            priority: 2,
            renderState: "default",
            activated: false,
            render: activateRender
        };
    },
    indulgeCuriosityGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.indulgeCuriosityGoal)));
        return ecs.worldGoal(args);
    },
    currency: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.currency)));
        args["tags"] = (args.tags) ? args.tags.concat([args.key]) : [args.key];
        return {
            pathVelocityComponent: ecs.pathVelocityComponent(args),
            rendererComponents: [ecs.currencyRenderComponent(args)],
            healthComponent: ecs.singleHealthComponent(args),
            identityComponent: ecs.identityComponent(args),
            positionComponent: ecs.positionComponent(args),
            dayResponseComponent: (args.checkDayEnd) ? ecs.currencyDayResponseComponent(args) : undefined
        };
    },
    autoPathMaker: function(args){
        return {
            replaceMarker: ecs.autoPathReplaceMarker(args)
        };
    },
    explainFleshFail: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.explainFleshFail)));
        return ecs.menuModifier(args);
    },
    interestWorkMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.interestWorkMenu)));
        return ecs.goalCompleteMenu(args);
    },
    dismissFailureMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.dismissFailureMenu)));
        return ecs.goalCompleteMenu(args);
    },
    need_lover: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.need_lover)));
        return ecs.currency(args);
    },
    autosaveTickComponent: function(args){
        return {
            lastChapterKey: "chapter1",
            tick: autosaveTick,
            ticksToAutosave: 1
        };
    },
    pairTransformMarker: function(args){
        if (args.trait1 === undefined){ throw "ECS fail " + args.key + " missing trait1"; }
        if (args.trait2 === undefined){ throw "ECS fail " + args.key + " missing trait2"; }
        return {
            trait2: args['trait2'],
            tempKey: "tempTrait",
            trait1: args['trait1'],
            getReplaceKeys: pairTransformGetReplaceKeys
        };
    },
    nascentSong: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.nascentSong)));
        return ecs.trait(args);
    },
    memory_drainedSad: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.memory_drainedSad)));
        return ecs.memory(args);
    },
    releasedMusic: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.releasedMusic)));
        return ecs.currency(args);
    },
    joyFleshTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.joyFleshTower)));
        return ecs.tower(args);
    },
    alienationToFriend: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.alienationToFriend)));
        return ecs.transformExec(args);
    },
    solutionButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.solutionButton)));
        return ecs.towerButton(args);
    },
    fleshSongButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.fleshSongButton)));
        return ecs.goalButton(args);
    },
    emotionSourceCreateExec: function(args){
        if (args.sourceKey === undefined){ throw "ECS fail " + args.key + " missing sourceKey"; }
        if (args.resourceKey === undefined){ throw "ECS fail " + args.key + " missing resourceKey"; }
        if (args.memoryKey === undefined){ throw "ECS fail " + args.key + " missing memoryKey"; }
        return {
            resourceKey: args['resourceKey'],
            sourceKey: args['sourceKey'],
            execute: emotionSourceCreateExec,
            memoryKey: args['memoryKey']
        };
    },
    explainCheerFail: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.explainCheerFail)));
        return ecs.menuModifier(args);
    },
    angerMap: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.angerMap)));
        return ecs.emotionResourceMap(args);
    },
    sadRideButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.sadRideButton)));
        return ecs.memoryGoalButton(args);
    },
    endHangOutMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.endHangOutMenu)));
        return ecs.goalCompleteMenu(args);
    },
    trainCreateExec: function(args){
        if (args.trainPath === undefined){ throw "ECS fail " + args.key + " missing trainPath"; }
        if (args.trainStartId === undefined){ throw "ECS fail " + args.key + " missing trainStartId"; }
        return {
            trainStartId: args['trainStartId'],
            execute: trainCreateExec,
            carriageKey: PARAMETERS.carriage,
            trainPath: args['trainPath']
        };
    },
    coolThing: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.coolThing)));
        return ecs.currency(args);
    },
    cheerLoverMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.cheerLoverMenu)));
        return ecs.goalCompleteMenu(args);
    },
    changeOpinion: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            personHeldComponent: ecs.personHeldComponent(args),
            createExecutables: [ecs.changeOpinionExec(args), ecs.killEntity(args)]
        };
    },
    towerButton: function(args){
        args["subPanel"] = (args["subPanel"] === undefined) ? "Towers" : args["subPanel"];
        args["multiTrait"] = (args["multiTrait"] === undefined) ? false : args["multiTrait"];
        return {
            healthComponent: ecs.singleHealthComponent(args),
            uiActionComponent: ecs.buttonUiActionComponent(args),
            uiDataComponent: ecs.uiDataComponent(args),
            buttonControlComponent: ecs.towerControlComponent(args),
            createExecutables: [ecs.markTraitNew(args)],
            timedDeathComponent: (args.timeToDeath) ? ecs.timedDeathComponent(args) : undefined,
            killExecutables: [ecs.killTowerButtonExec(args)],
            descriptionComponent: ecs.descriptionComponent(args),
            uniqueComponent: (args.timeToDeath === undefined && !args.multiTrait) ? ecs.uniqueComponent(args) : undefined,
            interactionComponent: ecs.interactionComponent(args),
            identityComponent: ecs.identityComponent(args)
        };
    },
    focusTickAliveMakerComponent: function(args){
        if (args.aliveEntities === undefined){ throw "ECS fail " + args.key + " missing aliveEntities"; }
        return {
            focusTick: aliveMakerFocusTick,
            aliveEntities: args['aliveEntities']
        };
    },
    destinyGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.destinyGoal)));
        return ecs.worldGoal(args);
    },
    pointCounter: function(args){
        return {
            pointCounterComponent: ecs.pointCounterComponent(args),
            notificationComponents: [ecs.pointCounterNotificationComponent(args)]
        };
    },
    loverBase_ext: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.loverBase_ext)));
        return ecs.sourceExtension(args);
    },
    carryTrainTickComponent: function(args){
        return {
            tick: carryTrainTick
        };
    },
    dayPanelRenderComponent: function(args){
        return {
            flashTimer: 0,
            dayActions: undefined,
            insetColor: "#000000",
            fullAnimationTime: 0.1,
            yTextOffset: 5,
            backImage: "opaqueBack",
            displayGameStage: undefined,
            drawImageButton: drawImageButton,
            animatingTime: 0.1,
            priority: 19,
            render: dayPanelRender,
            drawPanelButton: drawPanelButton,
            snapPanelPos: snapPanelPos,
            showPausingEntity: showPausingEntity,
            renderTime: 0,
            textColor: "#000000",
            renderState: "default",
            specificRender: args['specificRender'],
            drawHelpButton: drawHelpButton
        };
    },
    thinkSongBadButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.thinkSongBadButton)));
        return ecs.goalButton(args);
    },
    musicLove: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.musicLove)));
        return ecs.trait(args);
    },
    entityPanelComponent: function(args){
        if (args.sourceId === undefined){ throw "ECS fail " + args.key + " missing sourceId"; }
        return {
            panelButtons: [],
            sourceId: args['sourceId'],
            expandPanel: expandPanel,
            minimized: true,
            canClose: true,
            closed: true,
            ignoreWhenMinimized: true
        };
    },
    frustrationMine: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.frustrationMine)));
        return ecs.memoryResource(args);
    },
    trainPathComponent: function(args){
        if (args.trainStartId === undefined){ throw "ECS fail " + args.key + " missing trainStartId"; }
        if (args.trainStops === undefined){ throw "ECS fail " + args.key + " missing trainStops"; }
        return {
            makeTrain: makeTrain,
            trainId: undefined,
            trainStops: args['trainStops'],
            killTrainPath: undefined,
            stoppable: (args["stoppable"] === undefined) ? true : args["stoppable"],
            trainKey: PARAMETERS.train,
            canMakeTrain: canMakeTrain,
            requiresFullPath: (args["requiresFullPath"] === undefined) ? true : args["requiresFullPath"],
            getKillTrainPath: getKillTrainPath,
            baseMakeTrain: makeTrain,
            trainStartId: args['trainStartId'],
            paused: false,
            allPaths: [],
            trainColor: undefined
        };
    },
    randomOpenPositionExec: function(args){
        return {
            execute: randomOpenPositionExec,
            startEntityKey: (args["startEntityKey"] === undefined) ? undefined : args["startEntityKey"],
            startPos: (args.source && args.source.positionComponent) ? [args.source.positionComponent.x, args.source.positionComponent.y] : undefined,
            minSelfDist: (args["minSelfDist"] === undefined) ? 0 : args["minSelfDist"]
        };
    },
    setFlagExec: function(args){
        if (args.flag === undefined){ throw "ECS fail " + args.key + " missing flag"; }
        return {
            flagValue: (args["flagValue"] === undefined) ? true : args["flagValue"],
            execute: setFlagExec,
            flag: args['flag']
        };
    },
    memoryTower: function(args){
        args["touchable"] = true;
        args["canExpandInto"] = false;
        args["isPathEnd"] = (args["isPathEnd"] === undefined) ? (args.damageMultipliers ? true : false) : args["isPathEnd"];
        args["tags"] = (args["tags"] === undefined) ? [args.key] : args["tags"];
        return {
            junctionComponent: ecs.junctionComponent(args),
            rendererComponents: [ecs.pathStopRenderComponent(args), ecs.damageRenderComponent(args)],
            focusTickResponseComponent: ecs.towerFocusTickResponseComponent(args),
            managedComponent: ecs.managedComponent(args),
            absolutePositionComponent: ecs.absolutePositionComponent(args),
            damageComponent: (args.damageMultipliers) ? ecs.damageComponent(args) : undefined,
            healthModifyComponent: ecs.healthModifyComponent(args),
            createExecutables: [ecs.makeExtraPathsExec(args)].concat([ecs.towerCreateExec(args)]),
            focusPathStopComponent: ecs.towerFocusPathStopComponent(args),
            clickComponent: ecs.fullMenuClickComponent(args),
            uiDataComponent: ecs.uiDataComponent(args),
            tickComponent: ecs.towerTickComponent(args),
            towerComponent: ecs.towerComponent(args),
            descriptionComponent: ecs.descriptionComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            interactionComponent: ecs.interactionComponent(args),
            identityComponent: ecs.identityComponent(args),
            positionComponent: ecs.positionComponent(args),
            playerMadeComponent: ecs.playerMadeComponent(args)
        };
    },
    understandFailingsGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.understandFailingsGoal)));
        return ecs.worldGoal(args);
    },
    intrigueAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.intrigueAction)));
        return ecs.currencyAction(args);
    },
    personCurrency: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.personCurrency)));
        args["tags"] = (args.tags) ? args.tags.concat([args.key]) : [args.key];
        return {
            pathVelocityComponent: ecs.pathVelocityComponent(args),
            rendererComponents: [ecs.currencyRenderComponent(args)],
            healthComponent: ecs.singleHealthComponent(args),
            identityComponent: ecs.identityComponent(args),
            positionComponent: ecs.positionComponent(args),
            dayResponseComponent: (args.checkDayEnd) ? ecs.currencyDayResponseComponent(args) : undefined
        };
    },
    bruteForceTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.bruteForceTower)));
        return ecs.tower(args);
    },
    cynicismAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.cynicismAction)));
        return ecs.currencyAction(args);
    },
    sinkWorkButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.sinkWorkButton)));
        return ecs.towerButton(args);
    },
    progressRenderComponent: function(args){
        return {
            priority: 10,
            progressX: (args["progressX"] === undefined) ? 20 : args["progressX"],
            progressY: (args["progressY"] === undefined) ? 27 : args["progressY"],
            renderState: "default",
            render: progressRender
        };
    },
    chapterPanel: function(args){
        args["y"] = 5;
        args["x"] = 5;
        args["key"] = PARAMETERS.chapterPanel;
        args["size"] = [500, 300];
        return {
            rendererComponents: [ecs.chapterPanelRenderComponent(args)],
            absolutePositionComponent: ecs.absolutePositionComponent(args),
            uiClickComponent: ecs.panelUiClickComponent(args),
            panelComponent: ecs.panelComponent(args),
            uniqueComponent: ecs.uniqueComponent(args),
            identityComponent: ecs.identityComponent(args)
        };
    },
    captureInspirationMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.captureInspirationMenu)));
        return ecs.goalCompleteMenu(args);
    },
    personCurrencyAction: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            uniqueComponent: ecs.uniqueComponent(args),
            identityComponent: ecs.identityComponent(args),
            currencyActionComponent: ecs.currencyActionComponent(args)
        };
    },
    memory_aloneSad: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.memory_aloneSad)));
        return ecs.memory(args);
    },
    identifyProblemButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.identifyProblemButton)));
        return ecs.goalButton(args);
    },
    memory_loverSad: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.memory_loverSad)));
        return ecs.memory(args);
    },
    aloneness: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.aloneness)));
        return ecs.trait(args);
    },
    acceptAlienationMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.acceptAlienationMenu)));
        return ecs.goalCompleteMenu(args);
    },
    timeForceComponent: function(args){
        return {
            ticksPerTick: _.last(PARAMETERS.ticksPerTick)
        };
    },
    alienation_ext: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.alienation_ext)));
        return ecs.sourceExtension(args);
    },
    creepDeath: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            rendererComponents: [ecs.creepDeathRenderComponent(args)]
        };
    },
    refineInspirationButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.refineInspirationButton)));
        return ecs.goalButton(args);
    },
    memory_hurtLover: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.memory_hurtLover)));
        return ecs.memory(args);
    },
    hitRenderComponent: function(args){
        return {
            priority: 9,
            wasHit: false,
            renderState: "default",
            render: hitRender
        };
    },
    rechargeTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.rechargeTower)));
        return ecs.memoryTower(args);
    },
    makeExtraPathsExec: function(args){
        return {
            execute: makeExtraPathsExec
        };
    },
    goodSong: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.goodSong)));
        return ecs.trait(args);
    },
    makeRandomInspirationReplaceMarker: function(args){
        return {
            keysToUse: ["emoInspiration", "techInspiration"],
            getReplaceKeys: makeRandomInspirationGetReplaceKeys
        };
    },
    beCaredGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.beCaredGoalConstruction)));
        return ecs.construction(args);
    },
    flashback_sadness: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.flashback_sadness)));
        return ecs.emotionResource(args);
    },
    loveStrengthAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.loveStrengthAction)));
        return ecs.currencyAction(args);
    },
    cheerLoverGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.cheerLoverGoal)));
        return ecs.worldGoal(args);
    },
    releaseMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.releaseMenu)));
        return ecs.goalCompleteMenu(args);
    },
    resolveStrengthTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.resolveStrengthTower)));
        return ecs.tower(args);
    },
    toldWorthMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.toldWorthMenu)));
        return ecs.goalCompleteMenu(args);
    },
    techInspirationTraitConnectSong: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.techInspirationTraitConnectSong)));
        return ecs.traitConnect(args);
    },
    pathConnectRenderComponent: function(args){
        return {
            priority: 4,
            timeDrawn: 0,
            renderState: "default",
            render: pathConnectRender
        };
    },
    applyButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.applyButton)));
        return ecs.memoryTowerButton(args);
    },
    entityReplaceMarker: function(args){
        return {
            entitiesToMake: (args["entitiesToMake"] === undefined) ? [] : args["entitiesToMake"],
            skipFlag: (args["skipFlag"] === undefined) ? undefined : args["skipFlag"],
            getReplaceKeys: entityReplaceGetReplaceKeys
        };
    },
    revertFreshMusic: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.revertFreshMusic)));
        return ecs.entityReplace(args);
    },
    explainSadMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.explainSadMenu)));
        return ecs.goalCompleteMenu(args);
    },
    workerRequestComponent: function(args){
        return {
            needsWorker: trueFunction,
            calculateWorkerTicks: calculateWorkerTicks,
            workerRequested: args.sourceTrait + '_worker',
            workersActive: [],
            workerCount: 1,
            workerLife: 20
        };
    },
    quitWorkButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.quitWorkButton)));
        return ecs.goalButton(args);
    },
    bitterness: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.bitterness)));
        return ecs.trait(args);
    },
    project: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.project)));
        args["touchable"] = true;
        args["health"] = (args["health"] === undefined) ? 2000 : args["health"];
        args["panelKey"] = (args["panelKey"] === undefined) ? PARAMETERS.traitPanel : args["panelKey"];
        return {
            uiActionComponent: ecs.buildingUiActionComponent(args),
            experienceComponent: ecs.experienceComponent(args),
            createExecutables: [ecs.builtSourceCreateExec(args), ecs.makeExtraPathsExec(args)].concat([ecs.randomOpenPositionExec(args)]),
            timedDeathComponent: (args.timeToDeath) ? ecs.timedDeathComponent(args) : undefined,
            uiDataComponent: ecs.uiDataComponent(args),
            entityManagerComponent: ecs.entityManagerComponent(args),
            descriptionComponent: ecs.descriptionComponent(args),
            interactionComponent: ecs.interactionComponent(args),
            identityComponent: ecs.identityComponent(args),
            isaacEntityComponent: ecs.isaacEntityComponent(args),
            rendererComponents: [ecs.builtSourceRendererComponent(args)],
            focusTickResponseComponent: (args.aliveEntities) ? ecs.focusTickAliveMakerComponent(args) : undefined,
            menuCloseComponent: ecs.traitMenuCloseComponent(args),
            personHeldComponent: (args.personHeld) ? ecs.personHeldComponent(args) : undefined,
            clickComponent: ecs.fullMenuClickComponent(args),
            killExecutables: [ecs.killExtensions(args)].concat([ecs.makeEntityComponent(args)]),
            healthComponent: ecs.healthComponent(args),
            uniqueComponent: ecs.uniqueComponent(args),
            positionComponent: ecs.positionComponent(args),
            mapSpawnComponent: ecs.builtSourceMapSpawnComponent(args),
            junctionComponent: ecs.junctionComponent(args),
            playerMadeComponent: ecs.fixedPlayerMadeComponent(args)
        };
    },
    risingTextRenderComponent: function(args){
        if (args.text === undefined){ throw "ECS fail " + args.key + " missing text"; }
        if (args.effectPos === undefined){ throw "ECS fail " + args.key + " missing effectPos"; }
        return {
            effectTime: 0.5,
            render: risingTextRender,
            text: args['text'],
            effectPos: args['effectPos'],
            height: 100,
            priority: 10,
            currentHeight: 0,
            renderState: "default"
        };
    },
    explainMuseFail: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.explainMuseFail)));
        return ecs.menuModifier(args);
    },
    showMusicGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.showMusicGoal)));
        return ecs.worldGoal(args);
    },
    creepTickComponent: function(args){
        return {
            tick: creepTick,
            memoryChance: 0
        };
    },
    loseImpressedGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.loseImpressedGoal)));
        return ecs.personGoal(args);
    },
    winMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.winMenu)));
        return ecs.goalCompleteMenu(args);
    },
    settingsPanel: function(args){
        args["draggable"] = false;
        args["key"] = PARAMETERS.settingsPanel;
        args["size"] = [450, 400];
        return {
            rendererComponents: [ecs.settingsPanelRenderComponent(args)],
            absolutePositionComponent: ecs.absolutePositionComponent(args),
            uiClickComponent: ecs.panelUiClickComponent(args),
            panelComponent: ecs.panelComponent(args),
            uniqueComponent: ecs.uniqueComponent(args),
            identityComponent: ecs.identityComponent(args)
        };
    },
    bruteForceAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.bruteForceAction)));
        return ecs.currencyAction(args);
    },
    talkAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.talkAction)));
        return ecs.currencyAction(args);
    },
    pointCounterComponent: function(args){
        return {
            points: 0
        };
    },
    currencyRequestedFocusPathStopComponent: function(args){
        return {
            currencyEntities: (args["currencyEntities"] === undefined) ? undefined : args["currencyEntities"],
            interactWithTrain: currencyRequestedInteractWithTrain,
            currencyEffect: (args["currencyEffect"] === undefined) ? undefined : args["currencyEffect"],
            currenciesNeededAtPoint: currencyRequestedCurrenciesNeededAtPoint,
            updateCurrencyDictWithStop: updateCurrencyDictWithStop,
            getCurrencyComponent: trueFunction,
            currenciesExistAtPoint: currenciesExistAtPoint,
            currenciesMadeAtPoint: (args["currenciesMadeAtPoint"] === undefined) ? emptyArrayFunction : args["currenciesMadeAtPoint"],
            isPathEnd: true,
            requiredOpinions: (args["requiredOpinions"] === undefined) ? [] : args["requiredOpinions"],
            percentComplete: currencyRequestedFocusPathStopPercentComplete,
            checkFocusStopDisabled: checkFocusStopDisabled,
            isDisabled: false,
            enableChecks: (args["enableChecks"] === undefined) ? [] : args["enableChecks"]
        };
    },
    sinkWorkAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.sinkWorkAction)));
        return ecs.currencyAction(args);
    },
    irritabilityGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.irritabilityGoal)));
        return ecs.worldGoal(args);
    },
    explainRevert: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.explainRevert)));
        return ecs.menuModifier(args);
    },
    trainPathTowerActiveRender: function(args){
        return {
            priority: 3,
            renderState: "default",
            render: trainPathTowerActiveRender
        };
    },
    resourceModifierRenderComponent: function(args){
        return {
            render: towerTickModifierRender,
            image: args['image'],
            priority: 11,
            key: args['key'],
            getTowerForTickModifierRender: resourceModifierGetTowerForTickModifierRender,
            renderState: "default"
        };
    },
    captureThoughtMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.captureThoughtMenu)));
        return ecs.goalCompleteMenu(args);
    },
    tellQuitMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.tellQuitMenu)));
        return ecs.goalCompleteMenu(args);
    },
    particleEffect: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            lifeSentinelComponent: ecs.sourceLifeSentinelComponent(args),
            rendererComponents: [ecs.particleEffectRenderComponent(args)]
        };
    },
    tutorialUnpause: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            tutorialTextComponent: ecs.tutorialUnpauseTextComponent(args),
            rendererComponents: [ecs.tutorialUnpauseRenderComponent(args)]
        };
    },
    alienationDeposit: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.alienationDeposit)));
        return ecs.deposit(args);
    },
    experienceComponent: function(args){
        return {
            currentLevel: 1,
            updateExpansion: updateExpansion,
            xpPerLevel: 50,
            getXpLevel: getXpLevel,
            gainExp: gainExp,
            maxLevel: (args["maxLevel"] === undefined) ? PARAMETERS.maxExperience : args["maxLevel"]
        };
    },
    damageUpgradeCheck: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            createExecutables: [ecs.damamgeUpgradeCheckExec(args)]
        };
    },
    dismissSuccessGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.dismissSuccessGoal)));
        return ecs.worldGoal(args);
    },
    memoryFindPositionExec: function(args){
        if (args.startPos === undefined){ throw "ECS fail " + args.key + " missing startPos"; }
        return {
            execute: memoryFindPositionExec,
            startPos: args['startPos'],
            iterations: 25
        };
    },
    coolThingDeposit: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.coolThingDeposit)));
        return ecs.deposit(args);
    },
    toast: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.toast)));
        args["touchable"] = true;
        return {
            rendererComponents: [ecs.toastRenderComponent(args)],
            timedDeathComponent: ecs.timedDeathComponent(args),
            clickComponent: ecs.toastClickComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            identityComponent: ecs.identityComponent(args),
            positionComponent: ecs.positionComponent(args),
            notificationComponents: [ecs.toastNotificationComponent(args)]
        };
    },
    focusRenderComponent: function(args){
        return {
            priority: 9,
            renderState: "default",
            render: focusRender
        };
    },
    techProblemDeposit: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.techProblemDeposit)));
        return ecs.deposit(args);
    },
    memoryGoalCompleteExec: function(args){
        return {
            execute: memoryGoalCompleteExec,
            bonusTime: (args["bonusTime"] === undefined) ? 30 : args["bonusTime"],
            flareMemory: (args["flareMemory"] === undefined) ? false : args["flareMemory"],
            killMemory: (args["killMemory"] === undefined) ? false : args["killMemory"]
        };
    },
    selfLoathe: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.selfLoathe)));
        return ecs.currency(args);
    },
    curiousToMusicLove: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.curiousToMusicLove)));
        return ecs.transformExec(args);
    },
    dayPanel: function(args){
        return {
            panelComponent: ecs.setPanelComponent(args),
            absolutePositionComponent: ecs.dayPanelAbsolutePositionComponent(args),
            rendererComponents: [ecs.dayPanelRenderComponent(args)],
            uiClickComponent: ecs.panelUiClickComponent(args)
        };
    },
    panelComponent: function(args){
        return {
            panelButtons: [],
            expandPanel: expandPanel,
            minimized: true,
            canClose: true,
            closed: true,
            ignoreWhenMinimized: true
        };
    },
    tryForMeaningGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.tryForMeaningGoalConstruction)));
        return ecs.construction(args);
    },
    aloneTime: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.aloneTime)));
        return ecs.disableTrait(args);
    },
    steamAchievementNotificationComponent: function(args){
        return {
            notificationFunctions: simpleDict(PARAMETERS.goalComplete, checkSteamAchievement),
            serializeComponent: ecs.notificationSerializeComponent(args)
        };
    },
    trainPathMakerComponent: function(args){
        return {
            addTrainStop: addTrainStop,
            removeTrainStop: removeTrainStop,
            stops: [],
            checkTrainStop: checkTrainStop,
            memoizedCurrenciesNeeded: undefined,
            trainPathKey: PARAMETERS.trainPath,
            makeTrainPath: makeTrainPath,
            getPathCurrenciesNeeded: getPathCurrenciesNeeded
        };
    },
    difficultySettingComponent: function(args){
        return {
            difficulty: 0
        };
    },
    social_lover: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.social_lover)));
        return ecs.currency(args);
    },
    fadingAngryMemory: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.fadingAngryMemory)));
        return ecs.fadingMemory(args);
    },
    successDeposit: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.successDeposit)));
        return ecs.deposit(args);
    },
    endAlienationButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.endAlienationButton)));
        return ecs.goalButton(args);
    },
    memoryGoalButton: function(args){
        args["managerKey"] = args.sourceKey;
        args["subPanel"] = (args["subPanel"] === undefined) ? "Goals" : args["subPanel"];
        return {
            uiActionComponent: ecs.buttonUiActionComponent(args),
            buttonControlComponent: ecs.goalButtonControlComponent(args),
            createExecutables: [ecs.killIfFlagExec(args), ecs.markTraitNew(args)],
            timedDeathComponent: (args.timeToDeath) ? ecs.timedDeathComponent(args) : undefined,
            uiDataComponent: ecs.uiDataComponent(args),
            descriptionComponent: ecs.descriptionComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            interactionComponent: ecs.interactionComponent(args),
            identityComponent: ecs.identityComponent(args)
        };
    },
    dreamFutureMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.dreamFutureMenu)));
        return ecs.goalCompleteMenu(args);
    },
    reflectPastMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.reflectPastMenu)));
        return ecs.goalCompleteMenu(args);
    },
    findNewMusicMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.findNewMusicMenu)));
        return ecs.goalCompleteMenu(args);
    },
    quietDemonsStrengthMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.quietDemonsStrengthMenu)));
        return ecs.goalCompleteMenu(args);
    },
    helpRelationship: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.helpRelationship)));
        return ecs.entityReplace(args);
    },
    menuTutorialTextComponent: function(args){
        return {
            entityToMark: args['entityToMark'],
            tutorialDone: menuTutorialDone,
            checkTutorialUiKey: checkTutorialUiKey,
            nextEntities: (args["nextEntities"] === undefined) ? undefined : args["nextEntities"]
        };
    },
    emoInspiration_ext: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.emoInspiration_ext)));
        return ecs.sourceExtension(args);
    },
    chkForPlayerInterrupt: function(args){
        return {
            execute: chkForPlayerInterruptExec,
            playerExecute: ecs.makeParticleEffect(args)
        };
    },
    loveStrengthTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.loveStrengthTower)));
        return ecs.tower(args);
    },
    stuckBrute: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.stuckBrute)));
        return ecs.entityReplace(args);
    },
    indifferenceTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.indifferenceTower)));
        return ecs.tower(args);
    },
    selfHateMineConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.selfHateMineConstruction)));
        return ecs.emotionSourceConstruction(args);
    },
    openMenuMarker: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            rendererComponents: [ecs.openMenuMarkerRenderComponent(args)],
            openMenuMarkerComponent: ecs.openMenuMarkerComponent(args)
        };
    },
    songCounterComponent: function(args){
        return {
            addSongToCounter: addSongToCounter,
            songs: []
        };
    },
    autosaver: function(args){
        return {
            tickComponent: ecs.autosaveTickComponent(args)
        };
    },
    settingsPanelRenderComponent: function(args){
        return {
            sfxVolFunctions: undefined,
            fullAnimationTime: 0.1,
            yTextOffset: 5,
            drawPanelButton: drawPanelButton,
            quitFunction: undefined,
            renderState: "default",
            insetColor: "#000000",
            difficultyFunctions: undefined,
            priority: 18,
            snapPanelPos: snapPanelPos,
            soundVolFunctions: undefined,
            textColor: "#000000",
            backImage: "uiBack",
            drawImageButton: drawImageButton,
            render: outPanelRender,
            specificRender: settingsPanelRender,
            soundFunction: undefined,
            renderTime: 0,
            drawHelpButton: drawHelpButton,
            animatingTime: 0.1,
            fullscreenFunction: undefined,
            sfxFunction: undefined
        };
    },
    acceptDemonGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.acceptDemonGoal)));
        return ecs.worldGoal(args);
    },
    fadeExhaustion: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.fadeExhaustion)));
        return ecs.timedCreateEntities(args);
    },
    lackAccomplishmentButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.lackAccomplishmentButton)));
        return ecs.towerButton(args);
    },
    swallowBitternessButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.swallowBitternessButton)));
        return ecs.goalButton(args);
    },
    autosaveNotificationComponent: function(args){
        return {
            notificationFunctions: simpleDict(PARAMETERS.goalComplete, autosaveOnGoalComplete),
            serializeComponent: ecs.notificationSerializeComponent(args)
        };
    },
    museInspirationMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.museInspirationMenu)));
        return ecs.goalCompleteMenu(args);
    },
    flagQuitPrideGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.flagQuitPrideGoal)));
        return ecs.flaggedMake(args);
    },
    endHangOutButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.endHangOutButton)));
        return ecs.goalButton(args);
    },
    entityReplace: function(args){
        return {
            replaceMarker: ecs.entityReplaceMarker(args)
        };
    },
    endMuseButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.endMuseButton)));
        return ecs.goalButton(args);
    },
    tryComplimentsMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.tryComplimentsMenu)));
        return ecs.goalCompleteMenu(args);
    },
    sadSongToSong: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.sadSongToSong)));
        return ecs.transformExec(args);
    },
    entityPanelRenderComponent: function(args){
        if (args.entityImage === undefined){ throw "ECS fail " + args.key + " missing entityImage"; }
        return {
            drawImageButton: drawImageButton,
            fullAnimationTime: 0.1,
            yTextOffset: 5,
            backImage: "uiBack",
            entityImage: args['entityImage'],
            insetColor: "#000000",
            animatingTime: 0.1,
            priority: 18,
            render: outPanelRender,
            drawPanelButton: drawPanelButton,
            snapPanelPos: snapPanelPos,
            renderTime: 0,
            textColor: "#000000",
            renderState: "default",
            specificRender: entityPanelRender,
            drawHelpButton: drawHelpButton
        };
    },
    bitternessToAmbition: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.bitternessToAmbition)));
        return ecs.transformExec(args);
    },
    memory_selfHate: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.memory_selfHate)));
        return ecs.memory(args);
    },
    singleHealthComponent: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.singleHealthComponent)));
        return {
            kill: killHealthComponent,
            damageFactor: (args["damageFactor"] === undefined) ? 1 : args["damageFactor"],
            health: args['health'],
            maxHealth: args['health']
        };
    },
    songSatisfiedGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.songSatisfiedGoal)));
        return ecs.worldGoal(args);
    },
    captureWillGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.captureWillGoal)));
        return ecs.worldGoal(args);
    },
    angerRideMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.angerRideMenu)));
        return ecs.goalCompleteMenu(args);
    },
    listenEvent: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.listenEvent)));
        return ecs.randomReplace(args);
    },
    daydreamGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.daydreamGoalConstruction)));
        return ecs.construction(args);
    },
    pauseComponent: function(args){
        return {
            paused: true
        };
    },
    opinionTickComponent: function(args){
        return {
            emotionsToMake: (args["emotionsToMake"] === undefined) ? undefined : args["emotionsToMake"],
            tick: opinionTick,
            impulseTicks: (args["impulseTicks"] === undefined) ? 30 : args["impulseTicks"],
            ticks: 0
        };
    },
    quitWorkGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.quitWorkGoal)));
        return ecs.worldGoal(args);
    },
    mistake: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.mistake)));
        return ecs.currency(args);
    },
    sadFleshTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.sadFleshTowerConstruction)));
        return ecs.construction(args);
    },
    needLoverTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.needLoverTowerConstruction)));
        return ecs.construction(args);
    },
    duplicatePathExec: function(args){
        return {
            execute: duplicatePathExec
        };
    },
    letdown: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.letdown)));
        return ecs.currency(args);
    },
    hateWorkLoveDisable: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.hateWorkLoveDisable)));
        return ecs.disableTrait(args);
    },
    loveMineConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.loveMineConstruction)));
        return ecs.construction(args);
    },
    holdOnAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.holdOnAction)));
        return ecs.currencyAction(args);
    },
    friendTraitConnectSelfCore: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.friendTraitConnectSelfCore)));
        return ecs.traitConnect(args);
    },
    demon1Menu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.demon1Menu)));
        return ecs.goalCompleteMenu(args);
    },
    timedDeathRenderComponent: function(args){
        return {
            priority: 7,
            renderState: "default",
            render: timedDeathRender
        };
    },
    lovingCareTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.lovingCareTowerConstruction)));
        return ecs.construction(args);
    },
    showMusicButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.showMusicButton)));
        return ecs.goalButton(args);
    },
    indulgeBitternessMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.indulgeBitternessMenu)));
        return ecs.goalCompleteMenu(args);
    },
    dailyEntityMaker: function(args){
        return {
            dailyEntityMakerComponent: ecs.dailyEntityMakerComponent(args),
            createExecutables: [ecs.randomOpenPositionExec(args)],
            healthComponent: ecs.singleHealthComponent(args),
            identityComponent: ecs.identityComponent(args),
            positionComponent: ecs.positionComponent(args),
            dayResponseComponent: ecs.dailyEntityDayResponseComponent(args),
            playerMadeComponent: ecs.fixedPlayerMadeComponent(args)
        };
    },
    condCaptureInspirationGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.condCaptureInspirationGoal)));
        return ecs.conditionalMake(args);
    },
    museInspirationGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.museInspirationGoalConstruction)));
        return ecs.construction(args);
    },
    serializeComponent: function(args){
        if (args.serialize === undefined){ throw "ECS fail " + args.key + " missing serialize"; }
        if (args.postSerialize === undefined){ throw "ECS fail " + args.key + " missing postSerialize"; }
        return {
            serialize: args['serialize'],
            postSerialize: args['postSerialize']
        };
    },
    dreamFutureButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.dreamFutureButton)));
        return ecs.goalButton(args);
    },
    creepComponent: function(args){
        if (args.emotion === undefined){ throw "ECS fail " + args.key + " missing emotion"; }
        return {
            emotion: args['emotion'],
            towerModifier: (args["towerModifier"] === undefined) ? args['undefined'] : args["towerModifier"],
            selfDamage: PARAMETERS.creepSelfDamage
        };
    },
    tutorialUnpauseRenderComponent: function(args){
        if (args.text === undefined){ throw "ECS fail " + args.key + " missing text"; }
        if (args.yStart === undefined){ throw "ECS fail " + args.key + " missing yStart"; }
        return {
            priority: 24,
            text: args['text'],
            renderState: "default",
            yStart: args['yStart'],
            render: tutorialUnpauseRender
        };
    },
    dontGiveUpMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.dontGiveUpMenu)));
        return ecs.goalCompleteMenu(args);
    },
    explainCaptureFail: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.explainCaptureFail)));
        return ecs.menuModifier(args);
    },
    startNewSongMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.startNewSongMenu)));
        return ecs.goalCompleteMenu(args);
    },
    friendToRomance: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.friendToRomance)));
        return ecs.transformExec(args);
    },
    killTrain: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            createExecutables: [ecs.killTrainExec(args), ecs.killEntity(args)]
        };
    },
    opinionConditionalExec: function(args){
        if (args.person === undefined){ throw "ECS fail " + args.key + " missing person"; }
        if (args.opinions === undefined){ throw "ECS fail " + args.key + " missing opinions"; }
        if (args.entitiesToMake === undefined){ throw "ECS fail " + args.key + " missing entitiesToMake"; }
        return {
            sourceId: (args["sourceId"] === undefined) ? undefined : args["sourceId"],
            person: args['person'],
            execute: opinionConditional,
            entitiesToMake: args['entitiesToMake'],
            opinions: args['opinions']
        };
    },
    killTrait: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            createExecutables: [ecs.killTraitExec(args), ecs.killEntity(args)]
        };
    },
    pathMaker: function(args){
        return {
            replaceMarker: ecs.pathMakerReplaceMarker(args)
        };
    },
    opinionComponent: function(args){
        return {
            successOpinions: (args["successOpinions"] === undefined) ? {} : args["successOpinions"]
        };
    },
    nascentSong_ext: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.nascentSong_ext)));
        return ecs.sourceExtension(args);
    },
    spellRenderComponent: function(args){
        return {
            priority: 9,
            renderState: "default",
            render: spellRender
        };
    },
    disableMemoriesComponent: function(args){
        return {
            disableNewMemories: true,
            checkTraitDisabled: checkMemoriesDisabled
        };
    },
    goalCompleteMenuComponent: function(args){
        return {
            path: []
        };
    },
    songSatisfiedMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.songSatisfiedMenu)));
        return ecs.goalCompleteMenu(args);
    },
    techSolution: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.techSolution)));
        return ecs.currency(args);
    },
    identityComponent: function(args){
        if (args.key === undefined){ throw "ECS fail " + args.key + " missing key"; }
        if (args.uid === undefined){ throw "ECS fail " + args.key + " missing uid"; }
        return {
            uid: args['uid'],
            tags: (args["tags"] === undefined) ? [] : args["tags"],
            sourceId: (args["sourceId"] === undefined) ? undefined : args["sourceId"],
            image: (args["image"] === undefined) ? undefined : args["image"],
            sourceName: (args["sourceName"] === undefined) ? undefined : args["sourceName"],
            key: args['key'],
            touchable: (args["touchable"] === undefined) ? false : args["touchable"],
            name: (args["name"] === undefined) ? undefined : args["name"]
        };
    },
    startNewSongButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.startNewSongButton)));
        return ecs.goalButton(args);
    },
    faceEmotionMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.faceEmotionMenu)));
        return ecs.goalCompleteMenu(args);
    },
    keepTalkGoingGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.keepTalkGoingGoal)));
        return ecs.worldGoal(args);
    },
    musicMineConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.musicMineConstruction)));
        return ecs.construction(args);
    },
    panelRenderComponent: function(args){
        if (args.specificRender === undefined){ throw "ECS fail " + args.key + " missing specificRender"; }
        return {
            insetColor: "#000000",
            fullAnimationTime: 0.1,
            yTextOffset: 5,
            backImage: "uiBack",
            drawImageButton: drawImageButton,
            animatingTime: 0.1,
            priority: 18,
            render: outPanelRender,
            drawPanelButton: drawPanelButton,
            snapPanelPos: snapPanelPos,
            renderTime: 0,
            textColor: "#000000",
            renderState: "default",
            specificRender: args['specificRender'],
            drawHelpButton: drawHelpButton
        };
    },
    rendererSystemComponent: function(args){
        return {
            currentRenderState: "default"
        };
    },
    damageComponent: function(args){
        args["charges"] = (args["charges"] === undefined) ? 5 : args["charges"];
        if (args.damageMultipliers === undefined){ throw "ECS fail " + args.key + " missing damageMultipliers"; }
        if (args.charges === undefined){ throw "ECS fail " + args.key + " missing charges"; }
        return {
            checkTargetEntity: checkTargetEntity,
            ticks: 0,
            charges: args['charges'],
            targetCount: (args["targetCount"] === undefined) ? 1 : args["targetCount"],
            damage: (args["damage"] === undefined) ? 10 : args["damage"],
            damageMultipliers: args['damageMultipliers'],
            hitPos: undefined,
            maxCharges: args['charges'],
            calculateDamage: calculateDamage,
            getHitPos: getHitPos,
            impulseTicks: (args["impulseTicks"] === undefined) ? 0 : args["impulseTicks"]
        };
    },
    startNewSongGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.startNewSongGoalConstruction)));
        return ecs.construction(args);
    },
    alienation_lover: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.alienation_lover)));
        return ecs.currency(args);
    },
    rebuildMenuComponent: function(args){
        if (args.sourceKey === undefined){ throw "ECS fail " + args.key + " missing sourceKey"; }
        return {
            getRebuildButton: getRebuildButton,
            rebuildFunction: undefined,
            getRebuildFunction: getRebuildFunction,
            sourceKey: args['sourceKey']
        };
    },
    createModifyComponent: function(args){
        return {
            createModifiers: (args["createModifiers"] === undefined) ? {} : args["createModifiers"]
        };
    },
    loverCheck: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.loverCheck)));
        return ecs.checkQuality(args);
    },
    memory_workSatisfaction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.memory_workSatisfaction)));
        return ecs.memory(args);
    },
    allBuildingPanelRenderComponent: function(args){
        return {
            insetColor: "#000000",
            fullAnimationTime: 0.1,
            yTextOffset: 5,
            backImage: "uiBack",
            renderTime: 0,
            drawImageButton: drawImageButton,
            animatingTime: 0.1,
            pageDownFunction: undefined,
            priority: 18,
            render: outPanelRender,
            drawPanelButton: drawPanelButton,
            snapPanelPos: snapPanelPos,
            setCategoryFn: {},
            pageUpFunction: undefined,
            currentCategory: undefined,
            buttonGroupCount: 1,
            textColor: "#000000",
            renderState: "default",
            page: 1,
            specificRender: allBuildingPanelRender,
            drawHelpButton: drawHelpButton
        };
    },
    lackAccomplishmentTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.lackAccomplishmentTowerConstruction)));
        return ecs.construction(args);
    },
    notificationCurrencyComponent: function(args){
        if (args.notificationCurrency === undefined){ throw "ECS fail " + args.key + " missing notificationCurrency"; }
        if (args.currencyName === undefined){ throw "ECS fail " + args.key + " missing currencyName"; }
        return {
            notificationCurrency: args['notificationCurrency'],
            currencyName: args['currencyName']
        };
    },
    connectSourceReplaceMarker: function(args){
        if (args.sourceId === undefined){ throw "ECS fail " + args.key + " missing sourceId"; }
        return {
            sourceId: args['sourceId'],
            getReplaceKeys: connectSourceGetReplaceKeys
        };
    },
    ambition_ext: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.ambition_ext)));
        return ecs.sourceExtension(args);
    },
    noFocusNotifierRenderComponent: function(args){
        return {
            priority: 16,
            render: noFocusNotifierRender
        };
    },
    creep: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.creep)));
        return {
            pathVelocityComponent: ecs.pathVelocityComponent(args),
            rendererComponents: [ecs.creepRenderComponent(args), ecs.hitRenderComponent(args)],
            damageableComponent: ecs.damageableComponent(args),
            creepComponent: ecs.creepComponent(args),
            createExecutables: [ecs.creepCreateExec(args)],
            killExecutables: [ecs.creepDeathExec(args)],
            tickComponent: ecs.creepTickComponent(args),
            healthComponent: ecs.healthComponent(args),
            identityComponent: ecs.identityComponent(args),
            positionComponent: ecs.positionComponent(args),
            dayResponseComponent: ecs.currencyDayResponseComponent(args),
            loseControlExec: ecs.killEntity(args)
        };
    },
    isaacEntityComponent: function(args){
        return {
            endLifecycle: endLifecycle,
            startLifecycle: makeIsaacLifecycleEntities,
            makeLifecycleEntities: makeLifecycleEntities,
            entitiesMade: [],
            isaacEntities: (args["isaacEntities"] === undefined) ? [] : args["isaacEntities"]
        };
    },
    selfExtension: function(args){
        return {
            rendererComponents: [ecs.selfExtensionRender(args)],
            sourceExtensionComponent: ecs.sourceExtensionComponent(args),
            selfExtensionComponent: ecs.selfExtensionComponent(args),
            tickComponent: ecs.selfExtensionTickComponent(args),
            positionComponent: ecs.positionComponent(args)
        };
    },
    sustainFreshMusicMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.sustainFreshMusicMenu)));
        return ecs.goalCompleteMenu(args);
    },
    acceptDemonButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.acceptDemonButton)));
        return ecs.goalButton(args);
    },
    notification: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            notifyComponent: ecs.notifyComponent(args)
        };
    },
    memoryFadeComponent: function(args){
        if (args.sourceKey === undefined){ throw "ECS fail " + args.key + " missing sourceKey"; }
        return {
            sourceKey: args['sourceKey']
        };
    },
    tickComponent: function(args){
        return {
            tick: (args["tick"] === undefined) ? args['trueFunction'] : args["tick"]
        };
    },
    cheerMine: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.cheerMine)));
        return ecs.memoryResource(args);
    },
    overcomeExhaustGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.overcomeExhaustGoalConstruction)));
        return ecs.construction(args);
    },
    nascentSongTraitConnectMusic: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.nascentSongTraitConnectMusic)));
        return ecs.traitConnect(args);
    },
    loverBasePathHateWork: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.loverBasePathHateWork)));
        return ecs.pathMaker(args);
    },
    playerTriggeredNotification: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            identityComponent: ecs.IdentityComponent(args),
            notifyComponent: ecs.notifyComponent(args)
        };
    },
    toldWorthGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.toldWorthGoalConstruction)));
        return ecs.construction(args);
    },
    selfLoathing_ext: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.selfLoathing_ext)));
        return ecs.sourceExtension(args);
    },
    keepGoingMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.keepGoingMenu)));
        return ecs.goalCompleteMenu(args);
    },
    helpInfoPanel: function(args){
        args["size"] = [width, 678];
        return {
            rendererComponents: [ecs.helpInfoPanelRenderComponent(args)],
            absolutePositionComponent: ecs.absolutePositionComponent(args),
            uiClickComponent: ecs.panelUiClickComponent(args),
            panelComponent: ecs.panelComponent(args),
            pauseComponent: ecs.pauseComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            uniqueComponent: ecs.uniqueComponent(args),
            identityComponent: ecs.identityComponent(args)
        };
    },
    notifyEntityMaker: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            identityComponent: ecs.identityComponent(args),
            notificationComponents: [ecs.notifyEntityMakerNotificationComponent(args)]
        };
    },
    drainedSadMineConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.drainedSadMineConstruction)));
        return ecs.emotionSourceConstruction(args);
    },
    loveStrengthButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.loveStrengthButton)));
        return ecs.towerButton(args);
    },
    utilityPanelAbsolutePositionComponent: function(args){
        return {
            y: 678,
            x: 600,
            size: [250, 90]
        };
    },
    findMistakeGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.findMistakeGoal)));
        return ecs.worldGoal(args);
    },
    setTrainColor: function(args){
        return {
            execute: setTrainColorExec
        };
    },
    captureLoveMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.captureLoveMenu)));
        return ecs.goalCompleteMenu(args);
    },
    techInspiration_ext: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.techInspiration_ext)));
        return ecs.sourceExtension(args);
    },
    healthModifyComponent: function(args){
        return {
            idleHealthChange: -10,
            activeHealthChange: 30,
            activated: false,
            healthModifyAmount: healthModifyAmount
        };
    },
    dayKillExec: function(args){
        return {
            execute: dayKillExec
        };
    },
    hurtLoverMineConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.hurtLoverMineConstruction)));
        return ecs.emotionSourceConstruction(args);
    },
    startParty: function(args){
        return {
            createExecutables: [ecs.startPartyExec(args), ecs.killEntity(args)]
        };
    },
    traitHelpInfoPanel: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.traitHelpInfoPanel)));
        return ecs.helpInfoPanel(args);
    },
    startPartyExec: function(args){
        return {
            execute: startPartyExec,
            existsKey: "partyTimeDeposit",
            checkKey: "party"
        };
    },
    identifyProblemMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.identifyProblemMenu)));
        return ecs.goalCompleteMenu(args);
    },
    talkAboutLoverGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.talkAboutLoverGoalConstruction)));
        return ecs.construction(args);
    },
    makeAll: function(args){
        return {
            replaceMarker: ecs.makeAllReplaceMarker(args)
        };
    },
    makeRandomMultipleReplaceMarker: function(args){
        if (args.count === undefined){ throw "ECS fail " + args.key + " missing count"; }
        if (args.entitiesToMake === undefined){ throw "ECS fail " + args.key + " missing entitiesToMake"; }
        return {
            count: args['count'],
            minCount: (args["minCount"] === undefined) ? 1 : args["minCount"],
            entitiesToMake: args['entitiesToMake'],
            getReplaceKeys: makeRandomMultipleGetReplaceKeys
        };
    },
    failure: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.failure)));
        return ecs.currency(args);
    },
    solveMusicGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.solveMusicGoal)));
        return ecs.worldGoal(args);
    },
    prideStartMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.prideStartMenu)));
        return ecs.goalCompleteMenu(args);
    },
    minorFlirtMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.minorFlirtMenu)));
        return ecs.goalCompleteMenu(args);
    },
    depositFocusPathStopComponent: function(args){
        return {
            interactWithTrain: depositInteractWithTrain,
            currenciesNeededAtPoint: (args["currenciesNeededAtPoint"] === undefined) ? emptyArrayFunction : args["currenciesNeededAtPoint"],
            currenciesMadeAtPoint: depositCurrenciesMadeAtPoint,
            isDisabled: false,
            currenciesExistAtPoint: currenciesExistAtPoint,
            updateCurrencyDictWithStop: updateCurrencyDictWithStop,
            isPathEnd: (args["isPathEnd"] === undefined) ? false : args["isPathEnd"],
            checkFocusStopDisabled: checkFocusStopDisabled,
            requiredOpinions: (args["requiredOpinions"] === undefined) ? [] : args["requiredOpinions"],
            enableChecks: (args["enableChecks"] === undefined) ? [] : args["enableChecks"]
        };
    },
    endGoal: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            createExecutables: [ecs.endGoalExec(args), ecs.killEntity(args)]
        };
    },
    randomDemon: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.randomDemon)));
        return ecs.randomIfNone(args);
    },
    personTower: function(args){
        args["touchable"] = true;
        args["canExpandInto"] = false;
        args["isPathEnd"] = (args["isPathEnd"] === undefined) ? (args.damageMultipliers ? true : false) : args["isPathEnd"];
        args["tags"] = (args["tags"] === undefined) ? [args.key] : args["tags"];
        return {
            focusPathStopComponent: ecs.towerFocusPathStopComponent(args),
            rendererComponents: [ecs.pathStopRenderComponent(args), ecs.damageRenderComponent(args)],
            focusTickResponseComponent: ecs.towerFocusTickResponseComponent(args),
            healthModifyComponent: ecs.healthModifyComponent(args),
            personHeldComponent: ecs.personHeldComponent(args),
            absolutePositionComponent: ecs.absolutePositionComponent(args),
            damageComponent: (args.damageMultipliers) ? ecs.damageComponent(args) : undefined,
            managedComponent: ecs.managedComponent(args),
            createExecutables: [ecs.makeExtraPathsExec(args)].concat([ecs.towerCreateExec(args)]),
            junctionComponent: ecs.junctionComponent(args),
            clickComponent: ecs.fullMenuClickComponent(args),
            uiDataComponent: ecs.uiDataComponent(args),
            tickComponent: ecs.towerTickComponent(args),
            towerComponent: ecs.towerComponent(args),
            descriptionComponent: ecs.descriptionComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            interactionComponent: ecs.interactionComponent(args),
            identityComponent: ecs.identityComponent(args),
            positionComponent: ecs.positionComponent(args),
            playerMadeComponent: ecs.playerMadeComponent(args)
        };
    },
    museFleshAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.museFleshAction)));
        return ecs.currencyAction(args);
    },
    joyRideMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.joyRideMenu)));
        return ecs.goalCompleteMenu(args);
    },
    makePlaythingGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.makePlaythingGoalConstruction)));
        return ecs.construction(args);
    },
    trainPathMakingPanel: function(args){
        args["y"] = 5;
        args["x"] = width - 355;
        args["key"] = PARAMETERS.trainPathMakingPanel;
        args["size"] = [350, 300];
        return {
            rendererComponents: [ecs.trainPathMakingPanelRender(args)],
            absolutePositionComponent: ecs.absolutePositionComponent(args),
            uiClickComponent: ecs.panelUiClickComponent(args),
            panelComponent: ecs.panelComponent(args),
            pauseComponent: ecs.pauseComponent(args),
            uniqueComponent: ecs.uniqueComponent(args),
            identityComponent: ecs.identityComponent(args)
        };
    },
    traitConnectReplaceMarker: function(args){
        if (args.startKey === undefined){ throw "ECS fail " + args.key + " missing startKey"; }
        if (args.endKey === undefined){ throw "ECS fail " + args.key + " missing endKey"; }
        return {
            startKey: args['startKey'],
            endKey: args['endKey'],
            getReplaceKeys: traitConnectGetReplaceKeys
        };
    },
    killLoveDeposit: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.killLoveDeposit)));
        return ecs.entityKill(args);
    },
    worldGoalParticleEffectLifeSentinelComponent: function(args){
        if (args.sourceId === undefined){ throw "ECS fail " + args.key + " missing sourceId"; }
        return {
            sourceId: args['sourceId'],
            checkAlive: checkWorldGoalParticleEffectAlive
        };
    },
    solutionTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.solutionTower)));
        return ecs.tower(args);
    },
    towerFlashRenderComponent: function(args){
        return {
            priority: 6,
            images: ["border0", "border1", "border2"],
            renderState: "default",
            timeToDeath: 0.5,
            render: towerFlashRender
        };
    },
    trainRenderComponent: function(args){
        if (args.trainColor === undefined){ throw "ECS fail " + args.key + " missing trainColor"; }
        return {
            priority: 11,
            render: trainRender,
            linePos: false,
            timeDrawn: 0,
            previousPositions: [],
            renderState: "default",
            trainColor: args['trainColor']
        };
    },
    deriveCoolThingMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.deriveCoolThingMenu)));
        return ecs.goalCompleteMenu(args);
    },
    museInspirationButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.museInspirationButton)));
        return ecs.goalButton(args);
    },
    managedComponent: function(args){
        if (args.sourceTrait === undefined){ throw "ECS fail " + args.key + " missing sourceTrait"; }
        return {
            managerKey: args['sourceTrait'],
            addExperienceToManager: addExperienceToManager,
            killOnTransform: (args["killOnTransform"] === undefined) ? false : args["killOnTransform"],
            isManagingEntity: isManagingEntity
        };
    },
    proveSuccessGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.proveSuccessGoal)));
        args["touchable"] = true;
        args["particleKey"] = PARAMETERS.newParticle;
        args["killOnTransform"] = false;
        args["canExpandInto"] = false;
        args["playerKillable"] = !!(args.constructed);
        args["killOnTransform"] = (args["killOnTransform"] === undefined) ? true : args["killOnTransform"];
        return {
            uiActionComponent: ecs.buildingUiActionComponent(args),
            goalTrackComponent: ecs.checkWorldGoalComponent(args),
            createExecutables: [ecs.checkManagedComponentExec(args), (args.randomPosition) ? ecs.randomOpenPositionExec(args) : undefined, ecs.makeExtraPathsExec(args), ecs.chkForPlayerInterrupt(args), ecs.buildingConnectExec(args), (args.timeToDeath) ? ecs.timedDifficultyModifyExec(args) : undefined],
            timedDeathComponent: (args.timeToDeath) ? ecs.worldGoalTimedDeathComponent(args) : undefined,
            uiDataComponent: ecs.uiDataComponent(args),
            descriptionComponent: ecs.descriptionComponent(args),
            interactionComponent: ecs.interactionComponent(args),
            identityComponent: ecs.identityComponent(args),
            rendererComponents: [ecs.worldGoalRenderComponent(args), ecs.progressRenderComponent(args), ecs.timedDeathRenderComponent(args)],
            focusTickResponseComponent: (args.aliveEntities) ? ecs.aliveMakeComponent(args) : undefined,
            managedComponent: (args.sourceTrait) ? ecs.managedComponent(args) : undefined,
            flipComponent: (args.flipEntity) ? ecs.flipComponent(args) : undefined,
            worldGoalComponent: ecs.worldGoalComponent(args),
            focusPathStopComponent: ecs.worldGoalFocusPathStopComponent(args),
            clickComponent: ecs.fullMenuClickComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            uniqueComponent: ecs.uniqueComponent(args),
            positionComponent: ecs.positionComponent(args),
            notificationComponents: [ecs.worldGoalNotificationComponent(args)],
            junctionComponent: ecs.junctionComponent(args),
            pauseComponent: ecs.pauseComponent(args),
            playerMadeComponent: ecs.playerMadeComponent(args)
        };
    },
    focusMakingComponent: function(args){
        if (args.focusCount === undefined){ throw "ECS fail " + args.key + " missing focusCount"; }
        return {
            ai: (args["ai"] === undefined) ? false : args["ai"],
            ticks: 2,
            focusEfficiency: focusEfficiency,
            impulseTicks: 22,
            focusCount: args['focusCount'],
            focusUsing: 0
        };
    },
    memoryGoalTrackComponent: function(args){
        return {
            successExecutables: [ecs.goalCompleteExec(args)].concat([ecs.trackActionables(args), ecs.makeEntityComponent(args), ecs.makeGoalMenuExecutable(args)]).concat([ecs.memoryGoalCompleteExec(args)]),
            checkCompleted: checkWorldGoalComponent
        };
    },
    wobbleRendererComponent: function(args){
        return {
            wobble: 0,
            getWobble: getWobble
        };
    },
    focusComponent: function(args){
        return {
            focusMax: 100,
            focusUsed: 0
        };
    },
    bullet: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            rendererComponents: [ecs.bulletRenderComponent(args)],
            tickComponent: ecs.bulletTickComponent(args)
        };
    },
    sadFleshTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.sadFleshTower)));
        return ecs.tower(args);
    },
    willMineConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.willMineConstruction)));
        return ecs.construction(args);
    },
    makePlaythingGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.makePlaythingGoal)));
        return ecs.worldGoal(args);
    },
    findNewMusicGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.findNewMusicGoal)));
        return ecs.worldGoal(args);
    },
    resourceModifierComponent: function(args){
        if (args.stopResources === undefined){ throw "ECS fail " + args.key + " missing stopResources"; }
        if (args.doubleResources === undefined){ throw "ECS fail " + args.key + " missing doubleResources"; }
        if (args.towerId === undefined){ throw "ECS fail " + args.key + " missing towerId"; }
        return {
            doubleResources: args['doubleResources'],
            stopResources: args['stopResources'],
            towerId: args['towerId']
        };
    },
    disableMemoriesTickComponent: function(args){
        if (args.name === undefined){ throw "ECS fail " + args.key + " missing name"; }
        return {
            tick: disableMemoriesTick,
            name: args['name']
        };
    },
    indulgeCuriosityMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.indulgeCuriosityMenu)));
        return ecs.goalCompleteMenu(args);
    },
    randomCarousel: function(args){
        return {
            animationTime: 3,
            flatDisplayStartTime: undefined,
            carousel: undefined,
            render: randomCarouselRender,
            goodImage: "randomGoodImage",
            frameImage: "frame",
            badImage: "randomBadImage",
            goodResult: undefined,
            setMarker: randomCarouselSetMarker,
            imgSize: [100, 100],
            animationDone: false
        };
    },
    careDeposit: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.careDeposit)));
        return ecs.deposit(args);
    },
    stateComponent: function(args){
        return {
            paused: false,
            playerControlled: false
        };
    },
    makeIfMissing: function(args){
        return {
            createExecutables: [ecs.makeIfMissingExec(args), ecs.killEntity(args)]
        };
    },
    easyWorkTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.easyWorkTower)));
        return ecs.tower(args);
    },
    nullEntity: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            createExecutables: [ecs.killEntity(args)]
        };
    },
    makePlaythingButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.makePlaythingButton)));
        return ecs.goalButton(args);
    },
    selfDamageEffectRenderComponent: function(args){
        return {
            priority: 19,
            timeDrawn: 0,
            renderState: "default",
            animationTime: 0.3,
            render: selfDamageEffectRender
        };
    },
    endRealTalkGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.endRealTalkGoalConstruction)));
        return ecs.construction(args);
    },
    understandTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.understandTower)));
        return ecs.memoryTower(args);
    },
    museFishGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.museFishGoalConstruction)));
        return ecs.construction(args);
    },
    findNewMusicButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.findNewMusicButton)));
        return ecs.goalButton(args);
    },
    selfLoathingToBitterness: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.selfLoathingToBitterness)));
        return ecs.transformExec(args);
    },
    allCurrencyRenderClear: function(args){
        return {
            priority: 1,
            renderState: "default",
            render: allCurrencyRenderClear
        };
    },
    gameEndRenderComponent: function(args){
        return {
            priority: 24,
            renderState: "default",
            render: gameEndRender
        };
    },
    goalHelpInfoPanel: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.goalHelpInfoPanel)));
        return ecs.helpInfoPanel(args);
    },
    suspicionGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.suspicionGoal)));
        return ecs.worldGoal(args);
    },
    understandingDeposit: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.understandingDeposit)));
        return ecs.deposit(args);
    },
    failureMineButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.failureMineButton)));
        return ecs.resourceButton(args);
    },
    solveMusicGoalPathTechInspiration: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.solveMusicGoalPathTechInspiration)));
        return ecs.pathMaker(args);
    },
    closestOpenPositionExec: function(args){
        if (args.startPos === undefined){ throw "ECS fail " + args.key + " missing startPos"; }
        return {
            execute: closestOpenPositionExec,
            startPos: args['startPos']
        };
    },
    accomplishment: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.accomplishment)));
        return ecs.currency(args);
    },
    trainComponent: function(args){
        return {
            getCurrenciesHeld: getCurrenciesHeld,
            carriage: undefined,
            calculateTrainCurrencies: calculateTrainCurrencies,
            lastDrawPos: [0, 0],
            getCurrentCarriage: getCurrentCarriage
        };
    },
    goalTrackComponent: function(args){
        if (args.checkCompleted === undefined){ throw "ECS fail " + args.key + " missing checkCompleted"; }
        return {
            successExecutables: [ecs.goalCompleteExec(args)],
            checkCompleted: args['checkCompleted']
        };
    },
    relaxGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.relaxGoal)));
        return ecs.worldGoal(args);
    },
    talkAboutSelfMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.talkAboutSelfMenu)));
        return ecs.goalCompleteMenu(args);
    },
    indifferenceButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.indifferenceButton)));
        return ecs.towerButton(args);
    },
    lostControlDeathExec: function(args){
        return {
            execute: lostControlDeathExec
        };
    },
    identifyProblemGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.identifyProblemGoalConstruction)));
        return ecs.construction(args);
    },
    indulgeCuriosityButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.indulgeCuriosityButton)));
        return ecs.goalButton(args);
    },
    hurtLover: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.hurtLover)));
        return ecs.emotionSource(args);
    },
    explainSuspicionSuccess: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.explainSuspicionSuccess)));
        return ecs.menuModifier(args);
    },
    deriveStrengthGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.deriveStrengthGoalConstruction)));
        return ecs.construction(args);
    },
    boost: function(args){
        return {
            identityComponent: ecs.identityComponent(args),
            rendererComponents: [ecs.boostRenderComponent(args)],
            boostSlotComponent: ecs.boostSlotComponent(args),
            createExecutables: [ecs.randomOpenPositionExec(args)],
            positionComponent: ecs.positionComponent(args)
        };
    },
    shareCoolMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.shareCoolMenu)));
        return ecs.goalCompleteMenu(args);
    },
    destinyButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.destinyButton)));
        return ecs.goalButton(args);
    },
    steamThought: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.steamThought)));
        return ecs.steamAchievement(args);
    },
    boostSlotComponent: function(args){
        if (args.traits === undefined){ throw "ECS fail " + args.key + " missing traits"; }
        return {
            traits: args['traits'],
            checkEntityForBoost: checkEntityForBoost
        };
    },
    stopDemonStrengthGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.stopDemonStrengthGoal)));
        return ecs.worldGoal(args);
    },
    tickEmotionComponent: function(args){
        if (args.currency === undefined){ throw "ECS fail " + args.key + " missing currency"; }
        return {
            currency: args['currency'],
            checkTickContinue: checkTickContinue,
            traits: (args["traits"] === undefined) ? [] : args["traits"]
        };
    },
    deriveCoolThingButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.deriveCoolThingButton)));
        return ecs.goalButton(args);
    },
    understanding: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.understanding)));
        return ecs.currency(args);
    },
    unhappyDisable: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.unhappyDisable)));
        return ecs.disableTrait(args);
    },
    autoPathReplaceMarker: function(args){
        return {
            getReplaceKeys: autoPathGetReplaceKeys
        };
    },
    checkMakeDeposit: function(args){
        return {
            execute: checkMakeDeposit,
            checkMakeDeposit: (args["checkMakeDeposit"] === undefined) ? true : args["checkMakeDeposit"]
        };
    },
    refineAccomplishmentButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.refineAccomplishmentButton)));
        return ecs.towerButton(args);
    },
    sayRightButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.sayRightButton)));
        return ecs.towerButton(args);
    },
    playerMadeComponent: function(args){
        return {
            playerKillable: (args["playerKillable"] === undefined) ? true : args["playerKillable"],
            canExpandInto: (args["canExpandInto"] === undefined) ? true : args["canExpandInto"]
        };
    },
    loveInsult: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.loveInsult)));
        return ecs.randomReplace(args);
    },
    socialMuseMine: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.socialMuseMine)));
        return ecs.resource(args);
    },
    constructionTickComponent: function(args){
        return {
            tick: constructionTick
        };
    },
    lifeSentinelComponent: function(args){
        if (args.checkAlive === undefined){ throw "ECS fail " + args.key + " missing checkAlive"; }
        return {
            checkAlive: args['checkAlive']
        };
    },
    emotionResourceMap: function(args){
        return {
            emotionResourceMapComponent: ecs.emotionResourceMapComponent(args)
        };
    },
    endHangOutGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.endHangOutGoal)));
        return ecs.worldGoal(args);
    },
    loverInsultMineConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.loverInsultMineConstruction)));
        return ecs.emotionSourceConstruction(args);
    },
    unimpressed: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.unimpressed)));
        return ecs.setFlag(args);
    },
    solveMusicGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.solveMusicGoalConstruction)));
        return ecs.construction(args);
    },
    restartWorkGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.restartWorkGoal)));
        return ecs.worldGoal(args);
    },
    beCaredMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.beCaredMenu)));
        return ecs.goalCompleteMenu(args);
    },
    memory: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.memory)));
        args["touchable"] = true;
        args["path"] = [];
        args["interactionKeys"] = [PARAMETERS.self];
        args["randomTraitPosition"] = false;
        args["health"] = (args["health"] === undefined) ? 225 : args["health"];
        args["panelKey"] = (args["panelKey"] === undefined) ? PARAMETERS.traitPanel : args["panelKey"];
        return {
            uiActionComponent: ecs.buildingUiActionComponent(args),
            memoryComponent: ecs.memoryComponent(args),
            experienceComponent: ecs.experienceComponent(args),
            createExecutables: [ecs.memoryFindPositionExec(args), ecs.builtSourceCreateExec(args)],
            timedDeathComponent: (PARAMETERS.timedMemories) ? ecs.timedDeathComponent(args) : undefined,
            uiDataComponent: ecs.uiDataComponent(args),
            entityManagerComponent: ecs.entityManagerComponent(args),
            descriptionComponent: ecs.descriptionComponent(args),
            interactionComponent: ecs.interactionComponent(args),
            identityComponent: ecs.identityComponent(args),
            isaacEntityComponent: ecs.memoryIsaacEntityComponent(args),
            rendererComponents: [ecs.builtSourceRendererComponent(args)],
            focusTickResponseComponent: (args.aliveEntities) ? ecs.focusTickAliveMakerComponent(args) : undefined,
            tickComponent: ecs.memoryTickComponent(args),
            menuCloseComponent: ecs.traitMenuCloseComponent(args),
            personHeldComponent: (args.personHeld) ? ecs.personHeldComponent(args) : undefined,
            resourceComponent: undefined,
            clickComponent: ecs.fullMenuClickComponent(args),
            killExecutables: [ecs.killExtensions(args)],
            healthComponent: ecs.healthComponent(args),
            uniqueComponent: ecs.uniqueComponent(args),
            positionComponent: ecs.positionComponent(args),
            mapSpawnComponent: ecs.builtSourceMapSpawnComponent(args),
            junctionComponent: ecs.junctionComponent(args),
            playerMadeComponent: ecs.fixedPlayerMadeComponent(args)
        };
    },
    understandAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.understandAction)));
        return ecs.currencyAction(args);
    },
    fatigueGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.fatigueGoal)));
        return ecs.worldGoal(args);
    },
    plaything: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.plaything)));
        return ecs.currency(args);
    },
    markTraitNew: function(args){
        return {
            execute: markTraitNewExec,
            particleEffect: "newTraitParticle"
        };
    },
    findMistakeGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.findMistakeGoalConstruction)));
        return ecs.construction(args);
    },
    explainRideFail: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.explainRideFail)));
        return ecs.menuModifier(args);
    },
    stopDemonLoveMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.stopDemonLoveMenu)));
        return ecs.goalCompleteMenu(args);
    },
    towerHelpInfoPanel: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.towerHelpInfoPanel)));
        return ecs.helpInfoPanel(args);
    },
    discardFreshMusicMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.discardFreshMusicMenu)));
        return ecs.goalCompleteMenu(args);
    },
    stopDemonSelfEsteemMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.stopDemonSelfEsteemMenu)));
        return ecs.goalCompleteMenu(args);
    },
    damageUpgradeComponent: function(args){
        if (args.towerKeys === undefined){ throw "ECS fail " + args.key + " missing towerKeys"; }
        if (args.damageUpgrade === undefined){ throw "ECS fail " + args.key + " missing damageUpgrade"; }
        return {
            damageUpgrade: args['damageUpgrade'],
            towerKeys: args['towerKeys']
        };
    },
    swallowBitternessMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.swallowBitternessMenu)));
        return ecs.goalCompleteMenu(args);
    },
    curious_ext: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.curious_ext)));
        return ecs.sourceExtension(args);
    },
    pointNotification: function(args){
        args["notificationKey"] = PARAMETERS.addPoints;
        args["notificationArgs"] = {points: args.points};
        return {
            healthComponent: ecs.singleHealthComponent(args),
            notifyComponent: ecs.notifyComponent(args)
        };
    },
    trainPath: function(args){
        return {
            rendererComponents: [ecs.trainPathTowerActiveRender(args), ecs.trainPathSecondRender(args), ecs.trainPathCircRenderComponent(args)],
            trainPathComponent: ecs.trainPathComponent(args),
            menuCloseComponent: ecs.trainPathMenuCloseComponent(args),
            createExecutables: [ecs.setTrainColor(args)],
            killExecutables: [ecs.blinkTrainStops(args)],
            healthComponent: ecs.singleHealthComponent(args),
            interactionComponent: ecs.interactionComponent(args),
            focusCostComponent: ecs.focusCostComponent(args)
        };
    },
    angerRideButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.angerRideButton)));
        return ecs.memoryGoalButton(args);
    },
    hangingOutToFriend: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.hangingOutToFriend)));
        return ecs.transformExec(args);
    },
    killTrainExec: function(args){
        if (args.source === undefined){ throw "ECS fail " + args.key + " missing source"; }
        return {
            source: args['source'],
            execute: killTrainExec
        };
    },
    makeEmoInspirationGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.makeEmoInspirationGoal)));
        return ecs.memoryGoal(args);
    },
    interestWorkGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.interestWorkGoal)));
        return ecs.personGoal(args);
    },
    sourceExtension: function(args){
        args["touchable"] = true;
        return {
            rendererComponents: [ecs.sourceExtensionRenderComponent(args)],
            personHeldComponent: (args.personHeld) ? ecs.personHeldComponent(args) : undefined,
            createExecutables: [ecs.makeExtraPathsExec(args)],
            sourceExtensionComponent: ecs.sourceExtensionComponent(args),
            clickComponent: ecs.sourceExtensionClickComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            identityComponent: ecs.identityComponent(args),
            positionComponent: ecs.positionComponent(args),
            playerMadeComponent: ecs.fixedPlayerMadeComponent(args)
        };
    },
    care_lover: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.care_lover)));
        return ecs.currency(args);
    },
    hundredHealthComponent: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.hundredHealthComponent)));
        return {
            kill: killHealthComponent,
            damageFactor: (args["damageFactor"] === undefined) ? 1 : args["damageFactor"],
            health: args['health'],
            maxHealth: args['health']
        };
    },
    museFleshTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.museFleshTowerConstruction)));
        return ecs.construction(args);
    },
    interactionComponent: function(args){
        return {
            connectionKeys: [],
            interactionKeys: (args["interactionKeys"] === undefined) ? PARAMETERS.interactionKeys : args["interactionKeys"],
            canInteractSelf: canInteractSelf,
            canInteract: canInteract
        };
    },
    changeOpinionExec: function(args){
        return {
            execute: changeOpinion,
            opinionToAdd: (args["opinionToAdd"] === undefined) ? undefined : args["opinionToAdd"],
            opinionToRemove: (args["opinionToRemove"] === undefined) ? undefined : args["opinionToRemove"]
        };
    },
    swallowOpinionKill: function(args){
        return {
            execute: swallowOpinionKill
        };
    },
    struggleComplimentsMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.struggleComplimentsMenu)));
        return ecs.goalCompleteMenu(args);
    },
    museFishButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.museFishButton)));
        return ecs.goalButton(args);
    },
    clearTraitExec: function(args){
        if (args.trait === undefined){ throw "ECS fail " + args.key + " missing trait"; }
        return {
            trait: args['trait'],
            execute: clearTraitExec
        };
    },
    constructionComponent: function(args){
        if (args.currencyRequested === undefined){ throw "ECS fail " + args.key + " missing currencyRequested"; }
        if (args.constructionIdentifier === undefined){ throw "ECS fail " + args.key + " missing constructionIdentifier"; }
        return {
            constructionIdentifier: args['constructionIdentifier'],
            killWithoutPath: (args["killWithoutPath"] === undefined) ? false : args["killWithoutPath"],
            ticked: (args["ticked"] === undefined) ? false : args["ticked"],
            construct: construct,
            currencyRequested: args['currencyRequested'],
            origCurrencyRequested: _.clone(args.currencyRequested)
        };
    },
    helpPanelComponent: function(args){
        return {
            openHelpPanel: openHelpPanel
        };
    },
    allGoalPanelRenderComponent: function(args){
        return {
            insetColor: "#000000",
            fullAnimationTime: 0.1,
            yTextOffset: 5,
            backImage: "uiBack",
            drawImageButton: drawImageButton,
            animatingTime: 0.1,
            priority: 18,
            render: outPanelRender,
            drawPanelButton: drawPanelButton,
            snapPanelPos: snapPanelPos,
            renderTime: 0,
            textColor: "#000000",
            renderState: "default",
            specificRender: allGoalPanelRender,
            drawHelpButton: drawHelpButton
        };
    },
    releasedSong: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.releasedSong)));
        return ecs.trait(args);
    },
    chapter1: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.chapter1)));
        return ecs.gameStage(args);
    },
    makeGainControlMenuExec: function(args){
        if (args.emotion === undefined){ throw "ECS fail " + args.key + " missing emotion"; }
        return {
            emotion: args['emotion'],
            execute: makeGainControlMenuExec,
            gainControlMenuKey: "gainControl"
        };
    },
    towerFocusTickResponseComponent: function(args){
        return {
            isReady: false,
            isWaiting: false,
            focusTick: towerFocusTick
        };
    },
    reflectSuccessButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.reflectSuccessButton)));
        return ecs.goalButton(args);
    },
    intrigueButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.intrigueButton)));
        return ecs.towerButton(args);
    },
    hateWork_ext: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.hateWork_ext)));
        return ecs.sourceExtension(args);
    },
    endAlienationMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.endAlienationMenu)));
        return ecs.goalCompleteMenu(args);
    },
    checkQualityTickComponent: function(args){
        if (args.qualityKey === undefined){ throw "ECS fail " + args.key + " missing qualityKey"; }
        if (args.minQuality === undefined){ throw "ECS fail " + args.key + " missing minQuality"; }
        if (args.minQualityEntities === undefined){ throw "ECS fail " + args.key + " missing minQualityEntities"; }
        return {
            qualityKey: args['qualityKey'],
            tick: checkQualityTick,
            minQualityEntities: args['minQualityEntities'],
            minQuality: args['minQuality']
        };
    },
    killDayEndComponent: function(args){
        args["dayEnd"] = dayEndKillEntity;
        return {
            dayEnd: (args["dayEnd"] === undefined) ? undefined : args["dayEnd"],
            dayStart: (args["dayStart"] === undefined) ? undefined : args["dayStart"],
            dayChange: (args["dayChange"] === undefined) ? undefined : args["dayChange"]
        };
    },
    sustainFreshMusicGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.sustainFreshMusicGoal)));
        return ecs.worldGoal(args);
    },
    joyRideButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.joyRideButton)));
        return ecs.memoryGoalButton(args);
    },
    makeSadSong: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.makeSadSong)));
        return ecs.entityReplace(args);
    },
    fireUpGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.fireUpGoal)));
        return ecs.memoryGoal(args);
    },
    spellComponent: function(args){
        if (args.spellCost === undefined){ throw "ECS fail " + args.key + " missing spellCost"; }
        if (args.spellFn === undefined){ throw "ECS fail " + args.key + " missing spellFn"; }
        return {
            spellCost: args['spellCost'],
            spellButtonFunction: undefined,
            spellFn: args['spellFn']
        };
    },
    tellQuitButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.tellQuitButton)));
        return ecs.goalButton(args);
    },
    constructionMenuCloseComponent: function(args){
        return {
            execute: constructionMenuCloseExec
        };
    },
    damageUpgrade: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            identityComponent: ecs.identityComponent(args),
            damageUpgradeComponent: ecs.damageUpgradeComponent(args)
        };
    },
    creepRenderComponent: function(args){
        if (args.image === undefined){ throw "ECS fail " + args.key + " missing image"; }
        return {
            priority: 12,
            image: args['image'],
            renderState: "default",
            render: currencyRender,
            renderPos: undefined
        };
    },
    dayPanelAbsolutePositionComponent: function(args){
        return {
            y: 678,
            x: 0,
            size: [300, 90]
        };
    },
    impulseFocusPathStopComponent: function(args){
        return {
            interactWithTrain: impulseFocusStopInteractWithTrain,
            currenciesNeededAtPoint: (args["currenciesNeededAtPoint"] === undefined) ? emptyArrayFunction : args["currenciesNeededAtPoint"],
            currenciesMadeAtPoint: impulseFocusStopCurrenciesMadeAtPoint,
            isDisabled: false,
            currenciesExistAtPoint: currenciesExistAtPoint,
            updateCurrencyDictWithStop: updateCurrencyDictWithStop,
            isPathEnd: (args["isPathEnd"] === undefined) ? false : args["isPathEnd"],
            impulseCurrency: "impulse",
            checkFocusStopDisabled: checkFocusStopDisabled,
            requiredOpinions: (args["requiredOpinions"] === undefined) ? [] : args["requiredOpinions"],
            enableChecks: (args["enableChecks"] === undefined) ? [] : args["enableChecks"]
        };
    },
    selfLoathingMine: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.selfLoathingMine)));
        return ecs.resource(args);
    },
    sayRight_lover: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.sayRight_lover)));
        return ecs.currency(args);
    },
    songSatisfiedGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.songSatisfiedGoalConstruction)));
        return ecs.construction(args);
    },
    notificationComponent: function(args){
        if (args.notificationFunctions === undefined){ throw "ECS fail " + args.key + " missing notificationFunctions"; }
        return {
            notificationFunctions: args['notificationFunctions'],
            serializeComponent: ecs.notificationSerializeComponent(args)
        };
    },
    romance_ext: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.romance_ext)));
        return ecs.sourceExtension(args);
    },
    sourceFocusPathStopComponent: function(args){
        return {
            interactWithTrain: falseFunction,
            currenciesNeededAtPoint: sourceCurrenciesNeededAtPoint,
            currenciesMadeAtPoint: (args["currenciesMadeAtPoint"] === undefined) ? emptyArrayFunction : args["currenciesMadeAtPoint"],
            isDisabled: false,
            currenciesExistAtPoint: currenciesExistAtPoint,
            updateCurrencyDictWithStop: updateCurrencyDictWithStop,
            isPathEnd: (args["isPathEnd"] === undefined) ? false : args["isPathEnd"],
            checkFocusStopDisabled: checkFocusStopDisabled,
            requiredOpinions: (args["requiredOpinions"] === undefined) ? [] : args["requiredOpinions"],
            enableChecks: (args["enableChecks"] === undefined) ? [] : args["enableChecks"]
        };
    },
    impressGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.impressGoalConstruction)));
        return ecs.construction(args);
    },
    sinkWorkTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.sinkWorkTowerConstruction)));
        return ecs.construction(args);
    },
    makingWorkToFriend: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.makingWorkToFriend)));
        return ecs.transformExec(args);
    },
    cursorRenderComponent: function(args){
        return {
            priority: 19,
            renderState: "default",
            render: cursorRender
        };
    },
    settingTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.settingTowerConstruction)));
        return ecs.construction(args);
    },
    museBase_ext: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.museBase_ext)));
        return ecs.sourceExtension(args);
    },
    gameStateEntity: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.gameStateEntity)));
        return {
            rendererSystemComponent: ecs.rendererSystemComponent(args),
            uniqueComponent: ecs.assertUniqueComponent(args),
            identityComponent: ecs.identityComponent(args)
        };
    },
    memory_loverJoy: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.memory_loverJoy)));
        return ecs.memory(args);
    },
    angerFleshTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.angerFleshTowerConstruction)));
        return ecs.construction(args);
    },
    traitMaker: function(args){
        return {
            traitMakerComponent: ecs.traitMakerComponent(args)
        };
    },
    sustainAchievementMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.sustainAchievementMenu)));
        return ecs.goalCompleteMenu(args);
    },
    endAlienationStrengthMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.endAlienationStrengthMenu)));
        return ecs.goalCompleteMenu(args);
    },
    selfLoathingMineButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.selfLoathingMineButton)));
        return ecs.resourceButton(args);
    },
    buildPanelAbsolutePositionComponent: function(args){
        return {
            y: 678,
            x: 850,
            size: [174, 90]
        };
    },
    failureAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.failureAction)));
        return ecs.currencyAction(args);
    },
    stopDemonStrengthMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.stopDemonStrengthMenu)));
        return ecs.goalCompleteMenu(args);
    },
    sayRightAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.sayRightAction)));
        return ecs.currencyAction(args);
    },
    dreamFutureGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.dreamFutureGoal)));
        return ecs.worldGoal(args);
    },
    playMusicTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.playMusicTowerConstruction)));
        return ecs.construction(args);
    },
    lovingToFriend: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.lovingToFriend)));
        return ecs.transformExec(args);
    },
    instantEmotionResourceTickComponent: function(args){
        return {
            tick: instantEmotionResourceTick
        };
    },
    demolishPathRenderComponent: function(args){
        return {
            priority: 3,
            demolishPathButtonFunction: undefined,
            renderState: "default",
            render: demolishPathButtonRender
        };
    },
    shareTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.shareTower)));
        return ecs.tower(args);
    },
    menuModifierComponent: function(args){
        return {
            completeText: (args["completeText"] === undefined) ? undefined : args["completeText"],
            name: (args["name"] === undefined) ? undefined : args["name"]
        };
    },
    notifyComponent: function(args){
        if (args.notificationKey === undefined){ throw "ECS fail " + args.key + " missing notificationKey"; }
        if (args.notificationArgs === undefined){ throw "ECS fail " + args.key + " missing notificationArgs"; }
        return {
            notifyEntityKey: (args["notifyEntityKey"] === undefined) ? undefined : args["notifyEntityKey"],
            notificationKey: args['notificationKey'],
            notificationArgs: args['notificationArgs']
        };
    },
    showMusicDisable: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.showMusicDisable)));
        return ecs.disableTrait(args);
    },
    tryOldJokesGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.tryOldJokesGoalConstruction)));
        return ecs.construction(args);
    },
    dismissFailureGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.dismissFailureGoal)));
        return ecs.worldGoal(args);
    },
    gameStageStartMenu: function(args){
        args["y"] = 5;
        args["x"] = width / 2 - 325;
        args["size"] = [650, 600];
        return {
            rendererComponents: [ecs.goalCompleteMenuRender(args)],
            absolutePositionComponent: ecs.absolutePositionComponent(args),
            uiClickComponent: ecs.panelUiClickComponent(args),
            createExecutables: [ecs.goalCompleteMenuCreateExec(args)],
            goalCompleteMenuComponent: ecs.goalCompleteMenuComponent(args),
            panelComponent: ecs.goalCompleteMenuPanelComponent(args),
            pauseComponent: ecs.pauseComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            rebuildMenuComponent: ecs.rebuildMenuComponent(args),
            identityComponent: ecs.identityComponent(args)
        };
    },
    pushYourselfMineConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.pushYourselfMineConstruction)));
        return ecs.construction(args);
    },
    drownAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.drownAction)));
        return ecs.currencyAction(args);
    },
    techProblem: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.techProblem)));
        return ecs.currency(args);
    },
    loverSadMine: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.loverSadMine)));
        return ecs.emotionResource(args);
    },
    socialMineConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.socialMineConstruction)));
        return ecs.construction(args);
    },
    boostRenderComponent: function(args){
        return {
            priority: 6,
            renderState: args['planeOfExistence'],
            render: boostRender
        };
    },
    stuckProblemMineConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.stuckProblemMineConstruction)));
        return ecs.emotionSourceConstruction(args);
    },
    resourceFocusPathStopComponent: function(args){
        return {
            interactWithTrain: resourceInteractWithTrain,
            currenciesNeededAtPoint: (args["currenciesNeededAtPoint"] === undefined) ? emptyArrayFunction : args["currenciesNeededAtPoint"],
            currenciesMadeAtPoint: resourceCurrenciesMadeAtPoint,
            isDisabled: false,
            currenciesExistAtPoint: currenciesExistAtPoint,
            updateCurrencyDictWithStop: updateCurrencyDictWithStop,
            isPathEnd: (args["isPathEnd"] === undefined) ? false : args["isPathEnd"],
            checkFocusStopDisabled: checkFocusStopDisabled,
            requiredOpinions: (args["requiredOpinions"] === undefined) ? [] : args["requiredOpinions"],
            enableChecks: (args["enableChecks"] === undefined) ? [] : args["enableChecks"]
        };
    },
    alonenessToAmbition: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.alonenessToAmbition)));
        return ecs.transformExec(args);
    },
    releaseGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.releaseGoal)));
        return ecs.worldGoal(args);
    },
    swallowBitternessGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.swallowBitternessGoal)));
        return ecs.worldGoal(args);
    },
    damageableComponent: function(args){
        if (args.emotion === undefined){ throw "ECS fail " + args.key + " missing emotion"; }
        if (args.emotionTags === undefined){ throw "ECS fail " + args.key + " missing emotionTags"; }
        return {
            emotion: args['emotion'],
            traits: (args["traits"] === undefined) ? [] : args["traits"],
            getAll: getAllEmotionParts,
            emotionTags: args['emotionTags'],
            damageEffects: []
        };
    },
    captureMusicGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.captureMusicGoal)));
        return ecs.worldGoal(args);
    },
    acceptDemonMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.acceptDemonMenu)));
        return ecs.goalCompleteMenu(args);
    },
    killTechInspiration: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.killTechInspiration)));
        return ecs.entityKill(args);
    },
    dailyEntityMakerComponent: function(args){
        if (args.dayStartEntity === undefined){ throw "ECS fail " + args.key + " missing dayStartEntity"; }
        if (args.dayCount === undefined){ throw "ECS fail " + args.key + " missing dayCount"; }
        return {
            dayCount: args['dayCount'],
            dayStartEntity: args['dayStartEntity'],
            daysToMake: args['dayCount']
        };
    },
    rollSleevesGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.rollSleevesGoalConstruction)));
        return ecs.construction(args);
    },
    openMenuMarkerRenderComponent: function(args){
        return {
            priority: 14,
            renderState: "default",
            render: openMenuMarkerRender
        };
    },
    friend: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.friend)));
        return ecs.trait(args);
    },
    endAlienationStrengthGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.endAlienationStrengthGoalConstruction)));
        return ecs.construction(args);
    },
    convinceSelfGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.convinceSelfGoal)));
        return ecs.memoryGoal(args);
    },
    makingWork: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.makingWork)));
        return ecs.trait(args);
    },
    fleshSongMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.fleshSongMenu)));
        return ecs.goalCompleteMenu(args);
    },
    impressMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.impressMenu)));
        return ecs.goalCompleteMenu(args);
    },
    museVentButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.museVentButton)));
        return ecs.towerButton(args);
    },
    mapSpawnComponent: function(args){
        if (args.mapSpawnKey === undefined){ throw "ECS fail " + args.key + " missing mapSpawnKey"; }
        return {
            mapSpawnPos: mapSpawnPos,
            mapPostSpawn: trueFunction,
            mapShouldSpawn: trueFunction,
            mapSpawn: mapSpawn,
            mapSpawnKey: args['mapSpawnKey']
        };
    },
    cheerUpGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.cheerUpGoal)));
        return ecs.personGoal(args);
    },
    pathButtonRenderComponent: function(args){
        return {
            priority: 1,
            renderState: "default",
            render: pathButtonRender,
            pathButtonFunction: undefined
        };
    },
    apologizeMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.apologizeMenu)));
        return ecs.goalCompleteMenu(args);
    },
    healEntities: function(args){
        return {
            execute: healEntities,
            entitiesToHeal: (args["entitiesToHeal"] === undefined) ? {} : args["entitiesToHeal"]
        };
    },
    rechargeTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.rechargeTowerConstruction)));
        return ecs.memoryConstruction(args);
    },
    gainControl_sadness: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.gainControl_sadness)));
        return ecs.gainControlMenu(args);
    },
    revertMemoryExec: function(args){
        if (args.sourceId === undefined){ throw "ECS fail " + args.key + " missing sourceId"; }
        return {
            sourceId: args['sourceId'],
            execute: revertMemoryExec
        };
    },
    selfLoathingMineConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.selfLoathingMineConstruction)));
        return ecs.construction(args);
    },
    nameRenderComponent: function(args){
        return {
            priority: 8,
            renderState: "default",
            name: args['name'],
            render: nameRender
        };
    },
    gameStageDayResponseComponent: function(args){
        return {
            dayEnd: gameStageDayEnd,
            dayStart: (args["dayStart"] === undefined) ? undefined : args["dayStart"],
            daysLeft: 2,
            dayChange: (args["dayChange"] === undefined) ? undefined : args["dayChange"]
        };
    },
    drown_muse: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.drown_muse)));
        return ecs.currency(args);
    },
    satisfyResult: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.satisfyResult)));
        return ecs.randomReplace(args);
    },
    completeTechMineConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.completeTechMineConstruction)));
        return ecs.construction(args);
    },
    anger: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.anger)));
        return ecs.creep(args);
    },
    realTalk_ext: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.realTalk_ext)));
        return ecs.sourceExtension(args);
    },
    socialMineButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.socialMineButton)));
        return ecs.resourceButton(args);
    },
    overcomeDistanceMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.overcomeDistanceMenu)));
        return ecs.goalCompleteMenu(args);
    },
    demolishButton: function(args){
        return {
            rendererComponents: [ecs.demolishButtonRenderComponent(args)]
        };
    },
    museVentTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.museVentTowerConstruction)));
        return ecs.construction(args);
    },
    currencyPathComponent: function(args){
        return {
            currencies: (args["currencies"] === undefined) ? [] : args["currencies"],
            buildingActions: (args["buildingActions"] === undefined) ? [] : args["buildingActions"]
        };
    },
    killUnhappy: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.killUnhappy)));
        return ecs.entityKill(args);
    },
    resolveStrengthTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.resolveStrengthTowerConstruction)));
        return ecs.construction(args);
    },
    pain: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.pain)));
        return ecs.currency(args);
    },
    songSatisfaction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.songSatisfaction)));
        return ecs.currency(args);
    },
    memory_stuckProblem: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.memory_stuckProblem)));
        return ecs.memory(args);
    },
    aloneness_ext: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.aloneness_ext)));
        return ecs.sourceExtension(args);
    },
    fishComplimentsButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.fishComplimentsButton)));
        return ecs.goalButton(args);
    },
    listenDemonsGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.listenDemonsGoal)));
        return ecs.worldGoal(args);
    },
    daydreamButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.daydreamButton)));
        return ecs.goalButton(args);
    },
    shareCoolGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.shareCoolGoal)));
        return ecs.personGoal(args);
    },
    strengthTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.strengthTowerConstruction)));
        return ecs.construction(args);
    },
    cheerPrideGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.cheerPrideGoal)));
        return ecs.memoryGoal(args);
    },
    settingButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.settingButton)));
        return ecs.towerButton(args);
    },
    placedTower: function(args){
        return {
            replaceMarker: ecs.placedTowerReplaceMarker(args)
        };
    },
    worldGoalTimedDeathComponent: function(args){
        return {
            killOnTimeOut: (args["killOnTimeOut"] === undefined) ? [] : args["killOnTimeOut"],
            timeToDeath: args['timeToDeath'],
            deathExecs: [ecs.trackActionables(args)],
            makeOnTimeOut: (args["makeOnTimeOut"] === undefined) ? [] : args["makeOnTimeOut"]
        };
    },
    daydreaming_ext: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.daydreaming_ext)));
        return ecs.sourceExtension(args);
    },
    memoryResourceFocusTickResponseComponent: function(args){
        return {
            firstTick: true,
            destination: (args["destination"] === undefined) ? undefined : args["destination"],
            focusTick: resourceFocusTick,
            focusTickHook: triggerFlashback,
            flashbackChance: 0.25,
            emotionTags: (args["emotionTags"] === undefined) ? undefined : args["emotionTags"]
        };
    },
    failureTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.failureTower)));
        return ecs.tower(args);
    },
    memoryExtension: function(args){
        args["touchable"] = true;
        args["image"] = args.source ? args.source.rendererComponents[0].image : null;
        args["name"] = args.source ? args.source.identityComponent.name : null;
        args["key"] = args.source ? (args.source.identityComponent.key + '_ext') : null;
        args["sourceKey"] = args.source ? args.source.identityComponent.key : null;
        return {
            rendererComponents: [ecs.sourceExtensionRenderComponent(args)],
            positionComponent: ecs.positionComponent(args),
            personHeldComponent: (args.personHeld) ? ecs.personHeldComponent(args) : undefined,
            createExecutables: [ecs.makeExtraPathsExec(args)],
            clickComponent: ecs.sourceExtensionClickComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            identityComponent: ecs.identityComponent(args),
            sourceExtensionComponent: ecs.sourceExtensionComponent(args),
            playerMadeComponent: ecs.fixedPlayerMadeComponent(args)
        };
    },
    trainVelocityComponent: function(args){
        if (args.trainPath === undefined){ throw "ECS fail " + args.key + " missing trainPath"; }
        if (args.allPaths === undefined){ throw "ECS fail " + args.key + " missing allPaths"; }
        return {
            lastStop: undefined,
            trainStops: undefined,
            stopIndex: 0,
            currentPath: undefined,
            ticks: 0,
            isMoving: false,
            allPaths: args['allPaths'],
            trainPath: args['trainPath'],
            velocity: 1
        };
    },
    reflectMineConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.reflectMineConstruction)));
        return ecs.construction(args);
    },
    loverSadMineConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.loverSadMineConstruction)));
        return ecs.emotionSourceConstruction(args);
    },
    apologizeGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.apologizeGoal)));
        return ecs.worldGoal(args);
    },
    selfLoathingToAmbition: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.selfLoathingToAmbition)));
        return ecs.transformExec(args);
    },
    printKillExec: function(args){
        return {
            execute: printKillExec
        };
    },
    steamAchievementComponent: function(args){
        if (args.keyToCheck === undefined){ throw "ECS fail " + args.key + " missing keyToCheck"; }
        if (args.steamAchievementKey === undefined){ throw "ECS fail " + args.key + " missing steamAchievementKey"; }
        return {
            steamAchievementKey: args['steamAchievementKey'],
            keyToCheck: args['keyToCheck']
        };
    },
    gainControlMenuRender: function(args){
        if (args.emotion === undefined){ throw "ECS fail " + args.key + " missing emotion"; }
        if (args.textColor === undefined){ throw "ECS fail " + args.key + " missing textColor"; }
        if (args.text === undefined){ throw "ECS fail " + args.key + " missing text"; }
        if (args.barColor === undefined){ throw "ECS fail " + args.key + " missing barColor"; }
        return {
            emotion: args['emotion'],
            priority: 24,
            fullAnimationTime: 0.25,
            render: gainControlMenuRender,
            brainImage: "miniSelf",
            barWidth: 50,
            percentageComplete: 0,
            barColor: args['barColor'],
            text: args['text'],
            barPos: [250, 150],
            backImage: "uiBack",
            renderTime: 0,
            brainPos: [265, 152],
            textColor: args['textColor'],
            renderState: "default",
            resetVelTime: 0,
            brainVel: 0
        };
    },
    traitMakerComponent: function(args){
        if (args.traitMakerEmotion === undefined){ throw "ECS fail " + args.key + " missing traitMakerEmotion"; }
        if (args.traitsToMake === undefined){ throw "ECS fail " + args.key + " missing traitsToMake"; }
        return {
            traitMakerEmotion: args['traitMakerEmotion'],
            traitsToMake: args['traitsToMake']
        };
    },
    loveStrengthTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.loveStrengthTowerConstruction)));
        return ecs.construction(args);
    },
    sadFleshAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.sadFleshAction)));
        return ecs.currencyAction(args);
    },
    thoughtTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.thoughtTowerConstruction)));
        return ecs.construction(args);
    },
    resource: function(args){
        args["touchable"] = true;
        args["canExpandInto"] = false;
        return {
            interactionComponent: ecs.interactionComponent(args),
            junctionComponent: ecs.junctionComponent(args),
            rendererComponents: [ecs.pathStopRenderComponent(args)],
            focusTickResponseComponent: ecs.resourceFocusTickResponseComponent(args),
            managedComponent: ecs.managedComponent(args),
            createExecutables: [ecs.makeExtraPathsExec(args)],
            focusPathStopComponent: ecs.resourceFocusPathStopComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            clickComponent: ecs.fullMenuClickComponent(args),
            killExecutables: [ecs.killResourceCurrencies(args)],
            descriptionComponent: ecs.descriptionComponent(args),
            resourceComponent: ecs.resourceComponent(args),
            uiDataComponent: ecs.uiDataComponent(args),
            identityComponent: ecs.identityComponent(args),
            positionComponent: ecs.positionComponent(args),
            playerMadeComponent: ecs.playerMadeComponent(args)
        };
    },
    earworm: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.earworm)));
        return ecs.timedFocusHolder(args);
    },
    fadingJoyMemory: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.fadingJoyMemory)));
        return ecs.fadingMemory(args);
    },
    cheerPrideGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.cheerPrideGoalConstruction)));
        return ecs.memoryConstruction(args);
    },
    sadRideMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.sadRideMenu)));
        return ecs.goalCompleteMenu(args);
    },
    rollSleevesMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.rollSleevesMenu)));
        return ecs.goalCompleteMenu(args);
    },
    acceptDemonGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.acceptDemonGoalConstruction)));
        return ecs.construction(args);
    },
    worldGoalRenderComponent: function(args){
        if (args.image === undefined){ throw "ECS fail " + args.key + " missing image"; }
        return {
            outlineImage: false,
            render: worldGoalRender,
            wobble: 0,
            image: args['image'],
            priority: 6,
            timeToBlink: 1,
            newImage: "newOutline",
            getWobble: getWobble,
            renderState: "default",
            renderTrainIcon: renderTrainIcon,
            outlineTime: 0
        };
    },
    endMuseGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.endMuseGoal)));
        return ecs.worldGoal(args);
    },
    selfLoathingToAloneness: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.selfLoathingToAloneness)));
        return ecs.transformExec(args);
    },
    currencyOverloadComponent: function(args){
        if (args.resourceKey === undefined){ throw "ECS fail " + args.key + " missing resourceKey"; }
        if (args.currencyKey === undefined){ throw "ECS fail " + args.key + " missing currencyKey"; }
        if (args.overloadEntities === undefined){ throw "ECS fail " + args.key + " missing overloadEntities"; }
        if (args.currencyCount === undefined){ throw "ECS fail " + args.key + " missing currencyCount"; }
        return {
            resourceKey: args['resourceKey'],
            currencyCount: args['currencyCount'],
            currencyKey: args['currencyKey'],
            overloadEntities: args['overloadEntities']
        };
    },
    overcomeDistanceGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.overcomeDistanceGoal)));
        args["touchable"] = true;
        args["particleKey"] = PARAMETERS.newParticle;
        args["killOnTransform"] = false;
        args["canExpandInto"] = false;
        args["playerKillable"] = !!(args.constructed);
        args["killOnTransform"] = (args["killOnTransform"] === undefined) ? true : args["killOnTransform"];
        return {
            uiActionComponent: ecs.buildingUiActionComponent(args),
            goalTrackComponent: ecs.checkWorldGoalComponent(args),
            createExecutables: [ecs.checkManagedComponentExec(args), (args.randomPosition) ? ecs.randomOpenPositionExec(args) : undefined, ecs.makeExtraPathsExec(args), ecs.chkForPlayerInterrupt(args), ecs.buildingConnectExec(args), (args.timeToDeath) ? ecs.timedDifficultyModifyExec(args) : undefined],
            timedDeathComponent: (args.timeToDeath) ? ecs.worldGoalTimedDeathComponent(args) : undefined,
            uiDataComponent: ecs.uiDataComponent(args),
            descriptionComponent: ecs.descriptionComponent(args),
            interactionComponent: ecs.interactionComponent(args),
            identityComponent: ecs.identityComponent(args),
            rendererComponents: [ecs.worldGoalRenderComponent(args), ecs.progressRenderComponent(args), ecs.timedDeathRenderComponent(args)],
            focusTickResponseComponent: (args.aliveEntities) ? ecs.aliveMakeComponent(args) : undefined,
            managedComponent: (args.sourceTrait) ? ecs.managedComponent(args) : undefined,
            flipComponent: (args.flipEntity) ? ecs.flipComponent(args) : undefined,
            worldGoalComponent: ecs.worldGoalComponent(args),
            focusPathStopComponent: ecs.worldGoalFocusPathStopComponent(args),
            clickComponent: ecs.fullMenuClickComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            uniqueComponent: ecs.uniqueComponent(args),
            positionComponent: ecs.positionComponent(args),
            notificationComponents: [ecs.worldGoalNotificationComponent(args)],
            junctionComponent: ecs.junctionComponent(args),
            pauseComponent: ecs.pauseComponent(args),
            playerMadeComponent: ecs.playerMadeComponent(args)
        };
    },
    joy: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.joy)));
        return {
            pathVelocityComponent: ecs.pathVelocityComponent(args),
            rendererComponents: [ecs.creepRenderComponent(args), ecs.hitRenderComponent(args)],
            damageableComponent: ecs.damageableComponent(args),
            creepComponent: ecs.creepComponent(args),
            createExecutables: [ecs.creepCreateExec(args)].concat([ecs.joyCreateExec(args)]),
            killExecutables: [ecs.creepDeathExec(args)],
            tickComponent: ecs.creepTickComponent(args),
            healthComponent: ecs.healthComponent(args),
            identityComponent: ecs.identityComponent(args),
            positionComponent: ecs.positionComponent(args),
            dayResponseComponent: ecs.currencyDayResponseComponent(args),
            loseControlExec: ecs.killEntity(args)
        };
    },
    sayRightTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.sayRightTowerConstruction)));
        return ecs.construction(args);
    },
    freshMusic: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.freshMusic)));
        return ecs.trait(args);
    },
    solveMusicMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.solveMusicMenu)));
        return ecs.goalCompleteMenu(args);
    },
    drainGoalCreate: function(args){
        return {
            currencyToPull: "will",
            execute: drainGoalCreate
        };
    },
    fireUpMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.fireUpMenu)));
        return ecs.goalCompleteMenu(args);
    },
    romanceToLoving: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.romanceToLoving)));
        return ecs.transformExec(args);
    },
    endRealTalkMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.endRealTalkMenu)));
        return ecs.goalCompleteMenu(args);
    },
    loverBasePathMusicLove: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.loverBasePathMusicLove)));
        return ecs.pathMaker(args);
    },
    rawInspiration: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.rawInspiration)));
        return ecs.currency(args);
    },
    flashback_sadnessConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.flashback_sadnessConstruction)));
        return ecs.emotionSourceConstruction(args);
    },
    cynicismTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.cynicismTowerConstruction)));
        return ecs.construction(args);
    },
    controlPanelKeyPressComponent: function(args){
        return {
            handleKeyPress: controlPanelHandleKeyPress
        };
    },
    musicMine: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.musicMine)));
        return ecs.resource(args);
    },
    desireImprovementButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.desireImprovementButton)));
        return ecs.goalButton(args);
    },
    instantEmotionResource: function(args){
        args["touchable"] = true;
        args["canExpandInto"] = false;
        args["playerKillable"] = false;
        return {
            focusPathStopComponent: ecs.resourceFocusPathStopComponent(args),
            rendererComponents: [ecs.pathStopRenderComponent(args)],
            focusTickResponseComponent: ecs.resourceFocusTickResponseComponent(args),
            uiDataComponent: ecs.uiDataComponent(args),
            managedComponent: ecs.managedComponent(args),
            createExecutables: [ecs.makeExtraPathsExec(args)].concat([ecs.emotionResourceDifficultyExec(args)]),
            junctionComponent: ecs.junctionComponent(args),
            clickComponent: ecs.fullMenuClickComponent(args),
            killExecutables: [ecs.killResourceCurrencies(args)],
            resourceComponent: ecs.resourceComponent(args),
            descriptionComponent: ecs.descriptionComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            interactionComponent: ecs.interactionComponent(args),
            identityComponent: ecs.identityComponent(args),
            positionComponent: ecs.positionComponent(args),
            loseControlExec: ecs.killEntity(args),
            playerMadeComponent: ecs.playerMadeComponent(args),
            tickComponent: ecs.instantEmotionResourceTickComponent(args)
        };
    },
    easyWorkAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.easyWorkAction)));
        return ecs.currencyAction(args);
    },
    dismissSuccessGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.dismissSuccessGoalConstruction)));
        return ecs.construction(args);
    },
    apologizeButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.apologizeButton)));
        return ecs.towerButton(args);
    },
    rendererComponent: function(args){
        if (args.render === undefined){ throw "ECS fail " + args.key + " missing render"; }
        if (args.priority === undefined){ throw "ECS fail " + args.key + " missing priority"; }
        return {
            priority: args['priority'],
            renderState: "default",
            render: args['render']
        };
    },
    demonsTraitConnectMusic: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.demonsTraitConnectMusic)));
        return ecs.traitConnect(args);
    },
    dreamGreatness: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.dreamGreatness)));
        return ecs.currency(args);
    },
    tryOldJokesMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.tryOldJokesMenu)));
        return ecs.goalCompleteMenu(args);
    },
    towerTickComponent: function(args){
        return {
            tick: towerTick,
            timeToAttrition: (args["timeToAttrition"] === undefined) ? PARAMETERS.dayTicks : args["timeToAttrition"]
        };
    },
    exhaustion: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.exhaustion)));
        return ecs.trait(args);
    },
    prideBurstMineConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.prideBurstMineConstruction)));
        return ecs.emotionSourceConstruction(args);
    },
    requestComponent: function(args){
        if (args.currencyRequested === undefined){ throw "ECS fail " + args.key + " missing currencyRequested"; }
        return {
            currencyRequested: args['currencyRequested']
        };
    },
    sourceExtensionComponent: function(args){
        if (args.sourceKey === undefined){ throw "ECS fail " + args.key + " missing sourceKey"; }
        return {
            sourceKey: args['sourceKey']
        };
    },
    creepDeathRenderComponent: function(args){
        if (args.image === undefined){ throw "ECS fail " + args.key + " missing image"; }
        return {
            timeLeft: 1,
            render: creepDeathRender,
            renderPos: (args["renderPos"] === undefined) ? undefined : args["renderPos"],
            image: args['image'],
            priority: 12,
            renderState: "default",
            size: [20, 17]
        };
    },
    selfRelianceToAmbition: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.selfRelianceToAmbition)));
        return ecs.transformExec(args);
    },
    emotionResourceMapComponent: function(args){
        if (args.emotionToMap === undefined){ throw "ECS fail " + args.key + " missing emotionToMap"; }
        if (args.emotionMappedEntities === undefined){ throw "ECS fail " + args.key + " missing emotionMappedEntities"; }
        return {
            emotionMappedEntities: args['emotionMappedEntities'],
            startManagedEntities: (args["startManagedEntities"] === undefined) ? [] : args["startManagedEntities"],
            emotionToMap: args['emotionToMap']
        };
    },
    talkEmotion: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.talkEmotion)));
        return ecs.randomReplace(args);
    },
    reflectAloneGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.reflectAloneGoal)));
        return ecs.worldGoal(args);
    },
    builtSourceCreateExec: function(args){
        return {
            sector: (args["sector"] === undefined) ? undefined : args["sector"],
            skipBuiltSourceCreate: (args["skipBuiltSourceCreate"] === undefined) ? false : args["skipBuiltSourceCreate"],
            execute: builtSourceCreateExec,
            startManagedEntities: (args["startManagedEntities"] === undefined) ? [] : args["startManagedEntities"],
            randomTraitPosition: (args["randomTraitPosition"] === undefined) ? true : args["randomTraitPosition"]
        };
    },
    shameBurstMineConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.shameBurstMineConstruction)));
        return ecs.emotionSourceConstruction(args);
    },
    hurtBand: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.hurtBand)));
        return ecs.qualityImprovement(args);
    },
    findMistakeMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.findMistakeMenu)));
        return ecs.goalCompleteMenu(args);
    },
    trainPathMakerKill: function(args){
        return {
            execute: trainPathMakerKill
        };
    },
    paramPath: function(args){
        return {
            createExecutables: [ecs.paramPathExec(args), ecs.killEntity(args)]
        };
    },
    indulgeHappyMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.indulgeHappyMenu)));
        return ecs.goalCompleteMenu(args);
    },
    selfPityGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.selfPityGoal)));
        return ecs.worldGoal(args);
    },
    quietDemonsStrengthGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.quietDemonsStrengthGoal)));
        return ecs.worldGoal(args);
    },
    makeTechInspiration: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.makeTechInspiration)));
        return ecs.entityReplace(args);
    },
    killIfFlagExec: function(args){
        return {
            killIfFlag: (args["killIfFlag"] === undefined) ? undefined : args["killIfFlag"],
            execute: killIfFlagExec
        };
    },
    workEmotion: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.workEmotion)));
        return ecs.currency(args);
    },
    selfWorthlessMine: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.selfWorthlessMine)));
        return ecs.emotionResource(args);
    },
    panelTrackComponent: function(args){
        return {
            closeTrackedPanel: closeTrackedPanel,
            panelsOpened: [],
            closeAllPanels: closeAllPanels,
            trackPanels: trackPanels
        };
    },
    trainPathSecondRender: function(args){
        return {
            priority: 3,
            image: "outline",
            renderState: "default",
            render: trainPathSecondRender
        };
    },
    rageAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.rageAction)));
        return ecs.currencyAction(args);
    },
    bruteForceTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.bruteForceTowerConstruction)));
        return ecs.construction(args);
    },
    trackActionables: function(args){
        return {
            execute: trackActionables
        };
    },
    strengthButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.strengthButton)));
        return ecs.towerButton(args);
    },
    achievement_ext: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.achievement_ext)));
        return ecs.sourceExtension(args);
    },
    talkAboutLoverGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.talkAboutLoverGoal)));
        return ecs.worldGoal(args);
    },
    worldGoalParticleEffect: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            lifeSentinelComponent: ecs.worldGoalParticleEffectLifeSentinelComponent(args),
            rendererComponents: [ecs.particleEffectRenderComponent(args)]
        };
    },
    constructionFocusPathStopComponent: function(args){
        return {
            currencyEntities: (args["currencyEntities"] === undefined) ? undefined : args["currencyEntities"],
            interactWithTrain: currencyRequestedInteractWithTrain,
            currencyEffect: (args["currencyEffect"] === undefined) ? undefined : args["currencyEffect"],
            currenciesNeededAtPoint: currencyRequestedCurrenciesNeededAtPoint,
            updateCurrencyDictWithStop: updateCurrencyDictWithStop,
            getCurrencyComponent: constructionGetCurrencyComponent,
            currenciesExistAtPoint: currenciesExistAtPoint,
            currenciesMadeAtPoint: (args["currenciesMadeAtPoint"] === undefined) ? emptyArrayFunction : args["currenciesMadeAtPoint"],
            isPathEnd: true,
            requiredOpinions: (args["requiredOpinions"] === undefined) ? [] : args["requiredOpinions"],
            percentComplete: currencyRequestedFocusPathStopPercentComplete,
            checkFocusStopDisabled: checkFocusStopDisabled,
            isDisabled: false,
            enableChecks: (args["enableChecks"] === undefined) ? [] : args["enableChecks"]
        };
    },
    quitPrideMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.quitPrideMenu)));
        return ecs.goalCompleteMenu(args);
    },
    dreamOfGreatnessMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.dreamOfGreatnessMenu)));
        return ecs.goalCompleteMenu(args);
    },
    depositTickComponent: function(args){
        return {
            tick: depositTick
        };
    },
    fadeEmoInspiration: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.fadeEmoInspiration)));
        return ecs.timedCreateEntities(args);
    },
    solveMusicResult: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.solveMusicResult)));
        return ecs.randomReplace(args);
    },
    achievementMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.achievementMenu)));
        return ecs.goalCompleteMenu(args);
    },
    daydreaming: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.daydreaming)));
        return ecs.trait(args);
    },
    captureSocialMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.captureSocialMenu)));
        return ecs.goalCompleteMenu(args);
    },
    pairTransform: function(args){
        return {
            replaceMarker: ecs.pairTransformMarker(args)
        };
    },
    dismissFailureButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.dismissFailureButton)));
        return ecs.goalButton(args);
    },
    goalCompleteMenuRender: function(args){
        return {
            flipModeFunction: undefined,
            fullAnimationTime: 7,
            yTextOffset: 5,
            closeFunction: undefined,
            image: (args["image"] === undefined) ? "activeHex" : args["image"],
            height: (args["height"] === undefined) ? undefined : args["height"],
            drawPanelButton: drawPanelButton,
            animatingTime: 0.2,
            descriptionTime: 0.5,
            renderState: "default",
            insetColor: "#000000",
            render: outPanelRender,
            titleTime: 0.4,
            priority: 19,
            sourceTrait: (args["sourceTrait"] === undefined) ? undefined : args["sourceTrait"],
            snapPanelPos: snapPanelPos,
            carousel: undefined,
            textColor: "#000000",
            backImage: "uiBack",
            drawImageButton: drawImageButton,
            expandRect: [args.x, args.y, args.size[0], args.size[1]],
            renderTime: 0,
            getFlipModeFunction: goalCompleteMenuFlipMode,
            completeText: (args["completeText"] === undefined) ? "" : args["completeText"],
            drawHelpButton: drawHelpButton,
            showingDescription: true,
            expandFunction: undefined,
            endGame: (args["endGame"] === undefined) ? false : args["endGame"],
            showPoints: (args["showPoints"] === undefined) ? false : args["showPoints"],
            specificRender: goalCompleteMenuRender
        };
    },
    largeSongSatisfactionDeposit: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.largeSongSatisfactionDeposit)));
        return ecs.deposit(args);
    },
    gameStartMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.gameStartMenu)));
        return ecs.goalCompleteMenu(args);
    },
    indulgeBitternessButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.indulgeBitternessButton)));
        return ecs.goalButton(args);
    },
    deadInsideTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.deadInsideTowerConstruction)));
        return ecs.memoryConstruction(args);
    },
    prideStartButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.prideStartButton)));
        return ecs.goalButton(args);
    },
    currencyNotificationComponent: function(args){
        if (args.notificationKey === undefined){ throw "ECS fail " + args.key + " missing notificationKey"; }
        return {
            notificationFunctions: simpleDict(args.notificationKey, addNotificationCurrency),
            serializeComponent: ecs.notificationSerializeComponent(args),
            notificationKey: args['notificationKey']
        };
    },
    angryMemoryFade: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.angryMemoryFade)));
        return ecs.memoryFade(args);
    },
    painMineConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.painMineConstruction)));
        return ecs.memoryConstruction(args);
    },
    hangOutGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.hangOutGoal)));
        return ecs.worldGoal(args);
    },
    resolveStrengthButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.resolveStrengthButton)));
        return ecs.towerButton(args);
    },
    buildPanelRenderComponent: function(args){
        return {
            drawImageButton: drawImageButton,
            textColor: "#000000",
            fullAnimationTime: 0.1,
            yTextOffset: 5,
            backImage: "opaqueBack",
            insetColor: "#000000",
            animatingTime: 0.1,
            priority: 19,
            render: buildPanelRender,
            drawPanelButton: drawPanelButton,
            snapPanelPos: snapPanelPos,
            demolishButtonFunction: undefined,
            renderTime: 0,
            demolishPathButtonFunction: undefined,
            settingsFunction: undefined,
            renderState: "default",
            drawHelpButton: drawHelpButton,
            specificRender: args['specificRender'],
            pathButtonFunction: undefined
        };
    },
    creepAcceptComponent: function(args){
        return {
            creepHitEntities: (args["creepHitEntities"] === undefined) ? PARAMETERS.creepHit : args["creepHitEntities"],
            creepAccept: creepAccept
        };
    },
    workSatisfaction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.workSatisfaction)));
        return ecs.emotionSource(args);
    },
    personCallousnessButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.personCallousnessButton)));
        return ecs.towerButton(args);
    },
    personResource: function(args){
        args["touchable"] = true;
        args["canExpandInto"] = false;
        return {
            focusPathStopComponent: ecs.resourceFocusPathStopComponent(args),
            rendererComponents: [ecs.pathStopRenderComponent(args)],
            focusTickResponseComponent: ecs.resourceFocusTickResponseComponent(args),
            uiDataComponent: ecs.uiDataComponent(args),
            managedComponent: ecs.managedComponent(args),
            personHeldComponent: ecs.personHeldComponent(args),
            createExecutables: [ecs.makeExtraPathsExec(args)],
            junctionComponent: ecs.junctionComponent(args),
            clickComponent: ecs.fullMenuClickComponent(args),
            killExecutables: [ecs.killResourceCurrencies(args)],
            resourceComponent: ecs.resourceComponent(args),
            descriptionComponent: ecs.descriptionComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            interactionComponent: ecs.interactionComponent(args),
            identityComponent: ecs.identityComponent(args),
            positionComponent: ecs.positionComponent(args),
            playerMadeComponent: ecs.playerMadeComponent(args)
        };
    },
    tower: function(args){
        args["touchable"] = true;
        args["canExpandInto"] = false;
        args["isPathEnd"] = (args["isPathEnd"] === undefined) ? (args.damageMultipliers ? true : false) : args["isPathEnd"];
        args["tags"] = (args["tags"] === undefined) ? [args.key] : args["tags"];
        return {
            junctionComponent: ecs.junctionComponent(args),
            rendererComponents: [ecs.pathStopRenderComponent(args), ecs.damageRenderComponent(args)],
            focusTickResponseComponent: ecs.towerFocusTickResponseComponent(args),
            managedComponent: ecs.managedComponent(args),
            absolutePositionComponent: ecs.absolutePositionComponent(args),
            damageComponent: (args.damageMultipliers) ? ecs.damageComponent(args) : undefined,
            healthModifyComponent: ecs.healthModifyComponent(args),
            createExecutables: [ecs.makeExtraPathsExec(args)].concat([ecs.towerCreateExec(args)]),
            focusPathStopComponent: ecs.towerFocusPathStopComponent(args),
            clickComponent: ecs.fullMenuClickComponent(args),
            uiDataComponent: ecs.uiDataComponent(args),
            tickComponent: ecs.towerTickComponent(args),
            towerComponent: ecs.towerComponent(args),
            descriptionComponent: ecs.descriptionComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            interactionComponent: ecs.interactionComponent(args),
            identityComponent: ecs.identityComponent(args),
            positionComponent: ecs.positionComponent(args),
            playerMadeComponent: ecs.playerMadeComponent(args)
        };
    },
    notifyEntityMakerNotificationComponent: function(args){
        if (args.count === undefined){ throw "ECS fail " + args.key + " missing count"; }
        if (args.entitiesToMake === undefined){ throw "ECS fail " + args.key + " missing entitiesToMake"; }
        if (args.notificationArgs === undefined){ throw "ECS fail " + args.key + " missing notificationArgs"; }
        return {
            count: args['count'],
            notificationArgs: args['notificationArgs'],
            serializeComponent: ecs.notificationSerializeComponent(args),
            notificationFunctions: simpleDict(args.notificationChecked, notifyEntityMakerFn),
            impulseCount: args['count'],
            entitiesToMake: args['entitiesToMake']
        };
    },
    killTowerButtonExec: function(args){
        return {
            execute: killTowerButtonExec,
            killTowersOnDeath: (args["killTowersOnDeath"] === undefined) ? false : args["killTowersOnDeath"]
        };
    },
    traitMenuCloseComponent: function(args){
        return {
            execute: traitMenuCloseExec
        };
    },
    goodSong_ext: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.goodSong_ext)));
        return ecs.sourceExtension(args);
    },
    dayResponseComponent: function(args){
        return {
            dayEnd: (args["dayEnd"] === undefined) ? undefined : args["dayEnd"],
            dayStart: (args["dayStart"] === undefined) ? undefined : args["dayStart"],
            dayChange: (args["dayChange"] === undefined) ? undefined : args["dayChange"]
        };
    },
    tickModifyComponent: function(args){
        if (args.tickFactor === undefined){ throw "ECS fail " + args.key + " missing tickFactor"; }
        if (args.towerId === undefined){ throw "ECS fail " + args.key + " missing towerId"; }
        if (args.distractFactor === undefined){ throw "ECS fail " + args.key + " missing distractFactor"; }
        return {
            tickFactor: args['tickFactor'],
            distractFactor: args['distractFactor'],
            tickModifyInteraction: (args["tickModifyInteraction"] === undefined) ? trueFunction : args["tickModifyInteraction"],
            towerId: args['towerId']
        };
    },
    failureTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.failureTowerConstruction)));
        return ecs.construction(args);
    },
    lackAccomplishmentTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.lackAccomplishmentTower)));
        return ecs.tower(args);
    },
    refineAccomplishmentTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.refineAccomplishmentTowerConstruction)));
        return ecs.construction(args);
    },
    reflectAloneGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.reflectAloneGoalConstruction)));
        return ecs.construction(args);
    },
    sadRideGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.sadRideGoalConstruction)));
        return ecs.memoryConstruction(args);
    },
    dreamOfGreatnessButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.dreamOfGreatnessButton)));
        return ecs.goalButton(args);
    },
    killTellQuitButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.killTellQuitButton)));
        return ecs.entityKill(args);
    },
    gainControl_anger: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.gainControl_anger)));
        return ecs.gainControlMenu(args);
    },
    improveQualityExec: function(args){
        return {
            qualityDiff: (args["qualityDiff"] === undefined) ? 1 : args["qualityDiff"],
            execute: improveQuality,
            checkAgainstQuality: (args["checkAgainstQuality"] === undefined) ? true : args["checkAgainstQuality"],
            entitiesToMake: (args["entitiesToMake"] === undefined) ? undefined : args["entitiesToMake"],
            qualityEntity: (args["qualityEntity"] === undefined) ? undefined : args["qualityEntity"]
        };
    },
    reflectMineButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.reflectMineButton)));
        return ecs.resourceButton(args);
    },
    notificationCurrency: function(args){
        return {
            notificationCurrencyComponent: ecs.notificationCurrencyComponent(args),
            notificationComponents: [ecs.currencyNotificationComponent(args)]
        };
    },
    endMuseGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.endMuseGoalConstruction)));
        return ecs.construction(args);
    },
    achievementToAmbition: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.achievementToAmbition)));
        return ecs.transformExec(args);
    },
    executeComponent: function(args){
        if (args.executable === undefined){ throw "ECS fail " + args.key + " missing executable"; }
        return {
            executable: args['executable'],
            ticks: 0,
            requiresActive: (args["requiresActive"] === undefined) ? false : args["requiresActive"],
            impulseTicks: (args["impulseTicks"] === undefined) ? 10 : args["impulseTicks"]
        };
    },
    goalCompleteMenuPanelComponent: function(args){
        return {
            panelButtons: [],
            expandPanel: expandPanel,
            minimized: false,
            canClose: true,
            closed: false,
            ignoreWhenMinimized: false
        };
    },
    findNewMusicGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.findNewMusicGoalConstruction)));
        return ecs.construction(args);
    },
    disableTraitComponent: function(args){
        if (args.traitsToDisable === undefined){ throw "ECS fail " + args.key + " missing traitsToDisable"; }
        return {
            checkTraitDisabled: checkTraitDisabled,
            traitsToDisable: args['traitsToDisable']
        };
    },
    needTimeGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.needTimeGoal)));
        return ecs.worldGoal(args);
    },
    conditionalMake: function(args){
        return {
            replaceMarker: ecs.conditionalMakeReplaceMarker(args)
        };
    },
    personCallousnessTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.personCallousnessTower)));
        return ecs.tower(args);
    },
    strengthAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.strengthAction)));
        return ecs.currencyAction(args);
    },
    sayRightTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.sayRightTower)));
        return ecs.tower(args);
    },
    solutionTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.solutionTowerConstruction)));
        return ecs.construction(args);
    },
    strengthMineConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.strengthMineConstruction)));
        return ecs.construction(args);
    },
    exhaustionDisable: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.exhaustionDisable)));
        return ecs.disableTrait(args);
    },
    pathMakerReplaceMarker: function(args){
        if (args.startKey === undefined){ throw "ECS fail " + args.key + " missing startKey"; }
        if (args.endKey === undefined){ throw "ECS fail " + args.key + " missing endKey"; }
        return {
            startKey: args['startKey'],
            endKey: args['endKey'],
            getReplaceKeys: pathMakerGetReplaceKeys
        };
    },
    towerTickModifier: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.towerTickModifier)));
        return {
            healthComponent: ecs.singleHealthComponent(args),
            tickModifyComponent: ecs.tickModifyComponent(args),
            rendererComponents: [ecs.towerTickModifierRenderComponent(args)],
            dayResponseComponent: ecs.killDayEndComponent(args),
            timedDeathComponent: ecs.timedDeathComponent(args)
        };
    },
    explainCapturedInspiration: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.explainCapturedInspiration)));
        return ecs.menuModifier(args);
    },
    shameBurst: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.shameBurst)));
        return ecs.emotionSource(args);
    },
    toastRenderComponent: function(args){
        return {
            priority: 18,
            renderState: "default",
            render: toastRender
        };
    },
    struggleCompliment: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.struggleCompliment)));
        return ecs.randomReplace(args);
    },
    flashback_joy: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.flashback_joy)));
        return ecs.emotionResource(args);
    },
    hangOutMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.hangOutMenu)));
        return ecs.goalCompleteMenu(args);
    },
    friendToHangingOut: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.friendToHangingOut)));
        return ecs.transformExec(args);
    },
    toldWorthButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.toldWorthButton)));
        return ecs.goalButton(args);
    },
    infiniteEmotionSourceComponent: function(args){
        return {
            spawnComplete: resetEmotionSource,
            border: "#ffff00",
            emotions: unrollDict(args.emotionsToAdd)
        };
    },
    fearTickModifyComponent: function(args){
        return {
            tickFactor: args['tickFactor'],
            distractFactor: args['distractFactor'],
            fearFrozen: 0,
            tickModifyInteraction: fearTickModifyInteraction,
            towerId: args['towerId']
        };
    },
    painMineButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.painMineButton)));
        return ecs.memoryResourceButton(args);
    },
    dontGiveUpGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.dontGiveUpGoal)));
        return ecs.worldGoal(args);
    },
    museBase: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.museBase)));
        return ecs.trait(args);
    },
    trainPathHelpInfoPanel: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.trainPathHelpInfoPanel)));
        return ecs.helpInfoPanel(args);
    },
    strengthDeposit: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.strengthDeposit)));
        return ecs.deposit(args);
    },
    memoryGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.memoryGoal)));
        args["touchable"] = true;
        args["particleKey"] = PARAMETERS.newParticle;
        args["startEntityKey"] = args.sourceKey;
        args["canExpandInto"] = false;
        args["playerKillable"] = !!(args.constructed);
        args["killOnTransform"] = (args["killOnTransform"] === undefined) ? false : args["killOnTransform"];
        return {
            uiActionComponent: ecs.buildingUiActionComponent(args),
            onTraitComponent: ecs.onTraitComponent(args),
            goalTrackComponent: ecs.memoryGoalTrackComponent(args),
            createExecutables: [ecs.checkManagedComponentExec(args), (args.randomPosition) ? ecs.randomOpenPositionExec(args) : undefined, ecs.makeExtraPathsExec(args), ecs.chkForPlayerInterrupt(args), ecs.buildingConnectExec(args), (args.timeToDeath) ? ecs.timedDifficultyModifyExec(args) : undefined],
            timedDeathComponent: (args.timeToDeath) ? ecs.worldGoalTimedDeathComponent(args) : undefined,
            uiDataComponent: ecs.uiDataComponent(args),
            descriptionComponent: ecs.descriptionComponent(args),
            interactionComponent: ecs.interactionComponent(args),
            identityComponent: ecs.identityComponent(args),
            rendererComponents: [ecs.worldGoalRenderComponent(args), ecs.progressRenderComponent(args), ecs.timedDeathRenderComponent(args)],
            focusTickResponseComponent: (args.aliveEntities) ? ecs.aliveMakeComponent(args) : undefined,
            managedComponent: (args.sourceTrait) ? ecs.managedComponent(args) : undefined,
            flipComponent: (args.flipEntity) ? ecs.flipComponent(args) : undefined,
            worldGoalComponent: ecs.worldGoalComponent(args),
            focusPathStopComponent: ecs.worldGoalFocusPathStopComponent(args),
            clickComponent: ecs.fullMenuClickComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            uniqueComponent: ecs.uniqueComponent(args),
            positionComponent: ecs.positionComponent(args),
            notificationComponents: [ecs.worldGoalNotificationComponent(args)],
            junctionComponent: ecs.junctionComponent(args),
            pauseComponent: ecs.pauseComponent(args),
            playerMadeComponent: ecs.playerMadeComponent(args)
        };
    },
    sadness: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.sadness)));
        return ecs.creep(args);
    },
    discardGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.discardGoalConstruction)));
        return ecs.construction(args);
    },
    understandButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.understandButton)));
        return ecs.memoryTowerButton(args);
    },
    completeTechMineButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.completeTechMineButton)));
        return ecs.resourceButton(args);
    },
    loverBase: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.loverBase)));
        return ecs.trait(args);
    },
    lovingCareTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.lovingCareTower)));
        return ecs.tower(args);
    },
    tryForMeaningGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.tryForMeaningGoal)));
        return ecs.worldGoal(args);
    },
    rechargeAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.rechargeAction)));
        return ecs.currencyAction(args);
    },
    playMusicButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.playMusicButton)));
        return ecs.towerButton(args);
    },
    letdownPath: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.letdownPath)));
        return ecs.paramPath(args);
    },
    depositPositionExec: function(args){
        return {
            execute: depositPositionExec,
            randomPosition: (args["randomPosition"] === undefined) ? false : args["randomPosition"]
        };
    },
    upgradeControlComponent: function(args){
        if (args.baseKeys === undefined){ throw "ECS fail " + args.key + " missing baseKeys"; }
        if (args.entityKey === undefined){ throw "ECS fail " + args.key + " missing entityKey"; }
        if (args.constructionKey === undefined){ throw "ECS fail " + args.key + " missing constructionKey"; }
        return {
            aiVal: (args["aiVal"] === undefined) ? 0 : args["aiVal"],
            constructionKey: args['constructionKey'],
            checkMakePos: checkUpgradePos,
            baseKeys: args['baseKeys'],
            currenciesMade: (args["currenciesMade"] === undefined) ? [] : args["currenciesMade"],
            isNew: true,
            canMakeEntity: canMakeUpgrade,
            entityKey: args['entityKey'],
            wrappedFunction: undefined,
            managerKey: (args["managerKey"] === undefined) ? args.sourceKey : args["managerKey"],
            makeEntity: upgradeTower,
            getAiVal: controlComponentGetAiVal,
            getWrappedFunction: getWrappedFunction,
            baseUpgradeTowerForPos: baseUpgradeTowerForPos
        };
    },
    socialMuseMineConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.socialMuseMineConstruction)));
        return ecs.construction(args);
    },
    musicMineButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.musicMineButton)));
        return ecs.resourceButton(args);
    },
    hangOutButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.hangOutButton)));
        return ecs.goalButton(args);
    },
    emotionResource: function(args){
        args["touchable"] = true;
        args["canExpandInto"] = false;
        args["playerKillable"] = false;
        return {
            focusPathStopComponent: ecs.resourceFocusPathStopComponent(args),
            rendererComponents: [ecs.pathStopRenderComponent(args)],
            focusTickResponseComponent: ecs.resourceFocusTickResponseComponent(args),
            uiDataComponent: ecs.uiDataComponent(args),
            managedComponent: ecs.managedComponent(args),
            createExecutables: [ecs.makeExtraPathsExec(args)].concat([ecs.emotionResourceDifficultyExec(args)]),
            junctionComponent: ecs.junctionComponent(args),
            clickComponent: ecs.fullMenuClickComponent(args),
            killExecutables: [ecs.killResourceCurrencies(args)],
            resourceComponent: ecs.resourceComponent(args),
            descriptionComponent: ecs.descriptionComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            interactionComponent: ecs.interactionComponent(args),
            identityComponent: ecs.identityComponent(args),
            positionComponent: ecs.positionComponent(args),
            loseControlExec: ecs.killEntity(args),
            playerMadeComponent: ecs.playerMadeComponent(args)
        };
    },
    openMenu: function(args){
        return {
            execute: openMenuExecute,
            markerKey: "openMenuMarker"
        };
    },
    tutorialUnpauseTextComponent: function(args){
        return {
            tutorialDone: tutorialUnpauseTutorialDone,
            nextEntities: (args["nextEntities"] === undefined) ? undefined : args["nextEntities"]
        };
    },
    quitWorkGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.quitWorkGoalConstruction)));
        return ecs.construction(args);
    },
    building: function(args){
        args["touchable"] = true;
        args["canExpandInto"] = false;
        return {
            managedComponent: ecs.managedComponent(args),
            createExecutables: [ecs.makeExtraPathsExec(args)],
            junctionComponent: ecs.junctionComponent(args),
            clickComponent: ecs.fullMenuClickComponent(args),
            uiDataComponent: ecs.uiDataComponent(args),
            descriptionComponent: ecs.descriptionComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            interactionComponent: ecs.interactionComponent(args),
            identityComponent: ecs.identityComponent(args),
            positionComponent: ecs.positionComponent(args),
            playerMadeComponent: ecs.playerMadeComponent(args)
        };
    },
    reflectAloneButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.reflectAloneButton)));
        return ecs.goalButton(args);
    },
    deriveHumanInsightGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.deriveHumanInsightGoalConstruction)));
        return ecs.construction(args);
    },
    hangOutGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.hangOutGoalConstruction)));
        return ecs.construction(args);
    },
    loverSad: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.loverSad)));
        return ecs.emotionSource(args);
    },
    songToGoodSong: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.songToGoodSong)));
        return ecs.transformExec(args);
    },
    angerFleshAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.angerFleshAction)));
        return ecs.currencyAction(args);
    },
    drownButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.drownButton)));
        return ecs.towerButton(args);
    },
    killEmoInspiration: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.killEmoInspiration)));
        return ecs.entityKill(args);
    },
    dismissFailureGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.dismissFailureGoalConstruction)));
        return ecs.construction(args);
    },
    deadInsideAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.deadInsideAction)));
        return ecs.currencyAction(args);
    },
    socialMuseMineButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.socialMuseMineButton)));
        return ecs.resourceButton(args);
    },
    gameStageNotificationComponent: function(args){
        return {
            notificationFunctions: {goalComplete: checkStageComplete},
            serializeComponent: ecs.notificationSerializeComponent(args)
        };
    },
    applyTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.applyTower)));
        return ecs.memoryTower(args);
    },
    techWorkAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.techWorkAction)));
        return ecs.currencyAction(args);
    },
    dependenceDeposit: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.dependenceDeposit)));
        return ecs.deposit(args);
    },
    bulletTickComponent: function(args){
        return {
            tick: bulletTick
        };
    },
    proveSuccessMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.proveSuccessMenu)));
        return ecs.goalCompleteMenu(args);
    },
    sustainEmoInspirationMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.sustainEmoInspirationMenu)));
        return ecs.goalCompleteMenu(args);
    },
    pathButton: function(args){
        return {
            rendererComponents: [ecs.pathButtonRenderComponent(args)]
        };
    },
    makeParticleEffect: function(args){
        if (args.particleKey === undefined){ throw "ECS fail " + args.key + " missing particleKey"; }
        return {
            particleKey: args['particleKey'],
            execute: makeParticleEffect
        };
    },
    easyWorkButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.easyWorkButton)));
        return ecs.towerButton(args);
    },
    song: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.song)));
        return ecs.trait(args);
    },
    currencyPath: function(args){
        return {
            currencyPathComponent: ecs.currencyPathComponent(args)
        };
    },
    reflectPastGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.reflectPastGoalConstruction)));
        return ecs.construction(args);
    },
    discardButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.discardButton)));
        return ecs.goalButton(args);
    },
    easyProblemDeposit: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.easyProblemDeposit)));
        return ecs.deposit(args);
    },
    setPanelComponent: function(args){
        return {
            panelButtons: [],
            expandPanel: expandPanel,
            minimized: false,
            canClose: false,
            closed: false,
            ignoreWhenMinimized: true
        };
    },
    selfReliance: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.selfReliance)));
        return ecs.trait(args);
    },
    steamAlienation: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.steamAlienation)));
        return ecs.steamAchievement(args);
    },
    loveMine: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.loveMine)));
        return ecs.resource(args);
    },
    techWorkTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.techWorkTowerConstruction)));
        return ecs.construction(args);
    },
    selfRelianceToAloneness: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.selfRelianceToAloneness)));
        return ecs.transformExec(args);
    },
    memoryResource: function(args){
        args["touchable"] = true;
        args["canExpandInto"] = false;
        return {
            interactionComponent: ecs.interactionComponent(args),
            junctionComponent: ecs.junctionComponent(args),
            rendererComponents: [ecs.pathStopRenderComponent(args)],
            focusTickResponseComponent: ecs.memoryResourceFocusTickResponseComponent(args),
            managedComponent: ecs.managedComponent(args),
            createExecutables: [ecs.makeExtraPathsExec(args)],
            focusPathStopComponent: ecs.resourceFocusPathStopComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            clickComponent: ecs.fullMenuClickComponent(args),
            killExecutables: [ecs.killResourceCurrencies(args)],
            descriptionComponent: ecs.descriptionComponent(args),
            resourceComponent: ecs.resourceComponent(args),
            uiDataComponent: ecs.uiDataComponent(args),
            identityComponent: ecs.identityComponent(args),
            positionComponent: ecs.positionComponent(args),
            playerMadeComponent: ecs.playerMadeComponent(args)
        };
    },
    completeEmoMine: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.completeEmoMine)));
        return ecs.resource(args);
    },
    techInspiration: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.techInspiration)));
        return ecs.trait(args);
    },
    personCreateExec: function(args){
        return {
            execute: personCreateExec
        };
    },
    swallowOpinionComponent: function(args){
        return {
            previousOpinions: undefined
        };
    },
    carriageComponent: function(args){
        return {
            direction: 0,
            holding: []
        };
    },
    nextDayMuse: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.nextDayMuse)));
        return ecs.nextDayEntity(args);
    },
    descriptionComponent: function(args){
        return {
            description: (args["description"] === undefined) ? "" : args["description"]
        };
    },
    fadeTechInspiration: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.fadeTechInspiration)));
        return ecs.timedCreateEntities(args);
    },
    will: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.will)));
        return ecs.currency(args);
    },
    openMenuClickComponent: function(args){
        return {
            onClick: openMenuOnClick
        };
    },
    dismissSuccessButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.dismissSuccessButton)));
        return ecs.goalButton(args);
    },
    releaseButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.releaseButton)));
        return ecs.goalButton(args);
    },
    sustainEmoInspirationGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.sustainEmoInspirationGoal)));
        return ecs.worldGoal(args);
    },
    freshMusicToMusicLove: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.freshMusicToMusicLove)));
        return ecs.transformExec(args);
    },
    needLoverTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.needLoverTower)));
        return ecs.tower(args);
    },
    killLostControlExec: function(args){
        return {
            execute: killLostControlExec
        };
    },
    captureLoveGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.captureLoveGoal)));
        return ecs.worldGoal(args);
    },
    irritabilityMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.irritabilityMenu)));
        return ecs.goalCompleteMenu(args);
    },
    deriveStrengthMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.deriveStrengthMenu)));
        return ecs.goalCompleteMenu(args);
    },
    memoryTowerButtonIssacEntityComponent: function(args){
        return {
            endLifecycle: trueFunction,
            startLifecycle: makeIsaacLifecycleEntities,
            makeLifecycleEntities: makeLifecycleEntities,
            entitiesMade: [],
            isaacEntities: (args["isaacEntities"] === undefined) ? [] : args["isaacEntities"]
        };
    },
    timedMakeEntities: function(args){
        return {
            execute: timedMakeEntities,
            timedOutEntities: (args["timedOutEntities"] === undefined) ? [] : args["timedOutEntities"]
        };
    },
    largeStrengthDeposit: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.largeStrengthDeposit)));
        return ecs.deposit(args);
    },
    quitWorkMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.quitWorkMenu)));
        return ecs.goalCompleteMenu(args);
    },
    selfExtensionComponent: function(args){
        return {
            emotion: undefined,
            setEmotion: selfExtensionSetEmotion,
            emotionLife: 0
        };
    },
    buttonManageComponent: function(args){
        return {
            renderButton: renderButton,
            panelButtons: [],
            lineHeight: (args["lineHeight"] === undefined) ? 16 : args["lineHeight"]
        };
    },
    completeEmoMineButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.completeEmoMineButton)));
        return ecs.resourceButton(args);
    },
    rageButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.rageButton)));
        return ecs.memoryTowerButton(args);
    },
    timedDifficultyModifyExec: function(args){
        return {
            execute: timedDifficultyModify
        };
    },
    damageIndicatorRenderComponent: function(args){
        if (args.sourceId === undefined){ throw "ECS fail " + args.key + " missing sourceId"; }
        return {
            priority: 12,
            sourceId: args['sourceId'],
            renderState: "default",
            render: damageIndicatorRender
        };
    },
    apologizeTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.apologizeTower)));
        return ecs.tower(args);
    },
    executable: function(args){
        if (args.execute === undefined){ throw "ECS fail " + args.key + " missing execute"; }
        return {
            execute: args['execute']
        };
    },
    sourceLifeSentinelComponent: function(args){
        if (args.sourceId === undefined){ throw "ECS fail " + args.key + " missing sourceId"; }
        return {
            sourceId: args['sourceId'],
            checkAlive: checkParticleEffectAlive
        };
    },
    cheerLoverResult: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.cheerLoverResult)));
        return ecs.randomReplace(args);
    },
    memoryTickComponent: function(args){
        return {
            lastManagedCount: 0,
            tick: memoryTick
        };
    },
    joyFleshButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.joyFleshButton)));
        return ecs.towerButton(args);
    },
    sinkWorkTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.sinkWorkTower)));
        return ecs.tower(args);
    },
    refineInspirationMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.refineInspirationMenu)));
        return ecs.goalCompleteMenu(args);
    },
    playWithMusicButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.playWithMusicButton)));
        return ecs.towerButton(args);
    },
    personTowerButton: function(args){
        args["subPanel"] = (args["subPanel"] === undefined) ? "Towers" : args["subPanel"];
        args["multiTrait"] = (args["multiTrait"] === undefined) ? false : args["multiTrait"];
        return {
            healthComponent: ecs.singleHealthComponent(args),
            uiActionComponent: ecs.buttonUiActionComponent(args),
            uiDataComponent: ecs.uiDataComponent(args),
            buttonControlComponent: ecs.towerControlComponent(args),
            personHeldComponent: ecs.personHeldComponent(args),
            createExecutables: [ecs.markTraitNew(args)],
            timedDeathComponent: (args.timeToDeath) ? ecs.timedDeathComponent(args) : undefined,
            killExecutables: [ecs.killTowerButtonExec(args)],
            descriptionComponent: ecs.descriptionComponent(args),
            uniqueComponent: (args.timeToDeath === undefined && !args.multiTrait) ? ecs.uniqueComponent(args) : undefined,
            interactionComponent: ecs.interactionComponent(args),
            identityComponent: ecs.identityComponent(args)
        };
    },
    impulse: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.impulse)));
        return ecs.currency(args);
    },
    listenDemonsGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.listenDemonsGoalConstruction)));
        return ecs.construction(args);
    },
    constructionClickComponent: function(args){
        return {
            onClick: constructOnClick
        };
    },
    shareJoyMuseAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.shareJoyMuseAction)));
        return ecs.currencyAction(args);
    },
    desireImprovementMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.desireImprovementMenu)));
        return ecs.goalCompleteMenu(args);
    },
    uiClickComponent: function(args){
        return {
            uiClick: (args["uiClick"] === undefined) ? args['trueFunction'] : args["uiClick"]
        };
    },
    aloneSadMineConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.aloneSadMineConstruction)));
        return ecs.emotionSourceConstruction(args);
    },
    personCallousnessTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.personCallousnessTowerConstruction)));
        return ecs.construction(args);
    },
    shameBurstMine: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.shameBurstMine)));
        return ecs.emotionResource(args);
    },
    hateWorkToAmbition: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.hateWorkToAmbition)));
        return ecs.transformExec(args);
    },
    killFlesh: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.killFlesh)));
        return ecs.entityKill(args);
    },
    lovingToMakingWork: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.lovingToMakingWork)));
        return ecs.transformExec(args);
    },
    memory_loverInsult: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.memory_loverInsult)));
        return ecs.memory(args);
    },
    pathVelocityComponent: function(args){
        return {
            velocity: 1,
            isMoving: false,
            ticks: 0,
            isHeld: false,
            chosenPath: false
        };
    },
    focusPanel: function(args){
        return {
            panelComponent: ecs.setPanelComponent(args),
            absolutePositionComponent: ecs.focusPanelAbsolutePositionComponent(args),
            rendererComponents: [ecs.focusPanelRenderComponent(args)],
            uiClickComponent: ecs.panelUiClickComponent(args)
        };
    },
    quietDemonsLoveButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.quietDemonsLoveButton)));
        return ecs.goalButton(args);
    },
    qualityImprovement: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            personHeldComponent: (args.qualityEntity === undefined) ? ecs.personHeldComponent(args) : undefined,
            createExecutables: [ecs.improveQualityExec(args), ecs.killEntity(args)]
        };
    },
    selfReliance_ext: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.selfReliance_ext)));
        return ecs.sourceExtension(args);
    },
    reflectFailureButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.reflectFailureButton)));
        return ecs.goalButton(args);
    },
    releaseGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.releaseGoalConstruction)));
        return ecs.construction(args);
    },
    talkAboutSelfGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.talkAboutSelfGoalConstruction)));
        return ecs.construction(args);
    },
    menuModifier: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            menuModifierComponent: ecs.menuModifierComponent(args)
        };
    },
    resolveTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.resolveTowerConstruction)));
        return ecs.construction(args);
    },
    trainKillExec: function(args){
        return {
            execute: trainKillExec
        };
    },
    reflect: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.reflect)));
        return ecs.currency(args);
    },
    makeOnCreateEntityExec: function(args){
        args["makeOnCreateEntities"] = (args["makeOnCreateEntities"] === undefined) ? [] : args["makeOnCreateEntities"];
        return {
            execute: executeMakeEntities,
            entitiesToMake: args['makeOnCreateEntities'],
            adjacent: (args["adjacent"] === undefined) ? false : args["adjacent"]
        };
    },
    romanceToFriend: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.romanceToFriend)));
        return ecs.transformExec(args);
    },
    fleshSongGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.fleshSongGoalConstruction)));
        return ecs.construction(args);
    },
    memoryResourceButton: function(args){
        args["managerKey"] = args.sourceKey;
        args["multiTrait"] = true;
        args["subPanel"] = (args["subPanel"] === undefined) ? "Towers" : args["subPanel"];
        args["multiTrait"] = (args["multiTrait"] === undefined) ? false : args["multiTrait"];
        if (args.sourceKey === undefined){ throw "ECS fail " + args.key + " missing sourceKey"; }
        return {
            interactionComponent: ecs.interactionComponent(args),
            uiActionComponent: ecs.buttonUiActionComponent(args),
            buttonControlComponent: ecs.towerControlComponent(args),
            createExecutables: [ecs.markTraitNew(args)],
            timedDeathComponent: (args.timeToDeath) ? ecs.timedDeathComponent(args) : undefined,
            uiDataComponent: ecs.uiDataComponent(args),
            uniqueComponent: (args.timeToDeath === undefined && !args.multiTrait) ? ecs.uniqueComponent(args) : undefined,
            descriptionComponent: ecs.descriptionComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            killExecutables: [ecs.killTowerButtonExec(args)],
            identityComponent: ecs.identityComponent(args)
        };
    },
    memoryFadeExec: function(args){
        if (args.sourceKey === undefined){ throw "ECS fail " + args.key + " missing sourceKey"; }
        if (args.newTrait === undefined){ throw "ECS fail " + args.key + " missing newTrait"; }
        return {
            execute: memoryFadeExec,
            sourceKey: args['sourceKey'],
            newTrait: args['newTrait']
        };
    },
    makeAllReplaceMarker: function(args){
        if (args.csv === undefined){ throw "ECS fail " + args.key + " missing csv"; }
        return {
            csv: args['csv'],
            getReplaceKeys: makeAllGetReplaceKeys
        };
    },
    playMusicTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.playMusicTower)));
        return ecs.tower(args);
    },
    toastNotificationComponent: function(args){
        return {
            notificationFunctions: simpleDict(PARAMETERS.infoPanel, toastNotification)
        };
    },
    musicLoveToFreshMusic: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.musicLoveToFreshMusic)));
        return ecs.transformExec(args);
    },
    hurtRelationship: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.hurtRelationship)));
        return ecs.entityReplace(args);
    },
    personKillExec: function(args){
        return {
            execute: personKillExec
        };
    },
    buildPanel: function(args){
        return {
            panelComponent: ecs.setPanelComponent(args),
            absolutePositionComponent: ecs.buildPanelAbsolutePositionComponent(args),
            rendererComponents: [ecs.buildPanelRenderComponent(args)],
            uiClickComponent: ecs.panelUiClickComponent(args)
        };
    },
    drownTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.drownTower)));
        return ecs.tower(args);
    },
    loverJoyMine: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.loverJoyMine)));
        return ecs.emotionResource(args);
    },
    dreamOfGreatnessGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.dreamOfGreatnessGoalConstruction)));
        return ecs.construction(args);
    },
    checkQuality: function(args){
        return {
            tickComponent: ecs.checkQualityTickComponent(args)
        };
    },
    faceEmotionGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.faceEmotionGoal)));
        return ecs.memoryGoal(args);
    },
    obnoxiousMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.obnoxiousMenu)));
        return ecs.goalCompleteMenu(args);
    },
    resourceComponent: function(args){
        if (args.resourceKeys === undefined){ throw "ECS fail " + args.key + " missing resourceKeys"; }
        return {
            resourceCount: (args["resourceCount"] === undefined) ? undefined : args["resourceCount"],
            ticks: 2,
            impulseTicks: 10,
            currenciesAtResource: currenciesAtResource,
            resourceKeys: args['resourceKeys'],
            resourceEntities: (args["resourceEntities"] === undefined) ? undefined : args["resourceEntities"]
        };
    },
    towerFlash: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            rendererComponents: [ecs.towerFlashRenderComponent(args)],
            positionComponent: ecs.positionComponent(args)
        };
    },
    trainPathMakerHighlightRenderComponent: function(args){
        return {
            priority: 10,
            highlightImage: "activeOutline",
            renderState: "default",
            render: trainPathMakerHighlightRender,
            timeToBlink: 0.25
        };
    },
    newTraitParticle: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.newTraitParticle)));
        return ecs.traitParticleEffect(args);
    },
    allBuildingPanel: function(args){
        args["y"] = 5;
        args["x"] = (width - 800) / 2;
        args["key"] = PARAMETERS.allBuildingPanel;
        args["size"] = [800, 300];
        return {
            rendererComponents: [ecs.allBuildingPanelRenderComponent(args)],
            absolutePositionComponent: ecs.absolutePositionComponent(args),
            uiClickComponent: ecs.panelUiClickComponent(args),
            panelComponent: ecs.panelComponent(args),
            uniqueComponent: ecs.uniqueComponent(args),
            identityComponent: ecs.identityComponent(args)
        };
    },
    cheerPrideMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.cheerPrideMenu)));
        return ecs.goalCompleteMenu(args);
    },
    createModifyEntity: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            identityComponent: ecs.identityComponent(args),
            createModifyComponent: ecs.createModifyComponent(args)
        };
    },
    killCurrencyExec: function(args){
        if (args.currencyToKill === undefined){ throw "ECS fail " + args.key + " missing currencyToKill"; }
        return {
            currencyToKill: args['currencyToKill'],
            execute: killCurrencyExec
        };
    },
    makeEmoInspirationMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.makeEmoInspirationMenu)));
        return ecs.goalCompleteMenu(args);
    },
    deriveStrengthGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.deriveStrengthGoal)));
        return ecs.worldGoal(args);
    },
    menuTutorialTextRenderComponent: function(args){
        return {
            shownOnce: false,
            render: menuTutorialTextRender,
            text: args['text'],
            menuEntity: undefined,
            yOffset: (args["yOffset"] === undefined) ? 0 : args["yOffset"],
            priority: 24,
            entityToMark: args['entityToMark'],
            renderState: "default",
            shouldDrawArrow: (args["shouldDrawArrow"] === undefined) ? true : args["shouldDrawArrow"],
            hasDrawn: false,
            drawArrow: tutorialTextDrawArrow,
            xOffset: (args["xOffset"] === undefined) ? 100 : args["xOffset"]
        };
    },
    killPersonGoals: function(args){
        return {
            execute: killPersonGoals
        };
    },
    currencyAction: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            uniqueComponent: ecs.uniqueComponent(args),
            identityComponent: ecs.identityComponent(args),
            currencyActionComponent: ecs.currencyActionComponent(args)
        };
    },
    fadingSadMemory: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.fadingSadMemory)));
        return ecs.fadingMemory(args);
    },
    addOpinionExec: function(args){
        return {
            execute: addOpinionExec,
            opinionToAdd: (args["opinionToAdd"] === undefined) ? undefined : args["opinionToAdd"]
        };
    },
    flagBranch: function(args){
        return {
            replaceMarker: ecs.flagBranchReplaceMarker(args)
        };
    },
    randomIfNoneReplaceMarker: function(args){
        if (args.keys === undefined){ throw "ECS fail " + args.key + " missing keys"; }
        return {
            keys: args['keys'],
            getReplaceKeys: randomIfNoneGetReplaceKeys
        };
    },
    willMineButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.willMineButton)));
        return ecs.resourceButton(args);
    },
    personHeldComponent: function(args){
        return {
            personHeld: (args["personHeld"] === undefined) ? undefined : args["personHeld"]
        };
    },
    steamAchievement: function(args){
        return {
            steamAchievementComponent: ecs.steamAchievementComponent(args),
            notificationComponents: [ecs.steamAchievementNotificationComponent(args)]
        };
    },
    imageRenderComponent: function(args){
        if (args.image === undefined){ throw "ECS fail " + args.key + " missing image"; }
        return {
            priority: 6,
            image: args['image'],
            renderState: "default",
            render: imageRender
        };
    },
    randomIfNone: function(args){
        return {
            replaceMarker: ecs.randomIfNoneReplaceMarker(args)
        };
    },
    deriveInsightGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.deriveInsightGoal)));
        return ecs.worldGoal(args);
    },
    discussProblem: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.discussProblem)));
        return ecs.currency(args);
    },
    steamCynicism: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.steamCynicism)));
        return ecs.steamAchievement(args);
    },
    emotionResourceDifficultyExec: function(args){
        return {
            execute: emotionResourceDifficultyExec
        };
    },
    settingAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.settingAction)));
        return ecs.currencyAction(args);
    },
    loverJoy: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.loverJoy)));
        return ecs.emotionSource(args);
    },
    desireImprovementGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.desireImprovementGoal)));
        return ecs.worldGoal(args);
    },
    resolveButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.resolveButton)));
        return ecs.towerButton(args);
    },
    loverFleshTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.loverFleshTower)));
        return ecs.tower(args);
    },
    showMusic: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.showMusic)));
        return ecs.trait(args);
    },
    entityManagerComponent: function(args){
        return {
            findFreeManagedSpace: findFreeManagedSpace,
            disabled: false,
            hasNew: false,
            checkForNewManaged: checkForNewManaged,
            canMakeManagedEntity: canMakeManagedEntity
        };
    },
    makeEntityComponent: function(args){
        return {
            execute: executeMakeEntities,
            entitiesToMake: (args["entitiesToMake"] === undefined) ? [] : args["entitiesToMake"],
            adjacent: (args["adjacent"] === undefined) ? false : args["adjacent"]
        };
    },
    selfPityMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.selfPityMenu)));
        return ecs.goalCompleteMenu(args);
    },
    steamInspiration: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.steamInspiration)));
        return ecs.steamAchievement(args);
    },
    tutorialTextRenderComponent: function(args){
        if (args.entityToMark === undefined){ throw "ECS fail " + args.key + " missing entityToMark"; }
        if (args.text === undefined){ throw "ECS fail " + args.key + " missing text"; }
        return {
            render: tutorialTextRender,
            text: args['text'],
            yOffset: (args["yOffset"] === undefined) ? 0 : args["yOffset"],
            priority: 24,
            entityToMark: args['entityToMark'],
            renderState: "default",
            drawArrow: tutorialTextDrawArrow,
            xOffset: (args["xOffset"] === undefined) ? 100 : args["xOffset"]
        };
    },
    workSatisfactionMineConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.workSatisfactionMineConstruction)));
        return ecs.emotionSourceConstruction(args);
    },
    meetGroupie: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            createExecutables: [ecs.meetGroupieExec(args)]
        };
    },
    meetGroupieExec: function(args){
        return {
            execute: meetGroupie
        };
    },
    prideStartGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.prideStartGoal)));
        return ecs.worldGoal(args);
    },
    personGoalButton: function(args){
        args["managerKey"] = args.personHeld;
        args["subPanel"] = (args["subPanel"] === undefined) ? "Goals" : args["subPanel"];
        return {
            uiActionComponent: ecs.buttonUiActionComponent(args),
            buttonControlComponent: ecs.goalButtonControlComponent(args),
            personHeldComponent: ecs.personHeldComponent(args),
            createExecutables: [ecs.killIfFlagExec(args), ecs.markTraitNew(args)],
            timedDeathComponent: (args.timeToDeath) ? ecs.timedDeathComponent(args) : undefined,
            uiDataComponent: ecs.uiDataComponent(args),
            descriptionComponent: ecs.descriptionComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            interactionComponent: ecs.interactionComponent(args),
            identityComponent: ecs.identityComponent(args)
        };
    },
    qualityComponent: function(args){
        if (args.quality === undefined){ throw "ECS fail " + args.key + " missing quality"; }
        return {
            quality: args['quality']
        };
    },
    daydreamingToSelfLoathing: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.daydreamingToSelfLoathing)));
        return ecs.transformExec(args);
    },
    releaseSatisfyGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.releaseSatisfyGoal)));
        return ecs.worldGoal(args);
    },
    focusPathStopComponent: function(args){
        return {
            interactWithTrain: falseFunction,
            currenciesNeededAtPoint: (args["currenciesNeededAtPoint"] === undefined) ? emptyArrayFunction : args["currenciesNeededAtPoint"],
            currenciesMadeAtPoint: (args["currenciesMadeAtPoint"] === undefined) ? emptyArrayFunction : args["currenciesMadeAtPoint"],
            isDisabled: false,
            currenciesExistAtPoint: currenciesExistAtPoint,
            updateCurrencyDictWithStop: updateCurrencyDictWithStop,
            isPathEnd: (args["isPathEnd"] === undefined) ? false : args["isPathEnd"],
            checkFocusStopDisabled: checkFocusStopDisabled,
            requiredOpinions: (args["requiredOpinions"] === undefined) ? [] : args["requiredOpinions"],
            enableChecks: (args["enableChecks"] === undefined) ? [] : args["enableChecks"]
        };
    },
    captureWillMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.captureWillMenu)));
        return ecs.goalCompleteMenu(args);
    },
    dailyEntityDayResponseComponent: function(args){
        return {
            dayStart: dailyEntityMake
        };
    },
    lovingCareAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.lovingCareAction)));
        return ecs.currencyAction(args);
    },
    trainPathWarningRender: function(args){
        return {
            priority: 11,
            renderState: "default",
            render: trainPathWarningRender
        };
    },
    rollSleevesButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.rollSleevesButton)));
        return ecs.goalButton(args);
    },
    suppressDemonsMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.suppressDemonsMenu)));
        return ecs.goalCompleteMenu(args);
    },
    endMuseMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.endMuseMenu)));
        return ecs.goalCompleteMenu(args);
    },
    fadeAchievement: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.fadeAchievement)));
        return ecs.timedCreateEntities(args);
    },
    keyPressComponent: function(args){
        if (args.handleKeyPress === undefined){ throw "ECS fail " + args.key + " missing handleKeyPress"; }
        return {
            handleKeyPress: args['handleKeyPress']
        };
    },
    qualityResourceTickComponent: function(args){
        if (args.qualityThreshold === undefined){ throw "ECS fail " + args.key + " missing qualityThreshold"; }
        if (args.highQualityResources === undefined){ throw "ECS fail " + args.key + " missing highQualityResources"; }
        return {
            tick: qualityResourceTick,
            qualityThreshold: args['qualityThreshold'],
            highQualityResources: args['highQualityResources'],
            flipEntities: (args["flipEntities"] === undefined) ? [] : args["flipEntities"],
            lastQuality: args['quality'],
            lowQualityResources: args['resourceKeys']
        };
    },
    captureMusicMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.captureMusicMenu)));
        return ecs.goalCompleteMenu(args);
    },
    startNewSongGoalPathNascentGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.startNewSongGoalPathNascentGoal)));
        return ecs.pathMaker(args);
    },
    ignoreInsult2Menu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.ignoreInsult2Menu)));
        return ecs.goalCompleteMenu(args);
    },
    workerRenderComponent: function(args){
        if (args.image === undefined){ throw "ECS fail " + args.key + " missing image"; }
        return {
            priority: 12,
            image: args['image'],
            renderState: "default",
            render: currencyRender,
            renderPos: undefined
        };
    },
    experienceTrigger: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            experienceTriggerComponent: ecs.experienceTriggerComponent(args)
        };
    },
    self: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.self)));
        return ecs.source(args);
    },
    sourceExtensionClickComponent: function(args){
        return {
            onClick: sourceExtensionOnClick
        };
    },
    optimismTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.optimismTowerConstruction)));
        return ecs.memoryConstruction(args);
    },
    fireUpButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.fireUpButton)));
        return ecs.memoryGoalButton(args);
    },
    achievement: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.achievement)));
        return ecs.trait(args);
    },
    endAlienationStrengthGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.endAlienationStrengthGoal)));
        return ecs.worldGoal(args);
    },
    thinkSongBadGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.thinkSongBadGoal)));
        return ecs.worldGoal(args);
    },
    junctionRenderComponent: function(args){
        return {
            priority: 12,
            renderState: "default",
            render: junctionRender
        };
    },
    sustainAchievementGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.sustainAchievementGoal)));
        return ecs.worldGoal(args);
    },
    overcomeExhaustButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.overcomeExhaustButton)));
        return ecs.goalButton(args);
    },
    revertMemory: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            createExecutables: [ecs.revertMemoryExec(args), ecs.killEntity(args)]
        };
    },
    sadSong_ext: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.sadSong_ext)));
        return ecs.sourceExtension(args);
    },
    workThroughMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.workThroughMenu)));
        return ecs.goalCompleteMenu(args);
    },
    emotionTickModifier: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            emotionTickModifyComponent: ecs.emotionTickModifyComponent(args),
            identityComponent: ecs.identityComponent(args)
        };
    },
    pushYourselfMine: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.pushYourselfMine)));
        return ecs.resource(args);
    },
    stuckProblemMine: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.stuckProblemMine)));
        return ecs.emotionResource(args);
    },
    fireUpGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.fireUpGoalConstruction)));
        return ecs.memoryConstruction(args);
    },
    endRealTalkGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.endRealTalkGoal)));
        return ecs.worldGoal(args);
    },
    unhappy: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.unhappy)));
        return ecs.trait(args);
    },
    trainPathMakerTickComponent: function(args){
        return {
            tick: trainPathMakerTick
        };
    },
    convinceSelfMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.convinceSelfMenu)));
        return ecs.goalCompleteMenu(args);
    },
    flaggedMake: function(args){
        return {
            replaceMarker: ecs.flaggedMakeReplaceMarker(args)
        };
    },
    releaseSatisfyGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.releaseSatisfyGoalConstruction)));
        return ecs.construction(args);
    },
    impressGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.impressGoal)));
        return ecs.worldGoal(args);
    },
    angerFleshTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.angerFleshTower)));
        return ecs.tower(args);
    },
    shareJoyMuseTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.shareJoyMuseTower)));
        return ecs.tower(args);
    },
    buildingConnectExec: function(args){
        return {
            execute: buildingConnectExec,
            startEntityKey: (args["startEntityKey"] === undefined) ? undefined : args["startEntityKey"]
        };
    },
    talkButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.talkButton)));
        return ecs.towerButton(args);
    },
    resolve: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.resolve)));
        return ecs.currency(args);
    },
    meetMuseGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.meetMuseGoal)));
        return ecs.worldGoal(args);
    },
    risingText: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            rendererComponents: [ecs.risingTextRenderComponent(args)]
        };
    },
    towerControlComponent: function(args){
        if (args.entityCost === undefined){ throw "ECS fail " + args.key + " missing entityCost"; }
        if (args.entityKey === undefined){ throw "ECS fail " + args.key + " missing entityKey"; }
        if (args.constructionKey === undefined){ throw "ECS fail " + args.key + " missing constructionKey"; }
        if (args.limitingKeys === undefined){ throw "ECS fail " + args.key + " missing limitingKeys"; }
        return {
            aiVal: (args["aiVal"] === undefined) ? 0 : args["aiVal"],
            constructionKey: args['constructionKey'],
            checkMakePos: checkTowerMakePos,
            isNew: true,
            currenciesMade: (args["currenciesMade"] === undefined) ? [] : args["currenciesMade"],
            canMakeEntity: canBuildTower,
            entityKey: args['entityKey'],
            limitingKeys: args['limitingKeys'],
            wrappedFunction: undefined,
            managerKey: (args["managerKey"] === undefined) ? args.sourceKey : args["managerKey"],
            makeEntity: buildTower,
            getAiVal: controlComponentGetAiVal,
            getWrappedFunction: getWrappedFunction,
            maxTowerCount: (args["maxTowerCount"] === undefined) ? 2 : args["maxTowerCount"],
            entityCost: args['entityCost']
        };
    },
    callousnessButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.callousnessButton)));
        return ecs.towerButton(args);
    },
    explainObnoxiousMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.explainObnoxiousMenu)));
        return ecs.goalCompleteMenu(args);
    },
    demon2Goal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.demon2Goal)));
        return ecs.worldGoal(args);
    },
    prideStartToDemons: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.prideStartToDemons)));
        return ecs.transformExec(args);
    },
    reflectPastGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.reflectPastGoal)));
        return ecs.worldGoal(args);
    },
    uiActionComponent: function(args){
        return {
            getUiActions: getUiActions,
            generateUiActions: (args["generateUiActions"] === undefined) ? undefined : args["generateUiActions"],
            memoizedActions: undefined
        };
    },
    insightDeposit: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.insightDeposit)));
        return ecs.deposit(args);
    },
    trainPathMakingPanelRender: function(args){
        return {
            insetColor: "#000000",
            completeFunction: undefined,
            fullAnimationTime: 0.1,
            yTextOffset: 5,
            backImage: "uiBack",
            killStopFunctions: [],
            drawImageButton: drawImageButton,
            animatingTime: 0.1,
            trainIcon: "trainIcon",
            priority: 18,
            render: trainPathMakingPanelRender,
            drawPanelButton: drawPanelButton,
            snapPanelPos: snapPanelPos,
            renderTime: 0,
            textColor: "#000000",
            renderState: "default",
            blinkTime: 0.9,
            specificRender: args['specificRender'],
            drawHelpButton: drawHelpButton
        };
    },
    optimismAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.optimismAction)));
        return ecs.currencyAction(args);
    },
    gainControl_joy: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.gainControl_joy)));
        return ecs.gainControlMenu(args);
    },
    sadSong: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.sadSong)));
        return ecs.trait(args);
    },
    drownTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.drownTowerConstruction)));
        return ecs.construction(args);
    },
    demons: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.demons)));
        return ecs.trait(args);
    },
    freshMusicToCurious: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.freshMusicToCurious)));
        return ecs.transformExec(args);
    },
    trainPathCircRenderComponent: function(args){
        return {
            priority: 12,
            renderTime: 0,
            renderState: "default",
            render: trainPathCircRender
        };
    },
    trainPanel: function(args){
        args["x"] = width - 300;
        args["size"] = [300, 400];
        args["size"] = (args["size"] === undefined) ? [550, 450] : args["size"];
        return {
            rendererComponents: [ecs.trainPanelRenderComponent(args)],
            absolutePositionComponent: ecs.absolutePositionComponent(args),
            uiClickComponent: ecs.panelUiClickComponent(args),
            helpPanelComponent: ecs.helpPanelComponent(args),
            panelComponent: ecs.entityPanelComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            uniqueComponent: ecs.uniqueComponent(args),
            identityComponent: ecs.identityComponent(args)
        };
    },
    friendToLoving: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.friendToLoving)));
        return ecs.transformExec(args);
    },
    sustainTechInspirationGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.sustainTechInspirationGoal)));
        return ecs.worldGoal(args);
    },
    listenDemonsMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.listenDemonsMenu)));
        return ecs.goalCompleteMenu(args);
    },
    exhaustion_ext: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.exhaustion_ext)));
        return ecs.sourceExtension(args);
    },
    personGoalTrackComponent: function(args){
        return {
            successExecutables: [ecs.goalCompleteExec(args)].concat([ecs.trackActionables(args), ecs.makeEntityComponent(args), ecs.makeGoalMenuExecutable(args)]).concat([ecs.addOpinionExec(args)]),
            checkCompleted: checkWorldGoalComponent
        };
    },
    currencyBoostComponent: function(args){
        return {
            currencyBoost: (args["currencyBoost"] === undefined) ? {} : args["currencyBoost"]
        };
    },
    focusPanelRenderComponent: function(args){
        return {
            insetColor: "#000000",
            showFocusPanel: undefined,
            fullAnimationTime: 0.1,
            yTextOffset: 5,
            backImage: "opaqueBack",
            drawImageButton: drawImageButton,
            animatingTime: 0.1,
            priority: 19,
            render: focusPanelRender,
            drawPanelButton: drawPanelButton,
            snapPanelPos: snapPanelPos,
            toggleFocusPath: undefined,
            renderTime: 0,
            textColor: "#000000",
            renderState: "default",
            specificRender: args['specificRender'],
            drawHelpButton: drawHelpButton
        };
    },
    rollSleevesGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.rollSleevesGoal)));
        return ecs.worldGoal(args);
    },
    showMusicGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.showMusicGoalConstruction)));
        return ecs.construction(args);
    },
    indulgeBitternessGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.indulgeBitternessGoalConstruction)));
        return ecs.construction(args);
    },
    traitPanel: function(args){
        args["size"] = [740, 500];
        args["size"] = (args["size"] === undefined) ? [550, 450] : args["size"];
        return {
            rendererComponents: [ecs.traitPanelRenderComponent(args)],
            absolutePositionComponent: ecs.absolutePositionComponent(args),
            uiClickComponent: ecs.panelUiClickComponent(args),
            helpPanelComponent: ecs.helpPanelComponent(args),
            panelComponent: ecs.entityPanelComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            uniqueComponent: ecs.uniqueComponent(args),
            identityComponent: ecs.identityComponent(args)
        };
    },
    daydreamGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.daydreamGoal)));
        return ecs.worldGoal(args);
    },
    disableTrait: function(args){
        return {
            disableTraitComponent: ecs.disableTraitComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            identityComponent: ecs.identityComponent(args),
            timedDeathComponent: (args.timeToDeath) ? ecs.timedDeathComponent(args) : undefined
        };
    },
    gameStage: function(args){
        return {
            createExecutables: [ecs.makeOnCreateEntityExec(args)],
            stageChangeComponent: ecs.stageChangeComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            identityComponent: ecs.identityComponent(args),
            dayResponseComponent: ecs.gameStageDayResponseComponent(args),
            notificationComponents: [ecs.gameStageNotificationComponent(args)]
        };
    },
    randomMenuMarker: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            randomMenuComponent: ecs.randomMenuComponent(args)
        };
    },
    noFocusNotifier: function(args){
        return {
            rendererComponents: [ecs.noFocusNotifierRenderComponent(args)]
        };
    },
    wishMakingComponent: function(args){
        if (args.makeWish === undefined){ throw "ECS fail " + args.key + " missing makeWish"; }
        if (args.canMakeWish === undefined){ throw "ECS fail " + args.key + " missing canMakeWish"; }
        return {
            makeWish: args['makeWish'],
            canMakeWish: args['canMakeWish']
        };
    },
    selfExtensionTickComponent: function(args){
        return {
            tick: selfExtensionTick
        };
    },
    social_muse: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.social_muse)));
        return ecs.currency(args);
    },
    prideBurstMine: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.prideBurstMine)));
        return ecs.emotionResource(args);
    },
    worldGoalComponent: function(args){
        if (args.currencyRequested === undefined){ throw "ECS fail " + args.key + " missing currencyRequested"; }
        return {
            goalSeen: false,
            requestingCurrency: false,
            currencyRequested: args['currencyRequested'],
            origCurrencyRequested: _.clone(args.currencyRequested)
        };
    },
    stopDemonLoveGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.stopDemonLoveGoal)));
        return ecs.worldGoal(args);
    },
    traitConnect: function(args){
        return {
            replaceMarker: ecs.traitConnectReplaceMarker(args)
        };
    },
    indulgeBitternessGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.indulgeBitternessGoal)));
        return ecs.worldGoal(args);
    },
    healthComponent: function(args){
        if (args.health === undefined){ throw "ECS fail " + args.key + " missing health"; }
        return {
            kill: killHealthComponent,
            damageFactor: (args["damageFactor"] === undefined) ? 1 : args["damageFactor"],
            health: args['health'],
            maxHealth: args['health']
        };
    },
    songSatisfiedButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.songSatisfiedButton)));
        return ecs.goalButton(args);
    },
    blinkTrainStops: function(args){
        return {
            execute: blinkTrainStopsExec,
            image: "activeOutline-0099ff"
        };
    },
    rememberMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.rememberMenu)));
        return ecs.goalCompleteMenu(args);
    },
    killExhaustion: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.killExhaustion)));
        return ecs.entityKill(args);
    },
    killAchievementButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.killAchievementButton)));
        return ecs.entityKill(args);
    },
    shareButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.shareButton)));
        return ecs.towerButton(args);
    },
    demon: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.demon)));
        return ecs.randomReplace(args);
    },
    killShowMusic: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.killShowMusic)));
        return ecs.entityKill(args);
    },
    realTalkToFriend: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.realTalkToFriend)));
        return ecs.transformExec(args);
    },
    newParticle: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.newParticle)));
        return ecs.worldGoalParticleEffect(args);
    },
    demonsTraitConnectLoverBase: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.demonsTraitConnectLoverBase)));
        return ecs.traitConnect(args);
    },
    timedScaleRenderComponent: function(args){
        if (args.image === undefined){ throw "ECS fail " + args.key + " missing image"; }
        return {
            timeDrawn: 0,
            render: timedRender,
            image: args['image'],
            nextRenderComponent: args['nextRenderComponent'],
            renderWhileTimed: args['renderWhileTimed'],
            renderTime: (args["renderTime"] === undefined) ? 0.15 : args["renderTime"],
            renderWhenTimed: timedScaleRenderWhenTimed,
            renderState: "default",
            priority: args['priority']
        };
    },
    songCounter: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.songCounter)));
        return {
            identityComponent: ecs.identityComponent(args),
            songCounterComponent: ecs.songCounterComponent(args)
        };
    },
    killPride: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.killPride)));
        return ecs.killCurrency(args);
    },
    frustrationMineButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.frustrationMineButton)));
        return ecs.memoryResourceButton(args);
    },
    allTrainPanelRenderComponent: function(args){
        return {
            insetColor: "#000000",
            fullAnimationTime: 0.1,
            yTextOffset: 5,
            backImage: "uiBack",
            drawImageButton: drawImageButton,
            animatingTime: 0.1,
            priority: 18,
            render: outPanelRender,
            drawPanelButton: drawPanelButton,
            snapPanelPos: snapPanelPos,
            renderTime: 0,
            textColor: "#000000",
            renderState: "default",
            specificRender: allTrainPanelRender,
            drawHelpButton: drawHelpButton
        };
    },
    strength: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.strength)));
        return ecs.currency(args);
    },
    acceptAlienationGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.acceptAlienationGoal)));
        return ecs.worldGoal(args);
    },
    clearTrait: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            createExecutables: [ecs.clearTraitExec(args), ecs.killEntity(args)]
        };
    },
    quietDemonsLoveMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.quietDemonsLoveMenu)));
        return ecs.goalCompleteMenu(args);
    },
    relaxDeposit: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.relaxDeposit)));
        return ecs.deposit(args);
    },
    hangingOut: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.hangingOut)));
        return ecs.trait(args);
    },
    insultingMake: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.insultingMake)));
        return ecs.randomReplace(args);
    },
    person: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.person)));
        args["touchable"] = true;
        args["health"] = (args["health"] === undefined) ? 2000 : args["health"];
        args["panelKey"] = (args["panelKey"] === undefined) ? PARAMETERS.traitPanel : args["panelKey"];
        return {
            uiActionComponent: ecs.buildingUiActionComponent(args),
            experienceComponent: ecs.experienceComponent(args),
            createExecutables: [ecs.builtSourceCreateExec(args), ecs.makeExtraPathsExec(args)].concat([ecs.personCreateExec(args)]),
            uiDataComponent: ecs.uiDataComponent(args),
            entityManagerComponent: ecs.entityManagerComponent(args),
            descriptionComponent: ecs.descriptionComponent(args),
            interactionComponent: ecs.interactionComponent(args),
            identityComponent: ecs.identityComponent(args),
            isaacEntityComponent: ecs.isaacEntityComponent(args),
            rendererComponents: [ecs.builtSourceRendererComponent(args)],
            focusTickResponseComponent: (args.aliveEntities) ? ecs.focusTickAliveMakerComponent(args) : undefined,
            menuCloseComponent: ecs.traitMenuCloseComponent(args),
            personHeldComponent: (args.personHeld) ? ecs.personHeldComponent(args) : undefined,
            clickComponent: ecs.fullMenuClickComponent(args),
            killExecutables: [ecs.killExtensions(args)].concat([ecs.personKillExec(args)]),
            healthComponent: ecs.healthComponent(args),
            uniqueComponent: ecs.uniqueComponent(args),
            positionComponent: ecs.positionComponent(args),
            personalityComponent: ecs.personalityComponent(args),
            mapSpawnComponent: ecs.builtSourceMapSpawnComponent(args),
            junctionComponent: ecs.junctionComponent(args),
            playerMadeComponent: ecs.fixedPlayerMadeComponent(args)
        };
    },
    cheer: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.cheer)));
        return ecs.currency(args);
    },
    workSatisfactionMine: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.workSatisfactionMine)));
        return ecs.emotionResource(args);
    },
    damageIndicator: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.damageIndicator)));
        return {
            healthComponent: ecs.singleHealthComponent(args),
            timedDeathComponent: ecs.timedDeathComponent(args),
            rendererComponents: [ecs.damageIndicatorRenderComponent(args)]
        };
    },
    envyMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.envyMenu)));
        return ecs.goalCompleteMenu(args);
    },
    builtSourceMapSpawnComponent: function(args){
        args["mapSpawnKey"] = (args["mapSpawnKey"] === undefined) ? args.key + '_ext' : args["mapSpawnKey"];
        return {
            spawnDone: false,
            mapPostSpawn: trueFunction,
            tickos: 15,
            mapSpawnPos: builtSourceMapSpawnPos,
            mapShouldSpawn: builtSourceMapShouldSpawn,
            impulseTicks: 15,
            mapSpawn: mapSpawn,
            tendPosition: (args["tendPosition"] === undefined) ? false : args["tendPosition"],
            mapSpawnKey: args['mapSpawnKey']
        };
    },
    unhappy_ext: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.unhappy_ext)));
        return ecs.sourceExtension(args);
    },
    alienationMineConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.alienationMineConstruction)));
        return ecs.construction(args);
    },
    killSong: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.killSong)));
        return ecs.entityKill(args);
    },
    hateWorkPathSelf: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.hateWorkPathSelf)));
        return ecs.pathMaker(args);
    },
    alienationMineButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.alienationMineButton)));
        return ecs.resourceButton(args);
    },
    toastClickComponent: function(args){
        if (args.panelKey === undefined){ throw "ECS fail " + args.key + " missing panelKey"; }
        return {
            onClick: toastClick,
            panelKey: args['panelKey']
        };
    },
    songToSadSong: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.songToSadSong)));
        return ecs.transformExec(args);
    },
    source: function(args){
        args["touchable"] = true;
        return {
            junctionComponent: ecs.junctionComponent(args),
            rendererComponents: [ecs.sourceRenderComponent(args)],
            clickComponent: ecs.fullMenuClickComponent(args),
            uniqueComponent: ecs.assertUniqueComponent(args),
            focusPathStopComponent: ecs.sourceFocusPathStopComponent(args),
            focusTickerComponent: ecs.focusTickerComponent(args),
            focusMakingComponent: (args.focusCount) ? ecs.focusMakingComponent(args) : undefined,
            interactionComponent: ecs.interactionComponent(args),
            healthComponent: ecs.healthComponent(args),
            creepAcceptComponent: ecs.creepAcceptComponent(args),
            identityComponent: ecs.identityComponent(args),
            positionComponent: ecs.positionComponent(args),
            playerMadeComponent: ecs.fixedPlayerMadeComponent(args)
        };
    },
    techWorkTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.techWorkTower)));
        return ecs.tower(args);
    },
    fixedPlayerMadeComponent: function(args){
        return {
            playerKillable: false,
            canExpandInto: false
        };
    },
    particleEffectRenderComponent: function(args){
        if (args.image === undefined){ throw "ECS fail " + args.key + " missing image"; }
        if (args.effectPos === undefined){ throw "ECS fail " + args.key + " missing effectPos"; }
        if (args.effectRadius === undefined){ throw "ECS fail " + args.key + " missing effectRadius"; }
        return {
            priority: 14,
            startParticles: (args["startParticles"] === undefined) ? 20 : args["startParticles"],
            render: particleEffectRender,
            impulseTimeToCheck: (args["impulseTimeToCheck"] === undefined) ? 0.05 : args["impulseTimeToCheck"],
            image: args['image'],
            effectPos: args['effectPos'],
            particles: [],
            effectRadius: args['effectRadius'],
            timeToCheck: 0.05,
            renderState: "default",
            makeParticle: makeParticle
        };
    },
    clickText: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.clickText)));
        return {
            rendererComponents: [ecs.clickTextRenderComponent(args)],
            timedDeathComponent: ecs.timedDeathComponent(args),
            clickComponent: ecs.clickKillComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            identityComponent: ecs.identityComponent(args),
            positionComponent: ecs.positionComponent(args)
        };
    },
    joyMap: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.joyMap)));
        return ecs.emotionResourceMap(args);
    },
    randomReplaceMarker: function(args){
        return {
            goodEntities: (args["goodEntities"] === undefined) ? [] : args["goodEntities"],
            badEntities: (args["badEntities"] === undefined) ? [] : args["badEntities"],
            addReplaceMarker: (args["addReplaceMarker"] === undefined) ? true : args["addReplaceMarker"],
            getReplaceKeys: randomReplaceGetReplaceKeys
        };
    },
    connectSource: function(args){
        return {
            replaceMarker: ecs.connectSourceReplaceMarker(args)
        };
    },
    meetMuseMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.meetMuseMenu)));
        return ecs.goalCompleteMenu(args);
    },
    dreamFutureGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.dreamFutureGoalConstruction)));
        return ecs.construction(args);
    },
    explainApologyFail: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.explainApologyFail)));
        return ecs.menuModifier(args);
    },
    hangingOut_ext: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.hangingOut_ext)));
        return ecs.sourceExtension(args);
    },
    sadFleshButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.sadFleshButton)));
        return ecs.towerButton(args);
    },
    resolveStrengthAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.resolveStrengthAction)));
        return ecs.currencyAction(args);
    },
    memoryIsaacEntityComponent: function(args){
        return {
            endLifecycle: endLifecycle,
            startLifecycle: memoryStartLifecycle,
            makeLifecycleEntities: makeLifecycleEntities,
            entitiesMade: []
        };
    },
    museVentAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.museVentAction)));
        return ecs.currencyAction(args);
    },
    envyButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.envyButton)));
        return ecs.goalButton(args);
    },
    reflectPastButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.reflectPastButton)));
        return ecs.goalButton(args);
    },
    tutorialTextComponent: function(args){
        if (args.entityToMark === undefined){ throw "ECS fail " + args.key + " missing entityToMark"; }
        return {
            entityToMark: args['entityToMark'],
            tutorialDone: tutorialTextTutorialDone,
            checkTutorialUiKey: checkTutorialUiKey,
            nextEntities: (args["nextEntities"] === undefined) ? undefined : args["nextEntities"]
        };
    },
    drainedSadMine: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.drainedSadMine)));
        return ecs.emotionResource(args);
    },
    resourceFocusTickResponseComponent: function(args){
        return {
            firstTick: true,
            emotionTags: (args["emotionTags"] === undefined) ? undefined : args["emotionTags"],
            destination: (args["destination"] === undefined) ? undefined : args["destination"],
            focusTick: resourceFocusTick,
            focusTickHook: trueFunction
        };
    },
    joyCreateExec: function(args){
        return {
            execute: joyCreate
        };
    },
    tryComplimentsGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.tryComplimentsGoalConstruction)));
        return ecs.construction(args);
    },
    curious: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.curious)));
        return ecs.trait(args);
    },
    overcomeExhaustMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.overcomeExhaustMenu)));
        return ecs.goalCompleteMenu(args);
    },
    deadInsideTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.deadInsideTower)));
        return ecs.memoryTower(args);
    },
    desireImprovement: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.desireImprovement)));
        return ecs.currency(args);
    },
    indulgeHappyGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.indulgeHappyGoal)));
        return ecs.worldGoal(args);
    },
    difficultySetting: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.difficultySetting)));
        return {
            identityComponent: ecs.identityComponent(args),
            difficultySettingComponent: ecs.difficultySettingComponent(args)
        };
    },
    opinionConditionalMake: function(args){
        return {
            healthComponent: ecs.healthComponent(args),
            createExecutables: [ecs.opinionConditionalExec(args), ecs.killEntity(args)]
        };
    },
    clickTextRenderComponent: function(args){
        return {
            priority: 9,
            renderState: "default",
            render: clickTextRender
        };
    },
    killDeriveCoolThingButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.killDeriveCoolThingButton)));
        return ecs.entityKill(args);
    },
    timedFocusHolder: function(args){
        return {
            interactionComponent: ecs.interactionComponent(args),
            focusCostComponent: ecs.focusCostComponent(args),
            identityComponent: ecs.identityComponent(args),
            timedDeathComponent: ecs.timedDeathComponent(args)
        };
    },
    alienationMine: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.alienationMine)));
        return ecs.resource(args);
    },
    uniqueComponent: function(args){
        return {
            uniqueDuplicateEvent: falseFunction
        };
    },
    killEntityComponent: function(args){
        return {
            entitiesToKill: (args["entitiesToKill"] === undefined) ? [] : args["entitiesToKill"],
            execute: executeKillEntities
        };
    },
    timeOutMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.timeOutMenu)));
        return ecs.goalCompleteMenu(args);
    },
    chapter3Menu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.chapter3Menu)));
        return ecs.gameStageStartMenu(args);
    },
    talkAboutLoverMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.talkAboutLoverMenu)));
        return ecs.goalCompleteMenu(args);
    },
    hardWork: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.hardWork)));
        return ecs.currency(args);
    },
    deriveInsightButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.deriveInsightButton)));
        return ecs.goalButton(args);
    },
    findNewMusicResult: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.findNewMusicResult)));
        return ecs.randomReplace(args);
    },
    destinyMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.destinyMenu)));
        return ecs.goalCompleteMenu(args);
    },
    achievementGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.achievementGoal)));
        return ecs.worldGoal(args);
    },
    largePrideDeposit: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.largePrideDeposit)));
        return ecs.deposit(args);
    },
    selfExtensionRender: function(args){
        return {
            priority: 6,
            renderState: "default",
            render: selfExtensionRender
        };
    },
    tellQuitGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.tellQuitGoal)));
        return ecs.worldGoal(args);
    },
    sourceRenderComponent: function(args){
        return {
            priority: 5,
            image: args['image'],
            renderState: "default",
            render: imageRender
        };
    },
    entityLifecycleComponent: function(args){
        if (args.startLifecycle === undefined){ throw "ECS fail " + args.key + " missing startLifecycle"; }
        return {
            endLifecycle: endLifecycle,
            startLifecycle: args['startLifecycle'],
            makeLifecycleEntities: makeLifecycleEntities,
            entitiesMade: []
        };
    },
    emoWorkTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.emoWorkTowerConstruction)));
        return ecs.construction(args);
    },
    relaxButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.relaxButton)));
        return ecs.goalButton(args);
    },
    sharing_lover: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.sharing_lover)));
        return ecs.currency(args);
    },
    dailyHelpInfoPanel: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.dailyHelpInfoPanel)));
        return ecs.helpInfoPanel(args);
    },
    playWithMusicTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.playWithMusicTower)));
        return ecs.tower(args);
    },
    currencyActionComponent: function(args){
        if (args.towerKey === undefined){ throw "ECS fail " + args.key + " missing towerKey"; }
        return {
            nextIds: [],
            towerKey: args['towerKey'],
            entitiesOnConvert: (args["entitiesOnConvert"] === undefined) ? [] : args["entitiesOnConvert"],
            currenciesRequired: (args["currenciesRequired"] === undefined) ? [] : args["currenciesRequired"],
            towerIds: [],
            currenciesToMake: (args["currenciesToMake"] === undefined) ? [] : args["currenciesToMake"]
        };
    },
    overcomeExhaustGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.overcomeExhaustGoal)));
        return ecs.worldGoal(args);
    },
    endRealTalkButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.endRealTalkButton)));
        return ecs.goalButton(args);
    },
    pathStopRenderComponent: function(args){
        if (args.activeImage === undefined){ throw "ECS fail " + args.key + " missing activeImage"; }
        if (args.image === undefined){ throw "ECS fail " + args.key + " missing image"; }
        return {
            priority: 7,
            outlineImage: false,
            render: pathStopRender,
            activeImage: args['activeImage'],
            wobble: 0,
            image: args['image'],
            blinkingOutlineTime: 0,
            timeDrawn: 0,
            outlineTime: 0,
            blinkingOutlineImage: false,
            renderTime: 0.1,
            getWobble: getWobble,
            renderState: "default",
            renderTrainIcon: renderTrainIcon,
            isActive: false
        };
    },
    begLoverMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.begLoverMenu)));
        return ecs.goalCompleteMenu(args);
    },
    swallowOpinionCreate: function(args){
        return {
            execute: swallowOpinionCreate,
            swallowExceptions: (args["swallowExceptions"] === undefined) ? [] : args["swallowExceptions"]
        };
    },
    killResourceCurrencies: function(args){
        return {
            execute: killResourceCurrencies
        };
    },
    emotionSource: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            identityComponent: ecs.identityComponent(args),
            emotionSourceComponent: ecs.emotionSourceComponent(args),
            createExecutables: [ecs.emotionSourceCreateExec(args), ecs.killEntity(args)]
        };
    },
    timedCreateEntities: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            timedDeathComponent: ecs.timedDeathComponent(args)
        };
    },
    nascentSongTraitConnectLoverBase: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.nascentSongTraitConnectLoverBase)));
        return ecs.traitConnect(args);
    },
    restartWorkButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.restartWorkButton)));
        return ecs.goalButton(args);
    },
    rememberGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.rememberGoal)));
        return ecs.worldGoal(args);
    },
    selfDamageEffect: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            lifeSentinelComponent: ecs.sourceLifeSentinelComponent(args),
            rendererComponents: [ecs.selfDamageEffectRenderComponent(args)]
        };
    },
    absolutePositionComponent: function(args){
        return {
            getAbsPosRect: getAbsPosRect,
            draggable: (args["draggable"] === undefined) ? true : args["draggable"],
            isDragging: false,
            setAbsPosFromRect: setAbsPosFromRect,
            y: (args["y"] === undefined) ? 0 : args["y"],
            x: (args["x"] === undefined) ? 0 : args["x"],
            size: (args["size"] === undefined) ? [0, 0] : args["size"]
        };
    },
    majorIssueMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.majorIssueMenu)));
        return ecs.goalCompleteMenu(args);
    },
    loving: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.loving)));
        return ecs.trait(args);
    },
    tryComplimentsGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.tryComplimentsGoal)));
        return ecs.worldGoal(args);
    },
    romanceReplace: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.romanceReplace)));
        return ecs.entityReplace(args);
    },
    constructionRenderComponent: function(args){
        if (args.image === undefined){ throw "ECS fail " + args.key + " missing image"; }
        return {
            priority: 8,
            outlineImage: false,
            render: constructionRender,
            wobble: 0,
            image: args['image'],
            timeDrawn: 0,
            dustImage: "dust",
            scaleTime: 0.15,
            getWobble: getWobble,
            renderState: "default",
            renderTrainIcon: renderTrainIcon,
            outlineTime: 0
        };
    },
    emoSolution: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.emoSolution)));
        return ecs.currency(args);
    },
    musicLovePathHateWork: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.musicLovePathHateWork)));
        return ecs.pathMaker(args);
    },
    placedTowerReplaceMarker: function(args){
        if (args.tower === undefined){ throw "ECS fail " + args.key + " missing tower"; }
        if (args.x === undefined){ throw "ECS fail " + args.key + " missing x"; }
        if (args.y === undefined){ throw "ECS fail " + args.key + " missing y"; }
        if (args.sourceTrait === undefined){ throw "ECS fail " + args.key + " missing sourceTrait"; }
        return {
            y: args['y'],
            x: args['x'],
            tower: args['tower'],
            sourceTrait: args['sourceTrait'],
            getReplaceKeys: placedTowerGetReplaceKeys
        };
    },
    quitPrideGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.quitPrideGoal)));
        return ecs.worldGoal(args);
    },
    song_ext: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.song_ext)));
        return ecs.sourceExtension(args);
    },
    joyRideGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.joyRideGoal)));
        return ecs.memoryGoal(args);
    },
    currencyRequest: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            positionComponent: ecs.positionComponent(args),
            requestComponent: ecs.requestComponent(args)
        };
    },
    reflectFailureGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.reflectFailureGoal)));
        return ecs.worldGoal(args);
    },
    swallowOpinion: function(args){
        args["isaacEntities"] = generateOpinionIsaacs(args);
        return {
            isaacEntityComponent: ecs.isaacEntityComponent(args),
            opinionComponent: ecs.opinionComponent(args),
            focusTickResponseComponent: (args.aliveEntities) ? ecs.aliveMakeComponent(args) : undefined,
            personHeldComponent: ecs.personHeldComponent(args),
            swallowOpinionComponent: ecs.swallowOpinionComponent(args),
            createExecutables: [ecs.swallowOpinionCreate(args), ecs.killPersonGoals(args)],
            timedDeathComponent: (args.timeToDeath) ? ecs.timedDeathComponent(args) : undefined,
            killExecutables: [ecs.swallowOpinionKill(args)],
            tickComponent: ecs.opinionTickComponent(args),
            uniqueComponent: ecs.uniqueComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            focusMakingComponent: (args.focusCount) ? ecs.focusMakingComponent(args) : undefined,
            identityComponent: ecs.identityComponent(args)
        };
    },
    makeGoodSong: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.makeGoodSong)));
        return ecs.entityReplace(args);
    },
    angerRideGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.angerRideGoalConstruction)));
        return ecs.memoryConstruction(args);
    },
    upgradeButton: function(args){
        args["subPanel"] = (args["subPanel"] === undefined) ? "Upgrades" : args["subPanel"];
        args["multiTrait"] = (args["multiTrait"] === undefined) ? false : args["multiTrait"];
        return {
            interactionComponent: ecs.interactionComponent(args),
            uiActionComponent: ecs.buttonUiActionComponent(args),
            buttonControlComponent: ecs.upgradeControlComponent(args),
            createExecutables: [ecs.markTraitNew(args)],
            timedDeathComponent: (args.timeToDeath) ? ecs.timedDeathComponent(args) : undefined,
            uiDataComponent: ecs.uiDataComponent(args),
            uniqueComponent: (args.timeToDeath === undefined && !args.multiTrait) ? ecs.uniqueComponent(args) : undefined,
            descriptionComponent: ecs.descriptionComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            killExecutables: [ecs.killTowerButtonExec(args)],
            identityComponent: ecs.identityComponent(args)
        };
    },
    demon1Goal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.demon1Goal)));
        return ecs.worldGoal(args);
    },
    playMusicAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.playMusicAction)));
        return ecs.currencyAction(args);
    },
    clickComponent: function(args){
        if (args.onClick === undefined){ throw "ECS fail " + args.key + " missing onClick"; }
        return {
            onClick: args['onClick']
        };
    },
    captureInspirationResult: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.captureInspirationResult)));
        return ecs.randomReplace(args);
    },
    friend_ext: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.friend_ext)));
        return ecs.sourceExtension(args);
    },
    loverInsult: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.loverInsult)));
        return ecs.emotionSource(args);
    },
    currencyDayResponseComponent: function(args){
        return {
            dayEnd: currencyDayEnd,
            dayStart: (args["dayStart"] === undefined) ? undefined : args["dayStart"],
            dayChange: (args["dayChange"] === undefined) ? undefined : args["dayChange"]
        };
    },
    hangingOutToRealTalk: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.hangingOutToRealTalk)));
        return ecs.transformExec(args);
    },
    achievementButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.achievementButton)));
        return ecs.goalButton(args);
    },
    realTalk: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.realTalk)));
        return ecs.trait(args);
    },
    loverFleshAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.loverFleshAction)));
        return ecs.currencyAction(args);
    },
    traitParticleEffectLifeSentinelComponent: function(args){
        if (args.managerKey === undefined){ throw "ECS fail " + args.key + " missing managerKey"; }
        return {
            managerKey: args['managerKey'],
            checkAlive: checkTraitParticleEffectAlive
        };
    },
    train: function(args){
        return {
            rendererComponents: [ecs.trainRenderComponent(args)],
            trainComponent: ecs.trainComponent(args),
            trainVelocityComponent: ecs.trainVelocityComponent(args),
            createExecutables: [ecs.trainCreateExec(args)],
            killExecutables: [ecs.trainKillExec(args)],
            healthComponent: ecs.singleHealthComponent(args),
            positionComponent: ecs.positionComponent(args)
        };
    },
    lackAccomplishmentAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.lackAccomplishmentAction)));
        return ecs.currencyAction(args);
    },
    newProblem: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.newProblem)));
        return ecs.randomReplace(args);
    },
    workThroughButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.workThroughButton)));
        return ecs.memoryGoalButton(args);
    },
    flagQuitButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.flagQuitButton)));
        return ecs.flaggedMake(args);
    },
    integrateButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.integrateButton)));
        return ecs.goalButton(args);
    },
    makeIfMissingExec: function(args){
        if (args.entitiesToMake === undefined){ throw "ECS fail " + args.key + " missing entitiesToMake"; }
        if (args.missingKey === undefined){ throw "ECS fail " + args.key + " missing missingKey"; }
        if (args.source === undefined){ throw "ECS fail " + args.key + " missing source"; }
        return {
            source: args['source'],
            execute: makeIfMissingExec,
            entitiesToMake: args['entitiesToMake'],
            missingKey: args['missingKey']
        };
    },
    selfEsteem: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.selfEsteem)));
        return ecs.currency(args);
    },
    quietDemons: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.quietDemons)));
        return ecs.trait(args);
    },
    talkAboutSelfButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.talkAboutSelfButton)));
        return ecs.goalButton(args);
    },
    indifferenceTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.indifferenceTowerConstruction)));
        return ecs.construction(args);
    },
    creepDeathExec: function(args){
        return {
            execute: creepDeathExec,
            creepDeathKey: "creepDeath"
        };
    },
    angerRideGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.angerRideGoal)));
        return ecs.memoryGoal(args);
    },
    loving_ext: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.loving_ext)));
        return ecs.sourceExtension(args);
    },
    goalCompleteMenuCreateExec: function(args){
        return {
            source: (args["source"] === undefined) ? undefined : args["source"],
            execute: goalCompleteMenuCreateExec
        };
    },
    transformTrait: function(args){
        if (args.sourceTrait === undefined){ throw "ECS fail " + args.key + " missing sourceTrait"; }
        if (args.newTrait === undefined){ throw "ECS fail " + args.key + " missing newTrait"; }
        return {
            retainHealth: (args["retainHealth"] === undefined) ? true : args["retainHealth"],
            execute: transformTrait,
            sourceTrait: args['sourceTrait'],
            newTrait: args['newTrait']
        };
    },
    shareAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.shareAction)));
        return ecs.currencyAction(args);
    },
    playWithMusicAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.playWithMusicAction)));
        return ecs.currencyAction(args);
    },
    endAlienationGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.endAlienationGoal)));
        return ecs.worldGoal(args);
    },
    makeCurrencyRequest: function(args){
        if (args.currencyRequested === undefined){ throw "ECS fail " + args.key + " missing currencyRequested"; }
        return {
            execute: makeCurrencyRequest,
            requestKey: PARAMETERS.currencyRequest,
            currencyRequested: args['currencyRequested']
        };
    },
    quietDemonsStrengthGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.quietDemonsStrengthGoalConstruction)));
        return ecs.construction(args);
    },
    allGoalPanel: function(args){
        args["y"] = 5;
        args["x"] = (width - 800) / 2;
        args["key"] = PARAMETERS.allGoalPanel;
        args["size"] = [800, 300];
        return {
            rendererComponents: [ecs.allGoalPanelRenderComponent(args)],
            absolutePositionComponent: ecs.absolutePositionComponent(args),
            uiClickComponent: ecs.panelUiClickComponent(args),
            panelComponent: ecs.panelComponent(args),
            uniqueComponent: ecs.uniqueComponent(args),
            identityComponent: ecs.identityComponent(args)
        };
    },
    acceptCheerResult: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.acceptCheerResult)));
        return ecs.randomReplace(args);
    },
    completeTechMine: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.completeTechMine)));
        return ecs.resource(args);
    },
    controlPanel: function(args){
        return {
            panelTrackComponent: ecs.panelTrackComponent(args),
            keyPressComponent: ecs.controlPanelKeyPressComponent(args)
        };
    },
    reflectSuccessGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.reflectSuccessGoal)));
        return ecs.worldGoal(args);
    },
    panelUiClickComponent: function(args){
        return {
            uiClick: panelUiClick
        };
    },
    fearTickModifier: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.fearTickModifier)));
        return {
            healthComponent: ecs.singleHealthComponent(args),
            tickModifyComponent: ecs.fearTickModifyComponent(args),
            rendererComponents: [ecs.towerTickModifierRenderComponent(args)],
            dayResponseComponent: ecs.killDayEndComponent(args),
            timedDeathComponent: ecs.timedDeathComponent(args)
        };
    },
    hurtLoverMine: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.hurtLoverMine)));
        return ecs.emotionResource(args);
    },
    personGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.personGoal)));
        args["particleKey"] = PARAMETERS.newParticle;
        args["description"] = (args.description && args.description[args.personHeld]) ? args.description[args.personHeld] : args.description;
        args["touchable"] = true;
        args["playerKillable"] = !!(args.constructed);
        args["startEntityKey"] = args.personHeld;
        args["canExpandInto"] = false;
        args["currencyRequested"] = derivePersonGoalCurrencies(args.currencyRequested, args.personHeld);
        args["killOnTransform"] = (args["killOnTransform"] === undefined) ? true : args["killOnTransform"];
        if (args.personHeld === undefined){ throw "ECS fail " + args.key + " missing personHeld"; }
        return {
            uiActionComponent: ecs.buildingUiActionComponent(args),
            goalTrackComponent: ecs.personGoalTrackComponent(args),
            createExecutables: [ecs.checkManagedComponentExec(args), (args.randomPosition) ? ecs.randomOpenPositionExec(args) : undefined, ecs.makeExtraPathsExec(args), ecs.chkForPlayerInterrupt(args), ecs.buildingConnectExec(args), (args.timeToDeath) ? ecs.timedDifficultyModifyExec(args) : undefined],
            timedDeathComponent: (args.timeToDeath) ? ecs.worldGoalTimedDeathComponent(args) : undefined,
            uiDataComponent: ecs.uiDataComponent(args),
            descriptionComponent: ecs.descriptionComponent(args),
            interactionComponent: ecs.interactionComponent(args),
            identityComponent: ecs.identityComponent(args),
            rendererComponents: [ecs.worldGoalRenderComponent(args), ecs.progressRenderComponent(args), ecs.timedDeathRenderComponent(args)],
            focusTickResponseComponent: (args.aliveEntities) ? ecs.aliveMakeComponent(args) : undefined,
            managedComponent: (args.sourceTrait) ? ecs.managedComponent(args) : undefined,
            flipComponent: (args.flipEntity) ? ecs.flipComponent(args) : undefined,
            personHeldComponent: ecs.personHeldComponent(args),
            worldGoalComponent: ecs.worldGoalComponent(args),
            focusPathStopComponent: ecs.worldGoalFocusPathStopComponent(args),
            clickComponent: ecs.fullMenuClickComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            uniqueComponent: ecs.uniqueComponent(args),
            positionComponent: ecs.positionComponent(args),
            notificationComponents: [ecs.worldGoalNotificationComponent(args)],
            junctionComponent: ecs.junctionComponent(args),
            pauseComponent: ecs.pauseComponent(args),
            playerMadeComponent: ecs.playerMadeComponent(args)
        };
    },
    controlComponent: function(args){
        if (args.makeEntity === undefined){ throw "ECS fail " + args.key + " missing makeEntity"; }
        if (args.canMakeEntity === undefined){ throw "ECS fail " + args.key + " missing canMakeEntity"; }
        if (args.checkMakePos === undefined){ throw "ECS fail " + args.key + " missing checkMakePos"; }
        return {
            aiVal: (args["aiVal"] === undefined) ? 0 : args["aiVal"],
            checkMakePos: args['checkMakePos'],
            isNew: true,
            canMakeEntity: args['canMakeEntity'],
            currenciesMade: (args["currenciesMade"] === undefined) ? [] : args["currenciesMade"],
            wrappedFunction: undefined,
            managerKey: (args["managerKey"] === undefined) ? args.sourceKey : args["managerKey"],
            makeEntity: args['makeEntity'],
            getAiVal: controlComponentGetAiVal,
            getWrappedFunction: getWrappedFunction
        };
    },
    findProblem: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.findProblem)));
        return ecs.makeRandomMultiple(args);
    },
    endHangOutGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.endHangOutGoalConstruction)));
        return ecs.construction(args);
    },
    impulseFocusStop: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            focusPathStopComponent: ecs.impulseFocusPathStopComponent(args),
            positionComponent: ecs.positionComponent(args)
        };
    },
    willDrainGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.willDrainGoal)));
        args["touchable"] = true;
        args["particleKey"] = PARAMETERS.newParticle;
        args["canExpandInto"] = false;
        args["playerKillable"] = !!(args.constructed);
        args["killOnTransform"] = (args["killOnTransform"] === undefined) ? true : args["killOnTransform"];
        return {
            uiActionComponent: ecs.buildingUiActionComponent(args),
            goalTrackComponent: ecs.checkWorldGoalComponent(args),
            createExecutables: [ecs.randomOpenPositionExec(args), ecs.drainGoalCreate(args)],
            timedDeathComponent: (args.timeToDeath) ? ecs.worldGoalTimedDeathComponent(args) : undefined,
            uiDataComponent: ecs.uiDataComponent(args),
            descriptionComponent: ecs.descriptionComponent(args),
            interactionComponent: ecs.interactionComponent(args),
            identityComponent: ecs.identityComponent(args),
            rendererComponents: [ecs.worldGoalRenderComponent(args), ecs.progressRenderComponent(args), ecs.timedDeathRenderComponent(args)],
            focusTickResponseComponent: (args.aliveEntities) ? ecs.aliveMakeComponent(args) : undefined,
            managedComponent: (args.sourceTrait) ? ecs.managedComponent(args) : undefined,
            flipComponent: (args.flipEntity) ? ecs.flipComponent(args) : undefined,
            worldGoalComponent: ecs.worldGoalComponent(args),
            focusPathStopComponent: ecs.worldGoalFocusPathStopComponent(args),
            clickComponent: ecs.fullMenuClickComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            uniqueComponent: ecs.uniqueComponent(args),
            positionComponent: ecs.positionComponent(args),
            notificationComponents: [ecs.worldGoalNotificationComponent(args)],
            junctionComponent: ecs.junctionComponent(args),
            pauseComponent: ecs.pauseComponent(args),
            playerMadeComponent: ecs.playerMadeComponent(args)
        };
    },
    bulletRenderComponent: function(args){
        if (args.bulletSourcePos === undefined){ throw "ECS fail " + args.key + " missing bulletSourcePos"; }
        if (args.bulletEndPos === undefined){ throw "ECS fail " + args.key + " missing bulletEndPos"; }
        if (args.damage === undefined){ throw "ECS fail " + args.key + " missing damage"; }
        if (args.bonus === undefined){ throw "ECS fail " + args.key + " missing bonus"; }
        return {
            render: bulletRender,
            bonus: args['bonus'],
            bulletSourcePos: args['bulletSourcePos'],
            damage: args['damage'],
            priority: 9,
            renderState: "default",
            bulletEndPos: args['bulletEndPos']
        };
    },
    desireImprovementDeposit: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.desireImprovementDeposit)));
        return ecs.deposit(args);
    },
    thoughtAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.thoughtAction)));
        return ecs.currencyAction(args);
    },
    joyRideGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.joyRideGoalConstruction)));
        return ecs.memoryConstruction(args);
    },
    releasedSong_ext: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.releasedSong_ext)));
        return ecs.sourceExtension(args);
    },
    love_lover: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.love_lover)));
        return ecs.currency(args);
    },
    timeForceExec: function(args){
        return {
            execute: timeForceExec
        };
    },
    selfWorthlessMineConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.selfWorthlessMineConstruction)));
        return ecs.emotionSourceConstruction(args);
    },
    reflectSuccessGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.reflectSuccessGoalConstruction)));
        return ecs.construction(args);
    },
    addPersonAndOpinion: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            createExecutables: [ecs.addPersonAndOpinionExec(args)]
        };
    },
    goalButton: function(args){
        args["subPanel"] = (args["subPanel"] === undefined) ? "Goals" : args["subPanel"];
        return {
            uiActionComponent: ecs.buttonUiActionComponent(args),
            buttonControlComponent: ecs.goalButtonControlComponent(args),
            createExecutables: [ecs.killIfFlagExec(args), ecs.markTraitNew(args)],
            timedDeathComponent: (args.timeToDeath) ? ecs.timedDeathComponent(args) : undefined,
            uiDataComponent: ecs.uiDataComponent(args),
            descriptionComponent: ecs.descriptionComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            interactionComponent: ecs.interactionComponent(args),
            identityComponent: ecs.identityComponent(args)
        };
    },
    apologizeAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.apologizeAction)));
        return ecs.currencyAction(args);
    },
    musicLove_ext: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.musicLove_ext)));
        return ecs.sourceExtension(args);
    },
    towerTickModifierRenderComponent: function(args){
        if (args.key === undefined){ throw "ECS fail " + args.key + " missing key"; }
        if (args.image === undefined){ throw "ECS fail " + args.key + " missing image"; }
        return {
            render: towerTickModifierRender,
            image: args['image'],
            priority: 11,
            key: args['key'],
            getTowerForTickModifierRender: getTowerForTickModifierRender,
            renderState: "default"
        };
    },
    strengthMine: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.strengthMine)));
        return ecs.resource(args);
    },
    emoInspiration: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.emoInspiration)));
        return ecs.trait(args);
    },
    opinion: function(args){
        args["isaacEntities"] = generateOpinionIsaacs(args);
        return {
            isaacEntityComponent: ecs.isaacEntityComponent(args),
            opinionComponent: ecs.opinionComponent(args),
            focusTickResponseComponent: (args.aliveEntities) ? ecs.aliveMakeComponent(args) : undefined,
            personHeldComponent: ecs.personHeldComponent(args),
            timedDeathComponent: (args.timeToDeath) ? ecs.timedDeathComponent(args) : undefined,
            focusMakingComponent: (args.focusCount) ? ecs.focusMakingComponent(args) : undefined,
            tickComponent: ecs.opinionTickComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            uniqueComponent: ecs.uniqueComponent(args),
            identityComponent: ecs.identityComponent(args)
        };
    },
    hangingOutToAlienation: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.hangingOutToAlienation)));
        return ecs.transformExec(args);
    },
    toldWorthGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.toldWorthGoal)));
        return ecs.worldGoal(args);
    },
    chapter2Menu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.chapter2Menu)));
        return ecs.gameStageStartMenu(args);
    },
    swallowBitternessGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.swallowBitternessGoalConstruction)));
        return ecs.construction(args);
    },
    struggleComplimentsGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.struggleComplimentsGoalConstruction)));
        return ecs.construction(args);
    },
    demons_ext: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.demons_ext)));
        return ecs.sourceExtension(args);
    },
    nascentSongToSong: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.nascentSongToSong)));
        return ecs.transformExec(args);
    },
    discardFreshMusicGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.discardFreshMusicGoalConstruction)));
        return ecs.construction(args);
    },
    uiDataComponent: function(args){
        if (args.image === undefined){ throw "ECS fail " + args.key + " missing image"; }
        return {
            subPanel: (args["subPanel"] === undefined) ? undefined : args["subPanel"],
            image: args['image'],
            drawImage: uiDataDrawImage,
            lastPath: undefined
        };
    },
    joyFleshTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.joyFleshTowerConstruction)));
        return ecs.construction(args);
    },
    completeEmoMineConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.completeEmoMineConstruction)));
        return ecs.construction(args);
    },
    optimismTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.optimismTower)));
        return ecs.memoryTower(args);
    },
    goalCompleteExec: function(args){
        return {
            execute: goalCompleteExec
        };
    },
    stuckProblem: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.stuckProblem)));
        return ecs.emotionSource(args);
    },
    shareJoyTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.shareJoyTowerConstruction)));
        return ecs.construction(args);
    },
    emoWorkAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.emoWorkAction)));
        return ecs.currencyAction(args);
    },
    groupPanel: function(args){
        args["y"] = 5;
        args["x"] = (width - 800) / 2;
        args["size"] = [800, 300];
        return {
            panelComponent: ecs.panelComponent(args),
            absolutePositionComponent: ecs.absolutePositionComponent(args),
            uiClickComponent: ecs.panelUiClickComponent(args),
            identityComponent: ecs.identityComponent(args),
            uniqueComponent: ecs.uniqueComponent(args)
        };
    },
    trait: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.trait)));
        args["touchable"] = true;
        args["health"] = (args["health"] === undefined) ? 2000 : args["health"];
        args["panelKey"] = (args["panelKey"] === undefined) ? PARAMETERS.traitPanel : args["panelKey"];
        return {
            killExecutables: [ecs.killExtensions(args)],
            isaacEntityComponent: ecs.isaacEntityComponent(args),
            uniqueComponent: ecs.uniqueComponent(args),
            rendererComponents: [ecs.builtSourceRendererComponent(args)],
            uiActionComponent: ecs.buildingUiActionComponent(args),
            focusTickResponseComponent: (args.aliveEntities) ? ecs.focusTickAliveMakerComponent(args) : undefined,
            mapSpawnComponent: ecs.builtSourceMapSpawnComponent(args),
            experienceComponent: ecs.experienceComponent(args),
            personHeldComponent: (args.personHeld) ? ecs.personHeldComponent(args) : undefined,
            createExecutables: [ecs.builtSourceCreateExec(args), ecs.makeExtraPathsExec(args)],
            junctionComponent: ecs.junctionComponent(args),
            clickComponent: ecs.fullMenuClickComponent(args),
            uiDataComponent: ecs.uiDataComponent(args),
            entityManagerComponent: ecs.entityManagerComponent(args),
            descriptionComponent: ecs.descriptionComponent(args),
            healthComponent: ecs.healthComponent(args),
            interactionComponent: ecs.interactionComponent(args),
            identityComponent: ecs.identityComponent(args),
            positionComponent: ecs.positionComponent(args),
            menuCloseComponent: ecs.traitMenuCloseComponent(args),
            playerMadeComponent: ecs.fixedPlayerMadeComponent(args)
        };
    },
    talkTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.talkTowerConstruction)));
        return ecs.construction(args);
    },
    playthingDeposit: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.playthingDeposit)));
        return ecs.deposit(args);
    },
    chapterPanelRenderComponent: function(args){
        return {
            insetColor: "#000000",
            canShowGoal: chapterPanelCanShowGoal,
            tickImage: "tick",
            fullAnimationTime: 0.1,
            yTextOffset: 5,
            backImage: "uiBack",
            drawImageButton: drawImageButton,
            animatingTime: 0.1,
            priority: 18,
            render: outPanelRender,
            drawPanelButton: drawPanelButton,
            snapPanelPos: snapPanelPos,
            showGoal: chapterPanelShowGoal,
            renderTime: 0,
            textColor: "#000000",
            renderState: "default",
            goalOpenFunctions: {},
            specificRender: chapterPanelRender,
            drawHelpButton: drawHelpButton
        };
    },
    cheerUpMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.cheerUpMenu)));
        return ecs.goalCompleteMenu(args);
    },
    aloneSad: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.aloneSad)));
        return ecs.emotionSource(args);
    },
    subconsciousRequestComponent: function(args){
        return {
            needsWorker: trueFunction,
            calculateWorkerTicks: calculateWorkerTicks,
            workerRequested: args.sourceTrait ? (args.sourceTrait + '_subconscious') : PARAMETERS.subconsciousWorkerKey,
            workersActive: [],
            workerCount: 1,
            subconsciousRequest: true,
            workerLife: 20
        };
    },
    understandFailingsMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.understandFailingsMenu)));
        return ecs.goalCompleteMenu(args);
    },
    envyGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.envyGoal)));
        return ecs.worldGoal(args);
    },
    greatnessDreamDeposit: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.greatnessDreamDeposit)));
        return ecs.deposit(args);
    },
    demonsToQuietDemons: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.demonsToQuietDemons)));
        return ecs.transformExec(args);
    },
    talkTowerDamageComponent: function(args){
        args["charges"] = (args["charges"] === undefined) ? 5 : args["charges"];
        return {
            checkTargetEntity: checkTargetEntity,
            charges: args['charges'],
            targetCount: 100,
            damage: (args["damage"] === undefined) ? 10 : args["damage"],
            impulseTicks: (args["impulseTicks"] === undefined) ? 0 : args["impulseTicks"],
            damageMultipliers: args['damageMultipliers'],
            ticks: 0,
            maxCharges: args['charges'],
            calculateDamage: calculateDamage,
            getHitPos: talkTowerGetHitPos,
            hitPos: undefined
        };
    },
    rememberGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.rememberGoalConstruction)));
        return ecs.construction(args);
    },
    yourAloneTime: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.yourAloneTime)));
        return ecs.disableTrait(args);
    },
    endGoalExec: function(args){
        if (args.flag === undefined){ throw "ECS fail " + args.key + " missing flag"; }
        if (args.buttonKey === undefined){ throw "ECS fail " + args.key + " missing buttonKey"; }
        return {
            buttonKey: args['buttonKey'],
            execute: endGoalExecute,
            flag: args['flag']
        };
    },
    releasedSongTraitConnectSelfCore: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.releasedSongTraitConnectSelfCore)));
        return ecs.traitConnect(args);
    },
    daydreamingToAchievement: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.daydreamingToAchievement)));
        return ecs.transformExec(args);
    },
    allTrainPanel: function(args){
        args["y"] = 5;
        args["x"] = (width - 800) / 2;
        args["key"] = PARAMETERS.allTrainPanel;
        args["size"] = [500, 625];
        return {
            rendererComponents: [ecs.allTrainPanelRenderComponent(args)],
            absolutePositionComponent: ecs.absolutePositionComponent(args),
            uiClickComponent: ecs.panelUiClickComponent(args),
            panelComponent: ecs.panelComponent(args),
            uniqueComponent: ecs.uniqueComponent(args),
            identityComponent: ecs.identityComponent(args)
        };
    },
    tryForMeaningMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.tryForMeaningMenu)));
        return ecs.goalCompleteMenu(args);
    },
    rawInspirationDeposit: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.rawInspirationDeposit)));
        return ecs.deposit(args);
    },
    prideBurst: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.prideBurst)));
        return ecs.emotionSource(args);
    },
    memory_prideBurst: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.memory_prideBurst)));
        return ecs.memory(args);
    },
    loverJoyMineConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.loverJoyMineConstruction)));
        return ecs.emotionSourceConstruction(args);
    },
    resolveAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.resolveAction)));
        return ecs.currencyAction(args);
    },
    demolishPathButton: function(args){
        return {
            rendererComponents: [ecs.demolishPathRenderComponent(args)]
        };
    },
    conditionalMakeReplaceMarker: function(args){
        if (args.entitiesToMake === undefined){ throw "ECS fail " + args.key + " missing entitiesToMake"; }
        if (args.stopEntities === undefined){ throw "ECS fail " + args.key + " missing stopEntities"; }
        return {
            entitiesToMake: args['entitiesToMake'],
            stopEntities: args['stopEntities'],
            getReplaceKeys: conditionalMakeGetReplaceKeys
        };
    },
    tryForMeaningButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.tryForMeaningButton)));
        return ecs.goalButton(args);
    },
    deriveCoolThingGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.deriveCoolThingGoal)));
        return ecs.worldGoal(args);
    },
    reflectFailureGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.reflectFailureGoalConstruction)));
        return ecs.construction(args);
    },
    explainFindMusicFail: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.explainFindMusicFail)));
        return ecs.menuModifier(args);
    },
    breakdownMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.breakdownMenu)));
        return ecs.goalCompleteMenu(args);
    },
    minorInsultMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.minorInsultMenu)));
        return ecs.goalCompleteMenu(args);
    },
    releaseSatisfyMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.releaseSatisfyMenu)));
        return ecs.goalCompleteMenu(args);
    },
    alienation: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.alienation)));
        return ecs.trait(args);
    },
    rideResult: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.rideResult)));
        return ecs.randomReplace(args);
    },
    emoWorkTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.emoWorkTower)));
        return ecs.tower(args);
    },
    showMusicMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.showMusicMenu)));
        return ecs.goalCompleteMenu(args);
    },
    bitterness_ext: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.bitterness_ext)));
        return ecs.sourceExtension(args);
    },
    failureMine: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.failureMine)));
        return ecs.resource(args);
    },
    insight: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.insight)));
        return ecs.currency(args);
    },
    ignoreDifferencesMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.ignoreDifferencesMenu)));
        return ecs.goalCompleteMenu(args);
    },
    angerFleshButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.angerFleshButton)));
        return ecs.towerButton(args);
    },
    findMistakeButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.findMistakeButton)));
        return ecs.goalButton(args);
    },
    killExtensions: function(args){
        return {
            execute: killExtensions
        };
    },
    indulgeHappyButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.indulgeHappyButton)));
        return ecs.goalButton(args);
    },
    notificationSerializeComponent: function(args){
        return {
            serialize: args['serialize'],
            postSerialize: notificationSerialize
        };
    },
    quietDemonsStrengthButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.quietDemonsStrengthButton)));
        return ecs.goalButton(args);
    },
    emoWorkButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.emoWorkButton)));
        return ecs.towerButton(args);
    },
    apologizeTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.apologizeTowerConstruction)));
        return ecs.construction(args);
    },
    friendToMakingWork: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.friendToMakingWork)));
        return ecs.transformExec(args);
    },
    junction: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            junctionComponent: ecs.junctionComponent(args),
            positionComponent: ecs.positionComponent(args)
        };
    },
    spell: function(args){
        return {
            identityComponent: ecs.identityComponent(args),
            rendererComponents: [ecs.spellRenderComponent(args)],
            spellComponent: ecs.spellComponent(args)
        };
    },
    tryOldJokesButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.tryOldJokesButton)));
        return ecs.goalButton(args);
    },
    achievementGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.achievementGoalConstruction)));
        return ecs.construction(args);
    },
    ambition: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.ambition)));
        return ecs.trait(args);
    },
    workerRequireComponent: function(args){
        if (args.workerRequired === undefined){ throw "ECS fail " + args.key + " missing workerRequired"; }
        return {
            workerRequired: args['workerRequired'],
            workingTicks: 0
        };
    },
    day: function(args){
        return {
            dayCounterComponent: ecs.dayCounterComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            killExecutables: [ecs.dayKillExec(args)]
        };
    },
    museFleshTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.museFleshTower)));
        return ecs.tower(args);
    },
    tellQuitGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.tellQuitGoalConstruction)));
        return ecs.construction(args);
    },
    integrateGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.integrateGoal)));
        return ecs.worldGoal(args);
    },
    beCaredButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.beCaredButton)));
        return ecs.goalButton(args);
    },
    intrigueTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.intrigueTowerConstruction)));
        return ecs.construction(args);
    },
    dreamOfGreatnessGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.dreamOfGreatnessGoal)));
        return ecs.worldGoal(args);
    },
    explainMajorMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.explainMajorMenu)));
        return ecs.goalCompleteMenu(args);
    },
    singleUseTrainPath: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.singleUseTrainPath)));
        return {
            rendererComponents: [ecs.trainPathTowerActiveRender(args), ecs.trainPathSecondRender(args), ecs.trainPathCircRenderComponent(args)],
            trainPathComponent: ecs.singleUseTrainPathComponent(args),
            menuCloseComponent: ecs.trainPathMenuCloseComponent(args),
            createExecutables: [ecs.setTrainColor(args)],
            killExecutables: [ecs.blinkTrainStops(args)],
            healthComponent: ecs.singleHealthComponent(args),
            interactionComponent: ecs.interactionComponent(args),
            focusCostComponent: ecs.focusCostComponent(args)
        };
    },
    cynicismButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.cynicismButton)));
        return ecs.upgradeButton(args);
    },
    makeGoalMenuExecutable: function(args){
        if (args.goalMenuKey === undefined){ throw "ECS fail " + args.key + " missing goalMenuKey"; }
        return {
            execute: makeGoalMenuExecute,
            goalMenuKey: args['goalMenuKey']
        };
    },
    makeEmoInspiration: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.makeEmoInspiration)));
        return ecs.entityReplace(args);
    },
    loseImpressedMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.loseImpressedMenu)));
        return ecs.goalCompleteMenu(args);
    },
    needLoverAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.needLoverAction)));
        return ecs.currencyAction(args);
    },
    loverLeaveMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.loverLeaveMenu)));
        return ecs.goalCompleteMenu(args);
    },
    fishComplimentsGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.fishComplimentsGoal)));
        return ecs.worldGoal(args);
    },
    memoryEmotionSource: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            identityComponent: ecs.identityComponent(args),
            emotionSourceComponent: ecs.emotionSourceComponent(args),
            createExecutables: [ecs.emotionSourceCreateExec(args), ecs.killEntity(args)]
        };
    },
    allCurrencyRenderComponent: function(args){
        return {
            priority: 13,
            image: "currency",
            renderState: "default",
            render: allCurrencyRender
        };
    },
    fatigueMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.fatigueMenu)));
        return ecs.goalCompleteMenu(args);
    },
    acceptAlienationGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.acceptAlienationGoalConstruction)));
        return ecs.construction(args);
    },
    museFleshButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.museFleshButton)));
        return ecs.towerButton(args);
    },
    damamgeUpgradeCheckExec: function(args){
        if (args.towerKey === undefined){ throw "ECS fail " + args.key + " missing towerKey"; }
        if (args.flag === undefined){ throw "ECS fail " + args.key + " missing flag"; }
        return {
            entitiesToKill: (args["entitiesToKill"] === undefined) ? [] : args["entitiesToKill"],
            maxUpgradeCount: (args["maxUpgradeCount"] === undefined) ? 3 : args["maxUpgradeCount"],
            execute: damamgeUpgradeCheckExec,
            towerKey: args['towerKey'],
            flag: args['flag']
        };
    },
    steamRelease: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.steamRelease)));
        return ecs.steamAchievement(args);
    },
    museInspirationGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.museInspirationGoal)));
        return ecs.worldGoal(args);
    },
    randomMenuComponent: function(args){
        if (args.goodCount === undefined){ throw "ECS fail " + args.key + " missing goodCount"; }
        if (args.badCount === undefined){ throw "ECS fail " + args.key + " missing badCount"; }
        if (args.goodResult === undefined){ throw "ECS fail " + args.key + " missing goodResult"; }
        return {
            goodResult: args['goodResult'],
            goodCount: args['goodCount'],
            badCount: args['badCount']
        };
    },
    killCurrency: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            createExecutables: [ecs.killCurrencyExec(args), ecs.killEntity(args)]
        };
    },
    sustainTechInspirationMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.sustainTechInspirationMenu)));
        return ecs.goalCompleteMenu(args);
    },
    failureMineConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.failureMineConstruction)));
        return ecs.construction(args);
    },
    towerFocusPathStopComponent: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.towerFocusPathStopComponent)));
        return {
            interactWithTrain: towerInteractWithTrain,
            currenciesNeededAtPoint: towerCurrenciesNeededAtPoint,
            currenciesMadeAtPoint: towerCurrenciesMadeAtPoint,
            isDisabled: false,
            currenciesExistAtPoint: currenciesExistAtPoint,
            updateCurrencyDictWithStop: updateCurrencyDictWithStop,
            isPathEnd: (args["isPathEnd"] === undefined) ? false : args["isPathEnd"],
            convertExecutables: [ecs.makeParticleEffect(args)],
            checkFocusStopDisabled: checkFocusStopDisabled,
            risingTextKey: "risingText",
            requiredOpinions: (args["requiredOpinions"] === undefined) ? [] : args["requiredOpinions"],
            enableChecks: (args["enableChecks"] === undefined) ? [] : args["enableChecks"]
        };
    },
    cheerPrideButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.cheerPrideButton)));
        return ecs.memoryGoalButton(args);
    },
    emotionSourceConstruction: function(args){
        args["touchable"] = true;
        args["canExpandInto"] = false;
        args["killWithoutPath"] = true;
        return {
            focusPathStopComponent: ecs.constructionFocusPathStopComponent(args),
            rendererComponents: [ecs.constructionRenderComponent(args), ecs.progressRenderComponent(args)],
            personHeldComponent: ecs.personHeldComponent(args),
            managedComponent: ecs.managedComponent(args),
            constructionComponent: ecs.memoryConstructionComponent(args),
            createExecutables: [ecs.emotionSourceConstructionCreateExec(args)],
            junctionComponent: ecs.junctionComponent(args),
            clickComponent: ecs.fullMenuClickComponent(args),
            uiDataComponent: ecs.uiDataComponent(args),
            tickComponent: ecs.constructionTickComponent(args),
            descriptionComponent: ecs.descriptionComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            interactionComponent: ecs.interactionComponent(args),
            identityComponent: ecs.identityComponent(args),
            positionComponent: ecs.positionComponent(args),
            playerMadeComponent: ecs.fixedPlayerMadeComponent(args)
        };
    },
    buildingUiActionComponent: function(args){
        return {
            getUiActions: getUiActions,
            generateUiActions: entityGenerateUiActions,
            memoizedActions: undefined
        };
    },
    solutionAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.solutionAction)));
        return ecs.currencyAction(args);
    },
    fadeHangingOut: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.fadeHangingOut)));
        return ecs.timedCreateEntities(args);
    },
    museInspireResult: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.museInspireResult)));
        return ecs.randomReplace(args);
    },
    dismissSuccessMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.dismissSuccessMenu)));
        return ecs.goalCompleteMenu(args);
    },
    techWorkButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.techWorkButton)));
        return ecs.towerButton(args);
    },
    needTimeMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.needTimeMenu)));
        return ecs.goalCompleteMenu(args);
    },
    ambitionToDaydreaming: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.ambitionToDaydreaming)));
        return ecs.transformExec(args);
    },
    clickKillComponent: function(args){
        return {
            onClick: clickKill
        };
    },
    entityPanel: function(args){
        args["size"] = (args["size"] === undefined) ? [550, 450] : args["size"];
        return {
            rendererComponents: [ecs.entityPanelRenderComponent(args)],
            absolutePositionComponent: ecs.absolutePositionComponent(args),
            uiClickComponent: ecs.panelUiClickComponent(args),
            helpPanelComponent: ecs.helpPanelComponent(args),
            panelComponent: ecs.entityPanelComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            uniqueComponent: ecs.uniqueComponent(args),
            identityComponent: ecs.identityComponent(args)
        };
    },
    strengthTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.strengthTower)));
        return ecs.tower(args);
    },
    integrateGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.integrateGoalConstruction)));
        return ecs.construction(args);
    },
    rageTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.rageTower)));
        return ecs.memoryTower(args);
    },
    openMenuMarkerComponent: function(args){
        if (args.entityUid === undefined){ throw "ECS fail " + args.key + " missing entityUid"; }
        return {
            entityUid: args['entityUid']
        };
    },
    ignoreInsult1Menu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.ignoreInsult1Menu)));
        return ecs.goalCompleteMenu(args);
    },
    memoryTowerButton: function(args){
        args["managerKey"] = args.sourceKey;
        args["multiTrait"] = true;
        args["subPanel"] = (args["subPanel"] === undefined) ? "Towers" : args["subPanel"];
        args["multiTrait"] = (args["multiTrait"] === undefined) ? false : args["multiTrait"];
        if (args.sourceKey === undefined){ throw "ECS fail " + args.key + " missing sourceKey"; }
        return {
            healthComponent: ecs.singleHealthComponent(args),
            isaacEntityComponent: ecs.memoryTowerButtonIssacEntityComponent(args),
            uiActionComponent: ecs.buttonUiActionComponent(args),
            uiDataComponent: ecs.uiDataComponent(args),
            buttonControlComponent: ecs.towerControlComponent(args),
            createExecutables: [ecs.markTraitNew(args)],
            timedDeathComponent: (args.timeToDeath) ? ecs.timedDeathComponent(args) : undefined,
            killExecutables: [ecs.killTowerButtonExec(args)],
            descriptionComponent: ecs.descriptionComponent(args),
            uniqueComponent: (args.timeToDeath === undefined && !args.multiTrait) ? ecs.uniqueComponent(args) : undefined,
            interactionComponent: ecs.interactionComponent(args),
            identityComponent: ecs.identityComponent(args)
        };
    },
    selfHateMine: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.selfHateMine)));
        return ecs.emotionResource(args);
    },
    fishComplimentsGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.fishComplimentsGoalConstruction)));
        return ecs.construction(args);
    },
    workEmotionDeposit: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.workEmotionDeposit)));
        return ecs.deposit(args);
    },
    daydreamMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.daydreamMenu)));
        return ecs.goalCompleteMenu(args);
    },
    makePlaythingMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.makePlaythingMenu)));
        return ecs.goalCompleteMenu(args);
    },
    goalButtonControlComponent: function(args){
        if (args.entityCost === undefined){ throw "ECS fail " + args.key + " missing entityCost"; }
        if (args.entityKey === undefined){ throw "ECS fail " + args.key + " missing entityKey"; }
        if (args.constructionKey === undefined){ throw "ECS fail " + args.key + " missing constructionKey"; }
        return {
            entityCost: args['entityCost'],
            aiVal: (args["aiVal"] === undefined) ? 0 : args["aiVal"],
            constructionKey: args['constructionKey'],
            checkMakePos: checkTowerMakePos,
            isNew: true,
            currenciesMade: (args["currenciesMade"] === undefined) ? [] : args["currenciesMade"],
            canMakeEntity: canMakeGoal,
            requiredEntities: (args["requiredEntities"] === undefined) ? [] : args["requiredEntities"],
            entityKey: args['entityKey'],
            wrappedFunction: undefined,
            disablingEntities: (args["disablingEntities"] === undefined) ? [] : args["disablingEntities"],
            managerKey: (args["managerKey"] === undefined) ? args.sourceKey : args["managerKey"],
            makeEntity: makeGoal,
            getAiVal: controlComponentGetAiVal,
            getWrappedFunction: getWrappedFunction,
            displayRiskIcon: (args["displayRiskIcon"] === undefined) ? false : args["displayRiskIcon"]
        };
    },
    deriveInsightGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.deriveInsightGoalConstruction)));
        return ecs.construction(args);
    },
    depositComponent: function(args){
        if (args.deposit === undefined){ throw "ECS fail " + args.key + " missing deposit"; }
        return {
            deposit: args['deposit']
        };
    },
    dailyEmotionMaker: function(args){
        return {
            dayResponseComponent: ecs.dailyEmotionMakerDayResponseComponent(args)
        };
    },
    fadingMemory: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.fadingMemory)));
        args["touchable"] = true;
        args["key"] = args.key + '_' + args.uid;
        args["health"] = (args["health"] === undefined) ? 2000 : args["health"];
        args["panelKey"] = (args["panelKey"] === undefined) ? PARAMETERS.traitPanel : args["panelKey"];
        return {
            uiActionComponent: ecs.buildingUiActionComponent(args),
            experienceComponent: ecs.experienceComponent(args),
            createExecutables: [ecs.builtSourceCreateExec(args), ecs.makeExtraPathsExec(args)],
            timedDeathComponent: ecs.timedDeathComponent(args),
            uiDataComponent: ecs.uiDataComponent(args),
            entityManagerComponent: ecs.entityManagerComponent(args),
            descriptionComponent: ecs.descriptionComponent(args),
            interactionComponent: ecs.interactionComponent(args),
            identityComponent: ecs.identityComponent(args),
            isaacEntityComponent: ecs.isaacEntityComponent(args),
            rendererComponents: [ecs.builtSourceRendererComponent(args)],
            focusTickResponseComponent: (args.aliveEntities) ? ecs.focusTickAliveMakerComponent(args) : undefined,
            menuCloseComponent: ecs.traitMenuCloseComponent(args),
            personHeldComponent: (args.personHeld) ? ecs.personHeldComponent(args) : undefined,
            clickComponent: ecs.fullMenuClickComponent(args),
            killExecutables: [ecs.killExtensions(args)],
            healthComponent: ecs.healthComponent(args),
            uniqueComponent: ecs.uniqueComponent(args),
            positionComponent: ecs.positionComponent(args),
            mapSpawnComponent: ecs.builtSourceMapSpawnComponent(args),
            fadingMemoryComponent: ecs.fadingMemoryComponent(args),
            junctionComponent: ecs.junctionComponent(args),
            playerMadeComponent: ecs.fixedPlayerMadeComponent(args)
        };
    },
    minorInsultGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.minorInsultGoal)));
        return ecs.worldGoal(args);
    },
    qualityRenderComponent: function(args){
        return {
            priority: 9,
            renderState: "default",
            render: qualityRender
        };
    },
    disabledTowerRenderComponent: function(args){
        return {
            priority: 6,
            image: "disabledTower",
            renderState: "default",
            render: disabledTowerRender
        };
    },
    assertUniqueComponent: function(args){
        return {
            uniqueDuplicateEvent: assertUnique
        };
    },
    deposit: function(args){
        args["touchable"] = true;
        args["canExpandInto"] = false;
        return {
            junctionComponent: ecs.junctionComponent(args),
            rendererComponents: [ecs.pathStopRenderComponent(args), ecs.timedDeathRenderComponent(args)],
            uiActionComponent: ecs.buildingUiActionComponent(args),
            focusTickResponseComponent: ecs.aliveMakeComponent(args),
            depositComponent: ecs.depositComponent(args),
            managedComponent: (args.sourceTrait) ? ecs.managedComponent(args) : undefined,
            createExecutables: [ecs.checkMakeDeposit(args), ecs.depositPositionExec(args), ecs.makeExtraPathsExec(args), ecs.buildingConnectExec(args), (args.timeToDeath) ? ecs.timedDifficultyModifyExec(args) : undefined],
            focusPathStopComponent: ecs.depositFocusPathStopComponent(args),
            timedDeathComponent: (args.timeToDeath) ? ecs.timedDeathComponent(args) : undefined,
            clickComponent: ecs.fullMenuClickComponent(args),
            uiDataComponent: ecs.uiDataComponent(args),
            tickComponent: ecs.depositTickComponent(args),
            descriptionComponent: ecs.descriptionComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            interactionComponent: ecs.interactionComponent(args),
            identityComponent: ecs.identityComponent(args),
            positionComponent: ecs.positionComponent(args),
            playerMadeComponent: ecs.playerMadeComponent(args)
        };
    },
    bruteForceButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.bruteForceButton)));
        return ecs.towerButton(args);
    },
    easyWorkTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.easyWorkTowerConstruction)));
        return ecs.construction(args);
    },
    positionComponent: function(args){
        return {
            planeOfExistence: (args["planeOfExistence"] === undefined) ? undefined : args["planeOfExistence"],
            setPos: setPos,
            comparePos: compComparePos,
            canBuildAtPos: trueFunction,
            isPos: isPos,
            y: assertPosition(args.y, 1),
            x: assertPosition(args.x, 0),
            getAsPos: getAsPos
        };
    },
    personalityComponent: function(args){
        args["knownPersonalityTraits"] = (args["knownPersonalityTraits"] === undefined) ? [] : args["knownPersonalityTraits"];
        args["unknownPersonalityTraits"] = (args["unknownPersonalityTraits"] === undefined) ? [] : args["unknownPersonalityTraits"];
        if (args.knownPersonalityTraits === undefined){ throw "ECS fail " + args.key + " missing knownPersonalityTraits"; }
        return {
            knownPersonalityTraits: args['knownPersonalityTraits'],
            heldOpinions: (args["heldOpinions"] === undefined) ? PARAMETERS.defaultHeldOpinions : args["heldOpinions"],
            getAll: getAllPersonality,
            addOpinionToPersonality: addOpinionToPersonality,
            personHistory: [],
            personalityTraits: args.unknownPersonalityTraits.concat(args.knownPersonalityTraits)
        };
    },
    frustrationMineConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.frustrationMineConstruction)));
        return ecs.memoryConstruction(args);
    },
    focusPanelAbsolutePositionComponent: function(args){
        return {
            y: 678,
            x: 300,
            size: [300, 90]
        };
    },
    healthBleedTickComponent: function(args){
        return {
            tick: healthBleedTick
        };
    },
    cheerMineButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.cheerMineButton)));
        return ecs.memoryResourceButton(args);
    },
    selfHate: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.selfHate)));
        return ecs.emotionSource(args);
    },
    indulgeCuriosityGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.indulgeCuriosityGoalConstruction)));
        return ecs.construction(args);
    },
    applyAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.applyAction)));
        return ecs.currencyAction(args);
    },
    indulgeHappyGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.indulgeHappyGoalConstruction)));
        return ecs.construction(args);
    },
    workThroughGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.workThroughGoal)));
        return ecs.memoryGoal(args);
    },
    killTraitExec: function(args){
        if (args.traitToKill === undefined){ throw "ECS fail " + args.key + " missing traitToKill"; }
        return {
            traitToKill: args['traitToKill'],
            execute: killTraitExec
        };
    },
    discardFreshMusicGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.discardFreshMusicGoal)));
        return ecs.worldGoal(args);
    },
    timedDeathComponent: function(args){
        if (args.timeToDeath === undefined){ throw "ECS fail " + args.key + " missing timeToDeath"; }
        return {
            killOnTimeOut: (args["killOnTimeOut"] === undefined) ? [] : args["killOnTimeOut"],
            timeToDeath: args['timeToDeath'],
            deathExecs: (args["deathExecs"] === undefined) ? [] : args["deathExecs"],
            makeOnTimeOut: (args["makeOnTimeOut"] === undefined) ? [] : args["makeOnTimeOut"]
        };
    },
    focusTickerComponent: function(args){
        return {
            ticks: 2,
            impulseTicks: 10
        };
    },
    memoryConstructionComponent: function(args){
        if (args.sourceKey === undefined){ throw "ECS fail " + args.key + " missing sourceKey"; }
        return {
            constructionIdentifier: args['constructionIdentifier'],
            killWithoutPath: (args["killWithoutPath"] === undefined) ? false : args["killWithoutPath"],
            ticked: (args["ticked"] === undefined) ? false : args["ticked"],
            construct: memoryConstruct,
            currencyRequested: args['currencyRequested'],
            origCurrencyRequested: _.clone(args.currencyRequested),
            sourceKey: args['sourceKey']
        };
    },
    pointCounterNotificationComponent: function(args){
        return {
            notificationFunctions: simpleDict(PARAMETERS.addPoints, addPoints),
            serializeComponent: ecs.notificationSerializeComponent(args)
        };
    },
    freshMusic_ext: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.freshMusic_ext)));
        return ecs.sourceExtension(args);
    },
    setFlag: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            createExecutables: [ecs.setFlagExec(args), ecs.killEntity(args)]
        };
    },
    ignoreIssuesMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.ignoreIssuesMenu)));
        return ecs.goalCompleteMenu(args);
    },
    buttonUiActionComponent: function(args){
        return {
            getUiActions: getUiActions,
            inspectFunction: undefined,
            generateUiActions: buttonGenerateUiActions,
            memoizedActions: undefined
        };
    },
    quietDemonsToDemons: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.quietDemonsToDemons)));
        return ecs.transformExec(args);
    },
    callousnessTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.callousnessTower)));
        return ecs.tower(args);
    },
    stopDemonSelfEsteemGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.stopDemonSelfEsteemGoal)));
        return ecs.worldGoal(args);
    },
    worldGoalFocusPathStopComponent: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.worldGoalFocusPathStopComponent)));
        return {
            currencyEntities: (args["currencyEntities"] === undefined) ? undefined : args["currencyEntities"],
            interactWithTrain: currencyRequestedInteractWithTrain,
            currencyEffect: (args["currencyEffect"] === undefined) ? undefined : args["currencyEffect"],
            currenciesNeededAtPoint: currencyRequestedCurrenciesNeededAtPoint,
            updateCurrencyDictWithStop: updateCurrencyDictWithStop,
            getCurrencyComponent: worldGoalGetCurrencyComponent,
            currenciesExistAtPoint: currenciesExistAtPoint,
            currenciesMadeAtPoint: (args["currenciesMadeAtPoint"] === undefined) ? emptyArrayFunction : args["currenciesMadeAtPoint"],
            isPathEnd: true,
            requiredOpinions: (args["requiredOpinions"] === undefined) ? [] : args["requiredOpinions"],
            percentComplete: currencyRequestedFocusPathStopPercentComplete,
            checkFocusStopDisabled: checkFocusStopDisabled,
            isDisabled: false,
            enableChecks: (args["enableChecks"] === undefined) ? [] : args["enableChecks"]
        };
    },
    rechargeButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.rechargeButton)));
        return ecs.memoryTowerButton(args);
    },
    discardFreshMusicButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.discardFreshMusicButton)));
        return ecs.goalButton(args);
    },
    chapter3: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.chapter3)));
        return ecs.gameStage(args);
    },
    chapter2: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.chapter2)));
        return ecs.gameStage(args);
    },
    deriveHumanInsightMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.deriveHumanInsightMenu)));
        return ecs.goalCompleteMenu(args);
    },
    releaseSatisfyButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.releaseSatisfyButton)));
        return ecs.goalButton(args);
    },
    discardGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.discardGoal)));
        return ecs.worldGoal(args);
    },
    shareJoyMuseButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.shareJoyMuseButton)));
        return ecs.towerButton(args);
    },
    junctionComponent: function(args){
        return {
            heldItems: [],
            junctionItemForKey: junctionItemForKey
        };
    },
    understandTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.understandTowerConstruction)));
        return ecs.memoryConstruction(args);
    },
    holdableComponent: function(args){
        return {
            held: false
        };
    },
    memory_shameBurst: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.memory_shameBurst)));
        return ecs.memory(args);
    },
    lostControl: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.lostControl)));
        return {
            createExecutables: [ecs.makeGainControlMenuExec(args), ecs.timeForceExec(args)],
            holdControlComponent: ecs.holdControlComponent(args),
            killExecutables: [ecs.lostControlDeathExec(args)],
            healthComponent: ecs.singleHealthComponent(args),
            uniqueComponent: ecs.uniqueComponent(args),
            identityComponent: ecs.identityComponent(args),
            timeForceComponent: ecs.timeForceComponent(args)
        };
    },
    onTraitComponent: function(args){
        return {
            execute: memoryGoalCompleteExec,
            bonusTime: (args["bonusTime"] === undefined) ? 30 : args["bonusTime"],
            flareMemory: (args["flareMemory"] === undefined) ? false : args["flareMemory"],
            killMemory: (args["killMemory"] === undefined) ? false : args["killMemory"]
        };
    },
    flaggedMakeReplaceMarker: function(args){
        if (args.flagToCheck === undefined){ throw "ECS fail " + args.key + " missing flagToCheck"; }
        if (args.entitiesToMake === undefined){ throw "ECS fail " + args.key + " missing entitiesToMake"; }
        return {
            flagToCheck: args['flagToCheck'],
            entitiesToMake: args['entitiesToMake'],
            getReplaceKeys: flaggedMakeGetReplaceKeys
        };
    },
    emotionSourceComponent: function(args){
        return {
            emotions: unrollDict(args.emotionsToAdd)
        };
    },
    worldGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.worldGoal)));
        args["touchable"] = true;
        args["particleKey"] = PARAMETERS.newParticle;
        args["canExpandInto"] = false;
        args["playerKillable"] = !!(args.constructed);
        args["killOnTransform"] = (args["killOnTransform"] === undefined) ? true : args["killOnTransform"];
        return {
            uiActionComponent: ecs.buildingUiActionComponent(args),
            goalTrackComponent: ecs.checkWorldGoalComponent(args),
            createExecutables: [ecs.checkManagedComponentExec(args), (args.randomPosition) ? ecs.randomOpenPositionExec(args) : undefined, ecs.makeExtraPathsExec(args), ecs.chkForPlayerInterrupt(args), ecs.buildingConnectExec(args), (args.timeToDeath) ? ecs.timedDifficultyModifyExec(args) : undefined],
            timedDeathComponent: (args.timeToDeath) ? ecs.worldGoalTimedDeathComponent(args) : undefined,
            uiDataComponent: ecs.uiDataComponent(args),
            descriptionComponent: ecs.descriptionComponent(args),
            interactionComponent: ecs.interactionComponent(args),
            identityComponent: ecs.identityComponent(args),
            rendererComponents: [ecs.worldGoalRenderComponent(args), ecs.progressRenderComponent(args), ecs.timedDeathRenderComponent(args)],
            focusTickResponseComponent: (args.aliveEntities) ? ecs.aliveMakeComponent(args) : undefined,
            managedComponent: (args.sourceTrait) ? ecs.managedComponent(args) : undefined,
            flipComponent: (args.flipEntity) ? ecs.flipComponent(args) : undefined,
            worldGoalComponent: ecs.worldGoalComponent(args),
            focusPathStopComponent: ecs.worldGoalFocusPathStopComponent(args),
            clickComponent: ecs.fullMenuClickComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            uniqueComponent: ecs.uniqueComponent(args),
            positionComponent: ecs.positionComponent(args),
            notificationComponents: [ecs.worldGoalNotificationComponent(args)],
            junctionComponent: ecs.junctionComponent(args),
            pauseComponent: ecs.pauseComponent(args),
            playerMadeComponent: ecs.playerMadeComponent(args)
        };
    },
    willMine: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.willMine)));
        return ecs.resource(args);
    },
    flagBranchReplaceMarker: function(args){
        if (args.flagToCheck === undefined){ throw "ECS fail " + args.key + " missing flagToCheck"; }
        return {
            flagToCheck: args['flagToCheck'],
            flagEntities: (args["flagEntities"] === undefined) ? [] : args["flagEntities"],
            getReplaceKeys: flagBranchGetReplaceKeys,
            notFlagEntities: (args["notFlagEntities"] === undefined) ? [] : args["notFlagEntities"]
        };
    },
    addPersonAndOpinionExec: function(args){
        if (args.personKey === undefined){ throw "ECS fail " + args.key + " missing personKey"; }
        if (args.opinionKey === undefined){ throw "ECS fail " + args.key + " missing opinionKey"; }
        return {
            opinionKey: args['opinionKey'],
            execute: addPersonAndOpinionExec,
            personKey: args['personKey']
        };
    },
    randomReplace: function(args){
        return {
            replaceMarker: ecs.randomReplaceMarker(args)
        };
    },
    featureItemActionExec: function(args){
        if (args.execCurrencyKey === undefined){ throw "ECS fail " + args.key + " missing execCurrencyKey"; }
        if (args.execCurrencyChange === undefined){ throw "ECS fail " + args.key + " missing execCurrencyChange"; }
        return {
            execCurrencyChange: args['execCurrencyChange'],
            execute: featureItemActionExec,
            execCurrencyKey: args['execCurrencyKey']
        };
    },
    songSatisfactionDeposit: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.songSatisfactionDeposit)));
        return ecs.deposit(args);
    },
    quietDemonsLoveGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.quietDemonsLoveGoalConstruction)));
        return ecs.construction(args);
    },
    loverFleshButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.loverFleshButton)));
        return ecs.towerButton(args);
    },
    insulting: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.insulting)));
        return ecs.setFlag(args);
    },
    captureSocialGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.captureSocialGoal)));
        return ecs.worldGoal(args);
    },
    flipComponent: function(args){
        if (args.flipEntity === undefined){ throw "ECS fail " + args.key + " missing flipEntity"; }
        return {
            flipEntity: args['flipEntity']
        };
    },
    personHealAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.personHealAction)));
        args["personHeld"] = args.source.personHeldComponent.personHeld;
        return {
            healthComponent: ecs.healthComponent(args),
            personHeldComponent: ecs.personHeldComponent(args),
            createExecutables: [ecs.personRelationshipExec(args)]
        };
    },
    discardMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.discardMenu)));
        return ecs.goalCompleteMenu(args);
    },
    capturedInspiration: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.capturedInspiration)));
        return ecs.entityReplace(args);
    },
    drainedSad: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.drainedSad)));
        return ecs.emotionSource(args);
    },
    museFishMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.museFishMenu)));
        return ecs.goalCompleteMenu(args);
    },
    socialMine: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.socialMine)));
        return ecs.resource(args);
    },
    shareJoyButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.shareJoyButton)));
        return ecs.towerButton(args);
    },
    museBaseTraitConnectSelfCore: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.museBaseTraitConnectSelfCore)));
        return ecs.traitConnect(args);
    },
    impressButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.impressButton)));
        return ecs.goalButton(args);
    },
    startNewSongGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.startNewSongGoal)));
        return ecs.worldGoal(args);
    },
    quietDemons_ext: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.quietDemons_ext)));
        return ecs.sourceExtension(args);
    },
    flashback_anger: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.flashback_anger)));
        return ecs.emotionResource(args);
    },
    condLoverJoy: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.condLoverJoy)));
        return ecs.conditionalMake(args);
    },
    apology_lover: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.apology_lover)));
        return ecs.currency(args);
    },
    struggleComplimentsGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.struggleComplimentsGoal)));
        return ecs.worldGoal(args);
    },
    suspicionMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.suspicionMenu)));
        return ecs.goalCompleteMenu(args);
    },
    deadInsideButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.deadInsideButton)));
        return ecs.memoryTowerButton(args);
    },
    captureInspirationGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.captureInspirationGoal)));
        return ecs.worldGoal(args);
    },
    deriveHumanInsightGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.deriveHumanInsightGoal)));
        return ecs.worldGoal(args);
    },
    transformExec: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            createExecutables: [ecs.transformTrait(args), ecs.killEntity(args)]
        };
    },
    deriveCoolThingGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.deriveCoolThingGoalConstruction)));
        return ecs.construction(args);
    },
    refineInspirationGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.refineInspirationGoal)));
        return ecs.worldGoal(args);
    },
    acceptAlienationButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.acceptAlienationButton)));
        return ecs.goalButton(args);
    },
    resolveDeposit: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.resolveDeposit)));
        return ecs.deposit(args);
    },
    demon2Menu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.demon2Menu)));
        return ecs.goalCompleteMenu(args);
    },
    checkWorldGoalComponent: function(args){
        return {
            successExecutables: [ecs.goalCompleteExec(args)].concat([ecs.trackActionables(args), ecs.makeEntityComponent(args), ecs.makeGoalMenuExecutable(args)]),
            checkCompleted: checkWorldGoalComponent
        };
    },
    entityKill: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            createExecutables: [ecs.killEntityComponent(args), ecs.killEntity(args)]
        };
    },
    relaxMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.relaxMenu)));
        return ecs.goalCompleteMenu(args);
    },
    beCaredGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.beCaredGoal)));
        return ecs.worldGoal(args);
    },
    cursor: function(args){
        return {
            rendererComponents: [ecs.cursorRenderComponent(args)]
        };
    },
    bandCheck: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.bandCheck)));
        return ecs.checkQuality(args);
    },
    museVentTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.museVentTower)));
        return ecs.tower(args);
    },
    workThroughGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.workThroughGoalConstruction)));
        return ecs.memoryConstruction(args);
    },
    destinyGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.destinyGoalConstruction)));
        return ecs.construction(args);
    },
    showMusic_ext: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.showMusic_ext)));
        return ecs.sourceExtension(args);
    },
    hateWork: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.hateWork)));
        args["touchable"] = true;
        args["tendPosition"] = [0, 0];
        args["health"] = (args["health"] === undefined) ? 2000 : args["health"];
        args["panelKey"] = (args["panelKey"] === undefined) ? PARAMETERS.traitPanel : args["panelKey"];
        return {
            killExecutables: [ecs.killExtensions(args)],
            isaacEntityComponent: ecs.isaacEntityComponent(args),
            uniqueComponent: ecs.uniqueComponent(args),
            rendererComponents: [ecs.builtSourceRendererComponent(args)],
            uiActionComponent: ecs.buildingUiActionComponent(args),
            focusTickResponseComponent: (args.aliveEntities) ? ecs.focusTickAliveMakerComponent(args) : undefined,
            mapSpawnComponent: ecs.builtSourceMapSpawnComponent(args),
            experienceComponent: ecs.experienceComponent(args),
            personHeldComponent: (args.personHeld) ? ecs.personHeldComponent(args) : undefined,
            createExecutables: [ecs.builtSourceCreateExec(args), ecs.makeExtraPathsExec(args)],
            junctionComponent: ecs.junctionComponent(args),
            clickComponent: ecs.fullMenuClickComponent(args),
            uiDataComponent: ecs.uiDataComponent(args),
            entityManagerComponent: ecs.entityManagerComponent(args),
            descriptionComponent: ecs.descriptionComponent(args),
            healthComponent: ecs.healthComponent(args),
            interactionComponent: ecs.interactionComponent(args),
            identityComponent: ecs.identityComponent(args),
            positionComponent: ecs.positionComponent(args),
            menuCloseComponent: ecs.traitMenuCloseComponent(args),
            playerMadeComponent: ecs.fixedPlayerMadeComponent(args)
        };
    },
    museFishGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.museFishGoal)));
        return ecs.worldGoal(args);
    },
    killIgnoreIssuesGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.killIgnoreIssuesGoal)));
        return ecs.entityKill(args);
    },
    reflectAloneMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.reflectAloneMenu)));
        return ecs.goalCompleteMenu(args);
    },
    obnoxiousGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.obnoxiousGoal)));
        return ecs.worldGoal(args);
    },
    makingWork_ext: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.makingWork_ext)));
        return ecs.sourceExtension(args);
    },
    endAlienationStrengthButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.endAlienationStrengthButton)));
        return ecs.goalButton(args);
    },
    apologizeResult: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.apologizeResult)));
        return ecs.randomReplace(args);
    },
    experienceTriggerComponent: function(args){
        if (args.traitKey === undefined){ throw "ECS fail " + args.key + " missing traitKey"; }
        if (args.xpEntities === undefined){ throw "ECS fail " + args.key + " missing xpEntities"; }
        return {
            traitKey: args['traitKey'],
            xpEntities: args['xpEntities']
        };
    },
    romance: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.romance)));
        return ecs.trait(args);
    },
    steamPlay: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.steamPlay)));
        return ecs.steamAchievement(args);
    },
    fadeRealTalk: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.fadeRealTalk)));
        return ecs.timedCreateEntities(args);
    },
    mistakeDesposit: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.mistakeDesposit)));
        return ecs.deposit(args);
    },
    builtSourceRendererComponent: function(args){
        if (args.image === undefined){ throw "ECS fail " + args.key + " missing image"; }
        return {
            outlineImage: "activeOutline",
            render: builtSourceRender,
            wobble: 0,
            image: args['image'],
            priority: 5,
            timeToBlink: 1,
            newImage: "newOutline",
            getWobble: getWobble,
            renderState: "default",
            disabledImage: "trait-aaaaaa"
        };
    },
    captureThoughtGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.captureThoughtGoal)));
        return ecs.worldGoal(args);
    },
    gainControlMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.gainControlMenu)));
        args["size"] = [550, 350];
        return {
            healthComponent: ecs.singleHealthComponent(args),
            absolutePositionComponent: ecs.absolutePositionComponent(args),
            rendererComponents: [ecs.gainControlMenuRender(args)],
            killExecutables: [ecs.killLostControlExec(args)]
        };
    },
    worldGoalNotificationComponent: function(args){
        return {
            notificationFunctions: simpleDict(PARAMETERS.openMenu, worldGoalMenuOpened),
            serializeComponent: ecs.notificationSerializeComponent(args)
        };
    },
    shareJoyMuseTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.shareJoyMuseTowerConstruction)));
        return ecs.construction(args);
    },
    accomplishmentDeposit: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.accomplishmentDeposit)));
        return ecs.deposit(args);
    },
    thought: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.thought)));
        return ecs.currency(args);
    },
    joyMemoryFade: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.joyMemoryFade)));
        return ecs.memoryFade(args);
    },
    failureButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.failureButton)));
        return ecs.towerButton(args);
    },
    cheerFail: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.cheerFail)));
        return ecs.entityReplace(args);
    },
    strengthMineButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.strengthMineButton)));
        return ecs.resourceButton(args);
    },
    restartWorkMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.restartWorkMenu)));
        return ecs.goalCompleteMenu(args);
    },
    traitPanelRenderComponent: function(args){
        return {
            insetColor: "#000000",
            fullAnimationTime: 0.1,
            yTextOffset: 5,
            backImage: "uiBack",
            drawImageButton: drawImageButton,
            animatingTime: 0.1,
            priority: 18,
            render: traitPanelRender,
            drawPanelButton: drawPanelButton,
            snapPanelPos: snapPanelPos,
            currentCategory: undefined,
            setCategoryFn: {},
            renderTime: 0,
            baseSize: args['size'],
            textColor: "#000000",
            renderState: "default",
            specificRender: args['specificRender'],
            drawHelpButton: drawHelpButton
        };
    },
    personResourceButton: function(args){
        args["subPanel"] = (args["subPanel"] === undefined) ? "Towers" : args["subPanel"];
        args["multiTrait"] = (args["multiTrait"] === undefined) ? false : args["multiTrait"];
        return {
            interactionComponent: ecs.interactionComponent(args),
            uiActionComponent: ecs.buttonUiActionComponent(args),
            buttonControlComponent: ecs.towerControlComponent(args),
            personHeldComponent: ecs.personHeldComponent(args),
            createExecutables: [ecs.markTraitNew(args)],
            timedDeathComponent: (args.timeToDeath) ? ecs.timedDeathComponent(args) : undefined,
            uiDataComponent: ecs.uiDataComponent(args),
            uniqueComponent: (args.timeToDeath === undefined && !args.multiTrait) ? ecs.uniqueComponent(args) : undefined,
            descriptionComponent: ecs.descriptionComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            killExecutables: [ecs.killTowerButtonExec(args)],
            identityComponent: ecs.identityComponent(args)
        };
    },
    joyFleshAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.joyFleshAction)));
        return ecs.currencyAction(args);
    },
    emotionSourceConstructionCreateExec: function(args){
        if (args.sourceKey === undefined){ throw "ECS fail " + args.key + " missing sourceKey"; }
        return {
            focusStopKey: "impulse",
            execute: emotionSourceConstructionCreateExec,
            trainPath: PARAMETERS.singleUseTrainPath,
            sourceKey: args['sourceKey']
        };
    },
    cynicismTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.cynicismTower)));
        return ecs.tower(args);
    },
    tryComplimentsButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.tryComplimentsButton)));
        return ecs.goalButton(args);
    },
    gameEndComponent: function(args){
        return {
            gameEnded: true
        };
    },
    reflectSuccessMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.reflectSuccessMenu)));
        return ecs.goalCompleteMenu(args);
    },
    painMine: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.painMine)));
        return ecs.memoryResource(args);
    },
    memory_selfWorthless: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.memory_selfWorthless)));
        return ecs.memory(args);
    },
    pathMoveComponent: function(args){
        return {
            startPosition: args['start'],
            ticks: 1,
            endPosition: args['end']
        };
    },
    helpInfoPanelRenderComponent: function(args){
        if (args.helpLines === undefined){ throw "ECS fail " + args.key + " missing helpLines"; }
        return {
            drawPanelButton: drawPanelButton,
            insetColor: "#000000",
            fullAnimationTime: 0.1,
            yTextOffset: 5,
            backImage: "uiBack",
            drawImageButton: drawImageButton,
            animatingTime: 0.1,
            priority: 24,
            render: outPanelRender,
            helpLines: args['helpLines'],
            snapPanelPos: snapPanelPos,
            renderTime: 0,
            textColor: "#000000",
            renderState: "default",
            specificRender: helpInfoPanelRender,
            drawHelpButton: drawHelpButton
        };
    },
    selfEsteemDeposit: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.selfEsteemDeposit)));
        return ecs.deposit(args);
    },
    keepTalkGoingMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.keepTalkGoingMenu)));
        return ecs.goalCompleteMenu(args);
    },
    resourceButton: function(args){
        args["subPanel"] = (args["subPanel"] === undefined) ? "Towers" : args["subPanel"];
        args["multiTrait"] = (args["multiTrait"] === undefined) ? false : args["multiTrait"];
        return {
            interactionComponent: ecs.interactionComponent(args),
            uiActionComponent: ecs.buttonUiActionComponent(args),
            buttonControlComponent: ecs.towerControlComponent(args),
            createExecutables: [ecs.markTraitNew(args)],
            timedDeathComponent: (args.timeToDeath) ? ecs.timedDeathComponent(args) : undefined,
            uiDataComponent: ecs.uiDataComponent(args),
            uniqueComponent: (args.timeToDeath === undefined && !args.multiTrait) ? ecs.uniqueComponent(args) : undefined,
            descriptionComponent: ecs.descriptionComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            killExecutables: [ecs.killTowerButtonExec(args)],
            identityComponent: ecs.identityComponent(args)
        };
    },
    cheerMineConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.cheerMineConstruction)));
        return ecs.memoryConstruction(args);
    },
    memoryFade: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            timedDeathComponent: ecs.timedDeathComponent(args),
            memoryFadeComponent: ecs.memoryFadeComponent(args),
            killExecutables: [ecs.memoryFadeExec(args)]
        };
    },
    reflectMine: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.reflectMine)));
        return ecs.resource(args);
    },
    starParticleEffect: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            lifeSentinelComponent: ecs.sourceLifeSentinelComponent(args),
            rendererComponents: [ecs.starParticleEffectRenderComponent(args)]
        };
    },
    rememberButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.rememberButton)));
        return ecs.goalButton(args);
    },
    suppressDemonsGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.suppressDemonsGoal)));
        return ecs.worldGoal(args);
    },
    success: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.success)));
        return ecs.currency(args);
    },
    replaceMarker: function(args){
        if (args.getReplaceKeys === undefined){ throw "ECS fail " + args.key + " missing getReplaceKeys"; }
        return {
            getReplaceKeys: args['getReplaceKeys']
        };
    },
    keepGoingGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.keepGoingGoal)));
        return ecs.worldGoal(args);
    },
    demolishButtonRenderComponent: function(args){
        return {
            priority: 2,
            renderState: "default",
            render: demolishButtonRender,
            demolishButtonFunction: undefined
        };
    },
    listenDemonsButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.listenDemonsButton)));
        return ecs.goalButton(args);
    },
    qualityResource: function(args){
        args["touchable"] = true;
        args["canExpandInto"] = false;
        args["quality"] = (args["quality"] === undefined) ? Math.floor(Math.random() * 3) + 2 : args["quality"];
        return {
            focusPathStopComponent: ecs.resourceFocusPathStopComponent(args),
            rendererComponents: [ecs.pathStopRenderComponent(args)].concat([ecs.qualityRenderComponent(args)]),
            qualityComponent: ecs.qualityComponent(args),
            focusTickResponseComponent: ecs.resourceFocusTickResponseComponent(args),
            uiDataComponent: ecs.uiDataComponent(args),
            managedComponent: ecs.managedComponent(args),
            createExecutables: [ecs.makeExtraPathsExec(args)],
            resourceComponent: ecs.resourceComponent(args),
            junctionComponent: ecs.junctionComponent(args),
            clickComponent: ecs.fullMenuClickComponent(args),
            killExecutables: [ecs.killResourceCurrencies(args)],
            tickComponent: ecs.qualityResourceTickComponent(args),
            descriptionComponent: ecs.descriptionComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            interactionComponent: ecs.interactionComponent(args),
            identityComponent: ecs.identityComponent(args),
            positionComponent: ecs.positionComponent(args),
            playerMadeComponent: ecs.playerMadeComponent(args)
        };
    },
    stageChangeComponent: function(args){
        if (args.goalsToCheck === undefined){ throw "ECS fail " + args.key + " missing goalsToCheck"; }
        return {
            executables: [ecs.makeEntityComponent(args), ecs.killEntityComponent(args)],
            shouldChange: false,
            checkAndChange: checkAndChangeGameState,
            goalsToCheck: args['goalsToCheck']
        };
    },
    goalCompleteMenu: function(args){
        args["y"] = 5;
        args["x"] = width / 2 - 325;
        args["size"] = [650, 600];
        return {
            rendererComponents: [ecs.goalCompleteMenuRender(args)],
            absolutePositionComponent: ecs.absolutePositionComponent(args),
            uiClickComponent: ecs.panelUiClickComponent(args),
            createExecutables: [ecs.goalCompleteMenuCreateExec(args)],
            goalCompleteMenuComponent: ecs.goalCompleteMenuComponent(args),
            panelComponent: ecs.goalCompleteMenuPanelComponent(args),
            pauseComponent: ecs.pauseComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            rebuildMenuComponent: ecs.rebuildMenuComponent(args),
            identityComponent: ecs.identityComponent(args)
        };
    },
    talkTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.talkTower)));
        args["touchable"] = true;
        args["canExpandInto"] = false;
        args["isPathEnd"] = (args["isPathEnd"] === undefined) ? (args.damageMultipliers ? true : false) : args["isPathEnd"];
        args["tags"] = (args["tags"] === undefined) ? [args.key] : args["tags"];
        return {
            junctionComponent: ecs.junctionComponent(args),
            rendererComponents: [ecs.pathStopRenderComponent(args), ecs.damageRenderComponent(args)],
            focusTickResponseComponent: ecs.towerFocusTickResponseComponent(args),
            managedComponent: ecs.managedComponent(args),
            absolutePositionComponent: ecs.absolutePositionComponent(args),
            damageComponent: ecs.talkTowerDamageComponent(args),
            healthModifyComponent: ecs.healthModifyComponent(args),
            createExecutables: [ecs.makeExtraPathsExec(args)].concat([ecs.towerCreateExec(args)]),
            focusPathStopComponent: ecs.towerFocusPathStopComponent(args),
            clickComponent: ecs.fullMenuClickComponent(args),
            uiDataComponent: ecs.uiDataComponent(args),
            tickComponent: ecs.towerTickComponent(args),
            towerComponent: ecs.towerComponent(args),
            descriptionComponent: ecs.descriptionComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            interactionComponent: ecs.interactionComponent(args),
            identityComponent: ecs.identityComponent(args),
            positionComponent: ecs.positionComponent(args),
            playerMadeComponent: ecs.playerMadeComponent(args)
        };
    },
    disableMemories: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.disableMemories)));
        args["name"] = "You're just too overwhelmed to feel this right now.";
        return {
            disableTraitComponent: ecs.disableMemoriesComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            identityComponent: ecs.identityComponent(args),
            tickComponent: ecs.disableMemoriesTickComponent(args),
            timedDeathComponent: (args.timeToDeath) ? ecs.timedDeathComponent(args) : undefined
        };
    },
    killEntity: function(args){
        return {
            execute: killEntity
        };
    },
    easyProblem: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.easyProblem)));
        return ecs.currency(args);
    },
    optimismButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.optimismButton)));
        return ecs.memoryTowerButton(args);
    },
    rageTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.rageTowerConstruction)));
        return ecs.memoryConstruction(args);
    },
    menuTutorialText: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            tutorialTextComponent: ecs.menuTutorialTextComponent(args),
            absolutePositionComponent: ecs.absolutePositionComponent(args),
            rendererComponents: [ecs.menuTutorialTextRenderComponent(args)]
        };
    },
    damageRenderComponent: function(args){
        if (args.image === undefined){ throw "ECS fail " + args.key + " missing image"; }
        return {
            priority: 7,
            image: args['image'],
            renderState: "default",
            render: damageRender,
            blinkTimer: 0
        };
    },
    loverFleshTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.loverFleshTowerConstruction)));
        return ecs.construction(args);
    },
    deriveStrengthButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.deriveStrengthButton)));
        return ecs.goalButton(args);
    },
    prideStartGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.prideStartGoalConstruction)));
        return ecs.construction(args);
    },
    refineInspirationGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.refineInspirationGoalConstruction)));
        return ecs.construction(args);
    },
    emotionTickModifyComponent: function(args){
        if (args.towerTrait === undefined){ throw "ECS fail " + args.key + " missing towerTrait"; }
        if (args.emotionState === undefined){ throw "ECS fail " + args.key + " missing emotionState"; }
        if (args.factor === undefined){ throw "ECS fail " + args.key + " missing factor"; }
        return {
            towerTrait: args['towerTrait'],
            emotionState: args['emotionState'],
            factor: args['factor']
        };
    },
    makeRandomInspiration: function(args){
        return {
            replaceMarker: ecs.makeRandomInspirationReplaceMarker(args)
        };
    },
    ignoreInsult1Goal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.ignoreInsult1Goal)));
        return ecs.worldGoal(args);
    },
    holdControlComponent: function(args){
        return {
            holdControl: true
        };
    },
    struggleComplimentsButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.struggleComplimentsButton)));
        return ecs.goalButton(args);
    },
    refineAccomplishmentAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.refineAccomplishmentAction)));
        return ecs.currencyAction(args);
    },
    dependence: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.dependence)));
        return ecs.currency(args);
    },
    nextDayEntityDayResponseComponent: function(args){
        return {
            dayChange: (args["dayChange"] === undefined) ? undefined : args["dayChange"],
            dayEnd: nextDayEntityDayEnd,
            dayStart: (args["dayStart"] === undefined) ? undefined : args["dayStart"],
            execs: [ecs.makeEntityComponent(args), ecs.killEntityComponent(args)]
        };
    },
    fleshSongGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.fleshSongGoal)));
        return ecs.worldGoal(args);
    },
    emoProblem: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.emoProblem)));
        return ecs.currency(args);
    },
    resourceModifier: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.resourceModifier)));
        return {
            healthComponent: ecs.singleHealthComponent(args),
            resourceModifierComponent: ecs.resourceModifierComponent(args),
            rendererComponents: [ecs.resourceModifierRenderComponent(args)],
            dayResponseComponent: ecs.killDayEndComponent(args),
            timedDeathComponent: ecs.timedDeathComponent(args)
        };
    },
    ambitionToSelfReliance: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.ambitionToSelfReliance)));
        return ecs.transformExec(args);
    },
    pushYourselfMineButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.pushYourselfMineButton)));
        return ecs.resourceButton(args);
    },
    needLoverButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.needLoverButton)));
        return ecs.towerButton(args);
    },
    suspicionResult: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.suspicionResult)));
        return ecs.randomReplace(args);
    },
    playWithMusicTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.playWithMusicTowerConstruction)));
        return ecs.construction(args);
    },
    trainPanelRenderComponent: function(args){
        return {
            arrowImage: "trainPathArrow",
            insetColor: "#000000",
            fullAnimationTime: 0.1,
            yTextOffset: 5,
            backImage: "uiBack",
            isConnectingTrack: false,
            drawImageButton: drawImageButton,
            animatingTime: 0.1,
            priority: 18,
            render: outPanelRender,
            drawPanelButton: drawPanelButton,
            snapPanelPos: snapPanelPos,
            renderTime: 0,
            textColor: "#000000",
            renderState: "default",
            specificRender: trainPanelRender,
            drawHelpButton: drawHelpButton
        };
    },
    talkAboutLoverButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.talkAboutLoverButton)));
        return ecs.goalButton(args);
    },
    dayCounterComponent: function(args){
        args["ticks"] = PARAMETERS.dayTicks;
        if (args.ticks === undefined){ throw "ECS fail " + args.key + " missing ticks"; }
        return {
            dayCount: (args["dayCount"] === undefined) ? 1 : args["dayCount"],
            ticks: args['ticks'],
            getPartOfDay: getPartOfDay,
            impulseTicks: args['ticks']
        };
    },
    killMuse: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.killMuse)));
        return ecs.entityKill(args);
    },
    frustration: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.frustration)));
        return ecs.currency(args);
    },
    focusCostComponent: function(args){
        return {
            focusCost: (args["focusCost"] === undefined) ? 1 : args["focusCost"]
        };
    },
    sadMemoryFade: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.sadMemoryFade)));
        return ecs.memoryFade(args);
    },
    deriveInsightMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.deriveInsightMenu)));
        return ecs.goalCompleteMenu(args);
    },
    tryOldJokesGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.tryOldJokesGoal)));
        return ecs.worldGoal(args);
    },
    paramPathExec: function(args){
        if (args.startKey === undefined){ throw "ECS fail " + args.key + " missing startKey"; }
        if (args.endKey === undefined){ throw "ECS fail " + args.key + " missing endKey"; }
        return {
            startKey: args['startKey'],
            execute: paramPathExec,
            trainPathKey: PARAMETERS.trainPath,
            endKey: args['endKey']
        };
    },
    quietDemonsLoveGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.quietDemonsLoveGoal)));
        return ecs.worldGoal(args);
    },
    construction: function(args){
        args["touchable"] = true;
        args["canExpandInto"] = false;
        return {
            junctionComponent: ecs.junctionComponent(args),
            rendererComponents: [ecs.constructionRenderComponent(args), ecs.progressRenderComponent(args)],
            personHeldComponent: ecs.personHeldComponent(args),
            managedComponent: ecs.managedComponent(args),
            constructionComponent: ecs.constructionComponent(args),
            createExecutables: [ecs.makeExtraPathsExec(args)].concat([ecs.openMenu(args)]),
            focusPathStopComponent: ecs.constructionFocusPathStopComponent(args),
            clickComponent: ecs.fullMenuClickComponent(args),
            uiDataComponent: ecs.uiDataComponent(args),
            tickComponent: ecs.constructionTickComponent(args),
            descriptionComponent: ecs.descriptionComponent(args),
            healthComponent: ecs.singleHealthComponent(args),
            interactionComponent: ecs.interactionComponent(args),
            identityComponent: ecs.identityComponent(args),
            positionComponent: ecs.positionComponent(args),
            playerMadeComponent: ecs.playerMadeComponent(args)
        };
    },
    shareTowerConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.shareTowerConstruction)));
        return ecs.construction(args);
    },
    failMakeEntityComponent: function(args){
        args["failEntities"] = (args["failEntities"] === undefined) ? [] : args["failEntities"];
        return {
            execute: executeMakeEntities,
            entitiesToMake: args['failEntities'],
            adjacent: (args["adjacent"] === undefined) ? false : args["adjacent"]
        };
    },
    trainPathMenuCloseComponent: function(args){
        return {
            execute: trainPathMenuCloseExec
        };
    },
    deriveHumanInsightButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.deriveHumanInsightButton)));
        return ecs.goalButton(args);
    },
    carriage: function(args){
        return {
            healthComponent: ecs.singleHealthComponent(args),
            positionComponent: ecs.positionComponent(args),
            carriageComponent: ecs.carriageComponent(args)
        };
    },
    thoughtTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.thoughtTower)));
        return ecs.tower(args);
    },
    indifferenceAction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.indifferenceAction)));
        return ecs.currencyAction(args);
    },
    integrateMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.integrateMenu)));
        return ecs.goalCompleteMenu(args);
    },
    refineAccomplishmentTower: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.refineAccomplishmentTower)));
        return ecs.tower(args);
    },
    towerCreateExec: function(args){
        return {
            execute: towerCreateExec
        };
    },
    firstParty: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.firstParty)));
        return ecs.timedCreateEntities(args);
    },
    fullMenuClickComponent: function(args){
        if (args.image === undefined){ throw "ECS fail " + args.key + " missing image"; }
        return {
            image: args['image'],
            onClick: fullMenuOnClick,
            uiKey: (args["uiKey"] === undefined) ? getUiKey(args.key, args.uid) : args["uiKey"],
            panelKey: (args["panelKey"] === undefined) ? PARAMETERS.towerPanel : args["panelKey"]
        };
    },
    envyGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.envyGoalConstruction)));
        return ecs.construction(args);
    },
    emoProblemDeposit: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.emoProblemDeposit)));
        return ecs.deposit(args);
    },
    loveMineButton: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.loveMineButton)));
        return ecs.resourceButton(args);
    },
    worker: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.worker)));
        args["tags"] = (args.tags) ? args.tags.concat([args.key]) : [args.key];
        return {
            pathVelocityComponent: ecs.pathVelocityComponent(args),
            rendererComponents: [ecs.workerRenderComponent(args)],
            healthComponent: ecs.singleHealthComponent(args),
            identityComponent: ecs.identityComponent(args),
            positionComponent: ecs.positionComponent(args),
            dayResponseComponent: (args.checkDayEnd) ? ecs.currencyDayResponseComponent(args) : undefined
        };
    },
    fishComplimentsMenu: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.fishComplimentsMenu)));
        return ecs.goalCompleteMenu(args);
    },
    thinkSongBadGoalConstruction: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.thinkSongBadGoalConstruction)));
        return ecs.construction(args);
    },
    minorFlirtGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.minorFlirtGoal)));
        return ecs.worldGoal(args);
    },
    majorIssueGoal: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.majorIssueGoal)));
        return ecs.worldGoal(args);
    },
    museBaseTraitConnectMusic: function(args){
        updateArgs(args, JSON.parse(JSON.stringify(allArgs.museBaseTraitConnectMusic)));
        return ecs.traitConnect(args);
    }
};


var setAbsPosFromRect = function(rect){
    this.x = rect[0];
    this.y = rect[1];
    this.size[0] = rect[2];
    this.size[1] = rect[3];
};

var getAbsPosRect = function(){
    return [this.x, this.y, this.size[0], this.size[1]];
};


var activateRender = function(rect){
    if (this.activated){
        ctx.drawImage(getImage('activeHex'), rect[0], rect[1]);
        this.activated = false;
    }
}


var addPersonAndOpinionExec = function(entity, world){
    var person = world.entityForKey(this.personKey);
    if (!person){
        person = makeEcs(this.personKey);
        var pos = world.randomOpenPosition(person.positionComponent.planeOfExistence);
        person.positionComponent.x = pos[0];
        person.positionComponent.y = pos[1];
        world.addEntity(person);
    }

    person.personalityComponent.addOpinionToPersonality(this.opinionKey, person, world);
    entity.healthComponent.health = 0;
};


var aliveMakeFocusTick = function(entity, world){
    world.makeEntities(entity, this.aliveEntities);
};


var allBuildingPanelRender = function(rect, entity, ui){
    var buttonRect;
    if (!this.pageUpFunction){
        this.pageUpFunction = () => {
            this.page -= 1;
            this.page = max(this.page, 1);
            this.currentCategory = undefined;
        };

        this.pageDownFunction = () => {
            this.page += 1;
            this.currentCategory = undefined;
        };
    }

    var panelButtons = [];
    var world = ui.world;

    var buttons = _.chain(world.entities)
        .filter(function(worldEntity){
            return worldEntity.buttonControlComponent;
        })
        .groupBy(function(worldEntity){
            return worldEntity.buttonControlComponent.managerKey;
        })
        .value();

    var maxPage = Object.keys(buttons).length;
    this.page = min(this.page, maxPage);

    var lineCount = _.reduce(buttons, function(memo, val, key){
        return memo + val.length + 1;
    }, 0);
    
    entity.absolutePositionComponent.size[1] = 650;
    rect = entity.absolutePositionComponent.getAbsPosRect();

    ctx.drawImage(getImage(this.backImage), rect[0], rect[1], rect[2], rect[3]);

    ctx.fillStyle = this.textColor;
    ctx.font = getFont(28);

    var title = 'Buildings';
    var titleWidth = ctx.measureText(title).width;

    ctx.fillText(title, rect[0] + (rect[2] - titleWidth) / 2, rect[1] + 60);

    var y = rect[1] + 150;
    ctx.font = getFont(18);
    ctx.strokeStyle = this.insetColor;

    var buttonsOnPage = 0;
    var traitPage = 0;

    _.each(buttons, (btns, trait) => {
        traitPage += 1;
        
        if (traitPage != this.page){
            return;
        }

        var traitImage = getImage(allArgs[trait].image);
        ctx.drawImage(traitImage, rect[0] + 10, y - 38);

        ctx.font = getFont(24);
        ctx.fillText(sanitize(trait), rect[0] + 80, y);
        ctx.font = getFont(18);
        y += 50;

        var traitEntity = world.entityForKey(trait);

        if (traitEntity.entityManagerComponent.disabled){
            var disablingEntity = _.chain(world.entities)
                .filter(worldEntity => (worldEntity.disableTraitComponent && 
                                        worldEntity.disableTraitComponent.checkTraitDisabled(traitEntity)))
                .first()
                .value();
            ctx.fillText('Disabled: ' + disablingEntity.identityComponent.name, rect[0] + 30, y);
            y += 50;
            return;
        }
        
        var categories = _.chain(btns)
            .map(function(button){ return button.uiDataComponent.subPanel; })
            .uniq()
            .sortBy(_.identity)
            .reverse()
            .value();

        if (this.currentCategory === undefined){
            this.currentCategory = categories[0];
        }

        _.each(categories, (panelKey, i) => {
            var x = rect[0] + 10 + 130 * i;
            if (panelKey == this.currentCategory){
                ctx.fillText(panelKey, x, y - 10);
                ctx.fillRect(x, y - 5, 70, 2);
            }
            else{
                var panelRect = [x, y - 30, 100, 30];
                this.drawPanelButton(panelRect, panelKey);

                if (!this.setCategoryFn[panelKey]){
                    this.setCategoryFn[panelKey] = () => {
                        this.currentCategory = panelKey;
                    };
                }
                var setCategoryFn = this.setCategoryFn[panelKey];
                panelButtons.push([panelRect, setCategoryFn]);
            }
        });

        btns = _.filter(btns, (button) => {
            return button.uiDataComponent.subPanel == this.currentCategory;
        });

        ctxRoundedRect([rect[0] + 5, y + 5, rect[2] - 10, 60 * btns.length + 20]);

        y += 50;

        _.each(btns, (button) => {
            ctx.font = getFont((button.identityComponent.name.length > 17) ? 14 : 18);

            ctx.drawImage(traitImage, rect[0] + 20, y - 38);
            ctx.drawImage(getImage(button.uiDataComponent.image), rect[0] + 20, y - 38);
            
            ctx.fillText(button.identityComponent.name, rect[0] + 80, y);

            ctx.font = '12px OpenSansItalic';
            if (_.isArray(button.descriptionComponent.description)){
                _.each(button.descriptionComponent.description, (desc, i) => {
                    ctx.fillText(desc, rect[0] + 260, y - 7 + 15 * i);
                });
            }
            else{
                ctx.fillText(button.descriptionComponent.description, rect[0] + 260, y - 2);
            }
            ctx.font = getFont(20);

            var x = rect[0] + rect[2] - 180;

            if (!_.contains(traitEntity.interactionComponent.connectionKeys, PARAMETERS.self)){
                var start = world.entityForKey(PARAMETERS.self).positionComponent.getAsPos();
                var end = traitEntity.positionComponent.getAsPos();
                if (determinePath(start, end, world.getAllPaths())){
                    traitEntity.interactionComponent.connectionKeys.push(PARAMETERS.self);
                }
            }

            if (button.interactionComponent.canInteractSelf() && _.contains(traitEntity.interactionComponent.connectionKeys, PARAMETERS.self)){
                var uiActions = button.uiActionComponent.getUiActions(button, ui, entity);

                _.each(uiActions, (fn, key) => {
                    buttonRect = [x, y - 30, 80, 35];
                    this.drawPanelButton(buttonRect, key);
                    panelButtons.push([buttonRect, fn]);
                    
                    x += 90;
                });

                if (!uiActions['Build']){
                    ctx.font = getFont(14);
                    ctx.fillText('Limit', x, y - 15);
                    ctx.fillText('Reached', x, y);
                    ctx.font = getFont(20);
                }
            }
            else{
                ctx.fillText('No Control', x, y - 10);
            }
                
            y += 60;
        });
    });

    ctx.font = BASE_FONT;

    y = rect[1] + rect[3] - 100;

    if (lineCount > 6){
        if (this.page > 1){
            buttonRect = [rect[0] + 10, y, 100, 40];
            this.drawPanelButton(buttonRect, 'Page Up');
            panelButtons.push([buttonRect, this.pageUpFunction]);
        }

        if (this.page < maxPage){
            buttonRect = [rect[0] + rect[2] - 110, y, 100, 40];
            this.drawPanelButton(buttonRect, 'Page Down');
            panelButtons.push([buttonRect, this.pageDownFunction]);
        }
    }

    return panelButtons;
};


var allGoalPanelRender = function(rect, entity, ui){
    var panelButtons = [];
    var world = ui.world;

    var goals = _.filter(world.entities, function(worldEntity){
        return worldEntity.goalTrackComponent;
    });

    entity.absolutePositionComponent.size[1] = 200 + goals.length * 70;
    rect = entity.absolutePositionComponent.getAbsPosRect();

    ctx.drawImage(getImage(this.backImage), rect[0], rect[1], rect[2], rect[3]);

    ctx.fillStyle = this.textColor;
    ctx.font = getFont(28);
    ctx.fillText('Goals', rect[0] + 60, rect[1] + 60);

    ctx.font = getFont(20);
    _.each(goals, (goal, i) => {
        var y = rect[1] + 100 + (i + 1) * 70;
        ctx.drawImage(getImage(goal.clickComponent.image), rect[0] + 10, y - 35);
        
        ctx.fillStyle = this.textColor;
        ctx.fillText(goal.identityComponent.name, rect[0] + 80, y);

        var x = rect[0] + rect[2] - 100
        _.each(goal.uiActionComponent.getUiActions(goal, ui, entity), (fn, key) => {
            var buttonRect = [x, y - 25, 85, 30];
            x -= 100;
            this.drawPanelButton(buttonRect, key);
            panelButtons.push([buttonRect, fn]);
        });

        ctx.strokeStyle = this.insetColor;
        ctxRoundedRect([rect[0] + 5, y - 40, rect[2] - 10, 60]);
    });

    return panelButtons;
};


var allTrainPanelRender = function(rect, entity, ui){
    var panelButtons = [];
    var world = ui.world;
    
    var trainPaths = _.filter(world.entities, function(worldEntity){
        return worldEntity.trainPathComponent && 
            worldEntity.trainPathComponent.stoppable &&
            worldEntity.interactionComponent.canInteractSelf();
    });

    rect = entity.absolutePositionComponent.getAbsPosRect();

    ctx.drawImage(getImage(this.backImage), rect[0], rect[1], rect[2], rect[3]);

    ctx.fillStyle = this.textColor;
    ctx.font = getFont(28);
    ctx.textAlign = 'center';
    ctx.fillText('Trains of Thought', rect[0] + rect[2] / 2, rect[1] + 60);
    ctx.textAlign = 'left';

    ctx.font = getFont(20);
    ctx.fillStyle = this.textColor;
    ctx.strokeStyle = '#000000';

    for (var i = 0; i < 5; i++){
        var path = trainPaths[i];
        var y = rect[1] + 100 + (i + 1) * 80;
        
        if (path){
            var lastStop = world.entityForUid(_.last(path.trainPathComponent.trainStops));
            if (lastStop){
                lastStop.uiDataComponent.drawImage(lastStop, world, [rect[0] + 10, y - 36]);

                ctx.fillText(lastStop.identityComponent.name, rect[0] + 75, y);

                var buttonRect = [rect[0] + rect[2] - 60, y - 25, 40, 30];
                this.drawPanelButton(buttonRect, 'X');
                var cancelFunction = path.trainPathComponent.getKillTrainPath(path, world);
                panelButtons.push([buttonRect, cancelFunction]);
            }
        }
        else{
            ctx.textAlign = 'center';
            ctx.fillText('IDLE', rect[0] + rect[2] / 2, y);
        }

        ctxRoundedRect([rect[0] + 10, y - 40, rect[2] - 20, 60])
    };
    ctx.textAlign = 'left';

    return panelButtons;
};


var assertUnique = function(entity){
    console.log('Tried to make a duplicate of ' + entity.identityComponent.key);
    return false;
};


var autoPathGetReplaceKeys = function(entity, world){
    world.entityDeath();
    var paths = _.filter(world.entities, worldEntity => worldEntity.pathMoveComponent);
    var traits = _.filter(world.entities, worldEntity => worldEntity.mapSpawnComponent);
    for (var i = 0; i < traits.length; i++){
        console.log(i);
        var start = [traits[i].positionComponent.x, traits[i].positionComponent.y];
        for (var j = i + 1; j < traits.length; j++){
            console.log(j);
            var end = [traits[j].positionComponent.x, traits[j].positionComponent.y];
            world.ensurePath(start, end, paths);
        }
        break;
    }
    return false;
};


var autosaveOnGoalComplete = function(entity, args){
    var saveKey = 'save_' + args.key;
    adminConsole.save(saveKey);
};


var autosaveTick = function(entity, world){
    this.ticksToAutosave -= 1;
    if (this.ticksToAutosave > 0){
        return;
    }
    
    var chapter = _.filter(world.entities, function(worldEntity){
        return worldEntity.stageChangeComponent;
    })[0];

    if (chapter.identityComponent.key !== this.lastChapterKey){
        this.lastChapterKey = chapter.identityComponent.key;
        adminConsole.save(this.lastChapterKey);
    }

    adminConsole.save(PARAMETERS.autosaveKey);
    this.ticksToAutosave += (world.entities.length > 500) ? 3 : 1;
};


var checkEntityForBoost = function(entity){
    var that = this
    return entity.boostSocketComponent &&
        _.all(this.traits, function(trait){
            return _.contains(entity.boostSocketComponent.boostTraits, trait);
        });
};


var boostRender = function(rect, entity){
    ctx.drawImage(getImage('towerHex'), rect[0], rect[1]);
    ctx.fillStyle = '#ffff00';
    ctx.fillText(entity.identityComponent.name, rect[0] + rect[2] / 3, rect[1] + 2 * rect[3] / 3);
}


var makeExtraPathsExec = function(entity, world){
    world.makeExtraPaths([entity.positionComponent.x, entity.positionComponent.y]);
};


var buildingConnectExec = function(entity, world){
    if (PARAMETERS.buildingConnect){
        var stop;

        if (entity.identityComponent.sourceId){
            stop = world.entityForUid(entity.identityComponent.sourceId);
        }
        
        if (!stop && this.startEntityKey){
            stop = world.entityForKey(this.startEntityKey);
        }

        if (!stop || !stop.positionComponent){
            var baseKey = PARAMETERS.baseConnectKey ? PARAMETERS.baseConnectKey : PARAMETERS.self;
            stop = world.entityForKey(baseKey);
        }
        
        world.ensurePath(entity.positionComponent.getAsPos(), stop.positionComponent.getAsPos());
    }
};


var entityGenerateUiActions = function(entity, ui, panel){
    return {
        Inspect: function(){
            entity.clickComponent.onClick(entity, ui.world, ui);
            panel.panelComponent.closed = true;
        }
    };
};


var buildPanelRender = function(rect, entity, ui){
    entity.panelComponent.panelButtons = [];
    ctx.drawImage(getImage(this.backImage), rect[0], rect[1], rect[2], rect[3]);

    var size = 40;
    var y = rect[1] + rect[3] / 2 - size / 2;
    
    if (PARAMETERS.buildablePaths){
        if (!this.pathButtonFunction){
            this.pathButtonFunction = function(){
                ui.world.togglePathBuilding();
            };
        }

        
        var pathRect = [rect[0] + 30, y, size, size];
        this.drawImageButton(pathRect, 'pathButton')
        entity.panelComponent.panelButtons.push([pathRect, this.pathButtonFunction]);
        
        if (!this.demolishPathButtonFunction){
            this.demolishPathButtonFunction = function(){
                if (!ui.world.destroyPathMode){
                    ui.world.resetCurrents();
                    ui.world.destroyPathMode = true;
                }
                else{
                    ui.world.resetCurrents();
                }
            };
        }

        var demolishPathRect = [rect[0] + 100, y, size, size];
        this.drawImageButton(demolishPathRect, 'noPath');
        entity.panelComponent.panelButtons.push([demolishPathRect, this.demolishPathButtonFunction]);
    }

    if (!this.demolishButtonFunction){
        this.demolishButtonFunction = function(){
            if (!ui.world.destructionMode){
                ui.world.resetCurrents();
                ui.world.destructionMode = true;
            }
            else{
                ui.world.resetCurrents();
            }
        };
    }
        
    var demolishRect = [rect[0] + rect[3] / 2 - size / 2 + 20, y, size, size];
    this.drawImageButton(demolishRect, 'bulldozer');
    entity.panelComponent.panelButtons.push([demolishRect, this.demolishButtonFunction]);

    if (!this.settingsFunction){
        this.settingsFunction = function(){
            ui.world.openSettingsPanel();
        };
    };

    this.font = getFont(10);
    var settingsRect = [rect[0] + rect[2] - 50, rect[1] + rect[3] - 20, 50, 20];
    this.drawPanelButton(settingsRect, 'Options');
    entity.panelComponent.panelButtons.push([settingsRect, this.settingsFunction]);
};


var bulletTick = function(entity){
    entity.healthComponent.health = 0;
};


var bulletRender = function(rect, entity, ui){
    var startRect = ui.world.hexMap.cellForPosComp(this.bulletSourcePos).rect;
    var endRect = ui.world.hexMap.cellForPosComp(this.bulletEndPos).rect;
    ctx.strokeStyle = '#ffffff';
    ctx.beginPath();
    ctx.moveTo(startRect[0] + startRect[2] / 2, startRect[1] + startRect[3] / 2);
    var endPos = [endRect[0] + endRect[2] / 2, endRect[1] + endRect[3] / 2];
    ctx.lineTo(endPos[0], endPos[1]);
    ctx.stroke();
};


var renderButton = function(text, buttonRect, buttonFunction){
    ctx.drawImage(getImage('button_l'), buttonRect[0] - 4, buttonRect[1] - 1);
    ctx.drawImage(getImage('button_c'), buttonRect[0], buttonRect[1] - 1, buttonRect[2], buttonRect[3] + 6);
    ctx.drawImage(getImage('button_r'), buttonRect[0] + buttonRect[2], buttonRect[1] - 1);
    
    ctx.fillStyle = '#000000';
    ctx.fillText(text, buttonRect[0], buttonRect[1] + this.lineHeight - 2);
    this.panelButtons.push([buttonRect, buttonFunction]);
};


var buttonGenerateUiActions = function(entity, ui, panel){
    var managerKey = entity.buttonControlComponent.managerKey;
    var manager = ui.world.entityForKey(managerKey);

    var actions = {};

    if (manager){
        if (this.inspectFunction === undefined){
            this.inspectFunction = function(){
                ui.minimizeAllPanels();
                manager.clickComponent.onClick(manager, ui.world, ui);
            };
        }
        actions['Inspect'] = this.inspectFunction;

        if (entity.buttonControlComponent.canMakeEntity(ui.world)){
            actions['Build'] = entity.buttonControlComponent.getWrappedFunction(entity, ui.world, ui);
        }
    }
    
    return actions;
};


var carryTrainTick = function(entity, world){
    var currenciesHeld = entity.trainComponent.getCurrenciesHeld(entity, world);
    if (currenciesHeld.length === 0){
        entity.healthComponent.kill();
    }
};


var changeOpinion = function(entity, world){
    if (this.opinionToRemove){
        var opinions = _.isArray(this.opinionToRemove) ? this.opinionToRemove : [this.opinionToRemove];
        _.chain(opinions)
            .map(function(opinionKey){ return world.entitiesForKey(opinionKey); })
            .filter(function(opinion){
                return opinion.personHeldComponent.personHeld == entity.personHeldComponent.personHeld
            })
            .each(function(opinion){
                opinion.healthComponent.health = 0;
            });
    }
    
    if (this.opinionToAdd){
        world.makeEntities(entity, this.opinionToAdd);
    }
};


var chapterPanelRender = function(rect, entity, ui){
    var panelButtons = [];
    var gameStage = ui.world.getGameStage();

    ctx.drawImage(getImage(this.backImage), rect[0], rect[1], rect[2], rect[3]);

    ctx.fillStyle = this.textColor;
    ctx.font = (gameStage.identityComponent.name.length > 12) ? getFont(22) : getFont(28);
    drawCenteredText(gameStage.identityComponent.name, rect[0] + rect[2] / 2, rect[1] + 40);
    
    var x = rect[0] + 10;
    var y = rect[1] + 100;
    
    ctx.font = getFont(20);
    ctx.strokeStyle = '#000000';

    if (gameStage.dayResponseComponent.daysLeft > 0 && 
        Object.keys(gameStage.stageChangeComponent.goalsToCheck).length > 0){
        ctx.fillText('Days to go: ' + gameStage.dayResponseComponent.daysLeft, x, y);
        y += 40;
    }
    
    _.each(gameStage.stageChangeComponent.goalsToCheck, (chk, goalKey) => {
        if (allArgs[goalKey]){
            ctx.strokeRect(x, y - 14, 14, 14);

            if (chk <= 0){
                ctx.drawImage(getImage("tick"), x, y - 16);
            }
            else if (this.canShowGoal(goalKey, ui.world)){
                var buttonRect = [rect[0] + rect[2] - 110, y - 18, 100, 25];
                this.drawPanelButton(buttonRect, 'GO');
                
                if (!this.goalOpenFunctions[goalKey]){
                    this.goalOpenFunctions[goalKey] = (entity, ui) => {
                        this.showGoal(goalKey, ui.world, entity, ui);
                    };
                }

                panelButtons.push([buttonRect, this.goalOpenFunctions[goalKey]]);
            }

            txt = allArgs[goalKey].name;

            ctx.fillText(txt, x + 30, y);
            
            y += 30;
        }
    });
     
    return panelButtons;
};

var chapterPanelCanShowGoal = function(goalKey, world){
    return world.entityForKey(goalKey) ||
        _.any(world.entities, worldEntity => (worldEntity.buttonControlComponent && 
                                              worldEntity.buttonControlComponent.entityKey == goalKey));
};

var chapterPanelShowGoal = function(goalKey, world, entity, ui){
    entityPanelClose(entity, ui);
    
    var goal = world.entityForKey(goalKey);
    if (goal){
        goal.clickComponent.onClick(goal, world, ui);
    }
    else{
        var button = _.filter(world.entities, worldEntity => (worldEntity.buttonControlComponent && 
                                                              worldEntity.buttonControlComponent.entityKey == goalKey))[0];
        if (button){
            var manager = world.entityForKey(button.buttonControlComponent.managerKey);
            if (manager){
                manager.clickComponent.onClick(manager, world, ui);
            }
        }
    }
};


var checkManagedComponent = function(entity, world){
    if (entity.managedComponent){
        var manager = world.entityForKey(entity.managedComponent.managerKey);
        if (!manager){
            entity.healthComponent.kill();
        }
    }
};


var checkQualityTick = function(entity, world){
    var qualityEntity = world.entityForKey(this.qualityKey);
    if (qualityEntity && qualityEntity.qualityComponent.quality < this.minQuality){
        world.makeEntities(entity, this.minQualityEntities);
    }
};


var clearTraitExec = function(entity, world){
    _.each(world.entities, worldEntity => {
        if (worldEntity.managedComponent && worldEntity.managedComponent.managerKey == this.trait){
            worldEntity.healthComponent.kill();
        }
    });
};


var clickTextRender = function(rect, entity){
    ctx.fillStyle = '#ff00ff';
    ctx.fillText(entity.identityComponent.name, rect[0], rect[1] + rect[3] / 4);
};


var clickKill = function(entity){
    entity.healthComponent.health = 0;
};


var closestOpenPositionExec = function(entity, world){
    var checkedPositions = [];
    var positionsToCheck = [this.startPos];
    for (var j = 0; j < 10; j++){
        var nextPositions = [];
        for (var i = 0; i < positionsToCheck.length; i++){
            var checkPos = positionsToCheck[i];
            if (world.isPosBuildable(checkPos) && (
                checkPos[0] > 1 && 
                    checkPos[1] > 1 && 
                    checkPos[0] < PARAMETERS.size[0] - 1 &&
                    checkPos[1] < PARAMETERS.size[1] - 1
            )){
                entity.positionComponent.x = checkPos[0];
                entity.positionComponent.y = checkPos[1];
                return;
            }
            else{
                checkedPositions.push(checkPos);
                _.each(world.hexMap.neighbors(checkPos[0], checkPos[1]), function(neighbor){
                    if (!_.any(checkedPositions, function(checkedPos){ return comparePos(checkedPos, neighbor); })){
                        nextPositions.push(neighbor);
                    }
                });
            }
        }
        positionsToCheck = nextPositions;
    }
    
    if (entity.healthComponent){
        entity.healthComponent.kill();
    }
};


var conditionalMakeGetReplaceKeys = function(entity, world){
    if (_.any(this.stopEntities, function(stopEntityKey){ return world.entityForKey(stopEntityKey); })){
        return false;
    }
    return this.entitiesToMake;
};


var connectSourceGetReplaceKeys = function(entity, world){
    var source = world.entityForUid(this.sourceId);
    
    if (!source){
        return;
    }
    
    var source2 = world.entityForUid(source.identityComponent.sourceId);

    world.connectEntities(source, source2);
};


var construct = function(entity, world){
    world.makeEntities(entity, this.constructionIdentifier, {
        constructed: true
    });
    
    world.addEntity(makeEcs(PARAMETERS.notification, {
        notificationKey: PARAMETERS.goalComplete,
        notificationArgs: {
            key: entity.identityComponent.key
        }
    }));
};


var constructionRender = function(rect, entity, ui, rectCount, timeToNextTick, lastInterval){
    if (this.timeDrawn < 2){
        this.timeDrawn += lastInterval;
    }

    var img = getImage(this.image);
    
    if (this.timeDrawn < 0.05){
        return;
    }
    else if (this.timeDrawn < this.scaleTime){
        var scale = 1 + 0.5 * (this.scaleTime - this.timeDrawn) / this.scaleTime;
        scaledDraw(img, rect, scale);
    }
    else{
        var x = rect[0] + this.getWobble(lastInterval);
        ctx.drawImage(img, x, rect[1]);

        ctx.fillStyle = '#ffffff';
        ctx.fillRect(rect[0] + 17, rect[1] + 32, 26, 17);
        ctx.drawImage(getImage('construction'), rect[0] + 17, rect[1] + 32);
        
        if (this.outlineImage){
            ctx.drawImage(getImage(this.outlineImage), x, rect[1]);
            this.outlineImage = false;

            this.renderTrainIcon(lastInterval, x, rect[1]);
        }
    }

    if (this.timeDrawn > this.scaleTime && this.timeDrawn < this.scaleTime * 1.5){
        var dustImage = getImage(this.dustImage);
        var dist = ((this.timeDrawn - this.scaleTime) / (3 * this.scaleTime)) + 1;
        for (var i = 0; i < 5; i++){
            var angle = 2 * Math.PI * i / 5;
            var x = 60 * dist * Math.cos(angle);
            var y = 60 * dist * Math.sin(angle);
            ctx.drawImage(dustImage, rect[0] + rect[2] / 2 + x, rect[1] + rect[3] / 2 + y);
        }
    }
};


var constructionGetCurrencyComponent = function(entity){
    return entity.constructionComponent;
}


var constructOnClick = function(entity, world, ui){
    var uiKey = '#' + entity.identityComponent.key + '_' + entity.uid;
    ui.openPanelAtPos(uiKey, entity.positionComponent);
};


var constructionTick = function(entity, world){
    entity.constructionComponent.ticked = true;
};


var constructionMenuCloseExec = function(entity, world){
    if  (!entity.constructionComponent.ticked &&
         !_.any(world.entities, function(worldEntity){
             return worldEntity.trainPathComponent &&
                 _.contains(worldEntity.trainPathComponent.trainStops, entity.uid);
         })
        ){
        entity.healthComponent.kill();
    }
};


var getWrappedFunction = function(entity, world, ui){
    if (!this.wrappedFunction){
        this.wrappedFunction = function(){
            world.resetCurrents();
            if (entity.buttonControlComponent.canMakeEntity(world)){
                world.currentMakingEntity = entity;
                ui.minimizeAllPanels();
            }
        }
    }

    return this.wrappedFunction;
};

var controlComponentGetAiVal = function(world){
    return this.aiVal;
};


var controlPanelHandleKeyPress = function(key, entity, ui){
    if (key == 48 || key == 32 || key == 112){
        ui.pause();
    }
    else if (key == 81){
        entity.panelTrackComponent.closeTrackedPanel(ui);
    }
    else if (key == 80 && PARAMETERS.buildablePaths){
        ui.world.togglePathBuilding();
    }
    else if (key == 27){
        if (!entity.panelTrackComponent.closeTrackedPanel(ui)){
            ui.world.openSettingsPanel();
        }
    }
    else if (key > 48 && (key - 48 <= PARAMETERS.ticksPerTick.length)){
        ui.setSpeed(PARAMETERS.ticksPerTick[key - 49]);
    }

    var panelDict = {
    };
    var panelKey = panelDict[key];
    if (panelKey !== undefined){
        var infoPanel = ui.getPanel(panelKey);
        flipPanel(infoPanel, ui);
    }
};


var trackPanels = function(entities){
    var that = this;
    var panelKeysToRemove = [];
    var panelKeysToAdd = [];

    _.chain(entities)
        .filter(function(entity){
            return entity.panelComponent && entity.panelComponent.canClose;
        })
        .each(function(entity){
            var key = entity.identityComponent.key;
            if (entity.panelComponent.closed && _.contains(that.panelsOpened, key)){
                panelKeysToRemove.push(key);
            }
            else if (!entity.panelComponent.closed && !_.contains(that.panelsOpened, key)){
                panelKeysToAdd.push(key);
            }
        });
    
    this.panelsOpened = _.difference(this.panelsOpened, panelKeysToRemove).concat(panelKeysToAdd);
};

var closeTrackedPanel = function(ui){
    var world = ui.world;
    if (this.panelsOpened.length > 0){
        var panel = world.entityForKey(_.last(this.panelsOpened));
        if (panel){
            entityPanelClose(panel, ui);
        }
        this.panelsOpened.splice(this.panelsOpened.length - 1, 1);
        return true;
    }
    return false;
};

var closeAllPanels = function(ui){
    var world = ui.world;
    _.each(this.panelsOpened, function(panel){
        var panelToClose = world.entityForKey(panel);
        if (panelToClose){
            entityPanelClose(panelToClose, ui);
        }
    });
    this.panelsOpened = [];
};


var creepTick = function(entity, world){
    _.each(entity.rendererComponents, function(renderComponent){
        if (renderComponent.activated){
            renderComponent.activated -= 1;
        }
    });
};


var creepCreateExec = function(entity, world){
    if (this.makeTrain){
        var source = world.entityForUid(this.sourceId);
        var source2 = world.entityForKey(source.managedComponent.managerKey);

        if (!source2){
            entity.healthComponent.kill();
            return;
        }

        var self = world.entityForKey(this.destination);

        if (this.firstTick){
            var paths = world.getAllPaths();
            world.ensurePath([source2.positionComponent.x, source2.positionComponent.y],
                             [entity.positionComponent.x, entity.positionComponent.y],
                             paths);
            
            world.ensurePath([source2.positionComponent.x, source2.positionComponent.y],
                             [self.positionComponent.x, self.positionComponent.y],
                             paths);
        }
                
        var trainPath = makeEcs(this.creepTrainPath, {
            trainStops: [self.uid],
            trainStartId: entity.uid,
            focusCost: 0
        });
        world.addEntity(trainPath);
    }
};


var creepDeathExec = function(entity, world){
    var creepDeath = makeEcs(this.creepDeathKey, {
        renderPos: entity.rendererComponents[0].renderPos,
        image: entity.rendererComponents[0].image
    });
    world.addEntity(creepDeath);
};


var creepDeathRender = function(rect, entity, ui, rectCount, timeToNextTick, lastInterval){
    if (this.timeLeft < 0.25 || this.renderPos === undefined){
        entity.healthComponent.kill();
    }
    else{
        var width = this.timeLeft * this.size[0];
        var height = this.timeLeft * this.size[1];
        ctx.drawImage(getImage(this.image), 
                      this.renderPos[0] + this.size[0] / 2 - width / 2,
                      this.renderPos[1] + this.size[1] / 2 - height / 2,
                      width, height);
        this.timeLeft -= lastInterval;
    }
};


var getImmediateEmotionRect = function(rect, entity, rectCount){
    var size = 20;
    var x = rect[0] + rect[2] / 2 - size / 2;
    var y = rect[1] + rect[3] / 2 - size / 2;
    

    return [x, y, size, size];
};

var getEmotionRect = function(rect, entity, rectCount, ui){
    var emotionRect = getImmediateEmotionRect(rect, entity, rectCount);
    
    var ticks = ui.world.game.tickSystemWrapper.ticks;

    if (entity.pathVelocityComponent.isMoving){
        var fraction;
        var movingIndex = Math.floor(ticks / 5);
        if (entity.pathVelocityComponent.moveIndex < movingIndex){
            fraction = 1;
        }
        else if (entity.pathVelocityComponent.moveIndex > movingIndex){
            fraction = 0;
        }
        else{
            ticks = ticks % 5;
            fraction = ticks / 5;
        }
        
        var nextPos = entity.pathVelocityComponent.chosenPath[0];
        var nextRect = ui.world.hexMap.cellForPosComp({ x: nextPos[0], y: nextPos[1]}).rect;
        
        var interpolatedPos = [
            rect[0] + (nextRect[0] - rect[0]) * fraction + (rect[2] / 2) - emotionRect[2] / 2,
            rect[1] + (nextRect[1] - rect[1]) * fraction + (rect[3] / 2) - emotionRect[3] / 2,
        ];
        return [interpolatedPos[0], interpolatedPos[1], emotionRect[2], emotionRect[3]];
    }

    return emotionRect;
};

var currencyRender = function(rect, entity, ui, rectCount){
    var renderPos = this.renderPos;
    if (renderPos){
        ctx.drawImage(getImage(this.image), renderPos[0], renderPos[1]);
        ctx.fillStyle = '#ffffff';
        ctx.fillText(entity.identityComponent.name, renderPos[0], renderPos[1]);
    }
    else{
        var currencyRenderEntity = ui.world.entityForKey(PARAMETERS.currencyRenderEntity);
        currencyRenderEntity.currencyRenderEntityComponent.addCurrencyToRender([entity.positionComponent.x, 
                                                                                entity.positionComponent.y],
                                                                               this.image,
                                                                               entity.identityComponent.name);
    }
};


var currencyDayEnd = function(entity, world){
    var heldIds = _.chain(world.entities)
        .filter(function(worldEntity){
            return worldEntity.carriageComponent;
        })
        .map(function(carriage){
            return carriage.carriageComponent.holding;
        })
        .flatten(true)
        .value();

    if (!_.contains(heldIds, entity.uid)){
        entity.healthComponent.kill();
    }
};


var addCurrencyToRender = function(pos, image, name){
    var record = _.chain(this.currenciesToRender)
        .filter(function(chkRecord){
            return chkRecord.x == pos[0] &&
                chkRecord.y == pos[1] &&
                chkRecord.image == image &&
                chkRecord.name == name;
        })
        .first()
        .value();

    if (record){
        record.count += 1;
    }
    else{
        this.currenciesToRender.push({
            x: pos[0],
            y: pos[1], 
            image: image, 
            name: name, 
            count: 1});
    }
};


var allCurrencyRenderClear = function(rect, entity){
    entity.currencyRenderEntityComponent.currenciesToRender = [];
};


var allCurrencyRender = function(rect, entity, ui){
    var size = 20;
    ctx.fillStyle = '#ffffff';
    _.each(entity.currencyRenderEntityComponent.currenciesToRender, function(record){
        var renderRect = ui.world.hexMap.cellForPosComp(record).rect;
        var x = renderRect[0] + renderRect[2] / 2 - size / 2;
        var y = renderRect[1] + renderRect[3] / 2 - size / 2 + 5;

        for (var i = 0; i < record.count; i++){
            ctx.drawImage(getImage(record.image), x, y + i * 4);
        }
        var txt = record.name;
        ctx.fillText(txt, x, y);
    });
};


var currencyRequestedCurrenciesNeededAtPoint = function(entity){
    var currencyComponent = this.getCurrencyComponent(entity);
    return _.chain(currencyComponent.currencyRequested)
        .keys()
        .filter(function(key){
            return currencyComponent.currencyRequested[key] > 0;
        })
        .value();
};

var currencyRequestedInteractWithTrain = function(entity, world, train){
    var carriage = train.trainComponent.getCurrentCarriage(train, world);
    var didInteract = false;
    var currencyComponent = this.getCurrencyComponent(entity);

    _.chain(carriage.carriageComponent.holding)
        .map(holdingId => world.entityForUid(holdingId))
        .filter(holding => holding && holding.identityComponent)
        .each(function(holding){
            didInteract = true;
            var holdingKey = holding.identityComponent.key;
            holding.healthComponent.kill();
            if (currencyComponent.currencyRequested[holdingKey] > 0){
                currencyComponent.currencyRequested[holdingKey] -= 1;
            }
        });
    
    if (didInteract){
        if (this.currencyEntities){
            world.makeEntities(entity, this.currencyEntities);
        }

        if (this.currencyEffect){
            var rect =  world.hexMap.cellForPosComp(entity.positionComponent).rect;
            world.makeEntities(entity, this.currencyEffect, {
                effectPos: [rect[0] + rect[2] / 2, rect[1] + rect[3] / 2]
            });
        }
    }

    if (_.all(currencyComponent.currencyRequested, function(val){ return val == 0; })){
        var trainPath = world.entityForUid(train.trainVelocityComponent.trainPath);
        
        if (trainPath){
            if (entity.uiDataComponent){
                var stops = _.map(trainPath.trainPathComponent.trainStops, function(stopId){
                    return world.entityForUid(stopId);
                });

                if (_.all(stops, _.identity)){
                    var path = _.map(stops, function(stop){
                        var traitImage = stop.managedComponent ? 
                            traitImageForKey(stop.managedComponent.managerKey, world) : 
                            undefined;
                        return {
                            name: stop.identityComponent.name,
                            traitImage: traitImage,
                            image: stop.uiDataComponent.image
                        };
                    });
                    
                    entity.uiDataComponent.lastPath = path;
                }
            }

            trainPath.healthComponent.kill();
        }
        train.healthComponent.kill();
    }
    
    return false;
};

var currencyRequestedFocusPathStopPercentComplete = function(entity){
    var comp = this.getCurrencyComponent(entity);
    var currencyLeft = _.reduce(comp.currencyRequested, addFunction, 0);
    var allCurrency = _.reduce(comp.origCurrencyRequested, addFunction, 0);
    return Math.round((1 - (currencyLeft / allCurrency)) * 100);
};


var cursorRender = function(rect, entity, ui){
    var mousePos = ui.game.lastPos;
    var image;
    var button = ui.world.currentMakingEntity;
    if (button){
        var trait = ui.world.entityForKey(button.buttonControlComponent.managerKey);
        if (trait){
            ctx.drawImage(getImage(trait.uiDataComponent.image), mousePos[0] - 30, mousePos[1] - 26);
        }
        image = getImage(button.uiDataComponent.image);
        ctx.drawImage(image, mousePos[0] - 30, mousePos[1] - 26);
    }
    else if (ui.world.currentPath){
        ctx.strokeStyle = '#0099ff';
        ctx.beginPath();
        ctx.moveTo(mousePos[0], mousePos[1] - 10);
        ctx.lineTo(mousePos[0], mousePos[1] + 10);
        ctx.stroke();
    }
    else if (ui.world.destructionMode || ui.world.destroyPathMode){
        image = getImage('demolishIcon');
        ctx.drawImage(image, mousePos[0] - 16, mousePos[1] - 16);
    }
};


var dailyEntityMake = function(entity, world){
    var comp = entity.dailyEntityMakerComponent;
    comp.dayCount -= 1;
    if (comp.dayCount <= 0){
        var neighbors = world.hexMap.neighbors(entity.positionComponent.x, entity.positionComponent.y);
        var pos = _.chain(neighbors)
            .filter(function(neighbor){
                return world.isPosBuildable(neighbor);
            })
            .sample()
            .value();

        if (!pos){
            return;
        }

        world.makeEntities(entity, comp.dayStartEntity, {
            x: pos[0],
            y: pos[1]
        });
        
        comp.dayCount = comp.daysToMake;
    }
};


var getAllEmotionParts = function(){
    return [this.emotion].concat(this.traits);
}


var checkTargetEntity = function(targetEntity, entity, world){
    var entityCheck = (targetEntity !== entity &&
                       targetEntity.identityComponent !== undefined &&
                       targetEntity.positionComponent !== undefined &&
                       targetEntity.healthComponent !== undefined &&
                       targetEntity.healthComponent.health > 0 &&
                       targetEntity.damageableComponent !== undefined);
    if (!entityCheck){
        return false;
    }
    
    var damage = this.calculateDamage(targetEntity);
    if (damage === 0){
        return false;
    }

    var targetPos = [targetEntity.positionComponent.x, targetEntity.positionComponent.y];

    var hitPos = this.getHitPos(entity, world);

    return _.any(hitPos, function(neighbor){ return _.isEqual(neighbor, targetPos); });
};

var getHitPos = function(entity, world){
    if (!this.hitPos){
        var neighbors = world.hexMap.neighbors(entity.positionComponent.x, entity.positionComponent.y);
        this.hitPos = neighbors.concat([entity.positionComponent.x, entity.positionComponent.y]);
    }
    return this.hitPos;
};

var calculateDamage = function(targetEntity){
    var relevantTraits = _.filter(targetEntity.damageableComponent.getAll(), (key) => { 
        return this.damageMultipliers[key] !== undefined; 
    });
    
    if (relevantTraits.length === 0){
        return 0;
    }

    var multiplier = _.chain(relevantTraits)
            .map((key) => { return this.damageMultipliers[key]; })
            .reduce(addFunction, 0)
            .value();

    return this.damage * multiplier;
};


var damageIndicatorRender = function(rect, entity, ui){
    var source = ui.world.entityForUid(self.sourceId);
    
    if (source){
        rect = ui.world.hexMap.cellForPosComp(source.positionComponent).rect;
        ctx.fillStyle = '#000000';
        ctx.fillRect(rect[0] + 20, rect[1] + 20, 20, 20);
    }
};


var damamgeUpgradeCheckExec = function(entity, world){
    var existingUpgrades =_.filter(world.entities, (worldEntity) => {
        return worldEntity.damageUpgradeComponent &&
            _.contains(worldEntity.damageUpgradeComponent.towerKeys, this.towerKey);
    });
    
    if (existingUpgrades.length >= this.maxUpgradeCount - 1){
        _.each(this.entitiesToKill, function(entityKey){
            _.each(world.entitiesForKey(entityKey), function(worldEntity){
                worldEntity.healthComponent.kill();
            });
        });

        world.flags[this.flag] = true;
    }
};


var getPartOfDay = function(){
    var partsOfDay = ['early morning', 'late morning', 'afternoon', 'evening', 'night'];
    var index = Math.floor((this.impulseTicks - this.ticks) / (this.impulseTicks / partsOfDay.length));
    return partsOfDay[index];
};


var dayKillExec = function(entity, world){
    world.makeEntities(entity, PARAMETERS.day, {
        dayCount: entity.dayCounterComponent.dayCount + 1
    });
    adminConsole.save('day');
};


var drawBar = function(rect, fraction){
    ctx.fillStyle = '#000000';
    ctxFillRect(rect);

    rect[2] *= fraction;
    ctx.fillStyle = '#ffffff';
    ctxFillRect(rect);
};

var dayPanelRender = function(rect, entity, ui, rectCount, timeToNextTick, lastInterval){
    entity.panelComponent.panelButtons = [];
    ctx.drawImage(getImage(this.backImage), rect[0], rect[1], rect[2], rect[3]);
    ctx.fillStyle = this.textColor;
        
    if (ui.world.isEntityPaused()){
        var pausingEntity = findPausingEntity(ui);
        this.flashTimer += lastInterval;
        if (this.flashTimer < 1.8){
            if (pausingEntity){
                var showPauseRect = [rect[0] + rect[2] / 2 - 100, rect[1] + rect[3] / 2 - 25, 200, 50];
                this.textColor = '#ffd700';
                this.insetColor = '#ffd700'
                this.drawPanelButton(showPauseRect, 'SHOW NEW GOAL');
                this.textColor = '#000000';
                this.insetColor = '#000000';
                entity.panelComponent.panelButtons.push([showPauseRect, this.showPausingEntity]);
            }
            else{
                ctx.font = getFont(30);
                drawCenteredText('PAUSED', rect[0] + rect[2] / 2, rect[1] + rect[3] / 2 + 15);
                ctx.font = BASE_FONT;
            }
        }
        else if (this.flashTimer > 2){
            this.flashTimer -= 2;
        }
    }
    else{
        var gameStage = ui.world.getGameStage();
                
        if (gameStage){
            if (this.displayGameStage === undefined){
                this.displayGameStage = function(){
                    var chapterMenu = ui.world.entityForKey(PARAMETERS.chapterPanel);
                    if (!chapterMenu){
                        chapterMenu = makeEcs(PARAMETERS.chapterPanel);
                        ui.world.addEntity(chapterMenu);
                    }
                    chapterMenu.panelComponent.expandPanel();
                };
            }

            var buttonRect = [rect[0] + rect[2] / 2 - 95, rect[1] + 10, 190, 30];
            this.drawPanelButton(buttonRect, gameStage.identityComponent.name);
            entity.panelComponent.panelButtons.push([buttonRect, this.displayGameStage]);
        }

        var day = _.chain(ui.world.entities)
            .filter(function(worldEntity){
                return worldEntity.dayCounterComponent;
            })
            .first()
            .value();

        if (day){
            ctx.fillText('Day ' + day.dayCounterComponent.dayCount, rect[0] + rect[2] / 2 - 95, rect[1] + rect[3] - 30);
            drawBar([rect[0] + rect[2] / 2 - 47, rect[1] + rect[3] - 38, 135, 5], 
                    1 - (day.dayCounterComponent.ticks / day.dayCounterComponent.impulseTicks));

            var dayActions;
            if (this.dayActions){
                dayActions = this.dayActions;
            }
            else{
                dayActions = [['[P]ause', ui.pause]];

                _.each(PARAMETERS.ticksPerTick, function(ticksPerTick){
                    txt = '[' + Math.floor(ticksPerTick / PARAMETERS.ticksPerTick[0]) + ']x';
                    dayActions.push([txt, function(){ ui.setSpeed(ticksPerTick); }]);
                });
                this.dayActions = dayActions;
            }

            var ticksPerTick = PARAMETERS.ticksPerTick;
            var currentIdx = ui.game.tickSystemWrapper.running ? 
                (ticksPerTick.indexOf(ui.game.ticksPerTick) + 1) : 
                0;

            _.each(dayActions, (data, i) => {
                var text = data[0];
                var fn =  data[1];
                var actionRect = [rect[0] + rect[2] / 2 - 87 + 60 * i, rect[1] + rect[3] - 22, 55, 20];
                this.drawPanelButton(actionRect, text);

                if (currentIdx == i){
                    ctx.strokeStyle = '#ff0000';
                    ctx.strokeRect(actionRect[0], actionRect[1], actionRect[2], actionRect[3]);
                }

                entity.panelComponent.panelButtons.push([actionRect, fn]);
            });

            
        }

        var pointCounter = _.filter(ui.world.entities, function(worldEntity){
            return worldEntity.pointCounterComponent;
        })[0];
    }
};

var showPausingEntity = function(entity, ui){
    var pausingEntity = findPausingEntity(ui);
    if (pausingEntity){
        pausingEntity.clickComponent.onClick(pausingEntity, ui.world, ui);
    }
};

var findPausingEntity = function(ui){
    return _.chain(ui.world.entities)
        .filter((worldEntity) => worldEntity.pauseComponent && worldEntity.pauseComponent.paused && worldEntity.clickComponent)
        .first()
        .value();
};


var demolishButtonRender = function(rect, entity, ui){
    if (!this.demolishButtonFunction){
        this.demolishButtonFunction = function(){
            if (!ui.world.destructionMode){
                ui.world.resetCurrents();
                ui.world.destructionMode = true;
            }
            else{
                ui.world.resetCurrents();
            }
        };
    }
    ui.informLine(PARAMETERS.buildPanel, {
        buttonFunction: this.demolishButtonFunction,
        text: 'demolish'
    }, PARAMETERS.baseButtonName);
};


var demolishPathButtonRender = function(rect, entity, ui){
    if (!this.demolishPathButtonFunction){
        this.demolishPathButtonFunction = function(){
            if (!ui.world.destroyPathMode){
                ui.world.resetCurrents();
                ui.world.destroyPathMode = true;
            }
            else{
                ui.world.resetCurrents();
            }
        };
    }

    ui.informLine(PARAMETERS.buildPanel, {
        buttonFunction: this.demolishPathButtonFunction,
        text: 'remove path'
    }, PARAMETERS.baseButtonName);
};


var depositInteractWithTrain = function(entity, world, train){
    var trainRequirements = train.trainComponent.calculateTrainCurrencies(train, world);

    var deposit = entity.depositComponent.deposit;
    
    for (var key in deposit){
        if (deposit[key] > 0 && trainRequirements[key] > 0){
            var currency = world.makeEntities(entity, key)[0];
            var carriage = train.trainComponent.getCurrentCarriage(train, world);
            carriage.carriageComponent.holding.push(currency.uid);

            deposit[key] -= 1;
        }
    }
    
    return false;
};

var depositCurrenciesMadeAtPoint = function(entity){
    return _.chain(entity.depositComponent.deposit)
        .map(function(val, key){
            return (val > 0) ? key : false;
        })
        .filter(_.identity)
        .value();
};


var depositTick = function(entity, world){
    if (_.all(entity.depositComponent.deposit, function(val){ return val == 0; })){
        entity.healthComponent.health = 0;
    }
};


var checkMakeDeposit = function(entity, world){
    if (this.checkMakeDeposit){
        var existingDeposit = _.chain(world.entities)
            .filter(function(worldEntity){
                return worldEntity.depositComponent &&
                    _.all(entity.depositComponent.deposit, function(val, key){
                        return worldEntity.depositComponent.deposit[key];
                    }) &&
                    ((!entity.managedComponent && !worldEntity.managedComponent) ||
                     (entity.managedComponent && worldEntity.managedComponent &&
                      entity.managedComponent.managerKey == worldEntity.managedComponent.managerKey))
            })
            .first()
            .value();
            
        if (existingDeposit){
            addDict(existingDeposit.depositComponent.deposit, entity.depositComponent.deposit);
            entity.healthComponent.kill();
            if (existingDeposit.timedDeathComponent && entity.timedDeathComponent){
                existingDeposit.timedDeathComponent.timeToDeath = entity.timedDeathComponent.timeToDeath;
            }
        }
    }
};


var depositPositionExec = function(entity, world){
    if (this.randomPosition ||
        entity.positionComponent.x === undefined ||
        entity.positionComponent.y === undefined ||
        entity.managedComponent === undefined){
        randomOpenPositionExec(entity, world);
    }
}


var disabledTowerRender = function(rect, entity){
    ctx.drawImage(getImage(this.image), rect[0], rect[1]);
    ctx.fillStyle = '#ffffff';
    ctx.fillText(entity.identityComponent.name, rect[0] + rect[2] / 2, rect[1] + 3 * rect[3] / 4);
};


var checkMemoriesDisabled = function(trait){
    return trait && trait.memoryComponent;
};


var disableMemoriesTick = function(entity, world){
    var timeLeft = Math.floor(entity.timedDeathComponent.timeToDeath / PARAMETERS.ticksPerTick[0]);
    entity.identityComponent.name = this.name + ' (' + timeLeft + ')';
};


var checkTraitDisabled = function(trait){
    return trait && _.contains(this.traitsToDisable, trait.identityComponent.key);
};


var emotionResourceDifficultyExec = function(entity, world){
    var difficulty = world.entityForKey(PARAMETERS.difficultySetting).difficultySettingComponent.difficulty;
    entity.resourceComponent.resourceCount = max(entity.resourceComponent.resourceCount - difficulty, 1);
};


var emotionSourceCreateExec = function(entity, world){
    var spawnSource = world.entityForKey(this.sourceKey);

    if (!spawnSource){
        console.log('Failed to make emotion source because source key missing - ' + this.sourceKey);
        return;
    }

    if (!spawnSource.positionComponent){
        console.log(this.sourceKey + ' is missing a position component, so cannot spawn emotion source.')
    }
    
    var memory = world.entityForKey(this.memoryKey);
    var paths = world.getAllPaths();

    if (memory){
        if (memory.entityManagerComponent.disabled){
            return;
        }
        
        memory.healthComponent.health += 100;

        var space = memory.entityManagerComponent.findFreeManagedSpace(memory, world);

        if (space){
            world.makeEntities(memory, this.resourceKey, {
                x: space.positionComponent.x,
                y: space.positionComponent.y,
                sourceTrait: this.memoryKey
            });

            world.ensurePath([memory.positionComponent.x, memory.positionComponent.y],
                             [space.positionComponent.x, space.positionComponent.y],
                             paths);
        }
    }
    else{
        if (_.any(world.entities, worldEntity => (worldEntity.disableTraitComponent && 
                                                  worldEntity.disableTraitComponent.disableNewMemories))){
            return;
        }
        
        var emotion = allArgs[this.memoryKey].memoryEmotions[0];
        
        var startManagedEntities = _.chain(world.entities)
            .filter(function(worldEntity){
                return worldEntity.emotionResourceMapComponent &&
                    worldEntity.emotionResourceMapComponent.emotionToMap == emotion;
            })
            .map(function(worldEntity){
                return worldEntity.emotionResourceMapComponent.startManagedEntities;
            })
            .flatten(true)
            .value();
        startManagedEntities.push(this.resourceKey)

        memory = world.makeEntities(entity, this.memoryKey, {
            source2Key: this.sourceKey,
            sourceName: spawnSource.identityComponent.name,
            startManagedEntities: startManagedEntities,
            startPos: [spawnSource.positionComponent.x, spawnSource.positionComponent.y]
        })[0];

        var shouldKill = true;
        if (memory && memory.healthComponent.health > 0){
            shouldKill = world.ensurePath([memory.positionComponent.x, memory.positionComponent.y],
                                          [spawnSource.positionComponent.x, spawnSource.positionComponent.y],
                                          paths) === undefined;
        }
        
        if (shouldKill){
            entity.healthComponent.kill();
        }
    }
};


var emotionSourceConstructionCreateExec = function(entity, world){
    var memory = world.entityForKey(this.sourceKey);
    var source = world.entityForKey(memory.memoryComponent.source2Key);
    
    if (source){
        var focusStop = world.makeEntities(source, this.focusStopKey)[0];
        
        var trainPath = world.makeEntities(entity, this.trainPath, {
            trainStops: [entity.uid],
            trainStartId: focusStop.uid
        })[0];

        world.ensurePath([entity.positionComponent.x, entity.positionComponent.y],
                         [memory.positionComponent.x, memory.positionComponent.y],
                         world.getAllPaths());
    }
    else{
        entity.healthComponent.kill();
    }
};


var endGoalExecute = function(entity, world){
    world.flags[this.flag] = true;
    _.each(world.entitiesForKey(this.buttonKey), entity => entity.healthComponent.kill());
};


var makeLifecycleEntities = function(entity, world, entitiesToMake){
    this.entitiesMade = _.map(world.makeEntities(entity, entitiesToMake), function(entity){
        return entity.uid;
    });
};

var endLifecycle = function(world){
    _.each(this.entitiesMade, function(entityUid){
        var entity = world.entityForUid(entityUid);
        if (entity){
            entity.healthComponent.kill();
        }
    });
};


var canMakeManagedEntity = function(entity, world){
    return true;
};

var findFreeManagedSpace = function(entity, world){
    var key = entity.identityComponent.key;

    var managedEntities = _.filter(world.entities, function(worldEntity){
        return worldEntity.managedComponent &&
            worldEntity.managedComponent.managerKey == key;
    });

    var space = _.chain(world.entities)
        .filter(function(worldEntity){
            return (worldEntity.identityComponent && worldEntity.identityComponent.key == key) ||
                (worldEntity.sourceExtensionComponent && worldEntity.sourceExtensionComponent.sourceKey == key);
        })
        .filter(function(worldEntity){
            return !_.any(managedEntities, function(managedEntity){
                return managedEntity.positionComponent.comparePos(worldEntity.positionComponent);
            });
        })
        .sample()
        .value();
    
    return space;
};

var checkForNewManaged = function(entity, world){
    this.hasNew = _.any(world.entities, worldEntity => (worldEntity.buttonControlComponent &&
                                                        worldEntity.buttonControlComponent.isNew &&
                                                        worldEntity.buttonControlComponent.managerKey == entity.identityComponent.key));
};


var getSaneName = function(entityKey){
    if (allArgs[entityKey]){
        return allArgs[entityKey].name;
    }
    else{
        return entityKey;
    }
};

var sanitize = function(entityKeys){
    if (_.isArray(entityKeys)){
        return reduceArray(_.map(entityKeys, getSaneName));
    }
    else if (_.isObject(entityKeys)){
        return _.reduce(entityKeys, function(memo, val, key){
            var st = sanitize(key) + ' (' + val + ')';
            return memo ? (memo + ', ' + st) : st;
        }, false);
    }
    else{
        return getSaneName(entityKeys);
    }
};

var entityPanelRender = function(rect, entity, ui){
    var panelButtons = [];
    var world = ui.world;
    var building = world.entityForUid(entity.panelComponent.sourceId);

    if (!building){
        return;
    }

    ctx.drawImage(getImage(this.backImage), rect[0], rect[1], rect[2], rect[3]);
    if (building.uiDataComponent){
        building.uiDataComponent.drawImage(building, world, [rect[0] + 5, rect[1] + 5]);
    }
    ctx.drawImage(getImage(this.entityImage), rect[0] + 5, rect[1] + 5);

    ctx.fillStyle = this.textColor;
    var titleLength = building.identityComponent.name.length;
    var titleFontSize = (titleLength > 20) ? 18 :
        (titleLength > 15) ? 24 :
        36;
    ctx.font = getFont(titleFontSize);
    var titleWidth = ctx.measureText(building.identityComponent.name).width;

    ctx.fillText(building.identityComponent.name, rect[0] + (rect[2] - titleWidth) / 2, rect[1] + 40);

    panelButtons.push(this.drawHelpButton(entity, rect));
    
    ctx.font = getFont(20);

    if (building.personHeldComponent && building.personHeldComponent.personHeld){
        ctx.fillText(sanitize(building.personHeldComponent.personHeld), rect[0] + 10, rect[1] + 75);
    }
    
    var currenciesNeeded, currenciesMade;
    
    if (building.focusPathStopComponent){
        currenciesNeeded = building.focusPathStopComponent.currenciesNeededAtPoint(building, world);
        currenciesMade = building.focusPathStopComponent.currenciesMadeAtPoint(building, world);
    }
    else{
        currenciesNeeded = [];
        currenciesMade = [];
    }

    var startY = rect[1] + 110;

    var description = (building.descriptionComponent && building.descriptionComponent.description.length > 0) ?
        building.descriptionComponent.description :
        undefined;

    if (description){
        ctx.font = '16px OpenSansItalic';

        if (_.isArray(description)){
            _.each(description, function(desc, i){
                ctx.fillText(desc, rect[0] + 35, startY);
                startY += 35;
            });
        }
        else{
            ctx.fillText(description, rect[0] + 35, startY);
            startY += 90;
        }

        ctx.font = getFont(20);
    }

    var lines = [];
    

    if (building.timedDeathComponent){
        lines.push('TIME LEFT: ' + Math.floor(building.timedDeathComponent.timeToDeath / PARAMETERS.ticksPerTick[0]));
    }

    if (currenciesMade.length > 0 && !building.emotionSourceComponent && !building.depositComponent){
        lines.push('MAKES:     ' + sanitize(currenciesMade));
    }
    
    if (building.resourceComponent && building.resourceComponent.resourceCount){
        lines.push('' + building.resourceComponent.resourceCount + ' left');
    }
    if (building.depositComponent){
        lines.push('Built by ' + building.identityComponent.sourceName);
        lines.push('HOLDS:     ' + sanitize(currenciesMade));
        var amountLeft = _.reduce(building.depositComponent.deposit, addFunction, 0);
        lines.push('' + amountLeft + ' left');
    }
    if (building.damageComponent){
        lines.push('DAMAGE DONE: ' + building.damageComponent.damage);
        var line = 'CHARGES HELD:  ' + building.damageComponent.charges + ' / ' + building.damageComponent.maxCharges;
        if (building.damageComponent.maxCharges > building.damageComponent.charges){
            line += '  (start train to recharge)'
        }
        lines.push(line);
    }
    if (building.emotionSourceComponent){
        lines.push('EMOTION LEFT:' + reduceArray(building.emotionSourceComponent.emotions));
        var sourceEntity = world.entityForUid(building.identityComponent.sourceId);
        if (sourceEntity){
            lines.push('SOURCE: ' + sourceEntity.identityComponent.name);
        }
    }
    if (building.focusPathStopComponent && building.focusPathStopComponent.currencyEntities){
        if (_.isArray(building.focusPathStopComponent.currencyEntities) ||
            _.isString(building.focusPathStopComponent.currencyEntities)){
            var colorText = {
                anger: '#660000',
                sadness: '#000066',
                fear: '#660066',
                joy: '#dddd00'
            };
            
            var st = sanitize(building.focusPathStopComponent.currencyEntities);
            var params = allArgs[building.focusPathStopComponent.currencyEntities];
            if (params && params.emotionsToAdd){
                var emotion = Object.keys(params.emotionsToAdd)[0];
                var color = colorText[emotion];
                emotion = emotion.charAt(0).toUpperCase() + emotion.slice(1);
                st = emotion + ' due to ' + st;
                lines.push({
                    text: 'MAKES: ' + st,
                    color: color
                })
            }
            else{
                lines.push('MAKES:  ' + st);
            }
        }
    }
    if (building.dailyEntityMakerComponent){
        var daysLeft = building.dailyEntityMakerComponent.dayCount;
        daysLeft = (daysLeft > 1) ? (daysLeft + ' DAYS') : (daysLeft + ' DAY')
        lines.push('MAKES: ' + sanitize(building.dailyEntityMakerComponent.dayStartEntity) + ' IN ' + daysLeft);
    }
    if (building.healthComponent && building.healthComponent.maxHealth > 1){
        lines.push('HEALTH: ' + building.healthComponent.health + '/' + building.healthComponent.maxHealth);
    }
    
    var i;
    
    for (i = 0; i < lines.length; i++){
        var line = lines[i];
        if (line.color){
            ctx.fillStyle = line.color;
            ctx.fillText(line.text, rect[0] + 35, startY);
            ctx.fillStyle = '#000000';
        }
        else{
            ctx.fillText(line, rect[0] + 35, startY);
        }
        startY += 40;
    }

    if (building.focusPathStopComponent && building.focusPathStopComponent.getCurrencyComponent){
        var comp = building.focusPathStopComponent.getCurrencyComponent(building);

        var xDiff = _.chain(comp.origCurrencyRequested)
            .map(function(val, key){
                return ctx.measureText(sanitize(key) + ':').width;
            })
            .max()
            .value() + 60;

        _.each(comp.origCurrencyRequested, function(val, key){
            if (val > 10){
                startY -= 10;
            }
            ctx.fillText(sanitize(key) + ':', rect[0] + 35, startY);
            for (var i = 0; i < val; i++){
                var img = (i < comp.currencyRequested[key]) ? 'currency' : 'filledCurrency';
                img = getImage(img);
                ctx.drawImage(img, rect[0] + xDiff + 25 * (i % 10), startY - 17 + Math.floor(i / 10) * 25);
            }
            startY += 40;
        });
    }

    if (building.flipComponent){
        var flipRect = [rect[0] + rect[2] / 2 - 100, startY + 20, 200, 30];
        var flipName = allArgs[building.flipComponent.flipEntity];
        this.drawPanelButton(flipRect, 'Flip to ' + flipName);
        panelButtons.push([rect, flipEntity]);
    }

    if (building.interactionComponent.canInteractSelf()){
        var paths = _.filter(world.entities, function(worldEntity){
            return worldEntity.trainPathComponent &&
                _.contains(worldEntity.trainPathComponent.trainStops, building.uid);
        });

        var pathRect = [rect[0] + rect[2] / 2 - 100, rect[1] + rect[3] - 100, 200, 30];

        if (paths.length > 0){
            var path = paths[0];
            if (path.trainPathComponent.canMakeTrain(path, world)){
                this.drawPanelButton(pathRect, 'SEE ACTIVE TRAIN');
                panelButtons.push([pathRect, entityPanelShowPath]);
            }
            else if (PARAMETERS.buildablePaths){
                this.drawPanelButton(pathRect, 'CONNECT TRACK');
                panelButtons.push([pathRect, entityPanelStartTrack]);
            }
        }
        else if (building.focusPathStopComponent && building.focusPathStopComponent.isPathEnd){
            if (!building.damageComponent || building.damageComponent.charges < building.damageComponent.maxCharges){
                var selfEntity = world.entityForKey(PARAMETERS.self);
                
                if (selfEntity.focusMakingComponent.focusUsing < selfEntity.focusMakingComponent.focusCount){
                    this.drawPanelButton(pathRect, 'SET UP TRAIN');
                    panelButtons.push([pathRect, entityPanelStartPath]);
                }
                else{
                    ctx.fillText('NO FOCUS LEFT', pathRect[0], pathRect[1] + pathRect[3]);
                }
            }
        }
    }
    else{
        ctx.fillText('You do not control this building.', rect[0] + rect[2] / 2 - 100, rect[1] + rect[3] - 70);
    }
 
    return panelButtons;
};

var flipEntity = function(entity, ui){
    var world = ui.world;
    var newEntity = world.makeEntities(entity, entity.flipComponent.flipEntity);
    entity.healthComponent.kill();

    if (entity.timedDeathComponent.timeToDeath){
        newEntity.timedDeathComponent.timeToDeath = entity.timedDeathComponent.timeToDeath;
    }
};

var entityPanelShowPath = function(entity, ui){
    var world = ui.world;
    var path = _.chain(world.entities)
        .filter(function(worldEntity){
            return worldEntity.trainPathComponent && 
                _.contains(worldEntity.trainPathComponent.trainStops, entity.panelComponent.sourceId);
        })
        .first()
        .value();
    
    if (path){
        var uiKey = 'trainPath_' + path.uid;
        var menu = world.entityForKey(uiKey);
        if (!menu){
            menu = makeEcs(PARAMETERS.trainPanel, {
                key: uiKey,
                sourceId: path.uid
            });
            world.addEntity(menu);
        }
        else{
            menu.panelComponent.expandPanel();
        }
    }
};

var entityPanelStartPath = function(entity, ui){
    var building = ui.world.entityForUid(entity.panelComponent.sourceId);
    ui.world.focusPathFromEntity(building);
    
    var menu = ui.world.entityForKey(PARAMETERS.trainPathMakingPanel);
    menu.panelComponent.expandPanel();

    entity.panelComponent.closed = true;
};

var entityPanelStartTrack = function(entity, ui){
    ui.world.togglePathBuilding();
};

var entityPanelTimeOut = function(entity, ui){
    var building = ui.world.entityForUid(entity.panelComponent.sourceId);
    building.timedDeathComponent.timeToDeath = 0;
};


var entityReplaceGetReplaceKeys = function(entity, world){
    if (this.skipFlag && world.flags[this.skipFlag]){
        return;
    }
    
    return this.entitiesToMake;
};


var getXpLevel = function(entity){
    var health = this.xpPerLevel;
    var i;
    for (i = 1; i < this.maxLevel; i++){
        health += i * this.xpPerLevel / 2;
        
        if (entity.healthComponent.health < health){
            break;
        }
    }
    return i;
};

var gainExp = function(entity, world){
    entity.healthComponent.health += 25;
    
    _.chain(world.entities)
        .filter(function(worldEntity){
            return worldEntity.experienceTriggerComponent &&
                worldEntity.experienceTriggerComponent.traitKey == entity.identityComponent.key;
        })
        .each(function(worldEntity){
            world.makeEntities(entity, worldEntity.experienceTriggerComponent.xpEntities);
        });
};

var updateExpansion = function(entity){
    var level = this.getXpLevel(entity);
    if (level > this.currentLevel){
        this.currentLevel = level;
    }
};


var fearTickModifyInteraction = function(entity, world, tower, train){
    if (this.fearFrozen > 0){
        this.fearFrozen -= 1;
    }
    else if (percentCheck(5)){
        this.fearFrozen = 3;
    }
    return this.fearFrozen <= 0;
};


var featureItemActionExec = function(entity, world){
    entity.currency[this.execCurrencyKey] += this.execCurrencyChange;
};


var flagBranchGetReplaceKeys = function(entity, world){
    return world.flags[this.flagToCheck] ? this.flagEntities : this.notFlagEntities;
};


var flaggedMakeGetReplaceKeys = function(entity, world){
    if (!world.flags[this.flagToCheck]){
        world.flags[this.flagToCheck] = true;
        return this.entitiesToMake;
    }
    return false;
};


var focusPanelRender = function(rect, entity, ui, rectCount, timeToNextTick, lastInterval){
    entity.panelComponent.panelButtons = [];
    ctx.drawImage(getImage(this.backImage), rect[0], rect[1], rect[2], rect[3]);

    var selfEntity = ui.world.entityForKey(PARAMETERS.self);
    
    if (!this.toggleFocusPath){
        this.toggleFocusPath = function(){
            ui.world.toggleFocusPath();
            ui.expandPanel(PARAMETERS.trainPathMakingPanel);
        };

        this.showFocusPanel = function(){
            ui.expandPanel(PARAMETERS.allTrainPanel);
        };
    }

    ctx.fillStyle = this.textColor;


    var lostControl = ui.world.lostControl();
    var focusPanelRect = [rect[0] + rect[2] / 2 - 75, rect[1] + 10, 150, 30];

    if (lostControl){
        var textColor = this.textColor;
        this.textColor = '#cc0000';
        this.drawPanelButton(focusPanelRect, 'LOST CONTROL');
        
        
        var healthText = 'HEALTH: ' + selfEntity.healthComponent.health + '/' + selfEntity.healthComponent.maxHealth

        ctx.fillText(healthText, rect[0] + 30, rect[1] + 60);
        this.textColor = textColor;
    }
    else{
        var focusText = 'Trains used - ' + selfEntity.focusMakingComponent.focusUsing + ' / ' + selfEntity.focusMakingComponent.focusCount;
        
        if (selfEntity.focusMakingComponent.focusUsing === 0){
            this.insetColor = '#cc0000';
            this.textColor = this.insetColor;

            this.drawPanelButton(focusPanelRect, focusText);
            
            this.insetColor = '#000000';
            this.textColor = '#000000';
        }
        else{
            this.drawPanelButton(focusPanelRect, focusText);
        }
        
        entity.panelComponent.panelButtons.push([focusPanelRect, this.showFocusPanel]);

        var fraction = 1 - (selfEntity.focusTickerComponent.ticks / selfEntity.focusTickerComponent.impulseTicks);
        fraction = (fraction === 0) ? 1 : fraction;
        drawBar([focusPanelRect[0], focusPanelRect[1] + 40, focusPanelRect[2], 5], 
                fraction);

        ctx.fillStyle = this.textColor;
        var efficiency = Math.floor(100 * selfEntity.focusMakingComponent.focusEfficiency());
        ctx.fillText('(EFFICIENCY: ' + efficiency + '%)', focusPanelRect[0], focusPanelRect[1] + 70);
    }
    
};


var updateCurrencyDictWithStop = function(stop, world, currencyDict){
    _.each(this.currenciesMadeAtPoint(stop, world), function(currency){
        incrementDict(currencyDict, currency, -1);
    });

    _.each(this.currenciesNeededAtPoint(stop, world), function(currency){
        incrementDict(currencyDict, currency);
    });
};

var currenciesExistAtPoint = function(entity, world){
    var currenciesAtPoint = _.filter(world.entities, function(worldEntity){
        return worldEntity.pathVelocityComponent &&
            worldEntity.positionComponent.comparePos(entity.positionComponent);
    });

    return _.all(this.currenciesMadeAtPoint(entity, world), function(currency){ 
        return _.any(currenciesAtPoint, function(currencyAtPoint){
            return currencyAtPoint.identityComponent.key == currency;
        });
    });
};

var checkFocusStopDisabled = function(entity, world){
    var chkEnables = () => {
        return ((this.enableChecks.length > 0) &&
                _.all(this.enableChecks, function(chkKey){ return !world.entityForKey(chkKey); }));
    }; 
    var chkRequiredOpinions = () => {
        ((this.requiredOpinions.length > 0) &&
         entity.personHeldComponent &&
         _.all(this.requiredOpinions, function(opinion){
             return !_.any(world.entitiesForKey(opinion), function(opinionEntity){
                 return opinionEntity.personHeldComponent.personHeld == entity.personHeldComponent.personHeld;
             });
         })); 
    };
    var chkDisablingEntity = entity.managedComponent &&
        _.any(world.entities, function(worldEntity){
            return worldEntity.disableTraitComponent && worldEntity.disableTraitComponent.checkTraitDisabled(entity);
        });

    return chkDisablingEntity || chkEnables() || chkRequiredOpinions();
};


var focusRender = function(rect, entity, ui){
};


var aliveMakerFocusTick = function(entity, world){
    world.makeEntities(entity, this.aliveEntities);
};


var fullMenuOnClick = function(entity, world, ui){
    var uiKey = this.uiKey;
    var menu = world.entityForKey(uiKey);
    if (!menu){
        menu = makeEcs(this.panelKey, {
            key: uiKey,
            sourceId: entity.uid,
            entityImage: this.image,
            size: (entity.descriptionComponent && entity.descriptionComponent.description.length > 0) ? [550, 400] : [425, 300]
        });
        ui.world.addEntity(menu);
    }
    
    menu.panelComponent.expandPanel();
    
    var rect = world.hexMap.cellForPosComp(entity.positionComponent).rect;

    menu.absolutePositionComponent.x = rect[0] + rect[2];
    menu.absolutePositionComponent.y = rect[1] + rect[3];

    world.addEntity(makeEcs(PARAMETERS.notification, {
        notificationKey: PARAMETERS.openMenu,
        notificationArgs: {
            menuSourceKey: entity.uid
        }
    }));
};

var getUiKey = function(key, uid){
    return key + '_' + uid + '_menu';
};


var gainControlMenuRender = function(rect, entity, ui, rectCount, timeToNextTick, lastInterval){
    var animateBack = false;
    if (this.percentageComplete > 100){
        this.renderTime -= lastInterval;
        if (this.renderTime < 0){
            entity.healthComponent.kill();
            return;
        }
        animateBack = true;
    }
    else if (this.renderTime < this.fullAnimationTime){
        this.renderTime += lastInterval;
        animateBack = true;
    }

    if (animateBack){
        var fraction = this.renderTime / this.fullAnimationTime;
        ctx.drawImage(getImage(this.backImage), rect[0], rect[1], rect[2] * fraction, rect[3] * fraction);
        return;
    }

    ctx.drawImage(getImage(this.backImage), rect[0], rect[1], rect[2], rect[3]);
    ctx.fillStyle = '#' + this.textColor;
    ctx.font = getFont(35);
    var txt = 'Regain Control';
    ctx.fillText(txt, rect[0] + (rect[2] - ctx.measureText(txt).width) / 2, rect[1] + 40);

    ctx.font = '16px OpenSansItalic';
    ctx.fillText(this.text, rect[0] + (rect[2] - ctx.measureText(this.text).width) / 2, rect[1] + 100);

    ctx.font = BASE_FONT;
    ctx.fillStyle = '#000000';
    txt = 'Keep your mind inside the bar to regain control.';
    ctx.fillText(txt, rect[0] + (rect[2] - ctx.measureText(txt).width) / 2, rect[1] + 140);
    
    ctx.fillStyle = '#' + this.barColor;
    ctx.fillRect(rect[0] + 10, rect[1] + this.barPos[1], rect[2] - 20, 40);
    
    this.barPos[0] = ui.game.lastPos[0] - (this.barWidth / 2) - rect[0];
    this.barPos[0] = clamp(this.barPos[0], rect[2] - 10 - this.barWidth, 10);

    ctx.fillStyle = '#dddddd';
    
    var controlRect = [rect[0] + this.barPos[0], rect[1] + this.barPos[1], this.barWidth, 40];
    ctxFillRect(controlRect);
    
    var brainRect = [rect[0] + this.brainPos[0], rect[1] + this.brainPos[1], 40, 35];
    ctx.drawImage(getImage(this.brainImage), brainRect[0], brainRect[1]);

    if (collideRect(controlRect, brainRect)){
        this.percentageComplete += 25 * lastInterval;
    }
    else{
        this.percentageComplete -= 20 * lastInterval;
        this.percentageComplete = max(this.percentageComplete, 0);
    }
    
    this.barWidth += 25 * lastInterval;
    this.barWidth = min(this.barWidth, rect[2] - 20);
    
    if (this.resetVelTime <= 0){
        this.brainVel = ((Math.random() * 4) - 2) * 60;
        this.resetVelTime = 1;
    }
    else{
        this.resetVelTime -= lastInterval;
    }
    
    this.brainPos[0] += this.brainVel * lastInterval;
    this.brainPos[0] = clamp(this.brainPos[0], rect[2] - 62, 10);

    if (this.percentageComplete < 100){
        ctx.fillStyle = '#ffffff';
        ctx.strokeStyle = '#ffffff';
        var lineWidth = ctx.lineWidth;
        ctx.lineWidth = 5;

        ctx.beginPath();
        var startAngle = Math.PI * -0.5;
        var endAngle = 2 * Math.PI * this.percentageComplete / 100 + startAngle;
        ctx.arc(rect[0] + rect[2] / 2, rect[1] + 300, 40, startAngle, endAngle);
        ctx.stroke();

        ctx.lineWidth = lineWidth;

        ctx.fillText('' + Math.floor(this.percentageComplete) + '%', rect[0] + rect[2] / 2 - 10, rect[1] + 300);
    }
};


var killLostControlExec = function(entity, world){
    _.chain(world.entities)
        .filter(worldEntity => worldEntity.holdControlComponent)
        .each((worldEntity) => { worldEntity.healthComponent.kill(); });
};


var gameEndRender = function(){
    ctx.fillStyle = '000000';
    ctx.fillRect(0, 0, width, height);
    ctx.font = getFont(40);
    ctx.fillStyle = '#ffffff';
    var txt = 'GAME OVER';
    var textWidth = ctx.measureText(txt).width;
    ctx.fillText(txt, (width - textWidth) / 2, height);
};


var checkStageComplete = function(entity, notificationArgs){
    if (entity.stageChangeComponent.goalsToCheck[notificationArgs.key]){
        entity.stageChangeComponent.goalsToCheck[notificationArgs.key] -= 1;
        entity.stageChangeComponent.shouldChange = _.all(entity.stageChangeComponent.goalsToCheck, function(val){
            return val <= 0;
        });
    }
};


var gameStageDayEnd = function(entity, world){
    world.trackActionables();
    this.daysLeft -= 1;
    if (this.daysLeft <= 0 && entity.stageChangeComponent.checkAndChange(entity, world)){
        entity.healthComponent.health = 0;
    }
    else{
        var dayEntity = _.filter(world.entities, function(worldEntity){
            return worldEntity.dayCounterComponent;
        })[0];
        
        var dayCount = dayEntity.dayCounterComponent.dayCount + 1;

        _.each(world.entities, function(worldEntity){
            if (worldEntity.panelComponent && worldEntity.panelComponent.canClose){
                worldEntity.panelComponent.closed = true;
            }
        });
    }
};


var checkAndChangeGameState = function(entity, world){
    if (this.shouldChange){
        _.each(this.executables, function(exec){
            exec.execute(entity, world);
        });
        return true;
    }
    return false;
};


var canMakeGoal = function(world){
    return world.entityForKey(this.entityKey) === undefined &&
        world.entityForKey(this.constructionKey) === undefined &&
        _.all(this.disablingEntities, function(disablingKey){ return world.entityForKey(disablingKey) === undefined; }) &&
        _.all(this.requiredEntities, function(requiredKey){ return world.entityForKey(requiredKey); });
};

var makeGoal = function(world, pos, entity, interactionKey){
    entity.buttonControlComponent.isNew = false;
    var manager = world.entityForKey(this.managerKey);
    if (manager){
        manager.entityManagerComponent.checkForNewManaged(manager, world);
    }


    var args = {
        x: pos[0],
        y: pos[1],
        sourceKey: this.managerKey,
        sourceTrait: this.managerKey
    };

    if (interactionKey){
        args.interactionKeys = [interactionKey];
    }

    if (entity.personHeldComponent){
        args.personHeld = entity.personHeldComponent.personHeld;
    }

    return makeEcs(this.constructionKey, args);
};


var goalCompleteExec = function(entity, world){
    world.addEntity(makeEcs(PARAMETERS.notification, {
        notificationKey: PARAMETERS.goalComplete,
        notificationArgs: {
            key: entity.identityComponent.key
        }
    }));
};


var goalCompleteMenuRender = function(rect, entity, ui){
    entity.panelComponent.panelButtons = [];

    if (entity.panelComponent.closed || entity.panelComponent.minimized){
        return;
    }

    this.snapPanelPos(entity);

    if (this.height !== undefined){
        rect[3] = this.height;
        entity.absolutePositionComponent.size[1] = this.height;
    }

    ctx.drawImage(getImage(this.backImage), rect[0], rect[1], rect[2], rect[3]);
    
    var world = ui.world;
    
    var name = this.endGame ? 'GAME OVER' : entity.identityComponent.name;
    var completeText = this.completeText;

    var actionables = world.getActionables();

    var modifier = _.filter(actionables, function(actionable){ return actionable.menuModifierComponent; })[0];
    var randomMarker = _.filter(actionables, function(actionable){ return actionable.randomMenuComponent; })[0];
    actionables = _.filter(actionables, function(actionable){ return actionable.uiActionComponent; });
    
    if (modifier){
        name = modifier.menuModifierComponent.name ? modifier.menuModifierComponent.name : name;
        completeText = modifier.menuModifierComponent.completeText ? modifier.menuModifierComponent.completeText : completeText;
    }

    if (!(this.showPoints || completeText || actionables.length > 0)){
        entity.panelComponent.closed = true;
        return;
    }

    if (this.renderTime < this.titleTime){
        return;
    }

    ctx.fillStyle = this.textColor;

    if (this.sourceTrait){
        var traitImage = traitImageForKey(this.sourceTrait, world);
        
        if (traitImage){
            ctx.drawImage(getImage(traitImage), rect[0] + 5, rect[1] + 5);
        }
    }

    ctx.drawImage(getImage(this.image), rect[0] + 5, rect[1] + 5);
    ctx.font = (name.length > 25) ? getFont(20) : getFont(36);
    ctx.fillText(name, rect[0] + 85, rect[1] + 50);


    if (randomMarker){
        if (!this.carousel){
            this.carousel = makeEcs('randomCarousel');
            this.carousel.setMarker(randomMarker);
        }

        if (!this.carousel.animationDone){
            this.carousel.render([rect[0] + (rect[2] - 100) / 2, rect[1] + 200], this.renderTime - this.titleTime);

            if (this.carousel.animationDone){
                this.renderTime = this.titleTime;
            }
            return;
        }
    }

    if (this.renderTime < this.descriptionTime){
        return;
    }

    var y = rect[1] + 100;

    if (this.showingDescription){
        ctx.font = '20px OpenSansItalic';
        
        if (_.isArray(completeText)){
            _.each(completeText, function(line, i){
                ctx.fillText(line, rect[0] + 20, rect[1] + 120 + i * 25);
            });
            y += 20 * completeText.length;
        }
        else{
            ctx.fillText(completeText, rect[0] + 20, rect[1] + 120);
            y += completeText.length ? 25 : 0;
        }

        y = rect[1] + 200;

        var pathXOffset = 0;
        if (entity.goalCompleteMenuComponent.path && entity.goalCompleteMenuComponent.path.length > 0){
            ctx.fillText('Path Taken', rect[0] + 20, y);
            y += 50;
            pathXOffset = 200;
        }

        _.each(entity.goalCompleteMenuComponent.path, (pathPieces, i) => {
            var timePerRow = 0.25;
            var timeBeforeRender = this.descriptionTime + 0.25 * (1 + i);
            if (this.renderTime < timeBeforeRender){
                return;
            }
            var scale = (this.renderTime - timeBeforeRender) / 0.25;
            scale = min(scale, 1);

            if (pathPieces.traitImage){
                ctx.drawImage(getImage(pathPieces.traitImage), rect[0] + 20, y - 32, 60 * scale, 52 * scale);
            }
            ctx.drawImage(getImage(pathPieces.image), rect[0] + 20, y - 32, 60 * scale, 52 * scale);
            if (scale == 1){
                ctx.fillText(pathPieces.name, rect[0] + 80, y);
            }
            y += 60;
        });
        
        if (this.time < this.fullAnimationTime && completeText){
            return;
        }
    }
    else{
        ctx.font = getFont(20);

        var traits = _.chain(actionables)
            .filter(function(actionable){
                return actionable.entityManagerComponent;
            })
            .map(function(actionable){ return actionable.identityComponent.key; })
            .value();
        
        actionables = _.filter(actionables, function(actionable){
            return !(actionable.buttonControlComponent &&
                     _.contains(traits, actionable.buttonControlComponent.managerKey));
        });
        
        _.each(actionables, (entityMade) => {
            entityMade.uiDataComponent.drawImage(entityMade, world, [rect[0] + 15, y - 1]);
            
            ctx.fillText(entityMade.identityComponent.name, rect[0] + 90, y + 21);

            var x = rect[0] + rect[2] - 100;

            _.each(entityMade.uiActionComponent.getUiActions(entityMade, ui, entity), (fn, key) => {
                var rect = [x, y + 5, 85, 30];
                this.drawPanelButton(rect, key);
                entity.panelComponent.panelButtons.push([rect, fn]);

                x -= 100;
            });
            
            ctx.strokeStyle = this.insetColor;
            ctxRoundedRect([rect[0] + 5, y - 25, rect[2] - 10, 90]);
            
            y += 100;
        });
    }
    
    ctx.font = getFont(20);

    var closeRect = [rect[0] + rect[2] / 2 - 100, rect[1] + rect[3] - 50, 200, 40];

    if (entity.rebuildMenuComponent && this.showingDescription && !this.endGame){
        var rebuildFunction = entity.rebuildMenuComponent.getRebuildFunction(ui);
        if (rebuildFunction){
            var panelRect = [closeRect[0] + pathXOffset, closeRect[1] - 250, closeRect[2], 40];
            this.drawPanelButton(panelRect, 'REBUILD');
            entity.panelComponent.panelButtons.push([panelRect, rebuildFunction]);
            y += 50;
        }
    }
    
    if (actionables.length > 0){
        var flipRect;
        if (this.showingDescription){
            flipRect = [closeRect[0] + pathXOffset, closeRect[1] - 350, closeRect[2], closeRect[3] + 50];
            this.drawPanelButton(flipRect, 'SHOW UNLOCKS');
        }
        else{
            flipRect = [closeRect[0], closeRect[1] - 50, closeRect[2], closeRect[3]];
            this.drawPanelButton(flipRect, 'SHOW DESCRIPTION');
        }
        entity.panelComponent.panelButtons.push([flipRect, this.getFlipModeFunction()]);
    }


    this.drawPanelButton(closeRect, this.endGame ? 'END GAME' : 'CLOSE');
    
    if (!this.closeFunction){
        this.closeFunction = () => {
            entity.healthComponent.kill();
            entity.panelComponent.closed = true;
            if (this.endGame){
                window.location = 'select.html';
            }
        };
    }

    entity.panelComponent.panelButtons.push([closeRect, entityPanelClose]);
    
    ctx.font = BASE_FONT;
    return false;
};

var goalCompleteMenuFlipMode = function(){
    if (!this.flipModeFunction){
        var that = this;
        this.flipModeFunction = function(){
            that.showingDescription = !that.showingDescription;
        };
    }
    return this.flipModeFunction;
};


var goalCompleteMenuCreateExec = function(entity, world){
    var actionables = world.getActionables();
    if (!(this.showPoints || 
          entity.rendererComponents[0].completeText || 
          entity.rebuildMenuComponent && entity.rebuildMenuComponent.getRebuildButton(world) ||
          actionables.length > 0)){
        entity.healthComponent.kill();
    }
    
    _.chain(world.entities)
        .filter(function(worldEntity){
            return worldEntity.goalCompleteMenuComponent &&
                worldEntity.uid != entity.uid;
        })
        .each(function(worldEntity){
            worldEntity.healthComponent.kill();
        });

    if (this.source && this.source.uiDataComponent){
        entity.goalCompleteMenuComponent.path = this.source.uiDataComponent.lastPath;
        this.source = undefined;
    }
};

var traitImageForKey = function(traitKey, world){
    var trait = world.entityForUid(traitKey);
    if (trait){
        return trait.uiDataComponent.image;
    }
    else if (allArgs[traitKey]){
        return allArgs[traitKey].image;
    }
    return "trait";
};


var getRebuildButton = function(world){
    return _.chain(world.entities)
        .filter((worldEntity) => {
            return worldEntity.buttonControlComponent &&
                worldEntity.buttonControlComponent.entityKey == this.sourceKey;
        })
        .filter(function(button){
            return button.buttonControlComponent.canMakeEntity(world);                    
        })
        .first()
        .value();
};

var getRebuildFunction = function(ui){
    var world = ui.world;
    if (this.rebuildFunction === undefined){
        var button = this.getRebuildButton(ui.world);
        
        if (button){
            this.rebuildFunction = button.buttonControlComponent.getWrappedFunction(button, ui.world, ui);
        }
        else{
            this.rebuildFunction = false;
        }
    }
    
    return this.rebuildFunction;
};


var healEntities = function(entity, world){
    _.each(this.entitiesToHeal, function(val, key){
        _.each(world.entitiesForKey(key), function(entityToHeal){
            entityToHeal.healthComponent.health += val;
        });
    });
};


var healthBleedTick = function(entity){
    entity.healthComponent.health -= 1;
};


var killHealthComponent = function(){
    this.health = 0;
};


var healthModifyAmount = function(){
    return this.activated ? this.activeHealthChange : this.idleHealthChange;
};


var helpInfoPanelRender = function(rect, entity, ui){
    var panelButtons = [];

    ctx.drawImage(getImage(this.backImage), rect[0], rect[1], rect[2], rect[3]);

    ctx.fillStyle = this.textColor;
    ctx.font = getFont(36);
    var titleWidth = ctx.measureText(entity.identityComponent.name).width;
    ctx.fillText(entity.identityComponent.name, rect[0] + (rect[2] - titleWidth) / 2, rect[1] + 50);
    
    ctx.font = getFont(20);
    _.each(this.helpLines, function(line, i){
        ctx.fillText(line, rect[0] + 10, rect[1] + 150 + 50 * i);
    });

    return panelButtons;
};


var openHelpPanel = function(entity, ui){
    var sourceEntity = ui.world.entityForUid(entity.panelComponent.sourceId);

    var panelKey = _.chain(csvIdentifiers.helpInfoPanel)
        .filter(function(helpInfoPanelKey){
            var helpInfoPanelArgs = allArgs[helpInfoPanelKey];
            return _.any(helpInfoPanelArgs.components, function(component){
                return sourceEntity[component];
            });
        })
        .first()
        .value();

    ui.expandPanel(panelKey);
};


var hitRender =  function(rect, entity, ui, rectCount){
    if (this.wasHit){
        var emotionRect = getEmotionRect(rect, entity, rectCount, ui);
        ctx.fillStyle = '#ffffff';
        ctx.fillRect(emotionRect[0], emotionRect[1], emotionRect[2], emotionRect[3]);
        this.wasHit = false;
    }
};


var imageRender = function(rect, entity){
    var image = getImage(this.image);
    ctx.drawImage(image, rect[0], rect[1]);
};


var impulseFocusStopInteractWithTrain = function(entity, world, train){
    var currencies = world.makeEntities(entity, this.impulseCurrency);
    var carriage = train.trainComponent.getCurrentCarriage(train, world);
    carriage.carriageComponent.holding = _.map(currencies, getUid);
    entity.healthComponent.health = 0;
    return false;
};

var impulseFocusStopCurrenciesMadeAtPoint = function(entity){
    return [this.impulseCurrency];
};


var resetEmotionSource = function(){
    this.spawnCount = 0;
};


var instantEmotionResourceTick = function(entity, world){
    entity.focusTickResponseComponent.focusTick(entity, world);
};


var canInteractSelf = function(){
    return this.canInteract(PARAMETERS.self);
};

var canInteract = function(key){
    return _.contains(this.interactionKeys, key);
};


var makeIsaacLifecycleEntities = function(entity, world){
    this.makeLifecycleEntities(entity, world, this.isaacEntities);
};


var junctionRender = function(rect){
    ctx.fillStyle = '#ff0000';
    ctx.beginPath();
    ctx.arc(rect[0] + rect[2] / 2, rect[1] + rect[3] / 2, 10, 0, 2 * Math.PI);
    ctx.fill();
};


var junctionItemForKey = function(key){
    for (var i = 0; i < this.heldItems.length; i++){
        if (this.heldItems[i].identityComponent.key == key &&
            this.heldItems[i].healthComponent.health > 0){
            return this.heldItems[i];
        }
    }
    return undefined;
};


var killCurrencyExec = function(entity, world){
    _.chain(world.entities)
        .filter((worldEntity) => {
            return (worldEntity.identityComponent && 
                    worldEntity.identityComponent.key == this.currencyToKill &&
                    !worldEntity.pathVelocityComponent.isHeld) ||
                (worldEntity.depositComponent && 
                 worldEntity.depositComponent.deposit[this.currencyToKill] &&
                 worldEntity.depositComponent.deposit[this.currencyToKill] > 0);
        })
        .each(function(worldEntity){
            worldEntity.healthComponent.health = 0;
        });
};


var dayEndKillEntity = function(entity, world){
    entity.healthComponent.health = 0;
};


var killEntity = function(entity){
    entity.healthComponent.kill();
};


var executeKillEntities = function(entity, world){
    _.each(this.entitiesToKill, function(key){
        _.each(world.entitiesForKey(key), function(entity){
            entity.healthComponent.kill();
        });
    });
};


var killIfFlagExec = function(entity, world){
    if (this.killIfFlag && world.flags(this.killIfFlag)){
        entity.healthComponent.kill();
    }
};


var killTrainExec = function(entity, world){
    _.chain(world.entities)
        .filter((entity) => {
            return entity.trainVelocityComponent &&
                _.contains(entity.trainVelocityComponent.trainStops, this.source.uid);
        })
        .each(function(entity){
            entity.healthComponent.kill();
        });
};


var killTraitExec = function(entity, world){
    _.chain(world.entities)
        .filter((worldEntity) => {
            return (worldEntity.identityComponent && _.contains(worldEntity.identityComponent.tags, this.traitToKill)) ||
                (worldEntity.managedComponent && worldEntity.managedComponent.managerKey == this.traitToKill) ||
                (worldEntity.identityComponent && worldEntity.identityComponent.key == this.traitToKill) ||
                (worldEntity.sourceExtensionComponent && worldEntity.sourceExtensionComponent.sourceKey == this.traitToKill);
        })
        .each(function(worldEntity){ 
            if (worldEntity.healthComponent){
                worldEntity.healthComponent.health = 0;
            }
        });
};


var lostControlDeathExec = function(entity, world){
    _.chain(world.entities)
        .filter(function(worldEntity){ return worldEntity.loseControlExec; })
        .each(function(worldEntity){ worldEntity.loseControlExec.execute(worldEntity, world); });
};


var makeGainControlMenuExec = function(entity, world){
    world.makeEntities(entity, this.gainControlMenuKey + '_' + this.emotion, {
        emotion: this.emotion
    });
};


var timeForceExec = function(entity, world){
    world.game.ticksPerTick = entity.timeForceComponent.ticksPerTick;
    world.game.tickSystemWrapper.running = true;
};


var makeAllGetReplaceKeys = function(){
    return csvIdentifiers[this.csv];
}


var makeCurrencyRequest = function(entity, world){
    var request = makeEcs(this.requestKey, {
        currencyRequested: _.clone(this.currencyRequested),
        x: entity.positionComponent.x,
        y: entity.positionComponent.y
    });
    world.addEntity(request);
};


var executeMakeEntities = function(entity, world){
    var args = {};

    if (entity.identityComponent){
        args.startEntityKey = entity.identityComponent.key;
    }
    

    var madeEntities = world.makeEntities(entity, this.entitiesToMake, args);
    if (this.adjacent && madeEntities.length > 0){
        var firstEntity = madeEntities[0];
        var pos = [firstEntity.positionComponent.x, firstEntity.positionComponent.y];
        var neighbors = _.chain(world.hexMap.neighbors(pos[0], pos[1]))
            .filter(function(neighbor){
                return world.isPosBuildable(neighbor);
            })
            .shuffle()
            .value();
        
        for (var i = 1; i < madeEntities.length; i++){
            if (madeEntities[i].positionComponent){
                madeEntities[i].positionComponent.x = neighbors[i - 1][0];
                madeEntities[i].positionComponent.y = neighbors[i - 1][1];
            }
        }
    }
};


var makeIfMissingExec = function(entity, world){
    if (!world.entityForKey(this.missingKey)){
        world.makeEntities(this.source, this.entitiesToMake);
    }
};


var makeParticleEffect = function(entity, world){
    var rect =  world.hexMap.cellForPosComp(entity.positionComponent).rect;
    world.makeEntities(entity, this.particleKey, {
        effectPos: [rect[0] + rect[2] / 2, rect[1] + rect[3] / 2]
    });
};


var makeRandomMultipleGetReplaceKeys = function(entity, world){
    var numberOfProblems = (Math.random() * (this.count - this.minCount)) + this.minCount;
    var replaceKeys = [];
    for (var i = 0; i < numberOfProblems; i++){
        replaceKeys.push(this.entitiesToMake);
    }
    return replaceKeys;
};


var addExperienceToManager = function(entity, world){
    var managingEntity = world.entityForKey(this.managerKey);
    
    if (!managingEntity){
        managingEntity = _.chain(world.entities)
            .filter(function(worldEntity){
                return (worldEntity.sourceExtensionComponent || worldEntity.entityManagerComponent) &&
                    worldEntity.positionComponent.comparePos(entity.positionComponent);
            })
            .first()
            .value();
        if (managingEntity && managingEntity.sourceExtensionComponent){
            managingEntity = world.entityForKey(managingEntity.sourceExtensionComponent.sourceKey);
        }
    }

    if (managingEntity){
        managingEntity.experienceComponent.gainExp(managingEntity, world);
    }
};

var isManagingEntity = function(managingEntity){
    return (managingEntity.identityComponent && managingEntity.identityComponent.key == this.managerKey) ||
        (managingEntity.sourceExtensionComponent && managingEntity.sourceExtensionComponent.sourceKey == this.managerKey);
};


var mapSpawnPos = function(entity, world){
    return _.chain(world.hexMap.neighbors(entity.positionComponent.x,
                                          entity.positionComponent.y))
        .filter(function(neighbor){ return world.isPosBuildable(neighbor); })
        .sample()
        .value();
};

var mapSpawn = function(entity, world, spawnedEntities){
    var pos = this.mapSpawnPos(entity, world);

    if (spawnedEntities){
        var attempts = 0;
        while (_.any(spawnedEntities, function(spawnedEntity){ return spawnedEntity.positionComponent.isPos(pos); })){
            attempts += 1;
            if (attempts > 10){
                pos = undefined;
                break;
            }
            pos = this.mapSpawnPos(entity, world);
        }
    }

    if (pos){
        var key = _.isFunction(this.mapSpawnKey) ? this.mapSpawnKey(entity, world) : this.mapSpawnKey;
        if (key){
            var newEntity = makeEcs(key, {
                x: pos[0],
                y: pos[1],
                source: entity
            });
            world.addEntity(newEntity);
            this.mapPostSpawn(entity, world, newEntity);
            return newEntity;
        }
    }
    return false;
};


var markTraitNewExec = function(entity, world){
    var managerKey = entity.buttonControlComponent.managerKey;
    var manager = world.entityForKey(managerKey);
    if (manager && entity.healthComponent.health > 0){
        if (manager.interactionComponent.canInteractSelf() && entity.interactionComponent.canInteractSelf()){
            manager.entityManagerComponent.hasNew = true;
        }

        var rect =  world.hexMap.cellForPosComp(manager.positionComponent).rect;
        world.makeEntities(manager, this.particleEffect, {
            managerKey: managerKey,
            effectPos: [rect[0] + rect[2] / 2, rect[1] + rect[3] / 2]
        });

        _.chain(world.entities)
            .filter(worldEntity => worldEntity.sourceExtensionComponent && worldEntity.sourceExtensionComponent.sourceKey == managerKey)
            .each((worldEntity) => {
                var rect =  world.hexMap.cellForPosComp(worldEntity.positionComponent).rect;
                world.makeEntities(manager, this.particleEffect, {
                    managerKey: managerKey,
                    effectPos: [rect[0] + rect[2] / 2, rect[1] + rect[3] / 2]
                });
            });
    }
};


var flashbackFunction = function(entity, world){
    if (entity.entityManagerComponent.disabled){
        return;
    }
    
    var emotion = _.sample(entity.memoryComponent.memoryEmotions);

    if (!emotion){
        return;
    }

    for (var i = 0; i < this.flashbackCount; i++){
        var space = entity.entityManagerComponent.findFreeManagedSpace(entity, world);

        if (space){
            world.makeEntities(entity, 'flashback_' + emotion, {
                x: space.positionComponent.x,
                y: space.positionComponent.y,
                sourceTrait: entity.identityComponent.key
            });
        }
    }
};


var memoryTick = function(entity, world){
    var managedEntitiesCount = _.filter(world.entities, function(worldEntity){
        return worldEntity.managedComponent &&
            worldEntity.managedComponent.managerKey == entity.identityComponent.key;
    }).length;
    
    if (managedEntitiesCount > this.lastManagedCount){
        if (entity.timedDeathComponent){
            entity.timedDeathComponent.timeToDeath += 50 * managedEntitiesCount - this.lastManagedCount;
        }
        this.lastManagedCount = managedEntitiesCount;
    }
};


var memoryStartLifecycle = function(entity, world){
    this.entitiesMade = _.chain(world.entities)
        .filter(function(emotionResourceMap){
            return emotionResourceMap.emotionResourceMapComponent &&
                _.contains(entity.memoryComponent.memoryEmotions, emotionResourceMap.emotionResourceMapComponent.emotionToMap);
        })
        .map(function(emotionResourceMap){
            return world.makeEntities(entity, emotionResourceMap.emotionResourceMapComponent.emotionMappedEntities);
        })
        .flatten(true)
        .map(function(entity){
            return entity.uid;
        })
        .value();
};


var memoryFindPositionExec = function(entity, world){
    var checkedPositions = [];
    var positionsToCheck = [this.startPos];
    var blockingEntities = world.getBlockingEntities();
    var blockingPositions = _.map(blockingEntities, 
                                  blockingEntity => [blockingEntity.positionComponent.x, blockingEntity.positionComponent.y]);

    for (var j = 0; j < this.iterations; j++){
        var nextPositions = [];
        for (var i = 0; i < positionsToCheck.length; i++){
            var checkPos = positionsToCheck[i];

            var posNeighbors = world.neighbors(checkPos);
            var allNeighbors = _.chain(posNeighbors)
                .map(posNeighbor => world.neighbors(posNeighbor))
                .flatten(true)
                .filter(posNeighbor => world.isPosInBounds(posNeighbor))
                .value();

            if (_.all(allNeighbors, neighbor => !_.any(blockingPositions, blockingPos => comparePos(neighbor, blockingPos)))){
                entity.positionComponent.x = checkPos[0];
                entity.positionComponent.y = checkPos[1];
                return;
            }
            else{
                checkedPositions.push(checkPos);
                _.chain(world.neighbors(checkPos))
                    .filter(newPos => world.isPosInBounds(newPos))
                    .filter(newPos => !_.any(checkedPositions, checkedPos => comparePos(checkedPos, newPos)))
                    .filter(newPos => !_.any(nextPositions, checkedPos => comparePos(checkedPos, newPos)))
                    .each(function(newPos){ nextPositions.push(newPos); });
            }
        }
        positionsToCheck = nextPositions;
    }

    if (entity.healthComponent){ //Could not find an open position
        entity.healthComponent.kill();
    }
};


var memoryConstruct = function(entity, world){
    var newEntity = makeEcs(this.constructionIdentifier, {
        x: entity.positionComponent.x,
        y: entity.positionComponent.y,
        sourceTrait: this.sourceKey
    });
    world.addEntity(newEntity);
};


var memoryFadeExec = function(entity, world){
    var newTrait = world.transformTrait(this.sourceKey, this.newTrait, true, true);
    if (newTrait){
        newTrait.fadingMemoryComponent.oldTrait = this.sourceKey;
    }
};


var memoryGoalCompleteExec = function(entity, world){
    if (!entity.managedComponent){
        return;
    }

    var source = world.entityForKey(entity.managedComponent.managerKey);

    if (!source){
        return;
    }
    
    if (this.flareMemory){
        source.memoryComponent.flashbackFunction(source, world);
    }
    if (this.killMemory){
        source.healthComponent.kill()
    }

    _.chain(world.entities)
        .filter((worldEntity) => (worldEntity.memoryFadeComponent &&
                                  worldEntity.memoryFadeComponent.sourceKey == source.identityComponent.key))
        .each((worldEntity) => {
            worldEntity.timedDeathComponent.timeToDeath += this.bonusTime;
        })
};


var triggerFlashback = function(entity, world){
    if (Math.random() < this.flashbackChance){
        var memory = world.entityForKey(entity.managedComponent.managerKey);
        if (memory && memory.memoryComponent){
            memory.memoryComponent.flashbackFunction(memory, world);
        }
    }
};


var menuTutorialTextRender = function(rect, entity, ui){
    this.hasDrawn = true;
    this.menuEntity = _.chain(ui.world.entities)
        .filter((worldEntity) => (worldEntity.panelComponent &&
                                  !(worldEntity.panelComponent.closed || worldEntity.panelComponent.minimized) &&
                                  worldEntity.identityComponent &&
                                  worldEntity.identityComponent.key.indexOf(this.entityToMark) == 0))
        .first()
        .value();
    
    if (!this.menuEntity){
        return;
    }

    this.shownOnce = true;

    rect = this.menuEntity.absolutePositionComponent.getAbsPosRect();

    ctx.fillStyle = '#ffffff';
    ctx.font = getFont((this.text.length > 10) ? 20 : 30);
    var textWidth = ctx.measureText(this.text).width;
    ctx.fillText(this.text, min(rect[0] + this.xOffset, width - textWidth - 10), rect[1] + this.yOffset - 20);
    ctx.font = BASE_FONT;
    
    if (this.shouldDrawArrow){
        this.drawArrow([rect[0] + this.xOffset + 100, rect[1] + this.yOffset - 10],
                       [rect[0] + this.xOffset, rect[1] + this.yOffset]);
    }
};


var menuTutorialDone = function(entity, world){
    var renderComponent = entity.rendererComponents[0];
    if (renderComponent.hasDrawn && renderComponent.menuEntity == undefined){
        if (!renderComponent.shownOnce){
            entity.tutorialTextComponent.nextEntities = undefined;
        }
        return true;
    }
    return false;
};


var nameRender = function(rect){
    ctx.fillStyle = '#ffffff';
    ctx.fillText(this.name, rect[0] + rect[2] / 3, rect[1] + rect[3] / 4);
};


var nextDayEntityDayEnd = function(entity, world){
    _.each(this.execs, function(exec){
        exec.execute(entity, world);
    });
    entity.healthComponent.health = 0;
};


var noFocusNotifierRender = function(rect, entity, ui){
    var self = ui.entityForKey(PARAMETERS.self);
    if (self.focusMakingComponent.focusUsing === 0){
        var entitiesToMark = [];
        var goalButtons = [];

        _.chain(ui.world.entities)
            .filter(worldEntity => {
                if (worldEntity.worldGoalComponent){
                    return true;
                }
                else if (worldEntity.damageComponent && worldEntity.damageComponent.charges < worldEntity.damageComponent.maxCharges){
                    return true;
                }
                return false;
            })
    }
};


var notificationSerialize = function(entity, serialized){
    delete serialized.notificationFunctions;
    return serialized;
};


var addNotificationCurrency = function(entity, args, world){
    world.addCost(entity.notificationCurrencyComponent.currencyName, 
                  entity.notificationCurrencyComponent.notificationCurrency);
};


var notifyEntityMakerFn = function(entity, args, world){
    if (_.all(this.notificationArgs, function(val, key){
        return args[key] == val;
    })){
        this.count -= 1;
        if (this.count <= 0){
            this.count = this.impulseCount;
            world.makeEntities(entity, this.entitiesToMake);
        }
    }
};


var openMenuExecute = function(entity, world){
    if (entity.interactionComponent && entity.interactionComponent.canInteract(PARAMETERS.self)){
        var markerEntity = makeEcs(this.markerKey, {
            entityUid: entity.uid,
            x: entity.positionComponent.x,
            y: entity.positionComponent.y
        });
        world.addEntity(markerEntity);
    }
};


var openMenuOnClick = function(entity, world, ui){
    ui.openPanelAtPos('#' + entity.identityComponent.key, entity.positionComponent);
};


var openMenuMarkerRender = function(rect, entity, ui){
    var entityToClick = ui.world.entityForUid(entity.openMenuMarkerComponent.entityUid);
    if (entityToClick){
        entityToClick.clickComponent.onClick(entityToClick, ui.world, ui);
        entity.healthComponent.health = 0;
    }
};


var generateOpinionIsaacs = function(args){
    var isaacEntities = (args.isaacEntities !== undefined) ? args.isaacEntities : [];
    var mangle = function(key){
        return mangleIdentityWithTrait(key, args.personHeld);
    };
    
    isaacEntities = isaacEntities.concat(_.map(args.buttonKeys, mangle));
    isaacEntities = isaacEntities.concat(_.map(args.opinionActions, mangle));
    
    return isaacEntities;
};


var opinionTick = function(entity, world){
    if (tickComp(this)){
        var person = world.entityForKey(entity.personHeldComponent.personHeld);
        world.makeEntities(person, this.emotionsToMake);
    }
};


var opinionConditionalExec = function(entity, world){
    if (_.any(this.opinions, (opinionKey) => {
        return _.any(world.entitiesForKey(opinionKey), (opinion) => {
            opinion.personHeldComponent.personHeld == this.person;
        });
    })){
        var source = world.entityForUid(this.sourceId);
        world.makeEntities(source, this.entitiesToMake);
    }
};


var pairTransformGetReplaceKeys = function(entity, world){
    var trait1 = world.entityForKey(this.trait1);
    var trait2 = world.entityForKey(this.trait2);

    if (!(trait1 && trait2)){
        return false;
    }

    var trait1InteractionKeys = trait1.interactionComponent.interactionKeys;
    var trait2InteractionKeys = trait2.interactionComponent.interactionKeys;

    var setTrait = function(trait, interactionKeys){
        trait.interactionComponent.interactionKeys = interactionKeys;
        
        _.chain(world.entities)
            .filter(worldEntity => (worldEntity.buttonControlComponent &&
                                    worldEntity.buttonControlComponent.managerKey == trait.identityComponent.key))
            .each(worldEntity => {
                worldEntity.interactionComponent.interactionKeys = interactionKeys;
            });

        _.chain(world.entities)
            .filter(worldEntity => (worldEntity.healthComponent &&
                                    worldEntity.managedComponent &&
                                    worldEntity.managedComponent.managerKey == trait.identityComponent.key))
            .each(worldEntity => worldEntity.healthComponent.kill());
    }

    setTrait(trait1, trait2InteractionKeys);
    setTrait(trait2, trait1InteractionKeys);

    return false;
};


var expandPanel = function(){
    this.minimized = false;
    this.closed = false;
};


var panelUiClick = function(pos, entity, ui){
    _.each(entity.panelComponent.panelButtons, function(pair){
        var rect = pair[0];
        var fn = pair[1];

        if (containsPos(rect, pos)){
            if (ui.gameProperties.settings.sfx){
                var pingEffect = getAudio.get('sounds/beep.ogg');
                pingEffect.play();
            }

            fn(entity, ui);
        }
    });
};


var snapPanelPos = function(entity){
    entity.absolutePositionComponent.y = min(entity.absolutePositionComponent.y, 678 - entity.absolutePositionComponent.size[1]);
    entity.absolutePositionComponent.y = max(entity.absolutePositionComponent.y, 50 - entity.absolutePositionComponent.size[1]);
    entity.absolutePositionComponent.x = min(entity.absolutePositionComponent.x, width - entity.absolutePositionComponent.size[0]);
    entity.absolutePositionComponent.x = max(entity.absolutePositionComponent.x, 50 - entity.absolutePositionComponent.size[0]);
};

var outPanelRender = function(rect, entity, ui, rectCount, timeToNextTick, lastInterval){
    entity.panelComponent.panelButtons = [];
    if (entity.panelComponent.closed){
        this.renderTime = 0;
        return;
    }

    this.snapPanelPos(entity);

    if (this.renderTime < this.fullAnimationTime){
        this.renderTime += lastInterval;
    }

    if (this.renderTime < this.animatingTime){
        this.renderTime += lastInterval;
        var fraction = this.renderTime / this.animatingTime;
        ctx.drawImage(getImage(this.backImage), rect[0], rect[1], rect[2] * fraction, rect[3] * fraction);
        return;
    }

    var panelButtons = this.specificRender(rect, entity, ui);
    
    if (panelButtons !== false){
        entity.panelComponent.panelButtons = panelButtons;
        
        if (entity.panelComponent.panelButtons !== undefined){
            ctx.font = getFont(20);
            
            var closeRect = [rect[0] + rect[2] / 2 - 50, rect[1] + rect[3] - 50, 100, 40];
            this.drawPanelButton(closeRect, 'CLOSE');
            
            entity.panelComponent.panelButtons.push([closeRect, entityPanelClose]);
        }
    }

    ctx.font = BASE_FONT;
};

var drawPanelButton = function(rect, text){
    ctx.strokeStyle = this.insetColor;
    ctxRoundedRect(rect);
    
    ctx.fillStyle = this.textColor;
    var measure = ctx.measureText(text);
    var textWidth = measure.width;
    ctx.fillText(text, rect[0] + rect[2] / 2 - textWidth / 2, rect[1] + rect[3] / 2 + this.yTextOffset);
};

var drawImageButton = function(rect, img){
    ctx.strokeStyle = this.insetColor;
    ctxRoundedRect(rect);

    img = getImage(img);

    ctx.drawImage(img, rect[0] + (rect[2] - img.width) / 2, rect[1] + (rect[3] - img.height) / 2);
}

var entityPanelClose = function(entity, ui){
    entity.panelComponent.closed = true;
    
    if (entity.panelComponent.sourceId){
        var building = ui.world.entityForUid(entity.panelComponent.sourceId);
        if (building && building.menuCloseComponent){
            building.menuCloseComponent.execute(building, ui.world);
        }
    }
};

var drawHelpButton = function(entity, rect){
    ctx.font = getFont(16);
    var helpRect = [rect[0] + rect[2] - 25, rect[1], 25, 25];
    ctx.strokeStyle = this.insetColor;
    ctx.beginPath();
    ctx.moveTo(helpRect[0], helpRect[1]);
    ctx.lineTo(helpRect[0], helpRect[1] + helpRect[3]);
    ctx.lineTo(helpRect[0] + helpRect[2], helpRect[1] + helpRect[3]);
    ctx.stroke();

    ctx.fillStyle = this.textColor;
    ctx.fillText('?', helpRect[0] + 7, helpRect[1] + helpRect[3] - 5);
    return [helpRect, entity.helpPanelComponent.openHelpPanel];
};


var paramPathExec = function(entity, world){
    var source = world.entityForKey(this.startKey);
    var end = world.entityForKey(this.endKey);
    if (source && end){
        var trainPath = makeEcs(this.trainPathKey, {
            trainStops: [end.uid],
            trainStartId: source.uid,
            focusCost: 0,
            stoppable: false
        });
        world.addEntity(trainPath);
    }
};


var makeParticle = function(){
    return {
        timeLeft: Math.random() * 0.5 + 0.5,
        radial: (Math.random() * 0.5 + 0.5) * this.effectRadius,
        angular: Math.random() * 2 * Math.PI
    };
};

var particleEffectRender = function(rect, entity, ui, rectCount, timeToNextTick, lastInterval){
    while (this.startParticles > 0){
        this.startParticles -= 1;
        this.particles.push(this.makeParticle());
    }
    this.timeToCheck -= lastInterval;
    if (this.timeToCheck < 0){
        this.timeToCheck += this.impulseTimeToCheck;
        this.particles.push(this.makeParticle());
    }
    
    this.particles = _.filter(this.particles, function(particle){
        particle.timeLeft -= lastInterval;
        return (particle.timeLeft > 0);
    });


    var image = getImage(this.image);
    _.each(this.particles, (particle) => {
        var x = particle.radial * Math.cos(particle.angular);
        var y = particle.radial * Math.sin(particle.angular);
        ctx.drawImage(image, this.effectPos[0] + x, this.effectPos[1] + y);
    });
};


var pathConnectRender = function(rect, entity, ui, rectCount, timeToNextTick, lastInterval){
    if (ui.world.lostControl()){
        return;
    }

    var endRect = ui.world.hexMap.cellForPosComp({x: entity.pathMoveComponent.endPosition[0],
                                                  y: entity.pathMoveComponent.endPosition[1]}).rect;
    var startPos = [rect[0] + rect[2] / 2, rect[1] + rect[3]/ 2];
    var endPos = [endRect[0] + endRect[2] / 2, endRect[1] + endRect[3] / 2];

    if (this.timeDrawn < 0.33){
        endPos = [
            startPos[0] + (endPos[0] - startPos[0]) * this.timeDrawn * 3,
            startPos[1] + (endPos[1] - startPos[1]) * this.timeDrawn * 3,
        ]
        this.timeDrawn += lastInterval;
    }

    ctx.strokeStyle = '#0099ff';
    ctx.beginPath();
    ctx.moveTo(startPos[0], startPos[1]);
    ctx.lineTo(endPos[0], endPos[1]);
    ctx.stroke();
};


var duplicatePathExec = function(entity, world){
    var chk = _.any(world.entities, function(worldEntity){
        return worldEntity.pathMoveComponent &&
            ((comparePos(worldEntity.pathMoveComponent.startPosition, entity.pathMoveComponent.startPosition) &&
              comparePos(worldEntity.pathMoveComponent.endPosition, entity.pathMoveComponent.endPosition)) ||
             (comparePos(worldEntity.pathMoveComponent.startPosition, entity.pathMoveComponent.endPosition) &&
              comparePos(worldEntity.pathMoveComponent.endPosition, entity.pathMoveComponent.startPosition)));
            
    });
    if (chk){
        entity.healthComponent.health = 0;
    }
};


var pathButtonRender = function(rect, entity, ui){
    if (!this.pathButtonFunction){
        this.pathButtonFunction = function(){
            ui.world.togglePathBuilding();
        };
    }
    ui.informLine(PARAMETERS.buildPanel, {
        buttonFunction: this.pathButtonFunction,
        text: 'path'
    }, PARAMETERS.baseButtonName);
};


var pathMakerGetReplaceKeys = function(entity, world){
    var start = world.entityForKey(this.startKey);
    var end = world.entityForKey(this.endKey);
    world.connectEntities(start, end);
};


var pathStopRender = function(rect, entity, ui, rectCount, timeToNextTick, lastInterval){
    if (this.timeDrawn < this.renderTime){
        this.timeDrawn += lastInterval;
        
        var scale = 1;
        if (this.timeDrawn < this.renderTime / 2){
            scale += 0.25 * this.timeDrawn * 2 / this.renderTime;
        }
        else{
            scale += 0.25 * (this.renderTime - this.timeDrawn) * 2 / this.renderTime;
        }
        var newRect = [rect[0] - rect[2] * (scale - 1) / 2, rect[1] - rect[3] * (scale - 1) / 2, rect[2] * scale, rect[3] * scale];
        ctx.drawImage(getImage(this.image), newRect[0], newRect[1], newRect[2], newRect[3]);
    }
    else{
        var image = getImage(this.isActive ? this.activeImage : this.image);
        var x = rect[0] + this.getWobble(lastInterval);

        ctx.drawImage(image, x, rect[1]);

        if (this.outlineImage){
            ctx.drawImage(getImage(this.outlineImage), x, rect[1]);
            this.outlineImage = false;
            
            this.renderTrainIcon(lastInterval, x, rect[1]);
        }
        else{
            if (this.blinkingOutlineImage){
                this.blinkingOutlineTime -= lastInterval;
                if (this.blinkingOutlineTime > 0){
                    ctx.globalAlpha = this.blinkingOutlineTime;
                    ctx.drawImage(getImage(this.blinkingOutlineImage), x, rect[1]);
                    ctx.globalAlpha = 1;
                }
                else{
                    this.blinkingOutlineImage = false;
                }
            }
        }
    }
    this.isActive = false;
};

var renderTrainIcon = function(lastInterval, x, y){
    ctx.fillStyle = this.trainColor;
    ctx.beginPath();
    ctx.arc(x + 30, y + 26, 4, 0, 2 * Math.PI);
    ctx.fill();
};


var getAllPersonality = function(){
    return _.map(this.heldOpinions, getEntityKey).concat(this.personalityTraits);
};

var addOpinionToPersonality = function(opinionKey, entity, world){
    var opinion = makeEcs(opinionKey, {
        personHeld: entity.identityComponent.key
    });
    world.addEntity(opinion);
    this.heldOpinions.push(opinion);
};


var personCreateExec = function(entity, world){
    entity.personalityComponent.heldOpinions = _.map(entity.personalityComponent.heldOpinions, function(opinion){
        opinion = makeEcs(opinion, {
            personHeld: entity.identityComponent.key
        });
        world.addEntity(opinion);
        return opinion;
    });
};


var personKillExec = function(entity, world){
    _.each(entity.personalityComponent.heldOpinions, function(opinion){
        opinion.healthComponent.health = 0;
    });
};


var derivePersonGoalCurrencies = function(currencies, personKey){
    var derivedCurrencies = {};
    _.each(currencies, function(val, key){
        var currencyKey;
        
        if (_.contains(csvIdentifiers.currency, key)){
            currencyKey = key;
        }
        else{
            currencyKey = key + '_' + personKey;
        }

        derivedCurrencies[currencyKey] = val;
    });
    return derivedCurrencies;
};


var addOpinionExec = function(entity, world){
    var person = world.entityForKey(entity.personHeldComponent.personHeld);

    var opinionsToMake = [];
    
    if (this.opinionToAdd){
        opinionsToMake.push(this.opinionToAdd);
    }

    var existingOpinions = [];

    _.each(person.personalityComponent.heldOpinions, function(opinion){
        existingOpinions.push(opinion.identityComponent.key);
        _.each(opinion.opinionComponent.successOpinions, function(val, key){
            if (percentCheck(val)){
                opinionsToMake.push(key);
            }
        });
    });

    _.chain(opinionsToMake)
        .uniq()
        .filter(function(key){
            return !_.contains(existingOpinions, key);
        })
        .each(function(key){
            person.personalityComponent.addOpinionToPersonality(key, person, world);
        });
};


var placedTowerGetReplaceKeys = function(entity, world, args){
    var managedEntities = _.filter(world.entities.concat(world.entitiesToAdd), worldEntity => worldEntity.managedComponent);
    var spaces = _.chain(world.entities)
        .filter(worldEntity => (
            ((worldEntity.identityComponent && worldEntity.identityComponent.key == this.sourceTrait) ||
             (worldEntity.sourceExtensionComponent && worldEntity.sourceExtensionComponent.sourceKey == this.sourceTrait)) &&
                (_.all(managedEntities, managedEntity => !managedEntity.positionComponent.comparePos(worldEntity.positionComponent)))
        ))
        .map(worldEntity => [worldEntity.positionComponent.x, worldEntity.positionComponent.y])
        .value();
    var space = _.min(spaces, pos => Math.abs(pos[0] - this.x) + Math.abs(pos[1] - this.y));
    
    if (space){
        args.x = space[0];
        args.y = space[1];
        args.sourceKey = this.sourceTrait;
        args.sourceTrait = this.sourceTrait;
        return this.tower;
    }
    else{
        return false;
    }
};


var addPoints = function(entity, args){
    entity.pointCounterComponent.points += args.points;
};


var setPos = function(pos){ 
    this.x = pos[0];
    this.y = pos[1]; 
};

var isPos = function(pos){
    return this.x == pos[0] && this.y == pos[1];
};

var compComparePos = function(positionComponent){
    return this.x == positionComponent.x &&
        this.y == positionComponent.y;
};

var assertPosition = function(val, idx){
    if (val !== undefined){
        if (val < 0 || val >= PARAMETERS.size[idx]){
            console.log('impossible position ' + val);
            throw "Impossible position " + val;
        }
    }
    return val;
};

var getAsPos = function(){
    return [this.x, this.y];
};


var printKillExec = function(entity, world){
    console.log(entity);
};


var progressRender = function(rect, entity){
    ctx.fillStyle = '#ffffff';
    var percentComplete = entity.focusPathStopComponent.percentComplete(entity);
    if (percentComplete > 0){
        ctx.fillText('' + percentComplete + '%', rect[0] + this.progressX, rect[1] + this.progressY);
    }
};


var improveQuality = function(entity, world){
    var qualityEntity;
    if (this.qualityEntity){
        qualityEntity = world.entityForKey(this.qualityEntity);
    }
    else{
        qualityEntity = _.filter(world.entities, function(worldEntity){
            return worldEntity.qualityComponent &&
                worldEntity.managedComponent &&
                worldEntity.managedComponent.managerKey == entity.personHeldComponent.personHeld;
        })[0];
    }

    if (this.checkAgainstQuality){
        qualityEntity.qualityComponent.quality += this.qualityDiff;
    }

    if (this.entitiesToMake){
        world.makeEntities(qualityEntity, this.entitiesToMake);
    }
};


var qualityResourceTick = function(entity, world){
    if (this.lastQuality <= this.qualityThreshold && entity.qualityComponent.quality > this.qualityThreshold){
        entity.resourceComponent.resourceKeys = this.highQualityResources;
        world.makeEntities(entity, this.flipEntities);
    }
    else if (this.lastQuality > this.qualityThreshold && entity.qualityComponent.quality <= this.qualityThreshold){
        entity.resourceComponent.resourceKeys = this.lowQualityResources;
        _.each(this.flipEntities, function(flipEntity){
            _.each(world.entitiesForKey(flipEntity), function(worldEntity){
                worldEntity.healthComponent.health = 0;
            });
        });
    }

    this.lastQuality = entity.qualityComponent.quality;
};


var qualityRender = function(rect, entity){
    ctx.fillStyle = '#ffffff';
    ctx.fillText(entity.qualityComponent.quality, rect[0], rect[1] + rect[3])
};


var randomCarouselSetMarker = function(marker){
    var goodCount = marker.randomMenuComponent.goodCount;
    var badCount = marker.randomMenuComponent.badCount;
    
    var badToAdd = 0;
    this.carousel = [];
    for (var i = 0; i < goodCount; i++){
        this.carousel.push(this.goodImage);
        badToAdd += badCount;
        while (badToAdd >= goodCount){
            this.carousel.push(this.badImage);
            badToAdd -= goodCount;
        }
    }
    
    this.goodResult = marker.randomMenuComponent.goodResult;
};

var randomCarouselRender = function(pos, renderTime){
    var finalImage = this.goodResult ? this.goodImage : this.badImage;
    if (this.flatDisplayStartTime){
        if (renderTime > this.flatDisplayStartTime + 1){
            this.animationDone = true;
        }
    }

    var timeToRender = this.flatDisplayStartTime ? this.flatDisplayStartTime : renderTime;
    var index = timeToRender / 0.3;
    var flatIndex = Math.floor(index);
    var fraction = index - flatIndex;
    flatIndex = this.carousel.length - flatIndex;
    while (flatIndex < 0){
        flatIndex += this.carousel.length;
    }
        
    var images = _.map(_.range(4), (i) => {
        var idx = (flatIndex + i) % this.carousel.length;
        return this.carousel[idx];
    });

    if (renderTime > this.animationTime && images[2] == finalImage && !this.flatDisplayStartTime){
        this.flatDisplayStartTime = renderTime;
    }

    var rect = [pos[0], pos[1], this.imgSize[0], this.imgSize[1]];

    
    var sheight = fraction * rect[3];

    ctx.drawImage(getImage(images[0]), 
                  0, rect[3] - sheight, rect[2], sheight,
                  rect[0], rect[1], rect[2], sheight);

    ctx.drawImage(getImage(images[1]), rect[0], rect[1] + sheight);
    ctx.drawImage(getImage(images[2]), rect[0], rect[1] + rect[3] + sheight);
    ctx.drawImage(getImage(images[3]),
                  0, 0, rect[2], rect[3] - sheight,
                  rect[0], rect[1] + 2 * rect[3] + sheight, rect[2], rect[3] - sheight);
    ctx.drawImage(getImage(this.frameImage), rect[0], rect[1]);
};


var randomIfNoneGetReplaceKeys = function(entity, world){
    if (_.all(this.keys, key => !world.entityForKey(key))){
        return _.sample(this.keys);
    }
};


var randomOpenPositionExec = function(entity, world){
    var pos;
    if (entity.managedComponent){
        var allEntities = world.entities.concat(world.entitiesToAdd);
        pos = _.chain(allEntities)
            .filter(function(worldEntity){
                return entity.managedComponent.isManagingEntity(worldEntity) &&
                    !_.any(allEntities, function(chkEntity){
                        return (chkEntity.managedComponent || chkEntity.goalTrackComponent) &&
                            chkEntity.positionComponent.comparePos(worldEntity.positionComponent);
                    });
            })
            .map(function(worldEntity){
                return [worldEntity.positionComponent.x, worldEntity.positionComponent.y];
            })
            .sample()
            .value();
    }
    
    if (!pos){
        if (this.startEntityKey){
            this.startPos = world.findStartPos(this.startEntityKey);
        }
        if (this.startPos && (this.startPos[0] === undefined || this.startPos[1] === undefined)){
            this.startPos = undefined;
        }
        pos = world.randomOpenPosition(entity.positionComponent.planeOfExistence, this.startPos, this.minSelfDist);
    }
    
    entity.positionComponent.setPos(pos);
};


var randomReplaceGetReplaceKeys = function(entity, world){
    var convertEntityGroup = function(entityGroup, entities){
        if (_.isString(entityGroup)){
            entities[entityGroup] = 1;
            return 1;
        }
        else if (_.isArray(entityGroup)){
            _.each(entityGroup, function(entityKey){ entities[entityKey] = 1; });
            return entityGroup.length;
        }
        else{
            _.each(entityGroup, function(val, key){
                entities[key] = val;
            });
            return _.reduce(entityGroup, addFunction, 0);
        }
    };
    
    var entities = {};

    var goodCount = convertEntityGroup(this.goodEntities, entities);
    var badCount = convertEntityGroup(this.badEntities, entities);

    var chanceSum = _.reduce(entities, addFunction, 0);
    var randVal = Math.random() * chanceSum;
    var keys = _.keys(entities);
    var keyToReturn = undefined;
    for (var i = 0; i < keys.length; i++){
        var key = keys[i];
        var chance = entities[key];
        if (chance > randVal){
            keyToReturn = key;
            break;
        }
        else{
            randVal -= chance;
        }
    }

    if (keyToReturn === undefined){
        throw 'random replace failed';
        return false;
    }

    var goodEntities = {};
    convertEntityGroup(this.goodEntities, goodEntities);
    var goodResult = goodEntities[keyToReturn] !== undefined;

    
    if (this.addReplaceMarker){
        world.makeEntities(entity, PARAMETERS.randomMenuMarker, {
            goodCount: goodCount,
            badCount: badCount,
            goodResult: goodResult
        });
    }

    return keyToReturn;
};


var currenciesAtResource = function(entity, world){
    return _.chain(this.resourceKeys)
        .map((resourceKey) =>  _.filter(world.entities, (worldEntity) => (
                worldEntity.identityComponent &&
                worldEntity.identityComponent.sourceId == entity.uid &&
                worldEntity.identityComponent.key == resourceKey &&
                worldEntity.positionComponent.comparePos(entity.positionComponent) &&
                !worldEntity.pathVelocityComponent.isHeld
            )))
        .flatten(true)
        .value();
};


var resourceInteractWithTrain = function(entity, world, train){
    var trainRequirements = train.trainComponent.calculateTrainCurrencies(train, world);

    var currenciesAtResource = entity.resourceComponent.currenciesAtResource(entity, world);

    for (var i = 0; i < entity.resourceComponent.resourceKeys.length; i++){
        var resourceKey = entity.resourceComponent.resourceKeys[i];

        if (trainRequirements[resourceKey] === undefined){
            continue;
        }

        var carriage = train.trainComponent.getCurrentCarriage(train, world);
        var waiting = true;

        _.chain(currenciesAtResource)
            .filter((worldEntity) => !_.contains(carriage.carriageComponent.holding, worldEntity.uid))
            .first(1)
            .each(function(worldEntity){
                carriage.carriageComponent.holding.push(worldEntity.uid);
                worldEntity.pathVelocityComponent.isHeld = true;
                waiting = false;
            });

        return waiting;
    }
    return false;
};

var resourceCurrenciesMadeAtPoint = function(entity){
    return entity.resourceComponent.resourceKeys;
};


var resourceFocusTick = function(entity, world){
    if (entity.resourceComponent.resourceCount !== undefined && entity.resourceComponent.resourceCount <= 0){
        entity.healthComponent.kill()
        return;
    }

    var resourceCountAtPoint = _.filter(world.entities, (worldEntity) => (
        worldEntity.identityComponent &&
            _.contains(entity.resourceComponent.resourceKeys, worldEntity.identityComponent.key) &&
            worldEntity.positionComponent.comparePos(entity.positionComponent)
    )).length;

    if (resourceCountAtPoint < 5){
        world.makeEntities(entity, entity.resourceComponent.resourceKeys, {
            emotionTags: this.emotionTags,
            destination: this.destination,
            firstTick: this.firstTick
        });

        this.firstTick = false;
                
        if (entity.managedComponent){
            entity.managedComponent.addExperienceToManager(entity, world);
        }

        if (entity.resourceComponent.resourceCount){
            entity.resourceComponent.resourceCount -= 1;
        }
        
        world.makeEntities(entity, PARAMETERS.towerFlash);

        if (entity.resourceComponent.resourceEntities){
            world.makeEntities(entity, entity.resourceComponent.resourceEntities);
        }
    }
    else{
        this.focusTickHook(entity, world);
    }
};


var killResourceCurrencies = function(entity, world){
    _.each(entity.resourceComponent.currenciesAtResource(entity, world), 
           function(currencyEntity){
               currencyEntity.healthComponent.kill();
           });
};


var resourceModifierGetTowerForTickModifierRender = function(entity, world){
    return world.entityForUid(entity.resourceModifierComponent.towerId);
};


var revertMemoryExec = function(entity, world){
    var source = world.entityForUid(this.sourceId);
    var sourceTrait = world.entityForKey(source.managedComponent.managerKey);
    var memory = world.transformTrait(sourceTrait.identityComponent.key,
                                      sourceTrait.fadingMemoryComponent.oldTrait, true);
    if (memory){
        _.each(_.range(3), function(){
            memory.memoryComponent.flashbackFunction(memory, world);
        });
    }
};


var risingTextRender = function(rect, entity, ui, rectCount, timeToNextTick, lastInterval){
    this.currentHeight += this.height * lastInterval / this.effectTime;
    if (this.currentHeight > this.height){
        entity.healthComponent.kill();
    }
    else{
        ctx.fillStyle = '#ffffff';
        ctx.globalAlpha = 1 - 0.5 * (this.currentHeight / this.height);
        ctx.fillText(this.text, this.effectPos[0], this.effectPos[1] - this.currentHeight);
        ctx.globalAlpha = 1;
    }
};


var selfDamageEffectRender = function(rect, entity, ui, rectCount, timeToNextTick, lastInterval){
    if (this.timeDrawn > this.animationTime){
        entity.healthComponent.kill();
    }

    this.timeDrawn += lastInterval;
    var dist = (this.timeDrawn / (3 * this.animationTime)) + 1;
    dist *= 60;

    ctx.fillStyle = '#ff0000';
    for (var i = 0; i < 3; i++){
        var angle = i * Math.PI / 6;
        var x = dist * Math.cos(angle);
        var y = dist * Math.sin(angle);
        ctx.fillRect(x, y, 2, 2);
    }
};


var selfExtensionSetEmotion = function(emotion){
    this.emotion = emotion;
    this.emotionLife = 30;
};


var selfExtensionRender = function(rect, entity){
    if (entity.selfExtensionComponent.emotion){
        ctx.fillStyle = EMOTION_FILLSTYLES[entity.selfExtensionComponent.emotion];
    }
    else {
        ctx.fillStyle = '#ffffff';
    }

    ctx.fillRect(rect[0] + 10, rect[1] + 10, 20, 20);
};


var selfExtensionTick = function(entity){
    if (entity.selfExtensionComponent.emotionLife){
        entity.selfExtensionComponent.emotionLife -= 1;
        if (entity.selfExtensionComponent.emotionLife <= 0){
            entity.selfExtensionComponent.emotion = undefined;
        }
    }
};


var setFlagExec = function(entity, world){
    world.flags[this.flag] = this.flagValue;
};


var settingsPanelRender = function(rect, entity, ui){
    var panelButtons = [];
    var that = this;

    ctx.drawImage(getImage(this.backImage), rect[0], rect[1], rect[2], rect[3]);
    
    ctx.fillStyle = '#000000';
    ctx.font = getFont(40);
    ctx.fillText('OPTIONS', rect[0] + rect[2] / 2 - ctx.measureText('OPTIONS').width / 2, 45);
    ctx.font = BASE_FONT;
    
    ctx.fillText('FULLSCREEN', rect[0] + 10, rect[1] + 85);

    var insetColor = (p) => p ? '#ff0000' : '#000000';

    var fullscreenRect = [rect[0] + 100, rect[1] + 70, 100, 30];

    if (!this.fullscreenFunction){
        this.fullscreenFunction = function(){
            if (ui.gameProperties.settings.fullscreen){
                document.webkitExitFullscreen();
            }
            else{
                canvas.webkitRequestFullscreen();
            }
            ui.gameProperties.settings.fullscreen = !ui.gameProperties.settings.fullscreen;
            ui.gameProperties.save();
        };
    }

    this.drawPanelButton(fullscreenRect, 'Toggle');
    panelButtons.push([fullscreenRect, this.fullscreenFunction]);

    ctx.fillText('SOUND', rect[0] + 10, rect[1] + 135);
    var soundRect = [rect[0] + 100, rect[1] + 120, 40, 40];

    if (!this.soundFunction){
        this.soundFunction = function(){
            if (ui.gameProperties.settings.music){
                ui.gameProperties.backgroundAudio.pause();
            }
            else{
                ui.gameProperties.backgroundAudio.play();
            }

            ui.gameProperties.settings.music = !ui.gameProperties.settings.music;
            ui.gameProperties.save();
        };
    }

    var soundVols = [0.05, 0.1, 0.2];
    if (!this.soundVolFunctions || this.soundVolFunctions.length === 0){
        this.soundVolFunctions = _.map(soundVols, function(vol){
            return function(){
                ui.gameProperties.settings.musicVolume = vol;
                ui.gameProperties.backgroundAudio.volume = vol;

                if (!ui.gameProperties.settings.music){
                    ui.gameProperties.settings.music = true;
                    ui.gameProperties.backgroundAudio.play();
                }

                ui.gameProperties.save();
            };
        });
    }

    this.insetColor = insetColor(!ui.gameProperties.settings.music);
    this.drawImageButton(soundRect, 'mute');
    panelButtons.push([soundRect, this.soundFunction]);
    
    _.each(this.soundVolFunctions, function(volFunction, i){
        var iRect = [soundRect[0] + 60 * (i + 1), soundRect[1], soundRect[2], soundRect[3]];
        that.insetColor = insetColor(ui.gameProperties.settings.music && ui.gameProperties.musicVolume == soundVols[i]);
        that.drawImageButton(iRect, 'sound' + i);
        panelButtons.push([iRect, volFunction]);
        that.insetColor = '#000000'
    });

    ctx.fillText('SFX', rect[0] + 10, rect[1] + 185);
    var sfxRect = [rect[0] + 100, rect[1] + 170, 40, 40];
    
    if (!this.sfxFunction){
        this.sfxFunction = function(){
            ui.gameProperties.settings.sfx = !ui.gameProperties.settings.sfx;
            ui.gameProperties.save();
        }
    }

    var sfxVols = [0.25, 0.5, 0.75];
    if (!this.sfxVolFunctions || this.sfxVolFunctions.length === 0){
        this.sfxVolFunctions = _.map(sfxVols, function(vol){
            return function(){
                getAudio.changeVol(vol);
                ui.gameProperties.settings.soundVolume = vol;
                ui.gameProperties.settings.sfx = true;
                ui.gameProperties.save();
            };
        })
    }

    this.insetColor = insetColor(!ui.gameProperties.settings.sfx);
    this.drawImageButton(sfxRect, 'mute')
    panelButtons.push([sfxRect, this.sfxFunction]);
    
    _.each(this.sfxVolFunctions, function(volFunction, i){
        var iRect = [sfxRect[0] + 60 * (i + 1), sfxRect[1], sfxRect[2], sfxRect[3]];
        that.insetColor = insetColor(ui.gameProperties.settings.sfx && ui.gameProperties.settings.soundVolume == sfxVols[i]);
        that.drawImageButton(iRect, 'sound' + i);
        that.insetColor = '#000000';
        panelButtons.push([iRect, volFunction]);
    });

    var difficultySetting = ui.world.entityForKey(PARAMETERS.difficultySetting);

    if (!this.difficultyFunctions || this.difficultyFunctions.length === 0){
        this.difficultyFunctions = _.map(_.range(4), function(i){
            return function(){
                difficultySetting.difficultySettingComponent.difficulty = i;
            }
        })
    }

    ctx.fillText('DIFFICULTY', rect[0] + 10, rect[1] + 235)
    _.each(this.difficultyFunctions, (difficultyFunction, i) => {
        var displayIdx = this.difficultyFunctions.length - i;
        var iRect = [rect[0] + 50 + 50 * displayIdx, rect[1] + 220, 40, 40];
        this.insetColor = insetColor(difficultySetting.difficultySettingComponent.difficulty == i);
        this.drawPanelButton(iRect, '' + displayIdx);
        this.insetColor = '#000000';
        panelButtons.push([iRect, difficultyFunction]);
    });

    if (!this.quitFunction){
        this.quitFunction = function(){
            nw.App.quit();
        };
    }

    var quitRect = [rect[0] + 100, rect[1] + 270, 100, 30];
    this.drawPanelButton(quitRect, 'Quit');
    panelButtons.push([quitRect, this.quitFunction]);

    return panelButtons;
};


var singleUseMakeTrain = function(entity, world){
    if (this.trainsToMake > 0){
        this.trainsToMake -= 1;
        return this.baseMakeTrain(entity, world);
    }
    else{
        entity.healthComponent.kill()
        return undefined;
    }
};


var sourceCurrenciesNeededAtPoint = function(){
    return csvIdentifiers.creep;
};


var creepAccept = function(entity, world, creep){
    entity.healthComponent.health -= creep.creepComponent.selfDamage;
    world.makeEntities(creep, this.creepHitEntities, {
        emotion: creep.creepComponent.emotion
    })
};


var focusEfficiency = function(){
    return 1 - (this.focusUsing / (this.focusCount + 2));
};


var sourceExtensionOnClick = function(entity, world, ui){
    var source = world.entityForKey(entity.sourceExtensionComponent.sourceKey);
    source.clickComponent.onClick(source, world, ui);
};


var sourceExtensionRender = function(rect, entity, ui, rectCount, timeToNextTick, lastInterval){
    if (_.isArray(this.image)){
        this.image = _.sample(this.image);
    }
    var world = ui.world;
    var source = world.entityForKey(entity.sourceExtensionComponent.sourceKey);
    var image = getImage(source.entityManagerComponent.disabled ? this.disabledImage : this.image);

    var xDiff = this.getWobble(lastInterval);
    var x = rect[0] + xDiff;

    ctx.drawImage(image, x, rect[1]);

    var button = world.currentMakingEntity;
    if (button){
        if (button.buttonControlComponent.checkMakePos(world, entity.positionComponent.getAsPos())){
            ctx.drawImage(getImage(this.outlineImage), x, rect[1]);
        }
    }

    if (source.entityManagerComponent.hasNew){
        this.timeToBlink -= lastInterval;
        if (this.timeToBlink > 0){
            ctx.drawImage(getImage(this.newImage), x, rect[1]);
        }
        else if (this.timeToBlink < -0.1){
            this.timeToBlink += 1;
        }
    }
};


var checkParticleEffectAlive = function(entity, world){
    return world.entityForUid(this.sourceId) !== undefined;
};


var spellRender = function(rect, entity, ui){
    if (entity.spellComponent.spellButtonFunction === undefined){
        entity.spellComponent.spellButtonFunction = makeWrappedSpellFunction(entity, ui.world);
    }
    ui.addLine('#spells', {
        line: [
            {
                buttonFunction: entity.spellComponent.spellButtonFunction,
                text: entity.identityComponent.name
            },
            dictString(entity.spellComponent.spellCost)
        ]
    });
};

var makeWrappedSpellFunction = function(entity, world){
    return function(){
        if (world.player.calculatedCurrencyComponent.satisfiesCurrencyReq(entity.spellComponent.spellCost)){
            world.currentSpell = entity;
        }
    };
};


var starParticleEffectRender = function(rect, entity, ui, rectCount, timeToNextTick, lastInterval){
    if (this.timeDrawn > this.scaleTime){
        entity.healthComponent.kill();
    }

    this.timeDrawn += lastInterval;
    var dist = (this.timeDrawn / (3 * this.scaleTime)) + 1;
    var image = getImage(this.image);

    for (var i = 8; i < 11; i++){
        var angle = i * Math.PI / 6;
        var x = 60 * dist * Math.cos(angle);
        var y = 60 * dist * Math.sin(angle);
        ctx.drawImage(image, this.effectPos[0] + x, this.effectPos[1] + y);
    }
};


var checkSteamAchievement = function(entity, args){
    if (args.key == entity.steamAchievementComponent.keyToCheck){
        console.log('Completed achievement: ' + entity.steamAchievementComponent.steamAchievementKey);
        if (STEAM){
            greenworks.activateAchievement(entity.steamAchievementComponent.steamAchievementKey, trueFunction);
        }
    }
};


var swallowOpinionCreate = function(entity, world){
    entity.swallowOpinionComponent.previousOpinions = _.chain(world.entities)
        .filter((worldEntity) => {
            return (worldEntity.opinionComponent &&
                    worldEntity.personHeldComponent.personHeld == entity.personHeldComponent.personHeld &&
                    worldEntity.identityComponent.key != entity.identityComponent.key &&
                    !_.contains(this.swallowExceptions, worldEntity.identityComponent.key));
        })
        .map(function(worldEntity){
            worldEntity.healthComponent.kill();
            return worldEntity.identityComponent.key;
        })
        .value();
};


var killPersonGoals = function(entity, world){
    _.chain(world.entities)
        .filter(function(worldEntity){ 
            return worldEntity.personHeldComponent &&
                worldEntity.worldGoalComponent;
        })
        .each(function(worldEntity){
            worldEntity.healthComponent.kill();
        });
};


var swallowOpinionKill = function(entity, world){
    world.makeEntities(entity, entity.swallowOpinionComponent.previousOpinions);
};


var checkTickContinue = function(world, entity){
    return !(this.currency.will && 
             this.currency.will < 0 && 
             world.player.calculatedCurrencyComponent.currencies.will < 0);
};


var timedDeathRender = function(rect, entity){
    if (entity.timedDeathComponent){
        ctx.fillStyle = '#ffffff';
        var timeLeft = Math.floor(entity.timedDeathComponent.timeToDeath / PARAMETERS.ticksPerTick[0]);
        ctx.fillText('' + timeLeft, rect[0] + rect[2] / 2 - 15, rect[1] + rect[3] - 2);
    }
};


var timedDifficultyModify = function(entity, world){
    var difficulty = world.entityForKey(PARAMETERS.difficultySetting).difficultySettingComponent.difficulty;
    entity.timedDeathComponent.timeToDeath += Math.floor(difficulty * entity.timedDeathComponent.timeToDeath / 3);
};


var timedMakeEntities = function(entity, world){
    if (entity.timedDeathComponent && entity.timedDeathComponent.timeToDeath <= 0){
        world.makeEntities(entity, this.timedOutEntities);
    }
};


var timedRender = function(rect, entity, ui, rectCount, timeToNextTick, lastInterval){
    if (this.timeDrawn < this.renderTime){
        this.timeDrawn += lastInterval;
        this.renderWhileTimed(rect, entity, ui, rectCount, timeToNextTick, lastInterval);
    }
    else{
        var idx = entity.rendererComponents.indexOf(this);
        if (this.nextRenderComponent){
            entity.rendererComponents[idx] = this.nextRenderComponent;
        }
        else{
            entity.rendererComponents.splice(idx, 1);
        }
    }
};


var timedScaleRenderWhenTimed = function(rect, entity){
    var img = getImage(this.image);
    var scale = 1 + 0.5 * (this.renderTime - this.timeDrawn) / this.renderTime;
    var newRect = [rect[0] - rect[2] * (scale - 1) / 2, rect[1] - rect[3] * (scale - 1) / 2, rect[2] * scale, rect[3] * scale];
    ctx.drawImage(img, newRect[0], newRect[1], newRect[2], newRect[3]);
};


var toastRender = function(rect, entity, ui, rectCount, timeToNextTick){
    ctx.fillStyle = '#ff0000';
    ctx.beginPath();
    ctx.arc(rect[0], rect[1], 5, 0, 2 * Math.PI);
    ctx.fill();
};


var toastClick = function(entity, world, ui){
    ui.openPanelAtPos(this.panelKey, entity.positionComponent);
    entity.healthComponent.health = 0;
};


var toastNotification = function(entity, args){
    if (args.key == entity.clickComponent.panelKey){
        entity.healthComponent.health = 0;
    }
};


var damageRender = function(rect, entity, ui, rectCount, timeToNextTick, lastInterval){
    if (entity.damageComponent){
        if (entity.damageComponent.ticks <= 0){
            ctx.fillStyle = '#ffffff';
        }
        else{
            ctx.fillStyle = '#ffff00';
            var cooldownImage = getImage('cooldown' + min(entity.damageComponent.ticks, 2));
            ctx.drawImage(cooldownImage, rect[0], rect[1]);
        }

        if (entity.damageComponent.charges === 0){
            this.blinkTimer += lastInterval
        }
        else{
            this.blinkTimer = 0;
        }

        if (this.blinkTimer < 1.8){
            var i;

            var width = (entity.damageComponent.maxCharges == 5) ? 4 : 6;

            for (i = 0; i < entity.damageComponent.charges; i++){
                ctx.fillRect(rect[0] + 17 + (width * 2 - 2) * i, rect[1] + rect[3] - 12, width, 10);
            }
            
            ctx.fillStyle = '#aaaaaa';
            for (; i < entity.damageComponent.maxCharges; i++){
                ctx.fillRect(rect[0] + 17 + (width * 2 - 2) * i, rect[1] + rect[3] - 12, width, 10);
            }
        }
        else if (this.blinkTimer > 2){
            this.blinkTimer -= 2;
        }
    }
};


var towerTick = function(entity){
    _.each(entity.rendererComponents, function(renderComponent){
        if (renderComponent.activated > 0){
            renderComponent.activated -= 1;
        }
    });
        
    this.timeToAttrition -= 1;

    if (this.timeToAttrition <= 0){
        this.timeToAttrition = PARAMETERS.dayTicks;
        if (this.damageComponent){
            this.damageComponent.charges -= 1;
            if (this.damageComponent.charges < 0){
                this.damageComponent.charges = 0;
            }
        }
    }
};


var currencyActionForTower = function(entity, world){
    return _.chain(world.entities)
        .filter(function(chkEntity){
            return chkEntity.currencyActionComponent && 
                _.contains(chkEntity.currencyActionComponent.towerIds, entity.uid);
        })
        .first()
        .value();
};

var possibleCurrencyActionForTower = function(entity, world){
    return _.chain(world.entities)
        .filter(function(chkEntity){
            return chkEntity.currencyActionComponent &&
                chkEntity.currencyActionComponent.towerKey == entity.identityComponent.key;
        })
        .first()
        .value();
};

var canDamageTower = function(entity){
    return entity.damageComponent === undefined;
};


var towerCreateExec = function(entity, world){
    _.chain(world.entities)
        .filter(function(worldEntity){
            return worldEntity.currencyActionComponent &&
                worldEntity.currencyActionComponent.towerKey == entity.identityComponent.key &&
                worldEntity.currencyActionComponent.towerIds.length > 0;
        })
        .each(function(worldEntity){
            worldEntity.currencyActionComponent.nextIds.push(entity.identityComponent.uid);
        });
};


var towerCurrenciesNeededAtPoint = function(entity, world){
    var currencyAction = entity.towerComponent.possibleCurrencyActionForTower(entity, world);

    return currencyAction ? currencyAction.currencyActionComponent.currenciesRequired : [];
};

var towerCurrenciesMadeAtPoint = function(entity, world){
    var currencyAction = entity.towerComponent.possibleCurrencyActionForTower(entity, world);

    return currencyAction ? currencyAction.currencyActionComponent.currenciesToMake : [];
};

var towerInteractWithTrain = function(entity, world, train){
    var currencyAction = entity.towerComponent.possibleCurrencyActionForTower(entity, world);
    var trainPath = world.entityForUid(train.trainVelocityComponent.trainPath);

    if (!currencyAction){
        train.healthComponent.kill();
        if (trainPath){
            trainPath.healthComponent.kill();
        }
        return false;
    }

    var entitiesToLoad = [];

    var currenciesNeeded = train.trainComponent.calculateTrainCurrencies(train, world, 1);

    var isLoading = _.any(this.currenciesMadeAtPoint(entity, world), function(currencyKey){
        return currenciesNeeded[currencyKey] && (currenciesNeeded[currencyKey] > 0);
    });

    if (isLoading && this.currenciesExistAtPoint(entity, world)){
        var currenciesMadeAtPoint = _.countBy(this.currenciesMadeAtPoint(entity, world), _.identity);

        var currenciesAtPoint = _.filter(world.entities, function(worldEntity){
            return worldEntity.pathVelocityComponent &&
                worldEntity.positionComponent.comparePos(entity.positionComponent);
        });

        entitiesToLoad = _.filter(currenciesAtPoint, function(currency){
            var key = currency.identityComponent.key;
            if (currenciesMadeAtPoint[key] && currenciesMadeAtPoint[key] > 0){
                currenciesMadeAtPoint[key] -= 1;
                return true;
            }
            return false;
        });
    }
    else{
        if (entity.focusTickResponseComponent.isReady || entity.damageComponent){
            var currenciesToConsume = this.currenciesNeededAtPoint(entity, world);

            var currencyCounts = _.countBy(currenciesToConsume, _.identity);
            
            
            var carriage = train.trainComponent.getCurrentCarriage(train, world);
            carriage.carriageComponent.holding = _.filter(carriage.carriageComponent.holding, function(holdId){
                var holdItem = world.entityForUid(holdId);
                if (!holdItem){
                    return false;
                }
                if (currencyCounts[holdItem.identityComponent.key] > 0){
                    currencyCounts[holdItem.identityComponent.key] -= 1;
                    holdItem.healthComponent.kill();
                    return false;
                }
                return true;
            });
            

            currencyAction = entity.towerComponent.currencyActionForTower(entity, world);
            
            if (!currencyAction){
                train.healthComponent.kill();
                var trainPath = world.entityForUid(train.trainVelocityComponent.trainPath);
                if (trainPath){
                    trainPath.healthComponent.kill();
                }
                return false;
            }
            
            entitiesToLoad = world.makeEntities(entity, currencyAction.currencyActionComponent.currenciesToMake);
            if (currencyAction.currencyActionComponent.entitiesOnConvert){
                world.makeEntities(entity, currencyAction.currencyActionComponent.entitiesOnConvert);
            }
                        
            
            if (entity.damageComponent && entity.damageComponent.charges < entity.damageComponent.maxCharges){
                entity.damageComponent.charges += 1;
                if (entity.damageComponent.charges >= entity.damageComponent.maxCharges){
                    trainPath.healthComponent.kill();
                    train.healthComponent.kill();
                }
            }

            _.each(this.convertExecutables, function(convertExecutable){
                convertExecutable.execute(entity, world);
            });

            var rect =  world.hexMap.cellForPosComp(entity.positionComponent).rect;
            world.makeEntities(entity, this.risingTextKey, {
                text: currencyAction.identityComponent.name,
                effectPos: [rect[0], rect[1]]
            })
            
            entity.focusTickResponseComponent.isReady = false;
        }
        else{
            entity.focusTickResponseComponent.isWaiting = true;
            return true;
        }
    }
    
    if (entitiesToLoad.length > 0){
        var carriage = train.trainComponent.getCurrentCarriage(train, world);
        _.each(entitiesToLoad, function(entityToLoad){
            carriage.carriageComponent.holding.push(getUid(entityToLoad));
        });
    }

    entity.managedComponent.addExperienceToManager(entity, world);

    return false;
};


var towerFocusTick = function(entity, world){
    if (this.isWaiting){
        this.isReady = true;
        this.isWaiting = false;
        world.makeEntities(entity, PARAMETERS.towerFlash);
    }
};


var canBuildTower = function(world){
    var managingEntity = world.entityForKey(this.managerKey);
    
    if (!managingEntity){
        return false;
    }

    if (!managingEntity.entityManagerComponent.canMakeManagedEntity(managingEntity, world)){
        return false;
    }

    var existingTowerCount = _.reduce(this.limitingKeys, function(memo, val){
        var existingTowers = world.entitiesForKey(val);
        existingTowers = _.filter(existingTowers, 
                                  existingTower => _.intersection(existingTower.interactionComponent.interactionKeys, 
                                                                  managingEntity.interactionComponent.interactionKeys).length > 0);
        return memo + existingTowers.length;
    }, 0);

    if (existingTowerCount >= this.maxTowerCount){
        return false;
    }

    return true;
};

var buildTower = function(world, pos, entity, interactionKey){
    entity.buttonControlComponent.isNew = false;
    var manager = world.entityForKey(this.managerKey);
    if (manager){
        manager.entityManagerComponent.checkForNewManaged(manager, world);
    }
    
    var args = {
        x: pos[0],
        y: pos[1],
        sourceKey: this.managerKey,
        sourceTrait: this.managerKey
    };
    if (interactionKey){
        args.interactionKeys = [interactionKey];
    }

    var entity = makeEcs(this.constructionKey, args);

    return entity;
};

var checkTowerMakePos = function(world, pos){
    var that = this;
    var isManagedPos = _.any(world.entities, function(entity){
        return (entity.identityComponent && entity.identityComponent.key == that.managerKey ||
                (entity.sourceExtensionComponent && entity.sourceExtensionComponent.sourceKey == that.managerKey)) &&
            entity.positionComponent.isPos(pos);
    });

    var isFreePos = !_.any(world.entities, function(entity){
        return entity.managedComponent && entity.positionComponent.isPos(pos);
    });
    
    return isManagedPos && isFreePos;
};


var killTowerButtonExec = function(entity, world){
    if (this.killTowersOnDeath){
        _.each(world.entitiesForKey(entity.buttonControlComponent.entityKey), function(tower){
            tower.healthComponent.health = 0;
        });
    }
};


var towerFlashRender = function(rect, entity, ui, rectCount, timeToDeath, lastInterval){
    if (this.timeToDeath <= 0){
        entity.healthComponent.kill();
    }
    else{
        var imageIndex = Math.floor(this.timeToDeath / 0.167);
        var image = getImage(this.images[imageIndex]);
        ctx.drawImage(image, rect[0], rect[1]);
        
        this.timeToDeath -= lastInterval;
    }
};


var getTowerForTickModifierRender = function(entity, world){
    return world.entityForUid(entity.tickModifyComponent.towerId);
};

var towerTickModifierRender = function(rect, entity, ui){
    var tower = this.getTowerForTickModifierRender(entity, ui.world);
    if (!tower){
        return;
    }

    var rect = ui.world.hexMap.cellForPosComp(tower.positionComponent).rect;
    ctx.drawImage(getImage(this.image), rect[0] + rect[2] / 2, rect[1] + rect[3] / 2);
};


var calculateTrainCurrencies = function(entity, world, offset){
    var trainPath = world.entityForUid(entity.trainVelocityComponent.trainPath);
    if (!trainPath){
        return {};
    }
    var currencies = [];
    offset = offset ? offset : 0;
    var startIndex = entity.trainVelocityComponent.stopIndex + offset;
    for (var i = startIndex; i < trainPath.trainPathComponent.trainStops.length; i++){
        var trainStop = trainPath.trainPathComponent.trainStops[i];
        var stop = world.entityForUid(trainStop);
        if (stop){
            currencies = currencies.concat(stop.focusPathStopComponent.currenciesNeededAtPoint(stop, world));
        }
    }
    
    return _.countBy(currencies, _.identity);
};

var getCurrentCarriage = function(entity, world){
    return world.entityForUid(this.carriage);
};

var getCurrenciesHeld = function(entity, world){
    var carriage = world.entityForUid(this.carriage);

    return _.chain(carriage.carriageComponent.holding)
        .map(function(currencyId){ return world.entityForUid(currencyId); })
        .filter(_.identity)
        .value();
};


var trainCreateExec = function(entity, world){
    var startEntity = world.entityForUid(this.trainStartId);

    if (!startEntity){
        entity.healthComponent.health = 0;
        return;
    }

    entity.positionComponent.x = startEntity.positionComponent.x;
    entity.positionComponent.y = startEntity.positionComponent.y;
    
    var trainPath = world.entityForUid(this.trainPath);
    var start = [entity.positionComponent.x, entity.positionComponent.y];


    var stops = _.map(trainPath.trainPathComponent.trainStops, path => _.clone(path));
    entity.trainVelocityComponent.trainStops = stops;
    
    entity.trainVelocityComponent.currentPath = entity.trainVelocityComponent.allPaths[0];
    
    var carriage = world.makeEntities(entity, this.carriageKey, {
        x: entity.positionComponent.x,
        y: entity.positionComponent.y,
    })[0];
                
    entity.trainComponent.carriage = carriage.uid;

    var existingCarriages = _.filter(world.entities, worldEntity => worldEntity.carriageComponent);

    _.each(entity.trainComponent.calculateTrainCurrencies(entity, world), function(val, key){
        var startCurrency = _.chain(world.entities)
            .filter(function(entity){
                return entity.identityComponent && 
                    entity.identityComponent.key == key &&
                    entity.positionComponent.comparePos(startEntity.positionComponent) &&
                    !_.any(existingCarriages, carriage => _.contains(carriage.carriageComponent.holding, entity.uid));
            })
            .first()
            .value();

        if (startCurrency){
            carriage.carriageComponent.holding.push(startCurrency.uid);
        }
    });
};


var trainKillExec = function(entity, world){
    var carriage = world.entityForUid(entity.trainComponent.carriage);
        
    carriage.healthComponent.kill();

    _.chain(carriage.carriageComponent.holding)
        .map(function(holdingId){ return world.entityForUid(holdingId); })
        .each(function(currency){
            if (currency){
                currency.healthComponent.health = 0;
            }
        });
};


var trainRender = function(rect, entity, ui, rectCount, timeToNextTick, lastInterval){
    var dx = 15;
    var dy = 15;
    
    if (ui.game.tickSystemWrapper.running){
        var subfraction = 1 - (timeToNextTick * ui.game.ticksPerTick * TICKS_PER_SECOND);
        var fraction = (ui.game.tickSystemWrapper.ticks + subfraction) / 10;

        if (ui.world.lostControl()){
            fraction = 0;
        }
        
        var nextPos = entity.trainVelocityComponent.currentPath[0];
        var drawPos = [rect[0], rect[1]];
        if (nextPos){
            nextPos = ui.world.hexMap.hexMap[nextPos[0]][nextPos[1]].rect;
            drawPos = [drawPos[0] + (nextPos[0] - drawPos[0]) * fraction, 
                       drawPos[1] + (nextPos[1] - drawPos[1]) * fraction];
        }

        _.each(entity.trainComponent.getCurrenciesHeld(entity, ui.world), function(currency, i){
            _.each(currency.rendererComponents, function(renderComponent){
                renderComponent.renderPos = [drawPos[0] + rect[2] / 2 - 8,
                                             drawPos[1] + rect[3] / 2 - 8 + i * 10];
            });
        });
        
        drawPos = [drawPos[0] + rect[2] / 2 - dx, drawPos[1] + rect[3] / 2 - dy];
        drawPos = [Math.floor(drawPos[0]), Math.floor(drawPos[1])];
        
        this.timeDrawn += lastInterval;
        var chkPreviousPos = (pos) => (!_.any(this.previousPositions, (prevPos) => comparePos(prevPos, pos)));
        if (this.timeDrawn > 0.1){
            if (chkPreviousPos(drawPos)){
                this.previousPositions.push(drawPos);
            }
            else{
                var nextPos = [drawPos[0], drawPos[1] - 10];
                if (!chkPreviousPos(nextPos)){
                    nextPos = [drawPos[0] - 10, drawPos[1] + 10];
                    if (!chkPreviousPos(nextPos)){
                        nextPos = [drawPos[0] + 10, drawPos[1] + 10];
                    }
                }
                this.previousPositions.push(nextPos);
            }
            this.linePos = drawPos;
            this.previousPositions = _.last(this.previousPositions, 3);
            
            this.timeDrawn = 0;
        }
    }
        
    ctx.fillStyle = '#ede9e6';
    ctx.strokeStyle = this.trainColor;
    
    _.each(this.previousPositions, function(prevPos){
        ctx.beginPath();
        ctx.arc(prevPos[0] + 15, prevPos[1] + 15, 3, 0, 2 * Math.PI);
        ctx.fill();
        
        ctx.beginPath();
        ctx.arc(prevPos[0] + 15, prevPos[1] + 15, 4, 0, 2 * Math.PI);
        ctx.stroke();
    });
};


var trainPanelRender = function(rect, entity, ui){
    var panelButtons = [];

    var world = ui.world;
    var path = world.entityForUid(entity.panelComponent.sourceId);
    
    if (!path){
        entity.panelComponent.closed = true;
    }
    else if (entity.panelComponent.minimized){
        entity.panelComponent.closed = true;
    }
    else {
        entity.absolutePositionComponent.size[1] = 350 + path.trainPathComponent.trainStops.length * 60;
        rect = entity.absolutePositionComponent.getAbsPosRect();
        
        ctx.drawImage(getImage(this.backImage), rect[0], rect[1], rect[2], rect[3]);

        ctx.fillStyle = this.textColor;
        ctx.font = getFont(28);

        var header = 'Train';

        var width = ctx.measureText(header).width;
        ctx.fillText(header, rect[0] + (rect[2] - width) / 2, rect[1] + 60);

        panelButtons.push(this.drawHelpButton(entity, rect));

        ctx.font = getFont(20);
        ctx.fillSyle = this.textColor;
        var y;
        _.each(path.trainPathComponent.trainStops, (stopId, i) => {
            if (i !== 0){
                ctx.drawImage(getImage(this.arrowImage), rect[0] + 120, y + 10);
            }

            y = rect[1] + 100 + (i + 1) * 60;

            var stop = world.entityForUid(stopId);

            if (!stop){
                return;
            }

            if (stop.uiDataComponent){
                stop.uiDataComponent.drawImage(stop, world, [rect[0] + 30, y - 30]);
            }
            else if (stop.identityComponent && stop.identityComponent.image){
                ctx.drawImage(getImage(stop.identityComponent.image), rect[0] + 30, y - 30);
            }
            
            ctx.fillText(stop.identityComponent.name, rect[0] + 100, y);
        });

        ctx.font = getFont(18);
        var buttonWidth = 200;

        if (!path.trainPathComponent.canMakeTrain(path, world) && PARAMETERS.buildablePaths){
            var connectRect = [rect[0] + (rect[2] - buttonWidth) / 2, y + 40, buttonWidth, 40];
            this.drawPanelButton(connectRect, 'CONNECT TRACK');
            panelButtons.push([connectRect, trainPanelStartTrack]);
        }
        else if (this.isConnectingTrack){
            entity.panelComponent.closed = true;
        }

        var killPathRect = [rect[0] + (rect[2] - buttonWidth) / 2, y + 100, buttonWidth, 40];
        this.drawPanelButton(killPathRect, 'STOP TRAIN');
        panelButtons.push([killPathRect, path.trainPathComponent.getKillTrainPath(path, world)]);

        ctx.font = BASE_FONT;
    }
    return panelButtons;
};

var trainPanelStartTrack = function(entity, ui){
    entity.rendererComponents[0].isConnectingTrack = true;
    entityPanelStartTrack(entity, ui);
};


var makeTrain = function(entity, world){
    if (this.allPaths.length === 0){
        var startEntity = world.entityForUid(this.trainStartId);

        if (!startEntity){
            entity.healthComponent.kill();
            return;
        }
        
        var start = [startEntity.positionComponent.x, startEntity.positionComponent.y];

        var paths = _.filter(world.entities, function(worldEntity){
            return worldEntity.pathMoveComponent;
        });

        _.each(this.trainStops, (stopId) => {
            var stop = world.entityForUid(stopId);
            stop = [stop.positionComponent.x, stop.positionComponent.y];
            var path = world.ensurePath(start, stop, paths);
            this.allPaths.push(path);
            start = stop;
        });
    }

    var train = makeEcs(this.trainKey, {
        trainPath: entity.uid,
        trainStartId: this.trainStartId,
        trainColor: this.trainColor,
        allPaths: _.map(this.allPaths, path => _.clone(path))
    });
    entity.trainPathComponent.trainId = train.uid;
    world.addEntity(train);
    return train;
};

var canMakeTrain = function(entity, world){
    if (this.requiresFullPath){
        var paths = world.getAllPaths();
        
        var stops = [this.trainStartId].concat(this.trainStops);
        
        stops = _.map(stops, function(stopId){ return world.entityForUid(stopId); });
        
        if (_.any(stops, function(stop){ return stop === undefined; })){
            return false;
        }

        var getPosition = function(entity){
            return [entity.positionComponent.x, entity.positionComponent.y];
        };
        for (var i = 0; i < stops.length - 1; i++){
            var start = getPosition(stops[i]);
            var stop = getPosition(stops[i + 1]);
            if (determinePath(start, stop, paths) === undefined){
                return false;
            }
        }
    }
    return true;
};

var getKillTrainPath = function(entity, world){
    if (this.killTrainPath === undefined){
        this.killTrainPath = function(){
            entity.healthComponent.kill();
            _.chain(world.entities)
                .filter(function(worldEntity){
                    return worldEntity.trainVelocityComponent &&
                        worldEntity.trainVelocityComponent.trainPath == entity.uid;
                })
                .each(function(worldEntity){
                    worldEntity.healthComponent.kill();
                });
        };
    }
    return this.killTrainPath;
};


var trainPathWarningRender = function(rect, entity, ui){
    var world = ui.world;

    if (!entity.trainPathComponent.canMakeTrain(entity, world)){
        var lastStop = world.entityForUid(_.last(entity.trainPathComponent.trainStops));
        
        if (!lastStop){
            return;
        }

        var lastStopRect = world.hexMap.cellForPosComp(lastStop.positionComponent).rect;
        ctx.fillStyle = '#ffff00';
        ctx.fillRect(lastStopRect[0] + lastStopRect[2] - 10, lastStopRect[1] + lastStopRect[3], 10, 10);
    }
};


var trainPathRenderWrapper = function(entity, world, fn){
    _.each(entity.trainPathComponent.trainStops, function(stopId){
        var trainStop = world.entityForUid(stopId);
        if (!trainStop){
            return;
        }

        fn(trainStop)
    });
};

var trainPathTowerActiveRender = function(rect, entity, ui){
    trainPathRenderWrapper(entity, ui.world, (trainStop) => {
        if (trainStop.rendererComponents[0].activeImage){
            trainStop.rendererComponents[0].isActive = true;
        }
    });
};


var trainPathSecondRender = function(rect, entity, ui){
    trainPathRenderWrapper(entity, ui.world, (trainStop) => {
        trainStop.rendererComponents[0].outlineImage = this.image;
        trainStop.rendererComponents[0].trainColor = entity.trainPathComponent.trainColor;
    });
};


var trainPathMenuCloseExec = function(entity, world){
    if (!PARAMETERS.buildablePaths && !entity.trainPathComponent.canMakeTrain(entity, world)){
        entity.healthComponent.kill();
        var lastStop = _.last(entity.trainPathComponent.trainStops);
        lastStop = world.entityForUid(lastStop);
        if (lastStop.constructionComponent && !lastStop.constructionComponent.ticked){
            lastStop.healthComponent.kill();
        }
    }
};


var blinkTrainStopsExec = function(entity, world){
    trainPathRenderWrapper(entity, world, (trainStop) => {
        var renderComponent = trainStop.rendererComponents[0];
        renderComponent.blinkingOutlineImage = this.image;
        renderComponent.blinkingOutlineTime = 1;
    });
};


var setTrainColorExec = function(entity, world){
    var existingPathColors = _.chain(world.entities)
        .filter(worldEntity => (worldEntity.trainPathComponent &&
                                worldEntity.trainPathComponent.stoppable &&
                                _.isEqual(worldEntity.interactionComponent.interactionKeys,
                                          entity.interactionComponent.interactionKeys)))
        .map(trainPath => trainPath.trainPathComponent.trainColor)
        .value();
    var possibleColors = [
        '#00ccff',
        '#ff9900',
        '#9900cc',
        '#00ff99',
        '#00ffff',
        '#00ffaa',
        '#0099ff'
    ];

    var color = _.chain(possibleColors)
        .filter(possibleColor => !_.contains(existingPathColors, possibleColor))
        .first()
        .value();

    color = color ? color : '#ffffff';

    entity.trainPathComponent.trainColor = color;
};


var trainPathCircRender = function(rect, entity, ui, rectCount, timeToNextTick, lastInterval){
    if (entity.trainPathComponent.allPaths.length === 0){
        return;
    }

    var startEntity = ui.world.entityForUid(entity.trainPathComponent.trainStartId);

    if (this.renderTime < 100){
        this.renderTime += lastInterval;
    }

    var paths = [];

    _.each(entity.trainPathComponent.allPaths, function(newPath){
        paths = paths.concat(newPath);
    });

    ctx.strokeStyle = entity.trainPathComponent.trainColor
    ctx.beginPath();
    
    var startPos;
    
    if (startEntity && startEntity.focusMakingComponent){
        var cellRect = ui.world.hexMap.cellForPosComp(startEntity.positionComponent).rect;
        startPos = [cellRect[0] + cellRect[2] / 2, cellRect[1] + cellRect[3] / 2];
        ctx.moveTo(startPos[0], startPos[1]);
    }
    
    _.each(paths, (pos, i) => {
        if (i < this.renderTime * 8){
            var cellRect = ui.world.hexMap.cellForPosComp({x: pos[0], y: pos[1]}).rect;
            var pos = [cellRect[0] + cellRect[2] / 2, cellRect[1] + cellRect[3] / 2];

            if (i === 0 && !startEntity){
                ctx.moveTo(pos[0], pos[1]);
            }
            else{
                ctx.lineTo(pos[0], pos[1]);
            }
        }
    });

    ctx.stroke();
};


var getPathCurrenciesNeeded = function(entity, world){
    if (!this.memoizedCurrenciesNeeded){
        var currenciesNeeded = {};
        
        var stayAlive = _.all(this.stops, function(stopId){
            var stop = world.entityForUid(stopId);
            if (!stop){
                return false;
            }
            stop.focusPathStopComponent.updateCurrencyDictWithStop(stop, world, currenciesNeeded);
            return true;
        });

        if (!stayAlive){
            return {};
        }

        currenciesNeeded = _.chain(currenciesNeeded)
            .map(function(val, key){
                return [key, val];
            })
            .filter(function(pair){
                return pair[1] > 0;
            })
            .value();
        
        this.memoizedCurrenciesNeeded = dictFromPairs(currenciesNeeded);
    }
    return this.memoizedCurrenciesNeeded;
};

var makeTrainPath = function(entity, world){
    entity.healthComponent.kill();

    this.stops.reverse();

    if (this.stops.length > 0){
        var selfEntity = world.entityForKey(PARAMETERS.self);
        var trainPath = makeEcs(this.trainPathKey, {
            trainStartId: selfEntity.uid,
            trainStops: this.stops,
        });
        world.addEntity(trainPath);

        if (!trainPath.trainPathComponent.canMakeTrain(trainPath, world)){
            var panel = makeEcs(PARAMETERS.trainPanel, {
                key: 'trainPath_' + trainPath.uid,
                sourceId: trainPath.uid
            });
            panel.panelComponent.expandPanel();
            world.addEntity(panel);
        }

        _.each(world.entitiesForKey(PARAMETERS.trainPathMakingPanel), function(panel){
            panel.panelComponent.closed = true;
        })
    }
};

var addTrainStop = function(entity, world, stop){
    if (this.checkTrainStop(entity, world, stop)){
        if (stop.identityComponent.key == PARAMETERS.self){
            this.makeTrainPath(entity, world);
            
            if (world.gameProperties.settings.sfx){
                var sound = getAudio.get('sounds/beep.ogg');
                sound.play();
            }
        }
        else{
            this.stops.push(stop.uid);
            if (this.stops.length > 1){
                stop.rendererComponents[0].timeDrawn = 0;

                if (world.gameProperties.settings.sfx){
                    var sound = getAudio.get('sounds/beep.ogg');
                    sound.play();
                }
            }
        }
    }
    this.memoizedCurrenciesNeeded = undefined;
};

var isRelevantFocusStop = function(worldEntity, world, pathCurrenciesNeeded){
    return worldEntity.focusPathStopComponent &&
        _.any(worldEntity.focusPathStopComponent.currenciesMadeAtPoint(worldEntity, world), 
              function(key){ return pathCurrenciesNeeded[key]; });
};

var isFreeStop = function(worldEntity, world){
    return !_.any(world.entities, function(chkEntity){
        return chkEntity.trainPathComponent && _.contains(chkEntity.trainPathComponent.trainStops, worldEntity.uid);
    });
};

var checkTrainStop = function(entity, world, worldEntity){
    if (worldEntity.focusPathStopComponent && worldEntity.focusPathStopComponent.isDisabled){
        return false;
    }
    
    if (this.stops.length > 0){
        var pathCurrenciesNeeded = this.getPathCurrenciesNeeded(entity, world);

        if (Object.keys(pathCurrenciesNeeded).length > 0){
            return isRelevantFocusStop(worldEntity, world, pathCurrenciesNeeded) &&
                worldEntity.interactionComponent.canInteractSelf() &&
                !_.contains(this.stops, worldEntity.uid) &&
                isFreeStop(worldEntity, world);
        }
        else{
            return worldEntity.identityComponent && worldEntity.identityComponent.key == PARAMETERS.self;
        }
    }
    else{
        return worldEntity.focusPathStopComponent && 
            worldEntity.focusPathStopComponent.isPathEnd &&
            isFreeStop(worldEntity, world);
    }
};

var removeTrainStop = function(index){
    this.stops.splice(index, 1);
    this.memoizedCurrenciesNeeded = undefined;
};


var trainPathMakerHighlightRender = function(rect, entity, ui, rectCount, timeToNextTick, lastInterval){
    var world = ui.world;
    
    this.timeToBlink -= lastInterval;
    
    if (this.timeToBlink > 0){
        var highlightEntities = _.filter(world.entities, function(worldEntity){
            return entity.trainPathMakerComponent.checkTrainStop(entity, world, worldEntity);
        });

        _.each(highlightEntities, (worldEntity) => {
            var rect = world.hexMap.cellForPosComp(worldEntity.positionComponent).rect;
            ctx.drawImage(getImage(this.highlightImage), rect[0], rect[1]);
        });
    }
    else if (this.timeToBlink < -0.1){
        this.timeToBlink += 1;
    }
};


var trainPathMakerKill = function(entity, world){
    _.chain(world.entities)
        .filter(function(entity){
            return entity.identityComponent &&
                entity.identityComponent.key == '#trainPathMaker';
        })
        .each(function(entity){
            entity.panelComponent.closed = true;
        });
};


var trainPathMakerTick = function(entity, world){
    entity.trainPathMakerComponent.memoizedCurrenciesNeeded = undefined;
};


var trainPathMakingPanelRender = function(rect, entity, ui, rectCount, timeToNextTick, lastInterval){
    entity.panelComponent.panelButtons = [];
        
    var panelButtons = [];
    var world = ui.world;
    
    var trainPathMaker = _.chain(world.entities)
        .filter(function(worldEntity){
            return worldEntity.trainPathMakerComponent;
        })
        .first()
        .value();

    if (!trainPathMaker){
        this.completeFunction = undefined;
        this.killStopFunctions = [];
        return;
    }

    if (entity.panelComponent.closed){
        return;
    }

    var stops = trainPathMaker.trainPathMakerComponent.stops;

    if (stops.length > 0){
        entity.panelComponent.sourceId = stops[0]
    }

    entity.absolutePositionComponent.size[1] = 350 + stops.length * 60;
    rect = entity.absolutePositionComponent.getAbsPosRect();

    ctx.drawImage(getImage(this.backImage), rect[0], rect[1], rect[2], rect[3]);

    ctx.fillStyle = this.textColor;
    ctx.textAlign = 'center';
    ctx.font = getFont(28);
    ctx.fillText('New Train', rect[0] + rect[2] / 2, rect[1] + 40);
    ctx.textAlign = 'left';


    ctx.font = getFont(20);
    var currenciesNeeded = trainPathMaker.trainPathMakerComponent.getPathCurrenciesNeeded(trainPathMaker, world);
    currenciesNeeded = _.chain(currenciesNeeded)
        .map(function(val, key){
            return [key, val];
        })
        .filter(function(pair){
            return pair[1] > 0;
        })
        .value();
    
    var y = rect[1] + 100;
    
    if (currenciesNeeded.length > 0){
        var currencySt = 'Currencies Needed:';
        ctx.fillText(currencySt, rect[0] + 20, y);
        ctx.font = getFont(18);
        _.each(currenciesNeeded, function(pair){
            y += 30;
            var st = getSaneName(pair[0]);
            if (pair[1] > 1){
                st += ' - ' + pair[1];
            }

            for (var i = 0; i < pair[1]; i++){
                ctx.drawImage(getImage('currency'), rect[0] + 20 + 5 * i, y - 9);
            }

            ctx.fillText(st, rect[0] + 50, y + 5);
        });
        ctx.font = getFont(20);

        ctx.strokeStyle = this.insetColor;
        ctxRoundedRect([rect[0] + 10, rect[1] + 110, rect[2] - 20, y - rect[1] - 85]);
    }
    
    y += 60;
    ctx.fillText('Path Taken:', rect[0] + 20, y)
    y += 50;
    
    var startStopsY = y - 40;

    for (var i = stops.length - 1; i >= 0; i--){
        var stop = world.entityForUid(stops[i]);

        if (!stop){
            break;
        }
        
        if (this.killStopFunctions[i] === undefined){
            this.killStopFunctions[i] = function(){
                trainPathMaker.trainPathMakerComponent.removeTrainStop(i);
            };
        }

        if (stop.uiDataComponent){
            stop.uiDataComponent.drawImage(stop, world, [rect[0] + 10, y - 30]);
        }
        if (stop.identityComponent && stop.identityComponent.image){
            ctx.drawImage(getImage(stop.identityComponent.image), rect[0] + 10, y - 30);
        }

        ctx.fillStyle = this.textColor;
        if (stop.identityComponent.name.length > 20){
            ctx.font = getFont(14);
            ctx.fillText(stop.identityComponent.name, rect[0] + 80, y);
            ctx.font = getFont(20);
        }
        else{
            ctx.fillText(stop.identityComponent.name, rect[0] + 80, y);
        }
        
        if (stops.length > 1 && i == stops.length - 1){
            var removeRect = [rect[0] + rect[2] - 55, y - 20, 40, 40];
            this.drawPanelButton(removeRect, 'X');
            panelButtons.push([removeRect, this.killStopFunctions[i]]);
        }

        y += 60;
    };

    ctxRoundedRect([rect[0] + 10, startStopsY, rect[2] - 20, y - startStopsY - 10]);

    if (stops.length > 0 && Object.keys(currenciesNeeded).length == 0){
        if (this.completeFunction === undefined){
            this.completeFunction = function(){
                trainPathMaker.trainPathMakerComponent.makeTrainPath(trainPathMaker, world);
                if (world.gameProperties.settings.sfx){
                    var sound = getAudio.get('sounds/beep.ogg');
                    sound.play();
                }
            };
        }

        this.blinkTime -= lastInterval;
        if (this.blinkTime < -0.2){
            this.blinkTime += 1;
        }
        
        var completeX = rect[0] + rect[2] / 2 - 75;
        var completeY = y + 20;

        var completeRect = [completeX, completeY, 150, 30];

        ctx.strokeStyle = (this.blinkTime > 0) ? '#000000' : '#ff0000';
        ctxRoundedRect(completeRect);

        ctx.fillStyle = this.textColor;
        ctx.drawImage(getImage(this.trainIcon), completeX + 5, completeY);
        ctx.fillText('Start Train', completeX + 45, completeY + 20);

        panelButtons.push([completeRect, this.completeFunction]);
    }

    entity.panelComponent.panelButtons = panelButtons;

    ctx.font = getFont(20);
    
    var closeRect = [rect[0] + rect[2] / 2 - 50, rect[1] + rect[3] - 50, 100, 40];
    this.drawPanelButton(closeRect, 'CLOSE');
    
    entity.panelComponent.panelButtons.push([closeRect, entityPanelClose]);    

    ctx.font = BASE_FONT;
};


var builtSourceMapSpawnPos = function(entity, world){
    var sourceEntities = _.filter(world.entities, function(chkEntity){
        return chkEntity.sourceExtensionComponent && 
            chkEntity.sourceExtensionComponent.sourceKey == entity.identityComponent.key;
    });
    
    sourceEntities.push(entity);

    var blockingEntities = world.getBlockingEntities();
    blockingEntities.push(entity);

    var blockingPositions = _.map(blockingEntities, 
                                  blockingEntity => [blockingEntity.positionComponent.x, blockingEntity.positionComponent.y]);
    var shallowBlockers = _.clone(blockingPositions);

    var checkForDifferentTrait = function(blockingEntity){
        if (!(blockingEntity.sourceExtensionComponent || blockingEntity.entityManagerComponent)){
            return false;
        }

        if (entity.personHeldComponent){
            if (!blockingEntity.personHeldComponent){
                return true;
            }
            return blockingEntity.personHeldComponent.personHeld != entity.personHeldComponent.personHeld;
        }

        return (blockingEntity.sourceExtensionComponent && 
                blockingEntity.sourceExtensionComponent.sourceKey != entity.identityComponent.key) ||
            (blockingEntity.entityManagerComponent && 
             blockingEntity.identityComponent.key != entity.identityComponent.key)
    };


    _.chain(blockingEntities)
        .filter(blockingEntity => checkForDifferentTrait(blockingEntity))
        .each(function(blockingEntity){
            _.each(world.hexMap.neighbors(blockingEntity.positionComponent.x, blockingEntity.positionComponent.y), 
                   function(pos){ blockingPositions.push(pos); });
        });

    var getAdjPos = (checkPositions) => {
        var positions = _.chain(sourceEntities)
            .map(posEntity => world.hexMap.neighbors(posEntity.positionComponent.x, posEntity.positionComponent.y))
            .flatten(true)
            .filter(function(neighbor){
                return world.isPosInBounds(neighbor) &&
                    !_.any(checkPositions, blockingPos => comparePos(blockingPos, neighbor));
            })
            .value();
        
        if (this.tendPosition){
            return _.min(positions, position => (Math.abs(position[0] - this.tendPosition[0]) + 
                                                 Math.abs(position[1] - this.tendPosition[1])));
        }
        else{
            return _.sample(positions);
        }
    };
    var adjPos = getAdjPos(blockingPositions);

    if (!adjPos){
        adjPos = getAdjPos(shallowBlockers);
    }

    if (!adjPos){
        this.spawnDone = true;
    }

    return adjPos;
};

var builtSourceMapShouldSpawn = function(entity, world, spawnedEntities){
    var level = entity.experienceComponent.getXpLevel(entity);
    var count = _.filter(world.entities, function(worldEntity){
        return worldEntity.sourceExtensionComponent &&
            worldEntity.sourceExtensionComponent.sourceKey == entity.identityComponent.key;
    }).length + 1 + spawnedEntities.length;
    return (count < level) && !this.spawnDone;
};


var killExtensions = function(entity, world){
    _.each(world.entitiesForKey(entity.mapSpawnComponent.mapSpawnKey), function(extension){
        extension.healthComponent.health = 0;
    });
};


var builtSourceCreateExec = function(entity, world){
    if (this.randomTraitPosition){
        randomOpenTraitPositionExec(entity, world, this.sector);
    }
    if (this.skipBuiltSourceCreate){
        return;
    }
    
    entity.experienceComponent.updateExpansion(entity);

    var spawnedEntities = [];

    while (entity.mapSpawnComponent.mapShouldSpawn(entity, world, [])){
        var spawnedEntity = entity.mapSpawnComponent.mapSpawn(entity, world, spawnedEntities);
        if (spawnedEntity){
            spawnedEntities.push(spawnedEntity);
            world.entityDeath();
        }
        else{
            break;
        }
    }

    spawnedEntities = [entity].concat(spawnedEntities);

    //spawnedEntities = _.shuffle(spawnedEntities);

    _.each(this.startManagedEntities, function(entityKey, i){
        world.makeEntities(entity, entityKey, {
            x: spawnedEntities[i].positionComponent.x,
            y: spawnedEntities[i].positionComponent.y,
            sourceTrait: entity.identityComponent.key
        });
    });
};

var randomOpenTraitPositionExec = function(entity, world, sector){
    var pos;
    if (entity.personHeldComponent){
        var blockingEntities = world.getBlockingEntities();
        var personEntityKeys = _.chain(world.entities)
            .filter(function(worldEntity){
                return worldEntity.personHeldComponent &&
                    worldEntity.personHeldComponent.personHeld == entity.personHeldComponent.personHeld &&
                    worldEntity.identityComponent &&
                    worldEntity.positionComponent;
            })
            .map(function(worldEntity){ return worldEntity.identityComponent.key; })
            .value();
        
        var positions = _.chain(world.entities)
            .filter(function(worldEntity){
                return (worldEntity.identityComponent &&
                        _.contains(personEntityKeys, worldEntity.identityComponent.key)) ||
                    (worldEntity.sourceExtensionComponent &&
                     _.contains(personEntityKeys, worldEntity.sourceExtensionComponent.sourceKey));
            }) 
            .map(function(worldEntity){
                return world.hexMap.neighbors(worldEntity.positionComponent.x, worldEntity.positionComponent.y);
            })
            .flatten(true)
            .filter(function(neighbor){
                return world.isPosInBounds(neighbor) &&
                    !_.any(blockingEntities, function(blockingEntity){
                        return blockingEntity.positionComponent.isPos(neighbor);
                    });
            })
            .value();
        
        if (positions.length > 0){
            if (sector){
                var sectorPosition = getSectorPos(sector);
                pos = _.min(positions, function(position){
                    var xdiff = Math.abs(position[0] - sectorPosition[0]);
                    var ydiff = Math.abs(position[1] - sectorPosition[1]);
                    return xdiff + ydiff;
                });
            }
            else{
                pos = _.sample(positions);
            }
            
            if (pos){
                entity.positionComponent.setPos(pos);
                return;
            }
        }
    }
    
    if (sector === undefined){
        pos = world.randomOpenPosition(entity.positionComponent.planeOfExistence);
        var entitiesToCheck = _.filter(world.entities, function(worldEntity){
            return ((worldEntity.identityComponent && worldEntity.identityComponent.key == PARAMETERS.self) ||
                    worldEntity.entityManagerComponent ||
                    worldEntity.sourceExtensionComponent);
        });
        
        var neighborPos = _.chain(world.hexMap.neighbors(pos[0], pos[1]))
            .filter(function(neighbor){
                if (!world.isPosInBounds(neighbor)){
                    return false;
                }
                var step = world.hexMap.neighbors(neighbor[0], neighbor[1]);
                return _.all(step, function(position){
                    return !_.any(entitiesToCheck, function(chkEntity){ return chkEntity.positionComponent.isPos(position); });
                });
            })
            .sample()
            .value();

        entity.positionComponent.setPos(neighborPos ? neighborPos : pos);
    }
    else{
        pos = false;
        var entitiesToCheck = world.getPositionedEntities(entity.positionComponent.planeOfExistence);
        var attempts = 0;
        while (!pos){
            pos = getSectorPos(sector);
            
            if (_.any(entitiesToCheck, function(chkEntity){
                return chkEntity.positionComponent.isPos(pos);
            })){
                pos = false;
                attempts += 1;
                if (attempts > 10){
                    break;
                }
            }
        }

        if (pos){
            entity.positionComponent.setPos(pos);
        }
        else{
            randomOpenTraitPositionExec(entity, world, undefined);
        }
    }
};

var getSectorPos = function(sector){
    return _.map(sector, function(sectorIndex, i){
        var sectorSize = PARAMETERS.size[i] / PARAMETERS.sectors[i];
        var specificIndex = sectorIndex + Math.random();
        var p = Math.floor(specificIndex * sectorSize);
        return min(max(p, 1), PARAMETERS.size[i] - 2);
    });
};


var builtSourceRender = function(rect, entity, ui, rectCount, timeToNextTick, lastInterval){
    var image = getImage(entity.entityManagerComponent.disabled ? this.disabledImage : this.image);
    var xDiff = this.getWobble(lastInterval);
    var x = rect[0] + xDiff;
    ctx.drawImage(image, x, rect[1]);

    var world = ui.world;
    var button = world.currentMakingEntity;
    if (button){
        if (button.buttonControlComponent.checkMakePos(world, entity.positionComponent.getAsPos())){
            ctx.drawImage(getImage(this.outlineImage), x, rect[1]);
        }
    }

    if (entity.entityManagerComponent.hasNew){
        this.timeToBlink -= lastInterval;
        if (this.timeToBlink > 0){
            ctx.drawImage(getImage(this.newImage), x, rect[1]);
        }
        else if (this.timeToBlink < -0.1){
            this.timeToBlink += 1;
        }
    }
};


var traitMenuCloseExec = function(entity, world){
    entity.entityManagerComponent.hasNew = false;
    _.chain(world.entities)
        .filter(worldEntity => (worldEntity.buttonControlComponent && 
                                worldEntity.buttonControlComponent.managerKey == entity.identityComponent.key))
        .each(worldEntity => worldEntity.buttonControlComponent.isNew = false);
};


var traitPanelRender = function(rect, entity, ui, rectCount, timeToNextTick, lastInterval){
    entity.panelComponent.panelButtons = [];
    if (entity.panelComponent.closed || entity.panelComponent.minimized){
        this.renderTime = 0;
        return;
    }

    this.snapPanelPos(entity);

    var world = ui.world;
    var trait = world.entityForUid(entity.panelComponent.sourceId);

    if (!trait){
        entity.panelComponent.closed = true;
        return;
    }

    var towerButtons = _.filter(world.entities, function(worldEntity){
        return worldEntity.buttonControlComponent &&
            worldEntity.buttonControlComponent.managerKey == trait.identityComponent.key;
    });

    var categories = _.chain(towerButtons)
            .map(function(button){ return button.uiDataComponent.subPanel; })
            .uniq()
            .sortBy(_.identity)
            .reverse()
            .value();

    if (!this.currentCategory){
        this.currentCategory = categories[0];
    }

    var getButtons = () => _.filter(towerButtons, towerButton => towerButton.uiDataComponent.subPanel == this.currentCategory);
    
    var buttons = getButtons();

    if (buttons.length == 0 && towerButtons.length > 0){
        this.currentCategory = towerButtons[0].uiDataComponent.subPanel;
        buttons = getButtons();
    }

    entity.absolutePositionComponent.size[1] = 275 + (buttons.length + 1) * 70;
    rect = entity.absolutePositionComponent.getAbsPosRect();

    if (this.renderTime < this.animatingTime){
        this.renderTime += lastInterval;
        var fraction = this.renderTime / this.animatingTime;
        ctx.drawImage(getImage(this.backImage), rect[0], rect[1], rect[2] * fraction, rect[3] * fraction);
        return;
    }
    
    ctx.drawImage(getImage(this.backImage), rect[0], rect[1], rect[2], rect[3]);

    entity.panelComponent.panelButtons.push(this.drawHelpButton(entity, rect));

    ctx.fillStyle = this.textColor;
    ctx.font = getFont(40);
    
    var titleWidth = ctx.measureText(trait.identityComponent.name).width;

    ctx.fillText(trait.identityComponent.name, rect[0] + (rect[2] - titleWidth) / 2, rect[1] + 60);

    ctx.font = getFont(20);

    if (trait.identityComponent.sourceName && trait.memoryComponent){
        ctx.fillText('Created by: ' + trait.identityComponent.sourceName, rect[0] + 10, rect[1] + 100);
    }

    if (trait.descriptionComponent.description){
        var y = rect[1] + 140;
        ctx.font = (trait.descriptionComponent.description.length > 75) ? '14px OpenSansItalic' : '20px OpenSansItalic';

        ctx.fillText(trait.descriptionComponent.description, rect[0] + 10, y);
        
        ctx.font = getFont(20);
    }
    
    y = rect[1] + 200;

    var standardRender = true;
    if (trait.entityManagerComponent.disabled){
        var disablingEntity = _.chain(world.entities)
            .filter(worldEntity => worldEntity.disableTraitComponent && worldEntity.disableTraitComponent.checkTraitDisabled(trait))
            .first()
            .value();
        ctx.fillText('Disabled: ' + disablingEntity.identityComponent.name, rect[0] + 10, y);
        standardRender = false;
    }
    else if (!trait.interactionComponent.canInteractSelf()){
        ctx.fillText('You have no control over this trait.', rect[0] + 10, y);
        standardRender = false;
    }
    else if (!_.contains(trait.interactionComponent.connectionKeys, PARAMETERS.self)){
        var start = world.entityForKey(PARAMETERS.self).positionComponent.getAsPos();
        var end = trait.positionComponent.getAsPos();
        if (!determinePath(start, end, world.getAllPaths())){
            ctx.fillText('You need to connect to this trait before you can build on it.', rect[0] + 10, y);
            standardRender = false;
        }
        else{
            trait.interactionComponent.connectionKeys.push(PARAMETERS.self);
        }
    }

    if (standardRender){
        _.each(categories, (panelKey, i) => {
            var catWidth  = 140;
            var x = rect[0] + 10 + (catWidth + 10) * i;
            if (panelKey == this.currentCategory){
                ctx.strokeStyle = this.insetColor;
                
                var textWidth = ctx.measureText(panelKey).width;
                ctx.fillText(panelKey, x + catWidth / 2 - textWidth / 2, y - 10);
                ctx.fillRect(x, y - 5, catWidth, 2);
            }
            else{
                var panelRect = [x, y - 30, catWidth, 30];

                var color = _.any(towerButtons, button => button.uiDataComponent.subPanel == panelKey && button.buttonControlComponent.isNew) ? '#ffd700' : '#000000';

                this.insetColor = color;
                
                this.drawPanelButton(panelRect, panelKey);

                this.insetColor = '#000000';
                
                if (!this.setCategoryFn[panelKey]){
                    this.setCategoryFn[panelKey] = () => {
                        this.currentCategory = panelKey;
                    };
                }
                var setCategoryFn = this.setCategoryFn[panelKey];
                entity.panelComponent.panelButtons.push([panelRect, setCategoryFn]);
            }
        });

        
        
        if (buttons.length > 0){
            y += 70;
            _.each(buttons, (button, i) => {
                ctx.fillStyle = this.textColor;
                
                ctx.drawImage(getImage(trait.uiDataComponent.image), rect[0] + 15, y - 26);
                button.uiDataComponent.drawImage(button, world, [rect[0] + 15, y - 26]);
                                
                ctx.fillText(button.identityComponent.name, rect[0] + 80, y + 10);

                ctx.font = '14px OpenSansItalic';
                if (_.isArray(button.descriptionComponent.description)){
                    _.each(button.descriptionComponent.description, (desc, i) => {
                        ctx.fillText(desc, rect[0] + 320, y + 15 * i);
                    });
                }
                else{
                    if (button.descriptionComponent.description.length > 50){
                        ctx.font = '12px OpenSansItalic';
                    }
                    ctx.fillText(button.descriptionComponent.description, rect[0] + 325, y + 10);
                }
                
                if (!button.interactionComponent.canInteractSelf()){
                    ctx.font = getFont(14);
                    ctx.fillText('No Control', rect[0] + rect[2] - 100, y + 7);
                    ctx.font = getFont(20);
                }
                else if (button.buttonControlComponent.canMakeEntity(world)){
                    ctx.font = getFont(20);
                    var buttonRect = [rect[0] + rect[2] - 100, y - 15, 70, 30];

                    this.drawPanelButton(buttonRect, 'Build')
                    
                    var buildFunction = button.buttonControlComponent.getWrappedFunction(button, world, ui);
                    entity.panelComponent.panelButtons.push([buttonRect, buildFunction]);
                }
                else{
                    ctx.font = getFont(14);
                    ctx.fillText('Limit', rect[0] + rect[2] - 100, y);
                    ctx.fillText('Reached', rect[0] + rect[2] - 100, y + 15);
                    ctx.font = getFont(20);
                }

                if (button.buttonControlComponent.displayRiskIcon){
                    ctx.drawImage(getImage('dice'), rect[0] + 11, y - 29, 18, 18);
                }

                ctx.strokeStyle = button.buttonControlComponent.isNew ? '#ffd700' : '#000000';
                ctxRoundedRect([rect[0] + 10, y - 30, rect[2] - 20, 60]);
                ctx.strokeStyle = '#000000';
                y += 70;
            });
        }
    }
    
    var closeRect = [rect[0] + rect[2] / 2 - 50, rect[1] + rect[3] - 50, 100, 40];
    this.drawPanelButton(closeRect, 'CLOSE');
    entity.panelComponent.panelButtons.push([closeRect, entityPanelClose]);    

    ctx.font = BASE_FONT;
};


var checkTraitParticleEffectAlive = function(entity, world){
    var manager = world.entityForKey(this.managerKey);
    return manager && manager.entityManagerComponent.hasNew;
};


var transformTrait = function(entity, world){
    world.transformTrait(this.sourceTrait, this.newTrait, this.retainHealth);
};


var tutorialTextRender = function(rect, entity, ui){
    var world = ui.world;
    var entityToMark = world.entityForKey(this.entityToMark);
    
    if (entityToMark.positionComponent){
        rect = _.clone(world.hexMap.cellForPosComp(entityToMark.positionComponent).rect);
    }
    else if (entityToMark.absolutePositionComponent){
        rect = entityToMark.absolutePositionComponent.getAbsPosRect();
    }
    else{
        rect = [0, 0, 0, 0];
    }
    
    ctx.fillStyle = '#ffffff';
    ctx.font = getFont(30);

    var y = max(rect[1] + this.yOffset - 20, 32);

    ctx.fillText(this.text, rect[0] + this.xOffset, y);
    var textWidth = ctx.measureText(this.text).width;
    ctx.font = BASE_FONT;
    
    this.drawArrow([rect[0] + this.xOffset + textWidth / 2, y + 10],
                   [rect[0] + rect[2], rect[1] + rect[3] / 2]);
};

var tutorialTextDrawArrow = function(startPos, endPos){
    ctx.strokeStyle = '#ffffff';
    ctx.beginPath();
    ctx.moveTo(startPos[0], startPos[1]);
    ctx.bezierCurveTo(startPos[0] + 20, startPos[1] + 20,
                      endPos[0] + 10, endPos[1],
                      endPos[0], endPos[1]);
    ctx.lineTo(endPos[0] + 10, endPos[1] - 6);
    ctx.moveTo(endPos[0], endPos[1]);
    ctx.lineTo(endPos[0] + 10, endPos[1] + 6);
    ctx.stroke();
};


var tutorialTextTutorialDone = function(entity, world){
    var markingEntity = world.entityForKey(entity.tutorialTextComponent.entityToMark);
    if (!markingEntity){
        return true;
    }

    return this.checkTutorialUiKey(markingEntity, world);
    
};

var checkTutorialUiKey = function(markingEntity, world){
    var uiKey = markingEntity.clickComponent.uiKey;
    var panel = world.entityForKey(uiKey);
    if (panel === undefined){
        return false;
    }
    return !panel.panelComponent.closed;
};


var tutorialUnpauseRender = function(rect, entity, ui){
    ctx.fillStyle = '#ffffff';
    ctx.font = getFont(30);
    _.each(this.text, (txt, i) => ctx.fillText(txt, 50, this.yStart + 50 * i));
    ctx.font = BASE_FONT;

    ctx.strokeStyle = '#ffffff';
    ctx.beginPath();
    ctx.moveTo(50, this.yStart + 50 * (this.text.length - 1) + 25);
    ctx.bezierCurveTo(25, 625,
                      25, 700,
                      50, 760);
    ctx.lineTo(44, 754);
    ctx.moveTo(50, 760);
    ctx.lineTo(56, 754);
    ctx.stroke();
};


var tutorialUnpauseTutorialDone = function(entity, world){
    return world.game.tickSystemWrapper.running;
};


var getUiActions = function(entity, ui, panel){
    if (!(this.memoizedActions && (Object.keys(this.memoizedActions).length > 0) && _.all(this.memoizedActions, _.identity))){
        this.memoizedActions = this.generateUiActions(entity, ui, panel);
    }
    
    return this.memoizedActions;
};


var uiDataDrawImage = function(entity, world, pos){
    var managerKey = false;
    if (entity.managedComponent){
        managerKey = entity.managedComponent.managerKey;
    }
    if (entity.buttonControlComponent){
        managerKey = entity.buttonControlComponent.managerKey;
    }
    
    if (managerKey){
        var imageKey;
        if (allArgs[managerKey]){
            imageKey = allArgs[managerKey].image;
        }
        else{
            var manager = world.entityForKey(managerKey);
            if (manager){
                imageKey = manager.uiDataComponent.image;
            }
        }

        if (imageKey){
            ctx.drawImage(getImage(imageKey), pos[0], pos[1]);
        }
    }
    ctx.drawImage(getImage(this.image), pos[0], pos[1]);
};


var upgradeTower = function(world, pos, entity, interactionKey){
    var toUpgrade = this.baseUpgradeTowerForPos(world, pos);
        
    toUpgrade.healthComponent.kill();

    var args = {
        x: pos[0],
        y: pos[1],
        sourceKey: toUpgrade.managedComponent.managerKey,
        sourceTrait: toUpgrade.managedComponent.managerKey
    };

    if (interactionKey){
        args.interactionKeys = [interactionKey];
    }
    
    var newEntity = makeEcs(this.constructionKey, args);
    
    return newEntity;
};

var canMakeUpgrade = function(world){
    return _.any(this.baseKeys, function(baseKey){
        return world.entityForKeyExists(baseKey);
    });
};

var checkUpgradePos = function(world, pos){
    return this.baseUpgradeTowerForPos(world, pos) !== undefined;
};

var baseUpgradeTowerForPos = function(world, pos){
    return _.chain(this.baseKeys)
        .map(function(baseKey){
            return world.entitiesForKey(baseKey);
        })
        .flatten(true)
        .filter(function(baseEntity){
            return baseEntity.positionComponent.isPos(pos);
        })
        .first()
        .value();
};



var utilityEntityRender = function(rect, entity, ui){
    ctx.drawImage(getImage(this.backImage), rect[0], rect[1], rect[2], rect[3]);
    entity.panelComponent.panelButtons = [];

    if (!this.showAllBuildingPanel){
        this.showAllBuildingPanel = function(){
            var menu = ui.world.entityForKey(PARAMETERS.allBuildingPanel);
            if (!menu){
                menu = makeEcs(PARAMETERS.allBuildingPanel);
                ui.world.addEntity(menu);
            }
            
            menu.panelComponent.expandPanel();
        };
    }

    ctx.fillStyle = this.textColor;
    
    var buildingRect = [rect[0] + rect[2] / 2 - 75, rect[1] + rect[3] / 2 - 15, 150, 30];
    this.drawPanelButton(buildingRect, 'All Buildings');
    entity.panelComponent.panelButtons.push([buildingRect, this.showAllBuildingPanel]);
};


var getWobble = function(lastInterval){
    var xDiff = 0;
    if (this.wobble > 0){
        xDiff = 3 * Math.sin(this.wobble);
        this.wobble -= lastInterval * 8 * Math.PI;
    }
    return xDiff;
}


var calculateWorkerTicks = function(entity, world){
    var xpFactor = 1;
    if (entity.managedComponent){
        var manager = world.entityForKey(entity.managedComponent.managerKey);
        xpFactor = (1 - Math.pow(1/2, manager.experienceComponent.currentLevel)) * 2;
    }
    var workerFactor = (1 - Math.pow(1/2, this.workersActive.length)) * 2;
    return xpFactor * workerFactor;
};


var checkWorldGoalComponent = function(entity){
    return _.all(entity.worldGoalComponent.currencyRequested, function(val){ return val <= 0; });
};


var makeGoalMenuExecute = function(entity, world){
    if (entity.interactionComponent.canInteractSelf()){
        world.makeEntities(entity, this.goalMenuKey);
    }
};


var trackActionables = function(entity, world){
    world.trackActionables();
};


var worldGoalRender = function(rect, entity, ui, rectCount, timeToNextTick, lastInterval){
    var x = rect[0] + this.getWobble(lastInterval);
    ctx.drawImage(getImage(this.image), x, rect[1]);
    ctx.fillStyle = entity.worldGoalComponent.requestingCurrency ? '#ffffff' : '#000000';
    
    if (this.outlineImage){
        ctx.drawImage(getImage(this.outlineImage), x, rect[1]);
        this.outlineImage = false;

        this.renderTrainIcon(lastInterval, x, rect[1]);
    }

    if (!entity.worldGoalComponent.goalSeen && entity.interactionComponent.canInteractSelf()){
        this.timeToBlink -= lastInterval;
        if (this.timeToBlink > 0){
            ctx.drawImage(getImage(this.newImage), x, rect[1]);
        }
        else if (this.timeToBlink < -0.1){
            this.timeToBlink += 1;
        }
    }
};


var worldGoalGetCurrencyComponent = function(entity){
    return entity.worldGoalComponent;
};


var worldGoalMenuOpened = function(entity, args){
    if (args.menuSourceKey == entity.uid){
        entity.worldGoalComponent.goalSeen = true;
        entity.pauseComponent.paused = false;
    }
};


var chkForPlayerInterruptExec = function(entity, world){
    if (entity.interactionComponent.canInteract(PARAMETERS.self)){
        this.playerExecute.execute(entity, world);
    }
    else{
        entity.pauseComponent.paused = false;
    }
};


var checkWorldGoalParticleEffectAlive = function(entity, world){
    var source = world.entityForUid(this.sourceId);
    if (!source){
        return false;
    }
    return !source.worldGoalComponent.goalSeen;
};


var dailyEmotionMakerDayStart = function(entity, world){
    this.dayCount += 1;
    
    var sourceEntity = _.chain(this.sourceEntityKeys)
        .map(function(emotionSourceKey){
            return world.entityForKey(emotionSourceKey);
        })
        .filter(_.identity)
        .first()
        .value();

    if (sourceEntity){
        for (var i = this.daysPerStep; i < this.dayCount; i += this.daysPerStep){
            world.makeEntities(sourceEntity, this.entitiesToMake);
        }
    }
};


var joyCreate = function(entity, world){
    world.addEntity(makeEcs(PARAMETERS.pointNotification, {
        points: 10
    }));
};


var makeRandomInspirationGetReplaceKeys = function(entity, world){
    return _.chain(this.keysToUse)
        .filter(keyToUse => !world.entityForKey(keyToUse))
        .sample()
        .value();
};


var meetGroupie = function(entity, world){
    if (!world.flags['metGroupie'] && world.flags['firstParty']){
        world.flags['metGroupie'] = true;
    }
};


var addSongToCounter = function(quality){
    this.songs.push({
        name: 'Song',
        quality: quality
    });
};


var startPartyExec = function(entity, world){
    if (world.entityForKey(this.checkKey)){
        world.makeEntities(this.existsKey);
    }
    else{
        world.makeEntities(this.checkKey);
    }
};


var talkTowerGetHitPos = function(entity, world){
    if (!this.hitPos){
        var neighbors = world.hexMap.neighbors(entity.positionComponent.x, entity.positionComponent.y);
        this.hitPos = [];
        _.chain(neighbors)
            .map(function(neighbor){ return world.hexMap.neighbors(neighbor[0], neighbor[1]); })
            .flatten(true)
            .each((neighbor) => {
                if (!_.any(this.hitPos, function(hitPos){ return _.isEqual(hitPos, neighbor); })){
                    this.hitPos.push(neighbor);
                }
            });
    }
    return this.hitPos;
};


var traitConnectGetReplaceKeys = function(entity, world){
    var entityForKey = function(key){
        var keyedEntity = world.entityForKey(key);
        
        if (!keyedEntity){
            keyedEntity = _.filter(world.entities, worldEntity => (worldEntity.entityManagerComponent &&
                                                                   worldEntity.personHeldComponent &&
                                                                   worldEntity.personHeldComponent.personHeld == key))[0];
        }
        
        return keyedEntity;
    };


    var start = entityForKey(this.startKey);
    var end = entityForKey(this.endKey);
    
    world.connectEntities(start, end);
};


var drainGoalCreate = function(entity, world){
    //find the entities that make this currency
    //Find one that has a killable path or is blank
    //Build the train path
    //Make the road
};



var csvIdentifiers = {
    "sourceExtension": [
        "hateWork_ext", 
        "ambition_ext", 
        "selfLoathing_ext", 
        "achievement_ext", 
        "bitterness_ext", 
        "selfReliance_ext", 
        "daydreaming_ext", 
        "aloneness_ext", 
        "musicLove_ext", 
        "freshMusic_ext", 
        "curious_ext", 
        "nascentSong_ext", 
        "song_ext", 
        "goodSong_ext", 
        "sadSong_ext", 
        "techInspiration_ext", 
        "emoInspiration_ext", 
        "demons_ext", 
        "quietDemons_ext", 
        "releasedSong_ext", 
        "loverBase_ext", 
        "romance_ext", 
        "loving_ext", 
        "makingWork_ext", 
        "friend_ext", 
        "hangingOut_ext", 
        "realTalk_ext", 
        "alienation_ext", 
        "unhappy_ext", 
        "showMusic_ext", 
        "museBase_ext", 
        "exhaustion_ext"
    ], 
    "qualityResource": [], 
    "paramPath": [
        "letdownPath"
    ], 
    "resourceModifier": [], 
    "personGoal": [
        "interestWorkGoal", 
        "cheerUpGoal", 
        "loseImpressedGoal", 
        "shareCoolGoal"
    ], 
    "changeOpinion": [], 
    "worldGoal": [
        "quitPrideGoal", 
        "findNewMusicGoal", 
        "releaseGoal", 
        "reflectFailureGoal", 
        "dismissFailureGoal", 
        "reflectSuccessGoal", 
        "dismissSuccessGoal", 
        "solveMusicGoal", 
        "deriveHumanInsightGoal", 
        "thinkSongBadGoal", 
        "understandFailingsGoal", 
        "identifyProblemGoal", 
        "dontGiveUpGoal", 
        "discardGoal", 
        "sustainFreshMusicGoal", 
        "sustainTechInspirationGoal", 
        "sustainAchievementGoal", 
        "sustainEmoInspirationGoal", 
        "keepGoingGoal", 
        "keepTalkGoingGoal", 
        "proveSuccessGoal", 
        "restartWorkGoal", 
        "ignoreIssuesGoal", 
        "deriveInsightGoal", 
        "deriveCoolThingGoal", 
        "makePlaythingGoal", 
        "demon1Goal", 
        "demon2Goal", 
        "startNewSongGoal", 
        "songSatisfiedGoal", 
        "integrateGoal", 
        "selfPityGoal", 
        "obnoxiousGoal", 
        "indulgeCuriosityGoal", 
        "suppressDemonsGoal", 
        "prideStartGoal", 
        "swallowBitternessGoal", 
        "indulgeBitternessGoal", 
        "rollSleevesGoal", 
        "dreamFutureGoal", 
        "achievementGoal", 
        "irritabilityGoal", 
        "dreamOfGreatnessGoal", 
        "desireImprovementGoal", 
        "deriveStrengthGoal", 
        "envyGoal", 
        "minorFlirtGoal", 
        "releaseSatisfyGoal", 
        "meetMuseGoal", 
        "tellQuitGoal", 
        "fishComplimentsGoal", 
        "struggleComplimentsGoal", 
        "tryComplimentsGoal", 
        "hangOutGoal", 
        "acceptAlienationGoal", 
        "endHangOutGoal", 
        "talkAboutSelfGoal", 
        "tryForMeaningGoal", 
        "talkAboutLoverGoal", 
        "cheerLoverGoal", 
        "endAlienationGoal", 
        "endAlienationStrengthGoal", 
        "beCaredGoal", 
        "impressGoal", 
        "showMusicGoal", 
        "begLoverGoal", 
        "apologizeGoal", 
        "findMistakeGoal", 
        "majorIssueGoal", 
        "endRealTalkGoal", 
        "minorInsultGoal", 
        "ignoreInsult1Goal", 
        "ignoreInsult2Goal", 
        "museFishGoal", 
        "endMuseGoal", 
        "suspicionGoal", 
        "discardFreshMusicGoal", 
        "indulgeHappyGoal", 
        "fatigueGoal", 
        "relaxGoal", 
        "overcomeExhaustGoal", 
        "needTimeGoal", 
        "overcomeDistanceGoal", 
        "toldWorthGoal", 
        "daydreamGoal", 
        "fleshSongGoal", 
        "captureInspirationGoal", 
        "captureMusicGoal", 
        "captureSocialGoal", 
        "captureLoveGoal", 
        "captureWillGoal", 
        "captureThoughtGoal", 
        "refineInspirationGoal", 
        "museInspirationGoal", 
        "reflectPastGoal", 
        "rememberGoal", 
        "destinyGoal", 
        "quitWorkGoal", 
        "tryOldJokesGoal", 
        "reflectAloneGoal", 
        "ignoreDifferencesGoal", 
        "listenDemonsGoal", 
        "quietDemonsStrengthGoal", 
        "quietDemonsLoveGoal", 
        "acceptDemonGoal", 
        "stopDemonLoveGoal", 
        "stopDemonStrengthGoal", 
        "stopDemonSelfEsteemGoal"
    ], 
    "goalCompleteMenu": [
        "gameStartMenu", 
        "breakdownMenu", 
        "loverLeaveMenu", 
        "winMenu", 
        "timeOutMenu", 
        "explainSadMenu", 
        "explainObnoxiousMenu", 
        "explainMajorMenu", 
        "releaseMenu", 
        "reflectFailureMenu", 
        "dismissFailureMenu", 
        "solveMusicMenu", 
        "deriveHumanInsightMenu", 
        "thinkSongBadMenu", 
        "dontGiveUpMenu", 
        "deriveCoolThingMenu", 
        "songSatisfiedMenu", 
        "indulgeCuriosityMenu", 
        "indulgeBitternessMenu", 
        "rollSleevesMenu", 
        "minorFlirtMenu", 
        "acceptAlienationMenu", 
        "impressMenu", 
        "apologizeMenu", 
        "ignoreInsult1Menu", 
        "ignoreInsult2Menu", 
        "tellQuitMenu", 
        "findNewMusicMenu", 
        "deriveStrengthMenu", 
        "fishComplimentsMenu", 
        "struggleComplimentsMenu", 
        "tryComplimentsMenu", 
        "showMusicMenu", 
        "cheerLoverMenu", 
        "toldWorthMenu", 
        "tryOldJokesMenu", 
        "convinceSelfMenu", 
        "angerRideMenu", 
        "sadRideMenu", 
        "joyRideMenu", 
        "identifyProblemMenu", 
        "majorIssueMenu", 
        "museInspirationMenu", 
        "suspicionMenu", 
        "quitPrideMenu", 
        "reflectSuccessMenu", 
        "dismissSuccessMenu", 
        "understandFailingsMenu", 
        "discardMenu", 
        "sustainFreshMusicMenu", 
        "sustainTechInspirationMenu", 
        "sustainAchievementMenu", 
        "sustainEmoInspirationMenu", 
        "keepGoingMenu", 
        "keepTalkGoingMenu", 
        "proveSuccessMenu", 
        "restartWorkMenu", 
        "ignoreIssuesMenu", 
        "deriveInsightMenu", 
        "makePlaythingMenu", 
        "demon1Menu", 
        "demon2Menu", 
        "startNewSongMenu", 
        "integrateMenu", 
        "selfPityMenu", 
        "obnoxiousMenu", 
        "suppressDemonsMenu", 
        "prideStartMenu", 
        "swallowBitternessMenu", 
        "dreamFutureMenu", 
        "achievementMenu", 
        "irritabilityMenu", 
        "dreamOfGreatnessMenu", 
        "desireImprovementMenu", 
        "envyMenu", 
        "releaseSatisfyMenu", 
        "meetMuseMenu", 
        "hangOutMenu", 
        "endHangOutMenu", 
        "talkAboutSelfMenu", 
        "tryForMeaningMenu", 
        "talkAboutLoverMenu", 
        "endAlienationMenu", 
        "endAlienationStrengthMenu", 
        "beCaredMenu", 
        "begLoverMenu", 
        "findMistakeMenu", 
        "endRealTalkMenu", 
        "minorInsultMenu", 
        "museFishMenu", 
        "endMuseMenu", 
        "discardFreshMusicMenu", 
        "indulgeHappyMenu", 
        "fatigueMenu", 
        "relaxMenu", 
        "overcomeExhaustMenu", 
        "needTimeMenu", 
        "overcomeDistanceMenu", 
        "daydreamMenu", 
        "fleshSongMenu", 
        "captureInspirationMenu", 
        "captureMusicMenu", 
        "captureSocialMenu", 
        "captureLoveMenu", 
        "captureWillMenu", 
        "captureThoughtMenu", 
        "refineInspirationMenu", 
        "reflectPastMenu", 
        "rememberMenu", 
        "destinyMenu", 
        "quitWorkMenu", 
        "reflectAloneMenu", 
        "ignoreDifferencesMenu", 
        "listenDemonsMenu", 
        "quietDemonsStrengthMenu", 
        "quietDemonsLoveMenu", 
        "acceptDemonMenu", 
        "stopDemonLoveMenu", 
        "stopDemonStrengthMenu", 
        "stopDemonSelfEsteemMenu", 
        "interestWorkMenu", 
        "cheerUpMenu", 
        "loseImpressedMenu", 
        "shareCoolMenu", 
        "faceEmotionMenu", 
        "makeEmoInspirationMenu", 
        "workThroughMenu", 
        "cheerPrideMenu", 
        "fireUpMenu"
    ], 
    "memoryFade": [
        "sadMemoryFade", 
        "angryMemoryFade", 
        "joyMemoryFade"
    ], 
    "currency": [
        "accomplishment", 
        "alienation_lover", 
        "apology_lover", 
        "care_lover", 
        "cheer", 
        "coolThing", 
        "dependence", 
        "desireImprovement", 
        "discussProblem", 
        "dreamGreatness", 
        "drown_muse", 
        "easyProblem", 
        "emoProblem", 
        "emoSolution", 
        "failure", 
        "frustration", 
        "hardWork", 
        "holdOn", 
        "impulse", 
        "insight", 
        "intrigue", 
        "letdown", 
        "love_lover", 
        "mistake", 
        "music", 
        "need_lover", 
        "pain", 
        "plaything", 
        "reflect", 
        "relax", 
        "releasedMusic", 
        "resolve", 
        "sayRight_lover", 
        "selfEsteem", 
        "selfLoathe", 
        "sharing_lover", 
        "social_lover", 
        "social_muse", 
        "songSatisfaction", 
        "strength", 
        "success", 
        "techProblem", 
        "techSolution", 
        "thought", 
        "understanding", 
        "will", 
        "work", 
        "workEmotion", 
        "rawInspiration"
    ], 
    "fadingMemory": [
        "fadingSadMemory", 
        "fadingAngryMemory", 
        "fadingJoyMemory"
    ], 
    "instantEmotionResource": [], 
    "randomReplace": [
        "solveMusicResult", 
        "demon", 
        "newProblem", 
        "struggleCompliment", 
        "findNewMusicResult", 
        "talkEmotion", 
        "satisfyResult", 
        "acceptCheerResult", 
        "insultingMake", 
        "loveInsult", 
        "rideResult", 
        "cheerLoverResult", 
        "fleshResult", 
        "captureInspirationResult", 
        "listenEvent", 
        "museInspireResult", 
        "apologizeResult", 
        "suspicionResult"
    ], 
    "emotionResourceMap": [
        "angerMap", 
        "joyMap", 
        "sadnessMap"
    ], 
    "memoryGoalButton": [
        "angerRideButton", 
        "sadRideButton", 
        "joyRideButton", 
        "workThroughButton", 
        "cheerPrideButton", 
        "fireUpButton"
    ], 
    "disableTrait": [
        "aloneTime", 
        "unhappyDisable", 
        "showMusicDisable", 
        "exhaustionDisable", 
        "hateWorkLoveDisable", 
        "yourAloneTime"
    ], 
    "conditionalMake": [
        "condLoverJoy", 
        "condCaptureInspirationGoal"
    ], 
    "gameStage": [
        "chapter1", 
        "chapter2", 
        "chapter3"
    ], 
    "creep": [
        "anger", 
        "joy", 
        "sadness"
    ], 
    "killCurrency": [
        "killPride"
    ], 
    "personCurrencyAction": [], 
    "qualityImprovement": [
        "hurtBand"
    ], 
    "flagBranch": [], 
    "setFlag": [
        "unimpressed", 
        "insulting"
    ], 
    "person": [], 
    "helpInfoPanel": [
        "towerHelpInfoPanel", 
        "goalHelpInfoPanel", 
        "traitHelpInfoPanel", 
        "trainPathHelpInfoPanel", 
        "dailyHelpInfoPanel"
    ], 
    "checkQuality": [
        "loverCheck", 
        "bandCheck"
    ], 
    "transformExec": [
        "achievementToAmbition", 
        "alienationToFriend", 
        "ambitionToSelfReliance", 
        "bitternessToAmbition", 
        "curiousToMusicLove", 
        "freshMusicToCurious", 
        "freshMusicToMusicLove", 
        "friendToHangingOut", 
        "friendToLoving", 
        "friendToMakingWork", 
        "friendToRomance", 
        "hangingOutToFriend", 
        "lovingToFriend", 
        "makingWorkToFriend", 
        "musicLoveToFreshMusic", 
        "prideStartToDemons", 
        "romanceToFriend", 
        "sadSongToSong", 
        "selfLoathingToAmbition", 
        "selfLoathingToBitterness", 
        "selfRelianceToAmbition", 
        "songToGoodSong", 
        "songToSadSong", 
        "hangingOutToRealTalk", 
        "realTalkToFriend", 
        "romanceToLoving", 
        "lovingToMakingWork", 
        "hangingOutToAlienation", 
        "daydreamingToAchievement", 
        "daydreamingToSelfLoathing", 
        "ambitionToDaydreaming", 
        "nascentSongToSong", 
        "selfLoathingToAloneness", 
        "alonenessToAmbition", 
        "hateWorkToAmbition", 
        "demonsToQuietDemons", 
        "quietDemonsToDemons", 
        "selfRelianceToAloneness"
    ], 
    "currencyOverload": [], 
    "towerTickModifier": [], 
    "swallowOpinion": [], 
    "source": [
        "self"
    ], 
    "memoryTower": [
        "applyTower", 
        "deadInsideTower", 
        "optimismTower", 
        "rageTower", 
        "rechargeTower", 
        "understandTower"
    ], 
    "randomIfNone": [
        "randomDemon"
    ], 
    "memory": [
        "memory_selfHate", 
        "memory_stuckProblem", 
        "memory_loverInsult", 
        "memory_prideBurst", 
        "memory_workSatisfaction", 
        "memory_loverJoy", 
        "memory_shameBurst", 
        "memory_selfWorthless", 
        "memory_loverSad", 
        "memory_drainedSad", 
        "memory_hurtLover", 
        "memory_aloneSad"
    ], 
    "makeRandomMultiple": [
        "findProblem"
    ], 
    "entityReplace": [
        "revertFreshMusic", 
        "helpRelationship", 
        "hurtRelationship", 
        "stuckBrute", 
        "fullStuckProblem", 
        "cheerFail", 
        "makeGoodSong", 
        "romanceReplace", 
        "makeSadSong", 
        "capturedInspiration", 
        "makeTechInspiration", 
        "makeEmoInspiration"
    ], 
    "endGoal": [], 
    "personGoalButton": [], 
    "upgradeButton": [
        "cynicismButton", 
        "thoughtButton"
    ], 
    "menuModifier": [
        "explainRevert", 
        "explainStuck", 
        "explainFindMusicFail", 
        "explainCheerFail", 
        "explainRideFail", 
        "explainCheerLoverFail", 
        "explainCaptureFail", 
        "explainFleshFail", 
        "explainCapturedInspiration", 
        "explainMuseFail", 
        "explainApologyFail", 
        "explainSuspicionSuccess"
    ], 
    "emotionResource": [
        "selfHateMine", 
        "stuckProblemMine", 
        "loverInsultMine", 
        "prideBurstMine", 
        "workSatisfactionMine", 
        "loverJoyMine", 
        "shameBurstMine", 
        "selfWorthlessMine", 
        "loverSadMine", 
        "drainedSadMine", 
        "hurtLoverMine", 
        "aloneSadMine", 
        "flashback_anger", 
        "flashback_joy", 
        "flashback_sadness"
    ], 
    "resourceButton": [
        "willMineButton", 
        "musicMineButton", 
        "selfLoathingMineButton", 
        "reflectMineButton", 
        "pushYourselfMineButton", 
        "completeEmoMineButton", 
        "completeTechMineButton", 
        "socialMineButton", 
        "strengthMineButton", 
        "loveMineButton", 
        "socialMuseMineButton", 
        "failureMineButton", 
        "alienationMineButton"
    ], 
    "flaggedMake": [
        "flagQuitButton", 
        "flagQuitPrideGoal"
    ], 
    "steamAchievement": [
        "steamAlienation", 
        "steamCynicism", 
        "steamInspiration", 
        "steamPlay", 
        "steamThought", 
        "steamRelease"
    ], 
    "opinion": [], 
    "currencyAction": [
        "angerFleshAction", 
        "apologizeAction", 
        "applyAction", 
        "bruteForceAction", 
        "callousnessAction", 
        "cynicismAction", 
        "deadInsideAction", 
        "drownAction", 
        "easyWorkAction", 
        "emoWorkAction", 
        "failureAction", 
        "holdOnAction", 
        "indifferenceAction", 
        "intrigueAction", 
        "joyFleshAction", 
        "lackAccomplishmentAction", 
        "loveStrengthAction", 
        "loverFleshAction", 
        "lovingCareAction", 
        "museFleshAction", 
        "museVentAction", 
        "needLoverAction", 
        "optimismAction", 
        "personCallousnessAction", 
        "playMusicAction", 
        "playWithMusicAction", 
        "rageAction", 
        "rechargeAction", 
        "refineAccomplishmentAction", 
        "resolveAction", 
        "resolveStrengthAction", 
        "sadFleshAction", 
        "sayRightAction", 
        "settingAction", 
        "shareAction", 
        "shareJoyAction", 
        "shareJoyMuseAction", 
        "sinkWorkAction", 
        "solutionAction", 
        "strengthAction", 
        "talkAction", 
        "techWorkAction", 
        "thoughtAction", 
        "understandAction"
    ], 
    "timedFocusHolder": [
        "earworm"
    ], 
    "personTowerButton": [], 
    "entityKill": [
        "killEmoInspiration", 
        "killTechInspiration", 
        "killSong", 
        "killUnhappy", 
        "killDeriveCoolThingButton", 
        "killAchievementButton", 
        "killPrideStartButton", 
        "killTellQuitButton", 
        "killLoveDeposit", 
        "killSongProblems", 
        "killShowMusic", 
        "killMuse", 
        "killIgnoreIssuesGoal", 
        "killExhaustion", 
        "killFlesh"
    ], 
    "emotionSource": [
        "selfHate", 
        "stuckProblem", 
        "loverInsult", 
        "prideBurst", 
        "workSatisfaction", 
        "loverJoy", 
        "shameBurst", 
        "selfWorthless", 
        "loverSad", 
        "drainedSad", 
        "hurtLover", 
        "aloneSad"
    ], 
    "gainControlMenu": [
        "gainControl_anger", 
        "gainControl_joy", 
        "gainControl_sadness"
    ], 
    "personResource": [], 
    "gameStageStartMenu": [
        "chapter2Menu", 
        "chapter3Menu"
    ], 
    "memoryConstruction": [
        "frustrationMineConstruction", 
        "painMineConstruction", 
        "cheerMineConstruction", 
        "applyTowerConstruction", 
        "rechargeTowerConstruction", 
        "understandTowerConstruction", 
        "deadInsideTowerConstruction", 
        "optimismTowerConstruction", 
        "rageTowerConstruction", 
        "angerRideGoalConstruction", 
        "sadRideGoalConstruction", 
        "joyRideGoalConstruction", 
        "workThroughGoalConstruction", 
        "cheerPrideGoalConstruction", 
        "fireUpGoalConstruction"
    ], 
    "trait": [
        "hateWork", 
        "ambition", 
        "selfLoathing", 
        "achievement", 
        "bitterness", 
        "selfReliance", 
        "daydreaming", 
        "aloneness", 
        "musicLove", 
        "freshMusic", 
        "curious", 
        "nascentSong", 
        "song", 
        "goodSong", 
        "sadSong", 
        "techInspiration", 
        "emoInspiration", 
        "demons", 
        "quietDemons", 
        "releasedSong", 
        "loverBase", 
        "romance", 
        "loving", 
        "makingWork", 
        "friend", 
        "hangingOut", 
        "realTalk", 
        "alienation", 
        "unhappy", 
        "showMusic", 
        "museBase", 
        "exhaustion"
    ], 
    "goalButton": [
        "acceptAlienationButton", 
        "achievementButton", 
        "beCaredButton", 
        "daydreamButton", 
        "deriveCoolThingButton", 
        "deriveHumanInsightButton", 
        "deriveInsightButton", 
        "deriveStrengthButton", 
        "desireImprovementButton", 
        "discardButton", 
        "discardFreshMusicButton", 
        "dismissFailureButton", 
        "dismissSuccessButton", 
        "dreamFutureButton", 
        "dreamOfGreatnessButton", 
        "endAlienationButton", 
        "endHangOutButton", 
        "endMuseButton", 
        "endRealTalkButton", 
        "envyButton", 
        "findMistakeButton", 
        "findNewMusicButton", 
        "fishComplimentsButton", 
        "fleshSongButton", 
        "hangOutButton", 
        "identifyProblemButton", 
        "impressButton", 
        "indulgeBitternessButton", 
        "indulgeCuriosityButton", 
        "indulgeHappyButton", 
        "integrateButton", 
        "makePlaythingButton", 
        "museFishButton", 
        "overcomeExhaustButton", 
        "prideStartButton", 
        "reflectFailureButton", 
        "reflectSuccessButton", 
        "relaxButton", 
        "releaseButton", 
        "releaseSatisfyButton", 
        "restartWorkButton", 
        "rollSleevesButton", 
        "showMusicButton", 
        "solveMusicButton", 
        "songSatisfiedButton", 
        "startNewSongButton", 
        "struggleComplimentsButton", 
        "swallowBitternessButton", 
        "talkAboutLoverButton", 
        "talkAboutSelfButton", 
        "tellQuitButton", 
        "thinkSongBadButton", 
        "toldWorthButton", 
        "tryComplimentsButton", 
        "tryForMeaningButton", 
        "refineInspirationButton", 
        "museInspirationButton", 
        "reflectPastButton", 
        "rememberButton", 
        "destinyButton", 
        "quitWorkButton", 
        "tryOldJokesButton", 
        "reflectAloneButton", 
        "listenDemonsButton", 
        "quietDemonsStrengthButton", 
        "quietDemonsLoveButton", 
        "acceptDemonButton", 
        "endAlienationStrengthButton"
    ], 
    "resource": [
        "willMine", 
        "musicMine", 
        "selfLoathingMine", 
        "reflectMine", 
        "pushYourselfMine", 
        "completeEmoMine", 
        "completeTechMine", 
        "socialMine", 
        "strengthMine", 
        "loveMine", 
        "socialMuseMine", 
        "failureMine", 
        "alienationMine"
    ], 
    "towerButton": [
        "angerFleshButton", 
        "apologizeButton", 
        "bruteForceButton", 
        "callousnessButton", 
        "drownButton", 
        "easyWorkButton", 
        "emoWorkButton", 
        "failureButton", 
        "indifferenceButton", 
        "intrigueButton", 
        "joyFleshButton", 
        "lackAccomplishmentButton", 
        "loveStrengthButton", 
        "loverFleshButton", 
        "lovingCareButton", 
        "museFleshButton", 
        "museVentButton", 
        "needLoverButton", 
        "personCallousnessButton", 
        "playMusicButton", 
        "playWithMusicButton", 
        "refineAccomplishmentButton", 
        "resolveButton", 
        "resolveStrengthButton", 
        "sadFleshButton", 
        "sayRightButton", 
        "settingButton", 
        "shareButton", 
        "shareJoyButton", 
        "shareJoyMuseButton", 
        "sinkWorkButton", 
        "solutionButton", 
        "strengthButton", 
        "talkButton", 
        "techWorkButton"
    ], 
    "traitParticleEffect": [
        "newTraitParticle"
    ], 
    "memoryResource": [
        "frustrationMine", 
        "painMine", 
        "cheerMine"
    ], 
    "traitConnect": [
        "techInspirationTraitConnectSong", 
        "releasedSongTraitConnectSelfCore", 
        "museBaseTraitConnectSelfCore", 
        "museBaseTraitConnectMusic", 
        "friendTraitConnectSelfCore", 
        "demonsTraitConnectLoverBase", 
        "demonsTraitConnectMusic", 
        "nascentSongTraitConnectMusic", 
        "nascentSongTraitConnectLoverBase"
    ], 
    "memoryGoal": [
        "angerRideGoal", 
        "sadRideGoal", 
        "joyRideGoal", 
        "faceEmotionGoal", 
        "makeEmoInspirationGoal", 
        "convinceSelfGoal", 
        "workThroughGoal", 
        "cheerPrideGoal", 
        "fireUpGoal"
    ], 
    "worldGoalParticleEffect": [
        "newParticle"
    ], 
    "memoryResourceButton": [
        "frustrationMineButton", 
        "painMineButton", 
        "cheerMineButton"
    ], 
    "timedCreateEntities": [
        "firstParty", 
        "fadeFreshMusic", 
        "fadeTechInspiration", 
        "fadeEmoInspiration", 
        "fadeAchievement", 
        "fadeHangingOut", 
        "fadeQuietDemons", 
        "fadeExhaustion", 
        "fadeRealTalk"
    ], 
    "construction": [
        "willMineConstruction", 
        "musicMineConstruction", 
        "selfLoathingMineConstruction", 
        "reflectMineConstruction", 
        "pushYourselfMineConstruction", 
        "completeEmoMineConstruction", 
        "completeTechMineConstruction", 
        "socialMineConstruction", 
        "strengthMineConstruction", 
        "loveMineConstruction", 
        "socialMuseMineConstruction", 
        "failureMineConstruction", 
        "alienationMineConstruction", 
        "angerFleshTowerConstruction", 
        "apologizeTowerConstruction", 
        "bruteForceTowerConstruction", 
        "callousnessTowerConstruction", 
        "drownTowerConstruction", 
        "easyWorkTowerConstruction", 
        "emoWorkTowerConstruction", 
        "failureTowerConstruction", 
        "indifferenceTowerConstruction", 
        "intrigueTowerConstruction", 
        "joyFleshTowerConstruction", 
        "lackAccomplishmentTowerConstruction", 
        "loveStrengthTowerConstruction", 
        "loverFleshTowerConstruction", 
        "lovingCareTowerConstruction", 
        "museFleshTowerConstruction", 
        "museVentTowerConstruction", 
        "needLoverTowerConstruction", 
        "personCallousnessTowerConstruction", 
        "playMusicTowerConstruction", 
        "playWithMusicTowerConstruction", 
        "refineAccomplishmentTowerConstruction", 
        "resolveTowerConstruction", 
        "resolveStrengthTowerConstruction", 
        "sadFleshTowerConstruction", 
        "sayRightTowerConstruction", 
        "settingTowerConstruction", 
        "shareTowerConstruction", 
        "shareJoyTowerConstruction", 
        "shareJoyMuseTowerConstruction", 
        "sinkWorkTowerConstruction", 
        "solutionTowerConstruction", 
        "strengthTowerConstruction", 
        "talkTowerConstruction", 
        "techWorkTowerConstruction", 
        "acceptAlienationGoalConstruction", 
        "achievementGoalConstruction", 
        "beCaredGoalConstruction", 
        "daydreamGoalConstruction", 
        "deriveCoolThingGoalConstruction", 
        "deriveHumanInsightGoalConstruction", 
        "deriveInsightGoalConstruction", 
        "deriveStrengthGoalConstruction", 
        "desireImprovementGoalConstruction", 
        "discardGoalConstruction", 
        "discardFreshMusicGoalConstruction", 
        "dismissFailureGoalConstruction", 
        "dismissSuccessGoalConstruction", 
        "dreamFutureGoalConstruction", 
        "dreamOfGreatnessGoalConstruction", 
        "endAlienationGoalConstruction", 
        "endHangOutGoalConstruction", 
        "endMuseGoalConstruction", 
        "endRealTalkGoalConstruction", 
        "envyGoalConstruction", 
        "findMistakeGoalConstruction", 
        "findNewMusicGoalConstruction", 
        "fishComplimentsGoalConstruction", 
        "fleshSongGoalConstruction", 
        "hangOutGoalConstruction", 
        "identifyProblemGoalConstruction", 
        "impressGoalConstruction", 
        "indulgeBitternessGoalConstruction", 
        "indulgeCuriosityGoalConstruction", 
        "indulgeHappyGoalConstruction", 
        "integrateGoalConstruction", 
        "makePlaythingGoalConstruction", 
        "museFishGoalConstruction", 
        "overcomeExhaustGoalConstruction", 
        "prideStartGoalConstruction", 
        "reflectFailureGoalConstruction", 
        "reflectSuccessGoalConstruction", 
        "relaxGoalConstruction", 
        "releaseGoalConstruction", 
        "releaseSatisfyGoalConstruction", 
        "restartWorkGoalConstruction", 
        "rollSleevesGoalConstruction", 
        "showMusicGoalConstruction", 
        "solveMusicGoalConstruction", 
        "songSatisfiedGoalConstruction", 
        "startNewSongGoalConstruction", 
        "struggleComplimentsGoalConstruction", 
        "swallowBitternessGoalConstruction", 
        "talkAboutLoverGoalConstruction", 
        "talkAboutSelfGoalConstruction", 
        "tellQuitGoalConstruction", 
        "thinkSongBadGoalConstruction", 
        "toldWorthGoalConstruction", 
        "tryComplimentsGoalConstruction", 
        "tryForMeaningGoalConstruction", 
        "refineInspirationGoalConstruction", 
        "museInspirationGoalConstruction", 
        "reflectPastGoalConstruction", 
        "rememberGoalConstruction", 
        "destinyGoalConstruction", 
        "quitWorkGoalConstruction", 
        "tryOldJokesGoalConstruction", 
        "reflectAloneGoalConstruction", 
        "listenDemonsGoalConstruction", 
        "quietDemonsStrengthGoalConstruction", 
        "quietDemonsLoveGoalConstruction", 
        "acceptDemonGoalConstruction", 
        "endAlienationStrengthGoalConstruction", 
        "cynicismTowerConstruction", 
        "thoughtTowerConstruction"
    ], 
    "damageUpgrade": [], 
    "deposit": [
        "accomplishmentDeposit", 
        "alienationDeposit", 
        "careDeposit", 
        "coolThingDeposit", 
        "dependenceDeposit", 
        "desireImprovementDeposit", 
        "easyProblemDeposit", 
        "emoProblemDeposit", 
        "greatnessDreamDeposit", 
        "insightDeposit", 
        "largePrideDeposit", 
        "largeStrengthDeposit", 
        "mistakeDesposit", 
        "playthingDeposit", 
        "songSatisfactionDeposit", 
        "largeSongSatisfactionDeposit", 
        "selfEsteemDeposit", 
        "strengthDeposit", 
        "successDeposit", 
        "techProblemDeposit", 
        "understandingDeposit", 
        "workEmotionDeposit", 
        "relaxDeposit", 
        "rawInspirationDeposit", 
        "resolveDeposit"
    ], 
    "nextDayEntity": [
        "nextDayMuse"
    ], 
    "pathMaker": [
        "hateWorkPathSelf", 
        "musicLovePathHateWork", 
        "loverBasePathHateWork", 
        "loverBasePathMusicLove", 
        "startNewSongGoalPathNascentGoal", 
        "solveMusicGoalPathTechInspiration"
    ], 
    "tower": [
        "angerFleshTower", 
        "apologizeTower", 
        "bruteForceTower", 
        "callousnessTower", 
        "cynicismTower", 
        "drownTower", 
        "easyWorkTower", 
        "emoWorkTower", 
        "failureTower", 
        "indifferenceTower", 
        "intrigueTower", 
        "joyFleshTower", 
        "lackAccomplishmentTower", 
        "loveStrengthTower", 
        "loverFleshTower", 
        "lovingCareTower", 
        "museFleshTower", 
        "museVentTower", 
        "needLoverTower", 
        "personCallousnessTower", 
        "playMusicTower", 
        "playWithMusicTower", 
        "refineAccomplishmentTower", 
        "resolveTower", 
        "resolveStrengthTower", 
        "sadFleshTower", 
        "sayRightTower", 
        "settingTower", 
        "shareJoyMuseTower", 
        "shareJoyTower", 
        "shareTower", 
        "sinkWorkTower", 
        "solutionTower", 
        "strengthTower", 
        "talkTower", 
        "techWorkTower", 
        "thoughtTower"
    ], 
    "personResourceButton": [], 
    "personCurrency": [], 
    "personTower": [], 
    "emotionSourceConstruction": [
        "selfHateMineConstruction", 
        "stuckProblemMineConstruction", 
        "loverInsultMineConstruction", 
        "prideBurstMineConstruction", 
        "workSatisfactionMineConstruction", 
        "loverJoyMineConstruction", 
        "shameBurstMineConstruction", 
        "selfWorthlessMineConstruction", 
        "loverSadMineConstruction", 
        "drainedSadMineConstruction", 
        "hurtLoverMineConstruction", 
        "aloneSadMineConstruction", 
        "flashback_angerConstruction", 
        "flashback_joyConstruction", 
        "flashback_sadnessConstruction"
    ], 
    "memoryTowerButton": [
        "applyButton", 
        "rechargeButton", 
        "understandButton", 
        "deadInsideButton", 
        "optimismButton", 
        "rageButton"
    ]
}

var findKeyFor = function(csvId, key, val){
    return _.chain(csvIdentifiers[csvId])
    .filter(function(argKey){
        return (allArgs[argKey][key] == val);
    })
    .first()
    .value();
};

var updateArgs = function(args, defaultArgs){
    _.each(defaultArgs, function(val, key){
        args[key] = val;
    });
};

var uidManager = (function(){
    var uid = 0;
    return {
        getUid: function(){
            uid += 1;
            return uid;
        },
        setUid: function(newUid){
            uid = newUid;
        }
    };
})();

var makeEcs = function(key, args){
    if (ecs[key] === undefined){
        console.log('tried to build ' + key);
    }

    args = (args === undefined) ? {} : args;
    if (!args.uid){
        args.uid = uidManager.getUid();
    }
    
    var passedArgs = _.clone(args);
    
    var newEcs = ecs[key](args);
    
    newEcs.key = key;
    newEcs.passedArgs = passedArgs;
    newEcs.uid = args.uid;

    return newEcs;
};

var makeAllEcs = function(csvId){
    return _.map(csvIdentifiers[csvId], function(key){
        return makeEcs(key, {});
    });
};
