Add sway tracker to negotiations

This commit is contained in:
Kienan Stewart 2020-04-20 01:42:52 -04:00
parent 87b160e950
commit f1d23e78a7
9 changed files with 247 additions and 4 deletions

View File

@ -108,14 +108,36 @@ def handle_join_negotiation(json):
json['room'], len(participants)))
flask_socketio.emit('participants changed', {'participants': participants},
room = json['room'])
return True
# Update the user's negotation state with what is stored on the server
nego = get_negotiation(json['room'])
return nego.to_dict()
def get_room_participants(room):
if '/' not in socketio.server.manager.get_namespaces():
return []
sessions = [s for s in socketio.server.manager.get_participants('/', room)]
participants = [p for s,p in session_map.items() if s in sessions]
app.logger.debug(participants)
return participants
@socketio.on('update negotiation')
@pony.orm.db_session
def handle_update_negotiation(json):
r = flask.Response()
app.logger.info('Received update request: {}'.format(json))
uid, user = get_uid(r)
nego = get_negotiation(json['room'])
app.logger.info('Room owner is {}'.format(nego.owner.uid))
if nego.owner.uid != uid:
# Refuse the update from non-owners
app.logger.warning('Refusing update of {} from non-owner {}'.format(json['room'], uid))
return False
del json['room']
nego.set(**json)
pony.orm.commit()
flask_socketio.emit('negotiation updated', {**json}, room = nego.name)
return True
@socketio.on('leave negotiation')
def handle_leave_disconnect(json):
r = flask.Response()
@ -186,9 +208,64 @@ if __name__ == '__main__':
uid = pony.orm.Required(str)
display_name = pony.orm.Optional(str)
negotiations = pony.orm.Set('Negotiation')
# Negotiations have a state. Maybe storing a state machine object (pickle?)
# would be an idea. But maybe not.
#
# Let's start with understanding negotiations
# 1. Prep Work: Before a negotiation begins, each take may do Prep Work
# 2. The takers decide who their Lead Negotiator will be. Once set, this
# cannot be changed.
# 3. The Lead Negotiation makes a First Impression
# This is a leadership check which has four effects:
# 1. The number of rounds of negotiation: max(floor(black+ld/2), 5)
# 2. The starting position (crit success +1 sway, crit failure: -1 sway)
# 3. Whether or not the negotiation length is known to takers. T/F based
# simple succes.
# 4. Who leads the negotiations (starting round): takers on success, Market otherwise.
# 4. Negotiation Round(s)
# 1. Lead negotiator plays a negotiation tactic
# 2. Other negotiator plays a negotiation tactic
# 3. Check negotiation end condition
# 4. If not ended, a non-negotiator taker may play scam
# 5. Wrap-up
# 1. Taker negotiator rolls Leadership. Success moves up to meet Market,
# failure moves market down to meet takers.
# 2a. Takers may accept the deal.
# 2b. Takers may back out. Next negotiation starts on lowest tracker, has
# no prep work, scams, or will.
# 3. Competition undercuts. If able, the competition will undercut by one
# spot on the way tracker. _Any_ taker may make a CHA check to stop this,
# but on failure the takers must lower theire price to match the competition.
#
class Negotiation(db.Entity):
name = pony.orm.PrimaryKey(str)
owner = pony.orm.Required(User)
client_name = pony.orm.Optional(str)
negotiatior_name = pony.orm.Optional(str)
taker_crew_name = pony.orm.Optional(str)
# Manual negotation
manual_negotiation = pony.orm.Optional(bool)
# Pre-negotiation details
takers = pony.orm.Optional(int)
lead_negotiator = pony.orm.Optional(str)
is_first_negotiation = pony.orm.Optional(bool)
first_impression_black = pony.orm.Optional(int)
# One of critfail, fail, success, critsuccess
first_impression_state = pony.orm.Optional(str)
negotiation_round = pony.orm.Optional(int)
# One of leadnego, secondnego, scam
negotiation_phase = pony.orm.Optional(str)
taker_sway = pony.orm.Optional(int)
market_sway = pony.orm.Optional(int)
# If negotiation_round = (calculed length) && negotiation_phase = scam then
# the negotiation is no into the wrap up section
# One of critfail, fail, success, critsuccess
wrapup_state = pony.orm.Optional(int)
job_accepted = pony.orm.Optional(bool)
may_be_undercut = pony.orm.Optional(bool)
undercut = pony.orm.Optional(bool)
db.bind(**app.config['PONY'])
db.generate_mapping(create_tables=True)
Pony(app)

View File

@ -964,6 +964,53 @@ img {
margin: 0;
float: right;
}
.negotiation-wrapper {
display: flex;
}
.negotiation-sidebar {
max-width: 30%;
}
.negotiation-panel {
min-width: 70%;
}
/* maybe this should be grids */
.swaytracker .slot {
min-height: 50px;
min-width: 150px;
max-width: 13%;
border: 1px solid;
}
.swaytracker th {
color: #600000;
background: white;
border-color: black;
}
.swaytracker .market-position .active {
height: 50px;
background-image: url("/static/images/active-market.png");
background-repeat: no-repeat;
background-position: center;
}
.swaytracker .taker-position .active {
height: 50px;
background-image: url("/static/images/active-taker.png");
background-repeat: no-repeat;
background-position: center;
}
.swaytracker .slot span {
font-weight: bold;
}
.swaytracker .slot p {
margin: 0.5em;
}
#wrapper {
position: relative;
padding-right: 0;

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

