2018-10-02 23:54:56 +02:00
/ *
gang member upgrades - they should be cheaper as the gang gets more respect / power
kopelli09 / 12 / 2018
Another gang - related idea ( and perhaps I 'm not seeing it in the code) - gangs can lose power. Seems odd that the player' s power can drop by removing members , but the other gangs are forever gaining power ...
Grub09 / 12 / 2018
Maybe add a % chance of other gangs clashing ?
assign gangs a number of gang members and each clash kills a number of gang members based on each one ' s power
and they lose a proportionate number of members
Also add police clashes
balance point to keep them from running out of control
* /
import { gangMemberTasksMetadata } from "./data/gangmembertasks" ;
import { gangMemberUpgradesMetadata } from "./data/gangmemberupgrades" ;
2018-06-26 18:34:11 +02:00
import { Engine } from "./engine" ;
2018-03-27 02:46:21 +02:00
import { Faction , Factions ,
2018-06-26 18:34:11 +02:00
displayFactionContent } from "./Faction" ;
import { Player } from "./Player" ;
2018-09-12 17:53:08 +02:00
import { numeralWrapper } from "./ui/numeralFormat" ;
2018-06-26 18:34:11 +02:00
import { dialogBoxCreate } from "../utils/DialogBox" ;
2017-08-30 19:44:29 +02:00
import { Reviver , Generic _toJSON ,
2018-06-26 18:34:11 +02:00
Generic _fromJSON } from "../utils/JSONReviver" ;
2018-10-03 01:01:37 +02:00
import { KEY } from "../utils/helpers/keyCodes" ;
2018-07-08 06:38:13 +02:00
import { createAccordionElement } from "../utils/uiHelpers/createAccordionElement" ;
2018-07-05 06:38:00 +02:00
import { createElement } from "../utils/uiHelpers/createElement" ;
2018-07-05 19:09:00 +02:00
import { createPopup } from "../utils/uiHelpers/createPopup" ;
2018-07-20 03:51:18 +02:00
import { Page , routing } from "./ui/navigationTracking" ;
2018-06-22 23:30:24 +02:00
import { formatNumber } from "../utils/StringHelperFunctions" ;
2018-10-02 23:54:56 +02:00
import { exceptionAlert } from "../utils/helpers/exceptionAlert" ;
2018-07-05 02:35:33 +02:00
import { getRandomInt } from "../utils/helpers/getRandomInt" ;
2018-07-08 06:00:57 +02:00
import { removeChildrenFromElement } from "../utils/uiHelpers/removeChildrenFromElement" ;
2018-07-08 05:42:58 +02:00
import { removeElement } from "../utils/uiHelpers/removeElement" ;
2018-07-05 19:36:02 +02:00
import { removeElementById } from "../utils/uiHelpers/removeElementById" ;
2017-08-30 19:44:29 +02:00
import { yesNoBoxCreate , yesNoTxtInpBoxCreate ,
yesNoBoxGetYesButton , yesNoBoxGetNoButton ,
yesNoTxtInpBoxGetYesButton , yesNoTxtInpBoxGetNoButton ,
yesNoTxtInpBoxGetInput , yesNoBoxClose ,
2018-06-26 18:34:11 +02:00
yesNoTxtInpBoxClose , yesNoBoxOpen } from "../utils/YesNoBox" ;
2017-08-30 19:44:29 +02:00
2018-10-02 18:45:00 +02:00
// Constants
2018-10-15 02:28:44 +02:00
const GangRespectToReputationRatio = 2 ; // Respect is divided by this to get rep gain
const MaximumGangMembers = 50 ;
2018-10-02 18:45:00 +02:00
const GangRecruitCostMultiplier = 2 ;
2018-10-18 21:52:02 +02:00
const CyclesPerTerritoryAndPowerUpdate = 100 ;
2018-10-15 02:28:44 +02:00
const AscensionMultiplierRatio = 10 / 100 ; // Portion of upgrade multiplier that is kept after ascending
2018-10-02 18:45:00 +02:00
// Switch between territory and management screen with 1 and 2
2017-08-13 07:01:33 +02:00
$ ( document ) . keydown ( function ( event ) {
2018-10-15 02:28:44 +02:00
if ( routing . isOn ( Page . Gang ) && event . altKey ) {
if ( UIElems . gangMemberFilter != null && UIElems . gangMemberFilter === document . activeElement ) { return ; }
if ( event . keyCode === KEY [ "1" ] ) {
if ( UIElems . gangTerritorySubpage . style . display === "block" ) {
UIElems . managementButton . click ( ) ;
2017-08-13 07:01:33 +02:00
}
2018-10-15 02:28:44 +02:00
} else if ( event . keyCode === KEY [ "2" ] ) {
if ( UIElems . gangManagementSubpage . style . display === "block" ) {
UIElems . territoryButton . click ( ) ;
2017-08-13 07:01:33 +02:00
}
}
}
} ) ;
2017-08-20 03:36:19 +02:00
//Delete upgrade box when clicking outside
$ ( document ) . mousedown ( function ( event ) {
2018-02-15 05:26:43 +01:00
var boxId = "gang-member-upgrade-popup-box" ;
var contentId = "gang-member-upgrade-popup-box-content" ;
2018-10-15 02:28:44 +02:00
if ( UIElems . gangMemberUpgradeBoxOpened ) {
2018-02-15 05:26:43 +01:00
if ( $ ( event . target ) . closest ( "#" + contentId ) . get ( 0 ) == null ) {
2017-08-20 03:36:19 +02:00
//Delete the box
2018-10-15 02:28:44 +02:00
removeElement ( UIElems . gangMemberUpgradeBox ) ;
UIElems . gangMemberUpgradeBox = null ;
UIElems . gangMemberUpgradeBoxContent = null ;
UIElems . gangMemberUpgradeBoxOpened = false ;
UIElems . gangMemberUpgradeBoxElements = null ;
2017-08-20 03:36:19 +02:00
}
}
} ) ;
2017-08-30 19:44:29 +02:00
let GangNames = [ "Slum Snakes" , "Tetrads" , "The Syndicate" , "The Dark Army" , "Speakers for the Dead" ,
2017-08-13 07:01:33 +02:00
"NiteSec" , "The Black Hand" ] ;
2018-10-15 02:28:44 +02:00
export let AllGangs = {
2017-08-13 07:01:33 +02:00
"Slum Snakes" : {
power : 1 ,
territory : 1 / 7 ,
} ,
"Tetrads" : {
power : 1 ,
territory : 1 / 7 ,
} ,
"The Syndicate" : {
power : 1 ,
territory : 1 / 7 ,
} ,
"The Dark Army" : {
power : 1 ,
territory : 1 / 7 ,
} ,
"Speakers for the Dead" : {
power : 1 ,
territory : 1 / 7 ,
} ,
"NiteSec" : {
power : 1 ,
territory : 1 / 7 ,
} ,
"The Black Hand" : {
power : 1 ,
territory : 1 / 7 ,
} ,
}
2018-10-15 02:28:44 +02:00
export function resetGangs ( ) {
2017-09-02 22:05:42 +02:00
AllGangs = {
"Slum Snakes" : {
power : 1 ,
territory : 1 / 7 ,
} ,
"Tetrads" : {
power : 1 ,
territory : 1 / 7 ,
} ,
"The Syndicate" : {
power : 1 ,
territory : 1 / 7 ,
} ,
"The Dark Army" : {
power : 1 ,
territory : 1 / 7 ,
} ,
"Speakers for the Dead" : {
power : 1 ,
territory : 1 / 7 ,
} ,
"NiteSec" : {
power : 1 ,
territory : 1 / 7 ,
} ,
"The Black Hand" : {
power : 1 ,
territory : 1 / 7 ,
} ,
}
}
2018-10-15 02:28:44 +02:00
export function loadAllGangs ( saveString ) {
2017-08-30 19:44:29 +02:00
AllGangs = JSON . parse ( saveString , Reviver ) ;
}
2018-10-18 21:52:02 +02:00
/ * *
* @ param facName - Name of corresponding faction
* @ param hacking - Boolean indicating whether or not its a hacking gang
2017-08-13 07:01:33 +02:00
* /
2018-10-15 02:28:44 +02:00
export function Gang ( facName , hacking = false ) {
2017-08-13 07:01:33 +02:00
this . facName = facName ;
this . members = [ ] ; //Array of GangMembers
this . wanted = 1 ;
this . respect = 1 ;
2017-08-15 22:22:46 +02:00
this . power = 0 ;
2017-08-13 07:01:33 +02:00
this . isHackingGang = hacking ;
this . respectGainRate = 0 ;
this . wantedGainRate = 0 ;
this . moneyGainRate = 0 ;
2018-10-18 21:52:02 +02:00
// When processing gains, this stores the number of cycles until some
// limit is reached, and then calculates and applies the gains only at that limit
2017-08-13 07:01:33 +02:00
this . storedCycles = 0 ;
2018-10-18 21:52:02 +02:00
// Separate variable to keep track of cycles for Territry + Power gang, which
// happens on a slower "clock" than normal processing
this . storedTerritoryAndPowerCycles = 0 ;
this . territoryClashChance = 0 ;
this . territoryWarfareEngaged = false ;
2017-08-13 07:01:33 +02:00
}
Gang . prototype . process = function ( numCycles = 1 ) {
2018-10-15 02:28:44 +02:00
const CyclesPerSecond = 1000 / Engine . _idleSpeed ;
2017-08-13 07:01:33 +02:00
2018-10-15 02:28:44 +02:00
if ( isNaN ( numCycles ) ) {
console . error ( ` NaN passed into Gang.process(): ${ numCycles } ` ) ;
}
2017-08-13 07:01:33 +02:00
this . storedCycles += numCycles ;
2018-10-15 02:28:44 +02:00
// Only process if there are at least 3 seconds, and at most 10 seconds
if ( this . storedCycles < 3 * CyclesPerSecond ) ;
const cycles = Math . min ( this . storedCycles , 10 * CyclesPerSecond ) ;
try {
this . processGains ( cycles ) ;
this . processExperienceGains ( cycles ) ;
2018-10-18 21:52:02 +02:00
this . processTerritoryAndPowerGains ( cycles ) ;
2018-10-15 02:28:44 +02:00
this . storedCycles -= cycles ;
} catch ( e ) {
exceptionAlert ( ` Exception caught when processing Gang: ${ e } ` ) ;
2017-08-13 07:01:33 +02:00
}
2018-10-15 02:28:44 +02:00
}
Gang . prototype . processGains = function ( numCycles = 1 ) {
2017-08-13 07:01:33 +02:00
//Get gains per cycle
var moneyGains = 0 , respectGains = 0 , wantedLevelGains = 0 ;
for ( var i = 0 ; i < this . members . length ; ++ i ) {
respectGains += ( this . members [ i ] . calculateRespectGain ( ) ) ;
wantedLevelGains += ( this . members [ i ] . calculateWantedLevelGain ( ) ) ;
moneyGains += ( this . members [ i ] . calculateMoneyGain ( ) ) ;
}
this . respectGainRate = respectGains ;
this . wantedGainRate = wantedLevelGains ;
this . moneyGainRate = moneyGains ;
2018-10-02 23:54:56 +02:00
if ( typeof respectGains === "number" ) {
2018-10-15 02:28:44 +02:00
const gain = respectGains * numCycles ;
this . respect += gain ;
2018-10-02 23:54:56 +02:00
// Faction reputation gains is respect gain divided by some constant
2018-10-15 02:28:44 +02:00
const fac = Factions [ this . facName ] ;
2017-08-13 07:01:33 +02:00
if ( ! ( fac instanceof Faction ) ) {
dialogBoxCreate ( "ERROR: Could not get Faction associates with your gang. This is a bug, please report to game dev" ) ;
} else {
var favorMult = 1 + ( fac . favor / 100 ) ;
2018-10-02 18:45:00 +02:00
fac . playerReputation += ( ( Player . faction _rep _mult * gain * favorMult ) / GangRespectToReputationRatio ) ;
2017-08-13 07:01:33 +02:00
}
2018-10-02 23:54:56 +02:00
// Keep track of respect gained per member
for ( let i = 0 ; i < this . members . length ; ++ i ) {
2018-10-15 02:28:44 +02:00
this . members [ i ] . recordEarnedRespect ( numCycles ) ;
2018-10-02 23:54:56 +02:00
}
2017-08-13 07:01:33 +02:00
} else {
2018-10-02 23:54:56 +02:00
console . warn ( "respectGains calculated to be NaN" ) ;
2017-08-13 07:01:33 +02:00
}
2018-10-02 23:54:56 +02:00
if ( typeof wantedLevelGains === "number" ) {
2017-09-05 03:03:29 +02:00
if ( this . wanted === 1 && wantedLevelGains < 0 ) {
2018-10-15 02:28:44 +02:00
// At minimum wanted, do nothing
2017-09-05 03:03:29 +02:00
} else {
2018-10-02 23:54:56 +02:00
const oldWanted = this . wanted ;
2018-10-15 02:28:44 +02:00
let newWanted = oldWanted + ( wantedLevelGains * numCycles ) ;
2018-10-02 23:54:56 +02:00
// Prevent overflow
if ( wantedLevelGains <= 0 && newWanted > oldWanted ) {
newWanted = 1 ;
}
this . wanted = newWanted ;
2017-09-05 03:03:29 +02:00
if ( this . wanted < 1 ) { this . wanted = 1 ; }
}
2017-08-13 07:01:33 +02:00
} else {
2018-10-02 23:54:56 +02:00
console . warn ( "ERROR: wantedLevelGains is NaN" ) ;
2017-08-13 07:01:33 +02:00
}
2018-10-02 23:54:56 +02:00
if ( typeof moneyGains === "number" ) {
2018-10-15 02:28:44 +02:00
Player . gainMoney ( moneyGains * numCycles ) ;
2017-08-13 07:01:33 +02:00
} else {
2018-10-02 23:54:56 +02:00
console . warn ( "ERROR: respectGains is NaN" ) ;
2017-08-13 07:01:33 +02:00
}
}
2018-10-18 21:52:02 +02:00
Gang . prototype . processTerritoryAndPowerGains = function ( numCycles = 1 ) {
this . storedTerritoryAndPowerCycles += numCycles ;
if ( this . storedTerritoryAndPowerCycles < CyclesPerTerritoryAndPowerUpdate ) { return ; }
// Process power first
var gangName = this . facName ;
for ( const name in AllGangs ) {
if ( AllGangs . hasOwnProperty ( name ) ) {
if ( name == gangName ) {
AllGangs [ name ] . power += this . calculatePower ( ) ;
} else {
var gain = Math . random ( ) * 0.02 ; //TODO Adjust as necessary
AllGangs [ name ] . power += ( gain ) ;
}
}
}
// Determine if territory should be processed
if ( ! this . territoryWarfareEngaged ) { return ; }
// Then process territory
for ( var i = 0 ; i < GangNames . length ; ++ i ) {
const others = GangNames . filter ( ( e ) => {
return e !== i ;
} ) ;
const other = getRandomInt ( 0 , others . length - 1 ) ;
const thisGang = GangNames [ i ] ;
const otherGang = others [ other ] ;
// If either of the gangs involved in this clash is the player, determine
// whether to skip or process it using the clash chance
if ( thisGang === gangName || otherGang === gangName ) {
if ( ! ( Math . random ( ) <= this . territoryClashChance ) ) { continue ; }
}
const thisPwr = AllGangs [ thisGang ] . power ;
const otherPwr = AllGangs [ otherGang ] . power ;
const thisChance = thisPwr / ( thisPwr + otherPwr ) ;
if ( Math . random ( ) < thisChance ) {
if ( AllGangs [ otherGang ] . territory <= 0 ) {
return ;
}
AllGangs [ thisGang ] . territory += 0.0001 ;
AllGangs [ otherGang ] . territory -= 0.0001 ;
} else {
if ( AllGangs [ thisGang ] . territory <= 0 ) {
return ;
}
AllGangs [ thisGang ] . territory -= 0.0001 ;
AllGangs [ otherGang ] . territory += 0.0001 ;
}
}
this . storedTerritoryAndPowerCycles -= CyclesPerTerritoryAndPowerUpdate ;
}
2018-10-02 23:54:56 +02:00
Gang . prototype . canRecruitMember = function ( ) {
if ( this . members . length >= MaximumGangMembers ) { return false ; }
return ( this . respect >= this . getRespectNeededToRecruitMember ( ) ) ;
}
Gang . prototype . getRespectNeededToRecruitMember = function ( ) {
2018-10-11 01:13:31 +02:00
// First N gang members are free (can be recruited at 0 respect)
const numFreeMembers = 3 ;
if ( this . members . length < numFreeMembers ) { return 0 ; }
2018-10-02 23:54:56 +02:00
2018-10-11 01:13:31 +02:00
const i = this . members . length - ( numFreeMembers - 1 ) ;
2018-10-02 23:54:56 +02:00
return Math . round ( 0.7 * Math . pow ( i , 3 ) + 0.8 * Math . pow ( i , 2 ) ) ;
}
// Money and Respect gains multiplied by this number (< 1)
Gang . prototype . getWantedPenalty = function ( ) {
return ( this . respect ) / ( this . respect + this . wanted ) ;
}
2017-08-13 07:01:33 +02:00
Gang . prototype . processExperienceGains = function ( numCycles = 1 ) {
for ( var i = 0 ; i < this . members . length ; ++ i ) {
this . members [ i ] . gainExperience ( numCycles ) ;
this . members [ i ] . updateSkillLevels ( ) ;
}
}
2017-08-15 22:22:46 +02:00
//Calculates power GAIN, which is added onto the Gang's existing power
2017-08-13 07:01:33 +02:00
Gang . prototype . calculatePower = function ( ) {
var memberTotal = 0 ;
for ( var i = 0 ; i < this . members . length ; ++ i ) {
if ( this . members [ i ] . task instanceof GangMemberTask &&
this . members [ i ] . task . name == "Territory Warfare" ) {
memberTotal += this . members [ i ] . calculatePower ( ) ;
}
}
2017-08-15 22:22:46 +02:00
return ( 0.0005 * memberTotal ) ;
2017-08-13 07:01:33 +02:00
}
2018-10-02 23:54:56 +02:00
Gang . prototype . killMember = function ( memberObj ) {
// TODO
2018-02-09 22:11:43 +01:00
}
2018-10-02 23:54:56 +02:00
Gang . prototype . ascendMember = function ( memberObj ) {
try {
2018-10-15 02:28:44 +02:00
/ * *
* res is an object with the following format :
* {
* respect : Amount of respect to deduct
* hack / str / def / dex / agi / cha : Ascension multipliers gained for each stat
* }
* /
const res = memberObj . ascend ( ) ;
this . respect = Math . max ( 1 , this . respect - res . respect ) ;
dialogBoxCreate ( [ ` You ascended ${ memberObj . name } ! ` ,
` Your gang lost ${ numeralWrapper . format ( res . respect , "0.000a" ) } respect ` ,
` ${ memberObj . name } gained the following stat multipliers for ascending: ` ,
` Hacking: ${ res . hack } ` ,
` Strength: ${ res . str } ` ,
` Defense: ${ res . def } ` ,
` Dexterity: ${ res . dex } ` ,
` Agility: ${ res . agi } ` ,
` Charisma: ${ res . cha } ` ] . join ( "<br>" ) ) ;
this . displayGangMemberList ( ) ;
2018-10-02 23:54:56 +02:00
} catch ( e ) {
exceptionAlert ( e ) ;
2018-02-09 22:11:43 +01:00
}
}
2017-08-13 07:01:33 +02:00
Gang . prototype . toJSON = function ( ) {
return Generic _toJSON ( "Gang" , this ) ;
}
Gang . fromJSON = function ( value ) {
return Generic _fromJSON ( Gang , value . data ) ;
}
Reviver . constructors . Gang = Gang ;
/*** Gang Member object ***/
function GangMember ( name ) {
this . name = name ;
2018-02-09 22:11:43 +01:00
this . task = GangMemberTasks [ "Unassigned" ] ; //GangMemberTask object
2018-10-02 23:54:56 +02:00
this . earnedRespect = 0 ;
2017-08-13 07:01:33 +02:00
this . hack = 1 ;
this . str = 1 ;
this . def = 1 ;
this . dex = 1 ;
this . agi = 1 ;
this . cha = 1 ;
this . hack _exp = 0 ;
this . str _exp = 0 ;
this . def _exp = 0 ;
this . dex _exp = 0 ;
this . agi _exp = 0 ;
this . cha _exp = 0 ;
this . hack _mult = 1 ;
this . str _mult = 1 ;
this . def _mult = 1 ;
this . dex _mult = 1 ;
this . agi _mult = 1 ;
this . cha _mult = 1 ;
2018-02-09 22:11:43 +01:00
2018-10-02 23:54:56 +02:00
this . hack _asc _mult = 1 ;
this . str _asc _mult = 1 ;
this . def _asc _mult = 1 ;
this . dex _asc _mult = 1 ;
this . agi _asc _mult = 1 ;
this . cha _asc _mult = 1 ;
this . upgrades = [ ] ; //Names of upgrades
this . augmentations = [ ] ; //Names only
2017-08-13 07:01:33 +02:00
}
//Same formula for Player
2018-06-19 06:37:56 +02:00
GangMember . prototype . calculateSkill = function ( exp , mult = 1 ) {
2018-10-02 23:54:56 +02:00
return Math . max ( Math . floor ( mult * ( 32 * Math . log ( exp + 534.5 ) - 200 ) ) , 1 ) ;
2017-08-13 07:01:33 +02:00
}
GangMember . prototype . updateSkillLevels = function ( ) {
2018-10-02 23:54:56 +02:00
this . hack = this . calculateSkill ( this . hack _exp , this . hack _mult * this . hack _asc _mult ) ;
this . str = this . calculateSkill ( this . str _exp , this . str _mult * this . str _asc _mult ) ;
this . def = this . calculateSkill ( this . def _exp , this . def _mult * this . def _asc _mult ) ;
this . dex = this . calculateSkill ( this . dex _exp , this . dex _mult * this . dex _asc _mult ) ;
this . agi = this . calculateSkill ( this . agi _exp , this . agi _mult * this . agi _asc _mult ) ;
this . cha = this . calculateSkill ( this . cha _exp , this . cha _mult * this . cha _asc _mult ) ;
2017-08-13 07:01:33 +02:00
}
GangMember . prototype . calculatePower = function ( ) {
return ( this . hack + this . str + this . def +
this . dex + this . agi + this . cha ) / 100 ;
}
GangMember . prototype . assignToTask = function ( taskName ) {
if ( GangMemberTasks . hasOwnProperty ( taskName ) ) {
this . task = GangMemberTasks [ taskName ] ;
} else {
2018-02-15 05:26:43 +01:00
this . task = GangMemberTasks [ "Unassigned" ] ;
2017-08-13 07:01:33 +02:00
}
}
2018-02-09 22:11:43 +01:00
GangMember . prototype . unassignFromTask = function ( ) {
if ( GangMemberTasks . hasOwnProperty ( "Unassigned" ) ) {
this . task = GangMemberTasks [ "Unassigned" ] ;
} else {
console . log ( "ERROR: Can't find Unassigned Gang member task" ) ;
this . task = null ;
}
}
2017-08-13 07:01:33 +02:00
//Gains are per cycle
GangMember . prototype . calculateRespectGain = function ( ) {
var task = this . task ;
2017-10-26 00:05:12 +02:00
if ( task == null || ! ( task instanceof GangMemberTask ) || task . baseRespect === 0 ) { return 0 ; }
2017-08-13 07:01:33 +02:00
var statWeight = ( task . hackWeight / 100 ) * this . hack +
( task . strWeight / 100 ) * this . str +
( task . defWeight / 100 ) * this . def +
( task . dexWeight / 100 ) * this . dex +
( task . agiWeight / 100 ) * this . agi +
( task . chaWeight / 100 ) * this . cha ;
statWeight -= ( 3.5 * task . difficulty ) ;
2018-10-02 23:54:56 +02:00
if ( statWeight <= 0 ) { return 0 ; }
2017-08-13 07:01:33 +02:00
var territoryMult = AllGangs [ Player . gang . facName ] . territory ;
2018-10-02 23:54:56 +02:00
if ( territoryMult <= 0 ) { return 0 ; }
var respectMult = Player . gang . getWantedPenalty ( ) ;
2017-08-13 07:01:33 +02:00
return 12 * task . baseRespect * statWeight * territoryMult * respectMult ;
}
GangMember . prototype . calculateWantedLevelGain = function ( ) {
var task = this . task ;
2017-10-26 00:05:12 +02:00
if ( task == null || ! ( task instanceof GangMemberTask ) || task . baseWanted === 0 ) { return 0 ; }
2017-08-13 07:01:33 +02:00
var statWeight = ( task . hackWeight / 100 ) * this . hack +
( task . strWeight / 100 ) * this . str +
( task . defWeight / 100 ) * this . def +
( task . dexWeight / 100 ) * this . dex +
( task . agiWeight / 100 ) * this . agi +
( task . chaWeight / 100 ) * this . cha ;
statWeight -= ( 3.5 * task . difficulty ) ;
if ( statWeight <= 0 ) { return 0 ; }
var territoryMult = AllGangs [ Player . gang . facName ] . territory ;
2017-08-18 19:20:51 +02:00
if ( territoryMult <= 0 ) { return 0 ; }
2017-08-13 07:01:33 +02:00
if ( task . baseWanted < 0 ) {
return task . baseWanted * statWeight * territoryMult ;
} else {
2017-08-20 03:36:19 +02:00
return 6 * task . baseWanted / ( 3 * statWeight * territoryMult ) ;
2017-08-13 07:01:33 +02:00
}
}
GangMember . prototype . calculateMoneyGain = function ( ) {
var task = this . task ;
2017-10-26 00:05:12 +02:00
if ( task == null || ! ( task instanceof GangMemberTask ) || task . baseMoney === 0 ) { return 0 ; }
2017-08-13 07:01:33 +02:00
var statWeight = ( task . hackWeight / 100 ) * this . hack +
( task . strWeight / 100 ) * this . str +
( task . defWeight / 100 ) * this . def +
( task . dexWeight / 100 ) * this . dex +
( task . agiWeight / 100 ) * this . agi +
( task . chaWeight / 100 ) * this . cha ;
statWeight -= ( 3.5 * task . difficulty ) ;
if ( statWeight <= 0 ) { return 0 ; }
var territoryMult = AllGangs [ Player . gang . facName ] . territory ;
2017-08-18 19:20:51 +02:00
if ( territoryMult <= 0 ) { return 0 ; }
2018-10-02 23:54:56 +02:00
var respectMult = Player . gang . getWantedPenalty ( ) ;
2017-08-13 07:01:33 +02:00
return 5 * task . baseMoney * statWeight * territoryMult * respectMult ;
}
GangMember . prototype . gainExperience = function ( numCycles = 1 ) {
var task = this . task ;
2017-10-26 00:05:12 +02:00
if ( task == null || ! ( task instanceof GangMemberTask ) ) { return ; }
2017-08-13 07:01:33 +02:00
this . hack _exp += ( task . hackWeight / 1500 ) * task . difficulty * numCycles ;
this . str _exp += ( task . strWeight / 1500 ) * task . difficulty * numCycles ;
this . def _exp += ( task . defWeight / 1500 ) * task . difficulty * numCycles ;
this . dex _exp += ( task . dexWeight / 1500 ) * task . difficulty * numCycles ;
this . agi _exp += ( task . agiWeight / 1500 ) * task . difficulty * numCycles ;
this . cha _exp += ( task . chaWeight / 1500 ) * task . difficulty * numCycles ;
}
2018-10-02 23:54:56 +02:00
GangMember . prototype . recordEarnedRespect = function ( numCycles = 1 ) {
this . earnedRespect += ( this . calculateRespectGain ( ) * numCycles ) ;
}
GangMember . prototype . ascend = function ( ) {
2018-10-15 02:28:44 +02:00
const res = this . getAscensionResults ( ) ;
const hackAscMult = res . hack ;
const strAscMult = res . str ;
const defAscMult = res . def ;
const dexAscMult = res . dex ;
const agiAscMult = res . agi ;
const chaAscMult = res . cha ;
this . hack _asc _mult += hackAscMult ;
this . str _asc _mult += strAscMult ;
this . def _asc _mult += defAscMult ;
this . dex _asc _mult += dexAscMult ;
this . agi _asc _mult += agiAscMult ;
this . cha _asc _mult += chaAscMult ;
// Remove upgrades. Then re-calculate multipliers and stats
this . upgrades . length = 0 ;
this . hack _mult = 1 ;
this . str _mult = 1 ;
this . def _mult = 1 ;
this . dex _mult = 1 ;
this . agi _mult = 1 ;
this . cha _mult = 1 ;
for ( let i = 0 ; i < this . augmentations . length ; ++ i ) {
let aug = GangMemberUpgrades [ this . augmentations [ i ] ] ;
aug . apply ( this ) ;
}
// Clear exp and recalculate stats
this . hack _exp = 0 ;
this . str _exp = 0 ;
this . def _exp = 0 ;
this . dex _exp = 0 ;
this . agi _exp = 0 ;
this . cha _exp = 0 ;
this . updateSkillLevels ( ) ;
const respectToDeduct = this . earnedRespect ;
this . earnedRespect = 0 ;
return {
respect : respectToDeduct ,
hack : hackAscMult ,
str : strAscMult ,
def : defAscMult ,
dex : dexAscMult ,
agi : agiAscMult ,
cha : chaAscMult ,
} ;
}
// Returns the multipliers that would be gained from ascension
GangMember . prototype . getAscensionResults = function ( ) {
2018-10-02 23:54:56 +02:00
// Calculate ascension bonus to stat multipliers.
// This is based on the current number of multipliers from Non-Augmentation upgrades
// + Ascension Bonus = N% of current bonus from Augmentations
let hack = 1 ;
let str = 1 ;
let def = 1 ;
let dex = 1 ;
let agi = 1 ;
let cha = 1 ;
for ( let i = 0 ; i < this . upgrades . length ; ++ i ) {
let upg = GangMemberUpgrades [ this . upgrades [ i ] ] ;
if ( upg . mults . hack != null ) { hack *= upg . mults . hack ; }
if ( upg . mults . str != null ) { str *= upg . mults . str ; }
if ( upg . mults . def != null ) { def *= upg . mults . def ; }
if ( upg . mults . dex != null ) { dex *= upg . mults . dex ; }
if ( upg . mults . agi != null ) { agi *= upg . mults . agi ; }
if ( upg . mults . cha != null ) { cha *= upg . mults . cha ; }
}
2018-10-15 02:28:44 +02:00
// Subtract 1 because we're only interested in the actual "bonus" part
return {
hack : ( Math . max ( 0 , hack - 1 ) * AscensionMultiplierRatio ) ,
str : ( Math . max ( 0 , str - 1 ) * AscensionMultiplierRatio ) ,
def : ( Math . max ( 0 , def - 1 ) * AscensionMultiplierRatio ) ,
dex : ( Math . max ( 0 , dex - 1 ) * AscensionMultiplierRatio ) ,
agi : ( Math . max ( 0 , agi - 1 ) * AscensionMultiplierRatio ) ,
cha : ( Math . max ( 0 , cha - 1 ) * AscensionMultiplierRatio ) ,
2018-10-02 23:54:56 +02:00
}
}
2017-08-13 07:01:33 +02:00
GangMember . prototype . toJSON = function ( ) {
return Generic _toJSON ( "GangMember" , this ) ;
}
GangMember . fromJSON = function ( value ) {
return Generic _fromJSON ( GangMember , value . data ) ;
}
Reviver . constructors . GangMember = GangMember ;
//Defines tasks that Gang Members can work on
2018-10-11 01:13:31 +02:00
function GangMemberTask ( name = "" , desc = "" , isHacking = false , isCombat = false ,
2017-08-13 07:01:33 +02:00
params = { baseRespect : 0 , baseWanted : 0 , baseMoney : 0 ,
hackWeight : 0 , strWeight : 0 , defWeight : 0 ,
dexWeight : 0 , agiWeight : 0 , chaWeight : 0 ,
difficulty : 0 } ) {
this . name = name ;
this . desc = desc ;
2018-10-11 01:13:31 +02:00
// Flags that describe whether this Task is applicable for Hacking/Combat gangs
this . isHacking = isHacking ;
this . isCombat = isCombat ;
// Base gain rates for respect/wanted/money
2017-08-13 07:01:33 +02:00
this . baseRespect = params . baseRespect ? params . baseRespect : 0 ;
this . baseWanted = params . baseWanted ? params . baseWanted : 0 ;
this . baseMoney = params . baseMoney ? params . baseMoney : 0 ;
2018-10-11 01:13:31 +02:00
// Weighting for the effect that each stat has on the tasks effectiveness.
// Weights must add up to 100
2017-08-13 07:01:33 +02:00
this . hackWeight = params . hackWeight ? params . hackWeight : 0 ;
this . strWeight = params . strWeight ? params . strWeight : 0 ;
this . defWeight = params . defWeight ? params . defWeight : 0 ;
this . dexWeight = params . dexWeight ? params . dexWeight : 0 ;
this . agiWeight = params . agiWeight ? params . agiWeight : 0 ;
this . chaWeight = params . chaWeight ? params . chaWeight : 0 ;
2018-10-11 01:13:31 +02:00
// 1 - 100
2017-08-13 07:01:33 +02:00
this . difficulty = params . difficulty ? params . difficulty : 1 ;
}
GangMemberTask . prototype . toJSON = function ( ) {
return Generic _toJSON ( "GangMemberTask" , this ) ;
}
GangMemberTask . fromJSON = function ( value ) {
return Generic _fromJSON ( GangMemberTask , value . data ) ;
}
Reviver . constructors . GangMemberTask = GangMemberTask ;
//TODO Human trafficking and an equivalent hacking crime
2018-10-02 23:54:56 +02:00
const GangMemberTasks = { } ;
2018-10-11 01:13:31 +02:00
function addGangMemberTask ( name , desc , isHacking , isCombat , params ) {
GangMemberTasks [ name ] = new GangMemberTask ( name , desc , isHacking , isCombat , params ) ;
2017-08-13 07:01:33 +02:00
}
2018-10-02 23:54:56 +02:00
gangMemberTasksMetadata . forEach ( ( e ) => {
2018-10-11 01:13:31 +02:00
addGangMemberTask ( e . name , e . desc , e . isHacking , e . isCombat , e . params ) ;
2018-10-02 23:54:56 +02:00
} ) ;
2017-08-13 07:01:33 +02:00
2018-10-02 18:45:00 +02:00
function GangMemberUpgrade ( name = "" , cost = 0 , type = "w" , mults = { } ) {
2017-08-13 07:01:33 +02:00
this . name = name ;
2017-08-15 22:22:46 +02:00
this . cost = cost ;
2018-10-02 23:54:56 +02:00
this . type = type ; //w = weapon, a = armor, v = vehicle, r = rootkit, g = Aug
2018-10-02 18:45:00 +02:00
this . mults = mults ;
this . createDescription ( ) ;
}
GangMemberUpgrade . prototype . createDescription = function ( ) {
2018-10-02 23:54:56 +02:00
const lines = [ "Increases:" ] ;
2018-10-02 18:45:00 +02:00
if ( this . mults . str != null ) {
2018-10-02 23:54:56 +02:00
lines . push ( ` * Strength by ${ Math . round ( ( this . mults . str - 1 ) * 100 ) } % ` ) ;
2018-10-02 18:45:00 +02:00
}
if ( this . mults . def != null ) {
2018-10-02 23:54:56 +02:00
lines . push ( ` * Defense by ${ Math . round ( ( this . mults . def - 1 ) * 100 ) } % ` ) ;
2018-10-02 18:45:00 +02:00
}
if ( this . mults . dex != null ) {
2018-10-02 23:54:56 +02:00
lines . push ( ` * Dexterity by ${ Math . round ( ( this . mults . dex - 1 ) * 100 ) } % ` ) ;
2018-10-02 18:45:00 +02:00
}
if ( this . mults . agi != null ) {
2018-10-02 23:54:56 +02:00
lines . push ( ` * Agility by ${ Math . round ( ( this . mults . agi - 1 ) * 100 ) } % ` ) ;
2018-10-02 18:45:00 +02:00
}
if ( this . mults . cha != null ) {
2018-10-02 23:54:56 +02:00
lines . push ( ` * Charisma by ${ Math . round ( ( this . mults . cha - 1 ) * 100 ) } % ` ) ;
2018-10-02 18:45:00 +02:00
}
if ( this . mults . hack != null ) {
2018-10-02 23:54:56 +02:00
lines . push ( ` * Hacking by ${ Math . round ( ( this . mults . hack - 1 ) * 100 ) } % ` ) ;
2018-10-02 18:45:00 +02:00
}
2018-10-15 02:28:44 +02:00
this . desc = lines . join ( "<br>" ) ;
2017-08-13 07:01:33 +02:00
}
//Passes in a GangMember object
2018-02-09 22:11:43 +01:00
GangMemberUpgrade . prototype . apply = function ( member ) {
2018-10-02 18:45:00 +02:00
if ( this . mults . str != null ) { member . str _mult *= this . mults . str ; }
if ( this . mults . def != null ) { member . def _mult *= this . mults . def ; }
if ( this . mults . dex != null ) { member . dex _mult *= this . mults . dex ; }
if ( this . mults . agi != null ) { member . agi _mult *= this . mults . agi ; }
if ( this . mults . cha != null ) { member . cha _mult *= this . mults . cha ; }
if ( this . mults . hack != null ) { member . hack _mult *= this . mults . hack ; }
return ;
2017-08-13 07:01:33 +02:00
}
2017-08-15 22:22:46 +02:00
GangMemberUpgrade . prototype . toJSON = function ( ) {
return Generic _toJSON ( "GangMemberUpgrade" , this ) ;
}
GangMemberUpgrade . fromJSON = function ( value ) {
return Generic _fromJSON ( GangMemberUpgrade , value . data ) ;
}
Reviver . constructors . GangMemberUpgrade = GangMemberUpgrade ;
2018-10-02 23:54:56 +02:00
// Initialize Gang Member Upgrades
2018-10-02 18:45:00 +02:00
const GangMemberUpgrades = { }
function addGangMemberUpgrade ( name , cost , type , mults ) {
2018-10-02 23:54:56 +02:00
GangMemberUpgrades [ name ] = new GangMemberUpgrade ( name , cost , type , mults ) ;
2017-08-15 22:22:46 +02:00
}
2018-10-02 23:54:56 +02:00
gangMemberUpgradesMetadata . forEach ( ( e ) => {
addGangMemberUpgrade ( e . name , e . cost , e . upgType , e . mults ) ;
} ) ;
2018-10-02 18:45:00 +02:00
2018-10-15 02:28:44 +02:00
// Create a pop-up box that lets player purchase upgrades
Gang . prototype . createGangMemberUpgradeBox = function ( initialFilter = "" ) {
const boxId = "gang-member-upgrade-popup-box" ;
if ( UIElems . gangMemberUpgradeBoxOpened ) {
2018-02-15 05:26:43 +01:00
//Already opened, refreshing
2018-10-15 02:28:44 +02:00
if ( UIElems . gangMemberUpgradeBoxElements == null || UIElems . gangMemberUpgradeBox == null || UIElems . gangMemberUpgradeBoxContent == null ) {
console . error ( "Refreshing Gang member upgrade box throws error because required elements are null" ) ;
2018-02-15 05:26:43 +01:00
return ;
2017-08-15 22:22:46 +02:00
}
2018-10-15 02:28:44 +02:00
for ( var i = 1 ; i < UIElems . gangMemberUpgradeBoxElements . length ; ++ i ) {
removeElement ( UIElems . gangMemberUpgradeBoxElements [ i ] ) ;
2018-02-15 05:26:43 +01:00
}
2018-10-15 02:28:44 +02:00
UIElems . gangMemberUpgradeBoxElements = [ UIElems . gangMemberUpgradeBoxFilter ] ;
2018-02-15 05:26:43 +01:00
2018-10-15 02:28:44 +02:00
var filter = UIElems . gangMemberUpgradeBoxFilter . value . toString ( ) ;
2018-02-15 05:26:43 +01:00
for ( var i = 0 ; i < Player . gang . members . length ; ++ i ) {
if ( Player . gang . members [ i ] . name . indexOf ( filter ) > - 1 || Player . gang . members [ i ] . task . name . indexOf ( filter ) > - 1 ) {
2018-10-15 02:28:44 +02:00
var newPanel = Player . gang . members [ i ] . createGangMemberUpgradePanel ( this ) ;
UIElems . gangMemberUpgradeBoxContent . appendChild ( newPanel ) ;
UIElems . gangMemberUpgradeBoxElements . push ( newPanel ) ;
2018-02-15 05:26:43 +01:00
}
}
2017-08-15 22:22:46 +02:00
} else {
2018-02-15 05:26:43 +01:00
//New popup
2018-10-15 02:28:44 +02:00
UIElems . gangMemberUpgradeBoxFilter = createElement ( "input" , {
2018-02-15 05:26:43 +01:00
type : "text" , placeholder : "Filter gang members" ,
value : initialFilter ,
onkeyup : ( ) => {
2018-10-15 02:28:44 +02:00
var filterValue = UIElems . gangMemberUpgradeBoxFilter . value . toString ( ) ;
this . createGangMemberUpgradeBox ( filterValue ) ;
2018-02-15 05:26:43 +01:00
}
} ) ;
2018-10-15 02:28:44 +02:00
UIElems . gangMemberUpgradeBoxElements = [ UIElems . gangMemberUpgradeBoxFilter ] ;
2018-02-15 05:26:43 +01:00
2018-10-15 02:28:44 +02:00
var filter = UIElems . gangMemberUpgradeBoxFilter . value . toString ( ) ;
for ( var i = 0 ; i < this . members . length ; ++ i ) {
if ( this . members [ i ] . name . indexOf ( filter ) > - 1 || this . members [ i ] . task . name . indexOf ( filter ) > - 1 ) {
UIElems . gangMemberUpgradeBoxElements . push ( this . members [ i ] . createGangMemberUpgradePanel ( this ) ) ;
2018-02-15 05:26:43 +01:00
}
}
2018-10-15 02:28:44 +02:00
UIElems . gangMemberUpgradeBox = createPopup ( boxId , UIElems . gangMemberUpgradeBoxElements ) ;
UIElems . gangMemberUpgradeBoxContent = document . getElementById ( boxId + "-content" ) ;
UIElems . gangMemberUpgradeBoxOpened = true ;
2017-08-15 22:22:46 +02:00
}
2018-02-15 05:26:43 +01:00
}
//Create upgrade panels for each individual Gang Member
2018-10-15 02:28:44 +02:00
GangMember . prototype . createGangMemberUpgradePanel = function ( gangObj ) {
2018-02-15 05:26:43 +01:00
var container = createElement ( "div" , {
border : "1px solid white" ,
} ) ;
var header = createElement ( "h1" , {
2018-10-15 02:28:44 +02:00
innerText : this . name + " (" + this . task . name + ")"
2018-02-15 05:26:43 +01:00
} ) ;
container . appendChild ( header ) ;
var text = createElement ( "pre" , {
fontSize : "14px" , display : "inline-block" , width : "20%" ,
innerText :
2018-10-15 02:28:44 +02:00
"Hack: " + this . hack + " (x" + formatNumber ( this . hack _mult , 2 ) + ")\n" +
"Str: " + this . str + " (x" + formatNumber ( this . str _mult , 2 ) + ")\n" +
"Def: " + this . def + " (x" + formatNumber ( this . def _mult , 2 ) + ")\n" +
"Dex: " + this . dex + " (x" + formatNumber ( this . dex _mult , 2 ) + ")\n" +
"Agi: " + this . agi + " (x" + formatNumber ( this . agi _mult , 2 ) + ")\n" +
"Cha: " + this . cha + " (x" + formatNumber ( this . cha _mult , 2 ) + ")\n" ,
2018-02-15 05:26:43 +01:00
} ) ;
//Already purchased upgrades
2018-10-15 02:28:44 +02:00
const ownedUpgradesElements = [ ] ;
function pushOwnedUpgrade ( upgName ) {
const upg = GangMemberUpgrades [ upgName ] ;
2018-02-15 05:26:43 +01:00
if ( upg == null ) {
2018-10-15 02:28:44 +02:00
console . error ( ` Could not find GangMemberUpgrade object for name ${ upgName } ` ) ;
return ;
2018-02-15 05:26:43 +01:00
}
2018-10-15 02:28:44 +02:00
ownedUpgradesElements . push ( createElement ( "div" , {
class : "gang-owned-upgrade" ,
innerText : upgName ,
tooltip : upg . desc ,
} ) ) ;
2017-08-15 22:22:46 +02:00
}
2018-10-15 02:28:44 +02:00
for ( const upgName of this . upgrades ) { pushOwnedUpgrade ( upgName ) ; }
for ( const upgName of this . augmentations ) { pushOwnedUpgrade ( upgName ) ; }
2018-02-15 05:26:43 +01:00
var ownedUpgrades = createElement ( "div" , {
2018-10-15 02:28:44 +02:00
class : "gang-owned-upgrades-div" ,
innerText : "Purchased Upgrades:" ,
2018-02-15 05:26:43 +01:00
} ) ;
2018-10-15 02:28:44 +02:00
for ( const elem of ownedUpgradesElements ) { ownedUpgrades . appendChild ( elem ) ; }
2018-02-15 05:26:43 +01:00
container . appendChild ( text ) ;
container . appendChild ( ownedUpgrades ) ;
container . appendChild ( createElement ( "br" , { } ) ) ;
//Upgrade buttons. Only show upgrades that can be afforded
2018-10-02 23:54:56 +02:00
const weaponUpgrades = [ ] ;
const armorUpgrades = [ ] ;
const vehicleUpgrades = [ ] ;
const rootkitUpgrades = [ ] ;
const augUpgrades = [ ] ;
for ( let upgName in GangMemberUpgrades ) {
2018-02-15 05:26:43 +01:00
if ( GangMemberUpgrades . hasOwnProperty ( upgName ) ) {
2018-10-02 23:54:56 +02:00
let upg = GangMemberUpgrades [ upgName ] ;
2018-10-15 02:28:44 +02:00
if ( Player . money . lt ( upg . cost ) ) { continue ; }
if ( this . upgrades . includes ( upgName ) || this . augmentations . includes ( upgName ) ) { continue ; }
2018-02-15 05:26:43 +01:00
switch ( upg . type ) {
case "w" :
weaponUpgrades . push ( upg ) ;
break ;
case "a" :
armorUpgrades . push ( upg ) ;
break ;
case "v" :
vehicleUpgrades . push ( upg ) ;
break ;
case "r" :
rootkitUpgrades . push ( upg ) ;
break ;
2018-10-02 23:54:56 +02:00
case "g" :
augUpgrades . push ( upg ) ;
2018-10-15 02:28:44 +02:00
break ;
2018-02-15 05:26:43 +01:00
default :
2018-10-02 23:54:56 +02:00
console . error ( ` ERROR: Invalid Gang Member Upgrade Type: ${ upg . type } ` ) ;
2018-02-15 05:26:43 +01:00
}
}
2017-08-15 22:22:46 +02:00
}
2017-08-20 03:36:19 +02:00
2018-10-15 02:28:44 +02:00
// Create separate columns for each upgrade type
const weaponDiv = createElement ( "div" , { width : "20%" , display : "inline-block" } ) ;
const armorDiv = createElement ( "div" , { width : "20%" , display : "inline-block" } ) ;
const vehicleDiv = createElement ( "div" , { width : "20%" , display : "inline-block" } ) ;
const rootkitDiv = createElement ( "div" , { width : "20%" , display : "inline-block" } ) ;
const augDiv = createElement ( "div" , { width : "20%" , display : "inline-block" } ) ;
// Add a title/labe for each column
weaponDiv . appendChild ( createElement ( "h2" , { innerText : "Weapons" } ) ) ;
armorDiv . appendChild ( createElement ( "h2" , { innerText : "Armor" } ) ) ;
vehicleDiv . appendChild ( createElement ( "h2" , { innerText : "Vehicles" } ) ) ;
rootkitDiv . appendChild ( createElement ( "h2" , { innerText : "Rootkits" } ) ) ;
augDiv . appendChild ( createElement ( "h2" , { innerText : "Augmentations" } ) ) ;
// Add buttons to purchase each upgrade
2018-10-02 23:54:56 +02:00
const upgrades = [ weaponUpgrades , armorUpgrades , vehicleUpgrades , rootkitUpgrades , augUpgrades ] ;
const divs = [ weaponDiv , armorDiv , vehicleDiv , rootkitDiv , augDiv ] ;
for ( let i = 0 ; i < upgrades . length ; ++ i ) {
let upgradeArray = upgrades [ i ] ;
let div = divs [ i ] ;
for ( let j = 0 ; j < upgradeArray . length ; ++ j ) {
let upg = upgradeArray [ j ] ;
2018-10-15 02:28:44 +02:00
( function ( upg , div , memberObj , i ) {
let createElementParams = {
2018-09-12 17:53:08 +02:00
innerText : upg . name + " - " + numeralWrapper . format ( upg . cost , "$0.000a" ) ,
2018-02-15 05:26:43 +01:00
class : "a-link-button" , margin : "2px" , padding : "2px" , display : "block" ,
2018-10-02 23:54:56 +02:00
fontSize : "11px" ,
2018-02-15 05:26:43 +01:00
clickListener : ( ) => {
2018-10-02 23:54:56 +02:00
if ( Player . money . lt ( upg . cost ) ) { return false ; }
2018-02-15 05:26:43 +01:00
Player . loseMoney ( upg . cost ) ;
2018-10-02 23:54:56 +02:00
if ( upg . type === "g" ) {
memberObj . augmentations . push ( upg . name ) ;
} else {
memberObj . upgrades . push ( upg . name ) ;
}
2018-02-15 05:26:43 +01:00
upg . apply ( memberObj ) ;
2018-10-15 02:28:44 +02:00
var initFilterValue = UIElems . gangMemberUpgradeBoxFilter . value . toString ( ) ;
gangObj . createGangMemberUpgradeBox ( initFilterValue ) ;
2018-02-15 05:26:43 +01:00
return false ;
}
2018-10-15 02:28:44 +02:00
}
// For the last two divs, tooltip should be on the left
if ( i >= 3 ) {
createElementParams . tooltipleft = upg . desc ;
} else {
createElementParams . tooltip = upg . desc ;
}
div . appendChild ( createElement ( "a" , createElementParams ) ) ;
} ) ( upg , div , this , i ) ;
2017-08-15 22:22:46 +02:00
}
}
2018-02-15 05:26:43 +01:00
container . appendChild ( weaponDiv ) ;
container . appendChild ( armorDiv ) ;
container . appendChild ( vehicleDiv ) ;
container . appendChild ( rootkitDiv ) ;
2018-10-15 02:28:44 +02:00
container . appendChild ( augDiv ) ;
2018-02-15 05:26:43 +01:00
return container ;
2017-08-13 07:01:33 +02:00
}
2018-10-15 02:28:44 +02:00
// Gang UI Dom Elements
const UIElems = {
// Main elems
gangContentCreated : false ,
gangContainer : null ,
managementButton : null ,
territoryButton : null ,
// Subpages
gangManagementSubpage : null ,
gangTerritorySubpage : null ,
// Gang Management Subpage Elements
gangDesc : null ,
gangInfo : null ,
gangRecruitMemberButton : null ,
gangRecruitRequirementText : null ,
gangExpandAllButton : null ,
gangCollapseAllButton : null ,
gangMemberFilter : null ,
gangManageEquipmentButton : null ,
gangMemberList : null ,
2018-10-18 21:52:02 +02:00
gangMemberPanels : { } ,
2018-10-15 02:28:44 +02:00
// Gang Equipment Upgrade Elements
gangMemberUpgradeBoxOpened : false ,
gangMemberUpgradeBox : null ,
gangMemberUpgradeBoxContent : null ,
gangMemberUpgradeBoxFilter : null ,
gangMemberUpgradeBoxElements : null ,
// Gang Territory Elements
gangTerritoryDescText : null ,
2018-10-18 21:52:02 +02:00
gangTerritoryWarfareCheckbox : null ,
gangTerritoryWarfareCheckboxLabel : null ,
gangTerritoryWarfareClashChance : null ,
2018-10-15 02:28:44 +02:00
gangTerritoryInfoText : null ,
}
2018-02-09 22:11:43 +01:00
2018-10-15 02:28:44 +02:00
Gang . prototype . displayGangContent = function ( ) {
if ( ! UIElems . gangContentCreated || UIElems . gangContainer == null ) {
UIElems . gangContentCreated = true ;
2017-08-13 07:01:33 +02:00
//Create gang container
2018-10-15 02:28:44 +02:00
UIElems . gangContainer = createElement ( "div" , {
2018-02-09 22:11:43 +01:00
id : "gang-container" , class : "generic-menupage-container" ,
} ) ;
2017-08-13 07:01:33 +02:00
//Get variables
2018-10-15 02:28:44 +02:00
var facName = this . facName ,
members = this . members ,
wanted = this . wanted ,
respect = this . respect ;
2017-08-13 07:01:33 +02:00
2018-03-27 02:46:21 +02:00
//Back button
2018-10-15 02:28:44 +02:00
UIElems . gangContainer . appendChild ( createElement ( "a" , {
2018-03-27 02:46:21 +02:00
class : "a-link-button" , display : "inline-block" , innerText : "Back" ,
clickListener : ( ) => {
Engine . loadFactionContent ( ) ;
displayFactionContent ( facName ) ;
return false ;
}
} ) ) ;
2017-08-13 07:01:33 +02:00
//Buttons to switch between panels
2018-10-15 02:28:44 +02:00
UIElems . managementButton = createElement ( "a" , {
2018-02-09 22:11:43 +01:00
id : "gang-management-subpage-button" , class : "a-link-button-inactive" ,
2018-10-15 02:28:44 +02:00
display : "inline-block" , innerHTML : "Gang Management (Alt+1)" ,
2018-02-09 22:11:43 +01:00
clickListener : ( ) => {
2018-10-15 02:28:44 +02:00
UIElems . gangManagementSubpage . style . display = "block" ;
UIElems . gangTerritorySubpage . style . display = "none" ;
UIElems . managementButton . classList . toggle ( "a-link-button-inactive" ) ;
UIElems . managementButton . classList . toggle ( "a-link-button" ) ;
UIElems . territoryButton . classList . toggle ( "a-link-button-inactive" ) ;
UIElems . territoryButton . classList . toggle ( "a-link-button" ) ;
this . updateGangContent ( ) ;
2018-02-09 22:11:43 +01:00
return false ;
}
} )
2018-10-15 02:28:44 +02:00
UIElems . territoryButton = createElement ( "a" , {
2018-02-09 22:11:43 +01:00
id : "gang-territory-subpage-button" , class : "a-link-button" ,
2018-10-15 02:28:44 +02:00
display : "inline-block" , innerHTML : "Gang Territory (Alt+2)" ,
clickListener : ( ) => {
UIElems . gangManagementSubpage . style . display = "none" ;
UIElems . gangTerritorySubpage . style . display = "block" ;
UIElems . managementButton . classList . toggle ( "a-link-button-inactive" ) ;
UIElems . managementButton . classList . toggle ( "a-link-button" ) ;
UIElems . territoryButton . classList . toggle ( "a-link-button-inactive" ) ;
UIElems . territoryButton . classList . toggle ( "a-link-button" ) ;
this . updateGangContent ( ) ;
2018-02-09 22:11:43 +01:00
return false ;
}
2017-08-13 07:01:33 +02:00
} ) ;
2018-10-15 02:28:44 +02:00
UIElems . gangContainer . appendChild ( UIElems . managementButton ) ;
UIElems . gangContainer . appendChild ( UIElems . territoryButton ) ;
2017-08-13 07:01:33 +02:00
2018-02-09 22:11:43 +01:00
//Subpage for managing gang members
2018-10-15 02:28:44 +02:00
UIElems . gangManagementSubpage = createElement ( "div" , {
2018-02-09 22:11:43 +01:00
display : "block" , id : "gang-management-subpage" ,
2017-08-13 07:01:33 +02:00
} ) ;
2018-02-09 22:11:43 +01:00
var lowerWantedTask = "" ;
2018-10-15 02:28:44 +02:00
if ( this . isHackingGang ) {
2018-02-09 22:11:43 +01:00
lowerWantedTask = "Ethical Hacking" ;
} else {
lowerWantedTask = "Vigilante Justice" ;
}
2018-10-15 02:28:44 +02:00
UIElems . gangDesc = createElement ( "p" , { width : "70%" ,
2018-02-09 22:11:43 +01:00
innerHTML :
"This page is used to manage your gang members and get an overview of your " +
"gang's stats.<br><br>" +
"If a gang member is not earning much money or respect, the task that you " +
"have assigned to that member might be too difficult. Consider training that " +
"member's stats or choosing an easier task. The tasks closer to the " +
"top of the dropdown list are generally easier. Alternatively, the gang member's " +
"low production might be due to the fact that your wanted level is too high. " +
"Consider assigning a few members to the '" + lowerWantedTask + "' " +
"task to lower your wanted level. <br><br>" +
"Installing Augmentations does NOT reset your progress with your Gang. " +
"Furthermore, after installing Augmentations, you will " +
"automatically be a member of whatever Faction you created your gang with.<br><br>"
} ) ;
2018-10-15 02:28:44 +02:00
UIElems . gangManagementSubpage . appendChild ( UIElems . gangDesc ) ;
2018-02-09 22:11:43 +01:00
2018-10-15 02:28:44 +02:00
UIElems . gangInfo = createElement ( "p" , { id : "gang-info" , width : "70%" } ) ;
UIElems . gangManagementSubpage . appendChild ( UIElems . gangInfo ) ;
2018-02-09 22:11:43 +01:00
2018-10-15 02:28:44 +02:00
UIElems . gangRecruitMemberButton = createElement ( "a" , {
2018-02-09 22:11:43 +01:00
id : "gang-management-recruit-member-btn" , class : "a-link-button-inactive" ,
innerHTML : "Recruit Gang Member" , display : "inline-block" , margin : "10px" ,
clickListener : ( ) => {
2018-10-15 02:28:44 +02:00
const popupId = "recruit-gang-member-popup" ;
2018-10-03 01:01:37 +02:00
let yesBtn ;
const txt = createElement ( "p" , {
innerText : "Please enter a name for your new Gang member:" ,
} ) ;
const br = createElement ( "br" ) ;
const nameInput = createElement ( "input" , {
onkeyup : ( e ) => {
if ( e . keyCode === KEY . ENTER ) { yesBtn . click ( ) ; }
} ,
placeholder : "Name must be unique" ,
type : "text" ,
} ) ;
yesBtn = createElement ( "a" , {
2018-10-15 02:28:44 +02:00
class : "std-button" ,
2018-10-03 01:01:37 +02:00
clickListener : ( ) => {
let name = nameInput . value ;
// Check for already-existing names
2018-10-15 02:28:44 +02:00
let sameNames = this . members . filter ( ( m ) => {
2018-10-03 01:01:37 +02:00
return m . name === name ;
} ) ;
if ( sameNames . length >= 1 ) {
dialogBoxCreate ( "You already have a gang member with this name!" ) ;
return false ;
2017-08-13 07:01:33 +02:00
}
2018-10-03 01:01:37 +02:00
if ( name === "" ) {
dialogBoxCreate ( "You must enter a name for your Gang member!" ) ;
} else {
let member = new GangMember ( name ) ;
2018-10-15 02:28:44 +02:00
this . members . push ( member ) ;
this . createGangMemberDisplayElement ( member ) ;
this . updateGangContent ( ) ;
2018-10-03 01:01:37 +02:00
removeElementById ( popupId ) ;
}
return false ;
} ,
innerText : "Recruit Gang Member" ,
2018-02-09 22:11:43 +01:00
} ) ;
2018-10-03 01:01:37 +02:00
const noBtn = createElement ( "a" , {
2018-10-15 02:28:44 +02:00
class : "std-button" ,
2018-10-03 01:01:37 +02:00
clickListener : ( ) => {
removeElementById ( popupId ) ;
return false ;
} ,
innerText : "Cancel" ,
2018-02-09 22:11:43 +01:00
} ) ;
2018-10-03 01:01:37 +02:00
createPopup ( popupId , [ txt , br , nameInput , yesBtn , noBtn ] ) ;
2018-02-09 22:11:43 +01:00
}
2017-08-13 07:01:33 +02:00
} ) ;
2018-10-15 02:28:44 +02:00
UIElems . gangManagementSubpage . appendChild ( UIElems . gangRecruitMemberButton ) ;
2017-08-13 07:01:33 +02:00
2018-10-03 01:01:37 +02:00
// Text for how much reputation is required for recruiting next memberList
2018-10-15 02:28:44 +02:00
UIElems . gangRecruitRequirementText = createElement ( "p" , {
2018-10-03 01:01:37 +02:00
color : "red" ,
id : "gang-recruit-requirement-text" ,
margin : "10px" ,
} ) ;
2018-10-15 02:28:44 +02:00
UIElems . gangManagementSubpage . appendChild ( UIElems . gangRecruitRequirementText ) ;
2018-02-09 22:11:43 +01:00
//Gang Member List management buttons (Expand/Collapse All, select a single member)
2018-10-15 02:28:44 +02:00
UIElems . gangManagementSubpage . appendChild ( createElement ( "br" , { } ) ) ;
UIElems . gangExpandAllButton = createElement ( "a" , {
2018-02-15 05:26:43 +01:00
class : "a-link-button" , display : "inline-block" ,
2018-02-09 22:11:43 +01:00
innerHTML : "Expand All" ,
clickListener : ( ) => {
2018-10-15 02:28:44 +02:00
var allHeaders = UIElems . gangManagementSubpage . getElementsByClassName ( "accordion-header" ) ;
2018-02-15 05:26:43 +01:00
for ( var i = 0 ; i < allHeaders . length ; ++ i ) {
var hdr = allHeaders [ i ] ;
2018-02-09 22:11:43 +01:00
if ( ! hdr . classList . contains ( "active" ) ) {
hdr . click ( ) ;
}
2018-02-15 05:26:43 +01:00
}
return false ;
2018-02-09 22:11:43 +01:00
}
} ) ;
2018-10-15 02:28:44 +02:00
UIElems . gangCollapseAllButton = createElement ( "a" , {
2018-02-15 05:26:43 +01:00
class : "a-link-button" , display : "inline-block" ,
2018-02-09 22:11:43 +01:00
innerHTML : "Collapse All" ,
clickListener : ( ) => {
2018-10-15 02:28:44 +02:00
var allHeaders = UIElems . gangManagementSubpage . getElementsByClassName ( "accordion-header" ) ;
2018-02-15 05:26:43 +01:00
for ( var i = 0 ; i < allHeaders . length ; ++ i ) {
var hdr = allHeaders [ i ] ;
2018-02-09 22:11:43 +01:00
if ( hdr . classList . contains ( "active" ) ) {
hdr . click ( ) ;
}
2018-02-15 05:26:43 +01:00
}
return false ;
2018-02-09 22:11:43 +01:00
}
} ) ;
2018-10-15 02:28:44 +02:00
UIElems . gangMemberFilter = createElement ( "input" , {
2018-02-15 05:26:43 +01:00
type : "text" , placeholder : "Filter gang members" , margin : "5px" , padding : "5px" ,
2018-02-09 22:11:43 +01:00
onkeyup : ( ) => {
2018-10-15 02:28:44 +02:00
this . displayGangMemberList ( ) ;
2018-02-09 22:11:43 +01:00
}
} ) ;
2018-10-15 02:28:44 +02:00
UIElems . gangManageEquipmentButton = createElement ( "a" , {
2018-02-15 05:26:43 +01:00
class : "a-link-button" , display : "inline-block" ,
innerHTML : "Manage Equipment" ,
2018-10-15 02:28:44 +02:00
clickListener : ( ) => {
this . createGangMemberUpgradeBox ( ) ;
2018-02-15 05:26:43 +01:00
}
} ) ;
2018-10-15 02:28:44 +02:00
UIElems . gangManagementSubpage . appendChild ( UIElems . gangExpandAllButton ) ;
UIElems . gangManagementSubpage . appendChild ( UIElems . gangCollapseAllButton ) ;
UIElems . gangManagementSubpage . appendChild ( UIElems . gangMemberFilter ) ;
UIElems . gangManagementSubpage . appendChild ( UIElems . gangManageEquipmentButton ) ;
2018-02-09 22:11:43 +01:00
//Gang Member list
2018-10-15 02:28:44 +02:00
UIElems . gangMemberList = createElement ( "ul" , { id : "gang-member-list" } ) ;
this . displayGangMemberList ( ) ;
UIElems . gangManagementSubpage . appendChild ( UIElems . gangMemberList ) ;
2017-08-13 07:01:33 +02:00
//Subpage for seeing gang territory information
2018-10-15 02:28:44 +02:00
UIElems . gangTerritorySubpage = createElement ( "div" , {
2018-02-09 22:11:43 +01:00
id : "gang-territory-subpage" , display : "none"
} ) ;
2017-08-13 07:01:33 +02:00
//Info text for territory page
2018-10-15 02:28:44 +02:00
UIElems . gangTerritoryDescText = createElement ( "p" , {
2018-02-09 22:11:43 +01:00
width : "70%" ,
innerHTML : "This page shows how much territory your Gang controls. This statistic is listed as a percentage, " +
2017-08-13 07:01:33 +02:00
"which represents how much of the total territory you control.<br><br>" +
2018-10-18 21:52:02 +02:00
"Territory gain and loss is processed automatically and is updated every ~20 seconds. Your chances " +
2017-08-13 07:01:33 +02:00
"to gain and lose territory depend on your Gang's power, which is listed in the display below. " +
"Your gang's power is determined by the stats of all Gang members you have assigned to the " +
"'Territory Warfare' task. Gang members that are not assigned to this task do not contribute to " +
"your Gang's power.<br><br>" +
"The amount of territory you have affects all aspects of your Gang members' production, including " +
"money, respect, and wanted level. It is very beneficial to have high territory control.<br><br>"
2018-02-09 22:11:43 +01:00
} ) ;
2018-10-15 02:28:44 +02:00
UIElems . gangTerritorySubpage . appendChild ( UIElems . gangTerritoryDescText ) ;
2017-08-13 07:01:33 +02:00
2018-10-18 21:52:02 +02:00
// Checkbox for Engaging in Territory Warfare
UIElems . gangTerritoryWarfareCheckbox = createElement ( "input" , {
display : "inline-block" ,
id : "gang-management-territory-warfare-checkbox" ,
changeListener : ( ) => {
this . territoryWarfareEngaged = UIElems . gangTerritoryWarfareCheckbox . checked ;
} ,
margin : "2px" ,
type : "checkbox" ,
} ) ;
UIElems . gangTerritoryWarfareCheckbox . checked = this . territoryWarfareEngaged ;
UIElems . gangTerritoryWarfareCheckboxLabel = createElement ( "label" , {
color : "white" ,
for : "gang-management-territory-warfare-checkbox" ,
innerText : "Engage in Territory Warfare" ,
tooltip : "Test" ,
} ) ;
UIElems . gangTerritorySubpage . appendChild ( UIElems . gangTerritoryWarfareCheckbox ) ;
UIElems . gangTerritorySubpage . appendChild ( UIElems . gangTerritoryWarfareCheckboxLabel ) ;
// Territory Clash chance
UIElems . gangTerritoryWarfareClashChance = createElement ( "p" ) ;
UIElems . gangTerritorySubpage . appendChild ( UIElems . gangTerritoryWarfareClashChance ) ;
2017-08-13 07:01:33 +02:00
2018-10-18 21:52:02 +02:00
// Territory info (percentages of territory owned for each gang)
UIElems . gangTerritorySubpage . appendChild ( createElement ( "br" ) ) ;
var territoryBorder = createElement ( "fieldset" , { width : "50%" , display : "block" } ) ;
UIElems . gangTerritoryInfoText = createElement ( "p" ) ;
2017-08-13 07:01:33 +02:00
2018-10-15 02:28:44 +02:00
territoryBorder . appendChild ( UIElems . gangTerritoryInfoText ) ;
UIElems . gangTerritorySubpage . appendChild ( territoryBorder ) ;
2017-08-13 07:01:33 +02:00
2018-10-15 02:28:44 +02:00
UIElems . gangContainer . appendChild ( UIElems . gangTerritorySubpage ) ;
UIElems . gangContainer . appendChild ( UIElems . gangManagementSubpage ) ;
document . getElementById ( "entire-game-container" ) . appendChild ( UIElems . gangContainer ) ;
2017-08-13 07:01:33 +02:00
}
2018-10-15 02:28:44 +02:00
UIElems . gangContainer . style . display = "block" ;
this . updateGangContent ( ) ;
2017-08-13 07:01:33 +02:00
}
2018-10-15 02:28:44 +02:00
Gang . prototype . displayGangMemberList = function ( ) {
removeChildrenFromElement ( UIElems . gangMemberList ) ;
2018-10-18 21:52:02 +02:00
UIElems . gangMemberPanels = { } ;
2018-10-15 02:28:44 +02:00
const members = this . members ;
const filter = UIElems . gangMemberFilter . value . toString ( ) ;
2018-02-09 22:11:43 +01:00
for ( var i = 0 ; i < members . length ; ++ i ) {
2018-02-15 05:26:43 +01:00
if ( members [ i ] . name . indexOf ( filter ) > - 1 || members [ i ] . task . name . indexOf ( filter ) > - 1 ) {
2018-10-15 02:28:44 +02:00
this . createGangMemberDisplayElement ( members [ i ] ) ;
2018-02-09 22:11:43 +01:00
}
}
}
2018-10-15 02:28:44 +02:00
Gang . prototype . updateGangContent = function ( ) {
if ( ! UIElems . gangContentCreated ) { return ; }
2017-08-13 07:01:33 +02:00
2018-10-18 21:52:02 +02:00
if ( UIElems . gangTerritorySubpage . style . display === "block" ) {
// Territory Warfare Clash Chance
UIElems . gangTerritoryWarfareClashChance . innerText =
` Territory Clash Chance: ${ numeralWrapper . format ( this . gangTerritoryWarfareClashChance , '0.000%' ) } ` ;
// Update territory information
2018-10-15 02:28:44 +02:00
UIElems . gangTerritoryInfoText . innerHTML = "" ;
2017-08-13 07:01:33 +02:00
for ( var gangname in AllGangs ) {
if ( AllGangs . hasOwnProperty ( gangname ) ) {
2018-02-15 05:26:43 +01:00
var gangTerritoryInfo = AllGangs [ gangname ] ;
2018-10-18 21:52:02 +02:00
let territory = gangTerritoryInfo . territory * 100 ;
2018-05-07 19:25:44 +02:00
//Fix some rounding issues graphically
let displayNumber ;
if ( territory <= 0 ) {
displayNumber = formatNumber ( 0 , 2 ) ;
} else if ( territory >= 100 ) {
displayNumber = formatNumber ( 100 , 2 ) ;
} else {
displayNumber = formatNumber ( territory , 2 ) ;
}
2018-10-15 02:28:44 +02:00
if ( gangname == this . facName ) {
UIElems . gangTerritoryInfoText . innerHTML += ( "<b>" + gangname + "</b><br>(Power: " + formatNumber ( gangTerritoryInfo . power , 6 ) + "): " +
2018-05-07 19:25:44 +02:00
displayNumber + "%<br><br>" ) ;
2017-08-13 07:01:33 +02:00
} else {
2018-10-15 02:28:44 +02:00
UIElems . gangTerritoryInfoText . innerHTML += ( gangname + "<br>(Power: " + formatNumber ( gangTerritoryInfo . power , 6 ) + "): " +
2018-05-07 19:25:44 +02:00
displayNumber + "%<br><br>" ) ;
2017-08-13 07:01:33 +02:00
}
}
}
} else {
//Update information for overall gang
2018-10-15 02:28:44 +02:00
if ( UIElems . gangInfo instanceof Element ) {
var faction = Factions [ this . facName ] ;
2017-08-13 07:01:33 +02:00
var rep ;
if ( ! ( faction instanceof Faction ) ) {
rep = "ERROR" ;
} else {
rep = faction . playerReputation ;
}
2018-10-15 02:28:44 +02:00
removeChildrenFromElement ( UIElems . gangInfo ) ;
UIElems . gangInfo . appendChild ( createElement ( "p" , { // Respect
display : "inline-block" ,
innerText : "Respect: " + formatNumber ( this . respect , 6 ) +
" (" + formatNumber ( 5 * this . respectGainRate , 6 ) + " / sec)" ,
tooltip : "Represents the amount of respect your gang has from other gangs and criminal " +
"organizations. Your respect affects the amount of money " +
"your gang members will earn, and also determines how much " +
"reputation you are earning with your gang's corresponding Faction."
2018-02-09 22:11:43 +01:00
} ) ) ;
2018-10-15 02:28:44 +02:00
UIElems . gangInfo . appendChild ( createElement ( "br" ) ) ;
UIElems . gangInfo . appendChild ( createElement ( "p" , { // Wanted level
display : "inline-block" ,
innerText : "Wanted Level: " + formatNumber ( this . wanted , 6 ) +
" (" + formatNumber ( 5 * this . wantedGainRate , 6 ) + " / sec)" ,
tooltip : "Represents how much the gang is wanted by law enforcement. The higher " +
"your gang's wanted level, the harder it will be for your gang members " +
"to make money and earn respect. Note that the minimum wanted level is 1."
2018-02-09 22:11:43 +01:00
} ) ) ;
2018-10-15 02:28:44 +02:00
UIElems . gangInfo . appendChild ( createElement ( "br" ) ) ;
2018-02-09 22:11:43 +01:00
2018-10-15 02:28:44 +02:00
var wantedPenalty = this . getWantedPenalty ( ) ;
2018-02-09 22:11:43 +01:00
wantedPenalty = ( 1 - wantedPenalty ) * 100 ;
2018-10-15 02:28:44 +02:00
UIElems . gangInfo . appendChild ( createElement ( "p" , { // Wanted Level multiplier
display : "inline-block" ,
innerText : ` Wanted Level Penalty: - ${ formatNumber ( wantedPenalty , 2 ) } % ` ,
tooltip : "Penalty for respect and money gain rates due to Wanted Level"
2018-02-09 22:11:43 +01:00
} ) ) ;
2018-10-15 02:28:44 +02:00
UIElems . gangInfo . appendChild ( createElement ( "br" ) ) ;
2018-02-15 05:26:43 +01:00
2018-10-15 02:28:44 +02:00
UIElems . gangInfo . appendChild ( createElement ( "p" , { // Money gain rate
display : "inline-block" ,
innerText : ` Money gain rate: ${ numeralWrapper . format ( 5 * this . moneyGainRate , "$0.000a" ) } / sec ` ,
2018-02-09 22:11:43 +01:00
} ) ) ;
2018-10-15 02:28:44 +02:00
UIElems . gangInfo . appendChild ( createElement ( "br" ) ) ;
2018-02-09 22:11:43 +01:00
2018-05-07 19:25:44 +02:00
//Fix some rounding issues graphically
2018-10-15 02:28:44 +02:00
var territoryMult = AllGangs [ this . facName ] . territory * 100 ;
2018-05-07 19:25:44 +02:00
let displayNumber ;
if ( territoryMult <= 0 ) {
displayNumber = formatNumber ( 0 , 2 ) ;
} else if ( territoryMult >= 100 ) {
displayNumber = formatNumber ( 100 , 2 ) ;
} else {
displayNumber = formatNumber ( territoryMult , 2 ) ;
}
2018-10-15 02:28:44 +02:00
UIElems . gangInfo . appendChild ( createElement ( "p" , { // Territory multiplier
display : "inline-block" ,
innerText : ` Territory: ${ formatNumber ( displayNumber , 3 ) } % ` ,
tooltip : "The percentage of total territory your Gang controls"
2018-02-09 22:11:43 +01:00
} ) ) ;
2018-10-15 02:28:44 +02:00
UIElems . gangInfo . appendChild ( createElement ( "br" ) ) ;
2018-02-15 05:26:43 +01:00
2018-10-15 02:28:44 +02:00
UIElems . gangInfo . appendChild ( createElement ( "p" , { // Faction reputation
2018-02-15 05:26:43 +01:00
display : "inline-block" ,
2018-02-09 22:11:43 +01:00
innerText : "Faction reputation: " + formatNumber ( rep , 3 )
} ) ) ;
2018-10-15 02:28:44 +02:00
UIElems . gangInfo . appendChild ( createElement ( "br" ) ) ;
const CyclesPerSecond = 1000 / Engine . _idleSpeed ;
UIElems . gangInfo . appendChild ( createElement ( "p" , { // Stored Cycles
innerText : ` Bonus time(s): ${ this . storedCycles / CyclesPerSecond } ` ,
display : "inline-block" ,
tooltip : "You gain bonus time while offline or when the game is inactive (e.g. when the tab is throttled by the browser). " +
"Bonus time makes the Gang mechanic progress faster, up to 10x the normal speed" ,
} ) ) ;
UIElems . gangInfo . appendChild ( createElement ( "br" ) ) ;
2017-08-13 07:01:33 +02:00
} else {
2018-10-15 02:28:44 +02:00
console . error ( "gang-info DOM element DNE" ) ;
2017-08-13 07:01:33 +02:00
}
//Toggle the 'Recruit member button' if valid
2018-10-15 02:28:44 +02:00
const numMembers = this . members . length ;
const respectCost = this . getRespectNeededToRecruitMember ( ) ;
2018-10-03 01:01:37 +02:00
2018-10-15 02:28:44 +02:00
const btn = UIElems . gangRecruitMemberButton ;
2018-10-02 18:45:00 +02:00
if ( numMembers >= MaximumGangMembers ) {
2017-08-13 07:01:33 +02:00
btn . className = "a-link-button-inactive" ;
2018-10-15 02:28:44 +02:00
UIElems . gangRecruitRequirementText . style . display = "inline-block" ;
UIElems . gangRecruitRequirementText . innerHTML = "You have reached the maximum amount of gang members" ;
} else if ( this . canRecruitMember ( ) ) {
2017-08-13 07:01:33 +02:00
btn . className = "a-link-button" ;
2018-10-15 02:28:44 +02:00
UIElems . gangRecruitRequirementText . style . display = "none" ;
2017-08-13 07:01:33 +02:00
} else {
btn . className = "a-link-button-inactive" ;
2018-10-15 02:28:44 +02:00
UIElems . gangRecruitRequirementText . style . display = "inline-block" ;
UIElems . gangRecruitRequirementText . innerHTML = ` ${ formatNumber ( respectCost , 2 ) } respect needed to recruit next member ` ;
2017-08-13 07:01:33 +02:00
}
//Update information for each gang member
2018-10-15 02:28:44 +02:00
for ( let i = 0 ; i < this . members . length ; ++ i ) {
this . updateGangMemberDisplayElement ( this . members [ i ] ) ;
2017-08-13 07:01:33 +02:00
}
}
}
//Takes in a GangMember object
2018-10-15 02:28:44 +02:00
Gang . prototype . createGangMemberDisplayElement = function ( memberObj ) {
if ( ! UIElems . gangContentCreated ) { return ; }
2018-10-11 01:13:31 +02:00
const name = memberObj . name ;
2017-08-13 07:01:33 +02:00
2018-10-18 21:52:02 +02:00
// Clear/Update the UIElems map to keep track of this gang member's panel
UIElems . gangMemberPanels [ name ] = { } ;
// Create the accordion
2018-02-09 22:11:43 +01:00
var accordion = createAccordionElement ( {
2018-10-11 01:13:31 +02:00
id : name + "gang-member" ,
hdrText : name ,
} ) ;
const li = accordion [ 0 ] ;
const hdr = accordion [ 1 ] ;
const gangMemberDiv = accordion [ 2 ] ;
2018-10-18 21:52:02 +02:00
UIElems . gangMemberPanels [ name ] [ "panel" ] = gangMemberDiv ;
2018-10-11 01:13:31 +02:00
// Gang member content divided into 3 panels:
// Panel 1 - Shows member's stats & Ascension stuff
const statsDiv = createElement ( "div" , {
class : "gang-member-info-div" ,
id : name + "gang-member-stats" ,
2018-10-15 02:28:44 +02:00
tooltipsmall : [ ` Hk: x ${ numeralWrapper . format ( memberObj . hack _mult * memberObj . hack _asc _mult , "0,0.00" ) } (x ${ numeralWrapper . format ( memberObj . hack _mult , "0,0.00" ) } Up, x ${ numeralWrapper . format ( memberObj . hack _asc _mult , "0,0.00" ) } Asc) ` ,
` St: x ${ numeralWrapper . format ( memberObj . str _mult * memberObj . str _asc _mult , "0,0.00" ) } (x ${ numeralWrapper . format ( memberObj . str _mult , "0,0.00" ) } Up, x ${ numeralWrapper . format ( memberObj . str _asc _mult , "0,0.00" ) } Asc) ` ,
` Df: x ${ numeralWrapper . format ( memberObj . def _mult * memberObj . def _asc _mult , "0,0.00" ) } (x ${ numeralWrapper . format ( memberObj . def _mult , "0,0.00" ) } Up, x ${ numeralWrapper . format ( memberObj . def _asc _mult , "0,0.00" ) } Asc) ` ,
` Dx: x ${ numeralWrapper . format ( memberObj . dex _mult * memberObj . dex _asc _mult , "0,0.00" ) } (x ${ numeralWrapper . format ( memberObj . dex _mult , "0,0.00" ) } Up, x ${ numeralWrapper . format ( memberObj . dex _asc _mult , "0,0.00" ) } Asc) ` ,
` Ag: x ${ numeralWrapper . format ( memberObj . agi _mult * memberObj . agi _asc _mult , "0,0.00" ) } (x ${ numeralWrapper . format ( memberObj . agi _mult , "0,0.00" ) } Up, x ${ numeralWrapper . format ( memberObj . agi _asc _mult , "0,0.00" ) } Asc) ` ,
` Ch: x ${ numeralWrapper . format ( memberObj . cha _mult * memberObj . cha _asc _mult , "0,0.00" ) } (x ${ numeralWrapper . format ( memberObj . cha _mult , "0,0.00" ) } Up, x ${ numeralWrapper . format ( memberObj . cha _asc _mult , "0,0.00" ) } Asc) ` ] . join ( "<br>" ) ,
2018-02-09 22:11:43 +01:00
} ) ;
2018-10-18 21:52:02 +02:00
UIElems . gangMemberPanels [ name ] [ "statsDiv" ] = statsDiv ;
2018-10-15 02:28:44 +02:00
const statsP = createElement ( "pre" , {
display : "inline" ,
id : name + "gang-member-stats-text" ,
2018-02-09 22:11:43 +01:00
} ) ;
2018-10-15 02:28:44 +02:00
const brElement = createElement ( "br" ) ;
2018-10-11 01:13:31 +02:00
const ascendButton = createElement ( "button" , {
class : "accordion-button" ,
innerText : "Ascend" ,
clickListener : ( ) => {
2018-10-15 02:28:44 +02:00
const popupId = ` gang-management-ascend-member ${ memberObj . name } ` ;
const ascendBenefits = memberObj . getAscensionResults ( ) ;
const txt = createElement ( "pre" , {
innerText : [ "Are you sure you want to ascend this member? (S)he will lose all of" ,
"his non-Augmentation upgrades and his/her stats will reset back to 1." ,
"In return, (s)he will gain the following permanent boost to stat multipliers:\n" ,
` Hacking: + ${ numeralWrapper . format ( ascendBenefits . hack , "0.00%" ) } ` ,
` Strength: + ${ numeralWrapper . format ( ascendBenefits . str , "0.00%" ) } ` ,
` Defense: + ${ numeralWrapper . format ( ascendBenefits . def , "0.00%" ) } ` ,
` Dexterity: + ${ numeralWrapper . format ( ascendBenefits . dex , "0.00%" ) } ` ,
` Agility: + ${ numeralWrapper . format ( ascendBenefits . agi , "0.00%" ) } ` ,
` Charisma: + ${ numeralWrapper . format ( ascendBenefits . cha , "0.00%" ) } ` ] . join ( "\n" ) ,
} ) ;
const confirmBtn = createElement ( "button" , {
class : "std-button" ,
clickListener : ( ) => {
this . ascendMember ( memberObj ) ;
this . updateGangMemberDisplayElement ( memberObj ) ;
removeElementById ( popupId ) ;
return false ;
} ,
innerText : "Ascend" ,
} ) ;
const cancelBtn = createElement ( "button" , {
class : "std-button" ,
clickListener : ( ) => {
removeElementById ( popupId ) ;
return false ;
} ,
innerText : "Cancel" ,
} ) ;
createPopup ( popupId , [ txt , confirmBtn , cancelBtn ] ) ;
2018-10-11 01:13:31 +02:00
}
2018-02-09 22:11:43 +01:00
} ) ;
2018-10-11 01:13:31 +02:00
const ascendHelpTip = createElement ( "div" , {
class : "help-tip" ,
clickListener : ( ) => {
2018-10-15 02:28:44 +02:00
dialogBoxCreate ( [ "TODO Ascending a Gang Member resets the member's progress and stats in exchange" ,
"for a permanent boost to their stat multipliers. " ] . join ( " " ) ) ;
2018-10-11 01:13:31 +02:00
} ,
2018-10-15 02:28:44 +02:00
innerText : "?" ,
marginTop : "5px" ,
2018-02-09 22:11:43 +01:00
} ) ;
2017-08-13 07:01:33 +02:00
statsDiv . appendChild ( statsP ) ;
2018-10-15 02:28:44 +02:00
statsDiv . appendChild ( brElement ) ;
2018-10-11 01:13:31 +02:00
statsDiv . appendChild ( ascendButton ) ;
statsDiv . appendChild ( ascendHelpTip ) ;
2017-08-13 07:01:33 +02:00
2018-10-11 01:13:31 +02:00
// Panel 2 - Task Selection & Info
const taskDiv = createElement ( "div" , {
class : "gang-member-info-div" ,
id : name + "gang-member-task" ,
2018-02-09 22:11:43 +01:00
} ) ;
2018-10-11 01:13:31 +02:00
const taskSelector = createElement ( "select" , {
id : name + "gang-member-task-selector" ,
2018-02-09 22:11:43 +01:00
} ) ;
2018-02-15 05:26:43 +01:00
2018-10-11 01:13:31 +02:00
// Get an array of the name of all tasks that are applicable for this Gang
let tasks = null ;
const allTasks = Object . keys ( GangMemberTasks ) ;
2017-08-13 07:01:33 +02:00
if ( Player . gang . isHackingGang ) {
2018-10-11 01:13:31 +02:00
tasks = allTasks . filter ( ( e ) => {
let task = GangMemberTasks [ e ] ;
if ( task == null ) { return false ; }
if ( e === "Unassigned" ) { return false ; }
return task . isHacking ;
} ) ;
2017-08-13 07:01:33 +02:00
} else {
2018-10-11 01:13:31 +02:00
tasks = allTasks . filter ( ( e ) => {
let task = GangMemberTasks [ e ] ;
if ( task == null ) { return false ; }
if ( e === "Unassigned" ) { return false ; }
return task . isCombat ;
} ) ;
2017-08-13 07:01:33 +02:00
}
2018-10-11 01:13:31 +02:00
tasks . unshift ( "---" ) ;
// Create selector for Gang member task
2017-08-13 07:01:33 +02:00
for ( var i = 0 ; i < tasks . length ; ++ i ) {
var option = document . createElement ( "option" ) ;
option . text = tasks [ i ] ;
taskSelector . add ( option ) ;
}
2018-10-15 02:28:44 +02:00
taskSelector . addEventListener ( "change" , ( ) => {
2017-08-13 07:01:33 +02:00
var task = taskSelector . options [ taskSelector . selectedIndex ] . text ;
memberObj . assignToTask ( task ) ;
2018-10-15 02:28:44 +02:00
this . setGangMemberTaskDescription ( memberObj , task ) ;
this . updateGangContent ( ) ;
2017-08-13 07:01:33 +02:00
} ) ;
2018-10-11 01:13:31 +02:00
// Set initial task in selector
2017-08-13 07:01:33 +02:00
if ( memberObj . task instanceof GangMemberTask ) {
var taskName = memberObj . task . name ;
var taskIndex = 0 ;
2017-08-30 19:44:29 +02:00
for ( let i = 0 ; i < tasks . length ; ++ i ) {
2018-10-11 01:13:31 +02:00
if ( taskName === tasks [ i ] ) {
2017-08-13 07:01:33 +02:00
taskIndex = i ;
break ;
}
}
taskSelector . selectedIndex = taskIndex ;
}
2018-02-09 22:11:43 +01:00
var gainInfo = createElement ( "p" , { id : name + "gang-member-gain-info" } ) ;
2017-08-13 07:01:33 +02:00
taskDiv . appendChild ( taskSelector ) ;
taskDiv . appendChild ( gainInfo ) ;
//Panel for Description of task
2018-02-09 22:11:43 +01:00
var taskDescDiv = createElement ( "div" , {
2018-10-11 01:13:31 +02:00
class : "gang-member-info-div" ,
id : name + "gang-member-task-desc" ,
2018-02-09 22:11:43 +01:00
} ) ;
2017-08-13 07:01:33 +02:00
2018-10-15 02:28:44 +02:00
var taskDescP = createElement ( "p" , {
display : "inline" ,
id : name + "gang-member-task-description" ,
} ) ;
2017-08-13 07:01:33 +02:00
taskDescDiv . appendChild ( taskDescP ) ;
gangMemberDiv . appendChild ( statsDiv ) ;
gangMemberDiv . appendChild ( taskDiv ) ;
gangMemberDiv . appendChild ( taskDescDiv ) ;
2018-10-15 02:28:44 +02:00
UIElems . gangMemberList . appendChild ( li ) ;
this . setGangMemberTaskDescription ( memberObj , taskName ) ; //Initialize description, TODO doesnt work rn
this . updateGangMemberDisplayElement ( memberObj ) ;
2017-08-13 07:01:33 +02:00
}
2018-10-15 02:28:44 +02:00
Gang . prototype . updateGangMemberDisplayElement = function ( memberObj ) {
if ( ! UIElems . gangContentCreated || ! Player . inGang ( ) ) { return ; }
2017-08-13 07:01:33 +02:00
var name = memberObj . name ;
2018-10-18 21:52:02 +02:00
// Update stats + exp
2017-08-13 07:01:33 +02:00
var stats = document . getElementById ( name + "gang-member-stats-text" ) ;
if ( stats ) {
2018-10-15 02:28:44 +02:00
stats . innerText =
[ ` Hacking: ${ formatNumber ( memberObj . hack , 0 ) } ( ${ numeralWrapper . format ( memberObj . hack _exp , '(0.00a)' ) } exp) ` ,
` Strength: ${ formatNumber ( memberObj . str , 0 ) } ( ${ numeralWrapper . format ( memberObj . str _exp , '(0.00a)' ) } exp) ` ,
` Defense: ${ formatNumber ( memberObj . def , 0 ) } ( ${ numeralWrapper . format ( memberObj . def _exp , '(0.00a)' ) } exp) ` ,
` Dexterity: ${ formatNumber ( memberObj . dex , 0 ) } ( ${ numeralWrapper . format ( memberObj . dex _exp , '(0.00a)' ) } exp) ` ,
` Agility: ${ formatNumber ( memberObj . agi , 0 ) } ( ${ numeralWrapper . format ( memberObj . agi _exp , '(0.00a)' ) } exp) ` ,
` Charisma: ${ formatNumber ( memberObj . cha , 0 ) } ( ${ numeralWrapper . format ( memberObj . cha _exp , '(0.00a)' ) } exp) ` ] . join ( "\n" ) ;
2017-08-13 07:01:33 +02:00
}
2018-10-18 21:52:02 +02:00
// Update tooltip for stat multipliers
const panel = UIElems . gangMemberPanels [ name ] ;
if ( panel ) {
const statsDiv = panel [ "statsDiv" ] ;
if ( statsDiv ) {
statsDiv . firstChild . innerHTML =
[ ` Hk: x ${ numeralWrapper . format ( memberObj . hack _mult * memberObj . hack _asc _mult , "0,0.00" ) } (x ${ numeralWrapper . format ( memberObj . hack _mult , "0,0.00" ) } Up, x ${ numeralWrapper . format ( memberObj . hack _asc _mult , "0,0.00" ) } Asc) ` ,
` St: x ${ numeralWrapper . format ( memberObj . str _mult * memberObj . str _asc _mult , "0,0.00" ) } (x ${ numeralWrapper . format ( memberObj . str _mult , "0,0.00" ) } Up, x ${ numeralWrapper . format ( memberObj . str _asc _mult , "0,0.00" ) } Asc) ` ,
` Df: x ${ numeralWrapper . format ( memberObj . def _mult * memberObj . def _asc _mult , "0,0.00" ) } (x ${ numeralWrapper . format ( memberObj . def _mult , "0,0.00" ) } Up, x ${ numeralWrapper . format ( memberObj . def _asc _mult , "0,0.00" ) } Asc) ` ,
` Dx: x ${ numeralWrapper . format ( memberObj . dex _mult * memberObj . dex _asc _mult , "0,0.00" ) } (x ${ numeralWrapper . format ( memberObj . dex _mult , "0,0.00" ) } Up, x ${ numeralWrapper . format ( memberObj . dex _asc _mult , "0,0.00" ) } Asc) ` ,
` Ag: x ${ numeralWrapper . format ( memberObj . agi _mult * memberObj . agi _asc _mult , "0,0.00" ) } (x ${ numeralWrapper . format ( memberObj . agi _mult , "0,0.00" ) } Up, x ${ numeralWrapper . format ( memberObj . agi _asc _mult , "0,0.00" ) } Asc) ` ,
` Ch: x ${ numeralWrapper . format ( memberObj . cha _mult * memberObj . cha _asc _mult , "0,0.00" ) } (x ${ numeralWrapper . format ( memberObj . cha _mult , "0,0.00" ) } Up, x ${ numeralWrapper . format ( memberObj . cha _asc _mult , "0,0.00" ) } Asc) ` ] . join ( "<br>" ) ;
}
}
// Update info about gang member's earnings/gains
2017-08-13 07:01:33 +02:00
var gainInfo = document . getElementById ( name + "gang-member-gain-info" ) ;
if ( gainInfo ) {
gainInfo . innerHTML =
2018-10-15 02:28:44 +02:00
[ ` Money: $ ${ formatNumber ( 5 * memberObj . calculateMoneyGain ( ) , 2 ) } / sec ` ,
` Respect: ${ formatNumber ( 5 * memberObj . calculateRespectGain ( ) , 6 ) } / sec ` ,
` Wanted Level: ${ formatNumber ( 5 * memberObj . calculateWantedLevelGain ( ) , 6 ) } / sec ` ,
` Total Respect Earned: ${ formatNumber ( memberObj . earnedRespect , 6 ) } ` ] . join ( "<br>" ) ;
2017-08-13 07:01:33 +02:00
}
}
2018-10-15 02:28:44 +02:00
Gang . prototype . setGangMemberTaskDescription = function ( memberObj , taskName ) {
const name = memberObj . name ;
const taskDesc = document . getElementById ( name + "gang-member-task-description" ) ;
2017-08-13 07:01:33 +02:00
if ( taskDesc ) {
var task = GangMemberTasks [ taskName ] ;
2018-10-11 01:13:31 +02:00
if ( task == null ) { task = GangMemberTasks [ "Unassigned" ] ; }
2017-08-13 07:01:33 +02:00
var desc = task . desc ;
taskDesc . innerHTML = desc ;
}
}
2017-08-30 19:44:29 +02:00
2018-10-15 02:28:44 +02:00
Gang . prototype . clearUI = function ( ) {
if ( UIElems . gangContainer instanceof Element ) { removeElement ( UIElems . gangContainer ) ; }
2018-03-12 20:39:04 +01:00
2018-10-15 02:28:44 +02:00
for ( const prop in UIElems ) {
UIElems [ prop ] = null ;
}
2018-10-18 21:52:02 +02:00
UIElems . gangContentCreated = false ;
UIElems . gangMemberUpgradeBoxOpened = false ;
UIElems . gangMemberPanels = { } ;
2018-10-15 02:28:44 +02:00
}