⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 unittable.cpp

📁 这是整套横扫千军3D版游戏的源码
💻 CPP
📖 第 1 页 / 共 2 页
字号:
// Based on Submarine's BuildTable Class from AAI. Thanks sub!

#include "UnitTable.h"


/// CR_BIND(CUnitTable, );
/// CR_REG_METADATA(CUnitTable, (
/// 	CR_MEMBER(all_lists),
/// 	CR_MEMBER(ground_factories),
/// 	CR_MEMBER(ground_builders),
/// 	CR_MEMBER(ground_attackers),
/// 	CR_MEMBER(metal_extractors),
/// 	CR_MEMBER(metal_makers),
/// 	CR_MEMBER(ground_energy),
/// 	CR_MEMBER(ground_defences),
/// 	CR_MEMBER(metal_storages),
/// 	CR_MEMBER(energy_storages),
/// 	CR_MEMBER(nuke_silos),
/// 
/// 	CR_MEMBER(numOfSides),
/// 	CR_MEMBER(sideNames),
/// 	CR_MEMBER(modSideMap),
/// 	CR_MEMBER(teamSides),
/// 
/// 	CR_MEMBER(unitTypes)
/// ));


CUnitTable::CUnitTable(AIClasses* ai) {
	this->ai = ai;

	numOfUnits = 0;
	unitList = 0;

	BuildModSideMap();
	ReadTeamSides();

	// now set up the unit lists
	ground_factories = new vector<int>[numOfSides];
	ground_builders = new vector<int>[numOfSides];
	ground_attackers = new vector<int>[numOfSides];
	metal_extractors = new vector<int>[numOfSides];
	metal_makers = new vector<int>[numOfSides];
	ground_energy = new vector<int>[numOfSides];
	ground_defences = new vector<int>[numOfSides];
	metal_storages = new vector<int>[numOfSides];
	energy_storages = new vector<int>[numOfSides];
	nuke_silos = new vector<int>[numOfSides];

	all_lists.push_back(ground_factories);
	all_lists.push_back(ground_builders);
	all_lists.push_back(ground_attackers);
	all_lists.push_back(metal_extractors);
	all_lists.push_back(metal_makers);
	all_lists.push_back(ground_energy);
	all_lists.push_back(ground_defences);
	all_lists.push_back(metal_storages);
	all_lists.push_back(energy_storages);
	all_lists.push_back(nuke_silos);
}

CUnitTable::~CUnitTable() {
	delete[] unitTypes;
	delete[] unitList;

	delete[] ground_factories;
	delete[] ground_builders;
	delete[] ground_attackers;
	delete[] metal_extractors;
	delete[] metal_makers;
	delete[] ground_energy;
	delete[] ground_defences;
	delete[] metal_storages;
	delete[] energy_storages;
	delete[] nuke_silos;
}


int CUnitTable::BuildModSideMap() {
	// get all sides and commanders
	std::string commKey;	// eg. "SIDE4\\commander"
	std::string commName;	// eg. "arm_commander"
	std::string sideKey;	// eg. "SIDE4\\name"
	std::string sideName;	// eg. "Arm"
	char sideNum[64] = {0};

	ai->parser->LoadVirtualFile("gamedata\\SIDEDATA.tdf");

	// look at SIDE0 through SIDE9
	// (should be enough for any mod)
	for (int i = 0; i < 10; i++) {
		sprintf(sideNum, "%i", i);

		commKey = "SIDE" + std::string(sideNum) + "\\commander";
		sideKey = "SIDE" + std::string(sideNum) + "\\name";

		ai->parser->GetDef(commName, "-1", commKey);
		const UnitDef* udef = ai->cb->GetUnitDef(commName.c_str());

		if (udef) {
			// if this unit exists, the side is valid too
			startUnits.push_back(udef->id);
			ai->parser->GetDef(sideName, "-1", sideKey);
			sideNames.push_back(sideName);
			modSideMap[sideName] = i;
			numOfSides = i + 1;
		}
	}

	return numOfSides;
}

