Skip to content

Commit 79946f3

Browse files
committed
Add context menu, implement wait action
1 parent 083c17e commit 79946f3

File tree

11 files changed

+141
-21
lines changed

11 files changed

+141
-21
lines changed

index.html

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,46 @@
33
<head>
44
<meta charset="UTF-8">
55
<title>Game</title>
6+
<link type="text/css" rel="stylesheet" href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.no-icons.min.css" />
7+
<style>
8+
#content {
9+
position: relative;
10+
}
11+
.menu {
12+
position: absolute;
13+
border-radius: 10px;
14+
border: 2px solid #eee;
15+
box-shadow: 0px 2px 10px rgba(0,0,0,.5);
16+
background: rgba(255,255,255,.5);
17+
max-width: 100px;
18+
padding: 20px;
19+
}
20+
21+
.menu .action:not(:last-child) {
22+
margin-bottom:10px;
23+
}
24+
25+
.menu .action button {
26+
width: 100%;
27+
border-radius: 10px;
28+
}
29+
</style>
630
</head>
731
<body>
832
<div>
9-
<div id="content"></div>
33+
<div id="content">
34+
<div id="context-menu" class="container menu" style="display: none;">
35+
<div class="action">
36+
<button id="btn-attack" class="btn btn-danger">Attack</button>
37+
</div>
38+
<div class="action">
39+
<button id="btn-wait" class="btn btn-default">Wait</button>
40+
</div>
41+
<div class="action">
42+
<button id="btn-cancel" class="btn btn-default">Cancel</button>
43+
</div>
44+
</div>
45+
</div>
1046
</div>
47+
1148
</body>

