Back to Index Page generated: Jun 13, 2026, 7:54:50 PM

Expansion HOopy Casino

Content

Manifest

from Expansion Manager's OXP list from Expansion Manifest
Description Commanders who chose to dock with a HoOpy Casino are quickly ushered into the vast gaming halls, here to be offered the opportunity to win or lose credits in games of chance. Commanders who chose to dock with a HoOpy Casino are quickly ushered into the vast gaming halls, here to be offered the opportunity to win or lose credits in games of chance.
Identifier oolite.oxp.Murgh.HoOpyCasino oolite.oxp.Murgh.HoOpyCasino
Title HOopy Casino HOopy Casino
Category Dockables Dockables
Author Eric Walch, Murgh, CaptSolo, spara, Captain Beatnik, phkb Eric Walch, Murgh, CaptSolo, spara, Captain Beatnik, phkb
Version 2.3.3 2.3.3
Tags Casino, Gambling Casino, Gambling
Required Oolite Version
Maximum Oolite Version
Required Expansions
Optional Expansions
Conflict Expansions
Dependent Expansions
  • oolite.oxp.Thargoid.PlanetFall2_HOopyCasino:2.5
  • Information URL https://wiki.alioth.net/index.php/HoOpy_Casino n/a
    Download URL https://wiki.alioth.net/img_auth.php/8/80/HOopyCasino_2.3.3.oxz n/a
    License CC-BY-NC-SA 3.0 CC-BY-NC-SA 3.0
    File Size n/a
    Upload date 1777983243

    Relationships Diagram

    Documentation

    Also read http://wiki.alioth.net/index.php/HOopy%20Casino

    readme.txt

    Hoopy Casino
    By Murg
    
    Version 2.0
    (Requires Oolite 1.80 or newer.)
    
    Overview
    --------
    Multi-billionaire and FurCorp heiress Magda DeMug first dabbled in commercial enterprises founding CoachWhip Inc. Her fleet of CoachWhip luxury liners provide a first class transport service to well-healed business executives, VIPs and anyone else who can afford the 5,000 Cr ticket price. Having established a profitable enterprise fleecing the idle rich, the heiress turned her acute feline business mind to the question of sucking credits from the pockets of the proletariat.
    
    The basic hull of the CoachWhip liner was completely stripped, removing the cargo bay, hyperspace drive and inter-system drive. A docking port was added and the internal structure re-fitted as a luxurious Casino. The platforms were then deployed in geo-stationary orbit near the main GalCop Station in technologically advanced systems throughout the eight galaxies. Thus was the Chain of HoOpy Casinos born. These dens of inequity are found in all systems with a Tech Level of 11 or more. In such a system, setting an Advanced Space Compass to C will locate the nearest HoOpy Casino.
    
    The HoOpy Casino Chain
    ----------------------
    Commanders who chose to dock with a HoOpy Casino are quickly ushered into the vast gaming halls, here to be offered the opportunity to win or lose credits in games of chance.
    
    Some Commanders never leave the casino area of the station, claiming that they have a system to ‘beat the hoops’. These doomed lost souls can often be seen wagering their credits, ship, left kidney or spouse on just one more throw of the hoops.
    
    Commanders of a more level-headed nature visit HoOpy Casinos for the purpose of trading. Those who can resist the charms of the complementary drinks and scantily clad croupiers available in the main casino area of the orbital platform may engage in trading on the Station’s commodity market. The Casinos often have a surplus or deficient of certain goods and thus a canny trader can sometimes make more profit by off-loading or purchasing his cargo at the Casino rather than the system’s GalCop station.
    
    The Casino is protected by a reasonably strong shield, although it carries no weapons. The Casino Management rely on the fact that the casino closely orbits the GalCop Station, which will launch Vipers to defend the orbital platform from attack. Additionally a small fleet of Security Sidewinders in HoOpy livery are stationed aboard to defend the Casino if need be.
    
    (Background story by Little Bear)
    
    (Original JS scripting by Paul Wilkins & Eric Walch)
    
    Background image: https://www.freepik.com/free-vector/red-frame-with-light-bulbs-dark-blue-brick-wall_199782073.htm Designed by Freepik www.freepik.com
    
    Update (1.3) for Oolite 1.80 by CaptSolo & spara.
    * New and retouched textures and flashy shader effects by CaptSolo. Shader by Griff, Neon sign model Arexic_Heretic. Docking bay by Griff.
    * BGS docking tunnel texture by spara
    * Sidewinder defenders updated to Griff's model.
    * Script restructured and updated to spawn with 1.80 populator by spara.
    * Station welcome screen is removed and games are moved to station interfaces (F4).
    * Station AI tweaked to be more aggressive against attacking. Code borrowed from YAH.
    * Two new gambling games: Smuggler's Run dice game and Video Poker. Scripting by spara. Card model and textures by CaptSolo. Sound effects are borrowed from Oolite. Dice game graphics by spara with two screenshots by Cody and Redspear.
    
    Update (1.3.1)
    * New gambling game: Blackjack.
    * Backgrounds added to the gard games.
    
    Update (1.3.2)
    * New gambling game: hOopy Hold'em. 
    * Market stripped to gems, gold and platinum. In 1.80 prices follow the main station rules. In 1.81 prices are 10% below main station. In other words market is solely meant for exchanging valuables to play credits, with 10% commission.
    * AI rewitten to JS. Casino now cruises around the main station at the edge of the Aegis.
    
    Update (1.3.3)
    * AI finished.
    
    Update (1.3.4)
    * No shader neonsign added.
    
    Update (1.3.5)
    * Holdem bugfixed
    * Shiny cards
    
    Update (2.0)
    * Fixed incorrect normal and emission map for Sidewinder.
    * Reverted to using default shaders for the Casino Ship.
    * Updated textures on Casino Ship, added new normal specular and gloss maps, plus RGB lighting effects.
    * Added flashers to docking bay of Casino ship.
    * Fixed issue with NaN values in dice game.
    * Moved all text into missiontext.plist for easier localisation.
    * New background image.
    * Made compatible with Xenon UI.
    * Spelling corrections.
    * Removed deprecated files.
    
    Update (2.1)
    * Added Library Config options to limit Casinos to Corporate States, and to control the minimum TL required.
    
    Update (2.2)
    * Small tweaks to role checking method for determining if the station is a casino ship.
    
    Update (2.3)
    * Created a single F4 Interfaces page entry for the casino (rather than having 5 different ones)
    
    Update (2.3.1)
    * New texture graphics have been added for the playing cards by Captain Beatnik.
    
    Update (2.3.2)
    * Minor changes in the playing cards design by Captain Beatnik.
    
    Update (2.3.3)
    * Removed Hoopy logo from hoops background images, as it was interfering with the menu.
    * Animated the lights on the opening menu background image.
    * Random version of "Hoopy" used in most locations.
    * Tweaks to shipdata.
    

    Equipment

    This expansion declares no equipment.

    Ships

    Name
    CoachWhip hOopy Casino
    casinoship_dockingbay
    Neon Sign
    hoopy_blackjack_card_back
    Video Poker Card
    hoopy_blackjack_card_pos10
    hoopy_blackjack_card_pos11
    hoopy_blackjack_card_pos12
    hoopy_blackjack_card_pos13
    hoopy_blackjack_card_pos14
    hoopy_blackjack_card_pos15
    hoopy_blackjack_card_pos16
    hoopy_blackjack_card_pos17
    hoopy_blackjack_card_pos18
    hoopy_blackjack_card_pos19
    hoopy_blackjack_card_pos2
    hoopy_blackjack_card_pos20
    hoopy_blackjack_card_pos21
    hoopy_blackjack_card_pos22
    hoopy_blackjack_card_pos23
    hoopy_blackjack_card_pos24
    hoopy_blackjack_card_pos25
    hoopy_blackjack_card_pos26
    hoopy_blackjack_card_pos27
    hoopy_blackjack_card_pos28
    hoopy_blackjack_card_pos29
    hoopy_blackjack_card_pos3
    hoopy_blackjack_card_pos30
    hoopy_blackjack_card_pos31
    hoopy_blackjack_card_pos32
    hoopy_blackjack_card_pos33
    hoopy_blackjack_card_pos34
    hoopy_blackjack_card_pos35
    hoopy_blackjack_card_pos36
    hoopy_blackjack_card_pos37
    hoopy_blackjack_card_pos38
    hoopy_blackjack_card_pos39
    hoopy_blackjack_card_pos4
    hoopy_blackjack_card_pos40
    hoopy_blackjack_card_pos41
    hoopy_blackjack_card_pos42
    hoopy_blackjack_card_pos43
    hoopy_blackjack_card_pos44
    hoopy_blackjack_card_pos45
    hoopy_blackjack_card_pos46
    hoopy_blackjack_card_pos47
    hoopy_blackjack_card_pos48
    hoopy_blackjack_card_pos49
    hoopy_blackjack_card_pos5
    hoopy_blackjack_card_pos50
    hoopy_blackjack_card_pos51
    hoopy_blackjack_card_pos52
    hoopy_blackjack_card_pos53
    hoopy_blackjack_card_pos54
    hoopy_blackjack_card_pos55
    hoopy_blackjack_card_pos6
    hoopy_blackjack_card_pos7
    hoopy_blackjack_card_pos8
    hoopy_blackjack_card_pos9
    Video Blackjack Card
    hoopy_casino_blackjack_intro
    Video Poker Card
    hoopy_casino_holdem_intro
    Video Poker Card
    hoopy_casino_poker_card_spin
    hoopy_holdem_card_back
    Video Hold'em Card
    hoopy_holdem_card_pos2
    hoopy_holdem_card_pos3
    hoopy_holdem_card_pos4
    hoopy_holdem_card_pos5
    hoopy_holdem_card_pos6
    hoopy_holdem_card_pos7
    hoopy_holdem_card_pos8
    hoopy_holdem_card_pos9
    hoopy_poker_card_back
    Video Poker Card
    hoopy_poker_card_pos2
    hoopy_poker_card_pos3
    hoopy_poker_card_pos4
    hoopy_poker_card_pos5
    Sidewinder Casino Security

    Models

    This expansion declares no models.

    Scripts

    Path
    Scripts/hoopy_blackjack.js
    "use strict";
    this.name = "hoopy_blackjack";
    this.author = "spara";
    this.copyright = "2014 spara";
    this.license = "CC BY-NC-SA 4.0";
    
    //init a new deck
    this.$virginDeck = function () {
    	var deck = new Array();
    	var suits = ["triangle", "crescent", "square", "star"];
    	for (var i = 0; i < 4; i++) {
    		for (var j = 1; j < 14; j++) {
    			var card = new Object();
    			card.suit = suits[i];
    			card.rank = j;
    			card.double = 0;
    			card.hide = 0;
    			if (j > 10) card.value = 10;
    			else if (j === 1) card.value = 11;
    			else card.value = card.rank;
    			deck.push(card);
    		}
    	}
    	return deck;
    }
    
    //calculate the value of a hand
    this.$calculateHandValue = function (cards) {
    	var finished = false;
    	while (!finished) {
    		var sum = 0;
    		for (var i = 0; i < cards.length; i++) {
    			sum += cards[i].value;
    		}
    		if (sum > 21) {
    			//change aces' value from 11 to 1
    			for (var i = 0; i < cards.length; i++) {
    				if (cards[i].value === 11) {
    					cards[i].value = 1;
    					break;
    				}
    			}
    			//all aces have been changed, sum stands.
    			if (i === cards.length)
    				finished = true;
    		}
    		else finished = true;
    	}
    	return sum;
    }
    
    //intro screen
    this.$intro = function () {
    
    	var credits = "\n" + expandMissionText("hoopy-credits", { amount: formatCredits(player.credits, true, true) });
    
    	//gambling choices
    	var options = {
    		"1_EXIT": expandMissionText("hoopy-exit")
    	};
    	if (player.credits >= 10)
    		options["2_10"] = expandMissionText("hoopy-casino-bet", { amount: formatCredits(10, true, true) });
    	else credits += expandMissionText("hoopy-no-credit");
    	if (player.credits >= 100)
    		options["3_50"] = expandMissionText("hoopy-casino-bet", { amount: formatCredits(50, true, true) });
    	if (player.credits >= 500)
    		options["4_100"] = expandMissionText("hoopy-casino-bet", { amount: formatCredits(100, true, true) });
    
    	mission.runScreen({
    		screenID: "hoopy_casino",
    		titleKey: "hoopy-blackjack-title",
    		message: expandMissionText("hoopy-blackjack-intro-story") + credits,
    		model: "hoopy_casino_blackjack_card_intro",
    		background: { name: "hoopy_table_bg2.png", height: 546 },
    		choices: options,
    		allowInterrupt: true,
    		exitScreen: "GUI_SCREEN_INTERFACES"
    	},
    		function (choice) {
    			if (!choice) choice = "1_EXIT";
    			if (choice === "2_10") this.$playGame(10);
    			else if (choice === "3_50") this.$playGame(50);
    			else if (choice === "4_100") this.$playGame(100);
    			else if (choice == "1_EXIT") worldScripts.hoopy_populator.$displayMenu();
    		}.bind(this)
    	);
    }
    
    //first pay, then deal
    this.$playGame = function (bet) {
    
    	//pay up first
    	player.credits -= bet;
    	this.$bet = bet;//initial bet
    	this.$totalBet = bet;//sum of all bets in round
    
    	//init a new deck
    	this.$playDeck = this.$virginDeck();
    
    	//dealer initial hand
    	var dealerHand = new Object();
    	dealerHand.cards = [this.$drawCard(), this.$drawCard()];
    	dealerHand.cards[1].hide = 1;
    	this.$dealerHands = [dealerHand];
    
    	//player initial hand
    	var playerHand = new Object();
    	playerHand.cards = [this.$drawCard(), this.$drawCard()];
    	playerHand.bet = bet;
    	this.$playerHands = [playerHand];
    
    	//lets play some blackjack
    	this.$deal(0);
    }
    
    this.$updateHandProps = function (hand, hands) {
    	hand.value = this.$calculateHandValue(hand.cards);
    	if (hand.value === 21) {
    		if (hands.length === 1 && hand.cards.length === 2) {
    			hand.status = "blackjack";
    		}
    		else hand.status = "21";
    	}
    	else if (hand.value > 21) hand.status = "busted";
    	else if (hand.status !== "stand") hand.status = "hit";
    }
    
    //Deal
    this.$deal = function () {
    
    	//rotate till playable hand
    	for (var hand = 0; hand < this.$playerHands.length; hand++) {
    		var playerHand = this.$playerHands[hand];
    		this.$updateHandProps(playerHand, this.$playerHands);
    		if (playerHand.value < 21 && playerHand.status !== "stand")
    			break;
    	}
    
    	//dealer's turn?
    	if (hand === this.$playerHands.length) {
    		this.$resolve();
    		return;
    	}
    
    	//choices
    	var options = {};
    	if (playerHand.status === "hit") {
    		options["1_HIT"] = expandMissionText("hoopy-blackjack-hit");
    		options["2_STAND"] = expandMissionText("hoopy-blackjack-stand");
    	}
    	if (playerHand.cards.length === 2 && playerHand.status === "hit" && player.credits >= this.$bet) {
    		options["3_DOUBLE"] = expandMissionText("hoopy-blackjack-double", { bet: formatCredits(this.$bet, true, true) });
    	}
    	if (playerHand.cards.length === 2 && (playerHand.cards[0].value === playerHand.cards[1].value || playerHand.cards[0].rank === playerHand.cards[1].rank) && this.$playerHands.length < 4) {
    		options["4_SPLIT"] = expandMissionText("hoopy-blackjack-split", { bet: formatCredits(this.$bet, true, true) });
    	}
    	if (playerHand.cards.length === 2 && this.$playerHands.length === 1) {
    		options["5_SURRENDER"] = expandMissionText("hoopy-blackjack-surrender");
    	}
    
    	//status message
    	var message = expandMissionText("hoopy-blackjack-status", { player_hand: playerHand.value, bet: formatCredits(this.$totalBet, true, true), credits: formatCredits(player.credits, true, true) });
    
    	mission.runScreen({
    		screenID: "hoopy_casino",
    		titleKey: "hoopy-blackjack-title",
    		model: "hoopy_casino_blackjack_card_base",
    		background: { name: "hoopy_table_bg2.png", height: 546 },
    		message: message,
    		spinModel: false,
    		choices: options,
    	},
    		function (choice) {
    			if (choice === "1_HIT") {
    				playerHand.cards.push(this.$drawCard());
    				this.$deal();
    				return;
    			}
    			else if (choice === "2_STAND") {
    				playerHand.status = "stand";
    				this.$deal();
    				return;
    			}
    			else if (choice === "3_DOUBLE") {
    				playerHand.cards.push(this.$drawCard());
    				this.$updateHandProps(playerHand, this.$playerHands);
    				if (playerHand.value < 21) playerHand.status = "stand";
    				player.credits -= this.$bet;
    				this.$totalBet += this.$bet;
    				playerHand.bet = 2 * playerHand.bet;
    				this.$deal();
    				return;
    			}
    			else if (choice === "4_SPLIT") {
    
    				//limit to one card after splitting aces!!!!!!!
    				if (playerHand.cards[0].rank === 1) {
    					playerHand.cards[0].value = 11;
    					playerHand.cards[1].value = 11;
    				}
    				player.credits -= this.$bet;
    				this.$totalBet += this.$bet;
    				var newHand = new Object();
    				newHand.cards = [playerHand.cards.pop(), this.$drawCard()];
    				newHand.bet = this.$bet;
    				this.$playerHands.push(newHand);
    				playerHand.cards.push(this.$drawCard());
    				this.$deal();
    				return;
    			}
    			else if (choice === "5_SURRENDER") {
    				playerHand.status = "surrender";
    				this.$resolve();
    				return;
    			}
    			else {
    				this.$intro();
    				return;
    			}
    		}.bind(this)
    	);
    
    	this.$updateScreen();
    
    	//update flasher markers
    	//while playing: yellow - current hand, red - busted hand
    	for (var i = 0; i < this.$playerHands.length; i++) {
    		var status = this.$playerHands[i].status;
    		if (i === hand)
    			mission.displayModel.flashers[i].color = "yellowColor";
    		else if (status === "busted")
    			mission.displayModel.flashers[i].color = "redColor";
    		else {
    			mission.displayModel.flashers[i].position = [0, 0, 50];
    		}
    	}
    	for (var j = i; j < 4; j++)
    		mission.displayModel.flashers[j].position = [0, 0, 50];
    }
    
    this.$resolve = function () {
    
    	var dealerHand = this.$dealerHands[0];
    	this.$updateHandProps(dealerHand, this.$dealerHands);
    	dealerHand.cards[1].hide = false;
    
    	//check player for non-busted hands
    	var busted = true;
    	for (var i = 0; i < this.$playerHands.length; i++) {
    		if (this.$playerHands[i].status !== "busted") {
    			busted = false;
    			break;
    		}
    	}
    
    	//if all player cards are busted, dealer automatically wins
    	if (busted || this.$playerHands[i].status === "surrender") {
    		for (var i = 0; i < this.$playerHands.length; i++) {
    			this.$playerHands[i].result = "loss";
    		}
    	}
    	else {
    		//start drawing cards until we go over 16.
    		while (dealerHand.value < 17) {
    			dealerHand.cards.push(this.$drawCard());
    			this.$updateHandProps(dealerHand, this.$dealerHands);
    		}
    		//compare player hands to the dealer hand
    		for (var i = 0; i < this.$playerHands.length; i++) {
    			var playerHand = this.$playerHands[i];
    			//if the player has not busted, we'll compare
    			if (playerHand.status !== "busted") {
    				//blackjack
    				if (playerHand.status === "blackjack") {
    					if (dealerHand.status === "blackjack")
    						playerHand.result = "push";
    					else playerHand.result = "win";
    				}
    				//push
    				else if (dealerHand.value === playerHand.value)
    					playerHand.result = "push";
    				//win
    				else if (dealerHand.value > 21 || (21 - playerHand.value < 21 - dealerHand.value))
    					playerHand.result = "win";
    				//loss
    				else playerHand.result = "loss";
    			}
    			//if the player has busted, it's a loss
    			else playerHand.result = "loss";
    		}
    	}
    
    	//calculate the winnings
    	var win = 0;
    	for (var i = 0; i < this.$playerHands.length; i++) {
    		var playerHand = this.$playerHands[i];
    		if (playerHand.result === "win") {
    			if (playerHand.status === "blackjack")
    				win += 2.5 * playerHand.bet;
    			else win += 2 * playerHand.bet;
    		}
    		else if (playerHand.result === "push")
    			win += playerHand.bet;
    		else if (playerHand.status === "surrender" && dealerHand.status !== "blackjack")
    			win += playerHand.bet / 2;
    	}
    	player.credits += win;
    
    	//status message
    	var resultMessage = "";
    
    	//player
    	for (var i = 0; i < this.$playerHands.length; i++) {
    		var playerHand = this.$playerHands[i]
    		resultMessage += expandMissionText("hoopy-blackjack-playerhand");
    		if (playerHand.status === "blackjack")
    			resultMessage += expandMissionText("hoopy-blackjack-result-bj") + "\n";
    		else if (playerHand.status === "busted")
    			resultMessage += expandMissionText("hoopy-blackjack-result-bust") + "\n";
    		else if (playerHand.result === "push")
    			resultMessage += expandMissionText("hoopy-blackjack-result-push") + "\n";
    		else if (playerHand.status === "surrender")
    			resultMessage += expandMissionText("hoopy-blackjack-result-surrender") + "\n";
    		else resultMessage += playerHand.value + "\n";
    	}
    
    	//dealer
    	if (dealerHand.status === "blackjack")
    		resultMessage += expandMissionText("hoopy-blackjack-dealer-status", { result: expandMissionText("hoopy-blackjack-result-bj") }) + "\n\n"
    	else if (dealerHand.status === "busted")
    		resultMessage += expandMissionText("hoopy-blackjack-dealer-status", { result: expandMissionText("hoopy-blackjack-result-bust") }) + "\n\n";
    	else resultMessage += expandMissionText("hoopy-blackjack-dealer-status", { result: dealerHand.value }) + "\n\n";
    
    	//winnings
    	if (win - this.$totalBet > 0)
    		resultMessage += expandMissionText("hoopy-blackjack-win", { amount: formatCredits(win - this.$totalBet, true, true) }) + "\n";
    	else if (win - this.$totalBet < 0)
    		resultMessage += expandMissionText("hoopy-blackjack-lose", { amount: formatCredits(this.$totalBet - win, true, true) }) + "\n";
    	else resultMessage += expandMissionText("hoopy-blackjack-even") + "\n"
    
    	resultMessage += expandMissionText("hoopy-credits", { amount: formatCredits(player.credits, true, true) });
    
    	var options = {
    		"2_REBET": expandMissionText("hoopy-casino-change-bet"),
    		"3_EXIT": expandMissionText("hoopy-exit")
    	};
    	if (player.credits >= this.$bet)
    		options["1_AGAIN"] = expandMissionText("hoopy-dealagain", { amount: formatCredits(this.$bet, true, true) });
    
    	mission.runScreen({
    		screenID: "hoopy_casino",
    		titleKey: "hoopy-blackjack-title",
    		model: "hoopy_casino_blackjack_card_base",
    		message: resultMessage,
    		spinModel: false,
    		background: { name: "hoopy_table_bg2.png", height: 546 },
    		choices: options,
    		allowInterrupt: true,
    		exitScreen: "GUI_SCREEN_INTERFACES"
    	},
    		function (choice) {
    			if (choice === "1_AGAIN") {
    				this.$playGame(this.$bet);
    				return;
    			} else if (choice === "2_REBET") {
    				this.$intro();
    				return;
    			} else if (choice === "3_EXIT") {
    				worldScripts.hoopy_populator.$displayMenu();
    				return;
    			}
    		}.bind(this)
    	);
    
    	this.$updateScreen(-1);
    
    	//red: lose
    	//green: win
    	//yellow: push
    	for (var i = 0; i < this.$playerHands.length; i++) {
    		var result = this.$playerHands[i].result;
    		if (result === "win")
    			mission.displayModel.flashers[i].color = "greenColor";
    		else if (result === "loss")
    			mission.displayModel.flashers[i].color = "redColor";
    		else mission.displayModel.flashers[i].color = "yellowColor";
    	}
    	for (var j = i; j < 4; j++)
    		mission.displayModel.flashers[j].position = [0, 0, 50];
    }
    
    //draws a card from the current deck in play
    this.$drawCard = function () {
    	return this.$playDeck.splice(Math.floor(this.$playDeck.length * Math.random()), 1)[0];
    }
    
    //update textures and positions for cards
    this.$updateScreen = function () {
    	//player cards subents 0-10, 11-21, 22 -32, 33-43
    	//dealer cards subents 44-54
    
    	//player cards
    	for (var i = 0; i < this.$playerHands.length; i++) {
    		//show player hand
    		var playerHand = this.$playerHands[i];
    		for (var j = 0; j < playerHand.cards.length; j++) {
    			var card = playerHand.cards[j];
    			var fileName = "hoopy_card_" + card.suit + "_" + card.rank + ".png";
    			var place = i * 11 + j;
    			mission.displayModel.subEntities[place].setMaterials({ "casino_poker_card_diffuse.png": { diffuse_map: fileName, emission_map: fileName, emission_modulate_color: "darkGrayColor" } });
    		}
    		//double down
    		if (playerHand.bet === 2 * this.$bet) {
    			mission.displayModel.subEntities[place].orientation = [0.5, 0.5, 0.5, 0.5];
    		}
    		//hide non used player subents from view
    		for (var k = j; k < 11; k++) this.$hideCard(i * 11 + k);
    	}
    	//hide non used player subents from view
    	for (var k = i * 11; k < 44; k++) this.$hideCard(k);
    
    	//dealer cards
    	var dealerHand = this.$dealerHands[0];
    	for (var j = 0; j < dealerHand.cards.length; j++) {
    		var card = dealerHand.cards[j];
    		if (card.hide)
    			var fileName = "casino_blackjack_card_diffuse.png";
    		else
    			var fileName = "hoopy_card_" + card.suit + "_" + card.rank + ".png";
    		mission.displayModel.subEntities[4 * 11 + j].setMaterials({ "casino_poker_card_diffuse.png": { diffuse_map: fileName, emission_map: fileName, emission_modulate_color: "darkGrayColor" } });
    	}
    	//hide non used dealer subents from view
    	for (var k = 4 * 11 + j; k < 54; k++) this.$hideCard(k);
    }
    
    //moves the cards to the centerline and turns them out of view
    this.$hideCard = function (place) {
    	var position = mission.displayModel.subEntities[place].position;
    	position[2] = 0;
    	mission.displayModel.subEntities[place].position = position;
    	var orientation = [0, 0, 0, 1];
    	mission.displayModel.subEntities[place].orientation = orientation;
    }
    
    Scripts/hoopy_casinoship.js
    "use strict";
    this.name = "HoopyCasinoShip";
    this.author = "phkb";
    this.copyright = "2026 phkb";
    this.license = "CC BY-NC-SA 4.0";
    
    this._max = 6000; // maximum value for any of the components
    this._inc = 250; // how fast the color changes
    this._rgb = [0, 0, 0];
    this._change = [0, 0, 0];
    
    this.shipSpawned = function () {
    	if (this.ship.dataKey != "casinoship") return;
    	var check1 = Math.random();
    	var range = this._max / this._inc;
    	if (check1 > 0.67) {
    		this._rgb = [0, Math.floor(Math.random() * range) * this._inc, Math.floor(Math.random() * range) * this._inc];
    		this._change = [0, this._inc, this._inc * -1];
    	} else if (check1 > 0.33) {
    		this._rgb = [Math.floor(Math.random() * range) * this._inc, 0, Math.floor(Math.random() * range) * this._inc];
    		this._change = [this._inc * -1, 0, this._inc];
    	} else {
    		this._rgb = [Math.floor(Math.random() * range) * this._inc, Math.floor(Math.random() * range) * this._inc, 0];
    		this._change = [this._inc, this._inc * -1, 0];
    	}
    	this._timer = new Timer(this, this.$changeColor.bind(this), 0.25, 0.25);
    }
    
    this.shipDied = function () {
    	if (this._timer) this._timer.stop();
    }
    
    this.shipRemoved = function () {
    	if (this._timer) this._timer.stop();
    }
    
    this.$changeColor = function $changeColor() {
    	var that = $changeColor;
    	var _rgb = (that._rgb = that._rgb || this._rgb);
    	var _change = (that._change = that._change || this._change);
    	var _max = (that._max = that._max || this._max);
    	var _inc = (that._inc = that._inc || this._inc);
    
    	if (!this.ship || this.ship.isValid == false) {
    		this._timer.stop();
    		return;
    	}
    	_rgb[0] += _change[0];
    	if (_rgb[0] < 0) { _rgb[0] = 0; _change[0] = 0; _change[1] = _inc; }
    	if (_rgb[0] > _max) { _rgb[0] = _max; _change[0] = _inc * -1; }
    	_rgb[1] += _change[1];
    	if (_rgb[1] < 0) { _rgb[1] = 0; _change[1] = 0; _change[2] = _inc; }
    	if (_rgb[1] > _max) { _rgb[1] = _max; _change[1] = _inc * -1; }
    	_rgb[2] += _change[2];
    	if (_rgb[2] < 0) { _rgb[2] = 0; _change[2] = 0; _change[0] = _inc; }
    	if (_rgb[2] > _max) { _rgb[2] = _max; _change[2] = _inc * -1; }
    
    	this.ship.setMaterials({
    		"hoopyCasino_diffuse.png": {
    			diffuse_map: "hoopyCasino_diffuse.png",
    			normal_map: "hoopyCasino_normal.png",
    			specular_map: "hoopyCasino_specular.png",
    			specular_color: [0.1, 0.1, 0.1],
    			emission_map: "hoopyCasino_effects.png",
    			gloss: 0.7,
    			emission_modulate_color: {
    				red: _rgb[0],
    				green: _rgb[1],
    				blue: _rgb[2]
    			}
    		},
    		"hoopyCasino_diffuse2.png": {
    			diffuse_map: "hoopyCasino_diffuse2.png",
    			normal_map: "hoopyCasino_normal.png",
    			specular_map: "hoopyCasino_specular.png",
    			specular_color: [0.1, 0.1, 0.1],
    			emission_map: "hoopyCasino_effects.png",
    			gloss: 0.7,
    			emission_modulate_color: {
    				red: _rgb[0],
    				green: _rgb[1],
    				blue: _rgb[2]
    			}
    		}
    	});
    }
    Scripts/hoopy_dice.js
    "use strict";
    this.name = "hoopy_dice";
    this.author = "spara";
    this.copyright = "2014 spara";
    this.license = "CC BY-NC-SA 3.0";
    
    //game track with bet multipliers
    this.$track = [0, 0, 0, 4, 0, 8, 0, 17, 0, 35];
    
    //helper function for tabulating text on screen
    this.$tabulate = function (line, width) {
    	var hairSpace = String.fromCharCode(31);
    	var space = defaultFont.measureString(" ");
    	while (defaultFont.measureString(line) < width - space)
    		line += " ";
    	while (defaultFont.measureString(line) < width)
    		line += hairSpace;
    	return line;
    }
    
    //intro screen
    this.$intro = function () {
    	var bets = [10, 50, 100];
    	//build and format the payoff table for screen
    	var payoffMessage = expandMissionText("hoopy-dice-correct");
    	for (var i = 0; i < 3; i++) {
    		payoffMessage = this.$tabulate(payoffMessage, 5 * (i + 1));
    		payoffMessage += formatCredits(bets[i], true, true);
    	}
    	payoffMessage = "\n\n" + payoffMessage + "\n\n";
    	for (i = this.$track.length - 1; i > 0; i--) {
    		if (this.$track[i] !== 0) {
    			var line = i + 1;
    			for (var j = 0; j < 3; j++) {
    				line = this.$tabulate(line, 5 * (j + 1));
    				line += this.$track[i] * bets[j];
    			}
    			payoffMessage += line + "\n";
    		}
    	}
    
    	//format player credits for screen
    	var credits = expandMissionText("hoopy-credits", { amount: formatCredits(player.credits, true, true) });
    
    	var options = {
    		"1_EXIT": expandMissionText("hoopy-exit")
    	};
    	if (player.credits >= 10)
    		options["2_10"] = expandMissionText("hoopy-casino-bet", { amount: formatCredits(10, true, true) });
    	else credits += expandMissionText("hoopy-no-credit");
    	if (player.credits >= 50)
    		options["3_50"] = expandMissionText("hoopy-casino-bet", { amount: formatCredits(50, true, true) });
    	if (player.credits >= 100)
    		options["4_100"] = expandMissionText("hoopy-casino-bet", { amount: formatCredits(100, true, true) });
    	mission.runScreen({
    		screenID: "hoopy_casino",
    		titleKey: "hoopy-dice-title",
    		message: expandMissionText("hoopy-dice-intro-story") + payoffMessage + credits,
    		choices: options,
    		background: "hoopy_dice_bg-intro.png"
    	},
    		function (choice) {
    			if (choice === "2_10") this.$playGame(10);
    			else if (choice === "3_50") this.$playGame(50);
    			else if (choice === "4_100") this.$playGame(100);
    			else if (choice == "1_EXIT") worldScripts.hoopy_populator.$displayMenu();
    		}.bind(this)
    	);
    }
    
    //da game
    this.$playGame = function (bet, prevSum, round, track, select) {
    	//init the game
    	if (typeof round == "undefined") {
    		var round = 0;
    		track = "";
    		player.credits -= bet;
    	}
    
    	//roll the dice
    	var die1 = Math.ceil(6 * Math.random());
    	var die2 = Math.ceil(6 * Math.random());
    	var sum = die1 + die2;
    
    	//check the guess
    	var pass = true;
    	if (typeof prevSum != "undefined") {
    		if (select === "high_same") {
    			if (sum < prevSum)
    				pass = false;
    		}
    		else if (select === "high") {
    			if (sum <= prevSum)
    				pass = false;
    		}
    		else if (select === "low_same") {
    			if (sum > prevSum)
    				pass = false;
    		}
    		else if (select === "low") {
    			if (sum >= prevSum)
    				pass = false;
    		}
    	}
    
    	//debug	
    	//pass = true; 
    
    	//build the track display
    	track += sum;
    	if (round <= this.$track.length - 1)
    		track += " > ";
    	var messageTrack = track;
    	for (var i = round; i < this.$track.length; i++) {
    		if (this.$track[i] !== 0)
    			messageTrack += formatCredits(bet * this.$track[i], false, true);
    		else messageTrack += "?";
    		if (i < this.$track.length - 1)
    			messageTrack += " > ";
    	}
    	//correct guess
    	if (pass) {
    		var options = {};
    		//cash out
    		if (round > 0 && this.$track[round - 1] !== 0) {
    			var cash_out = formatCredits(bet * this.$track[round - 1], false, true);
    			options["1_CASH"] = expandMissionText("hoopy-dice-cashout", { amount: cash_out });
    			var message = expandMissionText("hoopy-dice-track-cash");
    			var background = "hoopy_dice_bg-cash.png";
    		}
    		else {
    			var message = expandMissionText("hoopy-dice-track-basic");
    			var background = "hoopy_dice_bg-basic.png";
    		}
    		//guess choices
    		if (round !== this.$track.length) {
    			if (sum >= 7)
    				options["2_HIGH_SAME"] = expandMissionText("hoopy-dice-highsame");
    			else
    				options["3_HIGH"] = expandMissionText("hoopy-dice-high");
    			if (sum <= 7)
    				options["4_LOW_SAME"] = expandMissionText("hoopy-dice-lowsame");
    			else
    				options["5_LOW"] = expandMissionText("hoopy-dice-low");
    		}
    		else
    			var message = expandMissionText("hoopy-dice-track-final");
    		mission.runScreen({
    			screenID: "hoopy_casino",
    			titleKey: "hoopy-dice-title",
    			overlay: "hoopy_dice_" + die1 + "-" + die2 + ".png",
    			message: messageTrack + message,
    			choices: options,
    			background: background
    		},
    			function (choice) {
    				if (choice === "1_CASH") {
    					this.$cashOut(bet, round);
    					return;
    				}
    				else if (choice === "2_HIGH_SAME") {
    					this.$playGame(bet, sum, round + 1, track, "high_same");
    					return;
    				}
    				else if (choice === "3_HIGH") {
    					this.$playGame(bet, sum, round + 1, track, "high");
    					return;
    				}
    				else if (choice === "4_LOW_SAME") {
    					this.$playGame(bet, sum, round + 1, track, "low_same");
    					return;
    				}
    				else if (choice === "5_LOW") {
    					this.$playGame(bet, sum, round + 1, track, "low");
    					return;
    				}
    			}.bind(this)
    		);
    	} else {
    		var options = {
    			"1_EXIT": expandMissionText("hoopy-exit"),
    			"2_AGAIN": expandMissionText("hoopy-play-again")
    		};
    		mission.runScreen({
    			screenID: "hoopy_casino",
    			titleKey: "hoopy-dice-title",
    			overlay: "hoopy_dice_" + die1 + "-" + die2 + ".png",
    			message: messageTrack + expandMissionText("hoopy-dice-track-fail"),
    			choices: options,
    			background: "hoopy_dice_bg-fail.png"
    		},
    			function (choice) {
    				if (choice === "2_AGAIN") {
    					this.$intro();
    					return;
    				}
    				if (choice === "1_EXIT") {
    					worldScripts.hoopy_populator.$displayMenu();
    					return;
    				}
    			}.bind(this)
    		);
    	}
    }
    
    this.$cashOut = function (bet, round) {
    	var win = this.$track[round - 1] * bet
    	player.credits += win;
    	var options = {
    		"1_EXIT": expandMissionText("hoopy-exit"),
    		"2_AGAIN": expandMissionText("hoopy-play-again")
    	};
    	mission.runScreen({
    		screenID: "hoopy_casino",
    		titleKey: "hoopy-dice-title",
    		message: formatCredits(win, false, true) + expandMissionText("hoopy-dice-track-win"),
    		choices: options,
    		background: "hoopy_dice_bg-win.png"
    	},
    		function (choice) {
    			if (choice === "2_AGAIN") {
    				this.$intro();
    				return;
    			}
    			if (choice === "1_EXIT") {
    				worldScripts.hoopy_populator.$displayMenu();
    				return;
    			}
    		}.bind(this)
    	);
    }
    
    Scripts/hoopy_holdem.js
    "use strict";
    this.name = "hoopy_holdem";
    this.author = "spara";
    this.copyright = "2014 spara";
    this.license = "CC BY-NC-SA 4.0";
    
    this.$highNames = expandMissionText("hoopy-holdem-highnames").split("|");
    this.$handRanks = expandMissionText("hoopy-holdem-handranks").split("|");
    
    //init a new deck
    this.$virginDeck = function () {
    	var deck = new Array();
    	var suits = ["triangle", "crescent", "square", "star"];
    	for (var i = 0; i < 4; i++) {
    		for (var j = 1; j < 14; j++) {
    			var card = new Object();
    			card.suit = suits[i];
    			card.rank = j;
    			card.show = false;
    			deck.push(card);
    		}
    	}
    	return deck;
    }
    
    //draws a card from the current deck in play
    this.$drawCard = function () {
    	return this.$playDeck.splice(Math.floor(this.$playDeck.length * Math.random()), 1)[0];
    }
    
    this.$intro = function () {
    	var credits = expandMissionText("hoopy-credits", { amount: formatCredits(player.credits, true, true) });
    	var options = {};
    	options["1_EXIT"] = expandMissionText("hoopy-exit");
    	if (player.credits >= 30)
    		options["2_10"] = expandMissionText("hoopy-casino-bet", { amount: formatCredits(10, true, true) });
    	else credits += expandMissionText("hoopy-no-credit");
    	if (player.credits >= 150)
    		options["3_50"] = expandMissionText("hoopy-casino-bet", { amount: formatCredits(50, true, true) });
    	if (player.credits >= 300)
    		options["4_100"] = expandMissionText("hoopy-casino-bet", { amount: formatCredits(100, true, true) });
    
    	mission.runScreen({
    		screenID: "hoopy_casino",
    		title: expandMissionText("hoopy-holdem-title", {name: worldScripts.hoopy_populator._hoopyName}),
    		message: expandMissionText("hoopy-holdem-intro-story") + credits,
    		model: "hoopy_casino_holdem_intro",
    		background: { name: "hoopy_table_bg2.png", height: 546 },
    		choices: options,
    		allowInterrupt: true,
    		exitScreen: "GUI_SCREEN_INTERFACES"
    	},
    		function (choice) {
    			if (choice === "2_10") this.$play(10);
    			else if (choice === "3_50") this.$play(50);
    			else if (choice === "4_100") this.$play(100);
    			else if (choice == "1_EXIT") worldScripts.hoopy_populator.$displayMenu();
    			return;
    		}.bind(this)
    	);
    }
    
    //first pay, then deal
    this.$play = function (bet) {
    	player.credits -= bet;
    	this.$ante = bet;
    	this.$bets = 0;
    	this.$playDeck = this.$virginDeck();
    	this.$cards = new Array();
    	for (var i = 0; i < 9; i++) this.$cards.push(this.$drawCard());
    	this.$deal("deal");
    }
    
    this.$deal = function (phase) {
    	var message = expandMissionText("hoopy-holdem-ante", { amount: formatCredits(this.$ante, true, true) });
    	if (this.$bets > 0)
    		message += expandMissionText("hoopy-holdem-bets", { amount: formatCredits(this.$bets, true, true) });
    	message += "\n" + expandMissionText("hoopy-credits", { amount: formatCredits(player.credits, true, true) });
    	var options = {};
    	switch (phase) {
    		case "deal": this.$cards[7].show = true;
    			this.$cards[8].show = true;
    			options["1_FLOP"] = expandMissionText("hoopy-holdem-play", { amount: formatCredits(this.$ante * 2, true, true) });
    			options["2_FOLD"] = expandMissionText("hoopy-fold");
    			break;
    		case "flop": this.$cards[2].show = true;
    			this.$cards[3].show = true;
    			this.$cards[4].show = true;
    			options["3_CHECK_TURN"] = expandMissionText("hoopy-check");
    			if (player.credits >= this.$ante)
    				options["4_BET_TURN"] = expandMissionText("hoopy-holdem-bet", { amount: formatCredits(this.$ante, true, true) });
    			break;
    		case "turn": this.$cards[5].show = true;
    			options["5_CHECK_RIVER"] = expandMissionText("hoopy-check");
    			if (player.credits >= this.$ante)
    				options["6_BET_RIVER"] = expandMissionText("hoopy-holdem-bet", { amount: formatCredits(this.$ante, true, true) });
    			break;
    	};
    	mission.runScreen({
    		screenID: "hoopy_casino",
    		titleKey: "hoopy-holdem-title",
    		model: "hoopy_casino_holdem_base",
    		message: message,
    		background: { name: "hoopy_table_bg2.png", height: 546 },
    		spinModel: false,
    		choices: options
    	},
    		function (choice) {
    			switch (choice) {
    				case "1_FLOP": player.credits -= 2 * this.$ante;
    					this.$bets += 2 * this.$ante;
    					this.$deal("flop");
    					return;
    				case "2_FOLD": this.$showDown(true);
    					return;
    				case "3_CHECK_TURN": this.$deal("turn");
    					return;
    				case "4_BET_TURN": player.credits -= this.$ante;
    					this.$bets += this.$ante;
    					this.$deal("turn");
    					return;
    				case "5_CHECK_RIVER": this.$showDown(false);
    					return;
    				case "6_BET_RIVER": player.credits -= this.$ante;
    					this.$bets += this.$ante;
    					this.$showDown(false);
    					return;
    			}
    		}.bind(this)
    	);
    	this.$updateCards();
    }
    
    this.$handName = function (value, high) {
    	if (value > 0)
    		return this.$handRanks[value];
    	else
    		return this.$highNames[high - 7] + " " + this.$handRanks[0];
    }
    
    this.$formatHand = function (hand) {
    	var formatted = "(";
    	for (var i = 0; i < 5; i++) {
    		var value = hand[0][i].value;
    		if (value === 11) value = expandMissionText("hoopy-casino-jack");
    		else if (value === 12) value = expandMissionText("hoopy-casino-queen");
    		else if (value === 13) value = expandMissionText("hoopy-casino-king");
    		else if (value === 14 || value === 1) value = expandMissionText("hoopy-casino-ace");
    		formatted += value;
    		if (i < 4) formatted += ", ";
    		else formatted += ")";
    	}
    	return formatted;
    }
    
    this.$showDown = function (fold) {
    	for (var i = 0; i < 9; i++) this.$cards[i].show = true;
    	var playerHand = this.$bestHand(2);
    	var dealerHand = this.$bestHand(0);
    	var playerValues = "\n" + this.$formatHand(playerHand) + "\n\n";
    	var dealerValues = "\n" + this.$formatHand(dealerHand) + "\n\n";
    	var message = expandMissionText("hoopy-holdem-player", { hand: this.$handName(playerHand[1], playerHand[0][0].value) + playerValues });
    	message += expandMissionText("hoopy-holdem-dealer", { hand: this.$handName(dealerHand[1], dealerHand[0][0].value) + dealerValues });
    
    	if (fold) var result = "fold";
    	else {
    		var result = "push";
    		if (dealerHand[1] > playerHand[1])
    			result = "loss";
    		else if (dealerHand[1] < playerHand[1]) {
    			result = "win";
    		} else {
    			for (var i = 0; i < 5; i++) {
    				if (playerHand[0][i].value > dealerHand[0][i].value) {
    					result = "win";
    					break;
    				} else if (playerHand[0][i].value < dealerHand[0][i].value) {
    					result = "loss";
    					break;
    				}
    			}
    		}
    	}
    	switch (result) {
    		case "win": if (playerHand[1] > 3)
    			var win = 2 * (this.$bets + this.$ante);
    		else var win = this.$ante + 2 * (this.$bets);
    			message += expandMissionText("hoopy-holdem-win", { amount: formatCredits(win - this.$bets - this.$ante, true, true) });
    			player.credits += win;
    			break;
    		case "loss": message += expandMissionText("hoopy-holdem-dealerwin", { amount: formatCredits(this.$bets + this.$ante, true, true) });
    			break;
    		case "push": message += expandMissionText("hoopy-holdem-push");
    			player.credits += this.$bets + this.$ante;
    			break;
    		case "fold": message += expandMissionText("hoopy-holdem-fold", { amount: formatCredits(this.$ante, true, true) });
    			break;
    	}
    	message += "\n" + expandMissionText("hoopy-credits", { amount: formatCredits(player.credits, true, true) });
    	var options = {};
    	if (player.credits >= 3 * this.$ante)
    		options["7_DEAL"] = expandMissionText("hoopy-dealagain", { amount: formatCredits(this.$ante, true, true) });
    	options["8_BET"] = expandMissionText("hoopy-holdem-changeante");
    	options["9_EXIT"] = expandMissionText("hoopy-exit");
    	mission.runScreen({
    		screenID: "hoopy_casino",
    		titleKey: "hoopy-holdem-title",
    		model: "hoopy_casino_holdem_base",
    		message: message,
    		background: { name: "hoopy_table_bg2.png", height: 546 },
    		spinModel: false,
    		choices: options,
    		allowInterrupt: true,
    		exitScreen: "GUI_SCREEN_INTERFACES"
    	},
    		function (choice) {
    			switch (choice) {
    				case "7_DEAL": 
    					this.$play(this.$ante);
    					return;
    				case "8_BET": 
    					this.$intro(1);
    					return;
    				case "9_EXIT": 
    					worldScripts.hoopy_populator.$displayMenu();
    					return;
    			};
    		}.bind(this)
    	);
    	this.$updateCards();
    }
    
    this.$updateCards = function () {
    	for (var i = 0; i < 9; i++) {
    		var card = this.$cards[i];
    		if (card.show)
    			var fileName = "hoopy_card_" + card.suit + "_" + card.rank + ".png";
    		else var fileName = "casino_holdem_card_diffuse.png";
    		mission.displayModel.subEntities[i].setMaterials({ "casino_poker_card_diffuse.png": { diffuse_map: fileName, emission_map: fileName, emission_modulate_color: "darkGrayColor" } });
    	}
    }
    
    this.$bestHand = function (start) {
    
    	//make a new hand with new objects
    	var hand = new Array();
    	for (var i = start; i < start + 7; i++) {
    		var suits = ["triangle", "crescent", "square", "star"];
    		var card = new Object();
    		card.value = this.$cards[i].rank;
    		card.suit = suits.indexOf(this.$cards[i].suit);
    		if (card.value === 1) card.value = 14;
    		hand.push(card);
    	}
    
    	//order cards by suit
    	hand.sort(function (a, b) { return a.suit - b.suit });
    
    	//handle flushes first	
    	if (hand[0].suit === hand[4].suit || hand[1].suit === hand[5].suit || hand[2].suit === hand[6].suit) {
    		//suit of flush
    		var suit = hand[2].suit;
    		//remove wrong suit cards
    		for (var i = 0; i < 7; i++) {
    			if (hand[6 - i].suit !== suit)
    				hand.splice(6 - i, 1);
    		}
    		//order cards by value
    		hand.sort(function (a, b) { return b.value - a.value })
    		//handle possible ace by duplicating to a different value
    		if (hand[0].value === 14) {
    			card = new Object();
    			card.value = 1;
    			card.suit = suit;
    			hand.push(card);
    		}
    		//look for a straight
    		var straight = -1;
    		var length = 0;
    		for (var i = 0; i < hand.length - 1; i++) {
    			//tracking a straight
    			if (hand[i].value - hand[i + 1].value === 1) length++;
    			else if (hand[i].value - hand[i + 1].value != 0) length = 0;
    			if (length === 4) {
    				//highest straight starts from index i - 3
    				straight = i - 3;
    				break;
    			}
    		}
    		//straight flush found
    		if (straight > -1) {
    			hand = hand.splice(straight, 5);
    			//royal flush
    			if (hand[0].value === 14)
    				return [hand, 9];
    			//straight flush
    			else return [hand, 8];
    		}
    		//a plain flush found
    		else {
    			//take five most valuable cards
    			hand = hand.splice(0, 5);
    			return [hand, 5];
    		}
    	}
    
    	//order cards by value
    	hand.sort(function (a, b) { return b.value - a.value });
    
    	//handle possible ace by duplicating to a different value
    	if (hand[0].value === 14) {
    		card = new Object();
    		card.value = 1;
    		card.suit = hand[0].suit;
    		hand.push(card);
    	}
    
    	//look for a straight
    	var straight = -1;
    	var length = 0;
    	for (var i = 0; i < hand.length - 1; i++) {
    		//tracking a straight
    		if (hand[i].value - hand[i + 1].value === 1) length++;
    		else if (hand[i].value - hand[i + 1].value != 0) length = 0;
    		if (length === 4) {
    			//highest straight starts from index i - 3
    			straight = i - 3;
    			break;
    		}
    	}
    
    	//straight found
    	if (straight > -1) {
    		hand = hand.splice(straight, 5);
    		return [hand, 4];
    	}
    
    	//remove possible added ace. this was needed for straight search
    	if (hand[0].value === 14)
    		hand.pop();
    
    	//make a new array of the cards so that cards with the same value are grouped ot sub arrays
    	var grouped = new Array();
    	grouped.push([hand[0]]);
    	for (var i = 1; i < hand.length; i++) {
    		//if there is a group for this card value, then push the card into it.
    		if (hand[i].value === grouped[grouped.length - 1][0].value)
    			grouped[grouped.length - 1].push(hand[i]);
    		//otherwise create a new group
    		else grouped.push([hand[i]]);
    	}
    
    	//sort grouped cards by group size
    	grouped.sort(function (a, b) { return b.length - a.length });
    
    	//four of a kind
    	if (grouped[0].length === 4) {
    		var newHand = grouped[0];
    		var kicker = grouped[1][0];
    		for (var i = 2; i < grouped.length; i++) {
    			if (grouped[i][0] > kicker) kicker = grouped[i][0];
    		}
    		newHand.push(kicker);
    		return [newHand, 7];
    	}
    
    	//three of a kind or full house
    	if (grouped[0].length === 3) {
    		var newHand = grouped[0];
    		//full house
    		if (grouped[1].length > 1) {
    			newHand.push(grouped[1][0]);
    			newHand.push(grouped[1][1]);
    			return [newHand, 6];
    		}
    		//three of a kind
    		else {
    			newHand.push(grouped[1][0]);
    			newHand.push(grouped[2][0]);
    			return [newHand, 3];
    		}
    	}
    
    	//pair or two pair
    	if (grouped[0].length === 2) {
    		var newHand = grouped[0];
    		//two pair
    		if (grouped[1].length > 1) {
    			newHand.push(grouped[1][0]);
    			newHand.push(grouped[1][1]);
    			newHand.push(grouped[2][0]);
    			return [newHand, 2];
    		}
    		//pair
    		else {
    			newHand.push(grouped[1][0]);
    			newHand.push(grouped[2][0]);
    			newHand.push(grouped[3][0]);
    			return [newHand, 1];
    		}
    	}
    
    	hand = hand.splice(0, 5);
    	return [hand, 0];
    }
    
    Scripts/hoopy_hoops.js
    "use strict";
    this.name = "hoopy_casino";
    this.author = "Paul Wilkins (updated by Eric Walch, further updated by spara)";
    this.copyright = "(C) 2009 Paul Wilkins";
    this.license = "CC BY-NC-SA 3.0";
    
    //init the hoops
    this.shipDockedWithStation = function (station) {
        if (player.ship.docked && station.hasRole("casinoship")) {
            missionVariables.hoopy_casino = "NOT_NOW";
            this.setBoundaries();
        }
    }
    
    this.missionScreenOpportunity = function () {
       if (player.ship.docked && player.ship.dockedStation.hasRole("casinoship")) {
            this.gamble();
       }
    }
    
    
    this.setBoundaries = function () {
        /*
        In the legacy version the random distribution was bad. One could win after analysing the odds
        during some time. This is a feature that should stay in. So we prepare the gamble table now a bit.
        */
        this.boundary1 = 1 + Math.random() - Math.random();
        this.boundary2 = 2 + Math.random() - Math.random();
        if (this.boundary1 > this.boundary2) this.boundary1 = this.boundary2;
        this.startCapital = player.credits;
    }
    
    this.$startGambling = function () {
        missionVariables.hoopy_casino = "HOOPY_ENTER";
        this.gambleChoices("YES_HOOPY");
    }
    
    this.gamble = function () {
        /*
        if (missionVariables.hoopy_casino === "HOOPY_REVISIT") {
            if (player.credits >= 100) {
                mission.runScreen({title: expandMissionText("hoopy_casino_title", {name: worldScripts.hoopy_populator._hoopyName}), messageKey: "hoopy_casino_intro", overlay: "hoopy_front.png", choicesKey: "hoopy_casino_enter_yesno", music: "hoopy_theme.ogg"}, this.gambleChoices);
                missionVariables.hoopy_casino = "HOOPY_ENTER";
                return;
            }
        }*/
        if (missionVariables.hoopy_casino === "HOOPY_GAMEON") {
            mission.runScreen({
                screenID: "hoopy_casino",
                title: expandMissionText("hoopy_casino_title", {name: worldScripts.hoopy_populator._hoopyName}),
                messageKey: "hoopy_casino_choices",
                overlay: "hoopy_cover.png",
                choicesKey: "hoopy_casino_pick_hoop",
                music: "hoopy_theme.ogg"
            },
                this.gambleChoices);
            this.hoopy_casino_hoop = Math.random() * 3;
            missionVariables.hoopy_casino = "HOOPY_MAKE_CHOICE";
            return;
        }
        if (player.credits > this.startCapital + 2000) {
            mission.runScreen({
                screenID: "hoopy_casino",
                title: expandMissionText("hoopy_casino_title", {name: worldScripts.hoopy_populator._hoopyName}),
                message: expandMissionText("hoopy_casino_new_table", {name: worldScripts.hoopy_populator._hoopy_name}),
                overlay: "hoopy_front.png",
                choicesKey: "hoopy_casino_new_table_yesno",
                music: "hoopy_theme.ogg"
            },
                this.gambleChoices);
            missionVariables.hoopy_casino = "HOOPY_NEW_TABLE";
            this.setBoundaries();
        } else {
            if (missionVariables.hoopy_casino === "HOOPY_WINNER") {
                mission.runScreen({
                    screenID: "hoopy_casino",
                    title: expandMissionText("hoopy_casino_title", {name: worldScripts.hoopy_populator._hoopyName}),
                    messageKey: "hoopy_casino_win",
                    overlay: this.hoopy_casino_image,
                    choicesKey: "hoopy_casino_replay_winner_yesno",
                    music: "hoopy_theme.ogg"
                },
                    this.gambleChoices);
                player.credits += 100;
                missionVariables.hoopy_casino = "HOOPY_REPLAY";
            }
            if (missionVariables.hoopy_casino === "HOOPY_LOSER") {
                mission.runScreen({
                    screenID: "hoopy_casino",
                    title: expandMissionText("hoopy_casino_title", {name: worldScripts.hoopy_populator._hoopyName}),
                    messageKey: "hoopy_casino_loss",
                    overlay: this.hoopy_casino_image,
                    choicesKey: "hoopy_casino_replay_loser_yesno",
                    music: "hoopy_theme.ogg"
                },
                    this.gambleChoices);
                player.credits -= 100;
                missionVariables.hoopy_casino = "HOOPY_REPLAY";
            }
        }
        if (missionVariables.hoopy_casino === "ALL_DONE") {
            delete this.hoopy_casino_hoop;
            delete this.hoopy_casino_image;
            delete missionVariables.hoopy_casino;
            worldScripts.hoopy_populator.$displayMenu();
        }
    }
    
    this.gambleChoices = function (choice) {
        if (missionVariables.hoopy_casino === "HOOPY_ENTER") {
            if (choice === "YES_HOOPY") {
                if (player.credits >= 100) {
                    mission.runScreen({
                        screenID: "hoopy_casino",
                        title: expandMissionText("hoopy_casino_title", {name: worldScripts.hoopy_populator._hoopyName}),
                        messageKey: "hoopy_casino_desc",
                        overlay: "hoopy_clean.png",
                        choicesKey: "hoopy_casino_gamble_yesno",
                        music: "hoopy_theme.ogg"
                    },
                        this.gambleChoices);
                    missionVariables.hoopy_casino = "HOOPY_GAMBLE";
                }
                else {
                    missionVariables.hoopy_casino = "HOOPY_REPLAY";
                    this.gambleChoices("YES_HOOPY");
                    return;
                }
            } else if (choice === "NO_HOOPY") {
                missionVariables.hoopy_casino = "ALL_DONE";
            }
            return;
        }
        if (missionVariables.hoopy_casino === "HOOPY_GAMBLE") {
            if (choice === "0_HOOPY") {
                mission.runScreen({
                    screenID: "hoopy_casino",
                    title: expandMissionText("hoopy_casino_title", {name: worldScripts.hoopy_populator._hoopyName}),
                    messageKey: "hoopy_casino_desc",
                    overlay: "hoopy_clean.png",
                    choicesKey: "hoopy_casino_gamble_yesno",
                    music: "hoopy_theme.ogg"
                },
                    this.gambleChoices);
            } else if (choice === "YES_HOOPY") {
                missionVariables.hoopy_casino = "HOOPY_GAMEON";
            } else if (choice === "NO_HOOPY") {
                missionVariables.hoopy_casino = "ALL_DONE";
            }
            return;
        }
        if (missionVariables.hoopy_casino === "HOOPY_MAKE_CHOICE") {
            missionVariables.hoopy_casino = "HOOPY_LOSER";
            if (this.hoopy_casino_hoop < this.boundary1) {
                this.hoopy_casino_image = "hoopy_Lwin.png";
                if (choice === "LEFT_HOOPY") {
                    missionVariables.hoopy_casino = "HOOPY_WINNER";
                }
            }
            if (this.hoopy_casino_hoop >= this.boundary1 && this.hoopy_casino_hoop < this.boundary2) {
                this.hoopy_casino_image = "hoopy_Cwin.png";
                if (choice === "MIDDLE_HOOPY") {
                    missionVariables.hoopy_casino = "HOOPY_WINNER";
                }
            }
            if (this.hoopy_casino_hoop >= this.boundary2) {
                this.hoopy_casino_image = "hoopy_Rwin.png";
                if (choice === "RIGHT_HOOPY") {
                    missionVariables.hoopy_casino = "HOOPY_WINNER";
                }
            }
            return;
        }
        if (missionVariables.hoopy_casino === "HOOPY_NEW_TABLE") {
            if (choice === "NEW_TABLE_HOOPY") {
                mission.runScreen({
                    screenID: "hoopy_casino",
                    title: expandMissionText("hoopy_casino_title", {name: worldScripts.hoopy_populator._hoopyName}),
                    messageKey: "hoopy_casino_desc",
                    overlay: "hoopy_clean.png",
                    choicesKey: "hoopy_casino_gamble_yesno",
                    music: "hoopy_theme.ogg"
                },
                    this.gambleChoices);
                missionVariables.hoopy_casino = "HOOPY_GAMBLE";
            } else if (choice === "NO_HOOPY") {
                missionVariables.hoopy_casino = "ALL_DONE";
            }
            return;
        }
        if (missionVariables.hoopy_casino === "HOOPY_REPLAY") {
            if (choice === "0-WINNER_HOOPY") {
                mission.runScreen({
                    screenID: "hoopy_casino",
                    title: expandMissionText("hoopy_casino_title", {name: worldScripts.hoopy_populator._hoopyName}),
                    messageKey: "hoopy_casino_win",
                    overlay: this.hoopy_casino_image,
                    choicesKey: "hoopy_casino_replay_winner_yesno",
                    music: "hoopy_theme.ogg"
                },
                    this.gambleChoices);
                return;
            }
            if (choice === "0-LOSER_HOOPY") {
                mission.runScreen({
                    screenID: "hoopy_casino",
                    title: expandMissionText("hoopy_casino_title", {name: worldScripts.hoopy_populator._hoopyName}),
                    messageKey: "hoopy_casino_loss",
                    overlay: this.hoopy_casino_image,
                    choicesKey: "hoopy_casino_replay_loser_yesno",
                    music: "hoopy_theme.ogg"
                },
                    this.gambleChoices);
                return;
            }
            if (choice === "YES_HOOPY") {
                if (player.credits >= 100) {
                    mission.runScreen({
                        screenID: "hoopy_casino",
                        title: expandMissionText("hoopy_casino_title", {name: worldScripts.hoopy_populator._hoopyName}),
                        messageKey: "hoopy_casino_again",
                        overlay: "hoopy_clean.png",
                        music: "hoopy_theme.ogg"
                    });
                    missionVariables.hoopy_casino = "HOOPY_GAMEON";
                } else {
                    mission.runScreen({
                        screenID: "hoopy_casino",
                        title: expandMissionText("hoopy_casino_title", {name: worldScripts.hoopy_populator._hoopyName}),
                        message: expandMissionText("hoopy_casino_nofunds", {name: worldScripts.hoopy_populator._hoopyName}),
                        overlay: "hoopy_front.png",
                        music: "hoopy_theme.ogg"
                    });
                    missionVariables.hoopy_casino = "ALL_DONE";
                }
                return;
            }
            if (choice === "NO_HOOPY") {
                mission.runScreen({
                    screenID: "hoopy_casino",
                    title: expandMissionText("hoopy_casino_title", {name: worldScripts.hoopy_populator._hoopyName}),
                    messageKey: "hoopy_casino_goodbye",
                    overlay: "hoopy_front.png",
                    music: "hoopy_theme.ogg"
                });
                missionVariables.hoopy_casino = "ALL_DONE";
            }
        }
    }
    
    Scripts/hoopy_poker.js
    "use strict";
    this.name = "hoopy_poker";
    this.author = "spara";
    this.copyright = "2014 spara";
    this.license = "CC BY-NC-SA 4.0";
    
    this.payoffHands = expandMissionText("hoopy-poker-payoff").split("|");;
    this.payoffTable = [
    	[10, 20, 30, 40, 60, 90, 250, 500, 2500],
    	[20, 40, 60, 80, 120, 180, 500, 1000, 5000],
    	[30, 60, 90, 120, 180, 270, 750, 1500, 7500],
    	[40, 80, 120, 160, 240, 360, 1000, 2000, 10000],
    	[50, 100, 150, 200, 300, 450, 1250, 2500, 40000]
    ];
    
    //init a new deck
    this.$virginDeck = function () {
    	var deck = new Array();
    	var suits = ["triangle", "crescent", "square", "star"];
    	for (var i = 0; i < 4; i++) {
    		for (var j = 1; j < 14; j++) {
    			var card = new Object();
    			card.suit = suits[i];
    			card.rank = j;
    			card.hold = 0;
    			deck.push(card);
    		}
    	}
    	return deck;
    }
    
    //helper function for tabulating text on screen
    this.$tabulate = function (line, width) {
    	var hairSpace = String.fromCharCode(31);
    	var space = defaultFont.measureString(" ");
    	while (defaultFont.measureString(line) < width - space)
    		line += " ";
    	while (defaultFont.measureString(line) < width)
    		line += hairSpace;
    	return line;
    }
    
    //intro screen
    this.$intro = function () {
    	//build and format the payoff table for screen
    	var payoffMessage = this.$tabulate("Hand", 10);
    	for (var i = 0; i < 5; i++) {
    		payoffMessage += formatCredits(10 * (i + 1), true, true);
    		payoffMessage = this.$tabulate(payoffMessage, 10 + 4 * (i + 1));
    	}
    	payoffMessage = "\n\n" + payoffMessage + "\n\n";
    	for (i = 0; i < this.payoffHands.length; i++) {
    		var line = this.payoffHands[this.payoffHands.length - i - 1];
    		line = this.$tabulate(line, 10);
    		for (var j = 0; j < 5; j++) {
    			line = this.$tabulate(line, 10 + 4 * j);
    			line += this.payoffTable[j][this.payoffHands.length - i - 1];
    		}
    		payoffMessage += line + "\n";
    	}
    
    	//format player credits for screen
    	var credits = expandMissionText("hoopy-credits", { amount: formatCredits(player.credits, true, true) });
    
    	//gambling choices
    	var options = {
    		"1_EXIT": expandMissionText("hoopy-exit")
    	};
    	if (player.credits >= 10) {
    		options["2_10"] = formatCredits(10, true, true);
    	}
    	else credits += expandMissionText("hoopy-no-credit");
    	if (player.credits >= 20)
    		options["3_20"] = formatCredits(20, true, true);
    	if (player.credits >= 30)
    		options["4_30"] = formatCredits(30, true, true);
    	if (player.credits >= 40)
    		options["5_40"] = formatCredits(40, true, true);
    	if (player.credits >= 50)
    		options["6_50"] = formatCredits(50, true, true);
    
    
    	mission.runScreen({
    		screenID: "hoopy_casino",
    		titleKey: "hoopy-poker-title",
    		message: expandMissionText("hoopy-poker-intro-story") + payoffMessage + credits,
    		model: "hoopy_casino_poker_card_spin",
    		background: { name: "hoopy_table_bg2.png", height: 546 },
    		choices: options,
    		allowInterrupt: true,
    		exitScreen: "GUI_SCREEN_INTERFACES"
    	},
    		function (choice) {
    			if (choice === "2_10") this.$play(10);
    			else if (choice === "3_20") this.$play(20);
    			else if (choice === "4_30") this.$play(30);
    			else if (choice === "5_40") this.$play(40);
    			else if (choice === "6_50") this.$play(50);
    			else if (choice == "1_EXIT") worldScripts.hoopy_populator.$displayMenu();
    		}.bind(this)
    	);
    }
    
    //first pay, then deal
    this.$play = function (bet) {
    	player.credits -= bet;
    	//init a new deck and draw a hand (don't show it yet)
    	this.$playDeck = this.$virginDeck();
    	var hand = new Array();
    	for (var i = 0; i < 5; i++) hand.push(this.$drawCard());
    	this.$deal(bet, hand);
    }
    
    //Deal
    this.$deal = function (bet, hand, choice) {
    	if (typeof choice === undefined) var choice = "0_HOLD";
    	var options = {
    		"0_HOLD": expandMissionText("hoopy-poker-hold1"),
    		"1_HOLD": expandMissionText("hoopy-poker-hold2"),
    		"2_HOLD": expandMissionText("hoopy-poker-hold3"),
    		"3_HOLD": expandMissionText("hoopy-poker-hold4"),
    		"4_HOLD": expandMissionText("hoopy-poker-hold5"),
    		"5_DEAL": expandMissionText("hoopy-poker-draw")
    	}
    	mission.runScreen({
    		screenID: "hoopy_casino",
    		titleKey: "hoopy-poker-title",
    		messageKey: "hoopy-poker-deal-story",
    		model: "hoopy_casino_poker_card_base",
    		background: { name: "hoopy_table_bg2.png", height: 546 },
    		spinModel: false,
    		choices: options,
    		initialChoicesKey: choice
    	},
    		function (choice) {
    			if (choice === "0_HOLD")
    				hand[0].hold = (hand[0].hold + 1) % 2;
    			else if (choice === "1_HOLD")
    				hand[1].hold = (hand[1].hold + 1) % 2;
    			else if (choice === "2_HOLD")
    				hand[2].hold = (hand[2].hold + 1) % 2;
    			else if (choice === "3_HOLD")
    				hand[3].hold = (hand[3].hold + 1) % 2;
    			else if (choice === "4_HOLD")
    				hand[4].hold = (hand[4].hold + 1) % 2;
    			else if (choice === "5_DEAL") {
    				this.$outCome(bet, hand);
    				return;
    			}
    			this.$deal(bet, hand, choice);
    			return;
    		}.bind(this)
    	);
    	//update the cards on screen
    	for (var i = 0; i < 5; i++)
    		this.$changeCard(i, hand[i]);
    }
    
    //draw && outcome
    this.$outCome = function (bet, hand) {
    	//draw new cards
    	for (var i = 0; i < 5; i++) {
    		if (!hand[i].hold)
    			hand[i] = this.$drawCard();
    		else hand[i].hold = 0;
    	}
    	//check for wins
    	var result = this.winCheck(hand);
    	//fail
    	if (result === -1) {
    		var sound = "hoopy_lose.ogg";
    		var resultText = expandMissionText("hoopy-poker-fail");
    	}
    	//win
    	else {
    		var win = this.payoffTable[(bet / 10 - 1)][result];
    		var resultText = expandMissionText("hoopy-poker-win", { result: this.payoffHands[result], amount: formatCredits(win, false, true) });
    		player.credits += win;
    		var sound = "hoopy_win.ogg";
    	}
    	//format player credits for screen
    	var credits = "\n" + expandMissionText("hoopy-credits", { amount: formatCredits(player.credits, true, true) }) + "\n" + expandMissionText("hoopy-casino-bet", { amount: formatCredits(bet, true, true) });
    	var options = {
    		"1_EXIT": expandMissionText("hoopy-exit"),
    		"2_AGAIN": expandMissionText("hoopy-poker-changebet"),
    	};
    	if (player.credits >= bet) options["3_DEAL"] = expandMissionText("hoopy-poker-dealagain");
    	mission.runScreen({
    		screenID: "hoopy_casino",
    		titleKey: "hoopy-poker-title",
    		message: resultText + credits,
    		model: "hoopy_casino_poker_card_base",
    		background: { name: "hoopy_table_bg2.png", height: 546 },
    		spinModel: false,
    		choices: options,
    		music: sound,
    		allowInterrupt: true,
    		exitScreen: "GUI_SCREEN_INTERFACES"
    	},
    		function (choice) {
    			if (choice === "2_AGAIN") {
    				this.$intro();
    				return;
    			} else if (choice === "3_DEAL") {
    				this.$play(bet);
    				return;
    			} else if (choice == "1_EXIT") {
    				worldScripts.hoopy_populator.$displayMenu();
    				return;
    			}
    		}.bind(this)
    	)
    	//update the cards on screen
    	for (var i = 0; i < 5; i++)
    		this.$changeCard(i, hand[i]);
    }
    
    //draws a card from the current deck in play
    this.$drawCard = function () {
    	return this.$playDeck.splice(Math.floor(this.$playDeck.length * Math.random()), 1)[0];
    }
    
    //update the texture and position for a card
    this.$changeCard = function (place, card) {
    	var fileName = "hoopy_card_" + card.suit + "_" + card.rank + ".png";
    	mission.displayModel.subEntities[place].setMaterials({ "casino_poker_card_diffuse.png": { diffuse_map: fileName, emission_map: fileName, emission_modulate_color: "darkGrayColor" } });
    	if (card.hold) {
    		var position = mission.displayModel.subEntities[place].position;
    		position[2] = 3;
    		mission.displayModel.subEntities[place].position = position;
    	}
    }
    
    //rules for a winning hand
    this.winCheck = function (hand) {
    	//work with a hand that has been ordered by rank
    	var orderedHand = hand.concat();
    	orderedHand.sort(function (a, b) { return a.rank - b.rank });
    
    	//check for flush
    	var suit = orderedHand[0].suit;
    	var i = 0;
    	var flush = false;
    	for (i = 1; i < 5; i++) {
    		if (orderedHand[i].suit !== suit) {
    			i = -1;
    			break;
    		}
    	}
    	//flush found
    	if (i !== -1) flush = true;
    
    	//check for straight
    	var prevRank = orderedHand[0].rank;
    	var straight = false
    	for (i = 1; i < 5; i++) {
    		if (orderedHand[i].rank - prevRank !== 1 && (i !== 1 || orderedHand[i].rank !== 10 || prevRank !== 1)) {
    			i = -1;
    			break;
    		}
    		prevRank = orderedHand[i].rank;
    	}
    
    	//straight found
    	if (i !== -1) straight = true;
    
    	//straight flush
    	if (flush && straight) {
    		if (orderedHand[0].rank === 1 && orderedHand[4].rank === 13)
    			return 8;//royal_flush
    		else return 7;//straight_flush
    	}
    
    	//flush 
    	if (flush) return 4;//flush"
    
    	//straight
    	if (straight) return 3;//straight
    
    	//in the ordered list, for every adjacent two cards, check if they form a rank pair.
    	prevRank = orderedHand[0].rank;
    	var pairs = new Array();
    	for (i = 1; i < 5; i++) {
    		if (orderedHand[i].rank === prevRank) pairs.push(i);
    		prevRank = orderedHand[i].rank;
    	}
    
    	//four of a kind (3 adjacent pairs)
    	//full house (3 adjacent pairs)
    	if (pairs.length === 3) {
    		if (orderedHand[1].rank === orderedHand[2].rank && orderedHand[2].rank === orderedHand[3].rank)
    			return 6;//four_of_a_kind
    		else return 5;//full_house
    	}
    
    	//three of a kind (2 adjacent pairs)
    	//two pair (2 adjacent pairs)
    	if (pairs.length === 2) {
    		if (pairs[1] - pairs[0] === 1)
    			return 2;//three_of_a_kind
    		else return 1;//two pair
    	}
    
    	//pair (jacks or better)
    	if (pairs.length === 1) {
    		if (orderedHand[pairs[0]].rank > 10 || orderedHand[pairs[0]].rank === 1)
    			return 0;
    	}
    
    	return -1;
    }
    
    Scripts/hoopy_populator.js
    "use strict";
    this.name = "hoopy_populator";
    this.author = "Paul Wilkins (updated by Eric Walch, further updated by spara, even further updated by phkb)";
    this.copyright = "(C) 2009 Paul Wilkins";
    this.license = "CC BY-NC-SA 3.0";
    
    this._limitCorpStates = false;
    this._minTL = 10;
    this._background = 1;
    this._ambienceSound;
    this._hoopyNames = expandMissionText("hoopy_name").split("|");
    this._hoopyName = this._hoopyNames[Math.floor(Math.random() * this._hoopyNames.length)];
    
    this._hoopyConfig = {
        Name: this.name,
        Display: expandMissionText("hoopy_casino_config"),
        Alias: expandMissionText("hoopy_casino_title", {name: this._hoopyName}),
        Alive: "_hoopyConfig",
    	Bool: {
    		B0: { Name: "_limitCorpStates", Def: true, Desc: expandMissionText("hoopy_config_bool") },
    		Info: expandMissionText("hoopy_config_bool_info")
    	},
    	SInt: {
    		S0: { Name: "_minTL", Def: 10, Min: 0, Max: 14, Desc: expandMissionText("hoopy_config_sint") },
    		Info: expandMissionText("hoopy_config_sint_info")
    	},
    };
    
    this.startUp = function() {
    	if (missionVariables.Hoopy_CorpStates) this._limitCorpStates = (missionVariables.Hoopy_CorpStates == "Y" ? true : false);
    	if (missionVariables.Hoopy_MinTL) this._minTL = parseInt(missionVariables.Hoopy_MinTL);
    }
    
    //add interface
    this.$addMainInterface = function () {
    	var casinos = system.shipsWithRole("casinoship");
    	if (casinos.length > 0) {
    		for (var i = 0; i < casinos.length; i++) {
    			casinos[i].setInterface("hoopy-casino", {
    				title: expandMissionText("hoopy_casino_title", {name: this._hoopyName} ),
    				category: expandDescription("[interfaces-category-organisations]"),
    				summary: expandMissionText("hoopy_casino_summary"),
    				callback: this.$displayMenu.bind(this)
    			});
    		}
    	}
    }
    
    this.startUpComplete = function () {
        // register our settings, if Lib_Config is present
        if (worldScripts.Lib_Config) {
            worldScripts.Lib_Config._registerSet(this._hoopyConfig);
        }
    
    	var w = worldScripts.XenonUI;
    	if (w) w.$addMissionScreenException("hoopy_casino");
    	w = worldScripts.XenonReduxUI;
    	if (w) w.$addMissionScreenException("hoopy_casino");
    	this.$addMainInterface();
    }
    
    this.shipExitedWitchspace = function() {
    	this.$addMainInterface();
    }
    
    //add casino to system
    this.systemWillPopulate = function () {
    	// add in high tech level systems, but not in comunist systems. But, always in systems famous for their casinos.
    	var check = this._limitCorpStates ? (system.techLevel >= this._minTL && system.government == 7) : (system.techLevel >= this._minTL && system.scrambledPseudoRandomNumber() > 0.55 && system.government != 4);
    
    	if (check || system.info.description.indexOf("hoopy casinos") > -1) {
    		//no main station, no casinos.
    		if (system.mainStation) {
    			var coords = system.mainStation.position.subtract(system.mainStation.position.direction().multiply(50000));
    			system.setPopulator("hoopy_casino", {
    				callback: function (pos) {
    					system.addShips("casinoship", 1, pos);
    				}.bind(this),
    				location: "COORDINATES",
    				coordinates: coords
    			})
    		}
    	}
    }
    
    this.playerWillSaveGame = function() {
    	missionVariables.Hoopy_CorpStates = (this._limitCorpStates ? "Y" : "N");
    	missionVariables.Hoopy_MinTL = this._milTL;
    }
    
    this.missionScreenEnded = function() {
    	if (this._timer && this._timer.isRunning) this._timer.stop();
    }
    
    this.$displayMenu = function() {
    	this._timer = new Timer(this, this.$changeBackground.bind(this), 0.25, 1.5);
    	mission.runScreen({
    		screenID: "hoopy_casino",
    		title: expandMissionText("hoopy_casino_title", {name: this._hoopyName}),
    		message: expandMissionText("hoopy_casino_message", {name: this._hoopyName}),
    		background: { name: "hoopy_casino_front.png", height: 546 },
    		choicesKey: "hoopy_casino_menu",
    		allowInterrupt: true,
    		exitScreen: "GUI_SCREEN_INTERFACES"
    	},
    		this.$processChoice.bind(this));
    }
    
    this.$processChoice = function(choice) {
    	this._timer.stop();
    	this._hoopyName = this._hoopyNames[Math.floor(Math.random() * this._hoopyNames.length)];
    
    	switch (choice) {
    		case "1_HOOPS": 
    			worldScripts.hoopy_casino.$startGambling();
    			return;
    		case "2_POKER":
    			worldScripts.hoopy_poker.$intro();
    			return;
    		case "3_BLACKJACK":
    			worldScripts.hoopy_blackjack.$intro();
    			return;
    		case "4_HOLDEM":
    			worldScripts.hoopy_holdem.$intro();
    			return;
    		case "5_SMUGGLERS":
    			worldScripts.hoopy_dice.$intro();
    			return;
    	}
    }
    
    this.$changeBackground = function $changeBackground() {
    	if (this._background == 1) this._background = 2; else this._background = 1;
    	setScreenBackground({ name: "hoopy_casino_front" + this._background + ".png", height: 546 })
    }