int CUnitTable::ReadTeamSides() {
	char scriptFileName[1024] = {0};
	ai->cb->GetValue(AIVAL_SCRIPT_FILENAME_CSTR, scriptFileName);

	teamSides.resize(MAX_TEAMS, 0);
	teamSides[0] = 0;	// team 0 defaults to side 0
	teamSides[1] = 1;	// team 1 defaults to side 1

	if (scriptFileName[0] > 0) {
		// got a GameSetup script
		char sideKey[128] = {0};
		char sideName[128] = {0};

		CSunParser scriptFileParser(ai);
		scriptFileParser.LoadRealFile(scriptFileName);

		for (int i = 0; i < MAX_TEAMS; i++) {
			sideName[0] = 0;
			snprintf(sideKey, 127, "GAME\\TEAM%d\\Side", i);
			scriptFileParser.GetDef(sideName, "", sideKey);

			if (sideName[0] > 0) {
				// FIXME: Gaia-team side?
				teamSides[i] = modSideMap[std::string(sideName)];
			}
		}

		return teamSides[ai->cb->GetMyTeam()];
	} else {
		return teamSides[1];
	}
}


int CUnitTable::GetSide(int unitID) {
	int team = ai->cb->GetUnitTeam(unitID);
	int side = teamSides[team];

	return side;
}

int CUnitTable::GetCategory(const UnitDef* unitdef) {
	return unitTypes[unitdef->id].category;
}

int CUnitTable::GetCategory(int unit) {
	assert(ai->cb->GetUnitDef(unit) != NULL);

	return (unitTypes[ai->cb->GetUnitDef(unit)->id].category);
}



// used to update threat-map
float CUnitTable::GetDPS(const UnitDef* unit) {
	if (unit) {
		float totaldps = 0.0f;

		for (vector<UnitDef::UnitDefWeapon>::const_iterator i = unit->weapons.begin(); i != unit->weapons.end(); i++) {
			float dps = 0.0f;
			if (!i->def->paralyzer) {
				int numberofdamages;
				ai->cb->GetValue(AIVAL_NUMDAMAGETYPES, &numberofdamages);
				float reloadtime = i->def->reload;

				for (int k = 0; k < numberofdamages; k++) {
					dps += i->def->damages[k];
				}

				dps = dps * i->def->salvosize / numberofdamages / reloadtime;
			}

			totaldps += dps;
		}

		return totaldps;
	}

	return 0.0f;
}



float CUnitTable::GetDPSvsUnit(const UnitDef* unit, const UnitDef* victim) {
	if (unit->weapons.size()) {
		ai->math->TimerStart();

		float dps = 0.0f;
		bool canhit = false;
		string targetcat;
		int armortype = victim->armorType;
		int numberofdamages;
		ai->cb->GetValue(AIVAL_NUMDAMAGETYPES, &numberofdamages);

		for (unsigned int i = 0; i != unit->weapons.size(); i++) {
			if (!unit->weapons[i].def->paralyzer) {
				targetcat = unitTypes[unit->id].TargetCategories[i];

				unsigned int a = victim->category;
				unsigned int b = unit->weapons[i].def->onlyTargetCategory;	// what the weapon can target
				unsigned int c = unit->weapons[i].onlyTargetCat;			// what the unit accepts as this weapons target
//				unsigned int d = unit->weapons[i].badTargetCat;				// what the unit thinks this weapon must be used for (hmm ?)
				bool canWeaponTarget = (a & b) > 0;
				bool canUnitTarget = (a & c) > 0;							// how is this used?
//				bool badUnitTarget = (a & d) > 0;							// probably means that it has low priority

				canhit = (canWeaponTarget && canUnitTarget);

				if (!unit->weapons[i].def->waterweapon && ai->cb->GetUnitDefHeight(victim->id) - victim->waterline < 0) {
					// weapon cannot hit this sub
					canhit = false;
				}

				if (unit->weapons[i].def->waterweapon && victim->minWaterDepth == 0) {
					// anti-sub weapon cannot kill this unit
					canhit = false;
				}

				// bombers are useless against air
				if (unit->weapons[i].def->dropped && victim->canfly && unit->canfly && unit->wantedHeight <= victim->wantedHeight) {
					canhit = false;
				}

				if (canhit) {
					float accuracy = unit->weapons[i].def->accuracy * 2.8;

					if (victim->speed != 0) {
						accuracy *= 1 - (unit->weapons[i].def->targetMoveError);
					}

					float basedamage = unit->weapons[i].def->damages[armortype] * unit->weapons[i].def->salvosize / unit->weapons[i].def->reload;
					float AOE = unit->weapons[i].def->areaOfEffect * 0.7;
					float tohitprobability = 0.0f;
					float impactarea = 0.0f;
					float targetarea = 0.0f;
					float distancetravelled = 0.7f * unit->weapons[i].def->range;
					float firingangle = 0.0f;
					float gravity = -(ai->cb->GetGravity() * 900);
					float timetoarrive = 0.0f;
					float u = unit->weapons[i].def->projectilespeed * 30;

					if (unit->weapons[i].def->type == string("Cannon")) {
						float sinoid = (distancetravelled * gravity) / (u * u);
						sinoid = min(sinoid, 1.0f);
						firingangle = asin(sinoid) / 2;

						if (unit->highTrajectoryType == 1) {
							firingangle = (PI / 2) - firingangle;
						}

						float heightreached = pow(u * sin(firingangle), 2) / (2 * gravity);
						float halfd = distancetravelled / 2;
						distancetravelled = 2 * sqrt(halfd * halfd + heightreached * heightreached) * 1.1;
					}

					if ((victim->canfly && unit->weapons[i].def->selfExplode) || !victim->canfly) {
						impactarea = pow((accuracy * distancetravelled) + AOE, 2);
						targetarea = ((victim->xsize * 16) + AOE) * ((victim->ysize * 16) + AOE);
					} else {
						impactarea = pow((accuracy) * (0.7f * distancetravelled), 2);
						targetarea = (victim->xsize * victim->ysize * 256);
					}

					if (impactarea > targetarea) {
						tohitprobability = targetarea / impactarea;
					} else {
						tohitprobability = 1;
					}

					if (!unit->weapons[i].def->guided && unit->weapons[i].def->projectilespeed != 0 && victim->speed != 0 && unit->weapons[i].def->beamtime == 1) {
						if (unit->weapons[i].def->type == string("Cannon")) {
							timetoarrive = (2 * u * sin(firingangle)) / gravity;
						} else {
							timetoarrive = distancetravelled / (unit->weapons[i].def->projectilespeed * 30);
						}

						float shotwindow = sqrt(targetarea) / victim->speed * 1.3;

						if (shotwindow < timetoarrive) {
							tohitprobability *= shotwindow / timetoarrive;
						}
					}

					dps += basedamage * tohitprobability;
				}
			}
		}

		return dps;
	}

	return 0.0f;
}



