Browse Source

Initial Commit

develop
Dale 4 weeks ago
commit
1cca7760af
Signed by: Deiru <deiru2k@gmail.com> GPG Key ID: AA250C0277B927E1
16 changed files with 9249 additions and 0 deletions
  1. +1
    -0
      .envrc
  2. +7
    -0
      .gitignore
  3. +51
    -0
      default.nix
  4. +30
    -0
      package.json
  5. +18
    -0
      shell.nix
  6. BIN
      src/assets/fonts/AnticSlab-Regular.ttf
  7. BIN
      src/assets/fonts/AnticSlab-Regular.woff2
  8. BIN
      src/assets/fonts/antic-slab.zip
  9. BIN
      src/assets/images/user.png
  10. +344
    -0
      src/game.ts
  11. +0
    -0
      src/index.html
  12. +6
    -0
      src/index.ts
  13. +38
    -0
      tsconfig.json
  14. +50
    -0
      webpack.config.js
  15. +3907
    -0
      yarn-error.log
  16. +4797
    -0
      yarn.lock

+ 1
- 0
.envrc View File

@@ -0,0 +1 @@
use_nix

+ 7
- 0
.gitignore View File

@@ -0,0 +1,7 @@
!.envrc
!default.nix
!shell.nix

node_modules/
dist/
lib/

+ 51
- 0
default.nix View File

@@ -0,0 +1,51 @@
with import <nixpkgs> {};

let
artifactName = "discoteque";
version = "0.1.0";
src = ./.;
nodePkg = pkgs.nodejs-12_x;
yarnPkg = pkgs.yarn.override { nodejs = nodePkg; };
vueCliPkg = pkgs.nodePackages."@vue/cli".override { nodejs = nodePkg; };
in mkYarnPackage rec {
name = "discoteque";
inherit version src nodePkg yarnPkg;

packageJSON = "${src}/package.json";
yarnLock = "${src}/yarn.lock";
yarnNix = "${src}/yarn.nix";

shellHook = ''
rm -rf ./node_modules || true
export PATH="$PATH:$node_modules/.bin"
export NODE_PATH=$NODE_PATH:$node_modules
ln -s $node_modules ./node_modules
'';

configurePhase = ''
rm -rf ./node_modules || true
export PATH=$PATH:$node_modules/.bin
export NODE_PATH=$NODE_PATH:$node_modules
ln -s $node_modules ./node_modules
'';

buildPhase = ''
${yarnPkg}/bin/yarn --offline build
'';

installPhase = ''
mkdir -p $out/dist
ls $out
cp -R ./dist/* $out/dist/
'';

distPhase = ''
mkdir -p $out/tarballs
tar -czvf $out/tarballs/${artifactName}.${version}.tar.gz $out/dist/*
'';

nativeBuildInputs = with pkgs; [
nodePkg yarnPkg
makeWrapper yarn2nix
];
}

+ 30
- 0
package.json View File

@@ -0,0 +1,30 @@
{
"name": "disco-demo",
"version": "1.0.0",
"description": "Demo game files for Discoteque",
"main": "index.js",
"license": "MIT",
"dependencies": {
"ramda": "^0.27.1",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-toastify": "^6.0.8",
"redux": "^4.0.5",
"typescript": "^4.0.3",
"webpack": "^4.44.2",
"webpack-dev-server": "^3.11.0"
},
"devDependencies": {
"@types/ramda": "^0.27.19",
"@types/react": "^16.9.49",
"@types/react-dom": "^16.9.8",
"css-loader": "^4.2.2",
"file-loader": "^6.1.0",
"html-webpack-plugin": "^4.4.1",
"style-loader": "^1.2.1",
"ts-loader": "^8.0.3",
"ts-node": "^9.0.0",
"typescript-plugin-css-modules": "^2.4.0",
"webpack-cli": "^3.3.12"
}
}

+ 18
- 0
shell.nix View File

@@ -0,0 +1,18 @@
{ pkgs ? import <nixpkgs> {} }:

with pkgs;

let
nodePkg = pkgs.nodejs-12_x;
yarnPkg = pkgs.yarn.override { nodejs = nodePkg; };
tslPkg = pkgs.nodePackages.typescript-language-server.override { nodejs = nodePkg; };
src = ./.;
in mkShell {
shellHook = ''
export PATH=$PATH:${src}/node_modules/.bin
'';
buildInputs = [
yarnPkg nodePkg tslPkg
yarn2nix
];
}

BIN
src/assets/fonts/AnticSlab-Regular.ttf View File


BIN
src/assets/fonts/AnticSlab-Regular.woff2 View File


BIN
src/assets/fonts/antic-slab.zip View File


BIN
src/assets/images/user.png View File

Before After
Width: 512  |  Height: 512  |  Size: 12KB

+ 344
- 0
src/game.ts View File

@@ -0,0 +1,344 @@
import { IGameState, setState, reducer } from '@/lib/store';
import { INode, IActor, EngineConfig, ILineOption, ILocation } from 'discoteque/lib/engine/types';
import { toast } from 'react-toastify';

import skills, { GameSkills } from './lib/skills';
import { setSkillpoints, lockSkills, setSkillMenu } from 'discoteque/lib/engine/lib/store';
import { Dispatch } from 'redux';

const awardSkill = (dispatch: Dispatch, skillPoints: number) => {
dispatch(setSkillpoints(skillPoints + 1));
toast("[Skill +1] You have been awarded a skill point!", {
onClick: () => dispatch(setSkillMenu(true)),
});
}

const nodes: INode<IGameState>[] = [
{
id: 'trying_out',
kind: 'node',
next: 'pre_choice',
lines: [
{ actorId: 'char_helper', text: "Hi!" },
(_, _g, dispatch) => dispatch(lockSkills()) && null,
{ actorId: 'char_helper', text: "Lets's start by advancing some lines!" },
{ actorId: 'char_helper', text: "This should be easy enough for you!" },
{ actorId: 'char_helper', text: "See?" },
{ text: "You think you're getting the hang of it!" },
{ actorId: 'char_helper', text: "Here's an exceptionally long line specifically designed to annoy me (and you!) and test us (and our patience!) on how long a single line could be. Turns out - pretty long!" },
{ actorId: 'char_helper', text: "Now let's try something harder. Left, or right?" },
{ actorId: 'char_helper', text: "This will use your two skills, tired and manic, representing two most important parts of your personality!" },
{ actorId: 'char_helper', text: "Failing the check on the left will allow you to go again." },
{ actorId: 'char_helper', text: "Failing the check on the right will instantly end the game." },
{ actorId: 'char_helper', text: "You could also try going outside." },
({ skillPoints }, _, dispatch) => {
awardSkill(dispatch, skillPoints);
return { actorId: 'char_helper', text: "By the way, here's a skill point just for you!" }
},
{ actorId: 'char_helper', text: "You can spend it using \"Skills\" menu in \"Options\", in the upper left corner! " },
{ actorId: 'char_helper', text: "Going outside will change your location, as displayed at the top." },
{ actorId: 'char_helper', text: "Current time is displayed in the upper right corner, along with current date (if you want it to!)" },
{ actorId: 'char_helper', text: "Turns out, talking takes time!" },
{ actorId: 'char_helper', text: "It has no logical consequence, but helps you as the player navigate the game better." },
]
},
{
kind: 'node',
id: 'failed_choice_tired',
next: 'pre_choice',
lines: [
{ text: '[FAIL] You tried very hard, but just weren\'t really in the mood' },
],
},
{
kind: 'node',
id: 'failed_choice_manic',
next: 'exit',
lines: [
{ text: '[FAIL] You were so overhyped you couldn\'t even say what you wanted to say' },
{ actorId: 'char_helper', text: 'Well, this is the end.' },
(_, _gameState, dispatch) => {
dispatch(setState({ isOver: true }))
},
],
},
{
id: 'pre_choice',
kind: 'node',
lines: [
(_, gameState) => {
const counterRight = gameState.visitedRight;
const counterLeft = gameState.visitedLeft;
const combinedCounter = counterRight + counterLeft;
const showExit = combinedCounter >= 3;
return { actorId: 'char_helper', text: 'So, left, or right?', options: [
{ text: '"I want to go outside!"', value: 'pre_outside' },
{ text: '"Left!"', value: 'left_choice', skill: { name: 'tired', difficulty: 17, failTo: 'failed_choice_tired' } },
{ text: '"Right!"', value: 'right_choice', skill: { name: 'manic', difficulty: 9, failTo: 'failed_choice_manic' } },
showExit && { 'text': 'Let me out, please', value: 'exit' }
].filter(x => x) as ILineOption[] };
}
]
},
{
id: 'pre_outside',
kind: 'node',
next: 'outside',
lines: [
(_, { beenOutside }) => beenOutside
? ({ actorId: 'char_helper', text: 'Ok, ok, I\'m not going anywhere' })
: ({ actorId: 'char_helper', text: 'Oki! Come find me inside the house if you wanna chat!', time: 10 }),
(_, { beenOutside }) => beenOutside
? { actorId: 'char_helper', text: 'I\'ll be here if you need me.' }
: { actorId: 'char_helper', text: 'Just be careful with the trap door. Many tried to find it, and many have failed.', time: 10 },
],
},
{
id: 'left_choice',
kind: 'node',
next: 'pre_choice',
lines: [
{ text: 'Pausing to think, you have decided to pick left option' },
(_, gameState) => {
const { visitedLeft } = gameState;
return visitedLeft ? (
{ actorId: 'char_helper', text: 'Huh. I think we\'ve been here before, no?' }
) : { actorId: 'char_helper', text: 'Good choice! As i\'ve said, picking left will lead you back.' };
},
({ skillPoints }, gameState, dispatch) => {
if ((gameState.visitedLeft + 1) === 3) {
awardSkill(dispatch, skillPoints);
}
dispatch(setState({ visitedLeft: gameState.visitedLeft + 1 }));
},
{ actorId: 'char_helper', text: 'Pick the other choice next time, maybe?' },
]
},
{
id: 'right_choice',
kind: 'node',
next: 'pre_choice',
lines: [
{ text: 'Without hesistation, you decided to go straight for the right option!' },
{ actorId: 'char_helper', text: 'Well, you passed this check. Try the other one now, and don\'t forget to fail this one.' },
(_, gameState, dispatch) => {
dispatch(setState({ visitedRight: gameState.visitedRight + 1 }));
}
]
},
{
id: 'exit',
kind: 'node',
lines: [
{ actorId: 'char_helper', text: 'Ok, it\'s time for us to part ways. We\'re done here' },
{ actorId: 'char_helper', text: 'Thanks, but this is the end.' },
(_engineState, _gameState, dispatch) => {
dispatch(setState({ isOver: true }))
},
]
},
{
id: 'mailbox',
kind: 'node',
next: 'outside',
lines: [
{ text: 'There\'s a letter inside.' },
{ text: 'You open it to take a look...' },
{ text: '"Welcome to Discoteque! An engine for games of adventure, danger, and low cunning!"' },
({ skillPoints }, gameState, dispatch) => {
if (!gameState.readMailbox) {
dispatch(setState({ readMailbox: true }));
awardSkill(dispatch, skillPoints);
}
return { text: `"No browser should be without one!" ${!gameState.readMailbox ? "[+1 Skill Point]" : ""}`, time: 5 };
}
]
},
{
id: 'trapdoor_end',
kind: 'node',
lines: [
{ text: 'You went to search for a trapdoor to the great underground empire!'},
(_engineState, _gameState, dispatch) => {
dispatch(setState({ isOver: true }));
return { text: 'It got dark and you got eaten by a grue.', time: 240 }
},
]
},
{
id: 'trapdoor_win',
kind: 'node',
lines: [
{ text: 'You went to search for a trapdoor to the great underground empire!', time: 240 },
{ text: 'You have found the coveted trap door! Many adventures await you...' },
(_engineState, _gameState, dispatch) => {
dispatch(setState({ isOver: true }));
return { text: 'But not in this demo!' }
},
]
},
{
id: 'about_trapdoor_fail',
kind: 'node', next: 'dialogue_helper',
lines: [
{ text: 'To ask this question you need to formulate it first...' },
{ text: 'But you can\'t be bothered to do it right now' },
],
},
{
'id': 'dialogue_helper', kind: 'node',
lines: [
(_, { talkedAboutTrapdoor }) => {
const trapdoorTalkSkill = talkedAboutTrapdoor ? 0 : 10;
return { text: 'What do you want to ask?', options: [
{ text: '"Tell me about that choice."', value: 'about_choice' },
{ text: '"Tell me about the trapdoor"', value: 'about_trapdoor', skill: {
name: 'tired', difficulty: trapdoorTalkSkill, failTo: 'about_trapdoor_fail'
} },
{ text: '"I\'m ready to pick"', value: 'pre_choice' },
{ text: '"Just came by to say hi!"', value: 'inside' },
] };
},
],
},
{
'id': 'about_choice', kind: 'node', next: 'dialogue_helper',
lines: [
(_, gameState, dispatch) => {
dispatch(setState({ askedAboutChoice: true }));
return gameState.askedAboutChoice
? { actorId: 'char_helper', text: 'Huh? I thought I already explained it to you...' }
: null;
},
{ actorId: 'char_helper', text: 'Well, there\'s not much to tell. It\'s just a demo Dale devised to showcase his engine.' },
{ actorId: 'char_helper', text: 'Don\' ask questions, just pick. Tell me when you\'re ready to pick.' },
]
},
{
id: 'about_trapdoor', kind: 'node', next: 'dialogue_helper',
lines: [
{ actorId: 'char_helper', text: 'Well, it\'s a cat in the box kind of thing.' },
{ actorId: 'char_helper', text: 'Some claim they have seen and used it. Others say it\'s a joke...' },
{ actorId: 'char_helper', text: 'What I know for sure is that those who aren\'t [MANIC] enough to take on the door are destined to fail.' },
{ actorId: 'char_helper', text: 'They will wander the house, going in circles, again and again...' },
{ actorId: 'char_helper', text: 'Searching for the door, in vain...' },
{ actorId: 'char_helper', text: 'Until it gets so, so dark that they get eaten by a fearsome grue, without even noticing!' },
{ actorId: 'char_helper', text: 'What a *grue*some end, right?' },
{ text: 'Helper starts laughing and can\'t help themselves. You don\'t appreciate the joke though.' },
{ actorId: 'char_helper', text: 'Well, enought about gruesome things, let\'s get back to the topic of chosing' },
({ skillPoints }, gameState, dispatch) => {
const { talkedAboutTrapdoor } = gameState;
if (!talkedAboutTrapdoor) {
dispatch(setState({ talkedAboutTrapdoor: true }));
awardSkill(dispatch, skillPoints);
return { text: 'You feel like this will help you search for the trap door. [+1 Skill Point]' };
}
}
]
},
{
id: 'look_trapdoor', kind: 'node', next: 'inside',
lines: [
{ text: 'You scan the room for the presence of a trapdoor...' },
{ text: 'But nothing comes up!' },
{ text: 'It\'s almost as if it\'s there but every time you look at it moves to another place.' },
{ text: 'Or perhaps it is always just right out of the corner of your eye...' },
({ skillPoints }, gameState, dispatch) => {
const { lookedAtTrapdoor } = gameState;
if (lookedAtTrapdoor) {
return { text: 'Looks like you will have to search more thoroughly.' }
}
dispatch(setState({ lookedAtTrapdoor: true }));
awardSkill(dispatch, skillPoints)
return { text: 'You feel like you\'ll have an easier time looking for the trapdoor properly. [+1 Skill Point]' }
},
],
}
]


// { actorId: 'char_helper', text: '' },
const actors: IActor<IGameState>[] = [
{ 'id': 'char_helper', kind: 'actor', name: 'Helper', image: require('@/assets/images/user.png').default, lines: [
{ actorId: 'char_helper', text: 'Back again, huh?' },
{ actorId: 'char_helper', text: 'Then I guess you\'re ready to pick right from left now.', },
], next: 'dialogue_helper' },
{ 'id': 'char_you', kind: 'actor', name: 'You', lines: [] },
]

const locations: ILocation<IGameState>[] = [
{ 'id': 'outside', kind: 'location', name: 'Great Outdoors', lines: [
(_, _gameState, dispatch) => {
dispatch(setState({ beenOutside: true }));
return { text: 'You are standing in an open field, west of house.' };
},
{ text: 'The sea of green extends into all directions, as far as your eye can see.' },
{ text: 'What now?', options: [
{ text: 'Look inside mailbox', value: 'mailbox' },
{ text: 'Go inside the house', value: 'inside' },
] },
] },
{ 'id': 'inside', kind: 'location', name: 'Inside House', lines: [
{ text: 'You are standing inside a small wooden house. The setup feels familiar... ' },
{ text: 'You almost expect there to be a trapdoor to a great underground empire.' },
{ text: 'You can see Helper here as well. They are standing in the corner, gesturing you to come and talk to them.' },
(_, { lookedAtTrapdoor, talkedAboutTrapdoor }) => {
const searchDifficulties = [
lookedAtTrapdoor && 10,
talkedAboutTrapdoor && 5,
].filter(id => id) as Array<number>;
const searchDifficulty = searchDifficulties.reduce(
(acc, val) => acc - val,
25,
);
const searchEffects = [
lookedAtTrapdoor && '[+10 from scanning the room]',
talkedAboutTrapdoor && '[+5 from talking about trap door with Helper]',
].filter(id => id);
return { text: 'What will you do now?', options: [
{ text: 'Go back outside', value: 'outside' },
{ text: 'Talk to Helper', value: 'char_helper' },
{ text: 'Scan the room for the trapdoor', value: 'look_trapdoor' },
{
text: `Search thoroughly for the trapdoor ${searchEffects.join(' ')}`,
value: 'trapdoor_win',
skill: { name: 'manic', difficulty: searchDifficulty, failTo: 'trapdoor_end' }
},
] }
},
] },
];

interface IGameConfig extends EngineConfig<IGameState, undefined, GameSkills> {
}

export const config: IGameConfig = {
reducer: reducer,
startNode: 'trying_out',
nodes: [
...nodes,
...actors,
...locations,
],
chrono: {
time: 1435,
date: "10 September",
},
onDayCycle: (time) => {
const day = time.date?.substr(0, 2) || "10";
const restOfDate = time.date?.substr(2) || " September";
const dayNum = Number(day) !== NaN ? Number(day) : 10;
return {
time: 0,
date: `${dayNum + 1}${restOfDate}`,
}
},
skills,
skillPointsOnStart: 5,
skillDescriptoins: {
'manic': 'Out of this world at the highest pace',
'tired': 'Slow burning and contemplation',
}
}

export default config;

+ 0
- 0
src/index.html View File


+ 6
- 0
src/index.ts View File

@@ -0,0 +1,6 @@
import makeApp from 'discoteque/lib';

import 'discoteque/src/index.css';
import config from '@/game';

makeApp(config);

+ 38
- 0
tsconfig.json View File

@@ -0,0 +1,38 @@
{
"compilerOptions": {
"target": "es2018",
"module": "commonjs",
"lib": [
"es2015",
"es2016",
"es2017",
"es2018",
"es2019",
"es2020",
"esNext",
"dom"
],
"outDir": "./lib",
"declaration": true,
"jsx": "react",
"sourceMap": true,
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"moduleResolution": "node",
"forceConsistentCasingInFileNames": true,
"esModuleInterop": true,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
},
"plugins": [
{
"name": "typescript-plugin-css-modules",
"options": {
"customMatcher": ".css"
}
}
]
}
}

+ 50
- 0
webpack.config.js View File

@@ -0,0 +1,50 @@
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
mode: 'none',
entry: {
app: path.join(__dirname, 'src', 'index.ts')
},
target: 'web',
resolve: {
alias: {
'@': path.resolve(__dirname, 'src/'),
},
extensions: ['.ts', '.tsx', '.js']
},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: '/node_modules/'
},
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader',
],
},
{
test: /\.(png|jpg|jpeg|gif|mp4)$/,
use: [
'file-loader',
],
},
],
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, 'src', 'index.html')
})
]
};

+ 3907
- 0
yarn-error.log
File diff suppressed because it is too large
View File


+ 4797
- 0
yarn.lock
File diff suppressed because it is too large
View File


Loading…
Cancel
Save