Back to Index Page generated: Apr 20, 2026, 4:52:50 AM

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 Eric Walch, Murgh, CaptSolo, spara
Version 2.3 2.3
Tags Casino, Gambling Casino, Gambling
Required Oolite Version
Maximum Oolite Version
Required Expansions
Optional Expansions
Conflict Expansions
Information URL https://wiki.alioth.net/index.php/HoOpy_Casino n/a
Download URL https://wiki.alioth.net/img_auth.php/d/d4/HOopyCasino_2.3.oxz n/a
License CC-BY-NC-SA 3.0 CC-BY-NC-SA 3.0
File Size n/a
Upload date 1776075423

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)

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",
		titleKey: "hoopy-holdem-title",
		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({titleKey: "hoopy_casino_title", 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",
            titleKey: "hoopy_casino_title",
            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",
            titleKey: "hoopy_casino_title",
            messageKey: "hoopy_casino_new_table",
            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",
                titleKey: "hoopy_casino_title",
                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",
                titleKey: "hoopy_casino_title",
                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",
                    titleKey: "hoopy_casino_title",
                    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",
                titleKey: "hoopy_casino_title",
                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",
                titleKey: "hoopy_casino_title",
                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",
                titleKey: "hoopy_casino_title",
                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",
                titleKey: "hoopy_casino_title",
                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",
                    titleKey: "hoopy_casino_title",
                    messageKey: "hoopy_casino_again",
                    overlay: "hoopy_clean.png",
                    music: "hoopy_theme.ogg"
                });
                missionVariables.hoopy_casino = "HOOPY_GAMEON";
            } else {
                mission.runScreen({
                    screenID: "hoopy_casino",
                    titleKey: "hoopy_casino_title",
                    messageKey: "hoopy_casino_nofunds",
                    overlay: "hoopy_front.png",
                    music: "hoopy_theme.ogg"
                });
                missionVariables.hoopy_casino = "ALL_DONE";
            }
            return;
        }
        if (choice === "NO_HOOPY") {
            mission.runScreen({
                screenID: "hoopy_casino",
                titleKey: "hoopy_casino_title",
                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._ambienceSound;

this._hoopyConfig = {
    Name: this.name,
    Display: expandMissionText("hoopy_casino_config"),
    Alias: expandMissionText("hoopy_casino_title"),
    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"),
				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.$displayMenu = function() {
	mission.runScreen({
		screenID: "hoopy_casino",
		titleKey: "hoopy_casino_title",
		messageKey: "hoopy_casino_message",
		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) {
	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;
	}
}