BIN
static/images/drag.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -27,9 +27,12 @@
// const negotiation = new Negotiation("{{negotiation.name}}")
var socket = io();
var room = "{{negotiation.name}}";
socket.on('connect', function() {
console.log("Trying to join room " + room);
socket.emit('join negotiation', {"room": room});
socket.emit('join negotiation', {"room": room}, function(e) {
update_from_data(e);
});
});
window.addEventListener('beforeunload', function(e) {
console.log("Trying to leave room " + room);
@ -60,5 +63,65 @@
}
}
});
socket.on('negotiation updated', function(e) {
update_from_data(e);
});
function update_from_data(e) {
console.log(e);
if ('taker_sway' in e) {
// Update taker way
var current_taker_sway = $('.swaytracker .taker-position .active').attr('id');
change_swayslot(current_taker_sway, 't' + e.taker_sway);
}
if ('market_sway' in e) {
var current_market_sway = $('.swaytracker .market-position .active').attr('id');
change_swayslot(current_market_sway, 'm' + e.market_sway);
}
}
function swayslot_on_dragstart(event) {
var ev = event.originalEvent;
ev.dataTransfer.setData("text/plain", ev.target.id);
let img = new Image();
img.src = '/static/images/drag.png';
ev.dataTransfer.setDragImage(img, 0, 0);
ev.dataTransfer.dropEffect = "move";
}
function swayslot_on_dragover(event) {
event.preventDefault();
event.originalEvent.dataTransfer.dropEffect = "move";
}
function swayslot_on_drop(event) {
event.preventDefault();
var source = event.originalEvent.dataTransfer.getData('text/plain');
// Check to see if this is in the same track (id starts with same letter)
var this_id = $(this).attr('id');
if (this_id[0] == source[0] && this_id != source) {
var key = (this_id[0] == 't' ? 'taker_sway' : 'market_sway');
var value = this_id[1];
var data = {
"room": room,
};
data[key] = value;
socket.emit('update negotiation', data, function (confirmation) {
if (confirmation) {
change_swayslot(source, this_id);
}
});
}
}
function change_swayslot(from, to) {
$("#" + from).removeClass('active').unbind('dragstart').attr('draggable', false);
$('#' + to).addClass('active').on('dragstart', swayslot_on_dragstart).attr('draggable', true);
}
// Drag/drog for the swaytracker
window.addEventListener('DOMContentLoaded', () => {
$('.swaytracker .active').on('dragstart', swayslot_on_dragstart);
$('.swaytracker .active').attr('draggable', true);
$('.swaytracker .market-position .slot').on('dragover', swayslot_on_dragover);
$('.swaytracker .taker-position .slot').on('dragover', swayslot_on_dragover);
$('.swaytracker .market-position .slot').on('drop', swayslot_on_drop);
$('.swaytracker .taker-position .slot').on('drop', swayslot_on_drop);
});
</script>
{% endblock content %}

View File

@ -1 +1,3 @@
@TODO Panel
<div id="swaytracker">
{% include 'partials/swaytracker.html' %}
</div>

View File

@ -1,10 +1,11 @@
<div class="negotiation-owner">
<p>Negotiation {{negotiation.name}} is being run by {{room_owner_display_name}}</p>
<p>The Market for this negotiation is {{room_owner_display_name}}</p>
{% if room_owner_display_name == uid %}
<p>Change your display name</p>
{% endif %}
</div>
{% if
<div class="participants">
<h4>Participants</h4>

View File

@ -0,0 +1,53 @@
<table class="swaytracker">
<tr class="market-position">
<th>Market</th>
<td id="m0" class="slot"></td>
<td id="m1" class="slot"></td>
<td id="m2" class="slot"></td>
<td id="m3" class="slot"></td>
<td id="m4" class="slot"></td>
<td id="m5" class="slot"></td>
<td id="m6" class="slot active"></td>
</tr>
<tr class="descriptions">
<th></th>
<td class="slot">
<span>As a favour</span>
<p>Contract is offered at the Demand price only and the client (market) earns a "- Rep" spot to use in future negotiations.</p>
</td>
<td class="slot">
<span>Buyer's Market</span>
<p>Contract is offered at the Demand price only.</p>
</td>
<td class="slot">
<span>At Value</span>
<p>Contract is offered at the value of Supply and Demand</p>
</td>
<td class="slot">
<span>Labour</span>
<p>Client agrees to add the Crew's "Break Point" in bounty to the value of Supply and Demand</p>
</td>
<td class="slot">
<span>Hazard Pay</span>
<p>Add one bounty per leg per taker to the value of the contract</p>
</td>
<td class="slot">
<span>100% Markup</span>
<p>Double the cost of the Supply and Demand for the job</p>
</td>
<td class="slot">
<span>Expenses</span>
<p>The upkeep of items is factored into the value of the contract. Takers pay no upkeep this session.</p>
</td>
</tr>
<tr class="taker-position">
<th>Takers</th>
<td id="t0" class="slot"></td>
<td id="t1" class="slot active"></td>
<td id="t2" class="slot"></td>
<td id="t3" class="slot"></td>
<td id="t4" class="slot"></td>
<td id="t5" class="slot"></td>
<td id="t6" class="slot"></td>
</tr>
</table>