Skip to content

Commit

Permalink
Merge pull request #133 from oskarrough/feat/map2
Browse files Browse the repository at this point in the history
Feat/map2
  • Loading branch information
oskarrough authored Feb 11, 2021
2 parents 5d87ac6 + 5e4ec53 commit 738f193
Show file tree
Hide file tree
Showing 31 changed files with 1,113 additions and 373 deletions.
7 changes: 5 additions & 2 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@
sourceType: "module"
},
env: {
"browser": true
"browser": true,
"es6": true
},
extends: [
"eslint:recommended",
"plugin:prettier/recommended"
],
"ignorePatterns": ["web_modules/"]
ignorePatterns: [
"web_modules/"
]
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"esm": "^3.2.25",
"gsap": "^3.5.1",
"htm": "^3.0.3",
"immer": "^8.0.0",
"immer": "^6.0.0",
"tone": "^14.8.9"
},
"ava": {
Expand Down
192 changes: 100 additions & 92 deletions public/content/dungeon-encounters.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,32 @@
import Dungeon, {MonsterRoom, Monster, CampfireRoom} from '../game/dungeon.js'
import {random} from '../game/utils.js'
import Dungeon, {MonsterRoom, Monster} from '../game/dungeon.js'

// Hello. With the imported functions above you can create a dungeon with different rooms and monsters.
// This file contains the example dungeon used in Slay the Web.

export const dungeonWithMap = () => {
return Dungeon({
rows: 7,
columns: 6,
minEncounters: 3,
maxEncounters: 4,
// encounters: 'ABC',
paths: '0235',
})
}

// This is the dungeon used in tests. Don't change it without running tests.
export const createTestDungeon = () => {
const dungeon = Dungeon({rows: 3, columns: 1})
// The tests rely on the first room having a single monster, second two monsters.
const intents = [{block: 7}, {damage: 10}, {damage: 8}, {}, {damage: 14}]
dungeon.graph[1][0].room = MonsterRoom(Monster({hp: 42, intents}))
dungeon.graph[2][0].room = MonsterRoom(Monster({hp: 24, intents}), Monster({hp: 13, intents}))
return dungeon
}

/*
// import Dungeon, {MonsterRoom, Monster, CampfireRoom} from '../game/dungeon.js'
// import {random} from '../game/utils.js'
const TrioMonsterA = () =>
Monster({
hp: random(39, 46),
Expand All @@ -15,93 +38,78 @@ const TrioMonsterB = () =>
hp: random(39, 46),
intents: [{damage: 10}, {weak: 1}],
})

export const createSimpleDungeon = () => {
return Dungeon({
rooms: [
MonsterRoom(
Monster({
hp: random(18, 20),
intents: [{damage: 7}, {damage: 11}, {damage: 7}, {block: 9}],
random: 2,
})
),
MonsterRoom(
Monster({
hp: random(43, 47),
intents: [{vulnerable: 1}, {damage: 10}, {damage: 6}, {}, {weak: 1}],
random: 2,
})
),
CampfireRoom(),
MonsterRoom(
Monster({
hp: random(13, 17),
intents: [{damage: 7}, {block: 4, damage: 8}, {damage: 6}, {}, {block: 6}],
random: 2,
}),
Monster({
hp: 29,
intents: [{damage: 9}, {damage: 8}, {weak: 1}, {damage: 6}, {}],
random: 2,
})
),
CampfireRoom(),
MonsterRoom(
Monster({
hp: random(34, 36),
intents: [{weak: 1}, {damage: 10}, {damage: 6}, {}, {weak: 1}],
random: 2,
}),
Monster({
hp: random(56, 58),
intents: [{vulnerable: 1}, {damage: 6}, {damage: 9}, {block: 10}],
random: 2,
})
),
MonsterRoom(Monster({hp: 70, block: 12, intents: [{block: 5}, {damage: 16}]})),
CampfireRoom(),
MonsterRoom(
Monster({hp: random(12, 15), random: 1, intents: [{damage: 6}]}),
Monster({hp: random(12, 15), random: 1, intents: [{damage: 6}]}),
Monster({hp: random(10, 16), random: 3, intents: [{damage: 6}]})
),
// TreasureRoom(),
CampfireRoom(),
MonsterRoom(
Monster({
hp: 46,
intents: [{damage: 12}, {block: 6, damage: 11}, {block: 5, damage: 16}, {}, {block: 6}],
})
),
// UnknownRoom(),
MonsterRoom(TrioMonsterA(), TrioMonsterB(), TrioMonsterA()),
MonsterRoom(Monster({hp: 60, intents: [{damage: 12}], random: 5})),
MonsterRoom(
Monster({
hp: 48,
intents: [{weak: 1}, {block: 10, damage: 10}, {damage: 21}],
})
),
CampfireRoom(),
MonsterRoom(
Monster({
hp: random(100, 140),
intents: [{damage: 12}, {block: 6}, {damage: 16}, {damage: 7}, {weak: 2}],
random: 5,
})
),
],
})
}

// This is the dungeon used in tests. Don't change it without running tests.
export const createTestDungeon = () => {
const intents = [{block: 7}, {damage: 10}, {damage: 8}, {}, {damage: 14}]
return Dungeon({
rooms: [
MonsterRoom(Monster({hp: 42, intents})),
MonsterRoom(Monster({hp: 24, intents}), Monster({hp: 13, intents})),
],
})
}
rooms: [
MonsterRoom(
Monster({
hp: random(18, 20),
intents: [{damage: 7}, {damage: 11}, {damage: 7}, {block: 9}],
random: 2,
})
),
MonsterRoom(
Monster({
hp: random(43, 47),
intents: [{vulnerable: 1}, {damage: 10}, {damage: 6}, {}, {weak: 1}],
random: 2,
})
),
CampfireRoom(),
MonsterRoom(
Monster({
hp: random(13, 17),
intents: [{damage: 7}, {block: 4, damage: 8}, {damage: 6}, {}, {block: 6}],
random: 2,
}),
Monster({
hp: 29,
intents: [{damage: 9}, {damage: 8}, {weak: 1}, {damage: 6}, {}],
random: 2,
})
),
CampfireRoom(),
MonsterRoom(
Monster({
hp: random(34, 36),
intents: [{weak: 1}, {damage: 10}, {damage: 6}, {}, {weak: 1}],
random: 2,
}),
Monster({
hp: random(56, 58),
intents: [{vulnerable: 1}, {damage: 6}, {damage: 9}, {block: 10}],
random: 2,
})
),
MonsterRoom(Monster({hp: 70, block: 12, intents: [{block: 5}, {damage: 16}]})),
CampfireRoom(),
MonsterRoom(
Monster({hp: random(12, 15), random: 1, intents: [{damage: 6}]}),
Monster({hp: random(12, 15), random: 1, intents: [{damage: 6}]}),
Monster({hp: random(10, 16), random: 3, intents: [{damage: 6}]})
),
// TreasureRoom(),
CampfireRoom(),
MonsterRoom(
Monster({
hp: 46,
intents: [{damage: 12}, {block: 6, damage: 11}, {block: 5, damage: 16}, {}, {block: 6}],
})
),
// UnknownRoom(),
MonsterRoom(TrioMonsterA(), TrioMonsterB(), TrioMonsterA()),
MonsterRoom(Monster({hp: 60, intents: [{damage: 12}], random: 5})),
MonsterRoom(
Monster({
hp: 48,
intents: [{weak: 1}, {block: 10, damage: 10}, {damage: 21}],
})
),
CampfireRoom(),
MonsterRoom(
Monster({
hp: random(100, 140),
intents: [{damage: 12}, {block: 6}, {damage: 16}, {damage: 7}, {weak: 2}],
random: 5,
})
),
],
*/
5 changes: 4 additions & 1 deletion public/game/action-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,28 @@ import actions from '../game/actions.js'

// The action manager makes use of queues to keep track of
// future and past actions in the game state. Also allowing us to undo.
export default function () {
export default function (props) {
const future = new Queue()
const past = new Queue()

// Enqueued items are added to the "future" list. An action looks like this:
// {type: 'dealDamage', amount: 7, ... }
function enqueue(action) {
if (props.debug) console.log('enqueue', action)
future.enqueue({action})
}

// Deqeueing means running the oldest action in the queue on a game state.
// The action is then moved to the "past". Returns the next state.
function dequeue(state) {
const {action} = future.dequeue() || {}
if (props.debug) console.log('dequeue', action)
let nextState
if (!action) return
try {
nextState = actions[action.type](state, action)
} catch (err) {
console.warn('Failed running action', action)
throw new Error(err)
}
past.enqueue({state, action})
Expand Down
42 changes: 27 additions & 15 deletions public/game/actions.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import produce, {setAutoFreeze} from '../web_modules/immer.js'
import produce from '../web_modules/immer.js'
import {createCard} from './cards.js'
import {shuffle, getTargets, getCurrRoom, clamp} from './utils.js'
import powers from './powers.js'
import {createSimpleDungeon} from '../content/dungeon-encounters.js'
import {dungeonWithMap} from '../content/dungeon-encounters.js'

// Without this, immer.js will throw an error if our `state` is modified outside of an action.
// While in theory a good idea, we're not there yet. It is a useful way to spot modifications
// of the game state that should not be there.
setAutoFreeze(false)
// setAutoFreeze(false)

// In Slay the Web, we have one big object with game state.
// Whenever we want to change something, call an "action" from this file.
Expand Down Expand Up @@ -35,7 +35,7 @@ function createNewGame() {

// By default a new game doesn't come with a dungeon. You have to set one explicitly. Look in dungeon-encounters.js for inspiration.
function setDungeon(state, dungeon) {
if (!dungeon) dungeon = createSimpleDungeon()
if (!dungeon) dungeon = dungeonWithMap()
return produce(state, (draft) => {
if (!dungeon) throw new Error('Missing a dungeon?')
draft.dungeon = dungeon
Expand Down Expand Up @@ -188,15 +188,15 @@ function applyCardPowers(state, {card, target}) {
draft.player.powers[name] = newStacks
} else if (card.target === 'all enemies') {
// Add powers that target all enemies.
draft.dungeon.rooms[draft.dungeon.index].monsters.forEach((monster) => {
draft.dungeon.graph[draft.dungeon.y][draft.dungeon.x].room.monsters.forEach((monster) => {
if (monster.currentHealth < 1) return
const newStacks = (monster.powers[name] || 0) + stacks
monster.powers[name] = newStacks
})
} else if (target) {
// const t = getTargets(draft, target)
const index = target.split('enemy')[1]
const monster = draft.dungeon.rooms[state.dungeon.index].monsters[index]
const monster = draft.dungeon.graph[draft.dungeon.y][draft.dungeon.x].room.monsters[index]
if (monster.currentHealth < 1) return
const newStacks = (monster.powers[name] || 0) + stacks
monster.powers[name] = newStacks
Expand Down Expand Up @@ -272,8 +272,13 @@ function reshuffleAndDraw(state) {

// Runs the "intent" for each monster in the current room
function takeMonsterTurn(state) {
const room = getCurrRoom(state)
if (!room.monsters) {
console.log('empty room?', room)
return state
}
return produce(state, (draft) => {
draft.dungeon.rooms[draft.dungeon.index].monsters.forEach((monster) => {
room.monsters.forEach((monster) => {
// Reset block at start of turn.
monster.block = 0

Expand Down Expand Up @@ -321,15 +326,22 @@ function rewardPlayer(state, {card}) {
})
}

function goToNextRoom(state) {
// Records a move on the map.
function move(state, {move}) {
let nextState = reshuffleAndDraw(state)

return produce(nextState, (draft) => {
draft.player.powers = {} // Clear temporary powers.
const number = state.dungeon.index
if (number === state.dungeon.rooms.length - 1) {
throw new Error('You have reached the end of the dungeon. Congratulations.')
}
draft.dungeon.index = number + 1
// Clear temporary powers, energy and block on player.
draft.player.powers = {}
draft.player.currentEnergy = 3
draft.player.block = 0
draft.dungeon.x = move.x
draft.dungeon.y = move.y
draft.dungeon.graph[move.y][move.x].didVisit = true
draft.dungeon.pathTaken.push({x: move.x, y: move.y})
// if (number === state.dungeon.rooms.length - 1) {
// throw new Error('You have reached the end of the dungeon. Congratulations.')
// }
})
}

Expand All @@ -349,7 +361,7 @@ export default {
discardHand,
drawCards,
endTurn,
goToNextRoom,
move,
playCard,
removeHealth,
reshuffleAndDraw,
Expand Down
Loading

0 comments on commit 738f193

Please sign in to comment.