547 lines
13 KiB
C
547 lines
13 KiB
C
// Copyright © 2016-2017 Graham Sanderson, all rights reserved.
|
|
#include "lith_common.h"
|
|
#include "lith_player.h"
|
|
#include "lith_list.h"
|
|
#include "lith_hudid.h"
|
|
#include "lith_world.h"
|
|
|
|
#include <limits.h>
|
|
|
|
// Extern Objects ------------------------------------------------------------|
|
|
|
|
noinit struct player players[MAX_PLAYERS];
|
|
|
|
// Static Objects ------------------------------------------------------------|
|
|
|
|
static struct {__str on, off;} Lith_GUISounds[GUI_MAX] = {
|
|
{},
|
|
{"player/cbi/open", "player/cbi/close"},
|
|
};
|
|
|
|
// Static Functions ----------------------------------------------------------|
|
|
|
|
static void Lith_PlayerRunScripts(struct player *p);
|
|
script static void Lith_BossWarning(struct player *p);
|
|
|
|
// Scripts -------------------------------------------------------------------|
|
|
|
|
//
|
|
// Lith_PlayerEntry
|
|
//
|
|
script type("enter") static void Lith_PlayerEntry(void)
|
|
{
|
|
if(ACS_GameType() == GAME_TITLE_MAP)
|
|
return;
|
|
|
|
struct player *p = LocalPlayer;
|
|
|
|
reinit:
|
|
while(!mapinit) ACS_Delay(1);
|
|
|
|
p->reset();
|
|
Lith_PlayerLogEntry(p);
|
|
Lith_PlayerEnterUpgrades(p);
|
|
p->loadData();
|
|
|
|
Lith_BossWarning(p);
|
|
|
|
while(p->active)
|
|
{
|
|
if(p->reinit)
|
|
goto reinit;
|
|
|
|
Lith_PlayerUpdateData(p);
|
|
|
|
// Check for resurrect.
|
|
if(p->health > 0 && p->dead)
|
|
p->reinit = true;
|
|
|
|
// These can be changed any time, so save them here.
|
|
struct player_delta olddelta = p->cur;
|
|
int oldhealth = p->health;
|
|
|
|
// Run logic and rendering
|
|
Lith_PlayerRunScripts(p);
|
|
|
|
// Update view
|
|
ACS_SetActorPitch(0, ACS_GetActorPitch(0) - p->addpitch);
|
|
ACS_SetActorAngle(0, ACS_GetActorAngle(0) - p->addyaw);
|
|
ACS_SetActorRoll (0, ACS_GetActorRoll (0) - p->addroll);
|
|
|
|
// Tic passes
|
|
ACS_Delay(1);
|
|
|
|
if(p->dlgnum)
|
|
{
|
|
script extern void Lith_DialogueVM(struct player *p, int dlgnum);
|
|
|
|
Lith_DialogueVM(p, p->dlgnum);
|
|
p->dlgnum = 0;
|
|
}
|
|
|
|
// Update previous-tic values
|
|
p->old = olddelta;
|
|
p->oldhealth = oldhealth;
|
|
|
|
// Reset view for next tic
|
|
ACS_SetActorPitch(0, ACS_GetActorPitch(0) + p->addpitch);
|
|
ACS_SetActorAngle(0, ACS_GetActorAngle(0) + p->addyaw);
|
|
ACS_SetActorRoll (0, ACS_GetActorRoll (0) + p->addroll);
|
|
|
|
// If the map changes this we need to make sure it's still correct.
|
|
p->validateTID();
|
|
|
|
p->ticks++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Lith_PlayerDeath
|
|
//
|
|
script type("death") static void Lith_PlayerDeath(void)
|
|
{
|
|
struct player *p = LocalPlayer;
|
|
|
|
p->dead = true;
|
|
|
|
Lith_PlayerDeinitUpgrades(p);
|
|
|
|
if(world.singleplayer || ACS_GetCVar("sv_cooploseinventory"))
|
|
{
|
|
Lith_PlayerDeallocUpgrades(p);
|
|
p->bip.deallocate();
|
|
p->score = p->scoreaccum = p->scoreaccumtime = 0;
|
|
}
|
|
|
|
if(world.singleplayer)
|
|
{
|
|
if(ACS_GetCVar("lith_sv_revenge"))
|
|
{
|
|
ACS_LocalAmbientSound("player/death1", 127);
|
|
ACS_Delay(35);
|
|
InvGive("Lith_PlayerDeath", 1);
|
|
ACS_Delay(25);
|
|
InvGive("Lith_PlayerDeathNuke", 1);
|
|
ACS_Delay(25);
|
|
}
|
|
|
|
while(p->dead)
|
|
{
|
|
ACS_Delay(35 * 5);
|
|
Log("%S", Language("LITH_DEATHMSG_%.2i", ACS_Random(1, 20)));
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Lith_PlayerRespawn
|
|
//
|
|
script type("respawn") static void Lith_PlayerRespawn(void)
|
|
{
|
|
LocalPlayer->reinit = true;
|
|
}
|
|
|
|
//
|
|
// Lith_PlayerReturn
|
|
//
|
|
script type("return") static void Lith_PlayerReturn(void)
|
|
{
|
|
LocalPlayer->reinit = true;
|
|
}
|
|
|
|
//
|
|
// Lith_PlayerDisconnect
|
|
//
|
|
script type("disconnect") static void Lith_PlayerDisconnect(void)
|
|
{
|
|
struct player *p = LocalPlayer;
|
|
|
|
p->bip.deallocate();
|
|
|
|
p->loginfo.hud.free();
|
|
p->hudstrlist.free(true);
|
|
p->loginfo.full.free(true);
|
|
p->loginfo.maps.free(true);
|
|
|
|
upgrademap_t_dtor(&p->upgrademap);
|
|
|
|
memset(p, 0, sizeof(*p));
|
|
}
|
|
|
|
// Extern Functions ----------------------------------------------------------|
|
|
|
|
//
|
|
// Lith_RecoilUp
|
|
//
|
|
script acs void Lith_RecoilUp(fixed amount)
|
|
{
|
|
withplayer(LocalPlayer) p->extrpitch += amount / 180;
|
|
}
|
|
|
|
#define upgrademap_t_GetKey(o) ((o)->info->key)
|
|
#define upgrademap_t_GetNext(o) (&(o)->next)
|
|
#define upgrademap_t_GetPrev(o) (&(o)->prev)
|
|
#define upgrademap_t_HashKey(k) (k)
|
|
#define upgrademap_t_HashObj(o) ((o)->info->key)
|
|
#define upgrademap_t_KeyCmp(l, r) ((l) - (r))
|
|
GDCC_HashMap_Defn(upgrademap_t, int, upgrade_t)
|
|
|
|
//
|
|
// Lith_PlayerGetNamedUpgrade
|
|
//
|
|
upgrade_t *Lith_PlayerGetNamedUpgrade(struct player *p, int name)
|
|
{
|
|
upgrade_t *upgr = p->upgrademap.find(name);
|
|
if(!upgr) Log("null pointer trying to find upgrade %i", name);
|
|
return upgr;
|
|
}
|
|
|
|
//
|
|
// Lith_PlayerGetUpgradeActive
|
|
//
|
|
bool Lith_PlayerGetUpgradeActive(struct player *p, int name)
|
|
{
|
|
ifauto(upgrade_t *, upgr, p->upgrademap.find(name)) return upgr->active;
|
|
else return false;
|
|
}
|
|
|
|
//
|
|
// Lith_GetPlayersExtern
|
|
//
|
|
struct player (*Lith_GetPlayersExtern(void))[MAX_PLAYERS]
|
|
{
|
|
return &players;
|
|
}
|
|
|
|
//
|
|
// Lith_PlayerDiscriminator
|
|
//
|
|
stkcall __str Lith_PlayerDiscriminator(int pclass)
|
|
{
|
|
switch(pclass) {
|
|
case pcl_marine: return "Stan";
|
|
case pcl_cybermage: return "Jem";
|
|
case pcl_informant: return "Fulk";
|
|
case pcl_wanderer: return "Luke";
|
|
case pcl_assassin: return "Omi";
|
|
case pcl_darklord: return "Ari";
|
|
case pcl_thoth: return "Kiri";
|
|
default: return "Mod";
|
|
}
|
|
}
|
|
|
|
//
|
|
// Lith_StepSpeed
|
|
//
|
|
script acs int Lith_StepSpeed()
|
|
{
|
|
struct player *p = LocalPlayer;
|
|
|
|
fixed vel = ACS_VectorLength(absk(p->velx), absk(p->vely));
|
|
fixed num = 1k - (vel / 24k);
|
|
fixed mul = minmax(num, 0.35k, 1k);
|
|
|
|
return 6 * (mul + 0.6k);
|
|
}
|
|
|
|
//
|
|
// Lith_GetPlayer
|
|
//
|
|
struct player *Lith_GetPlayer(int tid, int ptr)
|
|
{
|
|
int pnum = Lith_GetPlayerNumber(tid, ptr);
|
|
if(pnum >= 0) return &players[pnum];
|
|
else return null;
|
|
}
|
|
|
|
//
|
|
// Lith_PlayerCloseGUI
|
|
//
|
|
stkcall void Lith_PlayerCloseGUI(struct player *p)
|
|
{
|
|
if(p->activegui != GUI_NONE)
|
|
{
|
|
p->frozen--;
|
|
ACS_LocalAmbientSound(Lith_GUISounds[p->activegui].off, 127);
|
|
p->activegui = GUI_NONE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Lith_PlayerUseGUI
|
|
//
|
|
stkcall void Lith_PlayerUseGUI(struct player *p, int type)
|
|
{
|
|
if(p->dead) return;
|
|
if(p->activegui == GUI_NONE)
|
|
{
|
|
p->frozen++;
|
|
ACS_LocalAmbientSound(Lith_GUISounds[type].on, 127);
|
|
p->activegui = type;
|
|
}
|
|
else if(p->activegui == type)
|
|
p->closeGUI();
|
|
else
|
|
{
|
|
ACS_LocalAmbientSound(Lith_GUISounds[p->activegui].off, 127);
|
|
ACS_LocalAmbientSound(Lith_GUISounds[type].on, 127);
|
|
p->activegui = type;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Lith_GiveScore
|
|
//
|
|
i96 Lith_GiveScore(struct player *p, i96 score, bool nomul)
|
|
{
|
|
#pragma GDCC FIXED_LITERAL OFF
|
|
// Could cause division by zero
|
|
if(score == 0)
|
|
return 0;
|
|
|
|
if(!nomul) {
|
|
score *= p->scoremul;
|
|
score *= 1 + (double)RandomFloat(0, p->attr.attrs[at_luk] / 77.7);
|
|
score *= world.scoremul;
|
|
}
|
|
|
|
// Get a multiplier for the score accumulator and sound volume
|
|
double mul = minmax(score, 0, 15000) / 15000.0;
|
|
mul = minmax(mul, 0.1, 1.0);
|
|
double vol = 0.7 * mul;
|
|
|
|
// Play a sound when we pick up score
|
|
if(!IsSmallNumber(vol) && p->getCVarI("lith_player_scoresound"))
|
|
ACS_PlaySound(p->cameratid, "player/score", CHAN_ITEM, vol, false, ATTN_STATIC);
|
|
|
|
//
|
|
if(p->getUpgrActive(UPGR_CyberLegs) && ACS_Random(0, 10000) == 0) {
|
|
p->brouzouf += score;
|
|
p->log("> You gained brouzouf.");
|
|
}
|
|
|
|
if(p->getUpgrActive(UPGR_TorgueMode) && ACS_Random(0, 10) == 0) {
|
|
p->spuriousexplosions++;
|
|
ACS_SpawnForced("Lith_EXPLOOOSION", p->x, p->y, p->z);
|
|
}
|
|
|
|
// Add score and set score accumulator
|
|
p->score += score;
|
|
p->scoresum += score;
|
|
p->scoreaccum += score;
|
|
p->scoreaccumtime += 20 * (mul * 2.0);
|
|
|
|
// Log score
|
|
if(p->getCVarI("lith_player_scorelog"))
|
|
p->logH("> +\Cj%lli\Cnscr", score);
|
|
|
|
return score;
|
|
}
|
|
|
|
//
|
|
// Lith_TakeScore
|
|
//
|
|
stkcall void Lith_TakeScore(struct player *p, i96 score)
|
|
{
|
|
if(p->score - score >= 0) {
|
|
p->scoreused += score;
|
|
p->score -= score;
|
|
} else {
|
|
p->scoreused += p->score;
|
|
p->score = 0;
|
|
}
|
|
|
|
p->scoreaccum = 0;
|
|
p->scoreaccumtime = 0;
|
|
}
|
|
|
|
//
|
|
// Lith_GiveMeAllOfTheScore
|
|
//
|
|
script acs void Lith_GiveMeAllOfTheScore(void)
|
|
{
|
|
withplayer(LocalPlayer) p->giveScore(0x7FFFFFFFFFFFFFFFFFFFFFFFLL, true);
|
|
}
|
|
|
|
//
|
|
// Lith_GiveHealthBonus
|
|
//
|
|
script acs void Lith_GiveHealthBonus(int amount)
|
|
{
|
|
withplayer(LocalPlayer)
|
|
{
|
|
amount += p->health;
|
|
if(amount > p->maxhealth + 100) amount = p->maxhealth + 100;
|
|
p->health = amount;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Lith_GiveHealth
|
|
//
|
|
script acs void Lith_GiveHealth(int amount)
|
|
{
|
|
withplayer(LocalPlayer)
|
|
{
|
|
amount += p->health;
|
|
amount *= 1 + p->attr.attrs[at_vit] / 80.0;
|
|
if(amount > p->maxhealth) amount = p->maxhealth;
|
|
p->health = amount;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Lith_CheckHealth
|
|
//
|
|
script acs bool Lith_CheckHealth()
|
|
{
|
|
withplayer(LocalPlayer) return p->health < p->maxhealth;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Lith_Discount
|
|
//
|
|
script acs void Lith_Discount()
|
|
{
|
|
withplayer(LocalPlayer) p->discount = 0.9;
|
|
}
|
|
|
|
// Static Functions ----------------------------------------------------------|
|
|
|
|
//
|
|
// Lith_BossWarning
|
|
//
|
|
script static void Lith_BossWarning(struct player *p)
|
|
{
|
|
ACS_Delay(35 * 5);
|
|
|
|
if(world.bossspawned)
|
|
p->log("%S", Language("LITH_TXT_LOG_BossWarn%S", p->discrim));
|
|
}
|
|
|
|
//
|
|
// Lith_PlayerRunScripts
|
|
//
|
|
// Run main loop script.
|
|
//
|
|
static void Lith_PlayerRunScripts(struct player *p)
|
|
{
|
|
script extern void Lith_PlayerPreWeapons(struct player *p);
|
|
script static void Lith_PlayerPreScore(struct player *p);
|
|
script static void Lith_PlayerPreStats(struct player *p);
|
|
|
|
script extern void Lith_PlayerUpdateCBIGUI(struct player *p);
|
|
script static void Lith_PlayerUpdateAttributes(struct player *p);
|
|
script extern void Lith_PlayerUpdateUpgrades(struct player *p);
|
|
script extern void Lith_PlayerUpdateWeapons(struct player *p);
|
|
script extern void Lith_PlayerUpdateLog(struct player *p);
|
|
|
|
script extern void Lith_PlayerFootstep(struct player *p);
|
|
script extern void Lith_PlayerItemFx(struct player *p);
|
|
script extern void Lith_PlayerDamageBob(struct player *p);
|
|
script extern void Lith_PlayerView(struct player *p);
|
|
script extern void Lith_PlayerRenderUpgrades(struct player *p);
|
|
script extern void Lith_PlayerHUD(struct player *p);
|
|
script extern void Lith_PlayerStyle(struct player *p);
|
|
script extern void Lith_PlayerLevelup(struct player *p);
|
|
script extern void Lith_PlayerDebugStats(struct player *p);
|
|
|
|
// Pre-logic: Update data from the engine.
|
|
Lith_PlayerPreWeapons(p); // Update weapon info
|
|
Lith_PlayerPreScore(p); // Update score
|
|
|
|
if(ACS_Timer() > 4)
|
|
Lith_PlayerPreStats(p); // Update statistics
|
|
|
|
if(!p->dead)
|
|
Lith_PlayerUpdateUpgrades(p);
|
|
|
|
Lith_PlayerRenderUpgrades(p);
|
|
|
|
if(!p->dead)
|
|
{
|
|
// Logic: Update our data.
|
|
switch(p->activegui)
|
|
case GUI_CBI: Lith_PlayerUpdateCBIGUI(p);
|
|
|
|
Lith_PlayerUpdateAttributes(p);
|
|
Lith_PlayerUpdateWeapons(p);
|
|
Lith_PlayerUpdateLog(p);
|
|
|
|
// Post-logic: Update the engine's data.
|
|
Lith_PlayerUpdateStats(p); // Update engine info
|
|
}
|
|
|
|
// Rendering
|
|
Lith_PlayerFootstep(p);
|
|
Lith_PlayerItemFx(p);
|
|
Lith_PlayerDamageBob(p);
|
|
Lith_PlayerView(p);
|
|
Lith_PlayerHUD(p);
|
|
Lith_PlayerStyle(p);
|
|
Lith_PlayerLevelup(p);
|
|
Lith_PlayerDebugStats(p);
|
|
}
|
|
|
|
//
|
|
// Lith_PlayerUpdateAttributes
|
|
//
|
|
script static void Lith_PlayerUpdateAttributes(struct player *p)
|
|
{
|
|
fixed acc = p->attr.attrs[at_acc] / 210.0;
|
|
fixed def = p->attr.attrs[at_def] / 290.0;
|
|
int str = p->attr.attrs[at_str];
|
|
int stm = p->attr.attrs[at_stm];
|
|
int stmt = (ATTR_MAX*1.25 - stm) / (fixed)(ATTR_MAX / 4) * 15;
|
|
int rge = p->attr.attrs[at_rge];
|
|
|
|
if(p->health < p->oldhealth)
|
|
p->rage += rge * (p->oldhealth - p->health) / 1000.0;
|
|
|
|
p->maxhealth = p->spawnhealth + str;
|
|
ACS_SetActorPropertyFixed(0, APROP_DamageMultiplier, 1.0 + acc + p->rage);
|
|
ACS_SetActorPropertyFixed(0, APROP_DamageFactor, p->spawndfactor - def);
|
|
ACS_SetActorProperty (0, APROP_SpawnHealth, p->maxhealth);
|
|
|
|
if(p->health < stm+1 && (!stmt || p->ticks % stmt == 0))
|
|
p->health = p->health + 1;
|
|
|
|
p->rage = lerpk(p->rage, 0, 0.02);
|
|
}
|
|
|
|
//
|
|
// Lith_PlayerPreScore
|
|
//
|
|
script static void Lith_PlayerPreScore(struct player *p)
|
|
{
|
|
if(!p->scoreaccumtime || p->score < p->old.score)
|
|
{
|
|
p->scoreaccum = 0;
|
|
p->scoreaccumtime = 0;
|
|
}
|
|
|
|
if(p->scoreaccumtime > 0)
|
|
p->scoreaccumtime--;
|
|
else if(p->scoreaccumtime < 0)
|
|
p->scoreaccumtime++;
|
|
}
|
|
|
|
//
|
|
// Lith_PlayerPreStats
|
|
//
|
|
script static void Lith_PlayerPreStats(struct player *p)
|
|
{
|
|
if(p->health < p->oldhealth)
|
|
p->healthused += p->oldhealth - p->health;
|
|
else if(p->health > p->oldhealth && ACS_Timer() != 1)
|
|
p->healthsum += p->health - p->oldhealth;
|
|
|
|
if(p->x != p->old.x) p->unitstravelled += abs(p->x - p->old.x);
|
|
if(p->y != p->old.y) p->unitstravelled += abs(p->y - p->old.y);
|
|
if(p->z != p->old.z) p->unitstravelled += abs(p->z - p->old.z);
|
|
}
|
|
|
|
// EOF
|
|
|