diff --git a/packages/junon-common/protocol/base.proto b/packages/junon-common/protocol/base.proto
index 11a06c97..b17f6fc2 100644
--- a/packages/junon-common/protocol/base.proto
+++ b/packages/junon-common/protocol/base.proto
@@ -798,6 +798,7 @@ message ServerChat {
string username = 3;
string uid = 4;
bool isTeam = 5;
+ string prefixesList = 6;
}
message Chunk {
diff --git a/packages/junon-io/client/main.ejs b/packages/junon-io/client/main.ejs
index 7d696fbf..69ca7946 100644
--- a/packages/junon-io/client/main.ejs
+++ b/packages/junon-io/client/main.ejs
@@ -916,6 +916,11 @@
value1, value2, ... , valueN |
Sums all given values. Ex. $add(2,5,8) = 15 |
+
+ | $if |
+ value1, operator, value2, then, else |
+ Returns the value given the condition. |
+
| $subtract |
value1, value2, ... , valueN |
@@ -1041,6 +1046,16 @@
$itemId |
Returns the usage capacity of an item |
+
+ | $getNthLetter |
+ index, word |
+ Returns the Nth letter of a word |
+
+
+ | $getNthWord |
+ index, string |
+ Returns the Nth word of a string |
+
@@ -1922,6 +1937,15 @@
+
diff --git a/packages/junon-io/client/src/menus/chat_menu.js b/packages/junon-io/client/src/menus/chat_menu.js
index 21e75233..a1d0e52b 100644
--- a/packages/junon-io/client/src/menus/chat_menu.js
+++ b/packages/junon-io/client/src/menus/chat_menu.js
@@ -219,8 +219,7 @@ class ChatMenu extends BaseMenu {
tooltip.style.top = top + "px"
}
-
- createChatEntry(chatGroup, messageObj) {
+ createChatEntry(chatGroup, messageObj,prefixList) {
let chatMessage = document.createElement("div")
chatMessage.className = "chat_message"
chatMessage.dataset.username = messageObj.username
@@ -229,7 +228,11 @@ class ChatMenu extends BaseMenu {
let chatUsername = document.createElement("span")
chatUsername.className = "chat_user"
if (messageObj.username) {
- chatUsername.innerText = "[" + messageObj.username + "]"
+ if (prefixList && prefixList[messageObj.username]) {
+ chatUsername.innerHTML = ""+ prefixList[messageObj.username].styleA + prefixList[messageObj.username].prefix.slice(0, 16) + prefixList[messageObj.username].styleB + " [" + messageObj.username + "]"
+ } else {
+ chatUsername.innerText = "[" + messageObj.username + "]"
+ }
if (messageObj.uid) {
chatUsername.dataset.uid = messageObj.uid
}
@@ -261,6 +264,8 @@ class ChatMenu extends BaseMenu {
}
}
+
+
setOpenDisplay() {
this.el.style.display = 'inline-block'
}
@@ -275,7 +280,7 @@ class ChatMenu extends BaseMenu {
const playerId = data.playerId
const username = data.username
const message = data.message
-
+ const prefixList = JSON.parse(data.prefixesList||"{}")
const chatUser = this.game.sector.players[playerId]
let messageObj = this.parseServerChat(message)
@@ -285,7 +290,7 @@ class ChatMenu extends BaseMenu {
let chatGroup = data.isTeam ? "team" : "local"
if (!this.isMuted(username)) {
- this.createChatEntry(chatGroup, messageObj)
+ this.createChatEntry(chatGroup, messageObj, prefixList)
}
if (chatUser && !this.isMuted(chatUser.name)) {
diff --git a/packages/junon-io/client/src/menus/team_menu.js b/packages/junon-io/client/src/menus/team_menu.js
index 001ce567..01232b79 100644
--- a/packages/junon-io/client/src/menus/team_menu.js
+++ b/packages/junon-io/client/src/menus/team_menu.js
@@ -48,6 +48,9 @@ class TeamMenu extends BaseMenu {
this.el.querySelector("#enable_fov").addEventListener("click", this.onEnableFovClick.bind(this), true)
this.el.querySelector("#disable_fov").addEventListener("click", this.onDisableFovClick.bind(this), true)
+ this.el.querySelector("#enable_spectate").addEventListener("click", this.onAllowSpectateClick.bind(this), true)
+ this.el.querySelector("#disable_spectate").addEventListener("click", this.onDenySpectateClick.bind(this), true)
+
this.el.querySelector("#enable_minimap").addEventListener("click", this.onEnableMinimapClick.bind(this), true)
this.el.querySelector("#disable_minimap").addEventListener("click", this.onDisableMinimapClick.bind(this), true)
@@ -143,6 +146,15 @@ class TeamMenu extends BaseMenu {
this.el.querySelector("#disable_fov").checked = true
}
}
+
+ if (name === 'isSpectateAllowed') {
+ let value = settings[name]
+ if (value) {
+ this.el.querySelector("#enable_spectate").checked = true
+ } else {
+ this.el.querySelector("#disable_spectate").checked = true
+ }
+ }
if (name === 'showMiniMap') {
let value = settings[name]
@@ -405,7 +417,6 @@ class TeamMenu extends BaseMenu {
})
}
}
-
onDisableFovClick(e) {
e.preventDefault()
@@ -420,6 +431,33 @@ class TeamMenu extends BaseMenu {
}
}
+ onAllowSpectateClick(e) {
+ e.preventDefault()
+
+ let value = e.target.value
+ if (value === 'yes') {
+ SocketUtil.emit("SectorAction", {
+ action: 'editSetting',
+ sectorId: this.game.sector.uid,
+ key: 'isSpectateAllowed',
+ value: 'true'
+ })
+ }
+ }
+ onDenySpectateClick(e) {
+ e.preventDefault()
+
+ let value = e.target.value
+ if (value === 'no') {
+ SocketUtil.emit("SectorAction", {
+ action: 'editSetting',
+ sectorId: this.game.sector.uid,
+ key: 'isSpectateAllowed',
+ value: 'false'
+ })
+ }
+ }
+
onEnableFloorAutodirtClick(e) {
e.preventDefault()
diff --git a/packages/junon-io/server/commands/chatprefix.js b/packages/junon-io/server/commands/chatprefix.js
new file mode 100644
index 00000000..61bb021d
--- /dev/null
+++ b/packages/junon-io/server/commands/chatprefix.js
@@ -0,0 +1,65 @@
+const BaseCommand = require("./base_command")
+const Constants = require("../../common/constants")
+
+class ChatPrefix extends BaseCommand {
+
+ getUsage() {
+ return [
+ "/chatprefix set [player] [prefix] [color] [style]",
+ "/chatprefix clear [player]",
+ "Styles:",
+ "1: [Test], 2: (Test), 3: {Test}",
+ ]
+ }
+
+ allowOwnerOnly() {
+ return true
+ }
+
+ perform(caller, args) {
+ if (!this.game.playerChatPrefixes) {this.game.playerChatPrefixes = {}}
+ let selectedPlayers = this.getPlayersBySelector(args[1])
+ if (selectedPlayers.length === 0) {
+ caller.showChatError("No such player")
+ return
+ }
+
+ let subcommand = args[0]
+ let style = args[4]
+ if (style) {
+ if (style==1) {
+ style = "[]"
+ } else if (style==2) {
+ style = "()"
+ } else {
+ style = "{}"
+ }
+ } else {style="[]"}
+
+ switch(subcommand) {
+ case "set":
+ if (!args[2]) {
+ caller.showChatError("No prefix")
+ return
+ }
+ selectedPlayers.forEach(ply => {
+ console.log(ply.name)
+ this.game.playerChatPrefixes[ply.name] = {
+ prefix:args[2].replace(/[^a-zA-Z]/g, '').slice(0, 8),
+ color:args[3] || "#ffffff",
+ styleA:style[0],
+ styleB:style[1]
+ }
+ });
+ break
+ case "clear":
+ selectedPlayers.forEach(ply => {
+ delete this.game.playerChatPrefixes[ply.name]
+ })
+ break
+ }
+ }
+
+}
+
+module.exports = ChatPrefix
\ No newline at end of file
diff --git a/packages/junon-io/server/commands/fly.js b/packages/junon-io/server/commands/fly.js
index fee8f1cf..7f8fad43 100644
--- a/packages/junon-io/server/commands/fly.js
+++ b/packages/junon-io/server/commands/fly.js
@@ -5,8 +5,10 @@ class Fly extends BaseCommand {
getUsage() {
return [
+ "Enables or disables fly state for players.",
"/fly",
- "/fly [player]"
+ "/fly [player]",
+ "/fly [player] [true/false]"
]
}
@@ -20,17 +22,22 @@ class Fly extends BaseCommand {
perform(player, args) {
const username = args[0]
+ let nextState = args[1]
+
+ if ((nextState !== "true") && (nextState !== "false") && (nextState !== null)) {
+ nextState = null
+ }
let targetPlayers = this.getPlayersBySelector(username)
if (targetPlayers.length === 0) {
if (player.isPlayer()) {
- player.toggleFly()
+ player.toggleFly(nextState)
}
return
}
targetPlayers.forEach((targetPlayer) => {
- targetPlayer.toggleFly()
+ targetPlayer.toggleFly(nextState)
})
}
diff --git a/packages/junon-io/server/commands/god.js b/packages/junon-io/server/commands/god.js
index 58c69eee..366762d7 100644
--- a/packages/junon-io/server/commands/god.js
+++ b/packages/junon-io/server/commands/god.js
@@ -5,8 +5,10 @@ const Protocol = require('../../common/util/protocol')
class God extends BaseCommand {
getUsage() {
return [
+ "Enables or disables god state for players.",
"/god",
- "/god [player]"
+ "/god [player]",
+ "/god [player] [true/false]"
]
}
@@ -21,6 +23,12 @@ class God extends BaseCommand {
perform(caller, args) {
const selector = args[0]
+ let godPar = args[1]
+
+ if ((godPar !== "false") && (godPar !== "true") && (godPar !== null)) {
+ godPar = null
+ }
+
if (selector) {
if (!caller.isSectorOwner()) {
caller.showChatError("permission denied")
@@ -34,21 +42,30 @@ class God extends BaseCommand {
}
targetPlayers.forEach((player) => {
- this.toggleGod(player)
+ this.toggleGod(player,godPar)
})
} else {
- if (caller.isPlayer()) {
- this.toggleGod(caller)
+ if (caller.isPlayer()) {
+ this.toggleGod(caller,godPar)
}
}
}
- toggleGod(player) {
- player.godMode = !player.godMode
+ toggleGod(player,tostate) {
+
+ if (tostate) {
+ if (tostate == "true") {
+ player.godMode = true
+ } else {
+ player.godMode = false
+ }
+ } else {
+ player.godMode = !player.godMode
+ }
+
if (player.godMode) {
player.setHealth(player.getMaxHealth())
}
-
player.showChatSuccess("god mode: " + (player.godMode ? "ON" : "OFF" ))
}
}
diff --git a/packages/junon-io/server/commands/index.js b/packages/junon-io/server/commands/index.js
index 40964ca0..e0d560c3 100644
--- a/packages/junon-io/server/commands/index.js
+++ b/packages/junon-io/server/commands/index.js
@@ -71,6 +71,7 @@ Commands.dirt = require('./dirt')
Commands.blood = require('./blood')
Commands.setstatus = require('./set_status')
Commands.menu = require('./menu')
+Commands.chatprefix = require('./chatprefix')
Commands.accept_rules = require("./accept_rules")
diff --git a/packages/junon-io/server/commands/spectate.js b/packages/junon-io/server/commands/spectate.js
index 38ad31a0..cd6d8f6c 100644
--- a/packages/junon-io/server/commands/spectate.js
+++ b/packages/junon-io/server/commands/spectate.js
@@ -1,6 +1,7 @@
const BaseCommand = require("./base_command")
const Constants = require("../../common/constants")
const Protocol = require('../../common/util/protocol')
+const { setting } = require(".")
class Spectate extends BaseCommand {
getUsage() {
@@ -36,10 +37,12 @@ class Spectate extends BaseCommand {
this.toggleSpectate(player)
})
} else {
+ if (this.game.sector.settings.isSpectateAllowed) {
if (caller.isPlayer()) {
this.toggleSpectate(caller)
}
}
+ }
}
diff --git a/packages/junon-io/server/entities/event_handler.js b/packages/junon-io/server/entities/event_handler.js
index f7a0b468..c81fdece 100644
--- a/packages/junon-io/server/entities/event_handler.js
+++ b/packages/junon-io/server/entities/event_handler.js
@@ -467,6 +467,90 @@ class EventHandler {
return this.scoreIndexByPlayer[playerId]
}
+ if(...vals) {
+ let condition = vals[1]
+ let val1 = vals[0]
+ let val2 = vals[2]
+
+ switch(condition) {
+ case "=": {
+ if (val1 === val2) {
+ return vals[3]
+ } else {return vals[4]}
+ }
+ case "<": {
+ if (val1 < val2) {
+ return vals[3]
+ } else {return vals[4]}
+ }
+ case ">": {
+ if (val1 > val2) {
+ return vals[3]
+ } else {return vals[4]}
+ }
+ case "<=": {
+ if (val1 <= val2) {
+ return vals[3]
+ } else {return vals[4]}
+ }
+ case ">=": {
+ if (val1 >= val2) {
+ return vals[3]
+ } else {return vals[4]}
+ }
+ case "!=": {
+ if (!(val1 === val2)) {
+ return vals[3]
+ } else {return vals[4]}
+ }
+ case "=~": {
+ if (val1.includes(val2)) {
+ return vals[3]
+ } else {return vals[4]}
+ }
+ default: {
+ return "undefined"
+ }
+ }
+ }
+
+ getNthWord(...values) {
+ if (values.length < 2) return ""
+ let index = parseInt(values[0])
+ let word = values.slice(1).join(" ")
+ let stringArray = word.split(" ")
+
+ if (isNaN(index) || index < 1) {
+ return ""
+ }
+ if (!word || typeof word !== "string") {
+ return ""
+ }
+ if(index > stringArray.length+1) {
+ return ""
+ }
+
+ let letter = stringArray[index - 1]
+ return letter
+ }
+
+ getNthLetter(...values) {
+ if (values.length === 0) return ""
+ let index = parseInt(values[0])
+ let word = values[1];
+ if (isNaN(index)) {
+ return ""
+ }
+ if (!word || typeof word !== "string") {
+ return ""
+ }
+ if (index < 1 || index > word.length) {
+ return ""
+ }
+ let letter = word[index - 1];
+ return letter
+ }
+
setScoreIndex(playerId, index) {
this.scoreIndexByPlayer[playerId] = index
}
@@ -1218,6 +1302,7 @@ class EventHandler {
"$getInventoryItemCount": true,
"$getArmorEquip": true,
"$add": true,
+ "$if": true,
"$subtract": true,
"$multiply": true,
"$divide": true,
@@ -1243,7 +1328,9 @@ class EventHandler {
"$getTotalMobCount": true,
"$getAngle": true,
"$getUsage": true,
- "$getCapacity": true
+ "$getCapacity": true,
+ "$getNthLetter": true,
+ "$getNthWord": true,
}
}
@@ -1270,14 +1357,20 @@ class EventHandler {
return this.getFunctionTable()[funcName]
}
- interpolateFunctions(result) {
+ interpolateFunctions(result) {
+ result = result.replace(/[\r\n]+/g, " ").trim();
let chars = result.split("")
let functionBuffer = ""
let resultBuffer = ""
+ let padepth = 0
for (var i = 0; i < chars.length; i++) {
let char = chars[i]
let isEndOfString = i === chars.length - 1
+
+ if (char === '(') padepth++
+ if (char === ')') padepth--
+
if (isEndOfString) {
if (functionBuffer.length > 0) {
functionBuffer += char
@@ -1288,11 +1381,13 @@ class EventHandler {
resultBuffer += char
}
} else if (char === " ") {
- if (functionBuffer.length > 0) {
+ if (functionBuffer.length > 0 && padepth === 0) {
let result = this.parseAndEvalExpression(functionBuffer)
functionBuffer = ""
resultBuffer += result
resultBuffer += char
+ } else if (functionBuffer.length > 0 && padepth > 0) {
+ functionBuffer += char
} else {
resultBuffer += char
}
@@ -1305,27 +1400,44 @@ class EventHandler {
return resultBuffer
}
-
parseAndEvalExpression(expression) {
let stack = []
let characters = expression.split("")
let keyword = ""
+ let parenthesisDepth = 0
for (var i = 0; i < characters.length; i++) {
let character = characters[i]
+
if (character === '(') {
- stack.push(keyword)
+ parenthesisDepth++
+ if (keyword.trim()) {
+ stack.push(keyword.trim())
+ }
stack.push("(")
keyword = ""
- // end functionName
+ } else if (character === " ") {
+ if (parenthesisDepth > 0) {
+ if (keyword.trim()) {
+ stack.push(keyword.trim())
+ keyword = ""
+ }
+ } else {
+ if (keyword) {
+ stack.push(keyword)
+ keyword = ""
+ }
+ stack.push(" ")
+ }
} else if (character === ",") {
- if (keyword) {
- stack.push(keyword)
+ if (keyword.trim()) {
+ stack.push(keyword.trim())
keyword = ""
}
} else if (character === ")") {
- if (keyword) {
- stack.push(keyword)
+ parenthesisDepth--
+ if (keyword.trim()) {
+ stack.push(keyword.trim())
keyword = ""
}
@@ -1337,30 +1449,35 @@ class EventHandler {
if (arg === "(") {
isFuncFound = true
- arg = stack.pop() // func name
+ arg = stack.pop()
args.unshift(arg)
} else {
- args.unshift(arg)
+ if (arg !== "" && arg !== " ") {
+ args.unshift(arg)
+ }
}
-
}
if (isFuncFound) {
let funcName = args.shift()
if (this.hasFunction(funcName)) {
let result = this.runFunction(funcName, args)
- stack.push(result)
+ stack.push(typeof result === 'object' ? JSON.stringify(result) : String(result))
} else {
this.queueLog({ type: 'error', message: "Does not have function named: " + funcName })
}
- } else {
}
} else {
keyword += character
}
}
- return stack[0]
+ if (keyword) {
+ stack.push(keyword)
+ }
+
+ let finalResult = stack.map(item => typeof item === 'string' ? item : String(item)).join("")
+ return finalResult.trim()
}
interpolate(value, params, options = {}) {
diff --git a/packages/junon-io/server/entities/player.js b/packages/junon-io/server/entities/player.js
index ec0f5b15..4f5a75fc 100644
--- a/packages/junon-io/server/entities/player.js
+++ b/packages/junon-io/server/entities/player.js
@@ -712,11 +712,27 @@ class Player extends BaseEntity {
})
}
- toggleFly() {
- this.isFlying = !this.isFlying
- let message = this.isFlying ? "Flying mode enabled" : "Flying mode disabled"
+ toggleFly(currState) {
+
+ let nextState = currState
+ let prevState = this.isFlying
- this.showError(message, { isSuccess: true, fontSize: 36 })
+ if (nextState == null) {
+ nextState = !this.isFlying
+ } else {
+ if (nextState == "true") {
+ nextState = true
+ } else {
+ nextState = false
+ }
+ }
+
+ if (prevState !== nextState) {
+ this.isFlying = nextState
+ let message = this.isFlying ? "Flying mode enabled" : "Flying mode disabled"
+
+ this.showError(message, { isSuccess: true, fontSize: 36 })
+ }
}
onRoleAssigned() {
@@ -2000,6 +2016,7 @@ class Player extends BaseEntity {
return
}
+
const isSuccess = storage.craft(item, this.inventory.storage)
if (!isSuccess) return
@@ -4410,7 +4427,7 @@ class Player extends BaseEntity {
message = i18n.t(this.locale, message)
options.message = message
this.getSocketUtil().emit(this.getSocket(), "ErrorMessage", options)
- }
+ }
showChatError(message) {
this.getSocketUtil().emit(this.getSocket(), "ServerChat", { message: "%error%" + message })
@@ -5529,14 +5546,15 @@ class Player extends BaseEntity {
if (data.isGlobal) {
LOG.info("globalchat> " + this.name + ": " + message)
- let data = { username: this.name, message: message }
+ let data = { username: this.name, message: message, prefixesList:JSON.stringify(this.game.playerChatPrefixes||{})}
if (this.isLoggedIn()) {
data.uid = this.getUid()
}
this.game.sendToMatchmaker({ event: "GlobalClientChat", data: data })
} else {
LOG.info("[" + this.game.getSectorUid() + "] chat> " + this.name + ": " + message)
- let data = { playerId: this.id, message: message, username: this.name, isTeam: isTeamChat }
+ console.log(this.game.playerChatPrefixes)
+ let data = { playerId: this.id, message: message, username: this.name, isTeam: isTeamChat,prefixesList:JSON.stringify(this.game.playerChatPrefixes||{})}
if (this.isLoggedIn()) {
data.uid = this.getUid()
}
@@ -5999,7 +6017,7 @@ Object.assign(Player.prototype, Destroyable.prototype, {
{
this.removePendingItem()
}
-
+
this.getSocketUtil().broadcast(this.game.getSocketIds(), "PlayerDestroyed", { id: this.id, canRespawn: this.canRespawn(), restartCooldown: this.getRespawnCooldown() })
EventBus.dispatch(`${this.game.getId()}:entity:died:${this.getId()}`, this)
diff --git a/packages/junon-io/server/entities/sector.js b/packages/junon-io/server/entities/sector.js
index eff969e3..ce9b83e0 100644
--- a/packages/junon-io/server/entities/sector.js
+++ b/packages/junon-io/server/entities/sector.js
@@ -398,6 +398,7 @@ class Sector {
isMutantEnabled: true,
isGravityEnabled: false,
isFireSpreadEnabled: firespread,
+ isSpectateAllowed: true,
}
if (!entities) return
@@ -803,13 +804,23 @@ class Sector {
this.getSocketUtil().broadcast(this.game.getSocketIds(), "SectorUpdated", {
settings: this.settings
})
+
+ if (key === "isSpectateAllowed") {
+ if (value === false) {
+ this.game.forEachPlayer((player) => {
+ if (player.ghost) {
+ player.possess(player)
+ }
+ })
+ }
+ }
}
}
canEditSetting(key) {
if(this.gameMode === 'hardcore' || !this.gameMode) return false;
if(this.gameMode === 'survival') {
- let allowedSettingChanges = ['isPvPAllowed',"isFovMode", "isZoomAllowed", "showMiniMap", "showPlayerList", "isFloorAutodirt", "isChatEnabled", "isShadowsEnabled", "isPlayerSavingEnabled", "isBloodEnabled", "isGravityEnabled"]
+ let allowedSettingChanges = ['isPvPAllowed',"isFovMode", "isZoomAllowed", "showMiniMap", "showPlayerList", "isFloorAutodirt", "isChatEnabled", "isShadowsEnabled", "isPlayerSavingEnabled", "isBloodEnabled", "isGravityEnabled","isSpectateAllowed"]
if(allowedSettingChanges.indexOf(key) === -1) return false;
}
@@ -1209,7 +1220,7 @@ class Sector {
}
}
}
-
+
addClaim(entity, claimer) {
new Claim(this, entity, claimer, this.game.timestamp)
@@ -1496,6 +1507,10 @@ class Sector {
return this.settings.isFovMode
}
+ isSpectateAllowed() {
+ return this.settings.isSpectateAllowed
+ }
+
isZoomAllowed() {
if (this.game.isPvP()) return false
return this.settings.isZoomAllowed