mirror of
https://github.com/cxong/cdogs-sdl.git
synced 2025-07-23 07:23:01 +02:00
Quizzes, pickup menus #712
This commit is contained in:
@@ -114,6 +114,9 @@
|
||||
"Type": "Ammo",
|
||||
"Ammo": "Feed",
|
||||
"Amount": 10
|
||||
}, {
|
||||
"Type": "Sound",
|
||||
"Sound": "bonus"
|
||||
}]
|
||||
}, {
|
||||
"Text": "11",
|
||||
@@ -121,8 +124,8 @@
|
||||
"Type": "Health",
|
||||
"Health": 4
|
||||
}, {
|
||||
"Type": "Health",
|
||||
"Health": 4
|
||||
"Type": "Sound",
|
||||
"Sound": "wrong"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ include_directories(
|
||||
add_definitions(-DSTATIC)
|
||||
set(CDOGS_SOURCES
|
||||
actor_fire.c
|
||||
actor_pickup.c
|
||||
actor_placement.c
|
||||
actors.c
|
||||
aheasing/easing.c
|
||||
@@ -115,6 +116,7 @@ set(CDOGS_SOURCES
|
||||
yajl_utils.c)
|
||||
set(CDOGS_HEADERS
|
||||
actor_fire.h
|
||||
actor_pickup.h
|
||||
actor_placement.h
|
||||
actors.h
|
||||
aheasing/easing.h
|
||||
|
313
src/cdogs/actor_pickup.c
Normal file
313
src/cdogs/actor_pickup.c
Normal file
@@ -0,0 +1,313 @@
|
||||
/*
|
||||
C-Dogs SDL
|
||||
A port of the legendary (and fun) action/arcade cdogs.
|
||||
Copyright (c) 2014-2015, 2017-2020, 2022-2024 Cong Xu
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "actor_pickup.h"
|
||||
|
||||
#include "campaigns.h"
|
||||
#include "gamedata.h"
|
||||
#include "net_util.h"
|
||||
|
||||
static bool TreatAsGunPickup(const PickupEffect *pe, const TActor *a);
|
||||
static bool TryPickupAmmo(TActor *a, const Pickup *p, const PickupEffect *pe);
|
||||
static bool TryPickupGun(
|
||||
TActor *a, const PickupEffect *pe, const bool pickupAll,
|
||||
const char **sound);
|
||||
void PickupPickup(TActor *a, Pickup *p, const bool pickupAll)
|
||||
{
|
||||
if (p->PickedUp)
|
||||
return;
|
||||
CASSERT(a->PlayerUID >= 0, "NPCs cannot pickup");
|
||||
// can always pickup effect-less pickups
|
||||
bool canPickup = p->class->Effects.size == 0;
|
||||
const char *sound = p->class->Sound;
|
||||
GameEvent e;
|
||||
|
||||
CA_FOREACH(const PickupEffect, pe, p->class->Effects)
|
||||
canPickup = PickupApplyEffect(a, p, pe, pickupAll, &sound);
|
||||
CA_FOREACH_END()
|
||||
|
||||
if (canPickup)
|
||||
{
|
||||
if (sound != NULL)
|
||||
{
|
||||
e = GameEventNew(GAME_EVENT_SOUND_AT);
|
||||
strcpy(e.u.SoundAt.Sound, sound);
|
||||
e.u.SoundAt.Pos = Vec2ToNet(a->thing.Pos);
|
||||
GameEventsEnqueue(&gGameEvents, e);
|
||||
}
|
||||
e = GameEventNew(GAME_EVENT_REMOVE_PICKUP);
|
||||
e.u.RemovePickup.UID = p->UID;
|
||||
e.u.RemovePickup.SpawnerUID = p->SpawnerUID;
|
||||
GameEventsEnqueue(&gGameEvents, e);
|
||||
// Prevent multiple pickups by marking
|
||||
p->PickedUp = true;
|
||||
a->PickupAll = false;
|
||||
}
|
||||
}
|
||||
bool PickupApplyEffect(
|
||||
TActor *a, const Pickup *p, const PickupEffect *pe, const bool force,
|
||||
char **sound)
|
||||
{
|
||||
bool canPickup = false;
|
||||
GameEvent e;
|
||||
switch (pe->Type)
|
||||
{
|
||||
case PICKUP_JEWEL: {
|
||||
canPickup = true;
|
||||
e = GameEventNew(GAME_EVENT_SCORE);
|
||||
e.u.Score.PlayerUID = a->PlayerUID;
|
||||
e.u.Score.Score = pe->u.Score;
|
||||
GameEventsEnqueue(&gGameEvents, e);
|
||||
|
||||
e = GameEventNew(GAME_EVENT_ADD_PARTICLE);
|
||||
e.u.AddParticle.Class =
|
||||
StrParticleClass(&gParticleClasses, "score_text");
|
||||
e.u.AddParticle.ActorUID = a->uid;
|
||||
e.u.AddParticle.Pos = p->thing.Pos;
|
||||
e.u.AddParticle.DZ = 3;
|
||||
if (gCampaign.Setting.Ammo)
|
||||
{
|
||||
sprintf(e.u.AddParticle.Text, "$%d", pe->u.Score);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(e.u.AddParticle.Text, "+%d", pe->u.Score);
|
||||
}
|
||||
GameEventsEnqueue(&gGameEvents, e);
|
||||
|
||||
UpdateMissionObjective(
|
||||
&gMission, p->thing.flags, OBJECTIVE_COLLECT, 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case PICKUP_HEALTH:
|
||||
// Don't pick up unless taken damage
|
||||
if (a->health < ActorGetMaxHeal(a, pe->u.Heal.ExceedMax))
|
||||
{
|
||||
canPickup = true;
|
||||
e = GameEventNew(GAME_EVENT_ACTOR_HEAL);
|
||||
e.u.Heal.UID = a->uid;
|
||||
e.u.Heal.PlayerUID = a->PlayerUID;
|
||||
e.u.Heal.Amount = pe->u.Heal.Amount;
|
||||
e.u.Heal.ExceedMax = pe->u.Heal.ExceedMax;
|
||||
e.u.Heal.IsRandomSpawned = p->IsRandomSpawned;
|
||||
GameEventsEnqueue(&gGameEvents, e);
|
||||
}
|
||||
break;
|
||||
|
||||
case PICKUP_AMMO: // fallthrough
|
||||
case PICKUP_GUN:
|
||||
if (TreatAsGunPickup(pe, a))
|
||||
{
|
||||
canPickup = TryPickupGun(a, pe, force, sound) || canPickup;
|
||||
}
|
||||
else
|
||||
{
|
||||
canPickup = TryPickupAmmo(a, p, pe) || canPickup;
|
||||
}
|
||||
break;
|
||||
|
||||
case PICKUP_KEYCARD: {
|
||||
canPickup = true;
|
||||
e = GameEventNew(GAME_EVENT_ADD_KEYS);
|
||||
e.u.AddKeys.KeyFlags = pe->u.Keys;
|
||||
e.u.AddKeys.Pos = Vec2ToNet(a->thing.Pos);
|
||||
GameEventsEnqueue(&gGameEvents, e);
|
||||
if (*sound == NULL)
|
||||
{
|
||||
*sound = "key";
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PICKUP_SHOW_MAP: {
|
||||
canPickup = true;
|
||||
e = GameEventNew(GAME_EVENT_EXPLORE_TILES);
|
||||
e.u.ExploreTiles.Runs_count = 1;
|
||||
e.u.ExploreTiles.Runs[0].Run = gMap.Size.x * gMap.Size.y;
|
||||
GameEventsEnqueue(&gGameEvents, e);
|
||||
}
|
||||
break;
|
||||
|
||||
case PICKUP_LIVES: {
|
||||
canPickup = true;
|
||||
e = GameEventNew(GAME_EVENT_PLAYER_ADD_LIVES);
|
||||
e.u.PlayerAddLives.UID = a->PlayerUID;
|
||||
e.u.PlayerAddLives.Lives = pe->u.Lives;
|
||||
GameEventsEnqueue(&gGameEvents, e);
|
||||
}
|
||||
break;
|
||||
|
||||
case PICKUP_SOUND:
|
||||
canPickup = true;
|
||||
*sound = pe->u.Sound;
|
||||
break;
|
||||
|
||||
case PICKUP_MENU:
|
||||
canPickup = force;
|
||||
if (force)
|
||||
{
|
||||
a->pickupMenu.pickup = p;
|
||||
a->pickupMenu.effect = pe;
|
||||
a->pickupMenu.index = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
CASSERT(false, "unexpected pickup type");
|
||||
break;
|
||||
}
|
||||
return canPickup;
|
||||
}
|
||||
|
||||
static bool HasGunUsingAmmo(const TActor *a, const int ammoId);
|
||||
static bool TreatAsGunPickup(const PickupEffect *pe, const TActor *a)
|
||||
{
|
||||
// Grenades can also be gun pickups; treat as gun pickup if the player
|
||||
// doesn't have its ammo
|
||||
switch (pe->Type)
|
||||
{
|
||||
case PICKUP_AMMO:
|
||||
if (!HasGunUsingAmmo(a, pe->u.Ammo.Id))
|
||||
{
|
||||
const Ammo *ammo = AmmoGetById(&gAmmo, pe->u.Ammo.Id);
|
||||
if (ammo->DefaultGun)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
case PICKUP_GUN: {
|
||||
const WeaponClass *wc = IdWeaponClass(pe->u.GunId);
|
||||
return wc->Type != GUNTYPE_GRENADE ||
|
||||
!HasGunUsingAmmo(a, wc->u.Normal.AmmoId);
|
||||
}
|
||||
default:
|
||||
CASSERT(false, "unexpected pickup type");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
static bool HasGunUsingAmmo(const TActor *a, const int ammoId)
|
||||
{
|
||||
for (int i = 0; i < MAX_WEAPONS; i++)
|
||||
{
|
||||
const WeaponClass *wc = a->guns[i].Gun;
|
||||
if (wc == NULL)
|
||||
continue;
|
||||
for (int j = 0; j < WeaponClassNumBarrels(wc); j++)
|
||||
{
|
||||
if (WC_BARREL_ATTR(*wc, AmmoId, j) == ammoId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool TryPickupAmmo(TActor *a, const Pickup *p, const PickupEffect *pe)
|
||||
{
|
||||
// Don't pickup if not using ammo
|
||||
if (!gCampaign.Setting.Ammo)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// Don't pickup if ammo full
|
||||
const int ammoId = pe->Type == PICKUP_AMMO
|
||||
? (int)pe->u.Ammo.Id
|
||||
: IdWeaponClass(pe->u.GunId)->u.Normal.AmmoId;
|
||||
const Ammo *ammo = AmmoGetById(&gAmmo, ammoId);
|
||||
const int amount =
|
||||
pe->Type == PICKUP_AMMO ? (int)pe->u.Ammo.Amount : ammo->Amount;
|
||||
const int current = *(int *)CArrayGet(&a->ammo, ammoId);
|
||||
if (ammo->Max > 0 && current >= ammo->Max)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Take ammo
|
||||
GameEvent e = GameEventNew(GAME_EVENT_ACTOR_ADD_AMMO);
|
||||
e.u.AddAmmo.UID = a->uid;
|
||||
e.u.AddAmmo.PlayerUID = a->PlayerUID;
|
||||
e.u.AddAmmo.Ammo.Id = ammoId;
|
||||
e.u.AddAmmo.Ammo.Amount = amount;
|
||||
e.u.AddAmmo.IsRandomSpawned = p->IsRandomSpawned;
|
||||
// Note: receiving end will prevent ammo from exceeding max
|
||||
GameEventsEnqueue(&gGameEvents, e);
|
||||
return true;
|
||||
}
|
||||
static bool TryPickupGun(
|
||||
TActor *a, const PickupEffect *pe, const bool pickupAll,
|
||||
const char **sound)
|
||||
{
|
||||
// Guns can only be picked up manually
|
||||
if (!pickupAll)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// When picking up a gun, the actor always ends up with it equipped, but:
|
||||
// - If the player already has the gun:
|
||||
// - Switch to the same gun and drop the same gun
|
||||
// - If the player doesn't have the gun:
|
||||
// - If the player has an empty slot, pickup the gun into that slot
|
||||
// - If the player doesn't have an empty slot, replace the current gun,
|
||||
// dropping it in the process
|
||||
|
||||
const WeaponClass *wc =
|
||||
pe->Type == PICKUP_GUN
|
||||
? IdWeaponClass(pe->u.GunId)
|
||||
: StrWeaponClass(AmmoGetById(&gAmmo, pe->u.Ammo.Id)->DefaultGun);
|
||||
|
||||
ActorPickupGun(a, wc);
|
||||
|
||||
// If the player has less ammo than the default amount,
|
||||
// replenish up to this amount
|
||||
// TODO: support multi gun
|
||||
const int ammoId = WC_BARREL_ATTR(*wc, AmmoId, 0);
|
||||
if (ammoId >= 0)
|
||||
{
|
||||
const Ammo *ammo = AmmoGetById(&gAmmo, ammoId);
|
||||
const int ammoDeficit = ammo->Amount * AMMO_STARTING_MULTIPLE -
|
||||
*(int *)CArrayGet(&a->ammo, ammoId);
|
||||
if (ammoDeficit > 0)
|
||||
{
|
||||
GameEvent e = GameEventNew(GAME_EVENT_ACTOR_ADD_AMMO);
|
||||
e.u.AddAmmo.UID = a->uid;
|
||||
e.u.AddAmmo.PlayerUID = a->PlayerUID;
|
||||
e.u.AddAmmo.Ammo.Id = ammoId;
|
||||
e.u.AddAmmo.Ammo.Amount = ammoDeficit;
|
||||
e.u.AddAmmo.IsRandomSpawned = false;
|
||||
GameEventsEnqueue(&gGameEvents, e);
|
||||
|
||||
// Also play an ammo pickup sound
|
||||
*sound = ammo->Sound;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
36
src/cdogs/actor_pickup.h
Normal file
36
src/cdogs/actor_pickup.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
C-Dogs SDL
|
||||
A port of the legendary (and fun) action/arcade cdogs.
|
||||
Copyright (c) 2014-2015, 2024 Cong Xu
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "actors.h"
|
||||
#include "pickup.h"
|
||||
|
||||
void PickupPickup(TActor *a, Pickup *p, const bool pickupAll);
|
||||
bool PickupApplyEffect(
|
||||
TActor *a, const Pickup *p, const PickupEffect *pe, const bool force,
|
||||
char **sound);
|
@@ -55,6 +55,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "actor_fire.h"
|
||||
#include "actor_pickup.h"
|
||||
#include "actor_placement.h"
|
||||
#include "ai.h"
|
||||
#include "ai_coop.h"
|
||||
@@ -74,7 +75,6 @@
|
||||
#include "material.h"
|
||||
#include "mission.h"
|
||||
#include "pic_manager.h"
|
||||
#include "pickup.h"
|
||||
#include "sounds.h"
|
||||
#include "thing.h"
|
||||
#include "triggers.h"
|
||||
@@ -1057,71 +1057,126 @@ static bool TryGrenade(TActor *a, const int cmd)
|
||||
static bool ActorTryMove(TActor *actor, int cmd, int ticks);
|
||||
void CommandActor(TActor *actor, int cmd, int ticks)
|
||||
{
|
||||
actor->hasShot = false;
|
||||
// If this is a pilot, command the vehicle instead
|
||||
if (actor->vehicleUID != -1)
|
||||
GameEvent e;
|
||||
// If the actor is currently using a menu, control the menu instead
|
||||
if (actor->pickupMenu.pickup)
|
||||
{
|
||||
TActor *vehicle = ActorGetByUID(actor->vehicleUID);
|
||||
CommandActor(vehicle, cmd, ticks);
|
||||
}
|
||||
else if (actor->pilotUID == -1)
|
||||
{
|
||||
// If this is a vehicle and there's no pilot, do nothing
|
||||
if (Button1(cmd) && !Button1(actor->lastCmd))
|
||||
{
|
||||
// Apply effects of pickup menu item and reset
|
||||
const PickupMenuItem *m = CArrayGet(
|
||||
&actor->pickupMenu.effect->u.Menu.Items,
|
||||
actor->pickupMenu.index);
|
||||
bool canPickup = false;
|
||||
const char *sound = "menu_enter";
|
||||
CA_FOREACH(const PickupEffect, pe, m->Effects)
|
||||
CASSERT(pe->Type != PICKUP_MENU, "can't have nested menu effects");
|
||||
canPickup = PickupApplyEffect(
|
||||
actor, actor->pickupMenu.pickup, pe, true, &sound);
|
||||
CA_FOREACH_END()
|
||||
if (canPickup && sound != NULL)
|
||||
{
|
||||
e = GameEventNew(GAME_EVENT_SOUND_AT);
|
||||
strcpy(e.u.SoundAt.Sound, sound);
|
||||
e.u.SoundAt.Pos = Vec2ToNet(actor->thing.Pos);
|
||||
GameEventsEnqueue(&gGameEvents, e);
|
||||
}
|
||||
actor->pickupMenu.pickup = NULL;
|
||||
actor->pickupMenu.effect = NULL;
|
||||
actor->pickupMenu.index = 0;
|
||||
KeyLockKeys(&gEventHandlers.keyboard);
|
||||
JoyLock(&gEventHandlers.joysticks);
|
||||
}
|
||||
else if (Up(cmd) && !Up(actor->lastCmd))
|
||||
{
|
||||
actor->pickupMenu.index--;
|
||||
if (actor->pickupMenu.index < 0)
|
||||
{
|
||||
actor->pickupMenu.index =
|
||||
(int)actor->pickupMenu.effect->u.Menu.Items.size - 1;
|
||||
}
|
||||
SoundPlay(&gSoundDevice, StrSound("menu_switch"));
|
||||
}
|
||||
else if (Down(cmd) && !Down(actor->lastCmd))
|
||||
{
|
||||
actor->pickupMenu.index++;
|
||||
if (actor->pickupMenu.index ==
|
||||
(int)actor->pickupMenu.effect->u.Menu.Items.size)
|
||||
{
|
||||
actor->pickupMenu.index = 0;
|
||||
}
|
||||
SoundPlay(&gSoundDevice, StrSound("menu_switch"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (actor->confused)
|
||||
actor->hasShot = false;
|
||||
// If this is a pilot, command the vehicle instead
|
||||
if (actor->vehicleUID != -1)
|
||||
{
|
||||
cmd = CmdGetReverse(cmd);
|
||||
TActor *vehicle = ActorGetByUID(actor->vehicleUID);
|
||||
CommandActor(vehicle, cmd, ticks);
|
||||
}
|
||||
else if (actor->pilotUID == -1)
|
||||
{
|
||||
// If this is a vehicle and there's no pilot, do nothing
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (actor->confused)
|
||||
{
|
||||
cmd = CmdGetReverse(cmd);
|
||||
}
|
||||
|
||||
if (actor->health > 0)
|
||||
{
|
||||
const bool hasChangedDirection =
|
||||
ActorTryChangeDirection(actor, cmd, actor->lastCmd);
|
||||
actor->hasShot = ActorTryShoot(actor, cmd);
|
||||
const bool hasGrenaded = TryGrenade(actor, cmd);
|
||||
const bool hasMoved = ActorTryMove(actor, cmd, ticks);
|
||||
ActorAnimation anim = actor->anim.Type;
|
||||
// Idle if player hasn't done anything
|
||||
if (!(hasChangedDirection || actor->hasShot || hasGrenaded ||
|
||||
hasMoved))
|
||||
{
|
||||
anim = ACTORANIMATION_IDLE;
|
||||
}
|
||||
else if (hasMoved)
|
||||
{
|
||||
anim = ACTORANIMATION_WALKING;
|
||||
}
|
||||
else
|
||||
{
|
||||
anim = ACTORANIMATION_STAND;
|
||||
}
|
||||
if (actor->anim.Type != anim)
|
||||
{
|
||||
e = GameEventNew(GAME_EVENT_ACTOR_STATE);
|
||||
e.u.ActorState.UID = actor->uid;
|
||||
e.u.ActorState.State = (int32_t)anim;
|
||||
GameEventsEnqueue(&gGameEvents, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (actor->health > 0)
|
||||
actor->specialCmdDir = CMD_HAS_DIRECTION(cmd);
|
||||
if (Button2(cmd) && !actor->specialCmdDir)
|
||||
{
|
||||
const bool hasChangedDirection =
|
||||
ActorTryChangeDirection(actor, cmd, actor->lastCmd);
|
||||
actor->hasShot = ActorTryShoot(actor, cmd);
|
||||
const bool hasGrenaded = TryGrenade(actor, cmd);
|
||||
const bool hasMoved = ActorTryMove(actor, cmd, ticks);
|
||||
ActorAnimation anim = actor->anim.Type;
|
||||
// Idle if player hasn't done anything
|
||||
if (!(hasChangedDirection || actor->hasShot || hasGrenaded ||
|
||||
hasMoved))
|
||||
// Special: pick up things that can only be picked up on demand
|
||||
if (!actor->PickupAll && !Button2(actor->lastCmd) &&
|
||||
actor->vehicleUID == -1)
|
||||
{
|
||||
anim = ACTORANIMATION_IDLE;
|
||||
}
|
||||
else if (hasMoved)
|
||||
{
|
||||
anim = ACTORANIMATION_WALKING;
|
||||
}
|
||||
else
|
||||
{
|
||||
anim = ACTORANIMATION_STAND;
|
||||
}
|
||||
if (actor->anim.Type != anim)
|
||||
{
|
||||
GameEvent e = GameEventNew(GAME_EVENT_ACTOR_STATE);
|
||||
e.u.ActorState.UID = actor->uid;
|
||||
e.u.ActorState.State = (int32_t)anim;
|
||||
e = GameEventNew(GAME_EVENT_ACTOR_PICKUP_ALL);
|
||||
e.u.ActorPickupAll.UID = actor->uid;
|
||||
e.u.ActorPickupAll.PickupAll = true;
|
||||
GameEventsEnqueue(&gGameEvents, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
actor->specialCmdDir = CMD_HAS_DIRECTION(cmd);
|
||||
if (Button2(cmd) && !actor->specialCmdDir)
|
||||
{
|
||||
// Special: pick up things that can only be picked up on demand
|
||||
if (!actor->PickupAll && !Button2(actor->lastCmd) &&
|
||||
actor->vehicleUID == -1)
|
||||
{
|
||||
GameEvent e = GameEventNew(GAME_EVENT_ACTOR_PICKUP_ALL);
|
||||
e.u.ActorPickupAll.UID = actor->uid;
|
||||
e.u.ActorPickupAll.PickupAll = true;
|
||||
GameEventsEnqueue(&gGameEvents, e);
|
||||
}
|
||||
}
|
||||
|
||||
actor->lastCmd = cmd;
|
||||
}
|
||||
static bool ActorTryMove(TActor *actor, int cmd, int ticks)
|
||||
@@ -1384,26 +1439,7 @@ static bool CheckManualPickupFunc(
|
||||
strcpy(buttonName, "");
|
||||
InputGetButtonName(
|
||||
pData->inputDevice, pData->deviceIndex, CMD_BUTTON2, buttonName);
|
||||
// TODO: PickupGetName
|
||||
const char *pickupName = NULL;
|
||||
CA_FOREACH(const PickupEffect, pe, p->class->Effects)
|
||||
switch (pe->Type)
|
||||
{
|
||||
case PICKUP_AMMO:
|
||||
pickupName = AmmoGetById(&gAmmo, pe->u.Ammo.Id)->Name;
|
||||
break;
|
||||
case PICKUP_GUN:
|
||||
pickupName = IdWeaponClass(pe->u.GunId)->name;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (pickupName != NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
CA_FOREACH_END()
|
||||
CASSERT(pickupName != NULL, "unknown pickup name");
|
||||
const char *pickupName = PickupClassGetName(p->class);
|
||||
char buf[256];
|
||||
sprintf(buf, "%s to pick up\n%s", buttonName, pickupName);
|
||||
ActorSetChatter(a, buf, 2);
|
||||
|
@@ -54,6 +54,7 @@
|
||||
#include "game_mode.h"
|
||||
#include "grafx.h"
|
||||
#include "mathc/mathc.h"
|
||||
#include "pickup.h"
|
||||
#include "player.h"
|
||||
#include "thing.h"
|
||||
#include "weapon.h"
|
||||
@@ -87,8 +88,8 @@
|
||||
#define FLAGS_SNEAKY (1 << 23) // Always shoot back when player shoots
|
||||
#define FLAGS_SLEEPALWAYS (1 << 24)
|
||||
#define FLAGS_AWAKEALWAYS (1 << 25)
|
||||
#define FLAGS_RESCUED (1 << 26) // Run towards exit
|
||||
#define FLAGS_MOVE_AND_SHOOT (1 << 27) // Can move and shoot at the same time
|
||||
#define FLAGS_RESCUED (1 << 26) // Run towards exit
|
||||
#define FLAGS_MOVE_AND_SHOOT (1 << 27) // Can move and shoot at the same time
|
||||
|
||||
typedef enum
|
||||
{
|
||||
@@ -126,10 +127,10 @@ typedef struct Actor
|
||||
// -1 if human character (get from player data), otherwise index into
|
||||
// CharacterStore OtherChars
|
||||
int charId;
|
||||
int PlayerUID; // -1 unless a human player
|
||||
int uid; // unique ID across all actors
|
||||
int pilotUID; // the actor that controls this
|
||||
// (same as uid for normal actors)
|
||||
int PlayerUID; // -1 unless a human player
|
||||
int uid; // unique ID across all actors
|
||||
int pilotUID; // the actor that controls this
|
||||
// (same as uid for normal actors)
|
||||
int vehicleUID; // -1 unless piloting a vehicle
|
||||
Weapon guns[MAX_WEAPONS];
|
||||
CArray ammo; // of int
|
||||
@@ -174,6 +175,14 @@ typedef struct Actor
|
||||
|
||||
bool hasShot;
|
||||
|
||||
// Whether actor is in a pickup menu and their current selection
|
||||
struct
|
||||
{
|
||||
const Pickup *pickup;
|
||||
const PickupEffect *effect;
|
||||
int index;
|
||||
} pickupMenu;
|
||||
|
||||
// Signals to other AIs what this actor is doing
|
||||
ActorAction action;
|
||||
AIContext *aiContext;
|
||||
|
@@ -1,30 +1,30 @@
|
||||
/*
|
||||
C-Dogs SDL
|
||||
A port of the legendary (and fun) action/arcade cdogs.
|
||||
C-Dogs SDL
|
||||
A port of the legendary (and fun) action/arcade cdogs.
|
||||
|
||||
Copyright (c) 2013-2018 Cong Xu
|
||||
All rights reserved.
|
||||
Copyright (c) 2013-2018, 2024 Cong Xu
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "color.h"
|
||||
|
||||
@@ -34,56 +34,57 @@
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
color_t colorWhite = { 255, 255, 255, 255 };
|
||||
color_t colorRed = { 255, 0, 0, 255 };
|
||||
color_t colorGreen = { 0, 255, 0, 255 };
|
||||
color_t colorBlue = { 0, 0, 255, 255 };
|
||||
color_t colorPoison = { 64, 192, 64, 255 };
|
||||
color_t colorBlack = { 0, 0, 0, 255 };
|
||||
color_t colorDarker = { 192, 192, 192, 255 };
|
||||
color_t colorPurple = { 192, 0, 192, 255 };
|
||||
color_t colorGray = { 128, 128, 128, 255 };
|
||||
color_t colorDarkGray = { 64, 64, 64, 255 };
|
||||
color_t colorYellow = { 255, 255, 128, 255 };
|
||||
color_t colorMagenta = { 255, 0, 255, 255 };
|
||||
color_t colorCyan = { 0, 255, 255, 255 };
|
||||
color_t colorFog = { 96, 96, 96, 255 };
|
||||
color_t colorOrange = { 255, 128, 0, 255 };
|
||||
color_t colorTransparent = { 0, 0, 0, 0 };
|
||||
color_t colorWhite = {255, 255, 255, 255};
|
||||
color_t colorRed = {255, 0, 0, 255};
|
||||
color_t colorGreen = {0, 255, 0, 255};
|
||||
color_t colorBlue = {0, 0, 255, 255};
|
||||
color_t colorPoison = {64, 192, 64, 255};
|
||||
color_t colorBlack = {0, 0, 0, 255};
|
||||
color_t colorDarker = {192, 192, 192, 255};
|
||||
color_t colorPurple = {192, 0, 192, 255};
|
||||
color_t colorGray = {128, 128, 128, 255};
|
||||
color_t colorDarkGray = {64, 64, 64, 255};
|
||||
color_t colorYellow = {255, 255, 128, 255};
|
||||
color_t colorMagenta = {255, 0, 255, 255};
|
||||
color_t colorCyan = {0, 255, 255, 255};
|
||||
color_t colorFog = {96, 96, 96, 255};
|
||||
color_t colorOrange = {255, 128, 0, 255};
|
||||
color_t colorTransparent = {0, 0, 0, 0};
|
||||
|
||||
color_t colorMaroon = { 0x84, 0, 0, 255 };
|
||||
color_t colorLonestar = { 0x70, 0, 0, 255 };
|
||||
color_t colorRusticRed = { 0x48, 0, 0, 255 };
|
||||
color_t colorOfficeGreen = { 0, 0x84, 0, 255 };
|
||||
color_t colorPakistanGreen = { 0, 0x70, 0, 255 };
|
||||
color_t colorDarkFern = { 0, 0x48, 0, 255 };
|
||||
color_t colorNavyBlue = { 0, 0, 0x84, 255 };
|
||||
color_t colorArapawa = { 0, 0, 0x70, 255 };
|
||||
color_t colorStratos = { 0, 0, 0x48, 255 };
|
||||
color_t colorPatriarch = { 0x84, 0, 0x84, 255 };
|
||||
color_t colorPompadour = { 0x70, 0, 0x70, 255 };
|
||||
color_t colorLoulou = { 0x48, 0, 0x48, 255 };
|
||||
color_t colorBattleshipGrey = { 0x84, 0x84, 0x84, 255 };
|
||||
color_t colorDoveGray = { 0x70, 0x70, 0x70, 255 };
|
||||
color_t colorGravel = { 0x48, 0x48, 0x48, 255 };
|
||||
color_t colorComet = { 0x5C, 0x5C, 0x84, 255 };
|
||||
color_t colorFiord = { 0x48, 0x48, 0x70, 255 };
|
||||
color_t colorTuna = { 0x34, 0x34, 0x48, 255 };
|
||||
color_t colorHacienda = { 0x94, 0x80, 0x2C, 255 };
|
||||
color_t colorKumera = { 0x84, 0x70, 0x24, 255 };
|
||||
color_t colorHimalaya = { 0x74, 0x60, 0x1C, 255 };
|
||||
color_t colorChocolate = { 0x84, 0x44, 0, 255 };
|
||||
color_t colorNutmeg = { 0x70, 0x38, 0, 255 };
|
||||
color_t colorBracken = { 0x48, 0x24, 0, 255 };
|
||||
color_t colorTeal = { 0, 0x84, 0x84, 255 };
|
||||
color_t colorSkobeloff = { 0, 0x70, 0x70, 255 };
|
||||
color_t colorDeepJungleGreen = { 0, 0x48, 0x48, 255 };
|
||||
color_t colorMaroon = {0x84, 0, 0, 255};
|
||||
color_t colorLonestar = {0x70, 0, 0, 255};
|
||||
color_t colorRusticRed = {0x48, 0, 0, 255};
|
||||
color_t colorOfficeGreen = {0, 0x84, 0, 255};
|
||||
color_t colorPakistanGreen = {0, 0x70, 0, 255};
|
||||
color_t colorDarkFern = {0, 0x48, 0, 255};
|
||||
color_t colorNavyBlue = {0, 0, 0x84, 255};
|
||||
color_t colorArapawa = {0, 0, 0x70, 255};
|
||||
color_t colorStratos = {0, 0, 0x48, 255};
|
||||
color_t colorPatriarch = {0x84, 0, 0x84, 255};
|
||||
color_t colorPompadour = {0x70, 0, 0x70, 255};
|
||||
color_t colorLoulou = {0x48, 0, 0x48, 255};
|
||||
color_t colorBattleshipGrey = {0x84, 0x84, 0x84, 255};
|
||||
color_t colorDoveGray = {0x70, 0x70, 0x70, 255};
|
||||
color_t colorGravel = {0x48, 0x48, 0x48, 255};
|
||||
color_t colorComet = {0x5C, 0x5C, 0x84, 255};
|
||||
color_t colorFiord = {0x48, 0x48, 0x70, 255};
|
||||
color_t colorTuna = {0x34, 0x34, 0x48, 255};
|
||||
color_t colorHacienda = {0x94, 0x80, 0x2C, 255};
|
||||
color_t colorKumera = {0x84, 0x70, 0x24, 255};
|
||||
color_t colorHimalaya = {0x74, 0x60, 0x1C, 255};
|
||||
color_t colorChocolate = {0x84, 0x44, 0, 255};
|
||||
color_t colorNutmeg = {0x70, 0x38, 0, 255};
|
||||
color_t colorBracken = {0x48, 0x24, 0, 255};
|
||||
color_t colorTeal = {0, 0x84, 0x84, 255};
|
||||
color_t colorSkobeloff = {0, 0x70, 0x70, 255};
|
||||
color_t colorDeepJungleGreen = {0, 0x48, 0x48, 255};
|
||||
|
||||
color_t colorSkin = { 0xF4, 0x94, 0x4C, 255 };
|
||||
color_t colorDarkSkin = { 0x93, 0x5D, 0x37, 255 };
|
||||
color_t colorAsianSkin = { 0xFF, 0xCC, 0x99, 255 };
|
||||
color_t colorSkin = {0xF4, 0x94, 0x4C, 255};
|
||||
color_t colorDarkSkin = {0x93, 0x5D, 0x37, 255};
|
||||
color_t colorAsianSkin = {0xFF, 0xCC, 0x99, 255};
|
||||
|
||||
color_t colorLightBlue = { 80, 80, 160, 255 };
|
||||
color_t colorLightBlue = {80, 80, 160, 255};
|
||||
color_t colorSelectedBG = {0, 255, 255, 64};
|
||||
|
||||
color_t ColorMult(color_t c, color_t m)
|
||||
{
|
||||
@@ -95,22 +96,22 @@ color_t ColorMult(color_t c, color_t m)
|
||||
}
|
||||
color_t ColorAlphaBlend(color_t a, color_t b)
|
||||
{
|
||||
a.r = (uint8_t)(((int)a.r*(255 - b.a) + (int)b.r*b.a)/255);
|
||||
a.g = (uint8_t)(((int)a.g*(255 - b.a) + (int)b.g*b.a)/255);
|
||||
a.b = (uint8_t)(((int)a.b*(255 - b.a) + (int)b.b*b.a)/255);
|
||||
a.r = (uint8_t)(((int)a.r * (255 - b.a) + (int)b.r * b.a) / 255);
|
||||
a.g = (uint8_t)(((int)a.g * (255 - b.a) + (int)b.g * b.a) / 255);
|
||||
a.b = (uint8_t)(((int)a.b * (255 - b.a) + (int)b.b * b.a) / 255);
|
||||
a.a = 255;
|
||||
return a;
|
||||
}
|
||||
|
||||
HSV tintNone = { -1.0, 1.0, 1.0 };
|
||||
HSV tintRed = { 0.0, 1.0, 1.0 };
|
||||
HSV tintYellow = { 60.0, 1.0, 1.0 };
|
||||
HSV tintGreen = { 120.0, 1.0, 1.0 };
|
||||
HSV tintCyan = { 180.0, 1.0, 1.0 };
|
||||
HSV tintPoison = { 120.0, 0.33, 2.0 };
|
||||
HSV tintGray = { -1.0, 0.0, 1.0 };
|
||||
HSV tintPurple = { 300, 1.0, 1.0 };
|
||||
HSV tintDarker = { -1.0, 1.0, 0.75 };
|
||||
HSV tintNone = {-1.0, 1.0, 1.0};
|
||||
HSV tintRed = {0.0, 1.0, 1.0};
|
||||
HSV tintYellow = {60.0, 1.0, 1.0};
|
||||
HSV tintGreen = {120.0, 1.0, 1.0};
|
||||
HSV tintCyan = {180.0, 1.0, 1.0};
|
||||
HSV tintPoison = {120.0, 0.33, 2.0};
|
||||
HSV tintGray = {-1.0, 0.0, 1.0};
|
||||
HSV tintPurple = {300, 1.0, 1.0};
|
||||
HSV tintDarker = {-1.0, 1.0, 0.75};
|
||||
|
||||
color_t ColorTint(color_t c, HSV hsv)
|
||||
{
|
||||
@@ -147,7 +148,7 @@ color_t ColorTint(color_t c, HSV hsv)
|
||||
q = (uint8_t)CLAMP(vComponent * (1.0 - (hsv.s * ff)), 0, 255);
|
||||
t = (uint8_t)CLAMP(vComponent * (1.0 - (hsv.s * (1.0 - ff))), 0, 255);
|
||||
|
||||
switch(i)
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
out.r = vComponent;
|
||||
@@ -187,9 +188,12 @@ color_t ColorTint(color_t c, HSV hsv)
|
||||
{
|
||||
// Just set saturation and value
|
||||
// Use weighted average to shift components towards grey for saturation
|
||||
out.r = (uint8_t)CLAMP(hsv.v * (vAvg*(1.0-hsv.s) + hsv.s*c.r), 0, 255);
|
||||
out.g = (uint8_t)CLAMP(hsv.v * (vAvg*(1.0-hsv.s) + hsv.s*c.g), 0, 255);
|
||||
out.b = (uint8_t)CLAMP(hsv.v * (vAvg*(1.0-hsv.s) + hsv.s*c.b), 0, 255);
|
||||
out.r = (uint8_t)CLAMP(
|
||||
hsv.v * (vAvg * (1.0 - hsv.s) + hsv.s * c.r), 0, 255);
|
||||
out.g = (uint8_t)CLAMP(
|
||||
hsv.v * (vAvg * (1.0 - hsv.s) + hsv.s * c.g), 0, 255);
|
||||
out.b = (uint8_t)CLAMP(
|
||||
hsv.v * (vAvg * (1.0 - hsv.s) + hsv.s * c.b), 0, 255);
|
||||
}
|
||||
out.a = c.a;
|
||||
return out;
|
||||
@@ -203,7 +207,7 @@ bool HSVEquals(const HSV a, const HSV b)
|
||||
{
|
||||
const double epsilon = 0.000001;
|
||||
return fabs(a.h - b.h) < epsilon && fabs(a.s - b.s) < epsilon &&
|
||||
fabs(a.v - b.v) < epsilon;
|
||||
fabs(a.v - b.v) < epsilon;
|
||||
}
|
||||
|
||||
color_t StrColor(const char *s)
|
||||
|
@@ -2,7 +2,7 @@
|
||||
C-Dogs SDL
|
||||
A port of the legendary (and fun) action/arcade cdogs.
|
||||
|
||||
Copyright (c) 2013-2017 Cong Xu
|
||||
Copyright (c) 2013-2017, 2024 Cong Xu
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -90,6 +90,7 @@ extern color_t colorDarkSkin;
|
||||
extern color_t colorAsianSkin;
|
||||
|
||||
extern color_t colorLightBlue;
|
||||
extern color_t colorSelectedBG;
|
||||
|
||||
color_t ColorMult(color_t c, color_t m);
|
||||
color_t ColorAlphaBlend(color_t a, color_t b);
|
||||
|
@@ -22,7 +22,7 @@
|
||||
This file incorporates work covered by the following copyright and
|
||||
permission notice:
|
||||
|
||||
Copyright (c) 2013-2016, 2018-2022 Cong Xu
|
||||
Copyright (c) 2013-2016, 2018-2022, 2024 Cong Xu
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -68,7 +68,7 @@
|
||||
#include "pics.h"
|
||||
#include "texture.h"
|
||||
|
||||
//#define DEBUG_DRAW_HITBOXES
|
||||
// #define DEBUG_DRAW_HITBOXES
|
||||
|
||||
// Three types of tile drawing, based on line of sight:
|
||||
// Unvisited: black
|
||||
@@ -169,6 +169,9 @@ static void DrawObjectiveHighlights(
|
||||
static void DrawChatters(
|
||||
DrawBuffer *b, const struct vec2i offset, const Tile *t,
|
||||
const struct vec2i pos, const bool useFog);
|
||||
static void DrawPickupMenus(
|
||||
DrawBuffer *b, const struct vec2i offset, const Tile *t,
|
||||
const struct vec2i pos, const bool useFog);
|
||||
static void DrawExtra(
|
||||
DrawBuffer *b, struct vec2i offset, const DrawBufferArgs *args);
|
||||
|
||||
@@ -189,6 +192,8 @@ void DrawBufferDraw(
|
||||
DrawTiles(b, offset, DrawObjectiveHighlights);
|
||||
// Draw actor chatter
|
||||
DrawTiles(b, offset, DrawChatters);
|
||||
// Draw actor pickup menus
|
||||
DrawTiles(b, offset, DrawPickupMenus);
|
||||
}
|
||||
// Draw editor-only things
|
||||
DrawExtra(b, offset, args);
|
||||
@@ -398,6 +403,79 @@ static void DrawChatters(
|
||||
CA_FOREACH_END()
|
||||
}
|
||||
|
||||
static void DrawPickupMenu(
|
||||
DrawBuffer *b, const TActor *a, const struct vec2i offset);
|
||||
static void DrawPickupMenus(
|
||||
DrawBuffer *b, const struct vec2i offset, const Tile *t,
|
||||
const struct vec2i pos, const bool useFog)
|
||||
{
|
||||
UNUSED(pos);
|
||||
CA_FOREACH(ThingId, tid, t->things)
|
||||
// Draw the items that are in LOS
|
||||
if (t->outOfSight || ColorEquals(GetLOSMask(t, useFog), colorTransparent))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const Thing *ti = ThingIdGetThing(tid);
|
||||
if (ti->kind != KIND_CHARACTER)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const TActor *a = CArrayGet(&gActors, ti->id);
|
||||
// Draw pickup menu
|
||||
if (!a->pickupMenu.pickup || !ActorIsLocalPlayer(a->uid))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
DrawPickupMenu(b, a, offset);
|
||||
CA_FOREACH_END()
|
||||
}
|
||||
static void DrawPickupMenu(
|
||||
DrawBuffer *b, const TActor *a, const struct vec2i offset)
|
||||
{
|
||||
// Calculate width and height of menu and draw centered on the actor
|
||||
// TODO: may be expensive to do per-frame; cache somewhere?
|
||||
struct vec2i size = svec2i_zero();
|
||||
struct vec2i ssize = FontStrSize(a->pickupMenu.effect->u.Menu.Text);
|
||||
size.x = MAX(size.x, ssize.x);
|
||||
size.y += ssize.y;
|
||||
size.y += FontH(); // Separator
|
||||
CA_FOREACH(const PickupMenuItem, m, a->pickupMenu.effect->u.Menu.Items)
|
||||
ssize = FontStrSize(m->Text);
|
||||
size.x = MAX(size.x, ssize.x);
|
||||
size.y += ssize.y;
|
||||
CA_FOREACH_END()
|
||||
|
||||
struct vec2i pos = svec2i(
|
||||
(int)a->thing.Pos.x - b->xTop + offset.x - size.x / 2,
|
||||
(int)a->thing.Pos.y - b->yTop + offset.y - size.y / 2);
|
||||
const int startX = pos.x;
|
||||
// Draw box bg with a bit of padding
|
||||
const color_t cbg = {64, 64, 64, 128};
|
||||
DrawRectangle(
|
||||
b->g, svec2i_subtract(pos, svec2i(2, 2)),
|
||||
svec2i_add(size, svec2i(4, 4)), cbg, true);
|
||||
pos = FontStr(a->pickupMenu.effect->u.Menu.Text, pos);
|
||||
pos.x = startX;
|
||||
pos.y += FontH() * 2; // Separator
|
||||
CA_FOREACH(const PickupMenuItem, m, a->pickupMenu.effect->u.Menu.Items)
|
||||
const bool selected = _ca_index == a->pickupMenu.index;
|
||||
if (selected)
|
||||
{
|
||||
// Add 1px padding
|
||||
const struct vec2i bgPos = svec2i_subtract(pos, svec2i_one());
|
||||
const struct vec2i bgSize =
|
||||
svec2i_add(svec2i(size.x, FontH()), svec2i(2, 2));
|
||||
DrawRectangle(b->g, bgPos, bgSize, colorSelectedBG, true);
|
||||
}
|
||||
pos = FontStrMask(m->Text, pos, selected ? colorRed : colorWhite);
|
||||
pos.x = startX;
|
||||
pos.y += FontH();
|
||||
CA_FOREACH_END()
|
||||
}
|
||||
|
||||
static void DrawThing(DrawBuffer *b, const Thing *t, const struct vec2i offset)
|
||||
{
|
||||
const struct vec2i picPos = svec2i_add(
|
||||
@@ -419,7 +497,7 @@ static void DrawThing(DrawBuffer *b, const Thing *t, const struct vec2i offset)
|
||||
}
|
||||
else if (t->kind == KIND_CHARACTER)
|
||||
{
|
||||
TActor *a = CArrayGet(&gActors, t->id);
|
||||
const TActor *a = CArrayGet(&gActors, t->id);
|
||||
ActorPics pics = GetCharacterPicsFromActor(a);
|
||||
DrawActorPics(&pics, picPos, Rect2iZero());
|
||||
// Draw weapon indicators
|
||||
|
@@ -27,9 +27,6 @@
|
||||
*/
|
||||
#include "pickup.h"
|
||||
|
||||
#include "ammo.h"
|
||||
#include "game_events.h"
|
||||
#include "gamedata.h"
|
||||
#include "json_utils.h"
|
||||
#include "map.h"
|
||||
#include "net_util.h"
|
||||
@@ -130,272 +127,6 @@ void PickupsUpdate(CArray *pickups, const int ticks)
|
||||
CA_FOREACH_END()
|
||||
}
|
||||
|
||||
static bool TreatAsGunPickup(const PickupEffect *pe, const TActor *a);
|
||||
static bool TryPickupAmmo(TActor *a, const Pickup *p, const PickupEffect *pe);
|
||||
static bool TryPickupGun(
|
||||
TActor *a, const PickupEffect *pe, const bool pickupAll,
|
||||
const char **sound);
|
||||
void PickupPickup(TActor *a, Pickup *p, const bool pickupAll)
|
||||
{
|
||||
if (p->PickedUp)
|
||||
return;
|
||||
CASSERT(a->PlayerUID >= 0, "NPCs cannot pickup");
|
||||
// can always pickup effect-less pickups
|
||||
bool canPickup = p->class->Effects.size == 0;
|
||||
const char *sound = p->class->Sound;
|
||||
const struct vec2 actorPos = a->thing.Pos;
|
||||
|
||||
CA_FOREACH(const PickupEffect, pe, p->class->Effects)
|
||||
switch (pe->Type)
|
||||
{
|
||||
case PICKUP_JEWEL: {
|
||||
canPickup = true;
|
||||
GameEvent e = GameEventNew(GAME_EVENT_SCORE);
|
||||
e.u.Score.PlayerUID = a->PlayerUID;
|
||||
e.u.Score.Score = pe->u.Score;
|
||||
GameEventsEnqueue(&gGameEvents, e);
|
||||
|
||||
e = GameEventNew(GAME_EVENT_ADD_PARTICLE);
|
||||
e.u.AddParticle.Class =
|
||||
StrParticleClass(&gParticleClasses, "score_text");
|
||||
e.u.AddParticle.ActorUID = a->uid;
|
||||
e.u.AddParticle.Pos = p->thing.Pos;
|
||||
e.u.AddParticle.DZ = 3;
|
||||
if (gCampaign.Setting.Ammo)
|
||||
{
|
||||
sprintf(e.u.AddParticle.Text, "$%d", pe->u.Score);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(e.u.AddParticle.Text, "+%d", pe->u.Score);
|
||||
}
|
||||
GameEventsEnqueue(&gGameEvents, e);
|
||||
|
||||
UpdateMissionObjective(
|
||||
&gMission, p->thing.flags, OBJECTIVE_COLLECT, 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case PICKUP_HEALTH:
|
||||
// Don't pick up unless taken damage
|
||||
if (a->health < ActorGetMaxHeal(a, pe->u.Heal.ExceedMax))
|
||||
{
|
||||
canPickup = true;
|
||||
GameEvent e = GameEventNew(GAME_EVENT_ACTOR_HEAL);
|
||||
e.u.Heal.UID = a->uid;
|
||||
e.u.Heal.PlayerUID = a->PlayerUID;
|
||||
e.u.Heal.Amount = pe->u.Heal.Amount;
|
||||
e.u.Heal.ExceedMax = pe->u.Heal.ExceedMax;
|
||||
e.u.Heal.IsRandomSpawned = p->IsRandomSpawned;
|
||||
GameEventsEnqueue(&gGameEvents, e);
|
||||
}
|
||||
break;
|
||||
|
||||
case PICKUP_AMMO: // fallthrough
|
||||
case PICKUP_GUN:
|
||||
if (TreatAsGunPickup(pe, a))
|
||||
{
|
||||
canPickup = TryPickupGun(a, pe, pickupAll, &sound) || canPickup;
|
||||
}
|
||||
else
|
||||
{
|
||||
canPickup = TryPickupAmmo(a, p, pe) || canPickup;
|
||||
}
|
||||
break;
|
||||
|
||||
case PICKUP_KEYCARD: {
|
||||
canPickup = true;
|
||||
GameEvent e = GameEventNew(GAME_EVENT_ADD_KEYS);
|
||||
e.u.AddKeys.KeyFlags = pe->u.Keys;
|
||||
e.u.AddKeys.Pos = Vec2ToNet(actorPos);
|
||||
GameEventsEnqueue(&gGameEvents, e);
|
||||
if (sound == NULL)
|
||||
{
|
||||
sound = "key";
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PICKUP_SHOW_MAP: {
|
||||
canPickup = true;
|
||||
GameEvent e = GameEventNew(GAME_EVENT_EXPLORE_TILES);
|
||||
e.u.ExploreTiles.Runs_count = 1;
|
||||
e.u.ExploreTiles.Runs[0].Run = gMap.Size.x * gMap.Size.y;
|
||||
GameEventsEnqueue(&gGameEvents, e);
|
||||
}
|
||||
break;
|
||||
|
||||
case PICKUP_LIVES: {
|
||||
canPickup = true;
|
||||
GameEvent e = GameEventNew(GAME_EVENT_PLAYER_ADD_LIVES);
|
||||
e.u.PlayerAddLives.UID = a->PlayerUID;
|
||||
e.u.PlayerAddLives.Lives = pe->u.Lives;
|
||||
GameEventsEnqueue(&gGameEvents, e);
|
||||
}
|
||||
break;
|
||||
|
||||
case PICKUP_SOUND:
|
||||
canPickup = true;
|
||||
sound = pe->u.Sound;
|
||||
break;
|
||||
|
||||
case PICKUP_MENU:
|
||||
// TODO: manual activate menu, set player menu
|
||||
break;
|
||||
|
||||
default:
|
||||
CASSERT(false, "unexpected pickup type");
|
||||
break;
|
||||
}
|
||||
CA_FOREACH_END()
|
||||
|
||||
if (canPickup)
|
||||
{
|
||||
if (sound != NULL)
|
||||
{
|
||||
GameEvent es = GameEventNew(GAME_EVENT_SOUND_AT);
|
||||
strcpy(es.u.SoundAt.Sound, sound);
|
||||
es.u.SoundAt.Pos = Vec2ToNet(actorPos);
|
||||
GameEventsEnqueue(&gGameEvents, es);
|
||||
}
|
||||
GameEvent e = GameEventNew(GAME_EVENT_REMOVE_PICKUP);
|
||||
e.u.RemovePickup.UID = p->UID;
|
||||
e.u.RemovePickup.SpawnerUID = p->SpawnerUID;
|
||||
GameEventsEnqueue(&gGameEvents, e);
|
||||
// Prevent multiple pickups by marking
|
||||
p->PickedUp = true;
|
||||
a->PickupAll = false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool HasGunUsingAmmo(const TActor *a, const int ammoId);
|
||||
static bool TreatAsGunPickup(const PickupEffect *pe, const TActor *a)
|
||||
{
|
||||
// Grenades can also be gun pickups; treat as gun pickup if the player
|
||||
// doesn't have its ammo
|
||||
switch (pe->Type)
|
||||
{
|
||||
case PICKUP_AMMO:
|
||||
if (!HasGunUsingAmmo(a, pe->u.Ammo.Id))
|
||||
{
|
||||
const Ammo *ammo = AmmoGetById(&gAmmo, pe->u.Ammo.Id);
|
||||
if (ammo->DefaultGun)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
case PICKUP_GUN: {
|
||||
const WeaponClass *wc = IdWeaponClass(pe->u.GunId);
|
||||
return wc->Type != GUNTYPE_GRENADE ||
|
||||
!HasGunUsingAmmo(a, wc->u.Normal.AmmoId);
|
||||
}
|
||||
default:
|
||||
CASSERT(false, "unexpected pickup type");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
static bool HasGunUsingAmmo(const TActor *a, const int ammoId)
|
||||
{
|
||||
for (int i = 0; i < MAX_WEAPONS; i++)
|
||||
{
|
||||
const WeaponClass *wc = a->guns[i].Gun;
|
||||
if (wc == NULL)
|
||||
continue;
|
||||
for (int j = 0; j < WeaponClassNumBarrels(wc); j++)
|
||||
{
|
||||
if (WC_BARREL_ATTR(*wc, AmmoId, j) == ammoId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool TryPickupAmmo(TActor *a, const Pickup *p, const PickupEffect *pe)
|
||||
{
|
||||
// Don't pickup if not using ammo
|
||||
if (!gCampaign.Setting.Ammo)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// Don't pickup if ammo full
|
||||
const int ammoId = pe->Type == PICKUP_AMMO
|
||||
? (int)pe->u.Ammo.Id
|
||||
: IdWeaponClass(pe->u.GunId)->u.Normal.AmmoId;
|
||||
const Ammo *ammo = AmmoGetById(&gAmmo, ammoId);
|
||||
const int amount =
|
||||
pe->Type == PICKUP_AMMO ? (int)pe->u.Ammo.Amount : ammo->Amount;
|
||||
const int current = *(int *)CArrayGet(&a->ammo, ammoId);
|
||||
if (ammo->Max > 0 && current >= ammo->Max)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Take ammo
|
||||
GameEvent e = GameEventNew(GAME_EVENT_ACTOR_ADD_AMMO);
|
||||
e.u.AddAmmo.UID = a->uid;
|
||||
e.u.AddAmmo.PlayerUID = a->PlayerUID;
|
||||
e.u.AddAmmo.Ammo.Id = ammoId;
|
||||
e.u.AddAmmo.Ammo.Amount = amount;
|
||||
e.u.AddAmmo.IsRandomSpawned = p->IsRandomSpawned;
|
||||
// Note: receiving end will prevent ammo from exceeding max
|
||||
GameEventsEnqueue(&gGameEvents, e);
|
||||
return true;
|
||||
}
|
||||
static bool TryPickupGun(
|
||||
TActor *a, const PickupEffect *pe, const bool pickupAll,
|
||||
const char **sound)
|
||||
{
|
||||
// Guns can only be picked up manually
|
||||
if (!pickupAll)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// When picking up a gun, the actor always ends up with it equipped, but:
|
||||
// - If the player already has the gun:
|
||||
// - Switch to the same gun and drop the same gun
|
||||
// - If the player doesn't have the gun:
|
||||
// - If the player has an empty slot, pickup the gun into that slot
|
||||
// - If the player doesn't have an empty slot, replace the current gun,
|
||||
// dropping it in the process
|
||||
|
||||
const WeaponClass *wc =
|
||||
pe->Type == PICKUP_GUN
|
||||
? IdWeaponClass(pe->u.GunId)
|
||||
: StrWeaponClass(AmmoGetById(&gAmmo, pe->u.Ammo.Id)->DefaultGun);
|
||||
|
||||
ActorPickupGun(a, wc);
|
||||
|
||||
// If the player has less ammo than the default amount,
|
||||
// replenish up to this amount
|
||||
// TODO: support multi gun
|
||||
const int ammoId = WC_BARREL_ATTR(*wc, AmmoId, 0);
|
||||
if (ammoId >= 0)
|
||||
{
|
||||
const Ammo *ammo = AmmoGetById(&gAmmo, ammoId);
|
||||
const int ammoDeficit = ammo->Amount * AMMO_STARTING_MULTIPLE -
|
||||
*(int *)CArrayGet(&a->ammo, ammoId);
|
||||
if (ammoDeficit > 0)
|
||||
{
|
||||
GameEvent e = GameEventNew(GAME_EVENT_ACTOR_ADD_AMMO);
|
||||
e.u.AddAmmo.UID = a->uid;
|
||||
e.u.AddAmmo.PlayerUID = a->PlayerUID;
|
||||
e.u.AddAmmo.Ammo.Id = ammoId;
|
||||
e.u.AddAmmo.Ammo.Amount = ammoDeficit;
|
||||
e.u.AddAmmo.IsRandomSpawned = false;
|
||||
GameEventsEnqueue(&gGameEvents, e);
|
||||
|
||||
// Also play an ammo pickup sound
|
||||
*sound = ammo->Sound;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PickupIsManual(const Pickup *p)
|
||||
{
|
||||
if (p->PickedUp)
|
||||
@@ -413,6 +144,8 @@ bool PickupIsManual(const Pickup *p)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PICKUP_MENU:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@@ -1,33 +1,32 @@
|
||||
/*
|
||||
C-Dogs SDL
|
||||
A port of the legendary (and fun) action/arcade cdogs.
|
||||
Copyright (c) 2014-2015, Cong Xu
|
||||
All rights reserved.
|
||||
C-Dogs SDL
|
||||
A port of the legendary (and fun) action/arcade cdogs.
|
||||
Copyright (c) 2014-2015, Cong Xu
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "actors.h"
|
||||
#include "pic.h"
|
||||
#include "pickup_class.h"
|
||||
#include "tile.h"
|
||||
@@ -46,7 +45,7 @@ typedef struct
|
||||
int SpawnerUID;
|
||||
} Pickup;
|
||||
|
||||
extern CArray gPickups; // of Pickup
|
||||
extern CArray gPickups; // of Pickup
|
||||
|
||||
void PickupsInit(void);
|
||||
void PickupsTerminate(void);
|
||||
@@ -57,7 +56,6 @@ void PickupDestroy(const int uid);
|
||||
|
||||
void PickupsUpdate(CArray *pickups, const int ticks);
|
||||
|
||||
void PickupPickup(TActor *a, Pickup *p, const bool pickupAll);
|
||||
// Check if the pickup needs to be picked up manually
|
||||
bool PickupIsManual(const Pickup *p);
|
||||
|
||||
|
@@ -563,3 +563,30 @@ int PickupClassGetKeys(const PickupClass *p)
|
||||
CA_FOREACH_END()
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *PickupClassGetName(const PickupClass *p)
|
||||
{
|
||||
const char *pickupName = NULL;
|
||||
bool hasMultipleEffects = false;
|
||||
CA_FOREACH(const PickupEffect, pe, p->Effects)
|
||||
switch (pe->Type)
|
||||
{
|
||||
case PICKUP_AMMO:
|
||||
hasMultipleEffects = pickupName != NULL;
|
||||
pickupName = AmmoGetById(&gAmmo, pe->u.Ammo.Id)->Name;
|
||||
break;
|
||||
case PICKUP_GUN:
|
||||
hasMultipleEffects = pickupName != NULL;
|
||||
pickupName = IdWeaponClass(pe->u.GunId)->name;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
CA_FOREACH_END()
|
||||
if (pickupName == NULL || hasMultipleEffects)
|
||||
{
|
||||
// fallback to using the raw class name
|
||||
pickupName = p->Name;
|
||||
}
|
||||
return pickupName;
|
||||
}
|
||||
|
@@ -124,6 +124,7 @@ PickupClass *IntScorePickupClass(const int i);
|
||||
bool PickupClassHasAmmoEffect(const PickupClass *p);
|
||||
bool PickupClassHasKeyEffect(const PickupClass *p);
|
||||
int PickupClassGetKeys(const PickupClass *p);
|
||||
const char *PickupClassGetName(const PickupClass *p);
|
||||
|
||||
// Score for picking up an objective
|
||||
#define PICKUP_SCORE 10
|
||||
|
@@ -120,12 +120,11 @@ static void DrawEquipSlot(
|
||||
}
|
||||
else
|
||||
{
|
||||
const color_t bg = {0, 255, 255, 64};
|
||||
// Add 1px padding
|
||||
const struct vec2i bgPos = svec2i_subtract(pos, svec2i_one());
|
||||
const struct vec2i bgSize =
|
||||
svec2i(EQUIP_MENU_WIDTH / 2 + 2, h + 2);
|
||||
DrawRectangle(g, bgPos, bgSize, bg, true);
|
||||
DrawRectangle(g, bgPos, bgSize, colorSelectedBG, true);
|
||||
|
||||
color = colorRed;
|
||||
}
|
||||
|
@@ -468,11 +468,10 @@ struct vec2i DisplayMenuItem(
|
||||
}
|
||||
if (selected)
|
||||
{
|
||||
const color_t bg = {0, 255, 255, 64};
|
||||
// Add 1px padding
|
||||
const struct vec2i bgPos = svec2i_subtract(bounds.Pos, svec2i_one());
|
||||
const struct vec2i bgSize = svec2i_add(bounds.Size, svec2i(2, 2));
|
||||
DrawRectangle(g, bgPos, bgSize, bg, true);
|
||||
DrawRectangle(g, bgPos, bgSize, colorSelectedBG, true);
|
||||
return FontStrMask(s, bounds.Pos, colorRed);
|
||||
}
|
||||
if (!ColorEquals(color, colorTransparent))
|
||||
|
@@ -249,8 +249,7 @@ static void DrawUtilMenuItem(
|
||||
color_t color = colorWhite;
|
||||
if (selected && data->Active)
|
||||
{
|
||||
const color_t cbg = {0, 255, 255, 64};
|
||||
DrawRectangle(g, bgPos, bgSize, cbg, true);
|
||||
DrawRectangle(g, bgPos, bgSize, colorSelectedBG, true);
|
||||
color = colorRed;
|
||||
}
|
||||
|
||||
|
@@ -350,8 +350,7 @@ static void DrawGun(
|
||||
const color_t mask = color;
|
||||
if (selected && data->Active)
|
||||
{
|
||||
const color_t cbg = {0, 255, 255, 64};
|
||||
DrawRectangle(g, bgPos, bgSize, cbg, true);
|
||||
DrawRectangle(g, bgPos, bgSize, colorSelectedBG, true);
|
||||
color = colorRed;
|
||||
}
|
||||
else if (equipped)
|
||||
|
Reference in New Issue
Block a user