Commit 429e22d3 authored by 18류지석's avatar 18류지석

Merge branch 'server' into release

parents 59dde809 276169f8
var GameServer = GameServer || {}; var GameServer = GameServer || {};
GameServer.Phase = {READY: 0, START: 1, MAIN: 2, MUSIC: 3}; GameServer.Phase = {READY: 0, START: 1, MAIN: 2, MUSIC: 3};
GameServer.startCount = 1; GameServer.startCount = 2;
GameServer.currentPlayer = []; GameServer.currentPlayer = [];
GameServer.playingRoom = []; GameServer.playingRoom = [];
...@@ -98,10 +98,10 @@ GameServer.startRoom = function(roomIdx) ...@@ -98,10 +98,10 @@ GameServer.startRoom = function(roomIdx)
roomNum: room.roomNum, roomNum: room.roomNum,
players: room.currentPlayer players: room.currentPlayer
}; };
console.log(toSync); //console.log(toSync);
this.announceToRoom(roomIdx, 'syncRoomData', toSync); this.announceToRoom(roomIdx, 'syncRoomData', toSync);
console.log('[ROOM#'+room.roomNum+'] Game Start'); console.log('[ROOM#'+room.roomNum+'] Game Start with ' + room.currentPlayer.length + ' players');
this.announceToRoom(roomIdx, 'changePhase', this.Phase.START); this.announceToRoom(roomIdx, 'changePhase', this.Phase.START);
this.announceToRoom(roomIdx, 'startGame'); this.announceToRoom(roomIdx, 'startGame');
} }
...@@ -112,6 +112,14 @@ GameServer.announceToRoom = function(roomIdx, _message, _data = null) ...@@ -112,6 +112,14 @@ GameServer.announceToRoom = function(roomIdx, _message, _data = null)
element.socketId.emit(_message, _data); element.socketId.emit(_message, _data);
}); });
} }
GameServer.announceToTarget = function(roomIdx, targetNum, _message, _data = null)
{
let targetSocket = this.playingRoom[roomIdx].currentSocket.find(function(element)
{
return element.id === targetNum;
});
if (targetSocket != undefined) targetSocket.socketId.emit(_message, _data);
}
// 데이터 동기화 함수 만들기 // 데이터 동기화 함수 만들기
// 동기화할것: 유저리스트(id - nickname 쌍) // 동기화할것: 유저리스트(id - nickname 쌍)
......
...@@ -4,7 +4,7 @@ BackGround.brainGroup = null; ...@@ -4,7 +4,7 @@ BackGround.brainGroup = null;
BackGround.loadImage = function(scene) BackGround.loadImage = function(scene)
{ {
scene.load.image('brainGround', 'assets/placeholder/playback.png'); scene.load.image('brainGround', 'assets/image/background/background_brain.png');
scene.load.image('menuBackground', 'assets/placeholder/menuBackground.png') scene.load.image('menuBackground', 'assets/placeholder/menuBackground.png')
} }
......
var socket = io.connect(); var socket = io.connect();
// init account
socket.emit('idRequest'); socket.emit('idRequest');
socket.on('setId', function(msg) // {str, num playerNum} socket.on('setId', function(msg) // {str, num playerNum}
{ {
console.log(msg.str); console.log(msg.str);
PlayerData.idNum = msg.num; PlayerData.idNum = msg.num;
}); });
socket.on('setPlayerTypingRate', function(msg) // number playerTypingRate
{
WordSpace.PlayerTypingRate = msg;
console.log('rate: ' + msg);
});
// init game
socket.on('syncRoomData', function(msg) // {num roomNum, [] players} socket.on('syncRoomData', function(msg) // {num roomNum, [] players}
{ {
console.log(msg); console.log(msg);
RoomData.roomNum = msg.roomNum; RoomData.roomNum = msg.roomNum;
RoomData.players = msg.players; RoomData.players = msg.players;
RoomData.aliveCount = msg.players.length;
}); });
socket.on('startGame', function() socket.on('startGame', function()
{ {
game.scene.start('gameScene'); game.scene.start('gameScene');
}); });
// in game
socket.on('changePhase', function(msg) // number Phase socket.on('changePhase', function(msg) // number Phase
{ {
console.log('phase changed from ' + WordSpace.CurrentPhase + ' to ' + msg); console.log('phase changed from ' + WordSpace.CurrentPhase + ' to ' + msg);
WordSpace.CurrentPhase = msg; WordSpace.CurrentPhase = msg;
}); });
socket.on('setPlayerTypingRate', function(msg) // number playerTypingRate
{
WordSpace.PlayerTypingRate = msg;
//console.log('rate: ' + msg);
});
socket.on('attacked', function(msg) // object attackData
{
WordSpace.generateWord.Attack(WordSpace.gameSceneForTest, msg.text, msg.grade, msg.attacker, msg.isStrong);
});
socket.on('defeat', function(msg) // object player
{
RoomData.players[msg.index] = msg;
RoomData.aliveCount--;
console.log(RoomData.players[msg.index].nickname + ' defeated');
});
// out game
socket.on('userDisconnect', function(msg) // {num index , num id, str nickname} socket.on('userDisconnect', function(msg) // {num index , num id, str nickname}
{ {
console.log(msg.index + ' / ' + msg.id + ' / ' + msg.nickname + ' disconnected'); console.log(msg.index + ' / ' + msg.id + ' / ' + msg.nickname + ' disconnected');
RoomData.players[msg.index].isAlive = false; RoomData.players[msg.index] = msg;
RoomData.aliveCount--;
}); });
\ No newline at end of file
...@@ -7,7 +7,7 @@ Input.isShifted = false; ...@@ -7,7 +7,7 @@ Input.isShifted = false;
Input.isEntered = false; Input.isEntered = false;
Input.pressCount = 0; Input.pressCount = 0;
Input.justPressed = ''; Input.justPressed = '';
Input.maxInput = 5; Input.maxInput = 6;
Input.attackMode = false; Input.attackMode = false;
Input.attackOption = null; Input.attackOption = null;
...@@ -93,7 +93,7 @@ Input.convert = function() ...@@ -93,7 +93,7 @@ Input.convert = function()
if (this.input[i] >= ''.charCodeAt(0)) vowels.push(krInput.length - 1); // 모음일 경우 if (this.input[i] >= ''.charCodeAt(0)) vowels.push(krInput.length - 1); // 모음일 경우
} }
} }
if (vowels.length > 5) return false; if (vowels.length > 6) return false;
//console.log(vowels); //console.log(vowels);
//console.log(krInput); //console.log(krInput);
......
...@@ -56,10 +56,24 @@ var gameScene = new Phaser.Class( ...@@ -56,10 +56,24 @@ var gameScene = new Phaser.Class(
WordSpace.wordCycle.resetCycle(this, 3000, 0, true); WordSpace.wordCycle.resetCycle(this, 3000, 0, true);
WordSpace.nameCycle.resetCycle(this, 3000, 0, true); WordSpace.nameCycle.resetCycle(this, 3000, 0, true);
WordSpace.varAdjustCycle.resetCycle(this, 100, 0, true); WordSpace.varAdjustCycle.resetCycle(this, 100, 0, true);
WordSpace.playerTypingCycle = setInterval(function()
{
socket.emit('setPlayerTyping', WordSpace.playerTyping);
}, 500);
WordSpace.setPlayerTyping.initiate(this); WordSpace.setPlayerTyping.initiate(this);
WordSpace.nameWordTextForTest = WordSpace.gameSceneForTest.add.text(50,400,'현재 가진 호패들 : 없음').setDepth(10).setColor('#000000'); WordSpace.nameWordTextForTest = WordSpace.gameSceneForTest.add.text(50,400,'현재 가진 호패들 : 없음').setDepth(10).setColor('#000000');
WordSpace.nameQueue.initiate();
RoomData.players.forEach(function(element)
{
if(element.nickname == PlayerData.nickname)
{
RoomData.myself = element;
return;
}
});
console.log(RoomData.myself);
}, },
update: function() update: function()
...@@ -71,7 +85,7 @@ var gameScene = new Phaser.Class( ...@@ -71,7 +85,7 @@ var gameScene = new Phaser.Class(
let tempNames = ''; let tempNames = '';
WordSpace.nameGroup.forEach(function(element) WordSpace.nameGroup.forEach(function(element)
{ {
tempNames += element.wordText + '\n'; tempNames += element.wordText + element.isStrong + '\n';
}); });
WordSpace.nameWordTextForTest.setText('현재 가진 호패들 : \n' + tempNames); WordSpace.nameWordTextForTest.setText('현재 가진 호패들 : \n' + tempNames);
......
class WordObject class WordObject
{ {
constructor(text) constructor(text, isNameWord = false)
{ {
this.generationCode = WordSpace.nextWordCode++; this.generationCode = WordSpace.nextWordCode++;
this.wordText = text; this.wordText = text;
...@@ -9,21 +9,35 @@ class WordObject ...@@ -9,21 +9,35 @@ class WordObject
this.wordWeight = WordReader.getWordWeight(this.wordGrade); this.wordWeight = WordReader.getWordWeight(this.wordGrade);
//console.log("wordTyping : " + this.wordTyping + '\n' + "wordGrade : " + this.wordGrade + '\n' + "wordWeight : " + this.wordWeight + '\n'); //console.log("wordTyping : " + this.wordTyping + '\n' + "wordGrade : " + this.wordGrade + '\n' + "wordWeight : " + this.wordWeight + '\n');
this.wordSpeed = 0.5; this.wordSpeed = 0.5;
this.isNameWord = isNameWord;
} }
instantiate(scene,lenRate) instantiate(scene, lenRate)
{ {
let p = [{x : 3, y : 0.7}, {x : 20, y : 1.8}]; let p = [{x : 3, y : 0.7}, {x : 20, y : 1.2}];
let scale = ((p[1].y - p[0].y) / (p[1].x - p[0].x)) * (this.wordWeight - p[0].x) + p[0].y; let scale = ((p[1].y - p[0].y) / (p[1].x - p[0].x)) * (this.wordWeight - p[0].x) + p[0].y;
let fontscale = 25; let fontscale = 25;
var random = WordSpace.getSpawnPoint(lenRate); var random = WordSpace.getSpawnPoint(lenRate);
this.physicsObj = scene.physics.add.sprite(random.x, random.y, 'wordBgr' + this.wordGrade + '_' + Math.min(Math.max(2, this.wordText.length), 6)) if (!this.isNameWord)
.setMass(this.wordWeight * 10) {
.setScale(scale) this.physicsObj = scene.physics.add.sprite(random.x, random.y, 'wordBgr' + this.wordGrade + '_' + Math.min(Math.max(2, this.wordText.length), 6))
.setFrictionX(0) .setMass(this.wordWeight * 10)
.setFrictionY(0) .setScale(scale)
.setBounce(0.5); .setFrictionX(0)
.setFrictionY(0)
.setBounce(0.5);
}
else
{
this.physicsObj = scene.physics.add.sprite(random.x, random.y, 'nameBgr' + Math.min(Math.max(2, this.wordText.length), 6))
.setMass(this.wordWeight * 10)
.setScale(scale)
.setFrictionX(0)
.setFrictionY(0)
.setBounce(0.5);
}
let dist = Phaser.Math.Distance.Between(this.physicsObj.x, this.physicsObj.y, WordSpace.gravityPoint.x, WordSpace.gravityPoint.y); let dist = Phaser.Math.Distance.Between(this.physicsObj.x, this.physicsObj.y, WordSpace.gravityPoint.x, WordSpace.gravityPoint.y);
let angle = Phaser.Math.Angle.Between(this.physicsObj.x, this.physicsObj.y, WordSpace.gravityPoint.x, WordSpace.gravityPoint.y); let angle = Phaser.Math.Angle.Between(this.physicsObj.x, this.physicsObj.y, WordSpace.gravityPoint.x, WordSpace.gravityPoint.y);
...@@ -37,7 +51,9 @@ class WordObject ...@@ -37,7 +51,9 @@ class WordObject
fontSize: (scale * fontscale) +'pt', fontSize: (scale * fontscale) +'pt',
fontFamily: '"궁서", 궁서체, serif', fontFamily: '"궁서", 궁서체, serif',
fontStyle: (this.wordWeight > 5 ? 'bold' : '') fontStyle: (this.wordWeight > 5 ? 'bold' : '')
}).setColor('#000000').setOrigin(0.5,0.5); });
if (!this.isNameWord) this.wordObj.setColor('#000000').setOrigin(0.5,0.5);
else this.wordObj.setColor('#ffffff').setOrigin(0.45,0.5);
WordSpace.totalWeight += this.wordWeight; WordSpace.totalWeight += this.wordWeight;
WordSpace.totalWordNum += 1; WordSpace.totalWordNum += 1;
WordSpace.setGameOverTimer(); WordSpace.setGameOverTimer();
...@@ -114,19 +130,19 @@ class NormalWord extends WordObject ...@@ -114,19 +130,19 @@ class NormalWord extends WordObject
class AttackWord extends WordObject class AttackWord extends WordObject
{ {
constructor(text, _wordGrade, _attacker, isStrong) constructor(text, _wordGrade, _playerData, isStrong)
{ {
super(text); super(text);
this.wordGrade = _wordGrade; this.wordGrade = _wordGrade;
this.wordWeight = WordReader.getWordWeight(this.wordGrade); this.wordWeight = WordReader.getWordWeight(this.wordGrade);
if(WordReader.getWordTyping(_attacker) <= 9) if(WordReader.getWordTyping(_playerData.nickname) <= 9)
this.wordWeight += this.wordWeight * 0.2 * (WordReader.getWordTyping(PlayerData.nickname) - 9); this.wordWeight += this.wordWeight * 0.2 * (WordReader.getWordTyping(_playerData.nickname) - 9);
this.wordWeight *= isStrong ? 3 : 2; this.wordWeight *= isStrong ? 3 : 2;
this.attacker = _attacker; this.attacker = _playerData;
//서버 사용하게 되면 PlayerTyping을 피격자의 것으로 바꿔야 함 //서버 사용하게 되면 PlayerTyping을 피격자의 것으로 바꿔야 함
this.counterTime = WordSpace.gameTimer.now + 1000 * (this.wordTyping <= (5 - _wordGrade) * 2.5 ? this.wordTyping * (WordSpace.playerTyping / 60) * 2 : this.counterTime = WordSpace.gameTimer.now + 1000 * (this.wordTyping <= (5 - _wordGrade) * 2.5 ? this.wordTyping * (WordSpace.playerTyping / 60) * 2 :
((5 - _wordGrade) * 2.5 + (this.wordTyping - (5 - _wordGrade) * 2.5) * 3) * (WordSpace.playerTyping / 60) * 2); ((5 - _wordGrade) * 2.5 + (this.wordTyping - (5 - _wordGrade) * 2.5) * 3) * (WordSpace.playerTyping / 60) * 2);
console.log('Attack text : ' + text + ', Attacker : ' + this.attacker + ', Weight : ' + this.wordWeight); console.log('Attack text : ' + text + ', Attacker : ' + this.attacker.nickname + ', Weight : ' + this.wordWeight);
console.log('Counter time : ' + this.counterTime); console.log('Counter time : ' + this.counterTime);
} }
destroy() destroy()
...@@ -139,19 +155,22 @@ class AttackWord extends WordObject ...@@ -139,19 +155,22 @@ class AttackWord extends WordObject
case 3: WordSpace.attackGauge.add(0.5); break; case 3: WordSpace.attackGauge.add(0.5); break;
default: console.log('[ERR] wrong grade of word'); break; default: console.log('[ERR] wrong grade of word'); break;
} }
if(WordSpace.gameTimer.now < this.counterTime) WordSpace.generateWord.Name(WordSpace.gameSceneForTest, true); if(WordSpace.gameTimer.now < this.counterTime) WordSpace.nameGroup.push(new NameWord(this.attacker, true));
//강호패 넣기 구현해야됨
//WordSpace.generateWord.Name(WordSpace.gameSceneForTest, true);
super.destroy(); super.destroy();
} }
} }
class NameWord extends WordObject class NameWord extends WordObject
{ {
constructor(text, _isStrong = false) constructor(player, _isStrong = false)
{ {
super(text); super(player.nickname, true);
this.ownerId = player.id;
this.wordWeight = 2; this.wordWeight = 2;
this.isStrong = _isStrong; this.isStrong = _isStrong;
console.log('Name : ' + text + ', Strong : ' + this.isStrong + ', Weight : ' + this.wordWeight); console.log('Name : ' + player.nickname + ', Strong : ' + this.isStrong + ', Weight : ' + this.wordWeight);
} }
destroy() destroy()
{ {
......
var WordReader = WordReader || {}; var WordReader = WordReader || {};
//초성의 타수를 반환함 //초성의 타수를 반환함
function firstSound(charText) WordReader.firstSound = function(charText)
{ {
var r = parseInt(((charText.charCodeAt(0) - parseInt('0xac00',16)) /28) / 21); var r = parseInt(((charText.charCodeAt(0) - parseInt('0xac00',16)) /28) / 21);
//쌍자음일 경우 //쌍자음일 경우
...@@ -10,7 +10,7 @@ function firstSound(charText) ...@@ -10,7 +10,7 @@ function firstSound(charText)
} }
//중성의 타수를 반환함 //중성의 타수를 반환함
function middleSound(charText) WordReader.middleSound = function(charText)
{ {
var r = parseInt(((charText.charCodeAt(0)- parseInt('0xac00',16)) / 28) % 21); var r = parseInt(((charText.charCodeAt(0)- parseInt('0xac00',16)) / 28) % 21);
//'ㅒ' 또는 'ㅖ'일 경우 //'ㅒ' 또는 'ㅖ'일 경우
...@@ -21,7 +21,7 @@ function middleSound(charText) ...@@ -21,7 +21,7 @@ function middleSound(charText)
} }
//종성의 타수를 반환함 //종성의 타수를 반환함
function lastSound(charText) WordReader.lastSound = function(charText)
{ {
var r = parseInt((charText.charCodeAt(0) - parseInt('0xac00',16)) % 28); var r = parseInt((charText.charCodeAt(0) - parseInt('0xac00',16)) % 28);
//쌍자음일 경우 //쌍자음일 경우
...@@ -40,7 +40,7 @@ WordReader.getWordTyping = function(stringText) ...@@ -40,7 +40,7 @@ WordReader.getWordTyping = function(stringText)
for(var i = 0; i < stringText.length; i++) for(var i = 0; i < stringText.length; i++)
{ {
if(stringText.charCodeAt(i) < parseInt('0xac00',16) || stringText.charCodeAt(i) > parseInt('0xd7af',16)) return -1; if(stringText.charCodeAt(i) < parseInt('0xac00',16) || stringText.charCodeAt(i) > parseInt('0xd7af',16)) return -1;
temp += parseFloat(firstSound(stringText.charAt(i))) + middleSound(stringText.charAt(i)) + lastSound(stringText.charAt(i)); temp += parseFloat(WordReader.firstSound(stringText.charAt(i))) + WordReader.middleSound(stringText.charAt(i)) + WordReader.lastSound(stringText.charAt(i));
} }
return temp; return temp;
} }
......
This diff is collapsed.
...@@ -5,10 +5,10 @@ var config = { ...@@ -5,10 +5,10 @@ var config = {
physics: { physics: {
default: 'arcade', default: 'arcade',
arcade: { arcade: {
debug: true debug: false
} }
}, },
backgroundColor: Phaser.Display.Color.GetColor(0,0,0), backgroundColor: Phaser.Display.Color.HexStringToColor('#F0CB85').color,//GetColor(245,208,138),
scene: [ menuScene, gameScene ] scene: [ menuScene, gameScene ]
}; };
...@@ -25,4 +25,6 @@ PlayerData.nickname = '홍길동'; //플레이어 닉네임 ...@@ -25,4 +25,6 @@ PlayerData.nickname = '홍길동'; //플레이어 닉네임
var RoomData = RoomData || {}; var RoomData = RoomData || {};
RoomData.roomNum = -1; RoomData.roomNum = -1;
RoomData.players = null; RoomData.myself = null;
\ No newline at end of file RoomData.players = null;
RoomData.aliveCount = -1;
\ No newline at end of file
...@@ -27,12 +27,13 @@ io.on('connection', function(socket) ...@@ -27,12 +27,13 @@ io.on('connection', function(socket)
nickname: '게스트', nickname: '게스트',
socketId: socket, socketId: socket,
currentRoom: null, currentRoom: null,
playingData: null,
playerTyping: 0 playerTyping: 0
}; };
GameServer.currentPlayer.push(socket.playerData); GameServer.currentPlayer.push(socket.playerData);
console.log('['+socket.playerData.id+'] client request'); console.log('['+socket.playerData.id+'] client request');
socket.emit('idSet', socket.emit('setId',
{ {
str: 'your number is ' + socket.playerData.id, str: 'your number is ' + socket.playerData.id,
num: socket.playerData.id num: socket.playerData.id
...@@ -48,32 +49,62 @@ io.on('connection', function(socket) ...@@ -48,32 +49,62 @@ io.on('connection', function(socket)
socket.on('setPlayerTyping', function(msg) // number playerTyping socket.on('setPlayerTyping', function(msg) // number playerTyping
{ {
socket.playerData.playerTyping = msg; socket.playerData.playingData.playerTyping = msg;
//console.log(socket.playerData.currentRoom); if (socket.playerData.currentRoom.maxTypingPlayer.playerTyping < msg)
//console.log(socket.playerData.currentRoom.currentPlayer.length); {
//let playerTypingRate = (msg - (socket.playerData.currentRoom.minTypingPlayer.playerTyping - socket.playerData.currentRoom.rateArrangePoint)) / socket.playerData.currentRoom.maxTypingPlayer = socket.playerData.playingData;
//(socket.playerData.currentRoom.maxTypingPlayer.playerTyping - socket.playerData.currentRoom.minTypingPlayer.playerTyping + socket.playerData.currentRoom.rateArrangePoint * 2); }
//socket.emit('setPlayerTypingRate', playerTypingRate); if (socket.playerData.currentRoom.minTypingPlayer.playerTyping > msg)
{
socket.playerData.currentRoom.minTypingPlayer = socket.playerData.playingData;
}
let playerTypingRate = (msg - (socket.playerData.currentRoom.minTypingPlayer.playerTyping - socket.playerData.currentRoom.rateArrangePoint)) /
(socket.playerData.currentRoom.maxTypingPlayer.playerTyping - socket.playerData.currentRoom.minTypingPlayer.playerTyping + socket.playerData.currentRoom.rateArrangePoint * 2);
socket.emit('setPlayerTypingRate', playerTypingRate);
});
socket.on('attack', function(msg)
{
GameServer.announceToTarget(GameServer.findRoomIndex(msg.roomNum), msg.target, 'attacked', msg);
});
socket.on('defeated', function()
{
socket.playerData.playingData.isAlive = false;
socket.playerData.playingData.rank = socket.playerData.currentRoom.nextRank--;
// 패배단어 체크
GameServer.announceToRoom(socket.playerData.currentRoom.roomNum, 'defeat', socket.playerData.playingData);
console.log('['+socket.playerData.id+']'+ ' defeated');
}); });
socket.on('disconnect', function(reason) socket.on('disconnect', function(reason)
{ {
let idxToDel = GameServer.currentPlayer.findIndex(function(element) let data = socket.playerData;
console.log('['+ data.id +'] client disconnected, reason: ' + reason);
if (data.id === undefined)
{ {
return element.id === socket.playerData.id; console.log('[ERROR] data.id is undefined');
}); console.log(GameServer.currentPlayer);
if (idxToDel != -1) }
else // data.id is not undefined
{ {
console.log('['+ socket.playerData.id +'] client disconnected, reason: ' + reason); let idxToDel = GameServer.currentPlayer.findIndex(function(element)
GameServer.currentPlayer.splice(idxToDel, 1); {
// 룸에서도 제거 return element.id === data.id;
if (socket.playerData.currentRoom != null) });
if (idxToDel != -1)
{ {
socket.playerData.playingData.isAlive = false; GameServer.currentPlayer.splice(idxToDel, 1);
socket.playerData.playingData.rank = socket.playerData.currentRoom.nextRank--; // 룸에서도 제거
socket.playerData.currentRoom.currentSocket.splice(socket.playerData.playingData.index, 1); if (data.currentRoom != null)
GameServer.announceToRoom(GameServer.findRoomIndex(socket.playerData.currentRoom.roomNum), 'userDisconnect', socket.playerData.playingData); {
data.playingData.isAlive = false;
if (data.playingData.rank === -1) data.playingData.rank = data.currentRoom.nextRank--;
data.currentRoom.currentSocket.splice(data.playingData.index, 1);
GameServer.announceToRoom(GameServer.findRoomIndex(data.currentRoom.roomNum), 'userDisconnect', data.playingData);
}
} }
console.log('['+ data.id +'] disconnect complete');
} }
}); });
}); });
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment