Skip to content

Commit

Permalink
Merge pull request #20 from bananu7/milestone-v0.4
Browse files Browse the repository at this point in the history
Milestone v0.4
  • Loading branch information
bananu7 committed Oct 8, 2022
2 parents 477326d + 8dd87aa commit 50bb1a1
Show file tree
Hide file tree
Showing 18 changed files with 499 additions and 141 deletions.
13 changes: 13 additions & 0 deletions packages/client/debug.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>RTS - Debug view</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/debug/debug.tsx"></script>
</body>
</html>
3 changes: 3 additions & 0 deletions packages/client/src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@

border-radius: 10px 0 0 0;
box-shadow: 0px 0px 37px 0px rgba(0,0,0,0.5);

overflow: hidden;
}

.CommandPaletteHint {
Expand All @@ -81,6 +83,7 @@
backdrop-filter: blur(10px);
background-color: rgba(150, 150, 150, 0.3);
color: white;
overflow: hidden;

box-shadow: 0px 0px 37px 0px rgba(0,0,0,0.5);
}
Expand Down
102 changes: 70 additions & 32 deletions packages/client/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { useState, useEffect, useCallback } from 'react'
import './App.css'

import { MatchList } from './MatchList';
import { Minimap } from './Minimap';
import { MatchList } from './components/MatchList';
import { Minimap } from './components/Minimap';
import { CommandPalette, SelectedAction } from './components/CommandPalette';
import { BottomUnitView } from './components/BottomUnitView';
import { ResourceView } from './components/ResourceView';
Expand Down Expand Up @@ -41,6 +41,12 @@ function App() {
.then(s => setServerState(s));
}, []);

const leaveMatch = async () => {
await multiplayer.leaveMatch();
setLastUpdatePacket(null);
setServerState(null);
};

useEffect(() => {
multiplayer.setup({
onUpdatePacket: (p:UpdatePacket) => {
Expand Down Expand Up @@ -68,7 +74,7 @@ function App() {

const [selectedUnits, setSelectedUnits] = useState(new Set<UnitId>());

const mapClick = useCallback((p: Position, button: number) => {
const mapClick = useCallback((p: Position, button: number, shift: boolean) => {
if (selectedUnits.size === 0)
return;

Expand All @@ -78,15 +84,17 @@ function App() {
if (!selectedAction) {
break;
} else if (selectedAction.action === 'Move') {
multiplayer.moveCommand(Array.from(selectedUnits), p);
multiplayer.moveCommand(Array.from(selectedUnits), p, shift);
} else if (selectedAction.action === 'Attack') {
multiplayer.attackMoveCommand(Array.from(selectedUnits), p);
multiplayer.attackMoveCommand(Array.from(selectedUnits), p, shift);
} else if (selectedAction.action === 'Build') {
multiplayer.buildCommand(Array.from(selectedUnits), selectedAction.building, p);
// Only send one harvester to build
// TODO send the closest one
multiplayer.buildCommand([selectedUnits.keys().next().value], selectedAction.building, p, shift);
}
break;
case 2:
multiplayer.moveCommand(Array.from(selectedUnits), p);
multiplayer.moveCommand(Array.from(selectedUnits), p, shift);
break;
}

Expand All @@ -95,7 +103,7 @@ function App() {
}, [selectedAction, selectedUnits]);

// TODO it feels like it shouldn't be be here, maybe GameController component?
const unitClick = useCallback((targetId: UnitId, button: number) => {
const unitClick = useCallback((targetId: UnitId, button: number, shift: boolean) => {
if (!lastUpdatePacket)
return;

Expand All @@ -112,7 +120,21 @@ function App() {
switch (button) {
case 0:
if (!selectedAction) {
setSelectedUnits(new Set([targetId]));
if (shift) {
// shift-click means add if not there, but remove if there
setSelectedUnits(prev => {
const units = new Set(prev);
if (units.has(targetId)) {
units.delete(targetId);
}
else {
units.add(targetId);
}
return units;
});
} else {
setSelectedUnits(new Set([targetId]));
}
break;
}

Expand All @@ -121,30 +143,34 @@ function App() {
}

if (selectedAction.action === 'Move') {
multiplayer.followCommand(Array.from(selectedUnits), targetId);
multiplayer.followCommand(Array.from(selectedUnits), targetId, shift);
} else if (selectedAction.action === 'Attack') {
multiplayer.attackCommand(Array.from(selectedUnits), targetId);
multiplayer.attackCommand(Array.from(selectedUnits), targetId, shift);
}
break;
case 2:
// TODO properly understand alliances
if (target.owner === 0) { // neutral
// TODO actually check if can harvest and is resource
multiplayer.harvestCommand(Array.from(selectedUnits), targetId);
multiplayer.harvestCommand(Array.from(selectedUnits), targetId, shift);
}
else if (target.owner === multiplayer.getPlayerIndex()) {
multiplayer.followCommand(Array.from(selectedUnits), targetId);
multiplayer.followCommand(Array.from(selectedUnits), targetId, shift);
}
else if (target.owner !== multiplayer.getPlayerIndex()) {
multiplayer.attackCommand(Array.from(selectedUnits), targetId);
multiplayer.attackCommand(Array.from(selectedUnits), targetId, shift);
}
break;
}
}, [lastUpdatePacket, selectedAction, selectedUnits]);

const boardSelectUnits = (units: Set<UnitId>) => {
const boardSelectUnits = (newUnits: Set<UnitId>, shift: boolean) => {
setSelectedAction(undefined);
setSelectedUnits(units);
if (shift) {
setSelectedUnits(units => new Set([...units, ...newUnits]));
} else {
setSelectedUnits(newUnits);
}
};

// TODO track key down state for stuff like a-move clicks
Expand All @@ -164,13 +190,21 @@ function App() {
}
}, [selectedAction, selectedUnits]);

const style = selectedAction ? { cursor: "pointer"} : { };
const appDivStyle = selectedAction ? { cursor: "pointer"} : { };

const showGame =
serverState &&
lastUpdatePacket &&
( lastUpdatePacket.state.id === 'Precount'||
lastUpdatePacket.state.id === 'Play' ||
lastUpdatePacket.state.id === 'Paused'
);

return (
<div className="App" onKeyDown={keydown} tabIndex={0} style={style}>
<div className="App" onKeyDown={keydown} tabIndex={0} style={appDivStyle}>
{
<Chat
sendMessage={(msg) => multiplayer.sendChatMessage("lol")}
sendMessage={(msg) => multiplayer.sendChatMessage(msg)}
messages={messages}
/>
}
Expand All @@ -187,7 +221,10 @@ function App() {
back to your game.</p>
<p><strong>GLHF!</strong></p>
<br />
<MatchList joinMatch={(matchId) => multiplayer.joinMatch(matchId)} />
<MatchList
joinMatch={matchId => multiplayer.joinMatch(matchId)}
spectateMatch={matchId => multiplayer.spectateMatch(matchId)}
/>
<div style={{textAlign:"center"}}>
<button onClick={() => multiplayer.createMatch()}>Create</button>
</div>
Expand All @@ -213,24 +250,29 @@ function App() {
</div>
}

{ serverState &&
lastUpdatePacket &&
(lastUpdatePacket.state.id === 'Precount' || lastUpdatePacket.state.id === 'Play' || lastUpdatePacket.state.id === 'Paused')
&&
{
serverState &&
<>
<button className="MainMenuButton" onClick={() => setShowMainMenu((smm) => !smm) }>Menu</button>
<button className="MainMenuButton" onClick={() => setShowMainMenu((smm) => !smm) }>Menu</button>
{ showMainMenu &&
<div className="MainMenu">
<h3>Main menu</h3>
<h4>You are player #{multiplayer.getPlayerIndex()}</h4>
{ !serverState && <button>Play</button> }
{ serverState && <button onClick={() => { multiplayer.leaveMatch(); setServerState(null); }}>Leave game</button> }
{ serverState && <button onClick={async () => {
await leaveMatch();
setShowMainMenu(false);
}}>Leave game</button> }
{ serverState && <button onClick={() => { console.log(serverState) }}>Dump state</button> }
{ lastUpdatePacket && <button onClick={() => { console.log(lastUpdatePacket) }}>Dump update packet</button> }
{ serverState && <button onClick={() => { updateMatchState() }}>Update state</button> }
</div>
}

</>
}

{ showGame &&
<>
<CommandPalette
resources={lastUpdatePacket.player.resources}
selectedUnits={selectedUnits}
Expand Down Expand Up @@ -266,11 +308,7 @@ function App() {
lastUpdatePacket.state.id === "GameEnded" &&
<div className="card">
<h2>Game Over</h2>
<button onClick={async () => {
await multiplayer.leaveMatch();
setLastUpdatePacket(null);
setServerState(null);
}}>Return to main menu</button>
<button onClick={leaveMatch}>Return to main menu</button>
</div>
}
</div>
Expand Down
15 changes: 0 additions & 15 deletions packages/client/src/DebugApp.tsx

This file was deleted.

42 changes: 30 additions & 12 deletions packages/client/src/Multiplayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ export class Multiplayer {
this.onUpdatePacket && this.onUpdatePacket(u);
})

this.channel.on('spectating', (data: Data) => {
this.matchId = (data as {matchId: string}).matchId;
localStorage.setItem('matchId', this.matchId);
this.onMatchConnected && this.onMatchConnected(this.matchId);
});

this.channel.on('connected', (data: Data) => {
if (!this.matchId) {
throw "Server responded with connection but the multiplayer isn't initialized to a match";
Expand Down Expand Up @@ -139,6 +145,18 @@ export class Multiplayer {
this.channel.emit('connect', data);
});
};

spectateMatch(matchId: string) {
console.log(`[Multiplayer] spectating match ${matchId}`)
const data : IdentificationPacket = {
userId: this.userId,
matchId
};

this.channel.emit('spectate', data);
}

// TODO - no way to stop spectating

async leaveMatch() {
if (!this.matchId)
Expand Down Expand Up @@ -179,14 +197,14 @@ export class Multiplayer {
this.channel.emit('chat message', 'msg')
}

moveCommand(unitIds: UnitId[], target: Position) {
moveCommand(unitIds: UnitId[], target: Position, shift: boolean) {
const cmd : CommandPacket = {
action: {
typ: 'Move',
target
},
unitIds,
shift: false,
shift,
};
this.channel.emit('command', cmd)
}
Expand All @@ -202,38 +220,38 @@ export class Multiplayer {
this.channel.emit('command', cmd)
}

followCommand(unitIds: UnitId[], target: UnitId) {
followCommand(unitIds: UnitId[], target: UnitId, shift: boolean) {
const cmd : CommandPacket = {
action: {
typ: 'Follow',
target
},
unitIds,
shift: false,
shift,
};
this.channel.emit('command', cmd);
}

attackCommand(unitIds: UnitId[], target: UnitId) {
attackCommand(unitIds: UnitId[], target: UnitId, shift: boolean) {
const cmd : CommandPacket = {
action: {
typ: 'Attack',
target
},
unitIds,
shift: false,
shift,
};
this.channel.emit('command', cmd);
}

attackMoveCommand(unitIds: UnitId[], target: Position) {
attackMoveCommand(unitIds: UnitId[], target: Position, shift: boolean) {
const cmd : CommandPacket = {
action: {
typ: 'AttackMove',
target
},
unitIds,
shift: false,
shift,
};
this.channel.emit('command', cmd);
}
Expand All @@ -250,27 +268,27 @@ export class Multiplayer {
this.channel.emit('command', cmd);
}

buildCommand(unitIds: UnitId[], building: string, position: Position) {
buildCommand(unitIds: UnitId[], building: string, position: Position, shift: boolean) {
const cmd : CommandPacket = {
action: {
typ: 'Build',
building,
position
},
unitIds,
shift: false,
shift,
};
this.channel.emit('command', cmd);
}

harvestCommand(unitIds: UnitId[], target: UnitId) {
harvestCommand(unitIds: UnitId[], target: UnitId, shift: boolean) {
const cmd : CommandPacket = {
action: {
typ: 'Harvest',
target,
},
unitIds,
shift: false,
shift,
};
this.channel.emit('command', cmd);
}
Expand Down
Loading

0 comments on commit 50bb1a1

Please sign in to comment.