// 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 // 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