Added optional goals. Working on lines-of-code goal. Fixed bugs with HeroVictoryModal not firing.

This commit is contained in:
Nick Winter 2014-10-20 13:57:32 -07:00
parent 67166f1496
commit ec3e1d0bce
6 changed files with 32 additions and 7 deletions
app
lib/world
schemas
models
subscriptions
styles/play
views/play/level/modal

View file

@ -45,7 +45,7 @@ module.exports = class GoalManager extends CocoClass
'world:thang-left-map': 'onThangLeftMap'
'world:thang-collected-item': 'onThangCollectedItem'
'world:user-code-problem': 'onUserCodeProblem'
'world:ended': 'onWorldEnded'
'world:lines-of-code-counted': 'onLinesOfCodeCounted'
onLevelRestarted: ->
@goals = []
@ -116,10 +116,12 @@ module.exports = class GoalManager extends CocoClass
checkOverallStatus: (ignoreIncomplete=false) ->
overallStatus = null
goals = if @goalStates then _.values @goalStates else []
goals = (g for g in goals when not g.optional)
goals = (g for g in goals when g.team in [undefined, @team]) if @team
statuses = if @goalStates then (goal.status for goal in goals) else []
overallStatus = 'success' if statuses.length > 0 and _.every(statuses, (s) -> s is 'success' or (ignoreIncomplete and s is null))
overallStatus = 'failure' if statuses.length > 0 and 'failure' in statuses
#console.log 'got overallStatus', overallStatus, 'from goals', goals, 'goalStates', @goalStates, 'statuses', statuses
overallStatus
# WORLD GOAL TRACKING
@ -132,6 +134,7 @@ module.exports = class GoalManager extends CocoClass
status: null # should eventually be either 'success', 'failure', or 'incomplete'
keyFrame: 0 # when it became a 'success' or 'failure'
team: goal.team
optional: goal.optional
}
@initGoalState(state, [goal.killThangs, goal.saveThangs], 'killed')
for getTo in goal.getAllToLocations ? []
@ -142,7 +145,7 @@ module.exports = class GoalManager extends CocoClass
@initGoalState(state, [goal.leaveOffSides?.who, goal.keepFromLeavingOffSides?.who], 'left')
@initGoalState(state, [goal.collectThangs?.targets, goal.keepFromCollectingThangs?.targets], 'collected')
@initGoalState(state, [goal.linesOfCode?.who], 'lines') # TODO: find out how many lines there are
@initGoalState(state, [goal.codeProblems?.who], 'problems') # TODO: count initial problems, not just runtime
@initGoalState(state, [goal.codeProblems], 'problems') # TODO: count initial problems, not just runtime
@goalStates[goal.id] = state
onThangDied: (e, frameNumber) ->
@ -192,11 +195,21 @@ module.exports = class GoalManager extends CocoClass
onUserCodeProblem: (e, frameNumber) ->
for goal in @goals ? [] when goal.codeProblems
@checkCodeProblem goal.id, goal.codeProblems.who, e.thang, frameNumber
@checkCodeProblem goal.id, goal.codeProblems, e.thang, frameNumber
checkCodeProblem: (goalID, who, thang, frameNumber) ->
return unless thang.id in who or thang.team in who
@updateGoalState goal.id, thang.id, 'problems', frameNumber
@updateGoalState goalID, thang.id, 'problems', frameNumber
onLinesOfCodeCounted: (e, frameNumber) ->
for goal in @goals ? [] when goal.linesOfCode
@checkLinesOfCode goal.id, goal.linesOfCode, e.thang, e.linesUsed, frameNumber
checkLinesOfCode: (goalID, who, thang, linesUsed, frameNumber) ->
return unless thang.id in who or thang.team in who
# TODO: somehow pull in linesUsed
console.log 'checkLinesOfCode', goalID, who, thang, linesUsed, frameNumber
#@updateGoalState goalID, thang.id, 'lines', frameNumber
wrapUpGoalStates: (finalFrame) ->
for goalID, state of @goalStates
@ -258,12 +271,12 @@ module.exports = class GoalManager extends CocoClass
# saveThangs: by default we would want to save all the Thangs, which means that we would want none of them to be 'done'
numNeeded = _.size(stateThangs) - Math.max((goal.howMany ? 1), _.size stateThangs) + 1
numDone = _.filter(stateThangs).length
#console.log 'needed', numNeeded, 'done', numDone, 'of total', _.size(stateThangs), 'with how many', goal.howMany, 'and stateThangs', stateThangs, 'for', goalID, thangID, 'on frame', frameNumber
console.log 'needed', numNeeded, 'done', numDone, 'of total', _.size(stateThangs), 'with how many', goal.howMany, 'and stateThangs', stateThangs, 'for', goalID, thangID, 'on frame', frameNumber
return unless numDone >= numNeeded
return if state.status and not success # already failed it; don't wipe keyframe
state.status = if success then 'success' else 'failure'
state.keyFrame = frameNumber
#console.log goalID, 'became', success, 'on frame', frameNumber, 'with overallStatus', @checkOverallStatus true
console.log goalID, 'became', success, 'on frame', frameNumber, 'with overallStatus', @checkOverallStatus true
if overallStatus = @checkOverallStatus true
matchedGoals = (_.find(@goals, {id: goalID}) for goalID, goalState of @goalStates when goalState.status is overallStatus)
mostEagerGoal = _.min matchedGoals, 'worldEndsAfter'

