Ads for free campaign players

Display leaderboard ads on campaign and play views.
Do no show ads in classroom, picoCTF, or to teachers.
Add no ads blurb to subscription features matrix.
Scale game UI for ads on short screens.

Closes 
This commit is contained in:
Matt Lott 2016-03-15 15:51:59 -07:00
parent 1bab6cee88
commit 255ebbc048
12 changed files with 292 additions and 204 deletions

View file

@ -530,11 +530,20 @@ module.exports = Surface = class Surface extends CocoClass
newWidth = 0.55 * pageWidth
newHeight = newWidth / aspectRatio
return unless newWidth > 0 and newHeight > 0
return if newWidth is oldWidth and newHeight is oldHeight and not @options.spectateGame
return if newWidth < 200 or newHeight < 200
#scaleFactor = if application.isIPadApp then 2 else 1 # Retina
scaleFactor = 1
@normalCanvas.add(@webGLCanvas).attr width: newWidth * scaleFactor, height: newHeight * scaleFactor
if @options.stayVisible
availableHeight = window.innerHeight
availableHeight -= $('.ad-container').outerHeight()
availableHeight -= $('#game-area').outerHeight() - $('#canvas-wrapper').outerHeight()
scaleFactor = availableHeight / newHeight if availableHeight < newHeight
newWidth *= scaleFactor
newHeight *= scaleFactor
return if newWidth is oldWidth and newHeight is oldHeight and not @options.spectateGame
return if newWidth < 200 or newHeight < 200
@normalCanvas.add(@webGLCanvas).attr width: newWidth, height: newHeight
# Cannot do this to the webGLStage because it does not use scaleX/Y.
# Instead the LayerAdapter scales webGL-enabled layers.

View file

@ -502,6 +502,7 @@
feature5: "Video tutorials"
feature6: "Premium email support"
feature7: "Private <strong>Clans</strong>"
feature8: "<strong>No ads!</strong>"
free: "Free"
month: "month"
must_be_logged: "You must be logged in first. Please create an account or log in from the menu above."

View file

@ -137,6 +137,16 @@ module.exports = class User extends CocoModel
application.tracker.identify announcesActionAudioGroup: @announcesActionAudioGroup unless me.isAdmin()
@announcesActionAudioGroup
getCampaignAdsGroup: ->
return @campaignAdsGroup if @campaignAdsGroup
group = me.get('testGroupNumber') % 2
@campaignAdsGroup = switch group
when 0 then 'no-ads'
when 1 then 'leaderboard-ads'
@campaignAdsGroup = 'no-ads' if me.isAdmin()
application.tracker.identify campaignAdsGroup: @campaignAdsGroup unless me.isAdmin()
@campaignAdsGroup
getHomepageGroup: ->
# Only testing on en-US so localization issues are not a factor
return 'new-home-student' unless _.string.startsWith(me.get('preferredLanguage', true) or 'en-US', 'en')

View file

@ -111,7 +111,7 @@
text-align: center
tr
td
padding: 3px
padding: 2px
border-width: 0px
border-top-width: 1px
border-color: rgba(85, 85, 85, 0.1)

View file

@ -628,6 +628,14 @@ $gameControlMargin: 30px
img
height: 30px
.ad-container
width: 100%
height: 90px
text-align: center
.gameplay-container
position: absolute
body.ipad #campaign-view
// iPad only supports up to Kithgard Gates for now.
.campaign-switch

View file

@ -66,6 +66,7 @@ $level-resize-transition-time: 0.5s
.level-content
position: relative
background-color: black
#canvas-wrapper
top: 50px
@ -259,6 +260,10 @@ $level-resize-transition-time: 0.5s
right: 15px
font-size: 30px
.ad-container
width: 100%
height: 90px
text-align: center
html.fullscreen-editor
#level-view

View file

@ -68,14 +68,22 @@
span.glyphicon.glyphicon-ok
tr
td.feature-description
span(data-i18n="subscribe.feature6")
span(data-i18n="[html]subscribe.feature7")
if !me.isOnPremiumServer()
td.free-cell
td.center-ok
span.glyphicon.glyphicon-ok
tr
td.feature-description
span(data-i18n="[html]subscribe.feature7")
span(data-i18n="subscribe.feature6")
if !me.isOnPremiumServer()
td.free-cell
td.center-ok
span.glyphicon.glyphicon-ok
if me.getCampaignAdsGroup() === 'leaderboard-ads'
tr
td.feature-description
span(data-i18n="[html]subscribe.feature8")
if !me.isOnPremiumServer()
td.free-cell
td.center-ok

View file

