1 ////////////////////////////////////////////////////////////////////////////////
2 //    Scorched3D (c) 2000-2011
3 //
4 //    This file is part of Scorched3D.
5 //
6 //    Scorched3D is free software; you can redistribute it and/or modify
7 //    it under the terms of the GNU General Public License as published by
8 //    the Free Software Foundation; either version 2 of the License, or
9 //    (at your option) any later version.
10 //
11 //    Scorched3D is distributed in the hope that it will be useful,
12 //    but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //    GNU General Public License for more details.
15 //
16 //    You should have received a copy of the GNU General Public License along
17 //    with this program; if not, write to the Free Software Foundation, Inc.,
18 //    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 ////////////////////////////////////////////////////////////////////////////////
20 
21 #include <common/ToolTip.h>
22 #include <common/ModelID.h>
23 #include <weapons/Accessory.h>
24 #include <weapons/AccessoryStore.h>
25 #include <common/Defines.h>
26 #include <common/OptionsScorched.h>
27 #include <lang/LangResource.h>
28 #include <tank/Tank.h>
29 #include <tank/TankModel.h>
30 #include <tank/TankModelContainer.h>
31 #include <stdlib.h>
32 
33 unsigned int Accessory::nextAccessoryId_ = 0;
34 
35 #ifndef S3D_SERVER
36 #include <3dsparse/Model.h>
37 #include <3dsparse/ModelStore.h>
38 #include <tankgraph/MissileMesh.h>
39 #include <image/ImageFactory.h>
40 #endif
41 
Accessory()42 Accessory::Accessory() :
43 	accessoryId_(++nextAccessoryId_),
44 	name_("NONAME"), description_("NODESC"),
45 	accessoryAction_(0),
46 	toolTip_(ToolTip::ToolTipHelp, LangString(), LangString()),
47 	price_(0), bundle_(1), armsLevel_(9), freemarketLimits_(150),
48 	modelScale_(1),
49 	positionSelect_(ePositionSelectNone), positionSelectLimit_(10),
50 	maximumNumber_(0),
51 	startingNumber_(0),
52 	useNumber_(1),
53 	muzzleFlash_(true),
54 	aiOnly_(false),
55 	botOnly_(false),
56 	noBuy_(false)
57 {
58 }
59 
~Accessory()60 Accessory::~Accessory()
61 {
62 	delete accessoryAction_;
63 	accessoryAction_ = 0;
64 }
65 
parseXML(AccessoryCreateContext & context,XMLNode * accessoryNode)66 bool Accessory::parseXML(AccessoryCreateContext &context, XMLNode *accessoryNode)
67 {
68 	// Get the accessory name
69 	if (!accessoryNode->getNamedChild("name", name_)) return false;
70 
71 	// Get the accessory armslevel
72 	accessoryNode->getNamedChild("armslevel", armsLevel_, false);
73 
74 	// Get the optional muzzleflash
75 	XMLNode *muzzleFlashNode = 0;
76 	accessoryNode->getNamedChild("nomuzzleflash", muzzleFlashNode, false);
77 	if (muzzleFlashNode) muzzleFlash_ = false;
78 
79 	// Get the accessory description
80 	accessoryNode->getNamedChild("description", description_, false);
81 	toolTip_.setText(ToolTip::ToolTipHelp, LANG_STRING(getName()), LANG_STRING(getDescription()));
82 
83 	// Get the accessory icon
84 	if (accessoryNode->getNamedChild("icon", iconName_, false))
85 	{
86 		if (!S3D::checkDataFile(S3D::formatStringBuffer("data/textures/wicons/%s", getIconName()))) return false;
87 	}
88 
89 #ifndef S3D_SERVER
90 	if (getIconName()[0])
91 	{
92 		texture_.setImageID(ImageID(
93 			S3D::eModLocation,
94 			"",
95 			S3D::formatStringBuffer("data/textures/wicons/%s", getIconName())));
96 	}
97 	else
98 	{
99 		texture_.setImageID(ImageID(
100 			S3D::eModLocation,
101 			"",
102 			S3D::formatStringBuffer("data/textures/wicons/%s", "tracer.bmp")));
103 	}
104 #endif
105 
106 	// Get the accessory sound
107 	if (accessoryNode->getNamedChild("activationsound", activationSound_, false))
108 	{
109 		if (!S3D::checkDataFile(S3D::formatStringBuffer("data/wav/%s", getActivationSound()))) return false;
110 	}
111 
112 	// Get the accessory bundle
113 	accessoryNode->getNamedChild("bundlesize", bundle_, false);
114 
115 	// Get ai only
116 	accessoryNode->getNamedChild("aionly", aiOnly_, false);
117 
118 	// Get bot only (ie: only for bots, not for tank objects)
119 	accessoryNode->getNamedChild("botonly", botOnly_, false);
120 
121 	// Get no buy
122 	accessoryNode->getNamedChild("nobuy", noBuy_, false);
123 
124 	// Get the maximum number
125 	maximumNumber_ = context.getOptionsGame().getMaxNumberWeapons();
126 	accessoryNode->getNamedChild("maximumnumber", maximumNumber_, false);
127 
128 	// Get the starting number
129 	accessoryNode->getNamedChild("startingnumber", startingNumber_, false);
130 
131 	// Get the number to use of firing
132 	accessoryNode->getNamedChild("usenumber", useNumber_, false);
133 
134 	// Freemarket limits
135 	accessoryNode->getNamedChild("freemarketlimits", freemarketLimits_, false);
136 
137 	// Get the accessory cost
138 	accessoryNode->getNamedChild("cost", price_, false);
139 
140 	// Get the weapon model scale
141 	accessoryNode->getNamedChild("modelscale", modelScale_, false);
142 
143 	// Get the weapon model
144 	XMLNode *modelNode = 0;
145 	if (accessoryNode->getNamedChild("model", modelNode, false))
146 	{
147 		if (!modelId_.initFromNode(modelNode)) return false;
148 	}
149 
150 	// Get action
151 	XMLNode *subNode = 0;
152 	if (!accessoryNode->getNamedChild("accessoryaction", subNode)) return false;
153 	accessoryAction_ = context.getAccessoryStore().createAccessoryPart(context, this, subNode);
154 	if (!accessoryAction_)
155 	{
156 		S3D::dialogMessage("Accessory", S3D::formatStringBuffer(
157 			"Failed to create action \"%s\"", name_.c_str()));
158 		return false;
159 	}
160 
161 	// Setup price
162 	sellPrice_ = 0;
163 	if (price_ > 0 && bundle_ > 0) sellPrice_ = int((price_ / bundle_) * 0.8f);
164 	originalPrice_ = price_;
165 	originalSellPrice_ = sellPrice_;
166 
167 	// Position Selection Type
168 	std::string positionSelection;
169 	if (accessoryNode->getNamedChild("positionselection", positionSelection, false))
170 	{
171 		if (0 == strcmp(positionSelection.c_str(), "none"))
172 		{
173 			positionSelect_ = ePositionSelectNone;
174 		}
175 		else if (0 == strcmp(positionSelection.c_str(), "generic"))
176 		{
177 			positionSelect_ = ePositionSelectGeneric;
178 		}
179 		else if (0 == strcmp(positionSelection.c_str(), "fuel"))
180 		{
181 			positionSelect_ = ePositionSelectFuel;
182 
183 			// Make sure there is a "WeaponMoveTank" under here somewhere
184 			if (!context.getAccessoryStore().findAccessoryPartByAccessoryId(
185 				getAccessoryId(), "WeaponMoveTank"))
186 			{
187 				return accessoryNode->returnError(
188 					"Fuel selection can only be used with WeaponMoveTank weapons");
189 			}
190 		}
191 		else if (0 == strcmp(positionSelection.c_str(), "fuellimit"))
192 		{
193 			positionSelect_ = ePositionSelectFuelLimit;
194 			if (!accessoryNode->getNamedChild("positionselectionlimit", positionSelectLimit_)) return false;
195 		}
196 		else if (0 == strcmp(positionSelection.c_str(), "limit"))
197 		{
198 			positionSelect_ = ePositionSelectLimit;
199 			if (!accessoryNode->getNamedChild("positionselectionlimit", positionSelectLimit_)) return false;
200 		}
201 		else
202 		{
203 			return accessoryNode->returnError(S3D::formatStringBuffer(
204 				"Unknown accessory position selection type \"%s\"",
205 				positionSelection.c_str()));
206 		}
207 	}
208 
209 	// Get the accessory tabgroupname
210 	if (!accessoryNode->getNamedChild("tabgroup", tabGroupName_, false))
211 	{
212 		if (accessoryAction_->getType() == AccessoryPart::AccessoryWeapon)
213 		{
214 			tabGroupName_ = "weapon";
215 		}
216 		else
217 		{
218 			tabGroupName_ = "defense";
219 		}
220 	}
221 
222 	// Get the accessory groupname
223 	if (!accessoryNode->getNamedChild("group", groupName_, false))
224 	{
225 		switch (accessoryAction_->getType())
226 		{
227 		case AccessoryPart::AccessoryWeapon:
228 			groupName_ = "weapon";
229 			break;
230 		case AccessoryPart::AccessoryParachute:
231 			groupName_ = "parachute";
232 			break;
233 		case AccessoryPart::AccessoryShield:
234 			groupName_ = "shield";
235 			break;
236 		case AccessoryPart::AccessoryAutoDefense:
237 			groupName_ = "autodefense";
238 			break;
239 		case AccessoryPart::AccessoryBattery:
240 			groupName_ = "battery";
241 			break;
242 		default:
243 			groupName_ = "none";
244 			break;
245 		}
246 	}
247 
248 	return true;
249 }
250 
getStringName()251 LangString &Accessory::getStringName()
252 {
253 	if (stringName_.size() == 0)
254 	{
255 		stringName_ = LANG_RESOURCE(getName(), getName());
256 	}
257 	return stringName_;
258 }
259 
getActivationSound()260 const char *Accessory::getActivationSound()
261 {
262 	if (!activationSound_.c_str()[0]) return 0;
263 	return activationSound_.c_str();
264 }
265 
266 #ifndef S3D_SERVER
267 
268 std::map<std::string, MissileMesh *> Accessory::loadedMeshes_;
269 
getWeaponMesh(ModelID & id,Tank * currentPlayer)270 MissileMesh *Accessory::getWeaponMesh(ModelID &id, Tank *currentPlayer)
271 {
272 	// Set the default model to use if neither the tank
273 	// or weapon have one
274 	static ModelID defaultModelId;
275 	if (!defaultModelId.modelValid())
276 	{
277 		defaultModelId.initFromString(
278 			"MilkShape",
279 			"data/accessories/v2missile/v2missile.txt",
280 			"");
281 	}
282 
283 	// Set the model to use as the default model id
284 	ModelID *usedModelId = &defaultModelId;
285 
286 	// Get the model to use from the weapon (if there is one)
287 	if (id.modelValid())
288 	{
289 		usedModelId = &id;
290 	}
291 	else
292 	{
293 		// The weapon does not have a model defined for it
294 		// check the player to see if they have a default model
295 		if (currentPlayer)
296 		{
297 			TankModel *model = currentPlayer->getModelContainer().getTankModel();
298 			if (model &&
299 				model->getProjectileModelID().modelValid())
300 			{
301 				usedModelId = &model->getProjectileModelID();
302 			}
303 		}
304 	}
305 
306 	// Load or find the correct missile mesh
307 	MissileMesh *mesh = 0;
308 	const char *name = usedModelId->getStringHash();
309 	std::map<std::string, MissileMesh *>::iterator itor =
310 		loadedMeshes_.find(name);
311 	if (itor == loadedMeshes_.end())
312 	{
313 		mesh = new MissileMesh(*usedModelId);
314 		loadedMeshes_[name] = mesh;
315 	}
316 	else
317 	{
318 		// Find
319 		mesh = (*itor).second;
320 	}
321 	return mesh;
322 }
323 #endif // S3D_SERVER
324