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

📄 unittable.cpp

📁 这是整套横扫千军3D版游戏的源码
💻 CPP
📖 第 1 页 / 共 2 页
字号:
					* pow(RandNum, 2.5f)
					* pow(Cost, -0.5f);

			if (udef->canfly || udef->canhover) {
				// general hack: reduce feasibility of aircraft for 20 mins
				// and that of hovercraft permanently, should mostly prefer
				// real L2 units to hovers
				benefit = (udef->canfly && frame >= (30 * 60 * 20))? benefit: benefit * 0.01f;
			}
		} break;

		case CAT_DEFENCE: {
			benefit = pow((udef->weapons.front().def->areaOfEffect + 80), 1.5f)
					* pow(GetMaxRange(udef), 2.0f)
					* pow(GetCurrentDamageScore(udef), 1.5f)
					* pow(Hitpoints, 0.5f)
					* pow(RandNum, 2.5f)
					* pow(Cost, -1.0f);
		} break;

		case CAT_BUILDER: {
			for (unsigned int i = 0; i != unitTypes[udef->id].canBuildList.size(); i++) {
				if (unitTypes[unitTypes[udef->id].canBuildList[i]].category == CAT_FACTORY) {
					candevelop = true;
				}
			}

			// builder units that cannot construct any
			// factories are worthless, prevent them
			// from being chosen via GetUnitByScore()
			// (they might have other uses though, eg.
			// nano-towers)
			if (!candevelop) {
				benefit = 0.0f;
			} else {
				benefit = pow(udef->buildSpeed, 1.0f)
						* pow(udef->speed, 0.5f)
						* pow(Hitpoints, 0.3f)
						* pow(RandNum, 0.4f);
			}
		} break;

		case CAT_FACTORY: {
			// benefit of a factory is dependant on the kind of
			// offensive units it can build, but EE-hubs are only
			// capable of building other buildings
			for (unsigned int i = 0; i != unitTypes[udef->id].canBuildList.size(); i++) {
				int buildOption = unitTypes[udef->id].canBuildList[i];
				int buildOptionCategory = unitTypes[buildOption].category;

				if (buildOptionCategory == CAT_G_ATTACK || buildOptionCategory == CAT_FACTORY) {
					if (unitTypes[buildOption].def != udef) {
						// KLOOTNOTE: guard against infinite recursion (BuildTowers in
						// PURE trigger this since they are able to build themselves)
						benefit += GetScore(unitTypes[buildOption].def, buildOptionCategory);
						unitcounter++;
					}
				}
			}

			if (unitcounter > 0) {
				benefit /= (unitcounter * pow(float(ai->uh->AllUnitsByType[udef->id].size() + 1), 3.0f));
			} else {
				benefit = 0.0f;
			}
		} break;

		case CAT_MSTOR: {
			benefit = pow((udef->metalStorage), 1.0f) * pow(Hitpoints, 1.0f);
		} break;
		case CAT_ESTOR: {
			benefit = pow((udef->energyStorage), 1.0f) * pow(Hitpoints, 1.0f);
		} break;
		case CAT_NUKE: {
			// KLOOTNOTE: should factor damage into this as well
			float metalcost = udef->stockpileWeaponDef->metalcost;
			float energycost = udef->stockpileWeaponDef->energycost;
			float supplycost = udef->stockpileWeaponDef->supplycost;
			float denom = metalcost + energycost + supplycost + 1.0f;
			float range = udef->stockpileWeaponDef->range;
			benefit = (udef->stockpileWeaponDef->areaOfEffect + range) / denom;
		} break;
		/*
		case CAT_ANTINUKE: {
			benefit = udef->stockpileWeaponDef->coverageRange;
		} break;
		case CAT_SHIELD: {
			benefit = udef->shieldWeaponDef->shieldRadius;
		} break;
		*/
		default:
			benefit = 0.0f;
	}

	// return (benefit / (CurrentIncome + Cost));
	// return ((benefit / Cost) * CurrentIncome);
	return ((CurrentIncome / Cost) * benefit);
}