View file

@ -307,7 +307,7 @@ module.exports = class World
publishNote: (channel, event) ->
event ?= {}
channel = 'world:' + channel
for script in @scripts
for script in @scripts ? []
continue if script.channel isnt channel
scriptNote = new WorldScriptNote script, event
continue if scriptNote.invalid

View file

@ -31,6 +31,7 @@ GoalSchema = c.object {title: 'Goal', description: 'A goal that the player can a
worldEndsAfter: {title: 'World Ends After', description: 'When included, ends the world this many seconds after this goal succeeds or fails.', type: 'number', minimum: 0, exclusiveMinimum: true, maximum: 300, default: 3}
howMany: {title: 'How Many', description: 'When included, require only this many of the listed goal targets instead of all of them.', type: 'integer', minimum: 1}
hiddenGoal: {title: 'Hidden', description: 'Hidden goals don\'t show up in the goals area for the player until they\'re failed. (Usually they\'re obvious, like "don\'t die".)', 'type': 'boolean' }
optional: {title: 'Optional', description: 'Optional goals do not need to be completed for overallStatus to be success.', type: 'boolean'}
team: c.shortString(title: 'Team', description: 'Name of the team this goal is for, if it is not for all of the playable teams.')
killThangs: c.array {title: 'Kill Thangs', description: 'A list of Thang IDs the player should kill, or team names.', uniqueItems: true, minItems: 1, 'default': ['ogres']}, thang
saveThangs: c.array {title: 'Save Thangs', description: 'A list of Thang IDs the player should save, or team names', uniqueItems: true, minItems: 1, 'default': ['humans']}, thang
@ -62,6 +63,8 @@ GoalSchema = c.object {title: 'Goal', description: 'A goal that the player can a
keepFromCollectingThangs: c.object {title: 'Keep From Collecting', description: 'Thangs that the player must prevent other Thangs from collecting.', required: ['who', 'targets']},
who: c.array {title: 'Who', description: 'The Thangs which must not collect the target items.', minItems: 1}, thang
targets: c.array {title: 'Targets', description: 'The target items which the Thangs must not collect.', minItems: 1}, thang
codeProblems: c.array {title: 'Code Problems', description: 'A list of Thang IDs that should not have any code problems, or team names.', uniqueItems: true, minItems: 1, 'default': ['humans']}, thang
linesOfCode: {title: 'Lines of Code', description: 'A mapping of Thang IDs or teams to how many many lines of code should be allowed (well, statements).', type: 'object', default: {humans: 10}, additionalProperties: {type: 'integer', description: 'How many lines to allow for this Thang.'}}
ResponseSchema = c.object {title: 'Dialogue Button', description: 'A button to be shown to the user with the dialogue.', required: ['text']},
text: {title: 'Title', description: 'The text that will be on the button', 'default': 'Okay', type: 'string', maxLength: 30}

View file

@ -39,3 +39,7 @@ module.exports =
level: {type: 'string', enum: ['info', 'warning', 'error']}
type: {type: 'string'}
error: {type: 'object'}
'world:lines-of-code-counted': c.object {required: ['thang', 'linesUsed']},
thang: {type: 'object'}
linesUsed: {type: 'integer'}

View file

@ -287,3 +287,7 @@ $gameControlMargin: 30px
&.vol-down .glyphicon.glyphicon-volume-down
display: inline-block
body:not(.ipad) #world-map-view
.level-info-container
pointer-events: none

View file

@ -93,6 +93,7 @@ module.exports = class HeroVictoryModal extends ModalView
c.thangTypes = @thangTypes
c.me = me
c.readyToRank = @level.get('type', true) is 'hero-ladder' and @session.readyToRank()
c.level = @level
return c
afterRender: ->