2014-07-03 19:22:37 +00:00
|
|
|
/*--
|
|
|
|
Crystal communicator
|
|
|
|
Author: Sven2
|
|
|
|
|
|
|
|
Shining structure built from gems and metal
|
|
|
|
--*/
|
|
|
|
|
|
|
|
#include Library_Structure
|
|
|
|
|
|
|
|
local top_face, base_face;
|
|
|
|
|
|
|
|
public func IsCrystalCommunicator() { return !base_face; }
|
|
|
|
|
|
|
|
/* Construction */
|
|
|
|
|
|
|
|
public func SetConstructionSiteOverlay(object site, int dir, object stick, object component_obj)
|
|
|
|
{
|
|
|
|
// Play component-specific sound for adding stuff to the site
|
|
|
|
if (component_obj && !component_obj->GetDefFragile()) component_obj->~Hit();
|
|
|
|
// Construction site graphics by provided metal
|
|
|
|
var metal_completion = site->ContentsCount(Metal) * 3 / Max(GetComponent(Metal, nil, nil, this), 1);
|
|
|
|
site->SetGraphics(["Site0", "Site1", "Site2", nil][metal_completion], CrystalCommunicator, 1, GFXOV_MODE_Base);
|
|
|
|
site->SetGraphics(nil, nil, 2);
|
|
|
|
// Add graphics of contained gems
|
|
|
|
UpdateGemOverlays(site, [1, 3, 7, 12][metal_completion]);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public func DoConstructionEffects(object site)
|
|
|
|
{
|
|
|
|
// Grab all gems from site
|
|
|
|
GrabContents(site);
|
|
|
|
var metal;
|
|
|
|
while (metal = FindContents(Metal)) metal->RemoveObject();
|
|
|
|
// Site is done immediately
|
|
|
|
SetCon(100);
|
|
|
|
// Create TopFace overlay
|
2015-01-10 09:14:02 +00:00
|
|
|
top_face = CreateObjectAbove(GetID(),0,35,GetOwner());
|
2014-07-03 19:22:37 +00:00
|
|
|
top_face.Plane = this.Plane + 10;
|
|
|
|
top_face->SetGraphics("TopFace");
|
|
|
|
top_face->SetAction("Attach", this);
|
|
|
|
top_face.base_face = this;
|
|
|
|
// Transfer gem overlays
|
|
|
|
this.gem_overlays = site.gem_overlays;
|
|
|
|
UpdateGemOverlays(this, 13, true);
|
|
|
|
// Construction done. Remove site.
|
|
|
|
site->RemoveObject(site);
|
|
|
|
// Start finals effect
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Gem overlays */
|
|
|
|
|
|
|
|
static const CrystalCommunicator_GemsX = [ 15,440,336,221,121,298,220, 50, 14,333,129, 77],
|
|
|
|
CrystalCommunicator_GemsY = [255, 75,100, 84, 44,107, 15,130,107,153,149,106],
|
|
|
|
CrystalCommunicator_GemsZ = [ 5, 3, 4, 0, 4, 0, 5, 2, 3, 1, 1, 4],
|
|
|
|
CrystalCommunicator_GemCount = 12;
|
|
|
|
|
|
|
|
private func UpdateGemOverlays(object obj, int max_overlays, bool refresh_existing)
|
|
|
|
{
|
|
|
|
// Add overlays for any gems that have not yet been added
|
|
|
|
var gem_overlay_index = 3;
|
|
|
|
if (!obj.gem_overlays) obj.gem_overlays = [];
|
|
|
|
var n_overlays = GetLength(obj.gem_overlays);
|
|
|
|
var i;
|
|
|
|
// Remove overlays of gems that have left
|
|
|
|
for (i=0; i<n_overlays; ++i)
|
|
|
|
if (!obj.gem_overlays[i] || obj.gem_overlays[i]->Contained() != obj)
|
|
|
|
{
|
|
|
|
obj->SetGraphics(nil, nil, gem_overlay_index+i);
|
|
|
|
if (obj.top_face) obj.top_face->SetGraphics(nil, nil, gem_overlay_index+i);
|
|
|
|
obj.gem_overlays[i] = nil;
|
|
|
|
}
|
|
|
|
// Add new overlays
|
|
|
|
for (var gem in FindObjects(Find_Container(obj), Find_Func("GetGemColor")))
|
|
|
|
{
|
|
|
|
// Already displayed?
|
|
|
|
i = GetIndexOf(obj.gem_overlays, gem);
|
|
|
|
if (i>=0)
|
|
|
|
{
|
|
|
|
if (!refresh_existing) continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Find a spot for this gem
|
|
|
|
i = GetIndexOf(obj.gem_overlays, nil);
|
|
|
|
if (i<0) i=n_overlays;
|
|
|
|
// No free space?
|
|
|
|
if (i == max_overlays) if (refresh_existing) continue; else break;
|
|
|
|
}
|
|
|
|
// Add overlay
|
|
|
|
var gem_gfx = gem.graphics_index;
|
|
|
|
if (gem_gfx) gem_gfx = Format("%d", gem_gfx+1); else gem_gfx = nil;
|
|
|
|
var x = CrystalCommunicator_GemsX[i];
|
|
|
|
var y = CrystalCommunicator_GemsY[i];
|
|
|
|
var z = CrystalCommunicator_GemsZ[i];
|
|
|
|
var size = z*100+500;
|
|
|
|
var off_y;
|
|
|
|
if (obj == this) off_y = 35000; else off_y = 70000;
|
|
|
|
var gem_target;
|
|
|
|
if (obj.top_face && z>=3) gem_target = obj.top_face; else gem_target = obj;
|
|
|
|
gem_target->SetGraphics(gem_gfx, gem->GetID(), gem_overlay_index+i, GFXOV_MODE_Base);
|
|
|
|
gem_target->SetObjDrawTransform(size,0,x*200-45000, 0,size,y*200-off_y, gem_overlay_index+i);
|
|
|
|
if (z<3) gem_target->SetClrModulation(0xffb0b0b0, gem_overlay_index+i);
|
|
|
|
// Remember in list
|
|
|
|
obj.gem_overlays[i] = gem;
|
|
|
|
n_overlays = GetLength(obj.gem_overlays);
|
|
|
|
}
|
|
|
|
// Make sure a glitter effect is there
|
|
|
|
if (!GetEffect("IntGemGlitter", obj)) AddEffect("IntGemGlitter", obj, 1, 12, nil, CrystalCommunicator);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private func FxIntGemGlitterTimer(target)
|
|
|
|
{
|
|
|
|
// Glitter at random gem position
|
|
|
|
if (Random(2))
|
|
|
|
{
|
|
|
|
var i = Random(12), gem;
|
|
|
|
if (gem = target.gem_overlays[i])
|
|
|
|
{
|
|
|
|
var x = CrystalCommunicator_GemsX[i]/5 - 45;
|
|
|
|
var y = CrystalCommunicator_GemsY[i]/5 - 35;
|
|
|
|
if (target->GetID() == ConstructionSite) y -= 35;
|
|
|
|
var size = CrystalCommunicator_GemsZ[i]*4 + 10;
|
|
|
|
var sparkle_fx = GetEffect("Sparkle", gem);
|
|
|
|
sparkle_fx.Size = PV_KeyFrames(1, 0, 0, 500, size, 1000, 0); // modifying value directly in gem, assuming gem won't leave this structure any more
|
|
|
|
if (sparkle_fx && sparkle_fx.particles)
|
|
|
|
target->CreateParticle("MagicRing", x, y, 0, 0, size, sparkle_fx.particles, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* End sequence */
|
|
|
|
|
|
|
|
local ruby_particle, amethyst_particle, beam_particles, gem_particles;
|
|
|
|
local flash_particle, small_flash_particle, large_flash_particle;
|
|
|
|
local time;
|
|
|
|
local send_code, next_send_time, send_code_pos;
|
|
|
|
|
|
|
|
public func StartCommunication()
|
|
|
|
{
|
|
|
|
// forward to main object
|
|
|
|
if (base_face) return base_face->StartCommunication();
|
|
|
|
// Init particles
|
|
|
|
// Gem particles
|
|
|
|
beam_particles = CreateArray(CrystalCommunicator_GemCount);
|
|
|
|
gem_particles = CreateArray(CrystalCommunicator_GemCount);
|
|
|
|
ruby_particle = new Particles_MagicRing() { R = 0xff, G = 0x00, B = 0x30 };
|
|
|
|
amethyst_particle = new Particles_MagicRing() { R = 0xa0, G = 0x00, B = 0xff };
|
|
|
|
for (var i=0; i<CrystalCommunicator_GemCount; ++i)
|
|
|
|
{
|
|
|
|
var base;
|
|
|
|
if (this.gem_overlays && this.gem_overlays[i])
|
|
|
|
{
|
|
|
|
if (this.gem_overlays[i]->GetID() == Ruby) base = ruby_particle; else base = amethyst_particle;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (i%2) base = ruby_particle; else base = amethyst_particle;
|
|
|
|
}
|
|
|
|
gem_particles[i] = base;
|
|
|
|
var x = CrystalCommunicator_GemsX[i]/5 - 45;
|
|
|
|
beam_particles[i] = CreateCirclingParticle(base, 100, 20, Abs(x), x>0);
|
|
|
|
}
|
|
|
|
// Central flash particles
|
|
|
|
flash_particle = {
|
|
|
|
Size = PV_KeyFrames(0, 0, 0, 500, 60, 1000, 0),
|
|
|
|
R = PV_KeyFrames(0, 750, 255, 1000, 0),
|
|
|
|
G = PV_KeyFrames(0, 300, 255, 1000, 0),
|
|
|
|
B = PV_KeyFrames(0, 300, 255, 500, 0),
|
|
|
|
Rotation = PV_Random(0, 360),
|
|
|
|
Alpha = PV_KeyFrames(0, 0, 255, 750, 100, 1000, 0),
|
|
|
|
BlitMode = GFX_BLIT_Additive,
|
|
|
|
Attach = ATTACH_Front | ATTACH_MoveRelative,
|
|
|
|
};
|
|
|
|
large_flash_particle = {
|
|
|
|
Size = PV_KeyFrames(0, 0, 0, 500, 200, 1000, 0),
|
|
|
|
R = PV_KeyFrames(0, 750, 255, 1000, 0),
|
|
|
|
G = PV_KeyFrames(0, 300, 255, 1000, 0),
|
|
|
|
B = PV_KeyFrames(0, 300, 255, 500, 0),
|
|
|
|
Rotation = PV_Random(0, 360),
|
|
|
|
Alpha = PV_KeyFrames(0, 0, 255, 750, 100, 1000, 0),
|
|
|
|
BlitMode = GFX_BLIT_Additive,
|
|
|
|
Attach = ATTACH_Front | ATTACH_MoveRelative,
|
|
|
|
};
|
|
|
|
// Gem flash particle
|
|
|
|
small_flash_particle = {
|
|
|
|
Size = PV_KeyFrames(0, 0, 0, 500, 20, 1000, 0),
|
|
|
|
R = PV_KeyFrames(0, 750, 255, 1000, 0),
|
|
|
|
G = PV_KeyFrames(0, 300, 255, 1000, 0),
|
|
|
|
B = PV_KeyFrames(0, 300, 255, 500, 0),
|
|
|
|
Rotation = PV_Random(0, 360),
|
|
|
|
Alpha = PV_KeyFrames(0, 0, 255, 750, 100, 1000, 0),
|
|
|
|
BlitMode = GFX_BLIT_Additive,
|
|
|
|
Attach = ATTACH_Front | ATTACH_MoveRelative,
|
|
|
|
};
|
|
|
|
// Run effects
|
2015-12-13 21:14:55 +00:00
|
|
|
Sound("CrystalCommunicator::CrystalCommCharge");
|
2014-07-03 19:22:37 +00:00
|
|
|
time = 0;
|
|
|
|
AddTimer(this.PreActivity, 5);
|
|
|
|
}
|
|
|
|
|
|
|
|
private func PreActivity()
|
|
|
|
{
|
|
|
|
// Warmup effects
|
|
|
|
var i,x,y,z;
|
|
|
|
for (i=0; i<CrystalCommunicator_GemCount; ++i)
|
|
|
|
{
|
|
|
|
x = CrystalCommunicator_GemsX[i]/5 - 45;
|
|
|
|
y = CrystalCommunicator_GemsY[i]/5 - 35;
|
|
|
|
z = CrystalCommunicator_GemsZ[i];
|
|
|
|
var gem_target;
|
|
|
|
if (top_face && z>=3) gem_target = top_face; else gem_target = this;
|
|
|
|
if (time < 20 || !Random(3))
|
|
|
|
{
|
|
|
|
if (!(time % 5)) gem_target->CreateParticle("StarFlash", x,y, 0,0, 60+Random(10), small_flash_particle, 1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
gem_target->CreateParticle("StarFlash", x, y, -x, -y, 10, small_flash_particle, 10);
|
|
|
|
}
|
2015-12-13 21:14:55 +00:00
|
|
|
if (time == 20) Sound("CrystalCommunicator::CrystalCommBoost");
|
2014-07-03 19:22:37 +00:00
|
|
|
if (time > 50)
|
|
|
|
{
|
|
|
|
RemoveTimer(this.PreActivity);
|
|
|
|
time = 0;
|
|
|
|
CreateParticle("StarFlash", PV_Random(-12, +12), PV_Random(-12, +12), PV_Random(-10, +10),PV_Random(-10, +10), PV_Random(20, 100), large_flash_particle, 10);
|
2015-12-13 21:14:55 +00:00
|
|
|
Sound("CrystalCommunicator::CrystalCommWumm");
|
2014-07-03 19:22:37 +00:00
|
|
|
SetAction("Active");
|
|
|
|
AddTimer(this.Activity, 1);
|
|
|
|
}
|
|
|
|
++time;
|
|
|
|
}
|
|
|
|
|
2014-07-05 14:47:34 +00:00
|
|
|
public func StopCommunication()
|
|
|
|
{
|
|
|
|
// forward to main object
|
|
|
|
if (base_face) return base_face->StopCommunication();
|
|
|
|
// Stop activities
|
|
|
|
RemoveTimer(this.PreActivity);
|
|
|
|
RemoveTimer(this.Activity);
|
|
|
|
SetAction("Idle");
|
|
|
|
time = 0;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-07-03 19:22:37 +00:00
|
|
|
private func Activity()
|
|
|
|
{
|
|
|
|
// Send codes
|
|
|
|
if (send_code)
|
|
|
|
{
|
|
|
|
if (next_send_time == time)
|
|
|
|
{
|
|
|
|
var send_char = GetChar(send_code, send_code_pos++);
|
|
|
|
if (!send_char)
|
|
|
|
{
|
|
|
|
// All sent.
|
|
|
|
send_code = nil;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Next char to send
|
|
|
|
if (send_char == GetChar("."))
|
|
|
|
{
|
2015-12-13 21:14:55 +00:00
|
|
|
Sound("CrystalCommunicator::CrystalCommToneA");
|
2014-07-03 19:22:37 +00:00
|
|
|
next_send_time = time + 13;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-12-13 21:14:55 +00:00
|
|
|
Sound("CrystalCommunicator::CrystalCommToneB");
|
2014-07-03 19:22:37 +00:00
|
|
|
next_send_time = time + 27;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (next_send_time - time > 10)
|
|
|
|
{
|
|
|
|
// During tone: Effects
|
|
|
|
CreateParticle("StarFlash", PV_Random(-3, +3), 0, 0,-30, 500, flash_particle, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Effects
|
|
|
|
// Circulate through gems
|
|
|
|
var i = time % CrystalCommunicator_GemCount;
|
|
|
|
var x = CrystalCommunicator_GemsX[i]/5 - 45, y = CrystalCommunicator_GemsY[i]/5 - 35, z = CrystalCommunicator_GemsZ[i];
|
|
|
|
var gem_target;
|
|
|
|
if (top_face && z>=3) gem_target = top_face; else gem_target = this;
|
|
|
|
// Create ring moving upwards
|
|
|
|
if (Abs(x) > 5) CreateParticle("MagicRing", x, y, 0, -Min(time/20,10), 2000, beam_particles[i], 1);
|
|
|
|
// Create flash at gem
|
|
|
|
gem_target->CreateParticle("StarFlash", x, y, 0, 0, 20+Random(10), gem_particles[i], 1);
|
|
|
|
// Create central flash
|
|
|
|
if (!(time % 5)) CreateParticle("StarFlash", PV_Random(-6, +6), PV_Random(-6, +6), 0,0, 20+Random(10), flash_particle, 1);
|
|
|
|
++time;
|
|
|
|
}
|
|
|
|
|
|
|
|
private func CreateCirclingParticle(proplist prototype, int frames_per_cycle, int num_cycles, int rad, bool start_backmove)
|
|
|
|
{
|
|
|
|
var a = (rad * 10000000) / (2429 * frames_per_cycle * frames_per_cycle);
|
|
|
|
var ang0 = (!!start_backmove) * 180;
|
|
|
|
var particle = {
|
|
|
|
Prototype = prototype,
|
|
|
|
Size = PV_Sin(PV_Linear( ang0, 360*num_cycles),5,8),
|
|
|
|
ForceX = PV_Sin(PV_Linear( ang0+90, ang0+90+360*num_cycles), a, 0),
|
|
|
|
ForceY = 0,
|
|
|
|
Attach = ATTACH_Front | ATTACH_MoveRelative,
|
|
|
|
};
|
|
|
|
return particle;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public func SendCode(string code)
|
|
|
|
{
|
|
|
|
send_code = code;
|
|
|
|
next_send_time = time;
|
|
|
|
send_code_pos = 0;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Definition data */
|
|
|
|
|
|
|
|
public func Definition(proplist def)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
local ActMap = {
|
|
|
|
Attach = {
|
|
|
|
Prototype = Action,
|
|
|
|
Name = "Attach",
|
|
|
|
Procedure = DFA_ATTACH,
|
|
|
|
Directions = 1,
|
|
|
|
FacetBase = 1,
|
|
|
|
Length = 1,
|
|
|
|
Delay = 0,
|
|
|
|
NextAction = "Hold"
|
|
|
|
},
|
|
|
|
Active = {
|
|
|
|
Prototype = Action,
|
|
|
|
Name = "Active",
|
|
|
|
Procedure = DFA_NONE,
|
|
|
|
Directions = 1,
|
|
|
|
FacetBase = 1,
|
|
|
|
Delay = 0,
|
|
|
|
Length = 1,
|
|
|
|
NextAction = "Active",
|
2015-12-13 21:14:55 +00:00
|
|
|
Sound = "CrystalCommunicator::CrystalCommActive",
|
2014-07-03 19:22:37 +00:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
local Collectible = false;
|
|
|
|
local Name = "$Name$";
|
|
|
|
local Description = "$Description$";
|
|
|
|
local Touchable = 0;
|
2015-01-10 09:14:02 +00:00
|
|
|
local Plane = 280;
|