float CUnitTable::GetCurrentDamageScore(const UnitDef* unit) {
	int enemies[MAX_UNITS];
	int numEnemies = ai->cheat->GetEnemyUnits(enemies);
	vector<int> enemiesOfType;
	float score = 0.01f;
	float totalCost = 0.01f;
	enemiesOfType.resize(ai->cb->GetNumUnitDefs() + 1, 0);

	for (int i = 0; i < numEnemies; i++) {
		const UnitDef* udef = ai->cheat->GetUnitDef(enemies[i]);

		if (udef) {
			enemiesOfType[udef->id]++;
		}
	}

	for (unsigned int i = 1; i < enemiesOfType.size(); i++) {
		bool b1 = unitTypes[i].def->builder;
		bool b2 = (enemiesOfType[i] > 0);
		bool b3 = (unitTypes[i].sides.size() > 0);
		// bool b4 = (!unit->speed && !unitTypes[i].def->speed);

		if (!b1 && b2 && b3 /* && !b4 */) {
			float currentScore = 0.0f;
			float costOfEnemiesOfThisType = ((unitTypes[i].def->metalCost * METAL2ENERGY) + unitTypes[i].def->energyCost) * enemiesOfType[i];
			currentScore = unitTypes[unit->id].DPSvsUnit[i] * costOfEnemiesOfThisType;
			totalCost += costOfEnemiesOfThisType;

			/*
			if (unitTypes[i].DPSvsUnit[unit->id] * costofenemiesofthistype > 0) {
				currentscore -= (unitTypes[i].DPSvsUnit[unit->id] * costofenemiesofthistype);
			}
			*/

			score += currentScore;
		}
	}

	if (totalCost <= 0)
		return 0.0f;

	return (score / totalCost);
}