// operates in terms of GetScore()
const UnitDef* CUnitTable::GetUnitByScore(int builderUnitID, int category) {
	vector<int>* tempList = 0;
	const UnitDef* builderDef = ai->cb->GetUnitDef(builderUnitID);
	const UnitDef* tempUnitDef = 0;
	int side = GetSide(builderUnitID);
	float tempScore = 0.0f;
	float bestScore = 0.0f;

	switch (category) {
		case CAT_ENERGY:
			tempList = ground_energy;
			break;
		case CAT_MEX:
			tempList = metal_extractors;
			break;
		case CAT_MMAKER:
			tempList = metal_makers;
			break;
		case CAT_G_ATTACK:
			tempList = ground_attackers;
			break;
		case CAT_DEFENCE:
			tempList = ground_defences;
			break;
		case CAT_BUILDER:
			tempList = ground_builders;
			break;
		case CAT_FACTORY:
			tempList = ground_factories;
			break;
		case CAT_MSTOR:
			tempList = metal_storages;
			break;
		case CAT_ESTOR:
			tempList = energy_storages;
			break;
		case CAT_NUKE:
			tempList = nuke_silos;
			break;
	}

	// iterate over all units for <side> in tempList (eg. Core ground_defences)
	for (unsigned int i = 0; i != tempList[side].size(); i++) {
		int tempUnitDefID = tempList[side][i];

		// if our builder can build the i-th unit
		if (CanBuildUnit(builderDef->id, tempUnitDefID)) {
			// get the unit's heuristic score (based on current income)
			tempScore = GetScore(unitTypes[tempUnitDefID].def, category);

			if (tempScore > bestScore) {
				bestScore = tempScore;
				tempUnitDef = unitTypes[tempUnitDefID].def;
			}
		}
	}

	// if we didn't find a unit to build with score > 0 (ie.
	// if builder has no build-option matching this category)
	// then return NULL instead of first option on build menu
	// (to prevent radar farms and other bizarro side-effects)
	return ((bestScore > 0.0f)? tempUnitDef: NULL);
}






/*
 * find and return the unit that's best to make for this builder
 * (returns NULL if no buildings/units are better than minUsefulness
 * or buidler can't make any economy units)
 * TODO: make it, look at how to integrate into the main economy manager
 */
/*
const UnitDef* CUnitTable::GetBestEconomyBuilding(int builder, float minUsefulness) {
	return 0;
}
*/