@ -1,3 +1,20 @@
if view.showAds()
// TODO: loading this multiple times yields script error:
// Uncaught TagError: adsbygoogle.push() error: All ins elements in the DOM with class=adsbygoogle already have ads in them.
.ad-container
if campaign
script(async, src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js")
ins.adsbygoogle(style="display:inline-block;width:728px;height:90px", data-ad-client="ca-pub-6640930638193614", data-ad-slot="4924994487")
script.
(adsbygoogle = window.adsbygoogle || []).push({});
else
script(async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js")
ins.adsbygoogle(style="display:inline-block;width:728px;height:90px", data-ad-client="ca-pub-6640930638193614", data-ad-slot="4469166082")
script.
(adsbygoogle = window.adsbygoogle || []).push({});
// TODO: .gameplay-container causes world map buttons to briefly appear in top left of screen
.gameplay-container
a(href="/").picoctf-hide
img.small-nav-logo(src="/images/pages/base/logo.png", title="CodeCombat - Learn how to code by playing a game", alt="CodeCombat")

View file

@ -1,3 +1,13 @@
if view.showAds()
// TODO: loading this multiple times yields script error:
// Uncaught TagError: adsbygoogle.push() error: All ins elements in the DOM with class=adsbygoogle already have ads in them.
.ad-container
script(async, src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js")
ins.adsbygoogle(style="display:inline-block;width:728px;height:90px", data-ad-client="ca-pub-6640930638193614", data-ad-slot="5527096883")
script.
(adsbygoogle = window.adsbygoogle || []).push({});
.game-container
#level-loading-view
.level-content

View file

@ -22,7 +22,7 @@
.product
h4.subscription-gem-amount x{{gems}} / mo
h3(data-i18n="account.subscription")
if me.isPremium()
if me.hasSubscription()
button.disabled.start-subscription-button.btn.btn-lg.btn-illustrated.btn-success
| ✓
span(data-i18n="account.subscribed")

View file

@ -266,6 +266,11 @@ module.exports = class CampaignView extends RootView
authModal.mode = 'signup'
@openModalView authModal
showAds: ->
if application.isProduction() && !me.isPremium() && !me.isTeacher() && !window.serverConfig.picoCTF
return me.getCampaignAdsGroup() is 'leaderboard-ads'
false
annotateLevel: (level) ->
level.position ?= { x: 10, y: 10 }
level.locked = not me.ownsLevel level.original
@ -554,6 +559,7 @@ module.exports = class CampaignView extends RootView
aspectRatio = mapWidth / mapHeight
pageWidth = @$el.width()
pageHeight = @$el.height()
pageHeight -= adContainerHeight if adContainerHeight = $('.ad-container').outerHeight()
widthRatio = pageWidth / mapWidth
heightRatio = pageHeight / mapHeight
# Make sure we can see the whole map, fading to background in one dimension.

View file

@ -148,6 +148,13 @@ module.exports = class PlayLevelView extends RootView
application.tracker?.trackEvent 'Finished Level Load', category: 'Play Level', label: @levelID, level: @levelID, loadDuration: @loadDuration
application.tracker?.trackTiming @loadDuration, 'Level Load Time', @levelID, @levelID
isCourseMode: -> @courseID and @courseInstanceID
showAds: ->
if application.isProduction() && !me.isPremium() && !me.isTeacher() && !window.serverConfig.picoCTF && !@isCourseMode()
return me.getCampaignAdsGroup() is 'leaderboard-ads'
false
# CocoView overridden methods ###############################################
getRenderData: ->
@ -326,7 +333,13 @@ module.exports = class PlayLevelView extends RootView
initSurface: ->
webGLSurface = $('canvas#webgl-surface', @$el)
normalSurface = $('canvas#normal-surface', @$el)
@surface = new Surface(@world, normalSurface, webGLSurface, thangTypes: @supermodel.getModels(ThangType), observing: @observing, playerNames: @findPlayerNames(), levelType: @level.get('type', true))
surfaceOptions =
thangTypes: @supermodel.getModels(ThangType)
observing: @observing
playerNames: @findPlayerNames()
levelType: @level.get('type', true)
stayVisible: @showAds()
@surface = new Surface(@world, normalSurface, webGLSurface, surfaceOptions)
worldBounds = @world.getBounds()
bounds = [{x: worldBounds.left, y: worldBounds.top}, {x: worldBounds.right, y: worldBounds.bottom}]
@surface.camera.setBounds(bounds)
@ -499,7 +512,8 @@ module.exports = class PlayLevelView extends RootView
break
Backbone.Mediator.publish 'tome:cast-spell', {}
onWindowResize: (e) => @endHighlight()
onWindowResize: (e) =>
@endHighlight()
onDisableControls: (e) ->
return if e.controls and not ('level' in e.controls)
@ -535,7 +549,7 @@ module.exports = class PlayLevelView extends RootView
@endHighlight()
options = {level: @level, supermodel: @supermodel, session: @session, hasReceivedMemoryWarning: @hasReceivedMemoryWarning, courseID: @courseID, courseInstanceID: @courseInstanceID, world: @world}
ModalClass = if @level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop', 'course', 'course-ladder'] then HeroVictoryModal else VictoryModal
ModalClass = CourseVictoryModal if @courseID and @courseInstanceID
ModalClass = CourseVictoryModal if @isCourseMode()
ModalClass = PicoCTFVictoryModal if window.serverConfig.picoCTF
victoryModal = new ModalClass(options)
@openModalView(victoryModal)