From 8161ebb512fd066871771246f0039eb85846e02b Mon Sep 17 00:00:00 2001
From: Kayne Ruse
Date: Sat, 1 Jun 2019 23:02:08 +1000
Subject: [PATCH] Equipment buying and selling complete
---
common/utilities.js | 9 +-
server/equipment.js | 332 ++++++++++++++++++++++------
server/index.js | 2 +
sql/revert.sql | 0
sql/update.sql | 3 +-
src/components/pages/combat_log.jsx | 2 +
src/components/pages/equipment.jsx | 5 +
src/components/panels/equipment.jsx | 13 +-
8 files changed, 294 insertions(+), 72 deletions(-)
create mode 100644 sql/revert.sql
diff --git a/common/utilities.js b/common/utilities.js
index 61ab191..048c69a 100644
--- a/common/utilities.js
+++ b/common/utilities.js
@@ -21,7 +21,14 @@ let excluded = [ //messages that should not be logged
'News sent',
'Can\'t train while attacking',
- 'Can\'t untrain while attacking'
+ 'Can\'t untrain while attacking',
+ 'Can\'t purchase while attacking',
+ 'Can\'t sell while attacking',
+
+ 'Purchase made',
+ 'Sale made',
+
+ 'Cleaned database'
];
const log = (msg, ...args) => {
diff --git a/server/equipment.js b/server/equipment.js
index 379fb9e..c6dc898 100644
--- a/server/equipment.js
+++ b/server/equipment.js
@@ -4,95 +4,295 @@ require('dotenv').config();
//utilities
let { log } = require('../common/utilities.js');
-const statistics = (connection, req, res, cb) => {
+let { isAttacking } = require('./combat.js');
+
+const getStatistics = (cb) => {
+ //TODO: apiVisible field
return cb(undefined, { 'statistics': require('./equipment_statistics.json') });
};
-const owned = (connection, req, res, cb) => {
+const getOwned = (connection, id, cb) => {
+ let query = 'SELECT name, quantity FROM equipment WHERE accountId = ?;';
+ connection.query(query, [id], (err, results) => {
+ if (err) throw err;
+
+ let ret = {};
+
+ Object.keys(results).map((key) => {
+ if (ret[results[key].name] !== undefined) {
+ log('WARNING: Invalid database state, equipment owned', id, JSON.stringify(results));
+ }
+ ret[results[key].name] = results[key].quantity;
+ });
+
+ return cb(undefined, { 'owned': ret });
+ });
+};
+
+const equipmentRequest = (connection) => (req, res) => {
//validate the credentials
let query = 'SELECT COUNT(*) AS total FROM sessions WHERE accountId = ? AND token = ?;';
connection.query(query, [req.body.id, req.body.token], (err, results) => {
if (err) throw err;
if (results[0].total !== 1) {
- return cb('Invalid equipment owned credentials');
+ res.status(400).write(log('Invalid equipment credentials', req.body.id, req.body.token));
+ res.end();
+ return;
}
- let query = 'SELECT name, quantity FROM equipment WHERE accountId = ?;';
- connection.query(query, [req.body.id], (err, results) => {
- if (err) throw err;
-
- let res = {}
-
- Object.keys(results).map((key) => {
- if (res[results[key].name] !== undefined) {
- log('WARNING: Invalid database state, equipment owned', JSON.stringify(results));
- }
- res[results[key].name] = results[key].quantity;
- });
-
- return cb(undefined, { 'owned': res });
- });
- });
-};
-
-const equipmentRequest = (connection) => (req, res) => {
- //if no field received, send everything
- if (!req.body.field) {
- //compose the returned objects
- statistics(connection, req, res, (err, statisticsObj) => {
- if (err) {
- res.status(400).write(log(err, req.body.id, req.body.token, req.body.field));
- res.end();
- return;
- }
-
- return owned(connection, req, res, (err, ownedObj) => {
+ //if no field received, send everything
+ if (!req.body.field) {
+ //compose the returned objects
+ return getStatistics((err, statisticsObj) => {
if (err) {
res.status(400).write(log(err, req.body.id, req.body.token, req.body.field));
res.end();
return;
}
- //finally, compose the resulting objects
- res.status(200).json(Object.assign({}, statisticsObj, ownedObj));
- res.end();
+ return getOwned(connection, req.body.id, (err, ownedObj) => {
+ if (err) {
+ res.status(400).write(log(err, req.body.id, req.body.token, req.body.field));
+ res.end();
+ return;
+ }
+
+ //finally, compose the resulting objects
+ res.status(200).json(Object.assign({}, statisticsObj, ownedObj));
+ res.end();
+ });
});
- });
+ }
- return;
- }
+ //send specific fields
+ switch(req.body.field) {
+ case 'statistics':
+ return getStatistics((err, obj) => {
+ if (err) {
+ res.status(400).write(log(err, req.body.id, req.body.token, req.body.field));
+ } else {
+ res.status(200).json(obj);
+ }
- //send specific fields
- switch(req.body.field) {
- case 'statistics':
- return statistics(connection, req, res, (err, obj) => {
- if (err) {
- res.status(400).write(log(err, req.body.id, req.body.token, req.body.field));
- } else {
- res.status(200).json(obj);
- }
+ res.end();
+ });
+ case 'owned':
+ return getOwned(connection, req.body.id, (err, obj) => {
+ if (err) {
+ res.status(400).write(log(err, req.body.id, req.body.token, req.body.field));
+ } else {
+ res.status(200).json(obj);
+ }
+
+ res.end();
+ });
+
+ default:
+ res.status(400).write(log('Unknown field received', req.body.id, req.body.token, req.body.field));
res.end();
- });
-
- case 'owned':
- return owned(connection, req, res, (err, obj) => {
- if (err) {
- res.status(400).write(log(err, req.body.id, req.body.token, req.body.field));
- } else {
- res.status(200).json(obj);
- }
-
- res.end();
- });
-
- default:
- res.status(400).write(log('Unknown field received', req.body.id, req.body.token, req.body.field));
- res.end();
- }
+ }
+ });
};
+const purchaseRequest = (connection) => (req, res) => {
+ //validate the credentials
+ let query = 'SELECT COUNT(*) AS total FROM sessions WHERE accountId = ? AND token = ?;';
+ connection.query(query, [req.body.id, req.body.token], (err, credentials) => {
+ if (err) throw err;
+
+ if (credentials[0].total !== 1) {
+ res.status(400).write(log('Invalid equipment purchase credentials', JSON.stringify(body), body.id, body.token));
+ res.end();
+ return;
+ }
+
+ //no purchasing if you're attacking
+ isAttacking(connection, req.body.id, (err, attacking) => {
+ if (err) throw err;
+
+ if (attacking) {
+ res.status(400).write(log('Can\'t purchase while attacking', req.body.id, req.body. token, req.body.type, req.body.name));
+ res.end();
+ return;
+ }
+
+ //get the player's gold
+ let query = 'SELECT gold, scientists FROM profiles WHERE accountId = ?;';
+ connection.query(query, [req.body.id], (err, results) => {
+ if (err) throw err;
+
+ //just in case
+ if (results.length === 0) {
+ res.status(400).write(log('Purchase made on unrecognized account', req.body.id, req.body.token));
+ res.end();
+ return;
+ }
+
+ //get the stats for all objects
+ getStatistics((err, { statistics }) => {
+ if (err) throw err;
+
+ //valid parameters
+ if(!statistics[req.body.type] || !statistics[req.body.type][req.body.name]) {
+ res.status(400).write(log('Invalid equipment purchase parameters', req.body.id, req.body.token, req.body.type, req.body.name));
+ res.end();
+ return;
+ }
+
+ //enough gold?
+ if (results[0].gold < statistics[req.body.type][req.body.name].cost) {
+ res.status(400).write(log('Not enough gold', req.body.id, req.body.token, req.body.type, req.body.name));
+ res.end();
+ return;
+ }
+
+ //for sale?
+ if (!statistics[req.body.type][req.body.name].visible || !statistics[req.body.type][req.body.name].purchasable) {
+ res.status(400).write(log('Item not for sale', req.body.id, req.body.token, req.body.type, req.body.name));
+ res.end();
+ return;
+ }
+
+ //high enough level?
+ if (results[0].scientists < statistics[req.body.type][req.body.name].scientistsRequired) {
+ res.status(400).write(log('Not enough scientists', req.body.id, req.body.token, req.body.type, req.body.name));
+ res.end();
+ return;
+ }
+
+ //purchase approved.
+
+ //get the user's current item data (including quantity)
+ let query = 'SELECT * FROM equipment WHERE accountId = ? AND name = ?;';
+ connection.query(query, [req.body.id, req.body.name], (err, results) => {
+ if (err) throw err;
+
+ //add to or update the record
+ let query;
+ if (results.length > 0) {
+ query = 'UPDATE equipment SET quantity = quantity + 1 WHERE accountId = ? AND name = ? AND type = ?;';
+ } else {
+ query = 'INSERT INTO equipment (accountId, name, type, quantity) VALUES (?, ?, ?, 1);';
+ }
+
+ connection.query(query, [req.body.id, req.body.name, req.body.type], (err) => {
+ if (err) throw err;
+
+ //remove gold from the user's account
+ let query = 'UPDATE profiles SET gold = gold - ? WHERE accountId = ?;';
+ connection.query(query, [statistics[req.body.type][req.body.name].cost, req.body.id], (err) => {
+ if (err) throw err;
+
+ //return the new owned data
+ getOwned(connection, req.body.id, (err, results) => {
+ if (err) throw err;
+
+ res.status(200).json(Object.assign(results));
+ res.end();
+
+ log('Purchase made', req.body.id, req.body.token, req.body.type, req.body.name);
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+}
+
+const sellRequest = (connection) => (req, res) => {
+ //validate the credentials
+ let query = 'SELECT COUNT(*) AS total FROM sessions WHERE accountId = ? AND token = ?;';
+ connection.query(query, [req.body.id, req.body.token], (err, credentials) => {
+ if (err) throw err;
+
+ if (credentials[0].total !== 1) {
+ res.status(400).write(log('Invalid equipment sell credentials', JSON.stringify(body), body.id, body.token));
+ res.end();
+ return;
+ }
+
+ //no selling if you're attacking
+ isAttacking(connection, req.body.id, (err, attacking) => {
+ if (err) throw err;
+
+ if (attacking) {
+ res.status(400).write(log('Can\'t sell while attacking', req.body.id, req.body. token, req.body.type, req.body.name));
+ res.end();
+ return;
+ }
+
+ //get the player's item quantity
+ let query = 'SELECT * FROM equipment WHERE accountId = ? AND type = ? AND name = ?;';
+ connection.query(query, [req.body.id, req.body.type, req.body.name], (err, results) => {
+ if (err) throw err;
+
+ if (results.length === 0) {
+ res.status(400).write(log('Can\'t sell something you don\'t own', req.body.id, req.body. token, req.body.type, req.body.name));
+ res.end();
+ return;
+ }
+
+ //get the stats for all objects
+ getStatistics((err, { statistics }) => {
+ if (err) throw err;
+
+ //valid parameters
+ if(!statistics[req.body.type] || !statistics[req.body.type][req.body.name]) {
+ res.status(400).write(log('Invalid equipment sell parameters', req.body.id, req.body.token, req.body.type, req.body.name));
+ res.end();
+ return;
+ }
+
+ //for sale?
+ if (!statistics[req.body.type][req.body.name].saleable) {
+ res.status(400).write(log('Item can\'t be sold', req.body.id, req.body.token, req.body.type, req.body.name));
+ res.end();
+ return;
+ }
+
+ //sale approved.
+
+ //add gold to the user's account
+ let query = 'UPDATE profiles SET gold = gold + ? WHERE accountId = ?;';
+ connection.query(query, [Math.floor(statistics[req.body.type][req.body.name].cost/2), req.body.id], (err) => {
+ if (err) throw err;
+
+ //remove the item from the inventory
+ let query = 'UPDATE equipment SET quantity = quantity - 1 WHERE id = ?;';
+ connection.query(query, [results[0].id], (err) => {
+ if (err) throw err;
+
+ //return the new owned data
+ getOwned(connection, req.body.id, (err, results) => {
+ if (err) throw err;
+
+ res.status(200).json(Object.assign(results));
+ res.end();
+
+ log('Sale made', req.body.id, req.body.token, req.body.type, req.body.name);
+
+ //Extra: clean the database
+ let query = 'DELETE FROM equipment WHERE quantity <= 0;';
+ connection.query(query, (err) => {
+ if (err) throw err;
+
+ log('Cleaned database', 'equipment sale');
+ });
+ });
+ })
+ });
+ });
+ });
+ });
+ });
+}
+
module.exports = {
- equipmentRequest: equipmentRequest
+ equipmentRequest: equipmentRequest,
+ purchaseRequest: purchaseRequest,
+ sellRequest, sellRequest
};
\ No newline at end of file
diff --git a/server/index.js b/server/index.js
index 48709c8..8ae518b 100644
--- a/server/index.js
+++ b/server/index.js
@@ -49,6 +49,8 @@ combat.runCombatTick(connection);
let equipment = require('./equipment.js');
app.post('/equipmentrequest', equipment.equipmentRequest(connection));
+app.post('/equipmentpurchaserequest', equipment.purchaseRequest(connection));
+app.post('/equipmentsellrequest', equipment.sellRequest(connection));
//static directories
app.use('/styles', express.static(path.resolve(__dirname + '/../public/styles')) );
diff --git a/sql/revert.sql b/sql/revert.sql
new file mode 100644
index 0000000..e69de29
diff --git a/sql/update.sql b/sql/update.sql
index 239a293..93c9cbd 100644
--- a/sql/update.sql
+++ b/sql/update.sql
@@ -1 +1,2 @@
-ALTER TABLE pastCombat CHANGE casualtiesVictor attackerCasualties INTEGER;
\ No newline at end of file
+#NOTE: ALWAYS, ALWAYS, ALWAYS write a script in revert.sql that undoes these changes
+
diff --git a/src/components/pages/combat_log.jsx b/src/components/pages/combat_log.jsx
index 73b7036..65a23ff 100644
--- a/src/components/pages/combat_log.jsx
+++ b/src/components/pages/combat_log.jsx
@@ -54,6 +54,8 @@ class CombatLog extends React.Component {
{this.state.warning}
+ Combat Log
+
{this.state.warning}
+ Equipment
+ Equipment sells for half it's cost!
+ Scientists: {this.props.scientists} - Gold: {this.props.gold}
+
this.sendRequest('/profilerequest', {username: this.props.username}) }
/>
diff --git a/src/components/panels/equipment.jsx b/src/components/panels/equipment.jsx
index 4bca395..733f7aa 100644
--- a/src/components/panels/equipment.jsx
+++ b/src/components/panels/equipment.jsx
@@ -46,8 +46,8 @@ class Equipment extends React.Component {
{display[key].type}
{display[key].owned}
{display[key].cost}
- {display[key].purchasable ? : }
- {display[key].saleable ? : }
+ {display[key].purchasable ? : }
+ {display[key].saleable ? : }
)}
@@ -67,9 +67,13 @@ class Equipment extends React.Component {
//on success
this.setState(json);
+
+ if (this.props.onSuccess) {
+ this.props.onSuccess(json);
+ }
}
else if (xhr.status === 400 && this.props.setWarning) {
- this.setWarning(xhr.responseText);
+ this.props.setWarning(xhr.responseText);
}
}
};
@@ -121,7 +125,8 @@ Equipment.propTypes = {
token: PropTypes.number.isRequired,
setWarning: PropTypes.func,
- getFetch: PropTypes.func
+ getFetch: PropTypes.func,
+ onSuccess: PropTypes.func
};
const mapStoreToProps = (store) => {