void CUnitTable::Init() {
	// get unit defs from game and stick them in the unitTypes[] array
	numOfUnits = ai->cb->GetNumUnitDefs();
	unitList = new const UnitDef*[numOfUnits];
	ai->cb->GetUnitDefList(unitList);

	// one more than needed because 0 is dummy object (so
	// UnitDef->id can be used to adress that unit in array)
	unitTypes = new UnitType[numOfUnits + 1];

	// add units to UnitTable
	for (int i = 1; i <= numOfUnits; i++) {
		unitTypes[i].def = unitList[i - 1];
		// side has not been assigned - will be done later
		unitTypes[i].category = -1;

		// get build options
		for (map<int, string>::const_iterator j = unitTypes[i].def->buildOptions.begin(); j != unitTypes[i].def->buildOptions.end(); j++) {
			unitTypes[i].canBuildList.push_back(ai->cb->GetUnitDef(j->second.c_str())->id);
		}
	}


	// now set sides and create buildtree for each
	for (int s = 0; s < numOfSides; s++) {
		// set side of start unit (eg. commander) and continue recursively
		int unitDefID = startUnits[s];
		unitTypes[unitDefID].sides.insert(s);
		unitTypes[unitDefID].techLevel = 0;

		CalcBuildTree(unitDefID, s);
	}


	// add unit to different groups
	for (int i = 1; i <= numOfUnits; i++) {
		UnitType* me = &unitTypes[i];

		for (std::set<int>::iterator it = me->sides.begin(); it != me->sides.end(); it++) {
			int mySide = *it;
			int UnitCost = int(me->def->metalCost * METAL2ENERGY + me->def->energyCost);

			CSunParser attackerParser(ai);
			char weaponnumber[10] = "";

			attackerParser.LoadVirtualFile(me->def->filename.c_str());
			me->TargetCategories.resize(me->def->weapons.size());

			for (unsigned int w = 0; w != me->def->weapons.size(); w++) {
				itoa(w, weaponnumber, 10);
				attackerParser.GetDef(me->TargetCategories[w], "-1", string("UNITINFO\\OnlyTargetCategory") + string(weaponnumber));
			}

			me->DPSvsUnit.resize(numOfUnits + 1);

			// calculate this unit type's DPS against all other unit types
			for (int v = 1; v <= numOfUnits; v++) {
				me->DPSvsUnit[v] = GetDPSvsUnit(me->def, unitTypes[v].def);
			}

			if (me->def->speed && me->def->minWaterDepth <= 0) {
				if (me->def->buildOptions.size()) {
					ground_builders[mySide].push_back(i);
					me->category = CAT_BUILDER;
				}
				else if (!me->def->weapons.empty() && !me->def->weapons.begin()->def->stockpile) {
					ground_attackers[mySide].push_back(i);
					me->category = CAT_G_ATTACK;
				}
			}



			else if (!me->def->canfly) {
				if (me->def->minWaterDepth <= 0) {
					if (me->def->buildOptions.size() >= 1 && me->def->builder) {
						if ((((me->def)->TEDClassString) == "PLANT") || (((me->def)->speed) > 0.0f)) {
							me->isHub = false;
						} else {
							me->isHub = true;
						}

						ground_factories[mySide].push_back(i);
						me->category = CAT_FACTORY;
					}
					else {
						const WeaponDef* weapon = (me->def->weapons.empty())? 0: me->def->weapons.begin()->def;

						if (weapon && !weapon->stockpile) {
							if (!weapon->waterweapon) {
								// filter out depth-charge launchers etc
								ground_defences[mySide].push_back(i);
								me->category = CAT_DEFENCE;
							}
						}

						if (me->def->stockpileWeaponDef) {
							if (me->def->stockpileWeaponDef->targetable) {
								// nuke
								nuke_silos[mySide].push_back(i);
								me->category = CAT_NUKE;
							}
							if (me->def->stockpileWeaponDef->interceptor) {
								// anti-nuke, not implemented yet
							}
						}

						if (me->def->shieldWeaponDef && me->def->shieldWeaponDef->isShield) {
							// shield, not implemented yet
							// me->category = CAT_SHIELD;
						}

						if (me->def->makesMetal) {
							metal_makers[mySide].push_back(i);
							me->category = CAT_MMAKER;
						}
						if (me->def->extractsMetal > 0.0f) {
							metal_extractors[mySide].push_back(i);
							me->category = CAT_MEX;
						}
						if ((me->def->energyMake - me->def->energyUpkeep) / UnitCost > 0.002 || me->def->tidalGenerator || me->def->windGenerator) {
							if (me->def->minWaterDepth <= 0 && !me->def->needGeo) {
								// filter tidals and geothermals
								ground_energy[mySide].push_back(i);
								me->category = CAT_ENERGY;
							}
						}
						if (me->def->energyStorage / UnitCost > 0.2) {
							energy_storages[mySide].push_back(i);
							me->category = CAT_ESTOR;
						}
						if (me->def->metalStorage / UnitCost > 0.1) {
							metal_storages[mySide].push_back(i);
							me->category = CAT_MSTOR;
						}
					}
				}
			}
		}
	}

	// dump generated unit table to file
	DebugPrint();
}





bool CUnitTable::CanBuildUnit(int id_builder, int id_unit) {
	// look in build options of builder for unit
	for (unsigned int i = 0; i != unitTypes[id_builder].canBuildList.size(); i++) {
		if (unitTypes[id_builder].canBuildList[i] == id_unit)
			return true;
	}

	// unit not found in builder's buildoptions
	return false;
}

// determines sides of unitTypes by recursion
void CUnitTable::CalcBuildTree(int unitDefID, int rootSide) {
	UnitType* utype = &unitTypes[unitDefID];

	// go through all possible build options and set side if necessary
	for (unsigned int i = 0; i != utype->canBuildList.size(); i++) {
		// add this unit to target's built-by list
		int buildOptionIndex = utype->canBuildList[i];
		UnitType* buildOptionType = &unitTypes[buildOptionIndex];

		// KLOOTNOTE: techLevel will not make much sense if
		// unit has multiple ancestors at different depths
		// in tree (eg. Adv. Vehicle Plants in XTA)
		buildOptionType->techLevel = utype->techLevel;
		// buildOptionType->techLevel = utype->def->techLevel;
		// FIXME: causes duplicated entries in PURE
		// buildOptionType->builtByList.push_back(unitDefID);

		if (buildOptionType->sides.find(rootSide) == buildOptionType->sides.end()) {
			// unit has not been checked yet, set side
			// as side of its builder and continue
			buildOptionType->sides.insert(rootSide);
			CalcBuildTree(buildOptionIndex, rootSide);
		}
	}
}



