Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added assets/images/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
59 changes: 55 additions & 4 deletions less/joust.less
Original file line number Diff line number Diff line change
Expand Up @@ -297,11 +297,62 @@ div.visuals {
width: 100%;
}

.joust-message {
width: 100%;
text-align: center;
font-family: sans-serif;
.loading-screen {
position: relative;
display: flex;
flex-direction: column;
margin: auto;
color: white;
font-family: "Belwe Bd BT";
text-shadow: -1px -1px 0 #000,
1px -1px 0 #000,
-1px 1px 0 #000,
1px 1px 0 #000;

@keyframes rotate {
from {
-webkit-transform: rotate(0deg);
}
to {
-webkit-transform: rotate(360deg);
}
}

.logo {
width: 8em;
position: relative;
margin: 2em auto;
animation-name: rotate;
animation-duration: 3s;
animation-iteration-count: infinite;
animation-timing-function: ease;
}

.info {
height: 1em;
font-size: 2em;
margin: auto;
position: relative;
display: flex;
flex-direction: row;

.left {
text-align: right;
width: 8em;
}
.center {
text-align: center;
margin-left: 1em;
margin-right: 1em;
}
.right {
text-align: left;
width: 8em;
}
}
.joust-message {
font-size: 1.5em;
}
}

.deck {
Expand Down
37 changes: 33 additions & 4 deletions ts/TexturePreloader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ class TexturePreloader extends Stream.Writable {
protected textureQueue = ['GAME_005'];
protected images = [];
private working = 0;
private assetCount = 0;
private textureCount = 0;
private heroPowerCount = 0;
protected assetQueue = ['cardback', 'hero_frame', 'hero_power', 'inhand_minion', 'inhand_spell', 'inhand_weapon',
'inhand_minion_legendary', 'mana_crystal', 'inplay_minion', 'effect_sleep',
'hero_power_exhausted', 'hero_armor', 'hero_attack', 'icon_deathrattle', 'icon_inspire',
Expand Down Expand Up @@ -55,18 +58,23 @@ class TexturePreloader extends Stream.Writable {
this.consume();
};

let isAsset = false;
let isHeroPower = false;
let file = this.assetQueue.shift();
if (!!this.assetDirectory && file) {
file = this.assetDirectory + 'images/' + file + '.png';
isAsset = true;
}
else {
let cardId = this.textureQueue.shift();
if (!this.cards.get(cardId)) {
let card = this.cards.get(cardId);
if (!card) {
console.warn('No texture for ' + cardId + ' to preload');
next();
return;
}
file = this.textureDirectory + this.cards.get(cardId).texture + '.jpg';
isHeroPower = card.type == 'HERO_POWER'
file = this.textureDirectory + card.texture + '.jpg';
}

if (this.fired[file]) {
Expand All @@ -76,9 +84,22 @@ class TexturePreloader extends Stream.Writable {

this.fired[file] = true;

let updateProgress = (asset: boolean, heroPower: boolean) => {
if (asset) {
this.assetCount++;
}
else {
this.textureCount++;
if (heroPower) {
this.heroPowerCount++;
}
}
next();
};

let image = new Image;
image.onload = next;
image.onerror = next;
image.onload =() => updateProgress(isAsset, isHeroPower);
image.onerror = () => updateProgress(isAsset, isHeroPower);
image.src = file;
this.images[this.images.length] = image;

Expand All @@ -89,6 +110,14 @@ class TexturePreloader extends Stream.Writable {
public canPreload(): boolean {
return !!this.assetDirectory || !!this.textureDirectory;
}

public texturesReady(): boolean {
return (this.textureCount > 20 || !this.working) && this.heroPowerCount > 1;
}

public assetsReady(): boolean {
return this.assetCount > 10 || !this.working;
}
}

export default TexturePreloader;
1 change: 1 addition & 0 deletions ts/components/GameWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ class GameWidget extends React.Component<GameWidgetProps, GameWidgetState> {
assetDirectory={this.props.assetDirectory} textureDirectory={this.props.textureDirectory}
cards={this.state.cards} swapPlayers={this.state.swapPlayers}
cardOracle={this.state.isRevealingCards && this.state.cardOracle}
preloader={this.props.preloader}
/>);

if (this.props.scrubber) {
Expand Down
35 changes: 30 additions & 5 deletions ts/components/GameWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@ import * as React from "react";
import {CardDataProps} from "../interfaces";
import GameState from "../state/GameState";
import TwoPlayerGame from "./game/TwoPlayerGame";
import {CardType, OptionType} from "../enums";
import {CardType, OptionType, GameTag} from "../enums";
import Entity from "../Entity";
import Option from "../Option";
import PlayerEntity from "../Player";
import {InteractiveBackend, CardOracleProps, AssetDirectoryProps, TextureDirectoryProps} from "../interfaces";
import {Zone} from "../enums";
import TexturePreloader from "../TexturePreloader";
import LoadingScreen from "./LoadingScreen"

interface GameWrapperProps extends CardDataProps, CardOracleProps, AssetDirectoryProps, TextureDirectoryProps, React.Props<any> {
state: GameState;
preloader?: TexturePreloader;
interaction?: InteractiveBackend;
swapPlayers?: boolean;
}
Expand All @@ -23,11 +26,13 @@ class GameWrapper extends React.Component<GameWrapperProps, {}> {

private hasCheckedForSwap = false;
private swapPlayers = false;
private lastLog: string;

public render(): JSX.Element {
var gameState = this.props.state;
if (!gameState) {
return <p className="joust-message">Waiting for game state&hellip; </p>;
this.log('Waiting for game state...');
return <LoadingScreen players={null} assetDirectory={this.props.assetDirectory} />;
}

var entityTree = gameState.getEntityTree();
Expand All @@ -36,19 +41,32 @@ class GameWrapper extends React.Component<GameWrapperProps, {}> {
// check if any entites are present
var allEntities = gameState.getEntities();
if (!allEntities) {
return <p className="joust-message">Waiting for entities&hellip; </p>;
this.log('Waiting for entities...');
return <LoadingScreen players={null} assetDirectory={this.props.assetDirectory} />;
}

// find the game entity
var game = allEntities.filter(GameWrapper.filterByCardType(CardType.GAME)).first();
if (!game) {
return <p className="joust-message">Waiting for game&hellip; </p>;
this.log('Waiting for game...');
return <LoadingScreen players={null} assetDirectory={this.props.assetDirectory} />;
}

// find the players
var players = allEntities.filter(GameWrapper.filterByCardType(CardType.PLAYER)) as Immutable.Iterable<number, PlayerEntity>;
if (players.count() == 0) {
return <p className="joust-message">Waiting for players&hellip; </p>;
this.log('Waiting for players...')
return <LoadingScreen players={players} assetDirectory={this.props.assetDirectory} />;
}

if (this.props.preloader && !this.props.preloader.assetsReady()) {
this.log('Waiting for assets...');
return <LoadingScreen players={players} assetDirectory={this.props.assetDirectory} />;
}

if (this.props.preloader && !this.props.preloader.texturesReady()) {
this.log('Waiting for textures...');
return <LoadingScreen players={players} assetDirectory={this.props.assetDirectory} />;
}

// check if we need to swap the players
Expand Down Expand Up @@ -100,6 +118,13 @@ class GameWrapper extends React.Component<GameWrapperProps, {}> {
return !!entity && entity.getCardType() === cardType;
};
};

private log(message: string) {
if (message != this.lastLog) {
console.debug(message);
this.lastLog = message;
}
}
}

export default GameWrapper;
40 changes: 40 additions & 0 deletions ts/components/LoadingScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import * as React from "react";
import PlayerEntity from "../Player";
import {AssetDirectoryProps} from "../interfaces";

interface LoadingScreenProps extends AssetDirectoryProps, React.Props<any> {
players?: Immutable.Iterable<number, PlayerEntity>;
}

class LoadingScreen extends React.Component<LoadingScreenProps, {}> {

private currentMessage: string;
private lastUpdate = 0;
private messages = ['Sorting decks...', 'Painting cards...', 'Calculating lethal...', 'Calling customer service...',
'SMOrc', 'Verifying the face is the place...', 'Summoning heroes...', 'Nerfing cards...', 'Buffing cards...'];


private getMessage(): string {
var now = new Date().getTime();
if (!!this.messages.length && (now - this.lastUpdate) > 3000) {
var index = Math.floor(Math.random()*this.messages.length);
this.currentMessage = this.messages.splice(index, 1)[0];
this.lastUpdate = now;
}
return this.currentMessage;
}

public render(): JSX.Element {
return <div className="loading-screen">{this.props.players ?
<div className="info">
<span className="left">{this.props.players.first().getName()}</span>
<span className="center">VS</span>
<span className="right">{this.props.players.last().getName()}</span>
</div> : <div className="info"/>}
<img className="logo" src={this.props.assetDirectory + 'images/logo.png'} />
<span className="info joust-message">{this.getMessage()}</span>
</div>;
}
}

export default LoadingScreen;
2 changes: 2 additions & 0 deletions ts/interfaces.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import GameStateSink from "./state/GameStateSink";
import GameStateScrubber from "./state/GameStateScrubber";
import GameStateHistory from "./state/GameStateHistory";
import Player from "./Player";
import TexturePreloader from "./TexturePreloader";

export interface DropTargetProps {
connectDropTarget?(jsx);
Expand Down Expand Up @@ -154,6 +155,7 @@ export interface GameWidgetProps extends AssetDirectoryProps, TextureDirectoryPr
getImageURL?: (cardId: string) => string;
exitGame?: () => void;
cardOracle: CardOracle;
preloader?: TexturePreloader;
width?: any;
height?: any;
}
Expand Down
3 changes: 3 additions & 0 deletions ts/run.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ class Viewer {
this.opts.sink = sink;
this.opts.scrubber = scrubber;
this.opts.cardOracle = decoder;
if (preloader.canPreload()) {
this.opts.preloader = preloader;
}

this.render();
}
Expand Down