openclonk/planet/Missions.ocf/DeepSeaMining.ocs/CrystalCommunicator.ocd/Script.c

359 lines
10 KiB
C

/*--
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), 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
top_face = CreateObjectAbove(GetID(),0,35,GetOwner());
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
Sound("CrystalCommunicator::CrystalCommCharge");
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);
}
if (time == 20) Sound("CrystalCommunicator::CrystalCommBoost");
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);
Sound("CrystalCommunicator::CrystalCommWumm");
SetAction("Active");
AddTimer(this.Activity, 1);
}
++time;
}
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;
}
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("."))
{
Sound("CrystalCommunicator::CrystalCommToneA");
next_send_time = time + 13;
}
else
{
Sound("CrystalCommunicator::CrystalCommToneB");
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",
Sound = "CrystalCommunicator::CrystalCommActive",
},
};
local Collectible = false;
local Name = "$Name$";
local Description = "$Description$";
local Touchable = 0;
local Plane = 280;
local Components = {Ruby = 6, Amethyst = 6, Metal = 6};