Attacks are now pending
This commit is contained in:
+3
-1
@@ -13,7 +13,9 @@ let excluded = [ //messages that should not be logged
|
||||
'Not enough time has passed',
|
||||
|
||||
'Profile sent',
|
||||
'Ladder sent'
|
||||
'Ladder sent',
|
||||
// 'attacking',
|
||||
// 'idle'
|
||||
];
|
||||
|
||||
const log = (msg, ...args) => {
|
||||
|
||||
@@ -14,7 +14,13 @@ The plan is this:
|
||||
* The chance of success is determined by the ratio of each side's combatant strength.
|
||||
* Recruits have a strength equal to 0.25 times that of a soldier.
|
||||
* On a success, you steal 10% of the target's gold. On a failure, you steal 2% of the target's gold.
|
||||
* The attacking force will lose a percentage, rounded down, of their units - 5% on a success, 10% on a failure.
|
||||
* The attacking force will lose a percentage, rounded down, of their units - 5% on a success, 10% on a failure (edit: excluding the first 10 units).
|
||||
|
||||
All of these numbers can be adjusted later, but this is the initial gameplan for combat.
|
||||
|
||||
Edit: More aspects that I'd like to ensure are:
|
||||
|
||||
* If the server resets (which happens alot) combat still progresses as expected.
|
||||
* All combat is logged and presented to the player.
|
||||
* You can only attack one person at a time.
|
||||
|
||||
|
||||
+91
-2
@@ -5,10 +5,99 @@ require('dotenv').config();
|
||||
let { log } = require('../common/utilities.js');
|
||||
|
||||
const attackRequest = (connection) => (req, res) => {
|
||||
res.status(400).write(log('Not yet implemented'));
|
||||
//verify the attacker's credentials
|
||||
let query = 'SELECT accountId FROM sessions WHERE accountId IN (SELECT id FROM accounts WHERE username = ?) AND token = ?;';
|
||||
connection.query(query, [req.body.attacker, req.body.token], (err, results) => {
|
||||
if (err) throw err;
|
||||
|
||||
if (results.length !== 1) {
|
||||
res.status(400).write(log('Invalid attack credentials', req.body.attacker, req.body.defender, req.body.token));
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
let attackerId = results[0].accountId;
|
||||
|
||||
//verify that the defender exists
|
||||
let query = 'SELECT id FROM accounts WHERE username = ?;';
|
||||
connection.query(query, [req.body.defender], (err, results) => {
|
||||
if (err) throw err;
|
||||
|
||||
if (results.length !== 1) {
|
||||
res.status(400).write(log('Invalid defender credentials', req.body.attacker, req.body.defender));
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
let defenderId = results[0].id;
|
||||
|
||||
//verify that the attacker has enough soldiers
|
||||
let query = 'SELECT soldiers FROM profiles WHERE accountId = ?;';
|
||||
connection.query(query, [attackerId], (err, results) => {
|
||||
if (err) throw err;
|
||||
|
||||
if (results[0].soldiers <= 0) {
|
||||
res.status(400).write(log('Not enough soldiers', req.body.attacker, req.body.defender, results[0].soldiers));
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
let attackingUnits = results[0].soldiers;
|
||||
|
||||
//verify that the attacker is not already attacking someone
|
||||
isAttacking(connection, req.body.attacker, (isAttacking) => {
|
||||
if (isAttacking) {
|
||||
res.status(400).write(log('You are already attacking someone', req.body.attacker, req.body.defender));
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
//create the pending attack value
|
||||
let query = 'INSERT INTO pendingCombat (eventTime, attackerId, defenderId, attackingUnits) VALUES (DATE_ADD(CURRENT_TIMESTAMP(), INTERVAL 10 * ? SECOND), ?, ?, ?);';
|
||||
connection.query(query, [attackingUnits, attackerId, defenderId, attackingUnits], (err) => {
|
||||
if (err) throw err;
|
||||
|
||||
res.status(200).write(log(`Your soldiers are on their way to attack ${req.body.defender}`, req.body.attacker, req.body.defender));
|
||||
res.end();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const attackStatusRequest = (connection) => (req, res) => {
|
||||
isAttacking(connection, req.body.username, (isAttacking) => {
|
||||
res.status(200).write(log(isAttacking ? 'attacking' : 'idle', req.body.username));
|
||||
res.end();
|
||||
});
|
||||
}
|
||||
|
||||
const isAttacking = (connection, username, cb) => {
|
||||
let query = 'SELECT * FROM pendingCombat WHERE attackerId IN (SELECT id FROM accounts WHERE username = ?);';
|
||||
connection.query(query, [username], (err, results) => {
|
||||
if (err) throw err;
|
||||
|
||||
return cb(results.length !== 0);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
attackRequest: attackRequest
|
||||
attackRequest: attackRequest,
|
||||
attackStatusRequest: attackStatusRequest
|
||||
}
|
||||
|
||||
/*
|
||||
> You can attack another player using your soldiers (it doesn't work without soldiers).
|
||||
* Doing so takes time, up to 10 seconds for every soldier you have.
|
||||
* Combat takes place at the end of the time delay, at which point you can attack people again (after reloading the page).
|
||||
> While attacking, you are undefended.
|
||||
* While undefended, your recruits act as combatants, otherwise your soldiers do.
|
||||
* The chance of success is determined by the ratio of each side's combatant strength.
|
||||
* Recruits have a strength equal to 0.25 times that of a soldier.
|
||||
* On a success, you steal 10% of the target's gold. On a failure, you steal 2% of the target's gold.
|
||||
* The attacking force will lose a percentage, rounded down, of their units - 5% on a success, 10% on a failure (edit: excluding the first 10 units).
|
||||
* If the server resets (which happens alot) combat still progresses as expected.
|
||||
* All combat is logged and presented to the player.
|
||||
*/
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ profiles.runGoldTick(connection);
|
||||
|
||||
let combat = require('./combat.js');
|
||||
app.post('/attackrequest', combat.attackRequest(connection));
|
||||
app.post('/attackstatusrequest', combat.attackStatusRequest(connection));
|
||||
|
||||
//static directories
|
||||
app.use('/styles', express.static(path.resolve(__dirname + '/../public/styles')) );
|
||||
|
||||
@@ -55,3 +55,41 @@ CREATE TABLE IF NOT EXISTS profiles (
|
||||
|
||||
CONSTRAINT FOREIGN KEY fk_accountId(accountId) REFERENCES accounts(id) ON UPDATE CASCADE ON DELETE CASCADE
|
||||
);
|
||||
|
||||
#combat system
|
||||
CREATE TABLE IF NOT EXISTS pendingCombat (
|
||||
id INTEGER UNSIGNED AUTO_INCREMENT PRIMARY KEY UNIQUE,
|
||||
td TIMESTAMP DEFAULT CURRENT_TIMESTAMP(),
|
||||
|
||||
eventTime TIMESTAMP,
|
||||
|
||||
attackerId INTEGER UNSIGNED,
|
||||
defenderId INTEGER UNSIGNED,
|
||||
attackingUnits INTEGER UNSIGNED,
|
||||
|
||||
CONSTRAINT FOREIGN KEY fk_attackerId(attackerId) REFERENCES accounts(id) ON UPDATE CASCADE ON DELETE CASCADE,
|
||||
CONSTRAINT FOREIGN KEY fk_defenderId(defenderId) REFERENCES accounts(id) ON UPDATE CASCADE ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS pastCombat (
|
||||
id INTEGER UNSIGNED AUTO_INCREMENT PRIMARY KEY UNIQUE,
|
||||
td TIMESTAMP DEFAULT CURRENT_TIMESTAMP(),
|
||||
|
||||
eventTime TIMESTAMP,
|
||||
|
||||
attackerId INTEGER UNSIGNED,
|
||||
defenderId INTEGER UNSIGNED,
|
||||
attackingUnits INTEGER UNSIGNED,
|
||||
defindingUnits INTEGER UNSIGNED,
|
||||
|
||||
undefended BOOLEAN,
|
||||
|
||||
victor ENUM ('attacker', 'defender'),
|
||||
|
||||
spoilsGold INTEGER,
|
||||
|
||||
casualtiesVictor INTEGER,
|
||||
|
||||
CONSTRAINT FOREIGN KEY fk_attackerId(attackerId) REFERENCES accounts(id) ON UPDATE CASCADE ON DELETE CASCADE,
|
||||
CONSTRAINT FOREIGN KEY fk_defenderId(defenderId) REFERENCES accounts(id) ON UPDATE CASCADE ON DELETE CASCADE
|
||||
);
|
||||
@@ -218,7 +218,7 @@ class Profile extends React.Component {
|
||||
<div className='row'>
|
||||
<p className='col'>Recruits:</p>
|
||||
<p className='col'>{this.state.recruits}</p>
|
||||
<AttackButton className='col' style={{flex: '2 1 1.5%'}} setWarning={this.setWarning.bind(this)} attacker={this.props.username} defender={this.state.username} />
|
||||
<AttackButton className='col' style={{flex: '2 1 1.5%'}} setWarning={this.setWarning.bind(this)} attacker={this.props.username} defender={this.state.username} token={this.props.token} />
|
||||
</div>
|
||||
|
||||
<div className='row'>
|
||||
|
||||
@@ -8,15 +8,23 @@ class AttackButton extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
//
|
||||
message: ''
|
||||
};
|
||||
|
||||
this.sendAttackingStatusRequest();
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.message !== '') {
|
||||
return (
|
||||
<p>{this.state.message}</p>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<button className={this.props.className} style={this.props.style} onClick={this.sendAttackRequest.bind(this)} disabled={this.props.disabled}>Attack</button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
sendAttackRequest() {
|
||||
//build the XHR
|
||||
@@ -26,7 +34,7 @@ class AttackButton extends React.Component {
|
||||
xhr.onreadystatechange = () => {
|
||||
if (xhr.readyState === 4) {
|
||||
if (xhr.status === 200) {
|
||||
//DO NOTHING
|
||||
this.setState({message: xhr.responseText});
|
||||
} else if (xhr.status === 400) {
|
||||
if (this.props.setWarning) {
|
||||
this.props.setWarning(xhr.responseText);
|
||||
@@ -38,7 +46,8 @@ class AttackButton extends React.Component {
|
||||
xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
|
||||
xhr.send(JSON.stringify({
|
||||
attacker: this.props.attacker,
|
||||
defender: this.props.defender
|
||||
defender: this.props.defender,
|
||||
token: this.props.token
|
||||
}));
|
||||
|
||||
if (this.props.onClick) {
|
||||
@@ -47,6 +56,26 @@ class AttackButton extends React.Component {
|
||||
|
||||
this.props.setDisabled(true);
|
||||
}
|
||||
|
||||
sendAttackingStatusRequest() {
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', '/attackstatusrequest', true);
|
||||
|
||||
xhr.onreadystatechange = () => {
|
||||
if (xhr.readyState === 4) {
|
||||
if (xhr.status === 200) {
|
||||
if (xhr.responseText === 'attacking') {
|
||||
this.props.setDisabled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
|
||||
xhr.send(JSON.stringify({
|
||||
username: this.props.attacker
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
AttackButton.propTypes = {
|
||||
@@ -56,6 +85,7 @@ AttackButton.propTypes = {
|
||||
setWarning: PropTypes.func,
|
||||
attacker: PropTypes.string.isRequired,
|
||||
defender: PropTypes.string.isRequired,
|
||||
token: PropTypes.number.isRequired,
|
||||
|
||||
disabled: PropTypes.bool.isRequired,
|
||||
setDisabled: PropTypes.func.isRequired
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { SET_ATTACK_DISABLED } from '../actions/combat.js';
|
||||
|
||||
const initialStore = {
|
||||
attackDisabled: true
|
||||
attackDisabled: false
|
||||
};
|
||||
|
||||
export function combatReducer(store = initialStore, action) {
|
||||
|
||||
Reference in New Issue
Block a user