src/controllers/game/ai/controller.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export class AIController extends BaseController {
1919
private _playerAis: {[key: number]: IArmyCommandStrategy} = {};
2020

2121
preload() {
22+
this._gameSubject.subscribe(GameEvent.LoadMapCompleted, this._onLoadMapCompleted);
2223
this._gameSubject.subscribe(GameEvent.TurnStart, this._onTurnStart);
2324
this._gameSubject.subscribe(GameEvent.UnitMoveActionSelected, this._onMoveActionSelected);
2425
this._gameSubject.subscribe(GameEvent.UnitMoveCompleted, this._onMoveCompleted);
@@ -27,13 +28,14 @@ export class AIController extends BaseController {
2728
}
2829

2930
create() {
31+
32+
}
33+
34+
private _onLoadMapCompleted = (): void => {
3035
for(let i = 1; i < this._gameSubject.numberOfPlayers; i++) {
3136
this._playerAis[i] = this._armyStrategyFactory.create(i);
3237
}
33-
34-
//TODO: move this to GameController eventually.
35-
this._gameSubject.dispatch(GameEvent.TurnStart, 0);
36-
}
38+
};
3739

3840
private _onTurnStart = (playerNum: number): void => {
3941
if(!!this._playerAis[playerNum])

src/controllers/game/contextmenu/controller.ts

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {ContainerKeys} from "../../../inversify.config";
1212
export class ContextMenuController extends BaseController {
1313

1414
private _selectedUnit: BaseUnit;
15+
private _menuElement: HTMLElement;
1516

1617
constructor(
1718
private _game: Game,
@@ -20,23 +21,69 @@ export class ContextMenuController extends BaseController {
2021
@inject(ContainerKeys.CONFIG) private _config: GameConfig
2122
) {
2223
super();
24+
this._menuElement = document.getElementById('context-menu');
2325
}
2426

2527
preload() {
26-
this._gameSubject.subscribe(GameEvent.UnitSelected, unit => this._selectedUnit = unit);
27-
this._gameSubject.subscribe(GameEvent.CancelAction, unit => this._selectedUnit = null);
28+
this._gameSubject.subscribe(GameEvent.UnitSelected, this._onUnitSelected);
29+
this._gameSubject.subscribe(GameEvent.CancelAction, this._onCancelAction);
30+
this._gameSubject.subscribe(GameEvent.UnitAttackActionSelected, this._hideMenu);
31+
this._gameSubject.subscribe(GameEvent.UnitWaitActionSelected, this._hideMenu);
2832
this._inputSubject.subscribe(InputEvent.KeyAttack, this._onKeyAttack);
33+
this._inputSubject.subscribe(InputEvent.KeyWait, this._onKeyWait);
34+
35+
//handle events from context menu
36+
window.addEventListener('actionSelected', e => {
37+
let action = e['detail']['action'];
38+
switch (action) {
39+
case 'attack':
40+
this._gameSubject.dispatch(GameEvent.UnitAttackActionSelected, this._selectedUnit);
41+
break;
42+
case 'wait':
43+
this._gameSubject.dispatch(GameEvent.UnitWaitActionSelected, this._selectedUnit);
44+
break;
45+
case 'cancel':
46+
this._gameSubject.dispatch(GameEvent.CancelAction);
47+
break;
48+
default:
49+
console.error('Invalid action dispatched on actionSelected');
50+
}
51+
});
2952
}
3053

54+
private _hideMenu = (): void => {
55+
this._menuElement.style.display = 'none';
56+
};
57+
3158
// ------------------------------------
3259
// ---------- EVENT HANDLERS ----------
3360
// ------------------------------------
3461

62+
private _onUnitSelected = (unit: BaseUnit): void => {
63+
if(unit.hasActedThisTurn)
64+
return;
65+
66+
this._selectedUnit = unit;
67+
68+
this._menuElement.style.display = 'block';
69+
this._menuElement.style.left = `${unit.spr.x + 50}px`;
70+
this._menuElement.style.top = `${unit.spr.y - 80}px`;
71+
};
72+
73+
private _onCancelAction = (): void => {
74+
this._selectedUnit = null;
75+
this._hideMenu();
76+
};
77+
3578
private _onKeyAttack = (): void => {
36-
//TODO: eventually we only want to dispatch this if it is a VALID time to initiate attack. I put it in here because attack will be the menu
37-
// TODO: maybe this should be moved to unit controller? or perhaps action selected events belong here
3879
if(!!this._selectedUnit) {
3980
this._gameSubject.dispatch(GameEvent.UnitAttackActionSelected, this._selectedUnit);
4081
}
4182
};
83+
84+
private _onKeyWait = (): void => {
85+
if(!!this._selectedUnit) {
86+
this._gameSubject.dispatch(GameEvent.UnitWaitActionSelected, this._selectedUnit);
87+
}
88+
};
4289
}

src/controllers/game/controller.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ export class GameController extends BaseController {
4040
this._mapBuilder.load(mapName);
4141
this._gameSubject.numberOfPlayers = this._mapBuilder.getNumOfPlayers();
4242

43-
this._gameSubject.dispatch(GameEvent.LoadMapCompleted);
43+
this._gameSubject.dispatch(GameEvent.LoadMapCompleted); //ais will be created here.
44+
this._gameSubject.dispatch(GameEvent.TurnStart, 0);
4445
};
4546

4647
private _onTurnComplete = (playerNum: number): void => {

src/controllers/game/grid/controller.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ export class GridController extends BaseController {
3333
this._gameSubject.subscribe(GameEvent.LoadMapCompleted, this._onMapLoadCompleted);
3434
this._gameSubject.subscribe(GameEvent.UnitMoveActionSelected, this._onMoveActionSelected);
3535
this._gameSubject.subscribe(GameEvent.UnitAttackActionSelected, this._onAttackActionSelected);
36-
this._gameSubject.subscribe(GameEvent.CancelAction, this._onCancelAction);
36+
this._gameSubject.subscribe(GameEvent.CancelAction, this._unhighlightAll);
37+
this._gameSubject.subscribe(GameEvent.UnitWaitActionSelected, this._unhighlightAll);
3738
this._gameSubject.subscribe(GameEvent.UnitMove, (): void => {this._canActivateCells = false;}); // returning false will cancel the event.
3839
this._gameSubject.subscribe(GameEvent.UnitMoveCompleted, (): void => {this._canActivateCells = true;});
3940
}
@@ -135,10 +136,6 @@ export class GridController extends BaseController {
135136
}
136137
};
137138

138-
private _onCancelAction = () => {
139-
this._unhighlightAll();
140-
};
141-
142139
private _onMoveActionSelected = (unit: BaseUnit) => {
143140
//highlight all cells in move range
144141
let cellUnderUnit = this._getCellAt(unit);
@@ -165,7 +162,7 @@ export class GridController extends BaseController {
165162
// ---------- HELPER FUNCTIONS -----------
166163
// ---------------------------------------
167164

168-
private _unhighlightAll(): void {
165+
private _unhighlightAll = (): void => {
169166
this._highlightCellsForMove = null;
170167
this._highlightCellsForAttack = null;
171168

@@ -174,7 +171,7 @@ export class GridController extends BaseController {
174171
if(!cell.active)
175172
cell.spr.tint = cell.restingTint;
176173
});
177-
}
174+
};
178175

179176
private _highlightCellsInRange(cell: GridCell, range: number, isAttack: boolean = false, previousCell: GridCell = null, previousDirection: string = null) {
180177
if (!range || !cell || (cell.blocksMove && !isAttack) || (cell.blocksAttack && isAttack)

src/controllers/game/unit/controller.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,15 @@ export class UnitController extends BaseController {
3030
this._gameSubject.subscribe(GameEvent.LoadMapCompleted, this._onMapLoadCompleted);
3131
this._gameSubject.subscribe(GameEvent.GridCellActivated, this._onCellActivated);
3232
this._gameSubject.subscribe(GameEvent.CancelAction, this._onCancelAction);
33+
this._gameSubject.subscribe(GameEvent.UnitWaitActionSelected, this._onWaitActionSelected);
3334
this._gameSubject.subscribe(GameEvent.UnitMove, this._onUnitMove);
3435
this._gameSubject.subscribe(GameEvent.UnitMoveCompleted, this._onUnitMoveCompleted);
3536
this._gameSubject.subscribe(GameEvent.UnitAttack, this._onUnitAttack);
37+
this._gameSubject.subscribe(GameEvent.UnitAttackCompleted, this._onUnitAttackCompleted);
3638
this._gameSubject.subscribe(GameEvent.TurnComplete, this._onTurnComplete);
3739
}
3840

3941
create() {
40-
this.units = this._mapBuilder.buildUnits();
41-
this._gameSubject.units = this.units;
4242
}
4343

4444
update() {
@@ -76,6 +76,10 @@ export class UnitController extends BaseController {
7676
this._selectedUnit = null;
7777
};
7878

79+
private _onWaitActionSelected = (unit: BaseUnit): void => {
80+
this._unitFinishedTurn(unit);
81+
};
82+
7983
private _onUnitMove = (destinationCell: GridCell): void => {
8084
if(!destinationCell)
8185
return;
@@ -94,6 +98,10 @@ export class UnitController extends BaseController {
9498
this._handleCombat(this._selectedUnit, defendingUnit);
9599
};
96100

101+
private _onUnitAttackCompleted = (unit: BaseUnit): void => {
102+
this._unitFinishedTurn(unit);
103+
};
104+
97105
private _onTurnComplete = (playerNum: number): void => {
98106
this.units.filter(u => u.belongsToPlayer === playerNum).forEach(u => u.hasMovedThisTurn = false);
99107
};
@@ -102,6 +110,12 @@ export class UnitController extends BaseController {
102110
// ---------- HELPER FUNCTIONS ----------
103111
// ---------------------------------------
104112

113+
private _unitFinishedTurn(unit: BaseUnit): void {
114+
unit.hasActedThisTurn = true;
115+
116+
//TODO: set tint to grayscale or something.
117+
}
118+
105119
private _moveUnit(unit: BaseUnit, targetCell: GridCell): void {
106120
let path = targetCell.pathFromActiveCell;
107121

src/controllers/shared/input/controller.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ import {InputEvent, InputSubject} from "../../../services/subject/input/service"
1111
export class InputController extends BaseController {
1212
private _isMouseDown: boolean;
1313
private _attackKey: Key; //TODO: move all shortcut keys to a map
14+
private _waitKey: Key;
1415
private _isAttackKeyDown: boolean;
16+
private _isWaitKeyDown: boolean;
1517

1618
constructor(
1719
private _game: Game,
@@ -26,6 +28,7 @@ export class InputController extends BaseController {
2628
// }
2729
this._game.input.mouse.capture = true;
2830
this._attackKey = this._initKey(Phaser.Keyboard.A);
31+
this._waitKey = this._initKey(Phaser.Keyboard.W);
2932
}
3033

3134
update() {
@@ -49,6 +52,14 @@ export class InputController extends BaseController {
4952
} else if (this._isAttackKeyDown && !this._attackKey.isDown) {
5053
this._isAttackKeyDown = false;
5154
}
55+
56+
//wait key event
57+
if(!this._isWaitKeyDown && this._waitKey.isDown) {
58+
this._isWaitKeyDown = true;
59+
this._inputSubject.dispatch(InputEvent.KeyWait);
60+
} else if (this._isWaitKeyDown && !this._waitKey.isDown) {
61+
this._isWaitKeyDown = false;
62+
}
5263
}
5364

5465
private _initKey(keyCode: number): Key {

src/game_objects/units/base.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export abstract class BaseUnit implements Unit {
1414
belongsToPlayer: number;
1515

1616
hasMovedThisTurn: boolean;
17-
selectedAction: any;
17+
hasActedThisTurn: boolean;
1818

1919
x: number;
2020
y: number;

src/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,13 @@ class TbsGame {
4242

4343
window.onload = () => {
4444
const game = new TbsGame();
45+
46+
let click = (el, action) => {
47+
let event = new CustomEvent('actionSelected', {detail:{action}, bubbles: true, cancelable: false});
48+
el.dispatchEvent(event);
49+
};
50+
51+
document.getElementById('btn-attack').addEventListener('click', e => click(e.target, 'attack'));
52+
document.getElementById('btn-wait').addEventListener('click', e => click(e.target, 'wait'));
53+
document.getElementById('btn-cancel').addEventListener('click', e => click(e.target, 'cancel'));
4554
};

src/services/subject/game/service.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export enum GameEvent {
2121
LoadMapCompleted,
2222
GridCellActivated,
2323
UnitSelected,
24+
UnitWaitActionSelected,
2425
UnitMoveActionSelected,
2526
UnitMove,
2627
UnitMoveCompleted,

0 commit comments

Comments
 (0)