2014-01-03 13:32:13 -05:00
SpellView = require ' ./spell_view '
SpellListTabEntryView = require ' ./spell_list_tab_entry_view '
{ me } = require ' lib/auth '
2014-05-08 14:43:00 -04:00
Aether . addGlobal ' Vector ' , require ' lib/world/vector '
Aether . addGlobal ' _ ' , _
2014-01-03 13:32:13 -05:00
module.exports = class Spell
loaded: false
view: null
entryView: null
2014-01-28 18:24:08 -05:00
constructor: (options) ->
@spellKey = options . spellKey
@pathComponents = options . pathComponents
@session = options . session
2014-05-15 18:18:15 -04:00
@spectateView = options . spectateView
2014-01-28 18:24:08 -05:00
@supermodel = options . supermodel
@skipProtectAPI = options . skipProtectAPI
2014-02-17 20:38:49 -05:00
@worker = options . worker
2014-01-28 18:24:08 -05:00
2014-06-18 01:17:44 -04:00
p = options . programmableMethod
@languages = p . languages ? { }
@ languages . javascript ? = p . source
2014-01-03 13:32:13 -05:00
@name = p . name
@permissions = read: p . permissions ? . read ? [ ] , readwrite: p . permissions ? . readwrite ? [ ] # teams
2014-06-18 01:17:44 -04:00
@ setLanguage if @ canWrite ( ) then options . language else ' javascript '
@useTranspiledCode = @ shouldUseTranspiledCode ( )
@source = @ originalSource
2014-04-26 17:21:26 -04:00
@parameters = p . parameters
if @ permissions . readwrite . length and sessionSource = @ session . getSourceFor ( @ spellKey )
@source = sessionSource
2014-01-03 13:32:13 -05:00
@thangs = { }
2014-04-18 17:59:08 -04:00
@view = new SpellView { spell: @ , session: @ session , worker: @ worker }
2014-01-03 13:32:13 -05:00
@ view . render ( ) # Get it ready and code loaded in advance
2014-06-26 01:56:39 -04:00
@tabView = new SpellListTabEntryView spell: @ , supermodel: @ supermodel , language: @ language
2014-01-03 13:32:13 -05:00
@ tabView . render ( )
2014-02-10 16:18:39 -05:00
@team = @ permissions . readwrite [ 0 ] ? " common "
Backbone . Mediator . publish ' tome:spell-created ' , spell: @
2014-01-03 13:32:13 -05:00
2014-02-11 15:10:21 -05:00
destroy: ->
@ view . destroy ( )
@ tabView . destroy ( )
2014-02-11 18:38:36 -05:00
@thangs = null
2014-02-17 20:38:49 -05:00
@worker = null
2014-02-11 16:10:59 -05:00
2014-06-18 01:17:44 -04:00
setLanguage: (@language) ->
@originalSource = @ languages [ language ] ? @ languages . javascript
2014-01-03 13:32:13 -05:00
addThang: (thang) ->
2014-02-06 17:00:27 -05:00
if @ thangs [ thang . id ]
@ thangs [ thang . id ] . thang = thang
else
@ thangs [ thang . id ] = { thang: thang , aether: @ createAether ( thang ) , castAether: null }
2014-01-03 13:32:13 -05:00
2014-02-05 18:16:59 -05:00
removeThangID: (thangID) ->
delete @ thangs [ thangID ]
2014-01-03 13:32:13 -05:00
canRead: (team) ->
( team ? me . team ) in @ permissions . read or ( team ? me . team ) in @ permissions . readwrite
canWrite: (team) ->
( team ? me . team ) in @ permissions . readwrite
getSource: ->
@ view . getSource ( )
transpile: (source) ->
if source
@source = source
else
source = @ getSource ( )
2014-02-17 20:38:49 -05:00
[ pure , problems ] = [ null , null ]
2014-05-15 18:18:15 -04:00
if @ useTranspiledCode
transpiledCode = @ session . get ( ' code ' )
2014-02-17 20:38:49 -05:00
for thangID , spellThang of @ thangs
unless pure
2014-05-23 15:04:42 -04:00
if @ useTranspiledCode and transpiledSpell = transpiledCode [ @ spellKey . split ( ' / ' ) [ 0 ] ] ? [ @ name ]
2014-05-15 18:18:15 -04:00
spellThang.aether.pure = transpiledSpell
else
pure = spellThang . aether . transpile source
problems = spellThang . aether . problems
2014-05-23 15:04:42 -04:00
#console.log "aether transpiled", source.length, "to", spellThang.aether.pure.length, "for", thangID, @spellKey
2014-02-17 20:38:49 -05:00
else
spellThang.aether.pure = pure
spellThang.aether.problems = problems
#console.log "aether reused transpilation for", thangID, @spellKey
null
2014-01-03 13:32:13 -05:00
hasChanged: (newSource=null, currentSource=null) ->
( newSource ? @ originalSource ) isnt ( currentSource ? @ source )
2014-04-22 11:54:35 -04:00
hasChangedSignificantly: (newSource=null, currentSource=null, cb) ->
2014-01-03 13:32:13 -05:00
for thangID , spellThang of @ thangs
aether = spellThang . aether
break
unless aether
console . error @ toString ( ) , " couldn ' t find a spellThang with aether of " , @ thangs
2014-04-22 14:04:56 -04:00
cb false
2014-04-22 11:54:35 -04:00
workerMessage =
function: " hasChangedSignificantly "
a: ( newSource ? @ originalSource )
spellKey: @ spellKey
b: ( currentSource ? @ source )
careAboutLineNumbers: true
careAboutLint: true
@ worker . addEventListener " message " , (e) =>
workerData = JSON . parse e . data
2014-04-22 14:04:56 -04:00
if workerData . function is " hasChangedSignificantly " and workerData . spellKey is @ spellKey
2014-05-01 20:41:06 -04:00
@ worker . removeEventListener " message " , arguments . callee , false
2014-04-22 11:54:35 -04:00
cb ( workerData . hasChanged )
@ worker . postMessage JSON . stringify ( workerMessage )
2014-04-26 17:21:26 -04:00
2014-01-03 13:32:13 -05:00
createAether: (thang) ->
2014-03-28 09:42:08 -04:00
aceConfig = me . get ( ' aceConfig ' ) ? { }
2014-05-15 00:54:36 -04:00
writable = @ permissions . readwrite . length > 0
2014-01-03 13:32:13 -05:00
aetherOptions =
problems:
jshint_W040: { level: " ignore " }
2014-02-03 16:58:25 -05:00
jshint_W030: { level: " ignore " } # aether_NoEffect instead
2014-04-14 14:18:02 -04:00
jshint_W038: { level: " ignore " } # eliminates hoisting problems
jshint_W091: { level: " ignore " } # eliminates more hoisting problems
jshint_E043: { level: " ignore " } # https://github.com/codecombat/codecombat/issues/813 -- since we can't actually tell JSHint to really ignore things
jshint_Unknown: { level: " ignore " } # E043 also triggers Unknown, so ignore that, too
2014-05-08 14:43:00 -04:00
aether_MissingThis: { level: ' error ' }
2014-05-15 00:54:36 -04:00
language: if @ canWrite ( ) then @ language else ' javascript '
2014-01-03 13:32:13 -05:00
functionName: @ name
functionParameters: @ parameters
yieldConditionally: thang . plan ?
2014-05-08 14:43:00 -04:00
globals: [ ' Vector ' , ' _ ' ]
2014-01-29 11:38:37 -05:00
# TODO: Gridmancer doesn't currently work with protectAPI, so hack it off
2014-05-15 00:54:36 -04:00
protectAPI: not ( @ skipProtectAPI or window . currentView ? . level . get ( ' name ' ) . match ( " Gridmancer " ) ) and writable # If anyone can write to this method, we must protect it.
2014-05-09 12:29:50 -04:00
includeFlow: false
2014-05-25 15:15:32 -04:00
executionLimit: 1 * 1000 * 1000
2014-02-22 15:01:05 -05:00
#console.log "creating aether with options", aetherOptions
2014-01-03 13:32:13 -05:00
aether = new Aether aetherOptions
2014-04-18 17:59:08 -04:00
workerMessage =
function: " createAether "
spellKey: @ spellKey
options: aetherOptions
@ worker . postMessage JSON . stringify workerMessage
2014-01-03 13:32:13 -05:00
aether
2014-05-15 00:54:36 -04:00
updateLanguageAether: (@language) ->
2014-03-16 21:14:04 -04:00
for thangId , spellThang of @ thangs
2014-05-15 00:54:36 -04:00
spellThang . aether ? . setLanguage @ language
2014-03-16 21:14:04 -04:00
spellThang.castAether = null
2014-06-26 01:56:39 -04:00
Backbone . Mediator . publish ' tome:spell-changed-language ' , spell: @ , language: @ language
2014-04-26 17:21:26 -04:00
workerMessage =
2014-04-18 17:59:08 -04:00
function: " updateLanguageAether "
2014-05-15 00:54:36 -04:00
newLanguage: @ language
2014-04-18 17:59:08 -04:00
@ worker . postMessage JSON . stringify workerMessage
2014-03-16 21:14:04 -04:00
@ transpile ( )
2014-01-03 13:32:13 -05:00
toString: ->
" <Spell: #{ @ spellKey } > "
2014-06-18 01:17:44 -04:00
shouldUseTranspiledCode: ->
# Determine whether this code has already been transpiled, or whether it's raw source needing transpilation.
return false unless @ permissions . readwrite . length # Only player-writable code will be stored transpiled.
return true if @ spectateView # Use transpiled code for both teams if we're just spectating.
teamSpells = @ session . get ( ' teamSpells ' )
team = @ session . get ( ' team ' ) ? ' humans '
return true if teamSpells and not _ . contains ( teamSpells [ team ] , @ spellKey ) # Use transpiled for enemy spells.
# Players without permissions can't view the raw code.
return true if @ session . get ( ' creator ' ) isnt me . id and not ( me . isAdmin ( ) or ' employer ' in me . get ( ' permissions ' ) )
false