static void DrawPickupSpawner(
	UIObject *o, GraphicsDevice *g, Vec2i pos, void *vData)
{
	const IndexedEditorBrush *data = vData;
	const MapObject *mo = data->u.MapObject;
	DisplayMapItem(
		Vec2iAdd(Vec2iAdd(pos, o->Pos), Vec2iScaleDiv(o->Size, 2)), mo);
	const Pic *pic = mo->u.PickupClass->Pic;
	pos = Vec2iMinus(pos, Vec2iScaleDiv(pic->size, 2));
	Blit(g, pic, Vec2iAdd(Vec2iAdd(pos, o->Pos), Vec2iScaleDiv(o->Size, 2)));
}
void DrawKey(UIObject *o, GraphicsDevice *g, Vec2i pos, void *vData)
{
	EditorBrushAndCampaign *data = vData;
	if (data->Brush.ItemIndex == -1)
	{
		// No key; don't draw
		return;
	}
	const Pic *pic =
		KeyPickupClass(gMission.keyStyle, data->Brush.ItemIndex)->Pic;
	pos = Vec2iAdd(Vec2iAdd(pos, o->Pos), Vec2iScaleDiv(o->Size, 2));
	pos = Vec2iMinus(pos, Vec2iScaleDiv(pic->size, 2));
	Blit(g, pic, pos);
}
Example #3
0
void GrafxMakeRandomBackground(
	GraphicsDevice *device,
	CampaignOptions *co, struct MissionOptions *mo, Map *map)
{
	HSV tint;
	CampaignSettingInit(&co->Setting);
	ActorsInit();
	ObjsInit();
	MobObjsInit();
	SetupQuickPlayCampaign(&co->Setting, &gConfig.QuickPlay);
	co->seed = rand();
	tint.h = rand() * 360.0 / RAND_MAX;
	tint.s = rand() * 1.0 / RAND_MAX;
	tint.v = 0.5;
	DrawBuffer buffer;
	DrawBufferInit(&buffer, Vec2iNew(X_TILES, Y_TILES), device);
	co->MissionIndex = 0;
	GrafxMakeBackground(
		device, &buffer, co, mo, map,
		tint, 0, 1, Vec2iCenterOfTile(Vec2iScaleDiv(map->Size, 2)), NULL);
	DrawBufferTerminate(&buffer);
	ActorsTerminate();
	ObjsTerminate();
	MobObjsTerminate();
	RemoveAllWatches();
	MissionOptionsTerminate(mo);
	CampaignSettingTerminate(&co->Setting);
	co->seed = gConfig.Game.RandomSeed;
}
Example #4
0
const Pic *GetObjectPic(const int id, Vec2i *offset)
{
	const TObject *obj = CArrayGet(&gObjs, id);

	Pic *pic = NULL;
	// Try to get new pic if available
	if (obj->picName && obj->picName[0] != '\0')
	{
		pic = PicManagerGetPic(&gPicManager, obj->picName);
	}
	// Use new pic offset if old one unavailable
	const TOffsetPic *ofpic = obj->pic;
	if (!ofpic)
	{
		// If new one also unavailable, bail
		if (pic == NULL)
		{
			return NULL;
		}
		*offset = Vec2iScaleDiv(pic->size, -2);
	}
	else if (pic == NULL)
	{
		// Default old pic
		pic = PicManagerGetFromOld(&gPicManager, ofpic->picIndex);
		*offset = pic->offset;
	}
	if (ofpic != NULL)
	{
		*offset = Vec2iNew(ofpic->dx, ofpic->dy);
	}
	return pic;
}
Example #5
0
bool AreasCollide(
	const Vec2i pos1, const Vec2i pos2, const Vec2i size1, const Vec2i size2)
{
	const Vec2i d = Vec2iNew(abs(pos1.x - pos2.x), abs(pos1.y - pos2.y));
	const Vec2i r = Vec2iScaleDiv(Vec2iAdd(size1, size2), 2);
	return d.x < r.x && d.y < r.y;
}
Example #6
0
static void MakeBackground(GraphicsDevice *g, int buildTables)
{
	if (buildTables)
	{
		// Automatically pan camera to middle of screen
		Mission *m = gMission.missionData;
		Vec2i focusTile = Vec2iScaleDiv(m->Size, 2);
		// Better yet, if the map has a known start position, focus on that
		if (m->Type == MAPTYPE_STATIC &&
			!Vec2iEqual(m->u.Static.Start, Vec2iZero()))
		{
			focusTile = m->u.Static.Start;
		}
		camera = Vec2iCenterOfTile(focusTile);
	}

	// Clear background first
	for (int i = 0; i < GraphicsGetScreenSize(&g->cachedConfig); i++)
	{
		g->buf[i] = PixelFromColor(g, colorBlack);
	}
	GrafxDrawExtra extra;
	extra.guideImage = brush.GuideImageSurface;
	extra.guideImageAlpha = brush.GuideImageAlpha;

	DrawBufferTerminate(&sDrawBuffer);
	DrawBufferInit(&sDrawBuffer, Vec2iNew(X_TILES, Y_TILES), &gGraphicsDevice);
	GrafxMakeBackground(
		g, &sDrawBuffer, &gCampaign, &gMission, &gMap,
		tintNone, 1, buildTables, camera, &extra);
}
Example #7
0
static void DoDamageCharacter(
	const Vec2i hitVector,
	const int power,
	const int flags,
	const int playerUID,
	const int uid,
	TActor *actor,
	const special_damage_e special)
{
	// Create events: hit, damage, score
	CASSERT(actor->isInUse, "Cannot damage nonexistent player");
	CASSERT(CanHitCharacter(flags, uid, actor), "damaging undamageable actor");

	if (ConfigGetBool(&gConfig, "Game.ShotsPushback"))
	{
		GameEvent ei = GameEventNew(GAME_EVENT_ACTOR_IMPULSE);
		ei.u.ActorImpulse.UID = actor->uid;
		ei.u.ActorImpulse.Vel = Vec2i2Net(Vec2iScaleDiv(
			Vec2iScale(hitVector, power), SHOT_IMPULSE_DIVISOR));
		ei.u.ActorImpulse.Pos = Vec2i2Net(actor->Pos);
		GameEventsEnqueue(&gGameEvents, ei);
	}

	const bool canDamage =
		CanDamageCharacter(flags, playerUID, uid, actor, special);

	GameEvent e = GameEventNew(GAME_EVENT_ACTOR_HIT);
	e.u.ActorHit.UID = actor->uid;
	e.u.ActorHit.PlayerUID = actor->PlayerUID;
	e.u.ActorHit.HitterPlayerUID = playerUID;
	e.u.ActorHit.Special = special;
	e.u.ActorHit.Power = canDamage ? power : 0;
	e.u.ActorHit.Vel = Vec2i2Net(hitVector);
	GameEventsEnqueue(&gGameEvents, e);

	if (canDamage)
	{
		// Don't score for friendly or player hits
		const bool isFriendly =
			(actor->flags & FLAGS_GOOD_GUY) ||
			(!IsPVP(gCampaign.Entry.Mode) && actor->PlayerUID >= 0);
		if (playerUID >= 0 && power != 0 && !isFriendly)
		{
			// Calculate score based on
			// if they hit a penalty character
			e = GameEventNew(GAME_EVENT_SCORE);
			e.u.Score.PlayerUID = playerUID;
			if (actor->flags & FLAGS_PENALTY)
			{
				e.u.Score.Score = PENALTY_MULTIPLIER * power;
			}
			else
			{
				e.u.Score.Score = power;
			}
			GameEventsEnqueue(&gGameEvents, e);
		}
	}
}
Example #8
0
// Check collision with a diamond shape
// This means that the bounding box could be in collision, but the bounding
// "radius" is not. The diamond is expressed with a single "radius" - that is,
// the diamond is the same height and width.
// This arrangement is used so that axis movement can slide off corners by
// moving in a diagonal direction.
// E.g. this is not a collision:
//       x
//     x   x
//   x       x
// x           x
//   x       x
//     x   x wwwww
//       x   w
//           w
// Where 'x' denotes the bounding diamond, and 'w' represents a wall corner.
bool IsCollisionDiamond(const Map *map, const Vec2i pos, const Vec2i fullSize)
{
	const Vec2i mapSize =
		Vec2iNew(map->Size.x * TILE_WIDTH, map->Size.y * TILE_HEIGHT);
	const Vec2i size = Vec2iScaleDiv(fullSize, 2);
	if (pos.x - size.x < 0 || pos.x + size.x >= mapSize.x ||
		pos.y - size.y < 0 || pos.y + size.y >= mapSize.y)
	{
		return true;
	}

	// Only support wider-than-taller collision diamonds for now
	CASSERT(size.x >= size.y, "not implemented, taller than wider diamond");
	const double gradient = (double)size.y / size.x;

	// Now we need to check in a diamond pattern that the boundary does not
	// collide
	// Top to right
	for (int i = 0; i < size.x; i++)
	{
		const int y = (int)Round((-size.x + i)* gradient);
		const Vec2i p = Vec2iAdd(pos, Vec2iNew(i, y));
		if (HitWall(p.x, p.y))
		{
			return true;
		}
	}
	// Right to bottom
	for (int i = 0; i < size.x; i++)
	{
		const int y = (int)Round(i * gradient);
		const Vec2i p = Vec2iAdd(pos, Vec2iNew(size.x - i, y));
		if (HitWall(p.x, p.y))
		{
			return true;
		}
	}
	// Bottom to left
	for (int i = 0; i < size.x; i++)
	{
		const int y = (int)Round((size.x - i) * gradient);
		const Vec2i p = Vec2iAdd(pos, Vec2iNew(-i, y));
		if (HitWall(p.x, p.y))
		{
			return true;
		}
	}
	// Left to top
	for (int i = 0; i < size.x; i++)
	{
		const int y = (int)Round(-i * gradient);
		const Vec2i p = Vec2iAdd(pos, Vec2iNew(-size.x + i, y));
		if (HitWall(p.x, p.y))
		{
			return true;
		}
	}
	return false;
}
static void DrawMapItem(
	UIObject *o, GraphicsDevice *g, Vec2i pos, void *vData)
{
	UNUSED(g);
	const EditorBrush *brush = vData;
	DisplayMapItem(
		Vec2iAdd(Vec2iAdd(pos, o->Pos), Vec2iScaleDiv(o->Size, 2)),
		brush->u.MapObject);
}
Example #10
0
static void LoadMapObject(MapObject *m, json_t *node)
{
	memset(m, 0, sizeof *m);
	m->Idx = -1;

	LoadInt(&m->Idx, node, "Index");
	m->Name = GetString(node, "Name");
	LoadPic(&m->Normal.Pic, node, "Pic", "OldPic");
	LoadPic(&m->Wreck.Pic, node, "WreckPic", "OldWreckPic");
	if (m->Normal.Pic)
	{
		// Default offset: centered X, align bottom of tile and sprite
		m->Normal.Offset = Vec2iNew(
			-m->Normal.Pic->size.x / 2,
			TILE_HEIGHT / 2 - m->Normal.Pic->size.y);
		LoadVec2i(&m->Normal.Offset, node, "Offset");
	}
	if (m->Wreck.Pic)
	{
		m->Wreck.Offset = Vec2iScaleDiv(m->Wreck.Pic->size, -2);
		LoadVec2i(&m->Wreck.Offset, node, "WreckOffset");
	}
	// Default tile size
	m->Size = Vec2iNew(TILE_WIDTH, TILE_HEIGHT);
	LoadVec2i(&m->Size, node, "Size");
	LoadInt(&m->Health, node, "Health");
	LoadBulletGuns(&m->DestroyGuns, node, "DestroyGuns");
	json_t *flagsNode = json_find_first_label(node, "Flags");
	if (flagsNode != NULL && flagsNode->child != NULL)
	{
		for (json_t *flagNode = flagsNode->child->child;
			flagNode;
			flagNode = flagNode->next)
		{
			m->Flags |= 1 << StrPlacementFlag(flagNode->text);
		}
	}

	// Special types
	JSON_UTILS_LOAD_ENUM(m->Type, node, "Type", StrMapObjectType);
	switch (m->Type)
	{
	case MAP_OBJECT_TYPE_NORMAL:
		// Do nothing
		break;
	case MAP_OBJECT_TYPE_PICKUP_SPAWNER:
		{
			char *tmp = GetString(node, "Pickup");
			m->u.PickupClass = StrPickupClass(tmp);
			CFREE(tmp);
		}
		break;
	default:
		CASSERT(false, "unknown error");
		break;
	}
}
Example #11
0
void DrawKey(UIObject *o, GraphicsDevice *g, Vec2i pos, void *vData)
{
	UNUSED(g);
	EditorBrushAndCampaign *data = vData;
	PicPaletted *keyPic = PicManagerGetOldPic(
		&gPicManager,
		cGeneralPics[gMission.keyPics[data->Brush.ItemIndex]].picIndex);
	pos = Vec2iAdd(Vec2iAdd(pos, o->Pos), Vec2iScaleDiv(o->Size, 2));
	DrawTPic(pos.x, pos.y, keyPic);
}
static void DrawObjective(
	UIObject *o, GraphicsDevice *g, Vec2i pos, void *vData)
{
	UNUSED(g);
	EditorBrushAndCampaign *data = vData;
	Mission *m = CampaignGetCurrentMission(data->Campaign);
	const Objective *obj = CArrayGet(&m->Objectives, data->Brush.u.ItemIndex);
	CharacterStore *store = &data->Campaign->Setting.characters;
	pos = Vec2iAdd(Vec2iAdd(pos, o->Pos), Vec2iScaleDiv(o->Size, 2));
	switch (obj->Type)
	{
	case OBJECTIVE_KILL:
		{
			Character *c = CArrayGet(
				&store->OtherChars,
				CharacterStoreGetSpecialId(store, data->Brush.Index2));
			DrawCharacterSimple(c, pos, DIRECTION_DOWN, false, false);
		}
		break;
	case OBJECTIVE_RESCUE:
		{
			Character *c = CArrayGet(
				&store->OtherChars,
				CharacterStoreGetPrisonerId(store, data->Brush.Index2));
			DrawCharacterSimple(c, pos, DIRECTION_DOWN, false, false);
		}
		break;
	case OBJECTIVE_COLLECT:
		{
			const Pic *p = obj->u.Pickup->Pic;
			pos = Vec2iMinus(pos, Vec2iScaleDiv(p->size, 2));
			Blit(&gGraphicsDevice, p, pos);
		}
		break;
	case OBJECTIVE_DESTROY:
		DisplayMapItem(pos, obj->u.MapObject);
		break;
	default:
		assert(0 && "invalid objective type");
		break;
	}
}
static void DrawCharacter(
	UIObject *o, GraphicsDevice *g, Vec2i pos, void *vData)
{
	UNUSED(g);
	EditorBrushAndCampaign *data = vData;
	CharacterStore *store = &data->Campaign->Setting.characters;
	Character *c = CArrayGet(&store->OtherChars, data->Brush.u.ItemIndex);
	DrawCharacterSimple(
		c,
		Vec2iAdd(Vec2iAdd(pos, o->Pos), Vec2iScaleDiv(o->Size, 2)),
		DIRECTION_DOWN, false, false);
}
static void DrawWreck(
	UIObject *o, GraphicsDevice *g, Vec2i pos, void *vData)
{
	const IndexedEditorBrush *data = vData;
	const char **name =
		CArrayGet(&gMapObjects.Destructibles, data->u.ItemIndex);
	const MapObject *mo = StrMapObject(*name);
	pos = Vec2iAdd(Vec2iAdd(pos, o->Pos), Vec2iScaleDiv(o->Size, 2));
	Vec2i offset;
	const Pic *pic = MapObjectGetPic(mo, &offset, true);
	Blit(g, pic, Vec2iAdd(pos, offset));
}
Example #15
0
static Vec2i GetActorDrawOffset(
	const Pic *pic, const BodyPart part, const CharSprites *cs,
	const ActorAnimation anim, const int frame, const direction_e d)
{
	Vec2i offset = Vec2iScaleDiv(pic->size, -2);
	offset = Vec2iMinus(offset, CharSpritesGetOffset(
		cs->Offsets.Frame[part],
		anim == ACTORANIMATION_WALKING ? "run" : "idle",
		frame));
	offset = Vec2iAdd(offset, cs->Offsets.Dir[part][d]);
	return offset;
}
Example #16
0
void MousePrePoll(Mouse *mouse)
{
	memset(mouse->pressedButtons, 0, sizeof mouse->pressedButtons);
	memcpy(
		mouse->previousButtons,
		mouse->currentButtons,
		sizeof mouse->previousButtons);
	mouse->previousPos = mouse->currentPos;
	SDL_GetMouseState(&mouse->currentPos.x, &mouse->currentPos.y);
	mouse->currentPos =
		Vec2iScaleDiv(mouse->currentPos, gConfig.Graphics.ScaleFactor);
}
Example #17
0
static void DrawThing(DrawBuffer *b, const TTileItem *t, const Vec2i offset)
{
	const Vec2i picPos = Vec2iNew(
		t->x - b->xTop + offset.x, t->y - b->yTop + offset.y);

	if (!Vec2iIsZero(t->ShadowSize))
	{
		DrawShadow(&gGraphicsDevice, picPos, t->ShadowSize);
	}

	if (t->CPicFunc)
	{
		CPicDrawContext c = t->CPicFunc(t->id);
		CPicDraw(b->g, &t->CPic, picPos, &c);
	}
	else if (t->getPicFunc)
	{
		Vec2i picOffset;
		const Pic *pic = t->getPicFunc(t->id, &picOffset);
		Blit(&gGraphicsDevice, pic, Vec2iAdd(picPos, picOffset));
	}
	else if (t->kind == KIND_CHARACTER)
	{
		TActor *a = CArrayGet(&gActors, t->id);
		ActorPics pics = GetCharacterPicsFromActor(a);
		DrawActorPics(&pics, picPos);
		// Draw weapon indicators
		DrawLaserSight(&pics, a, picPos);
	}
	else
	{
		(*(t->drawFunc))(picPos, &t->drawData);
	}

#ifdef DEBUG_DRAW_HITBOXES
	const int pulsePeriod = ConfigGetInt(&gConfig, "Game.FPS");
	int alphaUnscaled =
		(gMission.time % pulsePeriod) * 255 / (pulsePeriod / 2);
	if (alphaUnscaled > 255)
	{
		alphaUnscaled = 255 * 2 - alphaUnscaled;
	}
	color_t color = colorPurple;
	color.a = (Uint8)alphaUnscaled;
	DrawRectangle(
		&gGraphicsDevice, Vec2iMinus(picPos, Vec2iScaleDiv(t->size, 2)),
		t->size, color, DRAW_FLAG_LINE);
#endif
}
Example #18
0
static void PlayerSelectionDraw(void *data)
{
	const PlayerSelectionData *pData = data;

	GraphicsBlitBkg(&gGraphicsDevice);
	const int w = gGraphicsDevice.cachedConfig.Res.x;
	const int h = gGraphicsDevice.cachedConfig.Res.y;
	int idx = 0;
	for (int i = 0; i < (int)gPlayerDatas.size; i++, idx++)
	{
		const PlayerData *p = CArrayGet(&gPlayerDatas, i);
		if (!p->IsLocal)
		{
			idx--;
			continue;
		}
		if (p->inputDevice != INPUT_DEVICE_UNSET)
		{
			MenuDisplay(&pData->menus[idx].ms);
		}
		else
		{
			Vec2i center = Vec2iZero();
			const char *prompt = "Press Fire to join...";
			const Vec2i offset = Vec2iScaleDiv(FontStrSize(prompt), -2);
			switch (GetNumPlayers(false, false, true))
			{
			case 1:
				// Center of screen
				center = Vec2iNew(w / 2, h / 2);
				break;
			case 2:
				// Side by side
				center = Vec2iNew(idx * w / 2 + w / 4, h / 2);
				break;
			case 3:
			case 4:
				// Four corners
				center = Vec2iNew(
					(idx & 1) * w / 2 + w / 4, (idx / 2) * h / 2 + h / 4);
				break;
			default:
				CASSERT(false, "not implemented");
				break;
			}
			FontStr(prompt, Vec2iAdd(center, offset));
		}
	}
}
Example #19
0
static bool ItemsCollide(
	const TTileItem *item1, const TTileItem *item2, const Vec2i pos)
{
	int dx = abs(pos.x - item2->x);
	int dy = abs(pos.y - item2->y);
	const Vec2i r = Vec2iScaleDiv(Vec2iAdd(item1->size, item2->size), 2);

	if (dx < r.x && dy < r.y)
	{
		int odx = abs(item1->x - item2->x);
		int ody = abs(item1->y - item2->y);

		if (dx <= odx || dy <= ody)
		{
			return 1;
		}
	}
	return 0;
}
Example #20
0
static void DrawObjectiveInfo(const Objective *o, const Vec2i pos)
{
	const CharacterStore *store = &gCampaign.Setting.characters;

	switch (o->Type)
	{
	case OBJECTIVE_KILL:
		{
			const Character *cd = CArrayGet(
				&store->OtherChars, CharacterStoreGetSpecialId(store, 0));
			DrawHead(cd, DIRECTION_DOWN, STATE_IDLE, pos);
		}
		break;
	case OBJECTIVE_RESCUE:
		{
			const Character *cd = CArrayGet(
				&store->OtherChars, CharacterStoreGetPrisonerId(store, 0));
			DrawHead(cd, DIRECTION_DOWN, STATE_IDLE, pos);
		}
		break;
	case OBJECTIVE_COLLECT:
		{
			const Pic *p = o->u.Pickup->Pic;
			Blit(&gGraphicsDevice, p,
				Vec2iMinus(pos, Vec2iScaleDiv(p->size, 2)));
		}
		break;
	case OBJECTIVE_DESTROY:
		{
			Vec2i picOffset;
			const Pic *p =
				MapObjectGetPic(o->u.MapObject, &picOffset, false);
			Blit(&gGraphicsDevice, p, Vec2iAdd(pos, picOffset));
		}
		break;
	case OBJECTIVE_INVESTIGATE:
		// Don't draw
		return;
	default:
		CASSERT(false, "Unknown objective type");
		return;
	}
}
Example #21
0
static Vec2i GetActorDrawOffset(
	const Vec2i pos, const Pic *pic, const BodyPart part, const direction_e d)
{
	Vec2i outPos = Vec2iMinus(pos, Vec2iScaleDiv(pic->size, 2));
	switch (part)
	{
	case BODY_PART_HEAD:
		outPos.y -= NECK_OFFSET;
		break;
	case BODY_PART_BODY:
		outPos.y -= FOOT_OFFSET;
		break;
	case BODY_PART_GUN:
		outPos = Vec2iAdd(outPos, cGunHandOffset[d]);
		outPos.y -= WRIST_OFFSET;
		break;
	default:
		CASSERT(false, "unknown body part");
		break;
	}
	return outPos;
}
Example #22
0
bool IsCollisionWithWall(const Vec2i pos, const Vec2i fullSize)
{
	Vec2i size = Vec2iScaleDiv(fullSize, 2);
	if (pos.x - size.x < 0 ||
		pos.y - size.y < 0 ||
		pos.x + size.x >= gMap.Size.x * TILE_WIDTH ||
		pos.y + size.y >= gMap.Size.y * TILE_HEIGHT)
	{
		return true;
	}
	if (HitWall(pos.x - size.x,	pos.y - size.y) ||
		HitWall(pos.x - size.x,	pos.y) ||
		HitWall(pos.x - size.x,	pos.y + size.y) ||
		HitWall(pos.x,			pos.y + size.y) ||
		HitWall(pos.x + size.x,	pos.y + size.y) ||
		HitWall(pos.x + size.x,	pos.y) ||
		HitWall(pos.x + size.x,	pos.y - size.y) ||
		HitWall(pos.x,			pos.y - size.y))
	{
		return true;
	}
	return false;
}
Example #23
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);
}
Example #24
0
void HUDDraw(HUD *hud, const input_device_e pausingDevice)
{
	char s[50];
	int flags = 0;
	const int numPlayersAlive =
		GetNumPlayers(PLAYER_ALIVE_OR_DYING, false, false);
	const int numLocalPlayers = GetNumPlayers(PLAYER_ANY, false, true);
	const int numLocalPlayersAlive =
		GetNumPlayers(PLAYER_ALIVE_OR_DYING, false, true);

	Rect2i r;
	r.Size = Vec2iNew(
		hud->device->cachedConfig.Res.x,
		hud->device->cachedConfig.Res.y);
	if (numLocalPlayersAlive <= 1)
	{
		flags = 0;
	}
	else if (
		ConfigGetEnum(&gConfig, "Interface.Splitscreen") == SPLITSCREEN_NEVER)
	{
		flags |= HUDFLAGS_SHARE_SCREEN;
	}
	else if (numLocalPlayers == 2)
	{
		r.Size.x /= 2;
		flags |= HUDFLAGS_HALF_SCREEN;
	}
	else if (numLocalPlayers == 3 || numLocalPlayers == 4)
	{
		r.Size.x /= 2;
		r.Size.y /= 2;
		flags |= HUDFLAGS_QUARTER_SCREEN;
	}
	else
	{
		assert(0 && "not implemented");
	}

	int idx = 0;
	for (int i = 0; i < (int)gPlayerDatas.size; i++, idx++)
	{
		const PlayerData *p = CArrayGet(&gPlayerDatas, i);
		if (!p->IsLocal)
		{
			idx--;
			continue;
		}
		int drawFlags = flags;
		r.Pos = Vec2iZero();
		if (idx & 1)
		{
			r.Pos.x = r.Size.x;
			drawFlags |= HUDFLAGS_PLACE_RIGHT;
		}
		if (idx >= 2)
		{
			r.Pos.y = r.Size.y;
			drawFlags |= HUDFLAGS_PLACE_BOTTOM;
		}
		TActor *player = NULL;
		if (IsPlayerAlive(p))
		{
			player = ActorGetByUID(p->ActorUID);
		}
		DrawPlayerStatus(hud, p, player, drawFlags, r);
		DrawScoreUpdate(&hud->scoreUpdates[idx], drawFlags);
		DrawHealthUpdate(&hud->healthUpdates[idx], drawFlags);
		DrawAmmoUpdate(&hud->ammoUpdates[idx], drawFlags);
	}
	// Only draw radar once if shared
	if (ConfigGetBool(&gConfig, "Interface.ShowHUDMap") &&
		(flags & HUDFLAGS_SHARE_SCREEN) &&
		IsAutoMapEnabled(gCampaign.Entry.Mode))
	{
		DrawSharedRadar(hud->device, RADAR_SCALE, hud->showExit);
	}

	if (numPlayersAlive == 0)
	{
		if (AreAllPlayersDeadAndNoLives())
		{
			if (!IsPVP(gCampaign.Entry.Mode))
			{
				FontStrCenter("Game Over!");
			}
			else
			{
				FontStrCenter("All Kill!");
			}
		}
	}
	else if (hud->mission->state == MISSION_STATE_PICKUP)
	{
		int timeLeft = gMission.pickupTime + PICKUP_LIMIT - gMission.time;
		sprintf(s, "Pickup in %d seconds\n",
			(timeLeft + (FPS_FRAMELIMIT - 1)) / FPS_FRAMELIMIT);
		FontStrCenter(s);
	}

	if (pausingDevice != INPUT_DEVICE_UNSET)
	{
		Vec2i pos = Vec2iScaleDiv(Vec2iMinus(
			gGraphicsDevice.cachedConfig.Res,
			FontStrSize("Foo\nPress foo or bar to unpause\nBaz")), 2);
		const int x = pos.x;
		FontStr("<Paused>", pos);

		pos.y += FontH();
		pos = FontStr("Press ", pos);
		color_t c = colorWhite;
		const char *buttonName =
			InputGetButtonNameColor(pausingDevice, 0, CMD_ESC, &c);
		pos = FontStrMask(buttonName, pos, c);
		FontStr(" again to quit", pos);

		pos.x = x;
		pos.y += FontH();
		pos = FontStr("Press ", pos);
		buttonName = InputGetButtonNameColor(
			pausingDevice, 0, CMD_BUTTON1, &c);
		pos = FontStrMask(buttonName, pos, c);
		pos = FontStr(" or ", pos);
		buttonName = InputGetButtonNameColor(
			pausingDevice, 0, CMD_BUTTON2, &c);
		pos = FontStrMask(buttonName, pos, c);
		FontStr(" to unpause", pos);
	}

	if (hud->messageTicks > 0 || hud->messageTicks == -1)
	{
		// Draw the message centered, and just below the automap
		Vec2i pos = Vec2iNew(
			(hud->device->cachedConfig.Res.x -
			FontStrW(hud->message)) / 2,
			AUTOMAP_SIZE + AUTOMAP_PADDING + AUTOMAP_PADDING);
		FontStrMask(hud->message, pos, colorCyan);
	}

	if (ConfigGetBool(&gConfig, "Interface.ShowFPS"))
	{
		FPSCounterDraw(&hud->fpsCounter);
	}
	if (ConfigGetBool(&gConfig, "Interface.ShowTime"))
	{
		WallClockDraw(&hud->clock);
	}

	DrawKeycards(hud);

	// Draw elapsed mission time as MM:SS
	int missionTimeSeconds = gMission.time / FPS_FRAMELIMIT;
	sprintf(s, "%d:%02d",
		missionTimeSeconds / 60, missionTimeSeconds % 60);

	FontOpts opts = FontOptsNew();
	opts.HAlign = ALIGN_CENTER;
	opts.Area = hud->device->cachedConfig.Res;
	opts.Pad.y = 5;
	FontStrOpt(s, Vec2iZero(), opts);

	if (HasObjectives(gCampaign.Entry.Mode))
	{
		DrawObjectiveCounts(hud);
	}
}
Example #25
0
void EventPoll(EventHandlers *handlers, Uint32 ticks)
{
	SDL_Event e;
	handlers->HasResolutionChanged = false;
	handlers->HasLostFocus = false;
	KeyPrePoll(&handlers->keyboard);
	MousePrePoll(&handlers->mouse);
	JoyPrePoll(&handlers->joysticks);
	SDL_free(handlers->DropFile);
	handlers->DropFile = NULL;
	// Don't process mouse events if focus just regained this cycle
	// This is to prevent bogus click events outside the window, e.g. in the
	// title bar
	bool regainedFocus = false;
	while (SDL_PollEvent(&e))
	{
		switch (e.type)
		{
		case SDL_KEYDOWN:
			if (e.key.repeat)
			{
				break;
			}
			KeyOnKeyDown(&handlers->keyboard, e.key.keysym);
			break;
		case SDL_KEYUP:
			KeyOnKeyUp(&handlers->keyboard, e.key.keysym);
			break;
		case SDL_TEXTINPUT:
			strcpy(handlers->keyboard.Typed, e.text.text);
			break;

		case SDL_CONTROLLERDEVICEADDED:
			{
				const SDL_JoystickID jid = JoyAdded(e.cdevice.which);
				if (jid == -1)
				{
					break;
				}
				// If there are players with unset devices,
				// set this controller to them
				CA_FOREACH(PlayerData, p, gPlayerDatas)
					if (p->inputDevice == INPUT_DEVICE_UNSET)
					{
						PlayerTrySetInputDevice(p, INPUT_DEVICE_JOYSTICK, jid);
						LOG(LM_INPUT, LL_INFO,
							"Joystick %d assigned to player %d", jid, p->UID);
						break;
					}
				CA_FOREACH_END()
			}
			break;

		case SDL_CONTROLLERDEVICEREMOVED:
			JoyRemoved(e.cdevice.which);
			// If there was a player using this joystick,
			// set their input device to nothing
			CA_FOREACH(PlayerData, p, gPlayerDatas)
				if (p->inputDevice == INPUT_DEVICE_JOYSTICK &&
					p->deviceIndex == e.cdevice.which)
				{
					PlayerTrySetInputDevice(p, INPUT_DEVICE_UNSET, 0);
					LOG(LM_INPUT, LL_WARN, "Joystick for player %d removed",
						p->UID);
					break;
				}
			CA_FOREACH_END()
			break;

		case SDL_CONTROLLERBUTTONDOWN:
			JoyOnButtonDown(e.cbutton);
			break;

		case SDL_CONTROLLERBUTTONUP:
			JoyOnButtonUp(e.cbutton);
			break;

		case SDL_CONTROLLERAXISMOTION:
			JoyOnAxis(e.caxis);
			break;

		case SDL_MOUSEBUTTONDOWN:
			if (regainedFocus) break;
			MouseOnButtonDown(&handlers->mouse, e.button.button);
			break;
		case SDL_MOUSEBUTTONUP:
			if (regainedFocus) break;
			MouseOnButtonUp(&handlers->mouse, e.button.button);
			break;
		case SDL_MOUSEWHEEL:
			if (regainedFocus) break;
			MouseOnWheel(&handlers->mouse, e.wheel.x, e.wheel.y);
			break;
		case SDL_WINDOWEVENT:
			switch (e.window.event)
			{
			case SDL_WINDOWEVENT_FOCUS_GAINED:
				regainedFocus = true;
				MusicSetPlaying(&gSoundDevice, true);
				break;
			case SDL_WINDOWEVENT_FOCUS_LOST:
				if (!gCampaign.IsClient && !ConfigGetBool(&gConfig, "StartServer"))
				{
					MusicSetPlaying(&gSoundDevice, false);
					handlers->HasLostFocus = true;
				}
				// Reset input handlers
				EventReset(
					handlers, handlers->mouse.cursor, handlers->mouse.trail);
				break;
			case SDL_WINDOWEVENT_SIZE_CHANGED:
				handlers->HasResolutionChanged = true;
				if (gGraphicsDevice.cachedConfig.IsEditor)
				{
					const int scale = ConfigGetInt(&gConfig, "Graphics.ScaleFactor");
					GraphicsConfigSet(
						&gGraphicsDevice.cachedConfig,
						Vec2iScaleDiv(
							Vec2iNew(e.window.data1, e.window.data2), scale),
						false,
						scale,
						gGraphicsDevice.cachedConfig.ScaleMode,
						gGraphicsDevice.cachedConfig.Brightness);
					GraphicsInitialize(&gGraphicsDevice);
				}
				break;
			default:
				// do nothing
				break;
			}
			break;
		case SDL_QUIT:
			handlers->HasQuit = true;
			break;
		case SDL_DROPFILE:
			handlers->DropFile = e.drop.file;
			break;
		default:
			break;
		}
	}
	KeyPostPoll(&handlers->keyboard, ticks);
	MousePostPoll(&handlers->mouse, ticks);
}
Example #26
0
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;
}
Example #27
0
int PlayerSelection(int numPlayers, GraphicsDevice *graphics)
{
	int i;
	int hasInputDevice[MAX_PLAYERS];
	PlayerSelectMenu menus[MAX_PLAYERS];
	for (i = 0; i < numPlayers; i++)
	{
		PlayerSelectMenusCreate(
			&menus[i], numPlayers, i,
			&gCampaign.Setting.characters.players[i], &gPlayerDatas[i],
			&gInputDevices, graphics, &gConfig.Input);
		hasInputDevice[i] = 0;
	}

	KeyInit(&gInputDevices.keyboard);
	for (;;)
	{
		int cmds[MAX_PLAYERS];
		int isDone = 1;
		InputPoll(&gInputDevices, SDL_GetTicks());
		if (KeyIsPressed(&gInputDevices.keyboard, SDLK_ESCAPE))
		{
			// TODO: destroy menus
			return 0; // hack to allow exit
		}
		GetPlayerCmds(&cmds, gPlayerDatas);
		for (i = 0; i < numPlayers; i++)
		{
			if (hasInputDevice[i] && !MenuIsExit(&menus[i].ms))
			{
				MenuProcessCmd(&menus[i].ms, cmds[i]);
			}
		}
		for (i = 0; i < numPlayers; i++)
		{
			if (!MenuIsExit(&menus[i].ms))
			{
				isDone = 0;
			}
		}
		if (isDone)
		{
			break;
		}

		AssignPlayerInputDevices(
			hasInputDevice, numPlayers,
			gPlayerDatas, &gInputDevices, &gConfig.Input);

		GraphicsBlitBkg(graphics);
		for (i = 0; i < numPlayers; i++)
		{
			if (hasInputDevice[i])
			{
				MenuDisplay(&menus[i].ms);
			}
			else
			{
				Vec2i center;
				const char *prompt = "Press Fire to join...";
				Vec2i offset = Vec2iScaleDiv(TextGetSize(prompt), -2);
				int w = graphics->cachedConfig.ResolutionWidth;
				int h = graphics->cachedConfig.ResolutionHeight;
				switch (numPlayers)
				{
				case 1:
					// Center of screen
					center = Vec2iNew(w / 2, h / 2);
					break;
				case 2:
					// Side by side
					center = Vec2iNew(i * w / 2 + w / 4, h / 2);
					break;
				case 3:
				case 4:
					// Four corners
					center = Vec2iNew(
						(i & 1) * w / 2 + w / 4, (i / 2) * h / 2 + h / 4);
					break;
				default:
					assert(0 && "not implemented");
					break;
				}
				DrawTextString(prompt, graphics, Vec2iAdd(center, offset));
			}
		}
		BlitFlip(graphics, &gConfig.Graphics);
		SDL_Delay(10);
	}

	for (i = 0; i < numPlayers; i++)
	{
		MenuSystemTerminate(&menus[i].ms);
	}
	return 1;
}