-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
205 additions
and
159 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import { html, render } from 'lit-html'; | ||
|
||
class TeamLayout { | ||
constructor(parentElement){ | ||
this.parentElement = parentElement; | ||
} | ||
|
||
createTeamLayouts() { | ||
const teamLayouts = html` | ||
<a-entity id="team-1" position="-9.5 0 0" rotation="0 90 0" arc-layout="radius: 10; startAngle: -40; endAngle: 40; itemSelector: .arc-item"></a-entity> | ||
<a-entity id="team-2" position="9.5 0 0" rotation="0 90 0" arc-layout="radius: 10; startAngle: -40; endAngle: 40; itemSelector: .arc-item"></a-entity> | ||
`; | ||
render(teamLayouts, this.parentElement); | ||
} | ||
|
||
|
||
renderPlayers(players) { | ||
const playerTemplate = (player) => html` | ||
<a-entity class="arc-item" look-towards="#camera" | ||
animation-mixer="${player.properties['animation-mixer'] || 'clip: Idle; loop:repeat'}" | ||
id="${player.nodeId}" | ||
gltf-model="#player-model" | ||
texture-map="src: assets/players/skins/${player.properties.skin || 'alienA'}.png" | ||
scale="${player.properties.scale || '1 1 1'}"> | ||
<a-text | ||
color="black" | ||
opacity="0.8" | ||
value="${player.properties.nickname || '...'}" | ||
width="1" align="center" position="0 0 2" label="overwrite:true"></a-text> | ||
</a-entity> | ||
`; | ||
|
||
const team1Players = Array.from(players.values()) | ||
.filter(player => player.properties.active === 'true' && player.properties['team-id'] === 'team-1'); | ||
|
||
const team2Players = Array.from(players.values()) | ||
.filter(player => player.properties.active === 'true' && player.properties['team-id'] === 'team-2'); | ||
|
||
const team1Template = html` | ||
${team1Players.map(player => playerTemplate(player))} | ||
`; | ||
|
||
const team2Template = html` | ||
${team2Players.map(player => playerTemplate(player))} | ||
`; | ||
|
||
const team1Element = this.parentElement.querySelector('#team-1'); | ||
const team2Element = this.parentElement.querySelector('#team-2'); | ||
|
||
if (team1Element) { | ||
render(team1Template, team1Element); | ||
team1Element.components['arc-layout'].update(); | ||
} | ||
|
||
if (team2Element) { | ||
render(team2Template, team2Element); | ||
team2Element.components['arc-layout'].update(); | ||
} | ||
} | ||
} | ||
|
||
export default TeamLayout; |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
import { createMqttHomieObserver, HomiePropertyBuffer, logger as homieLogger } from '@cmcrobotics/homie-lit'; | ||
import PropertyUpdate from '../homie-lit-components/PropertyUpdate'; | ||
import TeamLayout from '../homie-lit-components/TeamLayout'; | ||
import * as nools from 'nools'; | ||
import log from 'loglevel'; | ||
|
||
class PlayerNode { | ||
constructor(deviceId, nodeId, properties) { | ||
this.deviceId = deviceId; | ||
this.nodeId = nodeId; | ||
this.properties = properties; | ||
} | ||
} | ||
|
||
class QuizzSceneController { | ||
constructor(brokerUrl, parentElementId, mqttOptions = {}) { | ||
this.setupLogging(); | ||
this.homieObserver = createMqttHomieObserver(brokerUrl, mqttOptions); | ||
// this.homieObserver.subscribe("gateway/#"); | ||
// this.homieObserver.subscribe("terminal-+/#"); | ||
this.homieObserver.subscribe("#"); | ||
this.parentElement = document.getElementById(parentElementId); | ||
this.propertyBuffer = new HomiePropertyBuffer(this.homieObserver, 300); | ||
this.players = new Map(); | ||
this.terminalToPlayerMap = new Map(); | ||
this.teamLayout = new TeamLayout(this.parentElement); | ||
|
||
this.flow = this.initNoolsFlow(); | ||
this.session = this.flow.getSession(); | ||
|
||
this.setupObservers(); | ||
this.teamLayout.createTeamLayouts(); | ||
|
||
} | ||
|
||
setupLogging() { | ||
log.setLevel("debug"); | ||
homieLogger.setLevel("info"); | ||
} | ||
|
||
initNoolsFlow() { | ||
const flow = nools.compile(` | ||
rule ProcessPlayerUpdate { | ||
when { | ||
update: PropertyUpdate update.deviceId == 'gateway' && update.nodeId.startsWith('player-') | ||
} | ||
then { | ||
handlePropertyUpdate(update); | ||
} | ||
} | ||
`,{ | ||
define:{ | ||
PropertyUpdate : PropertyUpdate, | ||
PlayerNode: PlayerNode | ||
}, | ||
scope: { | ||
handlePropertyUpdate: this.handlePropertyUpdate.bind(this), | ||
logger: console | ||
}, | ||
name: "quizz" | ||
}); | ||
|
||
return flow; | ||
} | ||
|
||
setupObservers() { | ||
this.propertyBuffer.getBufferedUpdates().subscribe(updates => { | ||
const filteredUpdates = updates.filter(update => !this.isMetaProperty(update.propertyId)); | ||
|
||
filteredUpdates.forEach(update => { | ||
this.session.assert(new PropertyUpdate(update.deviceId, update.nodeId, update.propertyId, update.value)); | ||
}); | ||
|
||
if (filteredUpdates.length > 0) { | ||
this.session.match(); | ||
} | ||
}); | ||
} | ||
|
||
isMetaProperty(propertyId) { | ||
return propertyId.startsWith('$'); | ||
} | ||
|
||
handlePropertyUpdate(update) { | ||
if (this.isMetaProperty(update.propertyId)) { | ||
return; // Ignore meta properties | ||
} | ||
|
||
let player = this.players.get(update.nodeId); | ||
|
||
if (!player) { | ||
player = new PlayerNode(update.deviceId, update.nodeId, {}); | ||
this.players.set(update.nodeId, player); | ||
} | ||
|
||
player.properties[update.propertyId] = update.value; | ||
|
||
log.debug(`Updating player property: ${update.nodeId}/${update.propertyId} = ${update.value}`); | ||
|
||
if (update.propertyId === 'terminal-id') { | ||
this.updateTerminalToPlayerMap(player, update.value); | ||
} | ||
|
||
if (update.propertyId === 'active' || update.propertyId === 'team-id' || !player.properties['active']) { | ||
this.teamLayout.renderPlayers(this.players); | ||
} else if (update.propertyId === 'animation-mixer') { | ||
this.updatePlayerAnimation(player); | ||
} | ||
} | ||
|
||
|
||
|
||
updatePlayerAnimation(player) { | ||
const playerEntity = this.parentElement.querySelector(`#${player.nodeId}`); | ||
if (playerEntity) { | ||
playerEntity.setAttribute('animation-mixer', player.properties['animation-mixer']); | ||
log.debug(`Updated animation for player ${player.nodeId}: ${player.properties['animation-mixer']}`); | ||
} | ||
} | ||
|
||
updateTerminalToPlayerMap(player, terminalId) { | ||
// Remove old mapping if exists | ||
for (let [key, value] of this.terminalToPlayerMap) { | ||
if (value === player.nodeId) { | ||
this.terminalToPlayerMap.delete(key); | ||
break; | ||
} | ||
} | ||
// Add new mapping | ||
this.terminalToPlayerMap.set(terminalId, player.nodeId); | ||
log.debug(`Updated terminal-to-player mapping: Terminal ${terminalId} -> Player ${player.nodeId}`); | ||
} | ||
|
||
} | ||
|
||
export default QuizzSceneController; |
Oops, something went wrong.