Skip to content

Instantly share code, notes, and snippets.

@Rukia3d
Created October 19, 2018 10:36
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Rukia3d/92dd31ed8e7dc18cc2b3b2add25afa6a to your computer and use it in GitHub Desktop.
Save Rukia3d/92dd31ed8e7dc18cc2b3b2add25afa6a to your computer and use it in GitHub Desktop.
Electron Fiddle Gist
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>CARD CRAWL CLONE</title>
<style>
body {
text-align: center;
}
table {
margin: 0 auto;
}
td {
width: 100px;
height: 150px;
border: 1, solid, black;
background-color: grey;
}
.filled {
background-color: blue;
}
.target {
background-color: red;
}
.hover {
background-color: green;
}
</style>
</head>
<body>
<h1>Card Crawl Clone!</h1>
<div id="print"></div>
<div id="container">
<table>
<tr id="enemyHand">
<td data-index="0"></td>
<td data-index="1"></td>
<td data-index="2"></td>
<td data-index="3"></td>
</tr>
<tr id="playerHand">
<td data-index="0"></td>
<td data-index="1"></td>
<td data-index="2"></td>
<td data-index="3"></td>
</tr>
</table>
</div>
<script>
// You can also require other files to run in this process
require('./renderer.js')
</script>
</body>
</html>
/*
THIS IS BACKEND FOR THE WORKSHOP FIDDLING WITH ELECTEON FIDLE
CREATED FOR NODEGIRLS SYDNEY
CLONE OF THE GAME MECHANICS FROM "CARD CRAWL"
*/
// Modules to control application life and create native browser window
const {app, ipcMain, BrowserWindow} = require('electron')
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow
// Global Game State Object and cards that we're going to use in the game.
let heroIndex = 1;
let heroCard = { name: "hero", health: 10, type: "hero" };
let gso = {
deck: [],
enemyHand: [null, null, null, null],
playerHand: [null, null, null, null],
state: "PLAY"
}
let cardsArray = [
{ name: "sword", power: 3, type: "attack" },
{ name: "sword", power: 3, type: "attack" },
{ name: "sword", power: 3, type: "attack" },
{ name: "shield", power: 3, type: "shield" },
{ name: "shield", power: 3, type: "shield" },
{ name: "shield", power: 3, type: "shield" },
{ name: "heal", power: 3, type: "heal" },
{ name: "heal", power: 3, type: "heal" },
{ name: "heal", power: 3, type: "heal" },
{ name: "troll", health: 3, power: 3, type: "enemy" },
{ name: "troll", health: 3, power: 3, type: "enemy" },
{ name: "troll", health: 3, power: 3, type: "enemy" },
{ name: "ghost", health: 3, power: 3, type: "enemy" },
{ name: "ghost", health: 3, power: 3, type: "enemy" },
{ name: "ghost", health: 3, power: 3, type: "enemy" },
{ name: "witch", health: 3, power: 3, type: "enemy" },
{ name: "witch", health: 3, power: 3, type: "enemy" },
{ name: "witch", health: 3, power: 3, type: "enemy" },
]
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({width: 800, height: 600})
// and load the index.html of the app.
mainWindow.loadFile('index.html')
// Open the DevTools.
mainWindow.webContents.openDevTools()
// Emitted when the window is closed.
mainWindow.on('closed', function () {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
mainWindow = null
})
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow)
// Quit when all windows are closed.
app.on('window-all-closed', function () {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', function () {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (mainWindow === null) {
createWindow()
}
})
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
ipcMain.on('MSG', (event, arg) => {
// Save changed object
let received = msgReceived(arg);
// Send back the answer with the object
mainWindow.webContents.send("GSO", received);
});
// Parse the message to call a relevant function
function msgReceived(arg){
console.log("Received message "+arg.id);
switch(arg.id){
case "START":
return createGso();
case "ACT":
return actParsed(arg.active, arg.target);
default:
console.log("Unknown message "+ arg);
return gso;
}
}
// Function to give cards if the number of empty cards is >= 3
function giveCards(){
// Check the number of empty cards in enemy hand
const cardsEmpty = gso.enemyHand.filter(function(e){
return !e;
}).length;
if(cardsEmpty < 3) {
return;
}
for(var i=0; i<gso.enemyHand.length; i++){
if(!gso.enemyHand[i]){
gso.enemyHand[i] = gso.deck.pop()
}
}
}
// Based on the game logic check if the player won or lost or game continues
function checkGameState(){
if(gso.playerHand[heroIndex].health <=0 ){
gso.state = "LOST";
} else if(gso.deck.length == 0){
gso.state = "WON";
} else {
giveCards();
gso.state = "PLAY";
}
}
// Remove used card only if it's not hero card
function removeCard(card, position){
if(card.type=="hero"){
return;
}
gso[position.row][position.index] = null;
}
// The initial GSP creation
function createGso(){
let newDeck = cardsArray.sort(() => Math.random() - 0.5);
gso.deck = newDeck;
gso.playerHand[heroIndex] = heroCard;
giveCards();
return gso;
}
// Analyse the player's action and based on the 2 cards from message
// Determine the act result
function actParsed(active, target){
console.log(active, target);
let card1 = gso[active.row][active.index];
let card2 = gso[target.row][target.index];
console.log(card1, card2);
if(card2==null){
console.log("Got an empty space");
gso[target.row][target.index] = card1;
} else {
if(card2.type=="hero"){
console.log("got an attack against hero");
gso.playerHand[heroIndex].health = gso.playerHand[heroIndex].health - card1.power;
removeCard(card2, target);
}
if(card2.type=="shield"){
console.log("got an attack against shield");
removeCard(card2, target);
}
if(card1.type=="attack"){
console.log("hero attacks enemy");
removeCard(card2, target);
}
}
removeCard(card1, active);
if(card1.type=="heal" && target.index!=3){
gso.playerHand[heroIndex].health = gso.playerHand[heroIndex].health + card1.power;
gso.playerHand[target.index] = null;
}
checkGameState();
return gso;
}
/*
THIS IS FRONTED FOR THE WORKSHOP FIDDLING WITH ELECTEON FIDLE
CREATED FOR NODEGIRLS SYDNEY
CLONE OF THE GAME MECHANICS FROM "CARD CRAWL"
*/
const { ipcRenderer } = require('electron')
// We need to keep selected card to reference it
// And a global GSO that comes from the backend
let selectedCard = null //{ row: null, index: null };
let gso;
// Grab the selected card from the event target
function findSelectedCard(target){
return { row: target.parentElement.id, index: target.dataset.index };
}
// Communcation function that sends messages to backend
function sendMessage(obj){
//console.log("Sending a message with", obj);
ipcRenderer.send('MSG', obj);
}
// When we start dragging - save the card that we use and redraw the table
function onDragStart(event){
//console.log("Drag started ", event);
selectedCard = findSelectedCard(event.target);
renderTable();
}
// When we finish dragging the card around, remove the selected card and redraw the table
function onDragEnd(event){
//console.log("Drag ended ", event);
selectedCard = null;
renderTable();
}
// When the drag is over - remove styled from the available targets
function onDragOver(event){
//console.log("Drag Over ", event);
if(event.target.classList.contains("target")){
event.target.classList.add("hover");
event.preventDefault();
}
}
// When we leave the card, remove the style from the selected target
function onDragLeave(event){
//console.log("Drag Leave ", event);
event.target.classList.remove("hover");
}
// When we drop the card, send message to the backend
function onDrop(event){
//console.log("Drop Event ", event);
event.preventDefault();
sendMessage({id: "ACT", active: selectedCard, target: findSelectedCard(event.target)});
selectedCard = null;
}
// Check if this action satisfies all the game logic conditions
function isDropTarget(targetCard, targetRow){
console.log("Selected card ", selectedCard);
const selectedType = selectedCard ? gso[selectedCard.row][selectedCard.index].type : null;
//console.log("Selected type ", selectedType);
if(!selectedType){
return false;
}
if(targetCard==null){
// we're dragging a card to an empty slot
if(targetRow=="playerHand" && selectedType != 'enemy') {
// we can only put into empty spot in a hand a shield, potion or sword
if(selectedCard.row=="playerHand" && selectedCard.index!=3){
// we can move card from pocket to hand, but not back to the pocket
return false;
}
return true;
}
} else {
console.log(targetRow, selectedType);
// the slot is taken
if(targetRow=="playerHand" && selectedType == 'enemy'){
// enemy card can only attack hero and shields
if(targetCard.type=="hero" || targetCard.type=="shield"){
return true;
}
}
// sword can only attack if it's in player's hand and the enemy is in the enemy's hand
if(targetRow=="enemyHand" && selectedType=="attack" && selectedCard.row=="playerHand" && selectedCard.index!=3 && targetCard.type=="enemy"){
console.log("Here we are")
return true;
}
}
return false;
}
function renderCell(card, cell, row){
cell.className = "";
cell.innerHTML = "";
cell.draggable = false;
if(isDropTarget(card, row)){
cell.classList.add("target");
}
if(card){
let description = "Card:"+card.name+"</br>";
if(card.type=="hero"){
description = description+"Health: "+card.health;
} else if(card.type=="enemy"){
description = description+"Attack: "+card.power+"</br>Health: "+card.health;
} else {
description = description+"Power: "+card.power;
}
cell.classList.add("filled");
cell.innerHTML = description;
cell.draggable = card.type != "hero";
}
}
function renderRow(hand, selector){
const cells = document.getElementById(selector).children;
for(let i=0; i<hand.length; i++){
renderCell(hand[i], cells[i], selector);
}
}
function renderTable(){
renderRow(gso.enemyHand, "enemyHand");
renderRow(gso.playerHand, "playerHand");
}
function startGame(){
ipcRenderer.on("GSO", (event, arg) => {
renderGame(arg);
});
let container = document.getElementById("container");
container.addEventListener("dragstart", onDragStart);
container.addEventListener("dragend", onDragEnd);
container.addEventListener("dragover", onDragOver);
container.addEventListener("dragleave", onDragLeave);
container.addEventListener("drop", onDrop);
sendMessage({id: "START"});
}
function renderGame(newGso){
gso = newGso;
console.log(gso);
switch(gso.state){
case "PLAY":
renderTable();
console.log("Playing")
break;
case "LOST":
window.alert("You lost!");
break;
case "WON":
window.alert("You won!");
break;
}
}
startGame();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment