diff --git a/src/app/abilities/alien/latch.ts b/src/app/abilities/alien/latch.ts index 0c1f1efc..c8e0908e 100644 --- a/src/app/abilities/alien/latch.ts +++ b/src/app/abilities/alien/latch.ts @@ -12,6 +12,7 @@ import { ZONE_TYPE } from "app/world/zone-id"; import { FilterIsAlive } from "resources/filters"; import { ChatHook } from "app/chat/chat-module"; import { PLAYER_COLOR } from "lib/translators"; +import { GENERIC_CHAT_SOUND_REF } from "app/force/force-type"; export class LatchAbility implements Ability { @@ -112,6 +113,7 @@ export class LatchAbility implements Ability { if (crew) { hook.name = crew.name; hook.color = PLAYER_COLOR[crew.unit.owner.id]; + hook.sound = GENERIC_CHAT_SOUND_REF; } return hook; @@ -176,42 +178,36 @@ export class LatchAbility implements Ability { const newOrder = GetIssuedOrderId(); this.forceStop = true; - try { - - // Our survival instincts order - if (newOrder === 852252) { - this.isCastingSurvivalInstincts = true; - - this.targetUnit.addAbility(FourCC("Agho")); - // Pick all players and cause hostility - const group = CreateGroup(); - - GroupEnumUnitsInRange( - group, - this.targetUnit.x, - this.targetUnit.y, - 1200, - FilterIsAlive(this.targetUnit.owner) + // Our survival instincts order + if (newOrder === 852252) { + this.isCastingSurvivalInstincts = true; + + this.targetUnit.addAbility(FourCC("Agho")); + // Pick all players and cause hostility + const group = CreateGroup(); + + GroupEnumUnitsInRange( + group, + this.targetUnit.x, + this.targetUnit.y, + 1200, + FilterIsAlive(this.targetUnit.owner) + ); + + ForGroup(group, () => { + module.game.forceModule.aggressionBetweenTwoPlayers( + this.targetUnit.owner, + MapPlayer.fromHandle(GetOwningPlayer(GetEnumUnit())) ); - - ForGroup(group, () => { - module.game.forceModule.aggressionBetweenTwoPlayers( - this.targetUnit.owner, - MapPlayer.fromHandle(GetOwningPlayer(GetEnumUnit())) - ); - }); - - DestroyGroup(group); - - const t = new Timer(); - t.start(2, false, () => { - this.targetUnit.removeAbility(FourCC("Agho")); - t.destroy(); - }); - } - } - catch (e) { - Log.Error(e); + }); + + DestroyGroup(group); + + const t = new Timer(); + t.start(2, false, () => { + this.targetUnit.removeAbility(FourCC("Agho")); + t.destroy(); + }); } } diff --git a/src/app/chat/chat-module.ts b/src/app/chat/chat-module.ts index e2ceb3bf..52284f69 100644 --- a/src/app/chat/chat-module.ts +++ b/src/app/chat/chat-module.ts @@ -13,7 +13,8 @@ export interface ChatHook { recipients: MapPlayer[], name: string, color: string, - message: string + message: string, + sound: SoundWithCooldown | undefined } export enum PRIVS { @@ -251,24 +252,24 @@ export class ChatModule { const messageString = force.getChatMessage(player, message); - const postHookData = this.applyChatHooks(player, playername, recipients, color, message); + const postHookData = this.applyChatHooks(player, playername, recipients, color, message, sound); // Handle listen mode this.usersInListen.forEach(u => { if (recipients.indexOf(u) === -1) recipients.push(u); }); - this.postMessageFor(postHookData.recipients, postHookData.name, postHookData.color, postHookData.message, messageTag, sound); + this.postMessageFor(postHookData.recipients, postHookData.name, postHookData.color, postHookData.message, messageTag, postHookData.sound); } } } - private applyChatHooks(player: MapPlayer, playerName: string, recipients: MapPlayer[], color: string, message: string) { + private applyChatHooks(player: MapPlayer, playerName: string, recipients: MapPlayer[], color: string, message: string, sound?: SoundWithCooldown) { let idx = 0; let data: ChatHook = { who: player, name: playerName, - recipients, color, message + recipients, color, message, sound }; while (idx < this.onChatHooks.length) { diff --git a/src/app/force/alien-force.ts b/src/app/force/alien-force.ts index ebfed4f5..4593f11c 100644 --- a/src/app/force/alien-force.ts +++ b/src/app/force/alien-force.ts @@ -4,7 +4,7 @@ import { Log } from "../../lib/serilog/serilog"; import { ForceModule } from "./force-module"; import { ForceType } from "./force-type"; import { Vector2, vectorFromUnit } from "app/types/vector2"; -import { ABIL_CREWMEMBER_INFO, ABIL_TRANSFORM_HUMAN_ALIEN, ABIL_TRANSFORM_ALIEN_HUMAN, TECH_MAJOR_HEALTHCARE, TECH_ROACH_DUMMY_UPGRADE, ABIL_ALIEN_EVOLVE_T1, ABIL_ALIEN_EVOLVE_T2, TECH_PLAYER_INFESTS } from "resources/ability-ids"; +import { ABIL_CREWMEMBER_INFO, ABIL_TRANSFORM_HUMAN_ALIEN, ABIL_TRANSFORM_ALIEN_HUMAN, TECH_MAJOR_HEALTHCARE, TECH_ROACH_DUMMY_UPGRADE, ABIL_ALIEN_EVOLVE_T1, ABIL_ALIEN_EVOLVE_T2, TECH_PLAYER_INFESTS, ABIL_ALIEN_EVOLVE_T3 } from "resources/ability-ids"; import { Crewmember } from "app/crewmember/crewmember-type"; import { alienTooltipToAlien, alienTooltipToHuman } from "resources/ability-tooltips"; import { VISION_TYPE } from "app/world/vision-type"; @@ -559,14 +559,31 @@ export class AlienForce extends ForceType { const unit = this.playerAlienUnits.get(player); if (unit) { const unitIsSelected = unit.isSelected(player); - const replacedUnit = ReplaceUnitBJ(unit.handle, newForm, 1); + + + // Get old unit zone + const oldZone = this.forceModule.game.worldModule.getUnitZone(unit); + + // Remove the old unit from the zone + if (oldZone) { + this.forceModule.game.worldModule.removeUnit(unit); + } + ReplaceUnitBJ(unit.handle, newForm, 1); + + const replacedUnit = GetLastReplacedUnitBJ(); const alien = Unit.fromHandle(replacedUnit); + // And handle travel + if (oldZone) { + this.forceModule.game.worldModule.handleTravel(alien, oldZone.id); + } + if (unitIsSelected) { SelectUnitForPlayerSingle(alien.handle, player.handle); } this.playerAlienUnits.set(player, alien); alien.nameProper = 'Alien Host'; + alien.color = PLAYER_COLOR_BROWN; // Now we need to also set alien spawn penalties if (player !== alienHost) { @@ -577,6 +594,7 @@ export class AlienForce extends ForceType { alien.setScale(0.8, 0.8, 0.8); alien.removeAbility(ABIL_ALIEN_EVOLVE_T1); alien.removeAbility(ABIL_ALIEN_EVOLVE_T2); + alien.removeAbility(ABIL_ALIEN_EVOLVE_T3); alien.nameProper = 'Alien Spawn'; } // If a player isn't transformed force the transformation diff --git a/src/app/force/crewmember-force.ts b/src/app/force/crewmember-force.ts index 87f1e5df..b2460094 100644 --- a/src/app/force/crewmember-force.ts +++ b/src/app/force/crewmember-force.ts @@ -79,6 +79,9 @@ export class CrewmemberForce extends ForceType { const obsForce = this.forceModule.getForce(OBSERVER_FORCE_NAME); obsForce.addPlayerMainUnit(game, whichUnit, player); this.forceModule.addPlayerToForce(player, OBSERVER_FORCE_NAME); + + // Also remove their unit from the zone + this.forceModule.game.worldModule.removeUnit(whichUnit.unit); } this.removePlayer(player); diff --git a/src/app/force/force-module.ts b/src/app/force/force-module.ts index 450d47ea..9ae9b22f 100644 --- a/src/app/force/force-module.ts +++ b/src/app/force/force-module.ts @@ -5,7 +5,7 @@ import { ForceType } from "./force-type"; import { CrewmemberForce, CREW_FORCE_NAME } from "./crewmember-force"; import { AlienForce, ALIEN_FORCE_NAME } from "./alien-force"; import { ObserverForce } from "./observer-force"; -import { Trigger, MapPlayer, Timer } from "w3ts"; +import { Trigger, MapPlayer, Timer, Unit } from "w3ts"; import { COL_VENTS, COL_GOOD, COL_BAD } from "resources/colours"; import { OptSelection, OPT_TYPES, OptSelectOption, OptResult } from "./opt-selection"; import { STR_OPT_CULT, STR_OPT_ALIEN, STR_OPT_HUMAN } from "resources/strings"; @@ -450,6 +450,8 @@ export class ForceModule { ForGroup(allUnits, () => { const u = GetEnumUnit(); + // Also remove their unit from the zone + this.game.worldModule.removeUnit(Unit.fromHandle(u)); KillUnit(u); }); diff --git a/src/app/force/force-type.ts b/src/app/force/force-type.ts index 7239df1c..0710ac73 100644 --- a/src/app/force/force-type.ts +++ b/src/app/force/force-type.ts @@ -9,7 +9,7 @@ import { MapPlayer, Unit, Trigger } from "w3ts"; import { EVENT_TYPE } from "app/events/event"; -const GENERIC_CHAT_SOUND_REF = new SoundWithCooldown(3, 'Sounds\\RadioChatter.mp3', true); +export const GENERIC_CHAT_SOUND_REF = new SoundWithCooldown(3, 'Sounds\\RadioChatter.mp3', true); export abstract class ForceType { // Keep track of players in force protected players: Array = []; diff --git a/src/app/world/world-module.ts b/src/app/world/world-module.ts index c2c98bb3..06f7be01 100644 --- a/src/app/world/world-module.ts +++ b/src/app/world/world-module.ts @@ -37,7 +37,7 @@ export class WorldModule { * @param unit * @param to */ - private handleTravel(unit: Unit, to: ZONE_TYPE) { + public handleTravel(unit: Unit, to: ZONE_TYPE) { const uHandle = unit.id; const oldZone = this.unitLocation.get(uHandle); const newZone = this.getZone(to); @@ -130,4 +130,24 @@ export class WorldModule { if (!whichUnit) Log.Error("getUnitZone called but unit is undefined"); return this.unitLocation.get(whichUnit.id); } + + /** + * Removes the unit from our data list + * REQUIRED to maintain correct state + * @param whichUnit + */ + removeUnit(whichUnit: Unit) { + const zone = this.getUnitZone(whichUnit); + + if (zone) { + // Log.Information("Removing unit "+whichUnit.name+" from "+zone.id); + // Force on leave + zone.onLeave(this, whichUnit); + // Remove data on it + this.unitLocation.delete(whichUnit.id); + } + else { + Log.Information("Remove zone failed for "+whichUnit.name); + } + } } \ No newline at end of file diff --git a/src/app/world/zone-type.ts b/src/app/world/zone-type.ts index 4f229709..3975c8e1 100644 --- a/src/app/world/zone-type.ts +++ b/src/app/world/zone-type.ts @@ -34,6 +34,7 @@ export class Zone { public onLeave(world: WorldModule, unit: Unit) { const idx = this.unitsInside.indexOf(unit); if (idx >= 0) this.unitsInside.splice(idx, 1); + else Log.Warning("Failed to remove unit "+unit.name+" from "+this.id); } /** @@ -52,10 +53,20 @@ export class Zone { * Returns all players present in a zone */ public getPlayersInZone() { - let players = this.unitsInside.map(u => u.owner); - return players.filter(function(elem, index, self) { - return index === self.indexOf(elem); - }); + try { + let players = this.unitsInside.map(u => { + // Log.Information("Getting "+u.name); + return u.owner; + }); + return players.filter(function(elem, index, self) { + return index === self.indexOf(elem); + }); + } + catch (e) { + Log.Error("Failed to get players in zone "+this.id); + Log.Error(e); + return []; + } } public doCauseFear() { return false; }