void CUnitTable::DebugPrint() {
	if (!unitList)
		return;

	// NOTE: same order as all_lists, not as CAT_* enum
	const char* listCategoryNames[12] = {
		"GROUND-FACTORY", "GROUND-BUILDER", "GROUND-ATTACKER", "METAL-EXTRACTOR",
		"METAL-MAKER", "GROUND-ENERGY", "GROUND-DEFENSE", "METAL-STORAGE",
		"ENERGY-STORAGE", "NUKE-SILO", "SHIELD-GENERATOR", "LAST-CATEGORY"
	};

	char filename[1024] = ROOTFOLDER"CUnitTable.log";
	ai->cb->GetValue(AIVAL_LOCATE_FILE_W, filename);
	FILE* file = fopen(filename, "w");

	for (int i = 1; i <= numOfUnits; i++) {
		UnitType* utype = &unitTypes[i];

		fprintf(file, "UnitDef ID: %i\n", i);
		fprintf(file, "Name:       %s\n", unitList[i - 1]->humanName.c_str());
		fprintf(file, "Sides:      ");

		for (std::set<int>::iterator it = utype->sides.begin(); it != utype->sides.end(); it++) {
			fprintf(file, "%d (%s) ", *it, sideNames[*it].c_str());
		}

		fprintf(file, "\n");
		fprintf(file, "Can Build:  ");

		for (unsigned int j = 0; j != utype->canBuildList.size(); j++) {
			UnitType* buildOption = &unitTypes[utype->canBuildList[j]];

			for (std::set<int>::iterator it = buildOption->sides.begin(); it != buildOption->sides.end(); it++) {
				const char* sideName = sideNames[*it].c_str();
				const char* buildOptionName = buildOption->def->humanName.c_str();
				fprintf(file, "'(%s) %s' ", sideName, buildOptionName);
			}
		}

		fprintf(file, "\n");
		fprintf(file, "Built by:   ");

		for (unsigned int k = 0; k != utype->builtByList.size(); k++) {
			UnitType* parent = &unitTypes[utype->builtByList[k]];

			for (std::set<int>::iterator it = parent->sides.begin(); it != parent->sides.end(); it++) {
				const char* sideName = sideNames[*it].c_str();
				const char* parentName = parent->def->humanName.c_str();
				fprintf(file, "'(%s) %s' ", sideName, parentName);
			}
		}

		fprintf(file, "\nTech-Level: %d", utype->techLevel);
		fprintf(file, "\n\n");
	}

	for (int s = 0; s < numOfSides; s++) {
		for (unsigned int l = 0; l != all_lists.size(); l++) {
			fprintf(file, "\n\n%s (side %d) units of category %s:\n", sideNames[s].c_str(), s, listCategoryNames[l]);

			for (unsigned int i = 0; i != all_lists[l][s].size(); i++)
				fprintf(file, "\t%s\n", unitTypes[all_lists[l][s][i]].def->humanName.c_str());
		}
	}

	fclose(file);
}




float CUnitTable::GetMaxRange(const UnitDef* unit) {
	float max_range = 0.0f;

	for (vector<UnitDef::UnitDefWeapon>::const_iterator i = unit->weapons.begin(); i != unit->weapons.end(); i++) {
		if ((i->def->range) > max_range)
			max_range = i->def->range;
	}

	return max_range;
}

float CUnitTable::GetMinRange(const UnitDef* unit) {
	float min_range = FLT_MAX;

	for (vector<UnitDef::UnitDefWeapon>::const_iterator i = unit->weapons.begin(); i != unit->weapons.end(); i++) {
		if ((i->def->range) < min_range)
			min_range = i->def->range;
	}

	return min_range;
}

⌨️ 快捷键说明

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