Skip to content

Commit

Permalink
Droid ranks are now a brain property and can be upgraded.
Browse files Browse the repository at this point in the history
Droid ranks (levels) are now defined in the brain stats file,
and can be different for each defined brain type. The required
experience for increasing in rank, and the number of droids
that a commander can control (base and multiplied by level both),
are stored as upgrades, and can be modified from scripts. This
can be used to implement for example research upgrades touching
these stats. It is also possible to name ranks differently for
each brain type.

Adds new cheat 'bettertogether' that boosts the experience of
selected droids.

Adds new tech 'Command Turret Upgrade' that increases command
limit of commanders by 20%, increases extra command limit per
rank level by 50%, and reduces experience required per level
for commanders by 10%. This should not imbalance commanders
since they are still useless.
  • Loading branch information
perim committed Jun 13, 2017
1 parent 4a64d9d commit e760917
Show file tree
Hide file tree
Showing 11 changed files with 232 additions and 49 deletions.
4 changes: 4 additions & 0 deletions data/base/stats/brain.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@
"maxDroids": 6,
"maxDroidsMult": 2,
"name": "Command Turret",
"ranks": [ "Rookie", "Green", "Trained", "Regular", "Professional", "Veteran", "Elite", "Special", "Hero" ],
"thresholds": [ 0, 8, 16, 32, 64, 128, 256, 512, 1024 ],
"turret": "CommandTurret1"
},
"ZNULLBRAIN": {
"id": "ZNULLBRAIN",
"name": "Z NULL BRAIN",
"ranks": [ "Rookie", "Green", "Trained", "Regular", "Professional", "Veteran", "Elite", "Special", "Hero" ],
"thresholds": [ 0, 4, 8, 16, 32, 64, 128, 256, 512 ],
"turret": "ZNULLWEAPON"
}
}
29 changes: 27 additions & 2 deletions data/mp/multiplay/skirmish/rules.js
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ function eventResearched(research, structure, player)
for (var i = 0; i < research.results.length; i++)
{
var v = research.results[i];
//debug(" RESULT : class=" + v['class'] + " parameter=" + v['parameter'] + " value=" + v['value'] + " filter=" + v['filterParameter'] + " filterparam=" + v['filterParameter']);
//debug(" RESULT : class=" + v['class'] + " parameter=" + v['parameter'] + " value=" + v['value'] + " filter=" + v['filterParameter'] + " filterval=" + v['filterValue']);
for (var cname in Upgrades[player][v['class']]) // iterate over all components of this type
{
var parameter = v['parameter'];
Expand All @@ -349,7 +349,17 @@ function eventResearched(research, structure, player)
{
continue;
}
if (Stats[ctype][cname][parameter] > 0) // only applies if stat has above zero value already
if (Stats[ctype][cname][parameter] instanceof Array)
{
var dst = Upgrades[player][ctype][cname][parameter].slice()
for (var x = 0; x < dst.length; x++)
{
dst[x] += Math.ceil(Stats[ctype][cname][parameter][x] * v['value'] / 100);
}
Upgrades[player][ctype][cname][parameter] = dst
//debug(" upgraded to " + dst);
}
else if (Stats[ctype][cname][parameter] > 0) // only applies if stat has above zero value already
{
Upgrades[player][ctype][cname][parameter] += Math.ceil(Stats[ctype][cname][parameter] * v['value'] / 100);
//debug(" upgraded " + cname + " to " + Upgrades[player][ctype][cname][parameter] + " by " + Math.ceil(Stats[ctype][cname][parameter] * v['value'] / 100));
Expand All @@ -365,6 +375,21 @@ function eventCheatMode(entered)

function eventChat(from, to, message)
{
if (message == "bettertogether" && cheatmode)
{
for (var i in Upgrades[from].Brain)
{
if (Upgrades[from].Brain[i].BaseCommandLimit > 0) // is commander
{
Upgrades[from].Brain[i].BaseCommandLimit += 4;
Upgrades[from].Brain[i].CommandLimitByLevel += 2;
// you must set the thresholds this way, as an array, because of the clunky
// way that this is implemented behind the scenes
Upgrades[from].Brain[i].RankThresholds = [ 0, 2, 4, 8, 16, 24, 32, 48, 64 ];
}
}
console("Made player " + from + "'s commanders SUPERIOR!");
}
if (message == "makesuperior" && cheatmode)
{
for (var i in Upgrades[from].Body)
Expand Down
32 changes: 32 additions & 0 deletions data/mp/stats/research.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,38 @@
],
"statID": "CommandBrain01"
},
"R-Comp-CommandTurret02": {
"iconID": "IMAGE_RES_COMPUTERTECH",
"id": "R-Comp-CommandTurret02",
"msgName": "RES_C_CT2",
"name": "Command Turret Upgrade",
"requiredResearch": [
"R-Comp-CommandTurret01",
"R-Struc-Research-Upgrade07"
],
"researchPoints": 5000,
"researchPower": 450,
"results": [
{
"class": "Brain",
"parameter": "BaseCommandLimit",
"value": 20
},
{
"class": "Brain",
"parameter": "CommandLimitByLevel",
"value": 50
},
{
"class": "Brain",
"filterParameter": "Id",
"filterValue": "CommandBrain01",
"parameter": "RankThresholds",
"value": -10
}
],
"statID": "CommandBrain01"
},
"R-Comp-SynapticLink": {
"iconID": "IMAGE_RES_COMPUTERTECH",
"id": "R-Comp-SynapticLink",
Expand Down
9 changes: 9 additions & 0 deletions po/custom/fromJson.txt
Original file line number Diff line number Diff line change
Expand Up @@ -995,6 +995,15 @@ _("NEXUS Resistance Circuits Mk2")
_("NEXUS Resistance Circuits Mk3")
_("NEXUS Wall")
_("No Place To Hide")
NP_("rank", "Elite")
NP_("rank", "Green")
NP_("rank", "Hero")
NP_("rank", "Professional")
NP_("rank", "Regular")
NP_("rank", "Rookie")
NP_("rank", "Special")
NP_("rank", "Trained")
NP_("rank", "Veteran")
_("NullBot")
_("Oil Derrick")
_("Oil Drum")
Expand Down
11 changes: 7 additions & 4 deletions po/parseJson.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,21 @@

def printString(s, begin, end):
if not re.match(r'^(\*.*\*|CAM[0-9] .*|Z ?NULL.*)$', s):
sys.stdout.write('{}_({}){}'.format(begin, json.dumps(s, ensure_ascii=False), end))
sys.stdout.write('{}{}){}'.format(begin, json.dumps(s, ensure_ascii=False), end))

def parse(obj):
if isinstance(obj, dict):
for k, v in obj.items():
parse(v)
if k == 'name' and isinstance(v, str):
printString(v, '', '\n')
elif k == 'text' and isinstance(v, list):
printString(v, '_(', '\n')
elif k in ['text', 'ranks'] and isinstance(v, list):
for s in v:
if isinstance(s, str):
printString(s, '', '\n')
if k == 'text':
printString(s, '_(', '\n')
elif k == 'ranks':
printString(s, 'NP_("rank", ', '\n')
elif isinstance(obj, list):
for v in obj:
parse(v)
Expand Down
4 changes: 2 additions & 2 deletions src/cmddroid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,8 @@ SDWORD cmdDroidGetIndex(DROID *psCommander)
/** This function returns the maximum group size of the command droid.*/
unsigned int cmdDroidMaxGroup(const DROID *psCommander)
{
return getDroidLevel(psCommander) * getBrainStats(const_cast<DROID *>(psCommander))->maxDroidsMult
+ getBrainStats(const_cast<DROID *>(psCommander))->maxDroids;
const BRAIN_STATS *psStats = getBrainStats(psCommander);
return getDroidLevel(psCommander) * psStats->upgrade[psCommander->player].maxDroidsMult + psStats->upgrade[psCommander->player].maxDroids;
}

/** This function adds experience to the command droid of the psKiller's command group.*/
Expand Down
37 changes: 7 additions & 30 deletions src/droid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2130,41 +2130,26 @@ struct rankMap
const char *name; // name of this rank
};

static const struct rankMap arrRank[] =
{
{0, 0, N_("Rookie")},
{4, 8, NP_("rank", "Green")},
{8, 16, N_("Trained")},
{16, 32, N_("Regular")},
{32, 64, N_("Professional")},
{64, 128, N_("Veteran")},
{128, 256, N_("Elite")},
{256, 512, N_("Special")},
{512, 1024, N_("Hero")}
};

unsigned int getDroidLevel(const DROID *psDroid)
{
bool isCommander = (psDroid->droidType == DROID_COMMAND ||
psDroid->droidType == DROID_SENSOR) ? true : false;
unsigned int numKills = psDroid->experience / 65536;
unsigned int i;

// Search through the array of ranks until one is found
// which requires more kills than the droid has.
// Then fall back to the previous rank.
for (i = 1; i < ARRAY_SIZE(arrRank); ++i)
const BRAIN_STATS *psStats = getBrainStats(psDroid);
auto &vec = psStats->upgrade[psDroid->player].rankThresholds;
for (i = 1; i < vec.size(); ++i)
{
const unsigned int requiredKills = isCommander ? arrRank[i].commanderKills : arrRank[i].kills;

if (numKills < requiredKills)
if (numKills < vec.at(i))
{
return i - 1;
}
}

// If the criteria of the last rank are met, then select the last one
return ARRAY_SIZE(arrRank) - 1;
return vec.size() - 1;
}

UDWORD getDroidEffectiveLevel(DROID *psDroid)
Expand All @@ -2184,18 +2169,10 @@ UDWORD getDroidEffectiveLevel(DROID *psDroid)
return MAX(level, cmdLevel);
}


const char *getDroidNameForRank(UDWORD rank)
{
ASSERT_OR_RETURN(PE_("rank", "invalid"), rank < (sizeof(arrRank) / sizeof(struct rankMap)),
"given rank number (%d) out of bounds, we only have %lu ranks", rank, (unsigned long)(sizeof(arrRank) / sizeof(struct rankMap)));

return PE_("rank", arrRank[rank].name);
}

const char *getDroidLevelName(DROID *psDroid)
{
return (getDroidNameForRank(getDroidLevel(psDroid)));
const BRAIN_STATS *psStats = getBrainStats(psDroid);
return PE_("rank", psStats->rankNames[getDroidLevel(psDroid)].c_str());
}

UDWORD getNumDroidsForLevel(UDWORD level)
Expand Down
24 changes: 18 additions & 6 deletions src/qtscriptdebug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -410,30 +410,42 @@ QStandardItemList componentToString(const QString &name, const COMPONENT_STATS *
else if (psStats->compType == COMP_BRAIN)
{
const BRAIN_STATS *psBrain = (const BRAIN_STATS *)psStats;
key->appendRow(QStandardItemList{ new QStandardItem("^Max droids"), new QStandardItem(QString::number(psBrain->maxDroids)) });
key->appendRow(QStandardItemList{ new QStandardItem("^Extra droids/level"), new QStandardItem(QString::number(psBrain->maxDroidsMult)) });
QStringList ranks;
for (const std::string &s : psBrain->rankNames)
{
ranks.append(QString::fromStdString(s));
}
QStringList thresholds;
for (int t : psBrain->upgrade[player].rankThresholds)
{
thresholds.append(QString::number(t));
}
key->appendRow(QStandardItemList{ new QStandardItem("^Base command limit"), new QStandardItem(QString::number(psBrain->upgrade[player].maxDroids)) });
key->appendRow(QStandardItemList{ new QStandardItem("^Extra command limit by level"), new QStandardItem(QString::number(psBrain->upgrade[player].maxDroidsMult)) });
key->appendRow(QStandardItemList{ new QStandardItem("^Rank names"), new QStandardItem(ranks.join(", ")) });
key->appendRow(QStandardItemList{ new QStandardItem("^Rank thresholds"), new QStandardItem(thresholds.join(", ")) });
}
else if (psStats->compType == COMP_REPAIRUNIT)
{
const REPAIR_STATS *psRepair = (const REPAIR_STATS *)psStats;
key->appendRow(QStandardItemList{ new QStandardItem("^Repair time"), new QStandardItem(QString::number(psRepair->time)) });
key->appendRow(QStandardItemList{ new QStandardItem("^Base repair points"), new QStandardItem(QString::number(psRepair->base.repairPoints)) });
key->appendRow(QStandardItemList{ new QStandardItem("^Base repair points"), new QStandardItem(QString::number(psRepair->upgrade[player].repairPoints)) });
}
else if (psStats->compType == COMP_ECM)
{
const ECM_STATS *psECM = (const ECM_STATS *)psStats;
key->appendRow(QStandardItemList{ new QStandardItem("^Base range"), new QStandardItem(QString::number(psECM->base.range)) });
key->appendRow(QStandardItemList{ new QStandardItem("^Base range"), new QStandardItem(QString::number(psECM->upgrade[player].range)) });
}
else if (psStats->compType == COMP_SENSOR)
{
const SENSOR_STATS *psSensor = (const SENSOR_STATS *)psStats;
key->appendRow(QStandardItemList{ new QStandardItem("^Sensor type"), new QStandardItem(QString::number(psSensor->type)) });
key->appendRow(QStandardItemList{ new QStandardItem("^Base range"), new QStandardItem(QString::number(psSensor->base.range)) });
key->appendRow(QStandardItemList{ new QStandardItem("^Base range"), new QStandardItem(QString::number(psSensor->upgrade[player].range)) });
}
else if (psStats->compType == COMP_CONSTRUCT)
{
const CONSTRUCT_STATS *psCon = (const CONSTRUCT_STATS *)psStats;
key->appendRow(QStandardItemList{ new QStandardItem("^Base construct points"), new QStandardItem(QString::number(psCon->base.constructPoints)) });
key->appendRow(QStandardItemList{ new QStandardItem("^Base construct points"), new QStandardItem(QString::number(psCon->upgrade[player].constructPoints)) });
}
return QStandardItemList { key, value };
}
Expand Down
Loading

0 comments on commit e760917

Please sign in to comment.