From 8868199743fb218b16c29892ebd6f4c1092b14f3 Mon Sep 17 00:00:00 2001 From: dsc Date: Sun, 30 Jan 2011 01:20:13 -0800 Subject: [PATCH] Checkpoint adding equippable items. --- data/types/levels.yaml | 2 +- src/Y/types/array.cjs | 15 ++- src/ezl/layer/html.cjs | 12 +- src/ezl/layer/index.cjs | 14 ++ src/ezl/layer/layer.cjs | 51 ++++--- src/ezl/shape/circle.cjs | 2 +- src/ezl/shape/line.cjs | 2 +- src/ezl/shape/polygon.cjs | 6 +- src/ezl/shape/rect.cjs | 2 +- src/ezl/shape/shape.cjs | 2 +- src/ezl/widget/cooldown.cjs | 2 +- src/tanks/fx/explosion.cjs | 2 +- src/tanks/item/container.cjs | 269 ++++++++++++++++++++++++++++++++++ src/tanks/map/level.cjs | 4 +- src/tanks/mixins/inventoried.cjs | 15 +- src/tanks/ui/grid.cjs | 2 +- src/tanks/ui/inventory/backpack.cjs | 16 +- src/tanks/ui/inventory/container.cjs | 206 ++++++++++++++++++++++++++ src/tanks/ui/inventory/equipslot.cjs | 187 +++++++++++++++++++++++- src/tanks/ui/inventory/inventory.cjs | 17 -- src/tanks/ui/pathmapui.cjs | 2 +- www/css/lttl.css | 11 +- www/header.html | 8 +- 23 files changed, 765 insertions(+), 84 deletions(-) create mode 100644 src/tanks/item/container.cjs create mode 100644 src/tanks/ui/inventory/container.cjs delete mode 100644 src/tanks/ui/inventory/inventory.cjs diff --git a/data/types/levels.yaml b/data/types/levels.yaml index 89bb7fc..48e348f 100644 --- a/data/types/levels.yaml +++ b/data/types/levels.yaml @@ -71,7 +71,7 @@ types: - type: nitro loc: [325,475] - type: nitro - loc: [275,475] + loc: [325,25] three_test: name: Three Test diff --git a/src/Y/types/array.cjs b/src/Y/types/array.cjs index 3f8877d..37a181e 100644 --- a/src/Y/types/array.cjs +++ b/src/Y/types/array.cjs @@ -34,7 +34,10 @@ YCollection.subclass('YArray', function(YArray){ this['init'] = function initYArray(o){ - this._o = o || []; + if (o instanceof YArray) + this._o = o._o; + else + this._o = o || []; }; this['clone'] = @@ -90,10 +93,18 @@ YCollection.subclass('YArray', function(YArray){ return v; }; + function _has(v){ return this._o.indexOf(v) !== -1; } + + this['intersect'] = + function intersect(a){ + return Y(a).filter(_has, this); + }; + this['clear'] = function clear(){ var A = this._o; - while (A.length) A.pop(); + // while (A.length) A.pop(); + A.splice(0, A.length); return this; }; diff --git a/src/ezl/layer/html.cjs b/src/ezl/layer/html.cjs index ddfd032..8c2d0bf 100644 --- a/src/ezl/layer/html.cjs +++ b/src/ezl/layer/html.cjs @@ -6,13 +6,7 @@ var Y = require('Y').Y HtmlLayer = exports['HtmlLayer'] = Layer.subclass('HtmlLayer', { + _layerClasses : 'ezl layer', + hasCanvas : false - init : function initHtmlLayer(){ - Layer.init.call(this); - }, - - treatAsChild : function treatAsChild(){ - - }, - -}) +}); \ No newline at end of file diff --git a/src/ezl/layer/index.cjs b/src/ezl/layer/index.cjs index e69de29..55b4392 100644 --- a/src/ezl/layer/index.cjs +++ b/src/ezl/layer/index.cjs @@ -0,0 +1,14 @@ +var Y = require('Y').Y +, layer = require('ezl/layer/layer') +, html = require('ezl/layer/html') +; + +Y.core.extend(exports, { + 'layer' : layer, + 'Layer' : layer.Layer, + + 'html' : html, + 'HtmlLayer' : html.HtmlLayer, + + 'text' : require('ezl/layer/text') +}); diff --git a/src/ezl/layer/layer.cjs b/src/ezl/layer/layer.cjs index 48b71b6..32bd20a 100644 --- a/src/ezl/layer/layer.cjs +++ b/src/ezl/layer/layer.cjs @@ -17,7 +17,26 @@ FAUX_ACCESSORS = Y('width height position stroke fill origin rotate scale transl Layer = exports['Layer'] = Y.subclass('Layer', { - _cssClasses : 'ezl layer', + /// Class Defaults /// + + // _layer* is applied to this.layer (the DOM Element) + _layerHtml : '
', // HTML that will become the layer + _layerId : null, // HTML id attribute + _layerAttrs : null, // HTML attributes + _layerClasses : 'ezl layer',// CSS classes + + originX : 0, + originY : 0, + + hasCanvas : true, // Whether to create a canvas + useCanvasScaling : false, // Default to CSS3 scaling + alwaysClearDrawing : true, // Whether to clear the canvas content before redraw + alwaysClearAttrs : false, // Whether to remove all canvas attributes (CONTEXT_ATTRS) + // and transforms (scale, rotate, translate) and reset defaults before redraw + + + + /// State /// canvas : null, parent : null, @@ -42,24 +61,17 @@ Y.subclass('Layer', { _origin : null, // rotational origin transform : null, // Object - /// Defaults /// - - hasCanvas : true, // Whether to create a canvas - useCanvasScaling : false, // Default to CSS3 scaling - alwaysClearDrawing : true, // Whether to clear the canvas content before redraw - alwaysClearAttrs : false, // Whether to remove all canvas attributes (CONTEXT_ATTRS) - // and transforms (scale, rotate, translate) and reset defaults before redraw - - originX : 0, - originY : 0, - - /// Setup /// init : function init(props, attrs, html){ - if (props) - Y.core.extend(this, props); + if (props !== undefined && props !== null) { + switch (typeof props) { + case 'boolean' : this.hasCanvas = props; break; + case 'string' : this._layerHtml = props; break; + case 'object' : Y.core.extend(this, props); break; + } + } this.children = new Y.YArray(); this.animActive = new Y.YArray(); @@ -79,11 +91,14 @@ Y.subclass('Layer', { translate : new Loc(0,0) // translates canvas }; - this.layer = jQuery(html || '
') - .addClass(this._cssClasses) + this.layer = jQuery(html || this._layerHtml) + .addClass(this._layerClasses) .data('layer', this); this.layer[0].layer = this; - if (attrs) this.layer.attr(attrs); + + if (this._layerAttrs) this.layer.attr(this._layerAttrs); + if (this._layerId) this.layer.attr('id', this._layerId); + if (attrs) this.layer.attr(attrs); if (this.hasCanvas) { this.canvas = jQuery('') diff --git a/src/ezl/shape/circle.cjs b/src/ezl/shape/circle.cjs index 909ee8d..aabdcaf 100644 --- a/src/ezl/shape/circle.cjs +++ b/src/ezl/shape/circle.cjs @@ -4,7 +4,7 @@ var Shape = require('ezl/shape/shape').Shape Circle = exports['Circle'] = Shape.subclass('Circle', { - _cssClasses : 'ezl layer shape circle', + _layerClasses : 'ezl layer shape circle', originX : '50%', originY : '50%', diff --git a/src/ezl/shape/line.cjs b/src/ezl/shape/line.cjs index 3d592ea..9a05999 100644 --- a/src/ezl/shape/line.cjs +++ b/src/ezl/shape/line.cjs @@ -9,7 +9,7 @@ var Y = require('Y').Y Line = exports['Line'] = Shape.subclass('Line', { - _cssClasses : 'ezl layer shape line', + _layerClasses : 'ezl layer shape line', useCanvasScaling : true, fillStyle : 'transparent', diff --git a/src/ezl/shape/polygon.cjs b/src/ezl/shape/polygon.cjs index 8601c16..67250bc 100644 --- a/src/ezl/shape/polygon.cjs +++ b/src/ezl/shape/polygon.cjs @@ -8,7 +8,7 @@ var Y = require('Y').Y Polygon = exports['Polygon'] = Shape.subclass('Polygon', { - _cssClasses : 'ezl layer shape polygon', + _layerClasses : 'ezl layer shape polygon', /** * Expects two arrays of coordinate-halfs, which could be zipped @@ -56,7 +56,7 @@ Shape.subclass('Polygon', { Triangle = exports['Triangle'] = Polygon.subclass('Triangle', { - _cssClasses : 'ezl layer shape polygon triangle', + _layerClasses : 'ezl layer shape polygon triangle', init : function initTriangle(x1,y1, x2,y2){ Polygon.init.call(this, [x1,x2], [y1,y2]); @@ -82,7 +82,7 @@ Polygon.subclass('Triangle', { Quad = exports['Quad'] = Polygon.subclass('Quad', { - _cssClasses : 'ezl layer shape polygon quad', + _layerClasses : 'ezl layer shape polygon quad', init : function initQuad(x1,y1, x2,y2, x3,y3){ Polygon.init.call(this, [x1,x2,x3], [y1,y2,y3]); diff --git a/src/ezl/shape/rect.cjs b/src/ezl/shape/rect.cjs index d88a45c..2918a2b 100644 --- a/src/ezl/shape/rect.cjs +++ b/src/ezl/shape/rect.cjs @@ -4,7 +4,7 @@ var Shape = require('ezl/shape/shape').Shape Rect = exports['Rect'] = Shape.subclass('Rect', { - _cssClasses : 'ezl layer shape rect', + _layerClasses : 'ezl layer shape rect', originX : 0, originY : 0, diff --git a/src/ezl/shape/shape.cjs b/src/ezl/shape/shape.cjs index e7ffad7..d78907d 100644 --- a/src/ezl/shape/shape.cjs +++ b/src/ezl/shape/shape.cjs @@ -4,7 +4,7 @@ var Layer = require('ezl/layer/layer').Layer Shape = exports['Shape'] = Layer.subclass('Shape', { - _cssClasses : 'ezl layer shape', + _layerClasses : 'ezl layer shape', fillStyle : 'rgba(231,48,117, 1)', strokeStyle : 'transparent', diff --git a/src/ezl/widget/cooldown.cjs b/src/ezl/widget/cooldown.cjs index 61e1c0b..0a84612 100644 --- a/src/ezl/widget/cooldown.cjs +++ b/src/ezl/widget/cooldown.cjs @@ -14,7 +14,7 @@ CooldownGauge = exports['CooldownGauge'] = Layer.subclass('CooldownGauge', function setupCooldownGauge(CooldownGauge){ Y.extend(this, { - _cssClasses : 'ezl layer cooldown', + _layerClasses : 'ezl layer cooldown', fillStyle : '#000000', strokeStyle : 'transparent', lineWidth : 0, diff --git a/src/tanks/fx/explosion.cjs b/src/tanks/fx/explosion.cjs index d8069ea..9ee36d2 100644 --- a/src/tanks/fx/explosion.cjs +++ b/src/tanks/fx/explosion.cjs @@ -5,7 +5,7 @@ var Y = require('Y').Y Explosion = exports['Explosion'] = Circle.subclass('Explosion', { - _cssClasses : 'ezl layer circle explosion', + _layerClasses : 'ezl layer circle explosion', fillStyle : '#980011', ringSizes : [ 0.6, 0.3 ], diff --git a/src/tanks/item/container.cjs b/src/tanks/item/container.cjs new file mode 100644 index 0000000..2e6b6d5 --- /dev/null +++ b/src/tanks/item/container.cjs @@ -0,0 +1,269 @@ +var Y = require('Y').Y +, op = require('Y/op') +, deepcopy = require('Y/types/object').deepcopy + +, evt = require('evt') +, Item = require('tanks/thing/item').Item + +, kNull = op.K(null) +, + +Container = +exports['Container'] = +evt.subclass('Container', { + __bind__ : [], + + name : null, // Name of this container + max : 1, // Container capacity + reqs : null, // Array of tag requirements to be stored here + equips : false, // Whether held items are equipped + + size : 0, // Number of items in container + items : null, // item id -> Item + slots : null, // Array of positions of items in container: container idx -> Item + + + init : function initContainer(name, unit, items, options){ + this.name = name; + this.unit = unit; + Y.core.extend(this, options); + + this.items = {}; + this.slots = new Array(inv.max); + + if ( typeof this.reqs == 'string' ) + this.reqs = Y([ this.reqs ]); + + if (items) items.forEach(this.moveItem, this); + }, + + + /** + * @return {Boolean} Whether this container will accept this item. + */ + canAddItem : function canAddItem(item){ + return this.size < this.max && ( !item || !this.reqs || this.reqs.intersect(item.tags) ); + }, + + getItem : function getItem(idx){ + if (idx === undefined || idx === null) + return idx; + if (idx instanceof Item) + return idx; + else + return this.slots[idx]; + }, + + hasItem : function hasItem(id){ + if (id instanceof Item) id = id.__id__; + return !!this.items[id]; + }, + + + + /** + * @return {Integer} Index of first empty slot in this unit's backpack. + */ + getEmptySlot : function getEmptySlot(){ + var slots = this.slots + , max = this.max; + + if (this.size >= max) + return -1; + + for (var i=0, v=slots[i]; i= this.max ) + return false; + + if ( this.items[id] ) + delete slots[ui.bpIdx]; // Already in backpack? Remove from current slot + else + this.size++; // Otherwise increment size + + slots[idx] = item; + ui.bpIdx = idx; + this.items[id] = item; + + return true; + }, + + + /** + * Removes item from backpack, updating metadata. + * @param {Item} item + * @return {Boolean} + */ + _removeItem : function removeItem(item){ + if (!item) return false; + + // item = this.getItem(item); + var id = item.__id__ + , ui = item.ui + , idx = ui.bpIdx + , slot = ui.equipSlot + , hasItem = !!this.items[id] + ; + + delete this.items[id]; + + if (hasItem) { + // TODO: unequip slot + item.clean(); + delete this.slots[idx]; + this.size--; + return true; + } + + return false; + }, + + + _fireItemChange : function _fireItemChange(evt, item, data){ + data = Y.extend({ 'unit':this, 'item':item }, data || {}); + this.fire(evt, item, data); + item.fire(evt, this, data); + return this; + }, + + // TODO: update UI; UI to inform item on activation + /** + * @param {Item} item + * @return {this} + */ + addItem : function addItem(item){ + if ( this.hasItem(item) ) + return this; + + item = this.getItem(item); + var idx = this.getEmptySlot(); + + // TODO: UI feedback on failure + if ( !this._putItem(item, idx) ) + return this; + + return this._fireItemChange('item.acquire', item, { 'idx':idx }); + }, + + moveItem : function moveItem(item, idx){ + item = this.getItem(item); + if (!item || typeof idx != "number") + return this; + + // Make note of item we're displacing, if any + var displacedItem = this.slots[idx] + , oldIdx = item.ui.bpIdx + ; + + if ( !this._putItem(item, idx) ) + return this; + + idx = item.ui.bpIdx; + this._fireItemChange('item.move', item, { 'idx':idx, 'oldIdx':oldIdx }); + + if ( displacedItem && displacedItem !== item ) { + displacedItem.ui.bpIdx = null; + this._putItem(displacedItem, oldIdx); + this._fireItemChange('item.move', displacedItem, { 'idx':displacedItem.ui.bpIdx, 'oldIdx':idx }); + } + + return true; + }, + + removeItem : function removeItem(item){ + item = this.getItem(item); + var idx = item && item.backpack; + + // TODO: UI feedback on failure + if ( this._removeItem(item) ) + return this; + + this._fireItemChange('item.lose', item, { 'idx':idx }); + return item; + }, + + dropItem : function dropItem(idx){ + item = this.getItem(item); + var idx = item ? item.ui.bpIdx : -1; + + // TODO: UI feedback on failure + if ( this._removeItem(item) ) + return this; + + this._fireItemChange('item.lose.drop', item, { 'idx':idx }); + return item; + }, + + equipItem : function equipItem(slot, item){ + item = this.getItem(item); + + var inv = this.inventory + , eqs = inv.equipment; + + if ( !(slot in eqs) ) + throw new Error('Unit '+this+' does not have an equipment slot '+slot+'!'); + + if ( !(item instanceof Item) ) + throw new Error('Unit '+this+' cannot equip item '+item+'!'); + + // TODO: Ensure item has right tags for slot + var oldIdx = item.backpack; + this._removeItem(item); + + // TODO: UI feedback + if ( eqs[slot] ) + this.unequipSlot(slot, oldIdx); + + this.items[item.__id__] = item; + eqs[slot] = item; + item.slot = slot; + + return this._fireItemChange('item.equip', item, { 'slot':slot }); + }, + + unequipSlot : function unequipSlot(slot, idx){ + var inv = this.inventory + , eqs = inv.equipment + , item = eqs[slot] + ; + if (item) { + // TODO: UI feedback on failure + if ( !this._putItem(item, idx) ) + return this; + + eqs[slot] = null; + item.slot = null; + this._fireItemChange('item.unequip', item, { 'slot':slot, 'idx':idx }); + } + return this; + } + + + + +}); diff --git a/src/tanks/map/level.cjs b/src/tanks/map/level.cjs index 1093e53..ad898b1 100644 --- a/src/tanks/map/level.cjs +++ b/src/tanks/map/level.cjs @@ -22,14 +22,14 @@ Level = exports['Level'] = new evt.Class('Level', { __mixins__ : [ Speciated ], - _cssClasses : 'ezl layer level', + _layerClasses : 'ezl layer level', init : function init(game, capacity, buffer_size){ this.game = game; this.map = new Map(0,0, this.width, this.height, capacity, buffer_size); var shape = this.shape = new Rect(this.width,this.height).fill('transparent'); - shape.layer.attr('class', this._cssClasses); + shape.layer.attr('class', this._layerClasses); this.bbox = shape.bbox; }, diff --git a/src/tanks/mixins/inventoried.cjs b/src/tanks/mixins/inventoried.cjs index f4e1c3e..2831a52 100644 --- a/src/tanks/mixins/inventoried.cjs +++ b/src/tanks/mixins/inventoried.cjs @@ -13,15 +13,16 @@ exports['Inventoried'] = Mixin.subclass('Inventoried', { hasInventory : true, inventory : { - // Map of equipment slots to requirement tags - slots : { - 'weapon' : 'weapon', - 'armor' : 'armor', - 'item1' : 'consumable', - 'item2' : 'consumable' + + + slots : { // Map of equipment slots to requirement tags + 'weapon' : { max:1, reqs:'weapon' }, + 'armor' : { max:2, reqs:'armor' }, + 'backpack' : { max:12, reqs:null, defaultContainer:true } }, + max : 12, // Backpack capacity - size : 0, // Number of items + size : 0, // Number of items in backpack items : null, // item id -> Item backpack : null, // Array of positions of items in backpack: backpack idx -> Item equipment : null // slotName -> Item diff --git a/src/tanks/ui/grid.cjs b/src/tanks/ui/grid.cjs index e2f4822..e1f0cc6 100644 --- a/src/tanks/ui/grid.cjs +++ b/src/tanks/ui/grid.cjs @@ -11,7 +11,7 @@ Rect.subclass('Grid', { createGridCanvas : null, // Shape Config - _cssClasses : 'grid rect shape layer ezl', + _layerClasses : 'grid rect shape layer ezl', strokeStyle : '#6E6E6E', lineWidth : 0.5, diff --git a/src/tanks/ui/inventory/backpack.cjs b/src/tanks/ui/inventory/backpack.cjs index 704da1a..946678b 100644 --- a/src/tanks/ui/inventory/backpack.cjs +++ b/src/tanks/ui/inventory/backpack.cjs @@ -8,7 +8,9 @@ var Y = require('Y').Y Backpack = exports['Backpack'] = Layer.subclass('Backpack', { - _cssClasses : 'backpack hud', + _layerClasses : 'backpack item-container hud', + _layerAttrs : { 'id':'backpack' }, + _layerHtml : '

backpack

', hasCanvas : false, @@ -17,8 +19,6 @@ Layer.subclass('Backpack', { Layer.init.call(this); this.unit = unit; - this.layer.attr('id', 'backpack'); - this.layer.append('

backpack

'); var inv = this.inventory = unit.inventory , bp = inv.backpack ; @@ -27,7 +27,6 @@ Layer.subclass('Backpack', { return new BackpackSlot(this, idx).appendTo(this); }, this); - unit.addEventListener('item.acquire', this.onItemUpdated); unit.addEventListener('item.lose', this.onItemUpdated); unit.addEventListener('item.move', this.onItemUpdated); @@ -59,9 +58,11 @@ Layer.subclass('Backpack', { var item = ui.draggable.data('item') , toIdx = $(evt.target).data('idx') ; - console.log(this+'.onDrop(item='+item+', toIdx='+toIdx+')', evt); + // console.log(this+'.onDrop(item='+item+', toIdx='+toIdx+')', evt); this.unit.moveItem(item, toIdx); - setTimeout(function(){ item.dragging = false; }, 25); + + // Clear flag preventing the drag from activing the item + setTimeout(function(){ item.dragging = false; }, 250); }, @@ -76,8 +77,9 @@ Layer.subclass('Backpack', { BackpackSlot = exports['BackpackSlot'] = Layer.subclass('BackpackSlot', { - _cssClasses : 'backpack hud slot', + _layerClasses : 'backpack hud slot', hasCanvas : false, + artWidth : 50, artHeight : 50, diff --git a/src/tanks/ui/inventory/container.cjs b/src/tanks/ui/inventory/container.cjs new file mode 100644 index 0000000..faf1ce2 --- /dev/null +++ b/src/tanks/ui/inventory/container.cjs @@ -0,0 +1,206 @@ +//#ensure "jquery.tipsy" +//#ensure "jquery-ui" +var Y = require('Y').Y +, Layer = require('ezl/layer/layer').Layer +, HtmlLayer = require('ezl/layer/html').HtmlLayer +, + + +ItemContainerUI = +exports['ItemContainerUI'] = +HtmlLayer.subclass('ItemContainerUI', { + _layerClasses : 'item-container hud', + + name : null, + title : null, + showTitle : true, + defaultContainer : false, + + max : 1, + reqs : null, + unit : null, + + + + init : function initItemContainerUI(name, unit, items, options){ + Y.bindAll(this, 'onItemUpdated', 'onDrop'); + if (options) Y.core.extend(this, options); + + this.name = name; + this.unit = unit; + this.items = items; + + this._layerId = this.name+'_slot'; + HtmlLayer.init.call(this); + + if (this.showTitle) + this.layer.append( '

'+(this.title || this.name.toLowerCase())+'

' ); + + this.slots = new Y(0, this.max).map(this._makeSlot, this); + + if (this.defaultContainer) { + unit.addEventListener('item.acquire', this.onItemUpdated); + unit.addEventListener('item.lose', this.onItemUpdated); + unit.addEventListener('item.move', this.onItemUpdated); + } + }, + + _makeSlot : function _makeSlot(idx){ + return new ItemContainerUISlot(this, idx).appendTo(this); + }, + + refresh : function refresh(){ + this.slots.invoke('refresh'); + this.layer.find('.slot') + .droppable({ + accept : '.item', + hoverClass : 'drophover', + drop : this.onDrop + }); + + return this; + }, + + getSlot : function getSlot(idx){ + return this.slots[idx]; + }, + + onItemUpdated : function onItemUpdated(evt){ + var d = evt.data; + this.slots[d.idx].refresh(); + if ('oldIdx' in d) this.slots[d.oldIdx].refresh(); + }, + + onDrop : function onDrop(evt, ui){ + var item = ui.draggable.data('item') + , toIdx = $(evt.target).data('idx') + ; + // console.log(this+'.onDrop(item='+item+', toIdx='+toIdx+')', evt); + this.unit.moveItem(item, toIdx); + + // Clear flag preventing the drag from activing the item + setTimeout(function(){ item.dragging = false; }, 250); + }, + + + toString : function(){ + return this.className+'(unit='+this.unit+')'; + } + +}) +, + + +ItemContainerUISlot = +exports['ItemContainerUISlot'] = +Layer.subclass('ItemContainerUISlot', { + _layerClasses : 'item-container slot hud ', + hasCanvas : false, + + artWidth : 50, + artHeight : 50, + + idx : -1, + dragging : false, + backpack : null, + item : null, + + + init : function initItemContainerUISlot(backpack, idx){ + Y.bindAll(this, 'onActivate', 'onDragStart'); + Layer.init.call(this); + this.backpack = backpack; + this.inventory = backpack.inventory; + this.idx = idx; + this.layer.addClass('slot'+idx); + this.inner = + new Layer({ hasCanvas:false }) + .appendTo(this) + .position(8, 8); + }, + + refresh : function refresh(){ + this.layer.data({ + 'backpack-slot': this, + 'idx': this.idx + }); + var item = this.inventory.backpack[this.idx]; + if (item) + this.setItem(item); + else + this.removeItem(); + }, + + setItem : function setItem(item){ + if (!item) return this; + + this.removeItem(); + this.item = item; + this.layer.addClass('occupied'); + + var icon = item.art && item.art.inv_icon + , src = this.itemEl = + jQuery('') + .width(this.artWidth) + .height(this.artHeight) + .appendTo(this.inner.layer) + ; + this.inner.layer + .addClass('item') + .data('item', item) + .draggable({ + start : this.onDragStart, + revert : 'invalid', + zIndex : 10 + }) + .attr('title', ''+item.name+'
'+item.desc) + .tipsy({ 'html':true, 'gravity':$.fn.tipsy.autoNS }) + ; + + if (icon) src.attr('src', icon); + + this.inner.append(item.activateGauge); + this.inner.layer.bind('click', this.onActivate); + + return this; + }, + + removeItem : function removeItem(){ + if (this.item) { + this.layer + .removeClass('occupied'); + + // Dismiss tooltip if present + this.inner.layer + .trigger('mouseleave'); + + this.inner.remove(); + this.inner = + new Layer({ hasCanvas:false }) + .appendTo(this) + .position(8, 8); + } + + this.item = null; + this.itemEl = null; + return this; + }, + + onDragStart : function onDragStart(evt, ui){ + var item = this.item; + // console.log(this+'.dragStart(item='+item+', dragging='+(item || {}).dragging+')', evt, ui); + if (item) item.dragging = true; + }, + + onActivate : function onActivate(evt){ + var item = this.item; + // console.log(this+'.onActivate(item='+item+', dragging='+item.dragging+')'); + if (item && !item.dragging) item.activate(); + }, + + toString : function(){ + return this.className+'(idx='+this.idx+', item='+this.item+')'; + } + +}) +; diff --git a/src/tanks/ui/inventory/equipslot.cjs b/src/tanks/ui/inventory/equipslot.cjs index 7813a6b..50e05ab 100644 --- a/src/tanks/ui/inventory/equipslot.cjs +++ b/src/tanks/ui/inventory/equipslot.cjs @@ -1,14 +1,193 @@ +//#ensure "jquery.tipsy" +//#ensure "jquery-ui" var Y = require('Y').Y +, Layer = require('ezl/layer/layer').Layer +, HtmlLayer = require('ezl/layer/html').HtmlLayer , + EquipSlot = exports['EquipSlot'] = -Y.subclass('EquipSlot', { +HtmlLayer.subclass('EquipSlot', { + _layerClasses : 'equip item-container hud', + + + init : function initEquipSlot(slotName, reqs, unit){ + Y.bindAll(this, 'onItemUpdated', 'onDrop'); + + this.slotName = slotName; + this.reqs = reqs; + this.unit = unit; + + HtmlLayer.init.call(this, null, + { 'id':slotName+'_slot' }, // layer attributes + '

'+slotName.toLowerCase()+'

'); // layer html + + var inv = this.inventory = unit.inventory + , bp = inv.backpack + ; + this.slots = new Y(0, inv.max).map(function(idx){ + var item = bp[idx]; + return new BackpackSlot(this, idx).appendTo(this); + }, this); + + unit.addEventListener('item.acquire', this.onItemUpdated); + unit.addEventListener('item.lose', this.onItemUpdated); + unit.addEventListener('item.move', this.onItemUpdated); + }, + + refresh : function refresh(){ + this.slots.invoke('refresh'); + this.layer.find('.slot') + .droppable({ + accept : '.item', + hoverClass : 'drophover', + drop : this.onDrop + }); + + return this; + }, + + getSlot : function getSlot(idx){ + return this.slots[idx]; + }, + + onItemUpdated : function onItemUpdated(evt){ + var d = evt.data; + this.slots[d.idx].refresh(); + if ('oldIdx' in d) this.slots[d.oldIdx].refresh(); + }, + + onDrop : function onDrop(evt, ui){ + var item = ui.draggable.data('item') + , toIdx = $(evt.target).data('idx') + ; + // console.log(this+'.onDrop(item='+item+', toIdx='+toIdx+')', evt); + this.unit.moveItem(item, toIdx); + + // Clear flag preventing the drag from activing the item + setTimeout(function(){ item.dragging = false; }, 250); + }, + + + toString : function(){ + return this.className+'(unit='+this.unit+')'; + } + +}) +, + + +BackpackSlot = +exports['BackpackSlot'] = +HtmlLayer.subclass('BackpackSlot', { + _layerClasses : 'backpack hud slot', + hasCanvas : false, + + artWidth : 50, + artHeight : 50, + + idx : -1, + dragging : false, + backpack : null, + item : null, + + + init : function initBackpackSlot(backpack, idx){ + Y.bindAll(this, 'onActivate', 'onDragStart'); + Layer.init.call(this); + this.backpack = backpack; + this.inventory = backpack.inventory; + this.idx = idx; + this.layer.addClass('slot'+idx); + this.inner = + new Layer({ hasCanvas:false }) + .appendTo(this) + .position(8, 8); + }, - init : function initEquipSlot(){ - this.layer = $('
') + refresh : function refresh(){ + this.layer.data({ + 'backpack-slot': this, + 'idx': this.idx + }); + var item = this.inventory.backpack[this.idx]; + if (item) + this.setItem(item); + else + this.removeItem(); }, + setItem : function setItem(item){ + if (!item) return this; + + this.removeItem(); + this.item = item; + this.layer.addClass('occupied'); + + var icon = item.art && item.art.inv_icon + , src = this.itemEl = + jQuery('') + .width(this.artWidth) + .height(this.artHeight) + .appendTo(this.inner.layer) + ; + this.inner.layer + .addClass('item') + .data('item', item) + .draggable({ + start : this.onDragStart, + revert : 'invalid', + zIndex : 10 + }) + .attr('title', ''+item.name+'
'+item.desc) + .tipsy({ 'html':true, 'gravity':$.fn.tipsy.autoNS }) + ; + + if (icon) src.attr('src', icon); + + this.inner.append(item.activateGauge); + this.inner.layer.bind('click', this.onActivate); + + return this; + }, + + removeItem : function removeItem(){ + if (this.item) { + this.layer + .removeClass('occupied'); + + // Dismiss tooltip if present + this.inner.layer + .trigger('mouseleave'); + + this.inner.remove(); + this.inner = + new Layer({ hasCanvas:false }) + .appendTo(this) + .position(8, 8); + } + + this.item = null; + this.itemEl = null; + return this; + }, + + onDragStart : function onDragStart(evt, ui){ + var item = this.item; + // console.log(this+'.dragStart(item='+item+', dragging='+(item || {}).dragging+')', evt, ui); + if (item) item.dragging = true; + }, + + onActivate : function onActivate(evt){ + var item = this.item; + // console.log(this+'.onActivate(item='+item+', dragging='+item.dragging+')'); + if (item && !item.dragging) item.activate(); + }, + toString : function(){ + return this.className+'(idx='+this.idx+', item='+this.item+')'; + } -}); +}) +; diff --git a/src/tanks/ui/inventory/inventory.cjs b/src/tanks/ui/inventory/inventory.cjs deleted file mode 100644 index f8e855f..0000000 --- a/src/tanks/ui/inventory/inventory.cjs +++ /dev/null @@ -1,17 +0,0 @@ -var Y = require('Y').Y -, - -Inventory = -exports['Inventory'] = -Y.subclass('Inventory', { - unit : null, - inv : null, - layer : null, - - - init : function initInventory(unit){ - this.unit = unit; - this.inv = unit.inventory; - } - -}); diff --git a/src/tanks/ui/pathmapui.cjs b/src/tanks/ui/pathmapui.cjs index aca2005..435c5d7 100644 --- a/src/tanks/ui/pathmapui.cjs +++ b/src/tanks/ui/pathmapui.cjs @@ -20,7 +20,7 @@ Rect.subclass('PathMapUI', { overlayAiPaths : null, // Shape Config - _cssClasses : 'map rect shape layer ezl', + _layerClasses : 'map rect shape layer ezl', // Blocking objects diff --git a/www/css/lttl.css b/www/css/lttl.css index 967999a..689c605 100644 --- a/www/css/lttl.css +++ b/www/css/lttl.css @@ -48,11 +48,12 @@ td { text-align:center; vertical-align:middle; } #game { position:absolute; width:100%; height:100%; margin:0; padding:0; } #viewport { position:relative; margin:1em auto; cursor:crosshair; width:auto; /* width:500px; height:500px; overflow:hidden; top:50px; left:50px; */ } #viewport .layer.grid { outline:1px solid rgba(255,255,255,0.1); } -#backpack { position:absolute; bottom:1em; right:1em; width:304px; height:254px; /* top:auto; left:auto; */ } - #backpack h3 { color:#fff; margin:0.25em; padding:0.5em 0; } - #backpack .slot { float:left; position:relative; width:50px; height:50px; margin:0.25em; border:1px solid transparent; - /* padding:0; top:auto; left:auto; */ } - #backpack .slot.drophover { border:1px solid #FFF6AE; } +#backpack { position:absolute; bottom:1em; right:1em; width:304px; height:254px; } + +.item-container {} + .item-container h3 { color:#fff; margin:0.25em; padding:0.5em 0; } + .item-container .slot { float:left; position:relative; width:50px; height:50px; margin:0.25em; border:1px solid transparent; } + .item-container .slot.drophover { border:1px solid #FFF6AE; } diff --git a/www/header.html b/www/header.html index b2b0b95..5b1a109 100644 --- a/www/header.html +++ b/www/header.html @@ -1,11 +1,17 @@ -The Littlest Battletank + -- 1.7.0.4