예제 #1
0
static void DestroyObject(
	TObject *o, const int flags, const int playerUID, const int uid)
{
	o->Health = 0;

	const Vec2i realPos = Vec2iNew(o->tileItem.x, o->tileItem.y);

	if (!gCampaign.IsClient)
	{
		// Update objective
		UpdateMissionObjective(&gMission, o->tileItem.flags, OBJECTIVE_DESTROY);
		// Extra score if objective
		if ((o->tileItem.flags & TILEITEM_OBJECTIVE) && playerUID >= 0)
		{
			GameEvent e = GameEventNew(GAME_EVENT_SCORE);
			e.u.Score.PlayerUID = playerUID;
			e.u.Score.Score = OBJECT_SCORE;
			GameEventsEnqueue(&gGameEvents, e);
		}

		// Weapons that go off when this object is destroyed
		const Vec2i fullPos = Vec2iReal2Full(realPos);
		for (int i = 0; i < (int)o->Class->DestroyGuns.size; i++)
		{
			const GunDescription **g = CArrayGet(&o->Class->DestroyGuns, i);
			GunFire(*g, fullPos, 0, 0, flags, playerUID, uid, true, false);
		}

		// A wreck left after the destruction of this object
		// TODO: doesn't need to be network event
		GameEvent e = GameEventNew(GAME_EVENT_ADD_BULLET);
		e.u.AddBullet.UID = MobObjsObjsGetNextUID();
		strcpy(e.u.AddBullet.BulletClass, "fireball_wreck");
		e.u.AddBullet.MuzzlePos = Vec2i2Net(fullPos);
		e.u.AddBullet.MuzzleHeight = 0;
		e.u.AddBullet.Angle = 0;
		e.u.AddBullet.Elevation = 0;
		e.u.AddBullet.Flags = 0;
		e.u.AddBullet.PlayerUID = -1;
		e.u.AddBullet.ActorUID = -1;
		GameEventsEnqueue(&gGameEvents, e);
	}

	SoundPlayAt(&gSoundDevice, gSoundDevice.wreckSound, realPos);

	// Turn the object into a wreck, if available
	if (o->Class->Wreck.Pic)
	{
		o->tileItem.flags = TILEITEM_IS_WRECK;
	}
	else
	{
		ObjDestroy(o->tileItem.id);
	}

	// Update pathfinding cache since this object could have blocked a path
	// before
	PathCacheClear(&gPathCache);
}
예제 #2
0
파일: objs.c 프로젝트: evktalo/cdogs-sdl
void ObjAddDestructible(
	Vec2i pos, Vec2i size,
	const TOffsetPic *pic, const TOffsetPic *wreckedPic,
	const char *picName,
	int structure, int objFlags, int tileFlags)
{
	Vec2i fullPos = Vec2iReal2Full(pos);
	TObject *o = CArrayGet(&gObjs, ObjAdd(
		fullPos, size, picName, OBJ_NONE, tileFlags));
	o->pic = pic;
	o->wreckedPic = wreckedPic;
	o->structure = structure;
	o->flags = objFlags;
}
예제 #3
0
NetMsgVec2i PlaceBaddie(Map *map)
{
	// Don't try forever trying to place baddie
	for (int i = 0; i < 100; i++)
	{
		// Try spawning out of players' sights
		const Vec2i pos = Vec2iReal2Full(Vec2iNew(
			rand() % (map->Size.x * TILE_WIDTH),
			rand() % (map->Size.y * TILE_HEIGHT)));
		const TActor *closestPlayer = AIGetClosestPlayer(pos);
		if (closestPlayer && CHEBYSHEV_DISTANCE(
			pos.x, pos.y,
			closestPlayer->Pos.x, closestPlayer->Pos.y) >= 256 * 150 &&
			MapIsTileAreaClear(map, pos, Vec2iNew(ACTOR_W, ACTOR_H)))
		{
			NetMsgVec2i posNet;
			posNet.x = pos.x;
			posNet.y = pos.y;
			return posNet;
		}
	}
	// Keep trying, but this time try spawning anywhere,
	// even close to player
	for (;;)
	{
		const Vec2i pos = Vec2iReal2Full(Vec2iNew(
			rand() % (map->Size.x * TILE_WIDTH),
			rand() % (map->Size.y * TILE_HEIGHT)));
		if (MapIsTileAreaClear(map, pos, Vec2iNew(ACTOR_W, ACTOR_H)))
		{
			NetMsgVec2i posNet;
			posNet.x = pos.x;
			posNet.y = pos.y;
			return posNet;
		}
	}
}
예제 #4
0
void EmitterStart(
	Emitter *em, const Vec2i fullPos, const int z, const Vec2i vel)
{
	const Vec2i p = Vec2iAdd(fullPos, Vec2iReal2Full(em->offset));

	// TODO: single event multiple particles
	GameEvent e = GameEventNew(GAME_EVENT_ADD_PARTICLE);
	e.u.AddParticle.FullPos = p;
	e.u.AddParticle.Z = z * Z_FACTOR;
	e.u.AddParticle.Class = em->p;
	const int speed = RAND_INT(em->minSpeed, em->maxSpeed);
	const Vec2i baseVel = Vec2iFromPolar(speed, RAND_DOUBLE(0, PI * 2));
	e.u.AddParticle.Vel = Vec2iAdd(vel, baseVel);
	e.u.AddParticle.Angle = RAND_DOUBLE(0, PI * 2);
	e.u.AddParticle.DZ = RAND_INT(em->minDZ, em->maxDZ);
	e.u.AddParticle.Spin = RAND_DOUBLE(em->minRotation, em->maxRotation);
	GameEventsEnqueue(&gGameEvents, e);
}
예제 #5
0
Vec2i GunGetMuzzleOffset(const GunDescription *desc, const direction_e dir)
{
	if (!GunHasMuzzle(desc))
	{
		return Vec2iZero();
	}
	gunpic_e g = desc->pic;
	CASSERT(g >= 0, "Gun has no pic");
	int body = (int)g < 0 ? BODY_UNARMED : BODY_ARMED;
	Vec2i position = Vec2iNew(
		cGunHandOffset[body][dir].dx +
		cGunPics[g][dir][GUNSTATE_FIRING].dx +
		cMuzzleOffset[g][dir].dx,
		cGunHandOffset[body][dir].dy +
		cGunPics[g][dir][GUNSTATE_FIRING].dy +
		cMuzzleOffset[g][dir].dy + BULLET_Z);
	return Vec2iReal2Full(position);
}
예제 #6
0
Vec2i PlacePlayer(
	Map *map, const PlayerData *p, const Vec2i firstPos, const bool pumpEvents)
{
	NetMsgActorAdd aa = NetMsgActorAdd_init_default;
	aa.Id = ActorsGetFreeIndex();
	aa.Health = p->Char.maxHealth;
	aa.PlayerId = p->playerIndex;

	if (IsPVP(gCampaign.Entry.Mode))
	{
		// In a PVP mode, always place players apart
		aa.FullPos = PlaceActor(&gMap);
	}
	else if (gMission.missionData->Type == MAPTYPE_STATIC &&
		!Vec2iIsZero(gMission.missionData->u.Static.Start))
	{
		// place players near the start point
		Vec2i startPoint = Vec2iReal2Full(Vec2iCenterOfTile(
			gMission.missionData->u.Static.Start));
		aa.FullPos = PlaceActorNear(map, startPoint, true);
	}
	else if (gConfig.Interface.Splitscreen == SPLITSCREEN_NEVER &&
		!Vec2iIsZero(firstPos))
	{
		// If never split screen, try to place players near the first player
		aa.FullPos = PlaceActorNear(map, firstPos, true);
	}
	else
	{
		aa.FullPos = PlaceActor(map);
	}

	GameEvent e = GameEventNew(GAME_EVENT_ACTOR_ADD);
	e.u.ActorAdd = aa;
	GameEventsEnqueue(&gGameEvents, e);

	if (pumpEvents)
	{
		// Process the events that actually place the players
		HandleGameEvents(&gGameEvents, NULL, NULL, NULL, &gEventHandlers);
	}

	return Vec2iNew(aa.FullPos.x, aa.FullPos.y);
}
예제 #7
0
static bool TryPlacePickup(HealthPickups *h)
{
	Vec2i size = Vec2iNew(HEALTH_W, HEALTH_H);
	// Attempt to place one in unexplored area
	for (int i = 0; i < 100; i++)
	{
		const Vec2i v = MapGenerateFreePosition(h->map, size);
		if (!Vec2iIsZero(v) && !MapGetTile(h->map, Vec2iToTile(v))->isVisited)
		{
			MapPlaceHealth(v);
			return true;
		}
	}
	// Attempt to place one in out-of-sight area
	for (int i = 0; i < 100; i++)
	{
		const Vec2i v = MapGenerateFreePosition(h->map, size);
		const Vec2i fullpos = Vec2iReal2Full(v);
		const TActor *closestPlayer = AIGetClosestPlayer(fullpos);
		if (!Vec2iIsZero(v) &&
			(!closestPlayer || CHEBYSHEV_DISTANCE(
			fullpos.x, fullpos.y,
			closestPlayer->Pos.x, closestPlayer->Pos.y) >= 256 * 150))
		{
			MapPlaceHealth(v);
			return true;
		}
	}
	// Attempt to place one anyway
	for (int i = 0; i < 100; i++)
	{
		const Vec2i v = MapGenerateFreePosition(h->map, size);
		if (!Vec2iIsZero(v))
		{
			MapPlaceHealth(v);
			return true;
		}
	}
	return false;
}
예제 #8
0
static void AddBrass(
	const GunDescription *g, const direction_e d, const Vec2i pos)
{
	CASSERT(g->Brass, "Cannot create brass for no-brass weapon");
	GameEvent e = GameEventNew(GAME_EVENT_ADD_PARTICLE);
	e.u.AddParticle.Class = g->Brass;
	double x, y;
	const double radians = dir2radians[d];
	GetVectorsForRadians(radians, &x, &y);
	const Vec2i ejectionPortOffset = Vec2iReal2Full(Vec2iScale(Vec2iNew(
		(int)round(x), (int)round(y)), 7));
	const Vec2i muzzleOffset = GunGetMuzzleOffset(g, d);
	const Vec2i muzzlePosition = Vec2iAdd(pos, muzzleOffset);
	e.u.AddParticle.FullPos = Vec2iMinus(muzzlePosition, ejectionPortOffset);
	e.u.AddParticle.Z = g->MuzzleHeight;
	e.u.AddParticle.Vel = Vec2iScaleDiv(
		GetFullVectorsForRadians(radians + PI / 2), 3);
	e.u.AddParticle.Vel.x += (rand() % 128) - 64;
	e.u.AddParticle.Vel.y += (rand() % 128) - 64;
	e.u.AddParticle.Angle = RAND_DOUBLE(0, PI * 2);
	e.u.AddParticle.DZ = (rand() % 6) + 6;
	e.u.AddParticle.Spin = RAND_DOUBLE(-0.1, 0.1);
	GameEventsEnqueue(&gGameEvents, e);
}
예제 #9
0
파일: objs.c 프로젝트: evktalo/cdogs-sdl
static bool DoDamageCharacter(
	const Vec2i pos,
	const Vec2i hitVector,
	const int power,
	const int flags,
	const int player,
	const int uid,
	const TTileItem *target,
	const special_damage_e special,
	const HitSounds *hitSounds,
	const bool allowFriendlyHitSound)
{
	// Create events: hit, damage, score
	TActor *actor = CArrayGet(&gActors, target->id);
	CASSERT(actor->isInUse, "Cannot damage nonexistent player");
	bool canHit = CanHitCharacter(flags, uid, actor);
	if (canHit)
	{
		GameEvent e;
		e.Type = GAME_EVENT_HIT_CHARACTER;
		e.u.HitCharacter.TargetId = actor->tileItem.id;
		e.u.HitCharacter.Special = special;
		GameEventsEnqueue(&gGameEvents, e);
		if (gConfig.Sound.Hits && hitSounds != NULL &&
			!ActorIsImmune(actor, special) &&
			(allowFriendlyHitSound || !ActorIsInvulnerable(
			actor, flags, player, gCampaign.Entry.Mode)))
		{
			GameEvent es;
			es.Type = GAME_EVENT_SOUND_AT;
			es.u.SoundAt.Sound = hitSounds->Flesh;
			es.u.SoundAt.Pos = pos;
			GameEventsEnqueue(&gGameEvents, es);
		}
		if (gConfig.Game.ShotsPushback)
		{
			GameEvent ei;
			ei.Type = GAME_EVENT_ACTOR_IMPULSE;
			ei.u.ActorImpulse.Id = actor->tileItem.id;
			ei.u.ActorImpulse.Vel = Vec2iScaleDiv(
				Vec2iScale(hitVector, power), SHOT_IMPULSE_DIVISOR);
			GameEventsEnqueue(&gGameEvents, ei);
		}
		if (CanDamageCharacter(flags, player, uid, actor, special))
		{
			GameEvent e1;
			e1.Type = GAME_EVENT_DAMAGE_CHARACTER;
			e1.u.DamageCharacter.Power = power;
			e1.u.DamageCharacter.PlayerIndex = player;
			e1.u.DamageCharacter.TargetId = actor->tileItem.id;
			e1.u.DamageCharacter.TargetPlayerIndex = -1;
			if (actor->pData)
			{
				e1.u.DamageCharacter.TargetPlayerIndex =
					actor->pData->playerIndex;
			}
			GameEventsEnqueue(&gGameEvents, e1);

			if (gConfig.Game.Gore != GORE_NONE)
			{
				GameEvent eb;
				memset(&eb, 0, sizeof eb);
				eb.Type = GAME_EVENT_ADD_PARTICLE;
				eb.u.AddParticle.FullPos = Vec2iReal2Full(pos);
				eb.u.AddParticle.Z = 10 * Z_FACTOR;
				int bloodPower = power * 2;
				int bloodSize = 1;
				while (bloodPower > 0)
				{
					switch (bloodSize)
					{
					case 1:
						eb.u.AddParticle.Class =
							StrParticleClass(&gParticleClasses, "blood1");
						break;
					case 2:
						eb.u.AddParticle.Class =
							StrParticleClass(&gParticleClasses, "blood2");
						break;
					default:
						eb.u.AddParticle.Class =
							StrParticleClass(&gParticleClasses, "blood3");
						break;
					}
					bloodSize++;
					if (bloodSize > 3)
					{
						bloodSize = 1;
					}
					if (gConfig.Game.ShotsPushback)
					{
						eb.u.AddParticle.Vel = Vec2iScaleDiv(
							Vec2iScale(hitVector, (rand() % 8 + 8) * power),
							15 * SHOT_IMPULSE_DIVISOR);
					}
					else
					{
						eb.u.AddParticle.Vel = Vec2iScaleDiv(
							Vec2iScale(hitVector, rand() % 8 + 8), 20);
					}
					eb.u.AddParticle.Vel.x += (rand() % 128) - 64;
					eb.u.AddParticle.Vel.y += (rand() % 128) - 64;
					eb.u.AddParticle.Angle = RAND_DOUBLE(0, PI * 2);
					eb.u.AddParticle.DZ = (rand() % 6) + 6;
					eb.u.AddParticle.Spin = RAND_DOUBLE(-0.1, 0.1);
					GameEventsEnqueue(&gGameEvents, eb);
					switch (gConfig.Game.Gore)
					{
					case GORE_LOW:
						bloodPower /= 8;
						break;
					case GORE_MEDIUM:
						bloodPower /= 2;
						break;
					default:
						bloodPower = bloodPower * 7 / 8;
						break;
					}
				}
			}

			if (player >= 0 && power != 0)
			{
				// Calculate score based on
				// if they hit a penalty character
				GameEvent e2;
				e2.Type = GAME_EVENT_SCORE;
				e2.u.Score.PlayerIndex = player;
				if (actor->flags & FLAGS_PENALTY)
				{
					e2.u.Score.Score = PENALTY_MULTIPLIER * power;
				}
				else
				{
					e2.u.Score.Score = power;
				}
				GameEventsEnqueue(&gGameEvents, e2);
			}
		}
	}
	return canHit;
}
예제 #10
0
파일: objs.c 프로젝트: evktalo/cdogs-sdl
static void DamageObject(
	const int power, const int flags, const int player, const int uid,
	TTileItem *target)
{
	TObject *object = CArrayGet(&gObjs, target->id);
	// Don't bother if object already destroyed
	if (object->structure <= 0)
	{
		return;
	}

	object->structure -= power;
	const Vec2i pos = Vec2iNew(target->x, target->y);

	// Destroying objects and all the wonderful things that happen
	if (object->structure <= 0)
	{
		object->structure = 0;
		UpdateMissionObjective(
			&gMission, object->tileItem.flags, OBJECTIVE_DESTROY,
			player, pos);
		if (object->flags & OBJFLAG_QUAKE)
		{
			GameEvent shake;
			shake.Type = GAME_EVENT_SCREEN_SHAKE;
			shake.u.ShakeAmount = SHAKE_BIG_AMOUNT;
			GameEventsEnqueue(&gGameEvents, shake);
		}
		const Vec2i fullPos = Vec2iReal2Full(
			Vec2iNew(object->tileItem.x, object->tileItem.y));
		if (object->flags & OBJFLAG_EXPLOSIVE)
		{
			GunAddBullets(
				StrGunDescription("explosion1"), fullPos, 0, 0,
				flags, player, uid, true);
			GunAddBullets(
				StrGunDescription("explosion2"), fullPos, 0, 0,
				flags, player, uid, true);
			GunAddBullets(
				StrGunDescription("explosion3"), fullPos, 0, 0,
				flags, player, uid, true);
		}
		else if (object->flags & OBJFLAG_FLAMMABLE)
		{
			GunAddBullets(
				StrGunDescription("fire_explosion"), fullPos, 0, 0,
				flags, player, uid, true);
		}
		else if (object->flags & OBJFLAG_POISONOUS)
		{
			GunAddBullets(
				StrGunDescription("gas_poison_explosion"), fullPos, 0, 0,
				flags, player, uid, true);
		}
		else if (object->flags & OBJFLAG_CONFUSING)
		{
			GunAddBullets(
				StrGunDescription("gas_confuse_explosion"), fullPos, 0, 0,
				flags, player, uid, true);
		}
		else
		{
			// A wreck left after the destruction of this object
			GameEvent e;
			memset(&e, 0, sizeof e);
			e.Type = GAME_EVENT_ADD_BULLET;
			e.u.AddBullet.BulletClass = StrBulletClass("fireball_wreck");
			e.u.AddBullet.MuzzlePos = fullPos;
			e.u.AddBullet.MuzzleHeight = 0;
			e.u.AddBullet.Angle = 0;
			e.u.AddBullet.Elevation = 0;
			e.u.AddBullet.Flags = 0;
			e.u.AddBullet.PlayerIndex = -1;
			e.u.AddBullet.UID = -1;
			GameEventsEnqueue(&gGameEvents, e);
			SoundPlayAt(
				&gSoundDevice,
				gSoundDevice.wreckSound,
				Vec2iNew(object->tileItem.x, object->tileItem.y));
		}
		if (object->wreckedPic)
		{
			object->tileItem.flags = TILEITEM_IS_WRECK;
			object->pic = object->wreckedPic;
			object->picName = "";
		}
		else
		{
			ObjDestroy(object->tileItem.id);
		}
	}
}