sparkletwist / Storium Card Tweaks

// ==UserScript==
// @name		Storium Card Tweaks
// @author		sparkletwist
// @version		1.4
// @include		https://storium.com/game/*
// @include		https://storium.com/character/*
// @require     https://code.jquery.com/jquery-3.4.1.min.js
// @grant       GM_getValue
// @grant       GM_setValue
// @copyright   2020, sparkletwist
// @license     GPL-3.0-or-later
// ==/UserScript==

// doesn't actually use GM_getValue or GM_setValue, but @grant none breaks on Chrome for some reason
// contains code yoinked from Ssieth's Storium Improver

/* globals $ */
/* globals exportFunction */
/* globals cloneInto */

function do_setup() {
	var stored_active_gsm;
	var pending_polarized_reward;
	var last_requested_card;
	var edit_cardid;
	var chosen_challenge;
	var rewrite_asset_as = { };
	    
	// Will this break?
	function get_a_valid_gsm() {
		return stored_active_gsm;
	}
	
	function rng_from_card(gsm, card) {
		var random = gsm.core.stats.scenes;
		
		for(var i=0;i < card.cardId.length;i++) {
			random += card.cardId.charCodeAt(i);
		}
		
		if (card.v_stackGuid) {
			for(var i=0;i < card.v_stackGuid.length;i++) {
				random += card.v_stackGuid.charCodeAt(i);
			}			
		}
		
		return random;
	}
	
	function challenge_card_polarity(card_played_on) {
		if (card_played_on && card_played_on.description) {
			var chalchar = card_played_on.description.substring(0, 2);
			if (chalchar === "=+") {
				return 1;
			} else if (chalchar === "=-") {
				return -1;
			}
		}
		return undefined;
	}
		
	function special_polarity(card_played, ignore_ns) {
		if (!card_played) { return undefined; }
		if (ignore_ns || (card_played.namespace === "thing" || card_played.namespace === "subplot" || card_played.namespace === "goal")) {
			if (card_played.description) {			
				var pchar = card_played.description.substring(0, 1);		
				if (pchar == "+") {
					return 1;
				} else if (pchar == "-") {
					return -1;
				}
			}
			
			if (chosen_challenge) {
				var active_gsm = get_a_valid_gsm();
				if (active_gsm) {
					var chalcard = active_gsm.manifests.cards[chosen_challenge];
					var chalpol = challenge_card_polarity(chalcard);
					if (chalpol) {
						return chalpol;
					}
				}					
			}
		}
		
		return undefined;
	}
	
	function get_card_json_data(card_played) {
		if (card_played && card_played.description) {
			try {
				var nl = card_played.description.indexOf("\n\.\.\.\n");
				if (nl == -1) { return undefined; }
				
				var jstr = card_played.description.substr(nl+5);
				var dat = JSON.parse(jstr);
				return dat;
			} catch {
				return undefined;
			}
		}
		
		return undefined;
	}
	
	function force_rewrite_card(card_played) {
		if (!card_played || !card_played.description) { return undefined; }
		if (card_played.namespace !== "thing") { return false; }
		
		var rrtxt = card_played.description.substring(0, 2);
		if (rrtxt === "::") {
			return true;
		}
		
		return false;
	}
	
	function force_rewrite_random_card(card_played) {
		var rrtxt = card_played.description.substring(2, 4);
		if (rrtxt === "??") {
			var jd = get_card_json_data(card_played);
			if (jd && jd.drawfrom) {
				return jd.drawfrom;
			} else {
				return "default_deck";
			}
		}
		
		return undefined;
	}
	
	function force_rewrite_card_type(card_played) {
		var rrtxt = card_played.description.substring(2, 4);
		if (rrtxt === ">-") {
			return "weakness";
		} else if (rrtxt === ">+") {
			return "strength";
		}
		
		return undefined;
	}
	
	function card_polarity(card_played) {
		var sp = special_polarity(card_played);
		if (sp === 1 || sp === -1) {
			return sp;
		}
		
		if (card_played.namespace === "strength") {
			return 1;
		}
		
		if (card_played.namespace === "weakness") {
			return -1;
		}
		
		return 0;
	}
		
	function polarize_card(card_played) {
		var sp = special_polarity(card_played);
		if (sp === 1) {
			card_played.x__SCTold_namespace = card_played.namespace;
			card_played.namespace = "strength";
		} else if (sp === -1) {
			card_played.x__SCTold_namespace = card_played.namespace;
			card_played.namespace = "weakness";
		}
	}
	
	function namespace_polarize_card(card_played) {
		var sp = special_polarity(card_played, false);
		if (sp === 1) {
			card_played.namespace = "strong_" + card_played.namespace;
		} else if (sp === -1) {
			card_played.namespace = "weak_" + card_played.namespace;
		}
	}
	
	function mark_polarized_reward(card_played) {
		if (card_played.x__SCTold_namespace && (card_played.x__SCTold_namespace === "subplot" || card_played.x__SCTold_namespace === "goal")) {
			pending_polarized_reward = true;
		}
	}
  
	function polarize_challenge_asset(ev) {
		var active_gsm = get_a_valid_gsm();
		if (active_gsm) {
			var card_played = active_gsm.manifests.cards[ev.params.cardId];
			if (card_played) {				
				polarize_card(card_played);
				mark_polarized_reward(card_played);
			}
		}
	}
	
	function polarize_assets_for_selection(role) {
		var active_gsm = get_a_valid_gsm();
		if (active_gsm) {
			var scards = active_gsm.manifests.cards;
			var myhand = active_gsm.core.hands[role];
			for (var ccn in myhand) {
				var handcard = myhand[ccn];
				var ck = scards[handcard.cardId];
				var polarize = true;

				// don't polarize cards we can't play
				if (ck.namespace === "subplot" || ck.namespace === "goal") {
					if (special_polarity(ck) && active_gsm.core.plays.playedStackGuidsByRole[role]) {
						for (var cc in active_gsm.core.plays.playedStackGuidsByRole[role]) {
							if (active_gsm.core.plays.playedStackGuidsByRole[role][cc] === handcard.stackGuid) {
								polarize = false;
								break;
							}
						}
					}
				}

				if (polarize) {
					polarize_card(ck);
				}
				
			}
		}
	}
	
	function look_for_force_rewrites(role) {
		if (role === "narrator") {
			return false;
		}
		
		var active_gsm = get_a_valid_gsm();
		if (active_gsm) {
			var scards = active_gsm.manifests.cards;
			var myhand = active_gsm.core.hands[role];
			for (var ccn in myhand) {
				var handcard = myhand[ccn];
				var ck = scards[handcard.cardId];

				if (force_rewrite_card(ck)) {
					return ck;
				}
			}
		}
	}
	
	function setup_required_card_rewrite(scope, role) {
		var rr_card = look_for_force_rewrites(role);
		if (rr_card) {
			scope.selectedCard = rr_card;			
		}
	}
	
	function wild_card_into_editor(editor_scope) {
		editor_scope.cardCopy.name = "Wild";
		editor_scope.cardCopy.description = "";											
		editor_scope.cardCopy.imageAssetCrop = "";
		editor_scope.cardCopy.imageAssetId = "";
		editor_scope.cardCopy.imageAssetSize = "";
		editor_scope.cardCopy.attributionText = "";
		editor_scope.cardCopy.attributionUrl = "";
		editor_scope.cardCopy.altText = "";				
	}
	
	function depolarize_assets() {
		var active_gsm = get_a_valid_gsm();
		if (active_gsm) {
			var scards = active_gsm.manifests.cards;
			for (var k in scards) {
				var ck = scards[k];
				if (ck.x__SCTold_namespace) {
					ck.namespace = ck.x__SCTold_namespace;
					ck.x__SCTold_namespace = undefined;
				}
			}
		}
	}
    
	// hook an (unused?) function called via advanceSingleFictionEvent
	function trace_hook (ev) {
		//console.log(ev.type);
		
		if (ev.type === "chooseChallengeCard") {
			chosen_challenge = ev.params.cardId;
		}
		
		if (ev.type === "playCardOnChallenge") {
			pending_polarized_reward = false;
			polarize_challenge_asset(ev); 
		}
		
		if (ev.type === "rewriteAsset") {
			if (rewrite_asset_as[ev.params.stackGuid]) {
				var rewriter = rewrite_asset_as[ev.params.stackGuid];
				if (rewriter === "wild_strength") {
					ev.params.namespace = "strength";
					ev.params.isWild = true;
				} else if (rewriter === "wild_weakness") {
					ev.params.namespace = "weakness";
					ev.params.isWild = true;
				} else {
					ev.params.namespace = rewrite_asset_as[ev.params.stackGuid];
				}
			}
		}	
    
		if (ev.type === "publishEntry" || ev.type == "discardCard" || ev.type == "giveCard") {
			pending_polarized_reward = false;
			depolarize_assets();
		}
		
		if (ev.type === "publishEntry") {
			if (look_for_force_rewrites(ev.role)) {
				var active_gsm = get_a_valid_gsm();
				active_gsm.addValidationError(ev, "You have Asset cards that need to be rewritten. Click \"Browse your cards\" to do so.", cloneInto({ useCase: "forceRewriteCards" }, unsafeWindow));
			}
			chosen_challenge = undefined;
		}
	}
	unsafeWindow.gsmTrace = exportFunction(trace_hook, unsafeWindow);
	
	// past scenes are cached, so they need to be modified with the new cards
	function repolarize_scenes_in_cache(gsm, scenes) {
		var challenge_polarities = { };
		
		for (var k in scenes) {
			var c_scene = scenes[k];
			if (!c_scene.x__SCTcacheUpdated) {
				c_scene.x__SCTcacheUpdated = true;
				for (var en in c_scene.entries) {
					var c_ent = c_scene.entries[en];
					if (c_ent.format === "move") {
						
						if (c_ent.plays.cards.length > 0) {
							var challenge_copy = [];
							var chal = c_ent.plays.onChallenge.refPlayGuid;

							chosen_challenge = c_ent.plays.onChallenge.cardId;
							
							for (var cn in c_ent.plays.cards) {
								var c_card_id = c_ent.plays.cards[cn].cardId;
								var c_card = gsm.manifests.cards[c_card_id];
								var polarity = card_polarity(c_card);

								if (!challenge_polarities[chal]) {
									challenge_polarities[chal] = { cards: [ polarity ], num: 1 };	
								} else {
									var poln = challenge_polarities[chal].num;
									challenge_polarities[chal].cards[poln] = polarity;
									challenge_polarities[chal].num = poln + 1;
								}
							}
							// make a copy of the array in case cloneInto doesn't
							challenge_copy.push(...challenge_polarities[chal].cards);
							c_ent.plays.onChallenge.polaritiesWhenPublished = cloneInto(challenge_copy, unsafeWindow);	

							chosen_challenge = undefined;
						}
					}
				}
				
				for (var cc in c_scene.challenges) {
					if (challenge_polarities[cc]) {
						c_scene.challenges[cc].polarities = cloneInto(challenge_polarities[cc].cards, unsafeWindow);	
					}
				}
				
			}
		}
	}
	
	// make polarized goals still show a reward card, and add some card display hacks
	var helper_scope = unsafeWindow.gameplayHelpers;
	if (helper_scope) {
		
		// new icons for our polarized card types
		var IMAGES = {
			strong_goal: "https://drive.google.com/uc?export=download&id=1ibVPTd4t3poM3QAjRXOnC__M03LVlaN3",
			weak_goal: "https://drive.google.com/uc?export=download&id=1XDbgmOfnJYUpDgKib4dnTRXQz-8_S23t",
			strong_subplot: "https://drive.google.com/uc?export=download&id=1BRPIfNpyJ-89gMzS2k5Ovv1X0zRxSjV-", 
			weak_subplot: "https://drive.google.com/uc?export=download&id=1fpRMEYFyInF-Hj9X9R4djc05fU-SNbl0",
			strong_thing: "https://drive.google.com/uc?export=download&id=1i6QxB_-JI2A3t67qLGwXk0HKJ-iv_56n",
			weak_thing: "https://drive.google.com/uc?export=download&id=1uK5IMOu0W8FTqBfEY6-teIl1krTmZWEC",
		};
			
		var update_nsBelongs = function (id, grp) {
			if (grp === "rewardWildOnPlayedOut" && pending_polarized_reward) {
				pending_polarized_reward = false;
				return true;
			}
			return helper_scope.namespaceGroups[grp].indexOf(id) >= 0;
		};
		helper_scope.nsBelongs = exportFunction(update_nsBelongs, unsafeWindow);
		
		var base_stockCardImageUrl = helper_scope.stockCardImageUrl;
		var update_stockCardImageUrl = function (card_ns, imagetype) {
			if (IMAGES[card_ns]) { return IMAGES[card_ns]; }
			return base_stockCardImageUrl(card_ns, imagetype);
		};
		helper_scope.stockCardImageUrl = exportFunction(update_stockCardImageUrl, unsafeWindow);
		
		var update_cardImageUrl = function (card, imgtype) {			
			if (card) { 
				var card_namespace = card.namespace;
				var real_namespace = card_namespace.split("_")[1];
				if (real_namespace) { card_namespace = real_namespace; }
				
				if (card.x__SCTold_namespace) {
					card_namespace = card.x__SCTold_namespace;
				}
				if (imgtype === "art" && card.imageAssetId) {
					return helper_scope.cloudinaryImageUrl(card, helper_scope.isWideCardNamespace(card_namespace) ? 'wide_card' : 'card'); 
				} else {
					
					// this only works because the card renderer calls the functions in a certain order...
					if (imgtype === "icon") {
						last_requested_card = card;
						
						var sppol = special_polarity(card, true);
						if (sppol === 1 && IMAGES['strong_' + card_namespace]) {
							return IMAGES['strong_' + card_namespace];
						} else if (sppol === -1 && IMAGES['weak_' + card_namespace]) {
							return IMAGES['weak_' + card_namespace];
						}
						
					}
					return base_stockCardImageUrl(card_namespace, imgtype);
				}
			}
		};
		helper_scope.cardImageUrl = exportFunction(update_cardImageUrl, unsafeWindow);
				
		var base_prettyNameSpace = helper_scope.prettyNamespace;		
		var update_prettyNameSpaceWithPlus = function (ns) {
			var ns_card;
			var real_ns = ns;
			var polarity;
			var split_ns = ns.split("_")[1];
			
			if (split_ns) { 
				var l_split_ns = ns.split("_")[0];
				if (l_split_ns === "strong") {
					polarity = 1;
				} else if (l_split_ns === "weak") {
					polarity = -1;
				}
				
				real_ns = split_ns;
			}
			
			if (last_requested_card) {
				ns_card = last_requested_card;
				last_requested_card = undefined;
				
				if (ns_card.x__SCTold_namespace) {
					real_ns = ns_card.x__SCTold_namespace;
				}
			}
				
			var pns = base_prettyNameSpace(real_ns);
			if (!polarity) {
				polarity = special_polarity(ns_card);
				if (!polarity) {
					if (ns === "strength") {
						polarity = 1;
					} else if (ns === "weakness") {
						polarity = -1;
					}
				}
			}
			
			if (polarity == 1) {
				return pns + " +";
			} else if (polarity == -1) {
				return pns + " -";
			}

			return pns;
		};
		helper_scope.prettyNamespaceWithPlus = exportFunction(update_prettyNameSpaceWithPlus, unsafeWindow);
		
		var update_cardSortValueFnNamespaceName = function (card) {
			var ordnum = "2";
			var card_namespace = card.namespace;
			if (card.x__SCTold_namespace) {
				card_namespace = card.x__SCTold_namespace;
				if (card.namespace === "strength") { ordnum = "0"; }
				else if (card.namespace === "weakness") { ordnum = "1"; }
			} else {
				if (card.isWild) { ordnum = "9"; }
				//if (card.namespace === "thing" && force_rewrite_card(card)) { ordnum = "9"; }
			}
			
			return (helper_scope.namespaces.indexOf(card_namespace) + ordnum + ' ' + card.name).toLowerCase();
		};
		helper_scope.cardSortValueFnNamespaceName = exportFunction(update_cardSortValueFnNamespaceName, unsafeWindow);
		
		var document_scope = unsafeWindow.angular.element(document.body).scope();
		if (document_scope) {
			document_scope.helpers.stockCardImageUrl = exportFunction(update_stockCardImageUrl, unsafeWindow);
			document_scope.helpers.cardImageUrl = exportFunction(update_cardImageUrl, unsafeWindow);
			document_scope.helpers.cardSortValueFnNamespaceName = exportFunction(update_cardSortValueFnNamespaceName, unsafeWindow);
		}
		
	} else {
		console.error("HELPER SCOPE NOT FOUND");
	}
			  
	// hook the creation of a gsm and inject code to keep track of it
	var gsmw = unsafeWindow.getStore('gsmWrapper');
	if (gsmw) {
		if (gsmw.getFreshGsm) {
			//var base_getFreshGsm = gsmw.getFreshGsm;      
			var update_getFreshGsm = function(base_param) {

				if (unsafeWindow.gsmEvents.remoteEvents && unsafeWindow.gsmCaches.initialBootupCachesLoaded) {
					var new_gsm = new unsafeWindow.GSM();
				  
					stored_active_gsm = new_gsm;
					
					// hook the hydration so we can tweak the namespaces of the cards
					var base_gsm_hydrateEntry = new_gsm.hydrateEntry;
					var update_gsm_hydrateEntry = function (entry, role, scope) {	
						var hydrated_entry = base_gsm_hydrateEntry(entry, role, scope);
						
						if (hydrated_entry.v_cardsPlayedOnChallenge && hydrated_entry.v_cardsPlayedOnChallenge.length > 0) {
							for (var vcard in hydrated_entry.v_cardsPlayedOnChallenge) {
								// this is a deep copy of the cards, so there's no need to fix this
								namespace_polarize_card(hydrated_entry.v_cardsPlayedOnChallenge[vcard]);
							}
						}
						
						return hydrated_entry;
					}; 
					new_gsm.hydrateEntry = exportFunction(update_gsm_hydrateEntry, unsafeWindow);
					
					// block cards that need a rewrite first
					var base_gsm_cardIsPlayable = new_gsm.cardIsPlayable;
					var update_gsm_cardIsPlayable = function (card, role, stackguid, scene_type, core) {
						var playcard = base_gsm_cardIsPlayable(card, role, stackguid, scene_type, core);
						if (playcard && card.description) {							
							if (role !== "narrator" && force_rewrite_card(card)) {
								return false;
							}								
						}
						return playcard;
					};
					new_gsm.cardIsPlayable = exportFunction(update_gsm_cardIsPlayable, unsafeWindow);
					
					// hook getAvailableCardsForUxUseCase when we want to show polarized assets
					var base_gsm_getAvailableCardsForUxUseCase = new_gsm.getAvailableCardsForUxUseCase;
					var update_gsm_getAvailableCardsForUxUseCase = function (role, usecase, tbl) {	
						if (usecase === "chooseActionCard") {
							polarize_assets_for_selection(role);
							// there's no function to hook to clean up, so just do it as fast as we can
							setTimeout(depolarize_assets, 1);
							
							function delayed_text_error() {
								var cchooser = $(".challenge-chooser-modal-wrapper");
								// yeah ok.
								cchooser[0].children[1].childNodes[1].childNodes[0].children[0].children[2].children[1].children[5].children[1].children[0].children[0].children[1].childNodes[1].childNodes[0].textContent = "You cannot play this card without rewriting it.";

							}
							
							function delayed_card_scope() {
								var cardlist = $(".stacked-card");
								if (!cardlist) { setTimeout(delayed_card_scope, 100); return; }
								for (var cardnum=5;cardnum<999;cardnum++) {
									if (!cardlist[cardnum]) { break; }
									var cardscope = unsafeWindow.angular.element(cardlist[cardnum]).scope();
									if (!cardscope) { setTimeout(delayed_card_scope, 100); return; }
									// only wrap the function if we haven't wrapped it already
									if (cardscope.clickedCard && !cardscope.x__SCT_base_clickedCard) {
										
										cardscope.x__SCT_base_clickedCard = exportFunction(cardscope.clickedCard, unsafeWindow);
										var this_card_clickedCard = function(e, card) {
											cardscope.x__SCT_base_clickedCard(e, card);
											
											var cchooser = $(".challenge-chooser-modal-wrapper");
											if (cchooser) {
												if (force_rewrite_card(card)) {
													setTimeout(delayed_text_error, 1);
												}
											}
										};
										cardscope.clickedCard = exportFunction(this_card_clickedCard, unsafeWindow);
									}
								}
							}
							setTimeout(delayed_card_scope, 100);
							
						} else if (usecase === "characterHand") {
							function delayed_hook() {
								var cw_scope = unsafeWindow.angular.element(".card-chooser").scope();
								if (cw_scope) {
									// only wrap the function if we haven't wrapped it already
									if (cw_scope.replaceCard && !cw_scope.x__SCT_base_replaceCard) {
										
										var this_card_setLabel = function() {
											var cardid = edit_cardid;
											var newlabel;
											
											if (rewrite_asset_as[cardid] === "strength") {
												newlabel = " Asset -> Strength";
											} else if (rewrite_asset_as[cardid] === "weakness") {
												newlabel = " Asset -> Weakness";
											} else {
												newlabel = " Asset";
											}
											
											$(".control-group")[0].children[1].children[0].children[0].childNodes[2].textContent = newlabel;
										};
										cw_scope.x__SCT_setLabel = exportFunction(this_card_setLabel, unsafeWindow);
										
										var this_card_changeType = function() {
											var cardid = edit_cardid;
											var newlabel;
											
											if (rewrite_asset_as[cardid] === "strength") {
												rewrite_asset_as[cardid] = "weakness";
											} else if (rewrite_asset_as[cardid] === "weakness") {
												rewrite_asset_as[cardid] = undefined;
											} else {
												rewrite_asset_as[cardid] = "strength";
											}

											cw_scope.x__SCT_setLabel();
										};
										cw_scope.x__SCT_changeType = exportFunction(this_card_changeType, unsafeWindow);
										
										var this_card_wild = function() {
											var cardid = edit_cardid;
											var active_gsm = get_a_valid_gsm();
											var editor_scope = unsafeWindow.angular.element("#panel_editor").scope();
											var card = editor_scope.selectedCard;
											
											editor_scope.cardCopy = active_gsm.deepCopyCard(card);
											wild_card_into_editor(editor_scope);
										
											rewrite_asset_as[cardid] = "wild_" + force_rewrite_card_type(card);
	
											editor_scope.okToSave = true;
											editor_scope.saveCard();
											
										};
										cw_scope.x__SCT_wild = exportFunction(this_card_wild, unsafeWindow);
										
										function get_drawable_cards(random_deck) {
											var drawable_cards = [ ];
											var active_gsm = get_a_valid_gsm();
											if (active_gsm) {
												var scards = active_gsm.manifests.cards;
												for (var k in scards) {
													var ck = scards[k];
													if (ck.namespace === "strength" || ck.namespace === "weakness" || ck.namespace === "thing" || ck.namespace === "subplot" || ck.namespace === "goal") {
														var ckdata = get_card_json_data(ck);
														if (ckdata && ckdata.deck) {
															if (ckdata.deck === true) { ckdata.deck = "default_deck"; }
															if (ckdata.deck === random_deck) {
																drawable_cards.push(k);
															}
														}
													}
												}
											}
											return drawable_cards;
										};
										
										var this_card_random = function() {
											var cardid = edit_cardid;
											var active_gsm = get_a_valid_gsm();
											var editor_scope = unsafeWindow.angular.element("#panel_editor").scope();											
											var card = editor_scope.selectedCard;
											var random_deck = force_rewrite_random_card(card);
											
											var drawable_cards = get_drawable_cards(random_deck);
											
											if (drawable_cards.length > 0) {
												var rand_card_num = 0;
												if (drawable_cards.length > 1) {
													rand_card_num = rng_from_card(active_gsm, card) % drawable_cards.length;
												}
												
												var card_idx = drawable_cards[rand_card_num];
												var newcard = active_gsm.manifests.cards[card_idx];
												
												editor_scope.cardCopy = active_gsm.deepCopyCard(card);
												editor_scope.cardCopy.name = newcard.name;
												
												if (force_rewrite_card(newcard)) {
													var wild_type = force_rewrite_card_type(newcard);
													if (wild_type) {
														wild_card_into_editor(editor_scope);
														rewrite_asset_as[cardid] = "wild_" + wild_type;
													} else {
														editor_scope.cardCopy.description = "";
													}
												} else {												
													var nl = newcard.description.indexOf("\n\.\.\.\n");
													if (nl == -1) {
														editor_scope.cardCopy.description = newcard.description;
													} else {
														editor_scope.cardCopy.description = newcard.description.substring(0, nl);	
													}
													
													editor_scope.cardCopy.imageAssetCrop = newcard.imageAssetCrop;
													editor_scope.cardCopy.imageAssetId = newcard.imageAssetId;
													editor_scope.cardCopy.imageAssetSize = newcard.imageAssetSize;
													editor_scope.cardCopy.attributionText = newcard.attributionText;
													editor_scope.cardCopy.attributionUrl = newcard.attributionUrl;
													editor_scope.cardCopy.altText = newcard.altText;
													
													rewrite_asset_as[cardid] = newcard.namespace;
												}
												
												editor_scope.okToSave = true;
												editor_scope.saveCard();
											} else {
												console.log("No drawable cards found...");
											}
											
										};
										cw_scope.x__SCT_random = exportFunction(this_card_random, unsafeWindow);
										
										cw_scope.x__SCT_base_replaceCard = exportFunction(cw_scope.replaceCard, unsafeWindow);
										var this_card_replaceCard = function(card) {
											var force_rewrite_type;
											var random_card_deck;
											
											edit_cardid = card.v_stackGuid;
											
											if (force_rewrite_card(card)) {
												var randcard = force_rewrite_random_card(card);
												if (randcard) {
													random_card_deck = randcard;
												} else {
													var frtype = force_rewrite_card_type(card);
													if (frtype) {
														rewrite_asset_as[edit_cardid] = frtype;
														force_rewrite_type = frtype;
													}
												}
											}
																				
											function delayed_button_add() {
												var no_button = false;
												var editor = $("card-editor");
																						
												var nbutton = document.createElement("button");
												nbutton.setAttribute("type", "button");
												nbutton.setAttribute("class", "flat-btn bg3");
												
												// wait until enough of the table is in place, hopefully
												if (!editor || !editor[0] || !editor[0].children || !editor[0].children[0] || !editor[0].children[0].children) {
													setTimeout(delayed_button_add, 100);
													return;
												}
													
												// i don't have access to $compile for this scope so i can't inject this stuff properly
												if (random_card_deck) {
													var drawable_cards = get_drawable_cards(random_card_deck);
													if (drawable_cards.length >= 1) {
														nbutton.setAttribute("onClick", "{ window.angular.element('.ng-modal-header').scope().x__SCT_random(); }");
														nbutton.textContent = "Draw Random Card";
													} else {
														no_button = true;
													}
												} else if (force_rewrite_type) {
													nbutton.setAttribute("onClick", "{ window.angular.element('.ng-modal-header').scope().x__SCT_wild(); }");
													nbutton.textContent = "Convert to Wild Card";
												} else {													
													nbutton.setAttribute("onClick", "{ window.angular.element('.ng-modal-header').scope().x__SCT_changeType(); }");
													nbutton.textContent = "Change Card Type";
												}
												
												if (!no_button) {
													editor[0].appendChild(nbutton);
												}
												
												unsafeWindow.angular.element(".card-chooser").scope().x__SCT_setLabel();
											}
											setTimeout(delayed_button_add, 200);
											
											return cw_scope.x__SCT_base_replaceCard(card);
										};
										cw_scope.replaceCard = exportFunction(this_card_replaceCard, unsafeWindow);
									}
								}					
							}
							
							var cw_scope = unsafeWindow.angular.element(".card-chooser").scope();
							if (cw_scope) {
								setup_required_card_rewrite(cw_scope, role);
								setTimeout(delayed_hook, 2);
							}
							
						}

						return base_gsm_getAvailableCardsForUxUseCase(role, usecase, tbl);
					};
					new_gsm.getAvailableCardsForUxUseCase = exportFunction(update_gsm_getAvailableCardsForUxUseCase, unsafeWindow);
					
					var stash = new_gsm.stash = unsafeWindow.gsmCaches.caches;
					var stash_reset = new_gsm.resetToStash();
					var fiction_advanced = new_gsm.advanceFiction(unsafeWindow.gsmEvents.remoteEvents);
					
					if (new_gsm && new_gsm.manifests && new_gsm.manifests.scenes) {
						repolarize_scenes_in_cache(new_gsm, new_gsm.manifests.scenes);
					}
				  
					return new_gsm.vacationUserPidsAsOfPageLoad, // will this break vacation icons?
						stash,
						stash_reset,
						fiction_advanced,
						new_gsm;
				}
			};
			gsmw.getFreshGsm = exportFunction(update_getFreshGsm, unsafeWindow);
		}
	}
  
}

$(function () {
	do_setup();
});