void CUnitTable::UpdateChokePointArray() {
	vector<float> EnemyCostsByMoveType;
	EnemyCostsByMoveType.resize(ai->pather->NumOfMoveTypes);
	vector<int> enemiesOfType;
	float totalCost = 1.0f;
	int enemies[MAX_UNITS];
	int numEnemies = ai->cheat->GetEnemyUnits(enemies);
	enemiesOfType.resize(ai->cb->GetNumUnitDefs() + 1, 0);

	for (int i = 0; i < ai->pather->totalcells; i++) {
		ai->dm->ChokePointArray[i] = 0;
	}
	for (int i = 0; i < ai->pather->NumOfMoveTypes; i++) {
		EnemyCostsByMoveType[i] = 0;
	}
	for (int i = 0; i < numEnemies; i++) {
		enemiesOfType[ai->cheat->GetUnitDef(enemies[i])->id]++;
	}

	for (unsigned int i = 1; i < enemiesOfType.size(); i++) {
		if (unitTypes[i].sides.size() > 0 && !unitTypes[i].def->canfly && unitTypes[i].def->speed > 0) {
			float currentcosts = ((unitTypes[i].def->metalCost * METAL2ENERGY) + unitTypes[i].def->energyCost) * (enemiesOfType[i]);
			EnemyCostsByMoveType[unitTypes[i].def->moveType] += currentcosts;
			totalCost += currentcosts;
		}
	}

	for (int i = 0; i < ai->pather->NumOfMoveTypes; i++) {
		EnemyCostsByMoveType[i] /= totalCost;

		for (int c = 0; c < ai->pather->totalcells; c++) {
			ai->dm->ChokePointArray[c] += ai->dm->ChokeMapsByMovetype[i][c] * EnemyCostsByMoveType[i];
		}
	}
}






float CUnitTable::GetScore(const UnitDef* udef, int category) {
	int m = (ai->uh->AllUnitsByType[udef->id]).size();
	int n = udef->maxThisUnit;

	if (m >= n) {
		// if we've hit the build-limit for this
		// type of unit, make sure GetUnitByScore()
		// won't pick it for construction anyway
		return 0.0f;
	}

	int frame = ai->cb->GetCurrentFrame();
	float Cost = ((udef->metalCost * METAL2ENERGY) + udef->energyCost) + 0.1f;
	float CurrentIncome = INCOMEMULTIPLIER * (ai->cb->GetEnergyIncome() + (ai->cb->GetMetalIncome() * METAL2ENERGY)) + frame / 2;
	float Hitpoints = udef->health;
	float buildTime = udef->buildTime + 0.1f;
	float benefit = 0.0f;
	float dps = 0.0f;
	int unitcounter = 0;
	bool candevelop = false;

	float RandNum = ai->math->RandNormal(4, 3, 1) + 1;
	float randMult = float((rand() % 2) + 1);

	switch (category) {
		case CAT_ENERGY: {
			// KLOOTNOTE: factor build-time into this as well
			// (so benefit values generally lie closer together)
			// benefit = (udef->energyMake - udef->energyUpkeep);
			// benefit = (udef->energyMake - udef->energyUpkeep) * randMult;
			benefit = ((udef->energyMake - udef->energyUpkeep) / buildTime) * randMult;

			if (udef->windGenerator) {
				benefit += ai->cb->GetMinWind();
			}
			if (udef->tidalGenerator) {
				benefit += ai->cb->GetTidalStrength();
			}

			// filter geothermals
			if (udef->needGeo)
				benefit = 0.0f;

			// KLOOTNOTE: dividing by cost here as well means
			// benefit is inversely proportional to square of
			// cost, so expensive generators are quadratically
			// less likely to be built if original calculation
			// of score is used
			// benefit /= Cost;
		} break;

		case CAT_MEX: {
			benefit = pow(udef->extractsMetal, 4.0f);
		} break;
		case CAT_MMAKER: {
			// benefit = ((udef->metalMake - udef->metalUpkeep) / udef->energyUpkeep) + 0.01f;
			benefit = (udef->metalMake - udef->metalUpkeep) / (udef->energyUpkeep + 0.01f);
		} break;

		case CAT_G_ATTACK: {
			dps = GetCurrentDamageScore(udef);

			if (udef->canfly && !udef->hoverAttack) {
				// TODO: improve to set reload-time to the bomber's
				// turnspeed vs. movespeed (eg. time taken for a run)
				dps /= 6;
			}

			benefit = pow((udef->weapons.front().def->areaOfEffect + 80), 1.5f)
					* pow(GetMaxRange(udef) + 200, 1.5f)
					* pow(dps, 1.0f)
					* pow(udef->speed + 40, 1.0f)
					* pow(Hitpoints, 1.0f)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -